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);

Reply via email to