From 0c40990e3f197c41b296268f6d8cae95549f3815 Mon Sep 17 00:00:00 2001 From: florian Date: Wed, 11 Jul 2018 14:03:13 +0000 Subject: [PATCH] Detect when a router advertisement packet changes due to config 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 | 90 +++++++++++++++++++++++++++++++++++++---- usr.sbin/rad/frontend.c | 40 ++++++++++++------ usr.sbin/rad/rad.h | 3 +- 3 files changed, 112 insertions(+), 21 deletions(-) diff --git a/usr.sbin/rad/engine.c b/usr.sbin/rad/engine.c index a5d74cd12de..25df94b0ba3 100644 --- a/usr.sbin/rad/engine.c +++ b/usr.sbin/rad/engine.c @@ -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 @@ -43,13 +43,28 @@ #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)); +} diff --git a/usr.sbin/rad/frontend.c b/usr.sbin/rad/frontend.c index 76a70b477ab..f6a7e7bee43 100644 --- a/usr.sbin/rad/frontend.c +++ b/usr.sbin/rad/frontend.c @@ -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 @@ -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)); } } diff --git a/usr.sbin/rad/rad.h b/usr.sbin/rad/rad.h index c051a1e6e64..445bf9c6f8b 100644 --- a/usr.sbin/rad/rad.h +++ b/usr.sbin/rad/rad.h @@ -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 @@ -61,6 +61,7 @@ enum imsg_type { IMSG_STARTUP_DONE, IMSG_RA_RS, IMSG_SEND_RA, + IMSG_UPDATE_IF, IMSG_SOCKET_IPC }; -- 2.20.1