Browse Source

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 <alin.nastac@gmail.com>
Alin Nastac 2 years ago
parent
commit
c9578e1042
5 changed files with 54 additions and 37 deletions
  1. 15 2
      src/dhcpv6.c
  2. 25 30
      src/odhcp6c.c
  3. 1 2
      src/odhcp6c.h
  4. 2 2
      src/ra.c
  5. 11 1
      src/script.c

+ 15 - 2
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;

+ 25 - 30
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)

+ 1 - 2
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);

+ 2 - 2
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;
 }

+ 11 - 1
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);