summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Donald2025-11-18 01:07:58 +0000
committerÁlvaro Fernández Rojas2025-11-18 07:23:20 +0000
commit038407eca8a477da9b05ee7a9a7b6ac5fc837c8d (patch)
treee2274f547fea0309b28aee382c0459361a68334d
parentaf9968c0293fd2ca81edfc29575decb006cd73fc (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.c17
-rw-r--r--src/odhcp6c.c30
-rw-r--r--src/odhcp6c.h1
-rw-r--r--src/ra.c2
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;
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);