From 038407eca8a477da9b05ee7a9a7b6ac5fc837c8d Mon Sep 17 00:00:00 2001 From: Paul Donald Date: Tue, 18 Nov 2025 02:07:58 +0100 Subject: [PATCH] dhcpv6: restart DHCPv6 on receipt of RA containing a new prefix MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit When the upstream DHCPv6 server does not provide IA_NA or IA_PD options, odhcp6c enters into stateless mode, which will not be exited from until SIGUSR2 signal is received. This change enforces DHCPv6 restart on receipt of a RA that: a) advertises the presence of DHCPv6 server on the network, containing either M or O flags b) has a PI (prefix information) option that contains a new prefix thus allowing the switch to DHCPv6 stateful mode when RA PI options suggest that the upstream DHCPv6 server now manages a new prefix. Restart is useful even when DHCPv6 client is already in stateful mode, so the DHCPv6 server will be able to refresh the client's IA_NA and IA_PD options before renewal timeout is triggered, hence avoiding the usage of potentially deprecated addresses. Signed-off-by: Alin Nastac Signed-off-by: Paul Donald Link: https://github.com/openwrt/odhcp6c/pull/119 Signed-off-by: Álvaro Fernández Rojas --- src/dhcpv6.c | 17 +++++++++++++---- src/odhcp6c.c | 30 ++++++++++++++++++++++++++++++ src/odhcp6c.h | 1 + src/ra.c | 2 ++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 68c04f4..49ed052 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -1661,10 +1661,19 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end, int *ret) // Update address IA dhcpv6_for_each_option(&ia_hdr[1], end, otype, olen, odata) { - struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0, - IN6ADDR_ANY_INIT, 0, 0, 0, 0, 0, 0}; - - entry.iaid = ia_hdr->iaid; + struct odhcp6c_entry entry = { + .router = IN6ADDR_ANY_INIT, + .auxlen = 0, + .length = 0, + .ra_flags = 0, + .target = IN6ADDR_ANY_INIT, + .priority = 0, + .valid = 0, + .preferred = 0, + .t1 = 0, + .t2 = 0, + .iaid = ia_hdr->iaid, + }; switch (otype) { case DHCPV6_OPT_IA_PREFIX: { diff --git a/src/odhcp6c.c b/src/odhcp6c.c index 7b1a387..43ec72f 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -834,16 +835,43 @@ static uint8_t* odhcp6c_resize_state(enum odhcp6c_state state, ssize_t len) return n; } +static bool odhcp6c_server_advertised() +{ + size_t len; + uint8_t *start = odhcp6c_get_state(STATE_RA_ROUTE, &len); + + for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start; + (uint8_t*)c < &start[len] && + (uint8_t*)odhcp6c_next_entry(c) <= &start[len]; + c = odhcp6c_next_entry(c)) { + // Only default route entries have flags + if (c->length != 0 || IN6_IS_ADDR_UNSPECIFIED(&c->router)) + continue; + + if (c->ra_flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) + return true; + } + + return false; +} + bool odhcp6c_signal_process(void) { while (signal_io) { signal_io = false; + size_t old_ra_prefix_size = state_len[STATE_RA_PREFIX]; bool ra_updated = ra_process(); if (ra_link_up()) { signal_usr2 = true; ra = false; + } else if (old_ra_prefix_size != state_len[STATE_RA_PREFIX] && + odhcp6c_server_advertised()) { + // Restart DHCPv6 transaction when router advertisement flags + // show presence of a DHCPv6 server and new prefixes were + // added to STATE_RA_PREFIX state + signal_usr2 = true; } if (ra_updated && (bound || config_dhcp->allow_slaac_only >= 0)) { @@ -953,6 +981,8 @@ bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, return false; x->valid = new->valid; + x->ra_flags = new->ra_flags; + x->priority = new->priority; x->preferred = new->preferred; x->t1 = new->t1; x->t2 = new->t2; diff --git a/src/odhcp6c.h b/src/odhcp6c.h index b229412..410da1e 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -481,6 +481,7 @@ struct odhcp6c_entry { struct in6_addr router; uint8_t auxlen; uint8_t length; + uint8_t ra_flags; struct in6_addr target; int16_t priority; uint32_t valid; diff --git a/src/ra.c b/src/ra.c index ca9e691..b1011be 100644 --- a/src/ra.c +++ b/src/ra.c @@ -430,6 +430,7 @@ bool ra_process(void) entry->target = any; entry->length = 0; entry->router = from.sin6_addr; + entry->ra_flags = adv->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER); entry->priority = pref_to_priority(adv->nd_ra_flags_reserved); if (entry->priority < 0) entry->priority = pref_to_priority(0); @@ -438,6 +439,7 @@ bool ra_process(void) entry->preferred = entry->valid; changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, ra_holdoff_interval); + entry->ra_flags = 0; // other STATE_RA_* entries don't have flags // Parse hop limit changed |= ra_set_hoplimit(adv->nd_ra_curhoplimit); -- 2.30.2