dhcpv6: add support for null IA_PD valid lifetime
authorAlin Nastac <alin.nastac@gmail.com>
Wed, 10 Nov 2021 15:02:05 +0000 (16:02 +0100)
committerHans Dedecker <dedeckeh@gmail.com>
Sun, 5 Dec 2021 17:20:31 +0000 (18:20 +0100)
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 <alin.nastac@gmail.com>
src/dhcpv6.c
src/odhcp6c.c
src/odhcp6c.h
src/ra.c
src/script.c

index d9df23977067a8ff37fb3280d25177a42f33301d..8365dc4bf5343d82e360539937184b6af31758b4 100644 (file)
@@ -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;
index 808aee8ad29198adf4b4e1d23ccb6187ad2a9723..e2d4fca094a9d6fb5b0943f4a26e54facebd6a0c 100644 (file)
@@ -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)
index 6345f2f5d2702e4b5ef40f3d1455f208b248f46e..0a3be8027ee604b0bdc383a1fe7a9e418324b059 100644 (file)
@@ -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);
index 4e6dd64b2c4e119500e16b3b1a66d1522838609f..01a8b721d719f0e0e545a3ca7471c47490597969 100644 (file)
--- 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;
 }
index f32d4e2cd338a67687c5bdf2ffad703c073c15f7..bb93a53ed519927406f660659bd33c618bd6d0ea 100644 (file)
@@ -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);