diff options
| author | Paul Donald | 2025-11-18 01:07:58 +0000 |
|---|---|---|
| committer | Álvaro Fernández Rojas | 2025-11-18 07:23:20 +0000 |
| commit | 038407eca8a477da9b05ee7a9a7b6ac5fc837c8d (patch) | |
| tree | e2274f547fea0309b28aee382c0459361a68334d | |
| parent | af9968c0293fd2ca81edfc29575decb006cd73fc (diff) | |
dhcpv6: restart DHCPv6 on receipt of RA containing a new prefix
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 <alin.nastac@gmail.com>
Signed-off-by: Paul Donald <newtwen+github@gmail.com>
Link: https://github.com/openwrt/odhcp6c/pull/119
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
| -rw-r--r-- | src/dhcpv6.c | 17 | ||||
| -rw-r--r-- | src/odhcp6c.c | 30 | ||||
| -rw-r--r-- | src/odhcp6c.h | 1 | ||||
| -rw-r--r-- | 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 <limits.h> #include <linux/if_addr.h> #include <net/if.h> +#include <netinet/icmp6.h> #include <poll.h> #include <resolv.h> #include <signal.h> @@ -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; @@ -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); |