Works for me, verified both v4 and v6 selection actually uses that address. Suggested a small improvement for ntpd.conf(5) to Job privately.
Would love to see this feature get imported :) Thanks Job! Paul On Tue, May 30, 2017 at 06:10:03PM +0200, Job Snijders wrote: | 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); | -- >++++++++[<++++++++++>-]<+++++++.>+++[<------>-]<.>+++[<+ +++++++++++>-]<.>++[<------------>-]<+.--------------.[-] http://www.weirdnet.nl/