On Sun, Dec 17, 2023 at 05:37:50PM +0100, Otto Moerbeek wrote: > On Fri, Dec 15, 2023 at 02:54:19PM +0100, Otto Moerbeek wrote: > > > On Sun, Dec 10, 2023 at 09:57:08AM +0100, Otto Moerbeek wrote: > > > > > On Fri, Dec 01, 2023 at 09:18:32PM +0000, guilherme.janc...@yandex.com > > > wrote: > > > > > > > >Synopsis: Repeated NTP peers in OpenNTPD > > > > >Category: user > > > > >Environment: > > > > System : OpenBSD 7.4 > > > > Details : OpenBSD 7.4 (GENERIC.MP) #0: Sun Oct 22 12:13:42 > > > > MDT 2023 > > > > > > > > r...@syspatch-74-amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP > > > > > > > > Architecture: OpenBSD.amd64 > > > > Machine : amd64 > > > > >Description: > > > > If the same address/domain is specified multiple times in > > > > OpenNTPD's configuration file, or if multiple domains resolve > > > > to the same IP address, OpenNTPD will treat the same IP address > > > > as if it was multiple peers. > > > > >How-To-Repeat: > > > > This can be tested by appending `server 127.0.0.1` multiple > > > > times to the configuration file. > > > > > > > > Alternatively, assuming a default OpenNTPD configuration file > > > > from OpenBSD 7.4, the following entries can be added to > > > > /etc/hosts: > > > > 127.0.0.1 time.cloudflare.com > > > > 127.0.0.1 pool.ntp.org > > > > > > > > I noticed this bug using the default 7.4 configuration file. It > > > > can happen because time.cloudflare.com is part of pool.ntp.org: > > > > https://www.ntppool.org/scores/162.159.200.1 > > > > https://www.ntppool.org/scores/162.159.200.123 > > > > >Fix: > > > > Removing the `server time.cloudflare.com` line from the > > > > configuration file is a simple fix the user can make, but > > > > OpenNTPD should check if an IP address it tries to add to the > > > > list of peers is already a peer, and ignore it if so. If a > > > > server is added with the `server` (not `servers`) keyword in the > > > > configuration file, OpenNTPD should try the next IP the domain > > > > resolves to if applicable. > > > > > > > > > > Thanks for the report, I'll take a look. > > > > > > -Otto > > > > > > > Due to verious reasons this is all a bit complicated, I did not find a > > nice solution yet. Some patience required. > > > > -Otto > > > > Found some more time. Try this. The approach is: we assume the > addresses of a pool (from the "servers" keyword) vary over time. So if > one of the pool addresses is in the address list of a peer ("server") > we skip it ad try to re-resolve the pool, lookin for a new address, as > we already did earlier when a pool member doe snot respond. > > I decided to not implement "the move to abother address of the list" > in the "server" case. The address logic is already complex enough. > > -Otto > > Index: client.c > =================================================================== > RCS file: /home/cvs/src/usr.sbin/ntpd/client.c,v > diff -u -p -r1.117 client.c > --- client.c 24 Mar 2022 07:37:19 -0000 1.117 > +++ client.c 17 Dec 2023 09:13:16 -0000 > @@ -108,6 +108,7 @@ client_nextaddr(struct ntp_peer *p) > return (-1); > > if (p->addr_head.a == NULL) { > + log_debug("kicking query for %s", p->addr_head.name); > priv_dns(IMSG_HOST_DNS, p->addr_head.name, p->id); > p->state = STATE_DNS_INPROGRESS; > return (-1); > @@ -145,7 +146,15 @@ client_query(struct ntp_peer *p) > } > > if (p->state < STATE_DNS_DONE || p->addr == NULL) > - return (-1); > + return (0); > + > + /* if we're a pool member and a peer has taken our address, signal */ > + if (p->addr_head.pool != 0 && addr_already_used(&p->addr->ss)) { > + log_debug("pool addr %s used by peer, will reresolve pool", > + log_sockaddr((struct sockaddr *)&p->addr->ss)); > + p->senderrors++; > + return (0); > + } > > if (p->query.fd == -1) { > struct sockaddr *sa = (struct sockaddr *)&p->addr->ss; > Index: ntp.c > =================================================================== > RCS file: /home/cvs/src/usr.sbin/ntpd/ntp.c,v > diff -u -p -r1.171 ntp.c > --- ntp.c 6 Dec 2023 15:51:53 -0000 1.171 > +++ ntp.c 17 Dec 2023 09:13:16 -0000 > @@ -54,8 +54,6 @@ int ntp_dispatch_imsg(void); > int ntp_dispatch_imsg_dns(void); > void peer_add(struct ntp_peer *); > void peer_remove(struct ntp_peer *); > -int inpool(struct sockaddr_storage *, > - struct sockaddr_storage[MAX_SERVERS_DNS], size_t); > > void > ntp_sighdlr(int sig) > @@ -524,23 +522,52 @@ ntp_dispatch_imsg(void) > return (0); > } > > -int > +static int > +addr_equal(struct sockaddr_storage *a, struct sockaddr_storage *b) > +{ > + if (a->ss_family != b->ss_family) > + return 0; > + if (a->ss_family == AF_INET) { > + if (((struct sockaddr_in *)a)->sin_addr.s_addr == > + ((struct sockaddr_in *)b)->sin_addr.s_addr) > + return 1; > + } else if (memcmp(&((struct sockaddr_in6 *)a)->sin6_addr, > + &((struct sockaddr_in6 *)b)->sin6_addr, > + sizeof(struct sockaddr_in6)) == 0) { > + return 1; > + } > + return 0; > +} > + > +static int > inpool(struct sockaddr_storage *a, > struct sockaddr_storage old[MAX_SERVERS_DNS], size_t n) > { > size_t i; > > for (i = 0; i < n; i++) { > - if (a->ss_family != old[i].ss_family) > + if (addr_equal(a, &old[i])) > + return 1; > + } > + return 0; > +} > + > +/* check to see af an address is already listed by a "server" peer > + in that case, it does not get added to a pool */ > +int > +addr_already_used(struct sockaddr_storage *a) > +{ > + struct ntp_peer *peer; > + struct ntp_addr *addr; > + > + TAILQ_FOREACH(peer, &conf->ntp_peers, entry) { > + if (peer->addr_head.pool != 0) > continue; > - if (a->ss_family == AF_INET) { > - if (((struct sockaddr_in *)a)->sin_addr.s_addr == > - ((struct sockaddr_in *)&old[i])->sin_addr.s_addr) > + addr = peer->addr_head.a; > + while (addr != NULL) { > + if (addr_equal(a, &addr->ss)) > return 1; > - } else if (memcmp(&((struct sockaddr_in6 *)a)->sin6_addr, > - &((struct sockaddr_in6 *)&old[i])->sin6_addr, > - sizeof(struct sockaddr_in6)) == 0) { > - return 1; > + addr = addr->next; > } > } > return 0; > @@ -628,8 +655,11 @@ ntp_dispatch_imsg_dns(void) > free(h); > continue; > } > - if (inpool(&h->ss, existing, > - n)) { > + if (addr_already_used(&h->ss) || > + inpool(&h->ss, existing, n)) { > + log_debug("skipping address %s > for %s", > + log_sockaddr((struct > sockaddr *) > + &h->ss), > peer->addr_head.name); > free(h); > continue; > } > @@ -660,8 +690,11 @@ ntp_dispatch_imsg_dns(void) > } > if (dlen != 0) > fatalx("IMSG_HOST_DNS: dlen != 0"); > - if (peer->addr_head.pool) > - peer_remove(peer); > + if (peer->addr_head.pool) { > + if (peercount > addrcount) > + peer_remove(peer); > + peer->state = STATE_NONE; > + } > else > client_addr_init(peer); > break; > Index: ntpd.h > =================================================================== > RCS file: /home/cvs/src/usr.sbin/ntpd/ntpd.h,v > diff -u -p -r1.152 ntpd.h > --- ntpd.h 27 Nov 2022 13:19:00 -0000 1.152 > +++ ntpd.h 17 Dec 2023 09:13:16 -0000 > @@ -371,6 +371,7 @@ int server_dispatch(int, struct ntpd_con > int client_peer_init(struct ntp_peer *); > int client_addr_init(struct ntp_peer *); > int client_nextaddr(struct ntp_peer *); > +int addr_already_used(struct sockaddr_storage *); > int client_query(struct ntp_peer *); > int client_dispatch(struct ntp_peer *, u_int8_t, u_int8_t); > void client_log_error(struct ntp_peer *, const char *, int); >
Previous diff had a write after free. -Otto Index: client.c =================================================================== RCS file: /home/cvs/src/usr.sbin/ntpd/client.c,v diff -u -p -r1.117 client.c --- client.c 24 Mar 2022 07:37:19 -0000 1.117 +++ client.c 18 Dec 2023 06:20:13 -0000 @@ -108,6 +108,7 @@ client_nextaddr(struct ntp_peer *p) return (-1); if (p->addr_head.a == NULL) { + log_debug("kicking query for %s", p->addr_head.name); priv_dns(IMSG_HOST_DNS, p->addr_head.name, p->id); p->state = STATE_DNS_INPROGRESS; return (-1); @@ -145,7 +146,15 @@ client_query(struct ntp_peer *p) } if (p->state < STATE_DNS_DONE || p->addr == NULL) - return (-1); + return (0); + + /* if we're a pool member and a peer has taken our address, signal */ + if (p->addr_head.pool != 0 && addr_already_used(&p->addr->ss)) { + log_debug("pool addr %s used by peer, will reresolve pool", + log_sockaddr((struct sockaddr *)&p->addr->ss)); + p->senderrors++; + return (0); + } if (p->query.fd == -1) { struct sockaddr *sa = (struct sockaddr *)&p->addr->ss; Index: ntp.c =================================================================== RCS file: /home/cvs/src/usr.sbin/ntpd/ntp.c,v diff -u -p -r1.171 ntp.c --- ntp.c 6 Dec 2023 15:51:53 -0000 1.171 +++ ntp.c 18 Dec 2023 06:20:13 -0000 @@ -54,8 +54,6 @@ int ntp_dispatch_imsg(void); int ntp_dispatch_imsg_dns(void); void peer_add(struct ntp_peer *); void peer_remove(struct ntp_peer *); -int inpool(struct sockaddr_storage *, - struct sockaddr_storage[MAX_SERVERS_DNS], size_t); void ntp_sighdlr(int sig) @@ -524,23 +522,52 @@ ntp_dispatch_imsg(void) return (0); } -int +static int +addr_equal(struct sockaddr_storage *a, struct sockaddr_storage *b) +{ + if (a->ss_family != b->ss_family) + return 0; + if (a->ss_family == AF_INET) { + if (((struct sockaddr_in *)a)->sin_addr.s_addr == + ((struct sockaddr_in *)b)->sin_addr.s_addr) + return 1; + } else if (memcmp(&((struct sockaddr_in6 *)a)->sin6_addr, + &((struct sockaddr_in6 *)b)->sin6_addr, + sizeof(struct sockaddr_in6)) == 0) { + return 1; + } + return 0; +} + +static int inpool(struct sockaddr_storage *a, struct sockaddr_storage old[MAX_SERVERS_DNS], size_t n) { size_t i; for (i = 0; i < n; i++) { - if (a->ss_family != old[i].ss_family) + if (addr_equal(a, &old[i])) + return 1; + } + return 0; +} + +/* check to see af an address is already listed by a "server" peer + in that case, it does not get added to a pool */ +int +addr_already_used(struct sockaddr_storage *a) +{ + struct ntp_peer *peer; + struct ntp_addr *addr; + + TAILQ_FOREACH(peer, &conf->ntp_peers, entry) { + if (peer->addr_head.pool != 0) continue; - if (a->ss_family == AF_INET) { - if (((struct sockaddr_in *)a)->sin_addr.s_addr == - ((struct sockaddr_in *)&old[i])->sin_addr.s_addr) + addr = peer->addr_head.a; + while (addr != NULL) { + if (addr_equal(a, &addr->ss)) return 1; - } else if (memcmp(&((struct sockaddr_in6 *)a)->sin6_addr, - &((struct sockaddr_in6 *)&old[i])->sin6_addr, - sizeof(struct sockaddr_in6)) == 0) { - return 1; + addr = addr->next; } } return 0; @@ -628,8 +655,11 @@ ntp_dispatch_imsg_dns(void) free(h); continue; } - if (inpool(&h->ss, existing, - n)) { + if (addr_already_used(&h->ss) || + inpool(&h->ss, existing, n)) { + log_debug("skipping address %s for %s", + log_sockaddr((struct sockaddr *) + &h->ss), peer->addr_head.name); free(h); continue; } @@ -660,8 +690,12 @@ ntp_dispatch_imsg_dns(void) } if (dlen != 0) fatalx("IMSG_HOST_DNS: dlen != 0"); - if (peer->addr_head.pool) - peer_remove(peer); + if (peer->addr_head.pool) { + if (peercount > addrcount) + peer_remove(peer); + else + peer->state = STATE_NONE; + } else client_addr_init(peer); break; Index: ntpd.h =================================================================== RCS file: /home/cvs/src/usr.sbin/ntpd/ntpd.h,v diff -u -p -r1.152 ntpd.h --- ntpd.h 27 Nov 2022 13:19:00 -0000 1.152 +++ ntpd.h 18 Dec 2023 06:20:13 -0000 @@ -371,6 +371,7 @@ int server_dispatch(int, struct ntpd_con int client_peer_init(struct ntp_peer *); int client_addr_init(struct ntp_peer *); int client_nextaddr(struct ntp_peer *); +int addr_already_used(struct sockaddr_storage *); int client_query(struct ntp_peer *); int client_dispatch(struct ntp_peer *, u_int8_t, u_int8_t); void client_log_error(struct ntp_peer *, const char *, int);