From c9578e10428d12c683ceb05e17970a616817f577 Mon Sep 17 00:00:00 2001 From: Alin Nastac Date: Wed, 10 Nov 2021 16:02:05 +0100 Subject: [PATCH 1/1] dhcpv6: add support for null IA_PD valid lifetime This allows immediate removal of the old PD assignments, triggered by DHCPv6 server messages that contain both old and new IA_PD options: - new IA_PD, with normal valid & preferred lifetimes - old IA_PD, with valid & preffered lifetimes set to 0 Signed-off-by: Alin Nastac --- src/dhcpv6.c | 17 ++++++++++++++-- src/odhcp6c.c | 55 +++++++++++++++++++++++---------------------------- src/odhcp6c.h | 3 +-- src/ra.c | 4 ++-- src/script.c | 12 ++++++++++- 5 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/dhcpv6.c b/src/dhcpv6.c index d9df239..8365dc4 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -1067,7 +1067,7 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc, unsigned int updated_IAs = 0; bool handled_status_codes[_DHCPV6_Status_Max] = { false, }; - odhcp6c_expire(); + odhcp6c_expire(true); if (orig == DHCPV6_MSG_UNKNOWN) { static time_t last_update = 0; @@ -1462,12 +1462,19 @@ static unsigned int dhcpv6_calc_refresh_timers(void) { struct odhcp6c_entry *e; size_t ia_na_entries, ia_pd_entries, i; + size_t invalid_entries = 0; int64_t l_t1 = UINT32_MAX, l_t2 = UINT32_MAX, l_t3 = 0; e = odhcp6c_get_state(STATE_IA_NA, &ia_na_entries); ia_na_entries /= sizeof(*e); for (i = 0; i < ia_na_entries; i++) { + /* Exclude invalid IA_NA entries */ + if (!e[i].valid) { + invalid_entries++; + continue; + } + if (e[i].t1 < l_t1) l_t1 = e[i].t1; @@ -1482,6 +1489,12 @@ static unsigned int dhcpv6_calc_refresh_timers(void) ia_pd_entries /= sizeof(*e); for (i = 0; i < ia_pd_entries; i++) { + /* Exclude invalid IA_PD entries */ + if (!e[i].valid) { + invalid_entries++; + continue; + } + if (e[i].t1 < l_t1) l_t1 = e[i].t1; @@ -1492,7 +1505,7 @@ static unsigned int dhcpv6_calc_refresh_timers(void) l_t3 = e[i].valid; } - if (ia_pd_entries || ia_na_entries) { + if (ia_pd_entries + ia_na_entries - invalid_entries) { t1 = l_t1; t2 = l_t2; t3 = l_t3; diff --git a/src/odhcp6c.c b/src/odhcp6c.c index 808aee8..e2d4fca 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -580,7 +580,7 @@ int main(_unused int argc, char* const argv[]) break; } - odhcp6c_expire(); + odhcp6c_expire(false); size_t ia_pd_len, ia_na_len, server_id_len; odhcp6c_get_state(STATE_IA_PD, &ia_pd_len); @@ -779,37 +779,32 @@ 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, unsigned int holdoff_interval) { - size_t len; struct odhcp6c_entry *x = odhcp6c_find_entry(state, new); - uint8_t *start = odhcp6c_get_state(state, &len); if (x && x->valid > new->valid && new->valid < safe) new->valid = safe; - if (new->valid > 0) { - if (x) { - if (holdoff_interval && new->valid >= x->valid && - new->valid != UINT32_MAX && - new->valid - x->valid < holdoff_interval && - new->preferred >= x->preferred && - new->preferred != UINT32_MAX && - new->preferred - x->preferred < holdoff_interval) - return false; - - x->valid = new->valid; - x->preferred = new->preferred; - x->t1 = new->t1; - x->t2 = new->t2; - x->iaid = new->iaid; - } else if (odhcp6c_add_state(state, new, odhcp6c_entry_size(new))) + if (x) { + if (holdoff_interval && new->valid >= x->valid && + new->valid != UINT32_MAX && + new->valid - x->valid < holdoff_interval && + new->preferred >= x->preferred && + new->preferred != UINT32_MAX && + new->preferred - x->preferred < holdoff_interval) return false; - } else if (x) - odhcp6c_remove_state(state, ((uint8_t*)x) - start, odhcp6c_entry_size(x)); + + x->valid = new->valid; + x->preferred = new->preferred; + x->t1 = new->t1; + x->t2 = new->t2; + x->iaid = new->iaid; + } else if (odhcp6c_add_state(state, new, odhcp6c_entry_size(new))) + return false; return true; } -static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed) +static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed, bool remove_expired) { size_t len; uint8_t *start = odhcp6c_get_state(state, &len); @@ -838,7 +833,7 @@ static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed) else if (c->valid != UINT32_MAX) c->valid -= elapsed; - if (!c->valid) { + if (!c->valid && remove_expired) { odhcp6c_remove_state(state, ((uint8_t*)c) - start, odhcp6c_entry_size(c)); start = odhcp6c_get_state(state, &len); } else @@ -860,19 +855,19 @@ static uint8_t *odhcp6c_state_find_opt(const uint16_t code) return NULL; } -void odhcp6c_expire(void) +void odhcp6c_expire(bool expire_ia_pd) { time_t now = odhcp6c_get_milli_time() / 1000; uint32_t elapsed = (last_update > 0) ? now - last_update : 0; last_update = now; - odhcp6c_expire_list(STATE_RA_PREFIX, elapsed); - odhcp6c_expire_list(STATE_RA_ROUTE, elapsed); - odhcp6c_expire_list(STATE_RA_DNS, elapsed); - odhcp6c_expire_list(STATE_RA_SEARCH, elapsed); - odhcp6c_expire_list(STATE_IA_NA, elapsed); - odhcp6c_expire_list(STATE_IA_PD, elapsed); + odhcp6c_expire_list(STATE_RA_PREFIX, elapsed, true); + odhcp6c_expire_list(STATE_RA_ROUTE, elapsed, true); + odhcp6c_expire_list(STATE_RA_DNS, elapsed, true); + odhcp6c_expire_list(STATE_RA_SEARCH, elapsed, true); + odhcp6c_expire_list(STATE_IA_NA, elapsed, true); + odhcp6c_expire_list(STATE_IA_PD, elapsed, expire_ia_pd); } uint32_t odhcp6c_elapsed(void) diff --git a/src/odhcp6c.h b/src/odhcp6c.h index 6345f2f..0a3be80 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -319,7 +319,6 @@ enum odhcp6c_state { _STATE_MAX }; - struct icmp6_opt { uint8_t type; uint8_t len; @@ -431,6 +430,6 @@ void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len); bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, uint32_t safe, unsigned int holdoff_interval); -void odhcp6c_expire(void); +void odhcp6c_expire(bool expire_ia_pd); uint32_t odhcp6c_elapsed(void); struct odhcp6c_opt *odhcp6c_find_opt(const uint16_t code); diff --git a/src/ra.c b/src/ra.c index 4e6dd64..01a8b72 100644 --- a/src/ra.c +++ b/src/ra.c @@ -410,7 +410,7 @@ bool ra_process(void) continue; if (!found) { - odhcp6c_expire(); + odhcp6c_expire(false); found = true; } @@ -570,7 +570,7 @@ bool ra_process(void) } if (found) - odhcp6c_expire(); + odhcp6c_expire(false); return found && changed; } diff --git a/src/script.c b/src/script.c index f32d4e2..bb93a53 100644 --- a/src/script.c +++ b/src/script.c @@ -179,6 +179,14 @@ static void entry_to_env(const char *name, const void *data, size_t len, enum en buf[buf_len++] = '='; for (size_t i = 0; i < len / sizeof(*e); ++i) { + /* + * The only invalid entries allowed to be passed to the script are prefix entries. + * This will allow immediate removal of the old ipv6-prefix-assignment that might + * otherwise be kept for up to 2 hours (see L-13 requirement of RFC 7084). + */ + if (!e[i].valid && type != ENTRY_PREFIX) + continue; + inet_ntop(AF_INET6, &e[i].target, &buf[buf_len], INET6_ADDRSTRLEN); buf_len += strlen(&buf[buf_len]); @@ -238,6 +246,8 @@ static void search_to_env(const char *name, const uint8_t *start, size_t len) (uint8_t*)e < &start[len] && (uint8_t*)odhcp6c_next_entry(e) <= &start[len]; e = odhcp6c_next_entry(e)) { + if (!e->valid) + continue; c = mempcpy(c, e->auxtarget, e->auxlen); *c++ = ' '; } @@ -425,7 +435,7 @@ void script_call(const char *status, int delay, bool resume) signal(SIGTERM, SIG_DFL); if (delay > 0) { sleep(delay); - odhcp6c_expire(); + odhcp6c_expire(false); } struct in6_addr *addr = odhcp6c_get_state(STATE_SERVER_ADDR, &addr_len); -- 2.30.2