From 51733a6d3bfe0fb9e8c53aea22231e5b8a1f64c3 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Thu, 31 Aug 2017 17:03:02 +0200 Subject: [PATCH] ra: align RA update interval with RFC4861 (FS#964) RFC4861 paragraph 6.2.1 states the minimum allowed time between sending unsolicited multicast Router Advertisements is 3 seconds. Align the odhcp6c implementation to this requirement as before Router Advertisement updates were accepted each 30 seconds. This lead to IPv6 connectivity issues when a network sends Router Advertisements with an interval smaller than 30 seconds as reported in FS#964. While at it rework the RA update logic making it clear the -m option is only related to RA updates. Signed-off-by: Hans Dedecker --- src/dhcpv6.c | 4 ++-- src/odhcp6c.c | 16 ++++++++-------- src/odhcp6c.h | 5 +++-- src/ra.c | 23 ++++++++++++++++------- src/ra.h | 3 ++- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 58050dc..9dce577 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -1277,7 +1277,7 @@ static int dhcpv6_parse_ia(void *opt, void *end) } if (ok) { - odhcp6c_update_entry(STATE_IA_PD, &entry, 0, false); + odhcp6c_update_entry(STATE_IA_PD, &entry, 0, 0); parsed_ia++; } @@ -1302,7 +1302,7 @@ static int dhcpv6_parse_ia(void *opt, void *end) entry.length = 128; entry.target = addr->addr; - odhcp6c_update_entry(STATE_IA_NA, &entry, 0, false); + odhcp6c_update_entry(STATE_IA_NA, &entry, 0, 0); parsed_ia++; } } diff --git a/src/odhcp6c.c b/src/odhcp6c.c index 6e83c40..666af5c 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -57,7 +57,6 @@ static bool bound = false, release = true, ra = false; static time_t last_update = 0; static char *ifname = NULL; -static unsigned int min_update_interval = DEFAULT_MIN_UPDATE_INTERVAL; static unsigned int script_sync_delay = 10; static unsigned int script_accu_delay = 1; @@ -82,6 +81,7 @@ int main(_unused int argc, char* const argv[]) int c; unsigned int client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE; unsigned int ra_options = RA_RDNSS_DEFAULT_LIFETIME; + unsigned int ra_holdoff_interval = RA_MIN_ADV_INTERVAL; while ((c = getopt(argc, argv, "S::N:V:P:FB:c:i:r:Ru:s:kt:m:Lhedp:fav")) != -1) { switch (c) { @@ -195,7 +195,7 @@ int main(_unused int argc, char* const argv[]) break; case 'm': - min_update_interval = atoi(optarg); + ra_holdoff_interval = atoi(optarg); break; case 'L': @@ -253,7 +253,7 @@ int main(_unused int argc, char* const argv[]) if ((urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY)) < 0 || init_dhcpv6(ifname, client_options, sol_timeout) || - ra_init(ifname, &ifid, ra_options) || + ra_init(ifname, &ifid, ra_options, ra_holdoff_interval) || script_init(script, ifname)) { syslog(LOG_ERR, "failed to initialize: %s", strerror(errno)); return 3; @@ -456,7 +456,7 @@ static int usage(void) " -f Don't send Client FQDN option\n" " -k Don't send a RELEASE when stopping\n" " -t Maximum timeout for DHCPv6-SOLICIT (120)\n" - " -m Minimum time between accepting updates (30)\n" + " -m Minimum time between accepting RA updates (3)\n" " -L Ignore default lifetime for RDNSS records\n" "\nInvocation options:\n" " -p Set pidfile (/var/run/odhcp6c.pid)\n" @@ -597,7 +597,7 @@ static struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const } bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, - uint32_t safe, bool filterexcess) + uint32_t safe, unsigned int holdoff_interval) { size_t len; struct odhcp6c_entry *x = odhcp6c_find_entry(state, new); @@ -608,12 +608,12 @@ bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, if (new->valid > 0) { if (x) { - if (filterexcess && new->valid >= x->valid && + if (holdoff_interval && new->valid >= x->valid && new->valid != UINT32_MAX && - new->valid - x->valid < min_update_interval && + new->valid - x->valid < holdoff_interval && new->preferred >= x->preferred && new->preferred != UINT32_MAX && - new->preferred - x->preferred < min_update_interval) + new->preferred - x->preferred < holdoff_interval) return false; x->valid = new->valid; diff --git a/src/odhcp6c.h b/src/odhcp6c.h index e55d3c1..1d9bd3e 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -32,7 +32,7 @@ #define DHCPV6_REB_MAX_RT 600 #define DHCPV6_INF_MAX_RT 120 -#define DEFAULT_MIN_UPDATE_INTERVAL 30 +#define RA_MIN_ADV_INTERVAL 3 /* RFC 4861 paragraph 6.2.1 */ enum dhcvp6_opt { DHCPV6_OPT_CLIENTID = 1, @@ -356,7 +356,8 @@ void* odhcp6c_move_state(enum odhcp6c_state state, size_t *len); void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len); // Entry manipulation -bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, uint32_t safe, bool filterexcess); +bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, + uint32_t safe, unsigned int holdoff_interval); void odhcp6c_expire(void); uint32_t odhcp6c_elapsed(void); diff --git a/src/ra.c b/src/ra.c index d6a2158..40188b6 100644 --- a/src/ra.c +++ b/src/ra.c @@ -56,6 +56,7 @@ static char if_name[IF_NAMESIZE] = {0}; static volatile int rs_attempt = 0; static struct in6_addr lladdr = IN6ADDR_ANY_INIT; static unsigned int ra_options = 0; +static unsigned int ra_holdoff_interval = 0; struct { struct icmp6_hdr hdr; @@ -67,9 +68,11 @@ struct { static void ra_send_rs(int signal __attribute__((unused))); -int ra_init(const char *ifname, const struct in6_addr *ifid, unsigned int options) +int ra_init(const char *ifname, const struct in6_addr *ifid, + unsigned int options, unsigned int holdoff_interval) { ra_options = options; + ra_holdoff_interval = holdoff_interval; const pid_t ourpid = getpid(); sock = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); @@ -357,7 +360,8 @@ bool ra_process(void) entry->valid = router_valid; entry->preferred = entry->valid; - changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, 0, true); + changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, + 0, ra_holdoff_interval); // Parse hoplimit ra_conf_hoplimit(adv->nd_ra_curhoplimit); @@ -387,7 +391,8 @@ bool ra_process(void) continue; if (entry->priority > 0) - changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, 0, true); + changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, + 0, ra_holdoff_interval); } else if (opt->type == ND_OPT_PREFIX_INFORMATION && opt->len == 4) { struct nd_opt_prefix_info *pinfo = (struct nd_opt_prefix_info*)opt; entry->router = any; @@ -404,7 +409,8 @@ bool ra_process(void) continue; if (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) - changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, 7200, true); + changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, + 7200, ra_holdoff_interval); if (!(pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) || pinfo->nd_opt_pi_prefix_len != 64) @@ -413,7 +419,8 @@ bool ra_process(void) entry->target.s6_addr32[2] = lladdr.s6_addr32[2]; entry->target.s6_addr32[3] = lladdr.s6_addr32[3]; - changed |= odhcp6c_update_entry(STATE_RA_PREFIX, entry, 7200, true); + changed |= odhcp6c_update_entry(STATE_RA_PREFIX, entry, + 7200, ra_holdoff_interval); } else if (opt->type == ND_OPT_RECURSIVE_DNS && opt->len > 2) { entry->router = from.sin6_addr; entry->priority = 0; @@ -425,7 +432,8 @@ bool ra_process(void) for (ssize_t i = 0; i < (opt->len - 1) / 2; ++i) { memcpy(&entry->target, &opt->data[6 + i * sizeof(entry->target)], sizeof(entry->target)); - changed |= odhcp6c_update_entry(STATE_RA_DNS, entry, 0, true); + changed |= odhcp6c_update_entry(STATE_RA_DNS, entry, + 0, ra_holdoff_interval); } } else if (opt->type == ND_OPT_DNSSL && opt->len > 1) { uint32_t *valid = (uint32_t*)&opt->data[2]; @@ -446,7 +454,8 @@ bool ra_process(void) if (entry->auxlen == 0) continue; - changed |= odhcp6c_update_entry(STATE_RA_SEARCH, entry, 0, true); + changed |= odhcp6c_update_entry(STATE_RA_SEARCH, entry, + 0, ra_holdoff_interval); entry->auxlen = 0; } } diff --git a/src/ra.h b/src/ra.h index 3634f3c..c95c019 100644 --- a/src/ra.h +++ b/src/ra.h @@ -34,6 +34,7 @@ struct icmpv6_opt { (void*)(opt + opt->len) <= (void*)(end); opt += opt->len) -int ra_init(const char *ifname, const struct in6_addr *ifid, unsigned int options); +int ra_init(const char *ifname, const struct in6_addr *ifid, + unsigned int options, unsigned int holdoff_interval); bool ra_link_up(void); bool ra_process(void); -- 2.25.1