On Sun, May 28, 2017 at 10:52:24PM +0200, Sebastian Benoit wrote: > which makes me think: > would a global local-address be good enough?
Attached is a patch that allows you to specify the source for outgoing queries, as a global option. Example ntpd.conf: query from 165.254.255.33 query from 2001:728:1808::26 servers ntp.ring.nlnog.net I have a number of remarks myself: - unsure about the bzero() in parse_config() - should we check 2+ declarations of 'query from', or just use the last one like this patch does now, (we don't check for duplicate 'weight' etc either) - the ipv4 / ipv6 approach with 'struct dual_addr' seems clumsy, is this what life is like in an ipv4 + ipv6 world? Any suggestions how to improve? Kind regards, Job --- src/usr.sbin/ntpd/client.c | 13 +++++++++++++ src/usr.sbin/ntpd/ntp.c | 1 + src/usr.sbin/ntpd/ntpd.conf.5 | 8 ++++++++ src/usr.sbin/ntpd/ntpd.h | 7 +++++++ src/usr.sbin/ntpd/parse.y | 31 ++++++++++++++++++++++++++++++- 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/usr.sbin/ntpd/client.c b/src/usr.sbin/ntpd/client.c index ddbb1281..7d921dcf 100644 --- a/src/usr.sbin/ntpd/client.c +++ b/src/usr.sbin/ntpd/client.c @@ -137,11 +137,24 @@ client_query(struct ntp_peer *p) if (p->query->fd == -1) { struct sockaddr *sa = (struct sockaddr *)&p->addr->ss; + struct sockaddr *qa4 = (struct sockaddr *)&p->query_addr.v4; + struct sockaddr *qa6 = (struct sockaddr *)&p->query_addr.v6; if ((p->query->fd = socket(p->addr->ss.ss_family, SOCK_DGRAM, 0)) == -1) fatal("client_query socket"); + if (p->addr->ss.ss_family == qa4->sa_family) { + if (bind(p->query->fd, qa4, SA_LEN(qa4)) == -1) + fatal("couldn't bind to IPv4 query address: %s", + log_sockaddr(qa4)); + } + else if (p->addr->ss.ss_family == qa6->sa_family) { + if (bind(p->query->fd, qa6, SA_LEN(qa6)) == -1) + fatal("couldn't bind to IPv6 query address: %s", + log_sockaddr(qa6)); + } + if (connect(p->query->fd, sa, SA_LEN(sa)) == -1) { if (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EHOSTUNREACH || errno == EADDRNOTAVAIL) { diff --git a/src/usr.sbin/ntpd/ntp.c b/src/usr.sbin/ntpd/ntp.c index f3366640..b0f80294 100644 --- a/src/usr.sbin/ntpd/ntp.c +++ b/src/usr.sbin/ntpd/ntp.c @@ -521,6 +521,7 @@ ntp_dispatch_imsg_dns(void) if (peer->addr_head.pool) { npeer = new_peer(); npeer->weight = peer->weight; + npeer->query_addr = peer->query_addr; h->next = NULL; npeer->addr = h; npeer->addr_head.a = h; diff --git a/src/usr.sbin/ntpd/ntpd.conf.5 b/src/usr.sbin/ntpd/ntpd.conf.5 index 6e4e0012..b8f03b22 100644 --- a/src/usr.sbin/ntpd/ntpd.conf.5 +++ b/src/usr.sbin/ntpd/ntpd.conf.5 @@ -67,6 +67,14 @@ or listen on 127.0.0.1 listen on ::1 listen on 127.0.0.1 rtable 4 +.It Xo Ic source from Ar address +.Xc +Specify a Local IP address the +.Xr ntpd 8 +daemon should use for outgoing queries. +.Bd -literal -offset indent +query from 10.0.0.1 +query from 2001:db8::1 .Ed .It Xo Ic sensor Ar device .Op Ic correction Ar microseconds diff --git a/src/usr.sbin/ntpd/ntpd.h b/src/usr.sbin/ntpd/ntpd.h index 613b29b2..ded2948a 100644 --- a/src/usr.sbin/ntpd/ntpd.h +++ b/src/usr.sbin/ntpd/ntpd.h @@ -106,6 +106,11 @@ struct listen_addr { int rtable; }; +struct dual_addr { + struct sockaddr_storage v4; + struct sockaddr_storage v6; +}; + struct ntp_addr { struct ntp_addr *next; struct sockaddr_storage ss; @@ -153,6 +158,7 @@ struct ntp_peer { struct ntp_query *query; struct ntp_offset reply[OFFSET_ARRAY_SIZE]; struct ntp_offset update; + struct dual_addr query_addr; enum client_state state; time_t next; time_t deadline; @@ -219,6 +225,7 @@ struct ntpd_conf { TAILQ_HEAD(constraints, constraint) constraints; struct ntp_status status; struct ntp_freq freq; + struct dual_addr query_addr; u_int32_t scale; int debug; int verbose; diff --git a/src/usr.sbin/ntpd/parse.y b/src/usr.sbin/ntpd/parse.y index 6d507957..dc131270 100644 --- a/src/usr.sbin/ntpd/parse.y +++ b/src/usr.sbin/ntpd/parse.y @@ -58,6 +58,7 @@ int lungetc(int); int findeol(void); struct ntpd_conf *conf; +struct dual_addr query_addr; struct opts { int weight; @@ -80,7 +81,7 @@ typedef struct { %} -%token LISTEN ON CONSTRAINT CONSTRAINTS FROM +%token LISTEN ON CONSTRAINT CONSTRAINTS FROM QUERY %token SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT %token ERROR %token <v.string> STRING @@ -130,6 +131,30 @@ main : LISTEN ON address listen_opts { free($3->name); free($3); } + | QUERY FROM STRING { + struct sockaddr_in sin4 = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in) + }; + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6) + }; + + if (inet_pton(AF_INET, $3, &sin4.sin_addr) == 1) { + memcpy(&query_addr.v4, &sin4, sin4.sin_len); + } + else if (inet_pton(AF_INET6, $3, &sin6.sin6_addr) == 1) { + memcpy(&query_addr.v6, &sin6, sin6.sin6_len); + } + else { + yyerror("invalid IPv4 or IPv6 address: %s\n", $3); + free($3); + YYERROR; + } + + free($3); + } | SERVERS address server_opts { struct ntp_peer *p; struct ntp_addr *h, *next; @@ -153,6 +178,7 @@ main : LISTEN ON address listen_opts { p = new_peer(); p->weight = $3.weight; + p->query_addr = query_addr; p->addr = h; p->addr_head.a = h; p->addr_head.pool = 1; @@ -190,6 +216,7 @@ main : LISTEN ON address listen_opts { } p->weight = $3.weight; + p->query_addr = query_addr; p->addr_head.a = p->addr; p->addr_head.pool = 0; p->addr_head.name = strdup($2->name); @@ -461,6 +488,7 @@ lookup(char *s) { "from", FROM}, { "listen", LISTEN}, { "on", ON}, + { "query", QUERY}, { "refid", REFID}, { "rtable", RTABLE}, { "sensor", SENSOR}, @@ -747,6 +775,7 @@ parse_config(const char *filename, struct ntpd_conf *xconf) TAILQ_INIT(&conf->ntp_peers); TAILQ_INIT(&conf->ntp_conf_sensors); TAILQ_INIT(&conf->constraints); + bzero(&conf->query_addr, sizeof(struct dual_addr)); if ((file = pushfile(filename)) == NULL) { return (-1);