Detect when a router advertisement packet changes due to config
authorflorian <florian@openbsd.org>
Wed, 11 Jul 2018 14:03:13 +0000 (14:03 +0000)
committerflorian <florian@openbsd.org>
Wed, 11 Jul 2018 14:03:13 +0000 (14:03 +0000)
change and if it does send a new advertisement.

The way this is implemented gives us various things for free:
- periodic sending of router advertisements
- send initial advertisement for every interface on startup

usr.sbin/rad/engine.c
usr.sbin/rad/frontend.c
usr.sbin/rad/rad.h

index a5d74cd..25df94b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: engine.c,v 1.3 2018/07/10 22:14:19 florian Exp $      */
+/*     $OpenBSD: engine.c,v 1.4 2018/07/11 14:03:13 florian Exp $      */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
 #include "rad.h"
 #include "engine.h"
 
-__dead void     engine_shutdown(void);
-void            engine_sig_handler(int sig, short, void *);
-void            engine_dispatch_frontend(int, short, void *);
-void            engine_dispatch_main(int, short, void *);
-void            parse_ra_rs(struct imsg_ra_rs *);
-void            parse_ra(struct imsg_ra_rs *);
-void            parse_rs(struct imsg_ra_rs *);
+#define        MAX_RTR_ADV_INTERVAL    600
+#define        MIN_RTR_ADV_INTERVAL    200
+
+struct engine_iface {
+       TAILQ_ENTRY(engine_iface)       entry;
+       struct event                    timer;
+       uint32_t                        if_index;
+};
+
+TAILQ_HEAD(, engine_iface)     engine_interfaces;
+
+
+__dead void             engine_shutdown(void);
+void                    engine_sig_handler(int sig, short, void *);
+void                    engine_dispatch_frontend(int, short, void *);
+void                    engine_dispatch_main(int, short, void *);
+void                    parse_ra_rs(struct imsg_ra_rs *);
+void                    parse_ra(struct imsg_ra_rs *);
+void                    parse_rs(struct imsg_ra_rs *);
+void                    update_iface(uint32_t);
+struct engine_iface    *find_engine_iface_by_id(uint32_t);
+void                    iface_timeout(int, short, void *);
 
 struct rad_conf        *engine_conf;
 struct imsgev          *iev_frontend;
@@ -170,6 +185,7 @@ engine_dispatch_frontend(int fd, short event, void *bula)
        struct imsg              imsg;
        struct imsg_ra_rs        ra_rs;
        ssize_t                  n;
+       uint32_t                 if_index;
        int                      shut = 0, verbose;
 
        ibuf = &iev->ibuf;
@@ -201,6 +217,13 @@ engine_dispatch_frontend(int fd, short event, void *bula)
                        memcpy(&ra_rs, imsg.data, sizeof(ra_rs));
                        parse_ra_rs(&ra_rs);
                        break;
+               case IMSG_UPDATE_IF:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index))
+                               fatal("%s: IMSG_UPDATE_IF wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&if_index, imsg.data, sizeof(if_index));
+                       update_iface(if_index);
+                       break;
                case IMSG_CTL_LOG_VERBOSE:
                        /* Already checked by frontend. */
                        memcpy(&verbose, imsg.data, sizeof(verbose));
@@ -441,3 +464,54 @@ parse_rs(struct imsg_ra_rs *rs)
        engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
            sizeof(send_ra));
 }
+
+struct engine_iface*
+find_engine_iface_by_id(uint32_t if_index)
+{
+       struct engine_iface     *engine_iface;
+
+       TAILQ_FOREACH(engine_iface, &engine_interfaces, entry) {
+               if (engine_iface->if_index == if_index)
+                       return engine_iface;
+       }
+       return (NULL);
+}
+
+void
+update_iface(uint32_t if_index)
+{
+       struct engine_iface     *engine_iface;
+       struct timeval           tv;
+
+       if ((engine_iface = find_engine_iface_by_id(if_index)) == NULL) {
+               engine_iface = calloc(1, sizeof(*engine_iface));
+               engine_iface->if_index = if_index;
+               evtimer_set(&engine_iface->timer, iface_timeout, engine_iface);
+       }
+
+       tv.tv_sec = 0;
+       tv.tv_usec = arc4random_uniform(1000000);
+       evtimer_add(&engine_iface->timer, &tv);
+}
+
+void
+iface_timeout(int fd, short events, void *arg)
+{
+       struct engine_iface     *engine_iface = (struct engine_iface *)arg;
+       struct imsg_send_ra      send_ra;
+       struct timeval           tv;
+
+
+       tv.tv_sec = MIN_RTR_ADV_INTERVAL +
+           arc4random_uniform(MAX_RTR_ADV_INTERVAL - MIN_RTR_ADV_INTERVAL);
+       tv.tv_usec = arc4random_uniform(1000000);
+
+       log_debug("%s new timeout in %lld", __func__, tv.tv_sec);
+
+       evtimer_add(&engine_iface->timer, &tv);
+
+       send_ra.if_index = engine_iface->if_index;
+       memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
+       engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
+           sizeof(send_ra));
+}
index 76a70b4..f6a7e7b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: frontend.c,v 1.2 2018/07/10 22:14:19 florian Exp $    */
+/*     $OpenBSD: frontend.c,v 1.3 2018/07/11 14:03:13 florian Exp $    */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -86,6 +86,8 @@
 #include "frontend.h"
 #include "control.h"
 
+#define        RA_MAX_SIZE     1500
+
 struct icmp6_ev {
        struct event             ev;
        uint8_t                  answer[1500];
@@ -102,7 +104,7 @@ struct ra_iface {
        int                             removed;
        int                             prefix_count;
        size_t                          datalen;
-       uint8_t                         data[1500];
+       uint8_t                         data[RA_MAX_SIZE];
 };
 
 TAILQ_HEAD(, ra_iface) ra_interfaces;
@@ -829,21 +831,22 @@ build_package(struct ra_iface *ra_iface)
        struct ra_iface_conf            *ra_iface_conf;
        struct ra_options_conf          *ra_options_conf;
        struct ra_prefix_conf           *ra_prefix_conf;
-       uint8_t                         *buf;
+       size_t                           len;
+       uint8_t                         *p, buf[RA_MAX_SIZE];
 
        ra_iface_conf = find_ra_iface_conf(&frontend_conf->ra_iface_list,
            ra_iface->name);
        ra_options_conf = &ra_iface_conf->ra_options;
 
-       ra_iface->datalen = sizeof(*ra);
-       ra_iface->datalen += sizeof(*ndopt_pi) * ra_iface->prefix_count;
+       len = sizeof(*ra);
+       len += sizeof(*ndopt_pi) * ra_iface->prefix_count;
 
-       if (ra_iface->datalen > sizeof(ra_iface->data))
+       if (len > sizeof(ra_iface->data))
                fatal("%s: packet too big", __func__); /* XXX send multiple */
 
-       buf = ra_iface->data;
+       p = buf;
 
-       ra = (struct nd_router_advert *)buf;
+       ra = (struct nd_router_advert *)p;
 
        memset(ra, 0, sizeof(*ra));
 
@@ -853,15 +856,19 @@ build_package(struct ra_iface *ra_iface)
                ra->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED;
        if (ra_options_conf->o_flag)
                ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
-       if (ra_options_conf->dfr)
+       if (ra_iface->removed)
+               /* tell clients that we are no longer a default router */
+               ra->nd_ra_router_lifetime = 0;
+       else if (ra_options_conf->dfr) {
                ra->nd_ra_router_lifetime =
                    htons(ra_options_conf->router_lifetime);
+       }
        ra->nd_ra_reachable = htonl(ra_options_conf->reachable_time);
        ra->nd_ra_retransmit = htonl(ra_options_conf->retrans_timer);
-       buf += sizeof(*ra);
+       p += sizeof(*ra);
 
        SIMPLEQ_FOREACH(ra_prefix_conf, &ra_iface->prefixes, entry) {
-               ndopt_pi = (struct nd_opt_prefix_info *)buf;
+               ndopt_pi = (struct nd_opt_prefix_info *)p;
                memset(ndopt_pi, 0, sizeof(*ndopt_pi));
                ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
                ndopt_pi->nd_opt_pi_len = 4;
@@ -877,7 +884,16 @@ build_package(struct ra_iface *ra_iface)
                    htonl(ra_prefix_conf->pltime);
                ndopt_pi->nd_opt_pi_prefix = ra_prefix_conf->prefix;
 
-               buf += sizeof(*ndopt_pi);
+               p += sizeof(*ndopt_pi);
+       }
+
+       if (len != ra_iface->datalen || memcmp(buf, ra_iface->data, len)
+           != 0) {
+               memcpy(ra_iface->data, buf, len);
+               ra_iface->datalen = len;
+               /* packet changed; tell engine to send new advertisments */
+               frontend_imsg_compose_engine(IMSG_UPDATE_IF, 0,
+                   &ra_iface->if_index, sizeof(ra_iface->if_index));
        }
 }
 
index c051a1e..445bf9c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rad.h,v 1.3 2018/07/11 14:01:44 florian Exp $ */
+/*     $OpenBSD: rad.h,v 1.4 2018/07/11 14:03:13 florian Exp $ */
 
 /*
  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
@@ -61,6 +61,7 @@ enum imsg_type {
        IMSG_STARTUP_DONE,
        IMSG_RA_RS,
        IMSG_SEND_RA,
+       IMSG_UPDATE_IF,
        IMSG_SOCKET_IPC
 };