[PATCH] odhcp6c: add DNS RA option lifetime support

Asura Liu (asuliu) asuliu at cisco.com
Thu Aug 19 01:48:20 PDT 2021


To support RFC 8106 DNS RA option lifetime, add timers for RA DNS and RA SEARCH
options, when timer fires, expire invalid options in list.

Signed-off-by: Asura Liu <asuliu at cisco.com>
---
CMakeLists.txt |   2 +-
src/dhcpv6.c   |   2 +-
src/odhcp6c.c  | 110 +++++++++++++++++++++++++++++++++++++++++++++++--
src/odhcp6c.h  |   2 +
4 files changed, 111 insertions(+), 5 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 94f279c..38e24f8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,7 +22,7 @@ endif(${EXT_CER_ID})

set(SOURCES src/odhcp6c.c src/dhcpv6.c src/ra.c src/script.c)

-set(LIBRARIES resolv)
+set(LIBRARIES resolv rt)

if(USE_LIBUBOX)
	add_definitions(-DUSE_LIBUBOX)
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index 51b9992..16961d2 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -1350,7 +1350,7 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end)

	// Update address IA
	dhcpv6_for_each_option(&ia_hdr[1], end, otype, olen, odata) {
-		struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0,
+		struct odhcp6c_entry entry = {0, IN6ADDR_ANY_INIT, 0, 0,
				IN6ADDR_ANY_INIT, 0, 0, 0, 0, 0, 0};

		entry.iaid = ia_hdr->iaid;
diff --git a/src/odhcp6c.c b/src/odhcp6c.c
index 227aef6..58302de 100644
--- a/src/odhcp6c.c
+++ b/src/odhcp6c.c
@@ -59,6 +59,10 @@ static volatile bool signal_io = false;
static volatile bool signal_usr1 = false;
static volatile bool signal_usr2 = false;
static volatile bool signal_term = false;
+static volatile bool signal_timer = false;
+
+static struct sigaction sa;
+static struct sigevent se;

static int urandom_fd = -1, allow_slaac_only = 0;
static bool bound = false, release = true, ra = false;
@@ -163,6 +167,77 @@ static struct odhcp6c_opt opts[] = {
	{ .code = 0, .flags = 0, .str = NULL },
};

+static void timer_handler()
+{
+	syslog(LOG_DEBUG, "timer fired.\n");
+	signal_timer = true;
+}
+
+static int timer_init()
+{
+	int signo = SIGRTMIN;
+
+	sa.sa_flags = SA_SIGINFO;
+	sa.sa_sigaction = timer_handler;
+
+	syslog(LOG_DEBUG, "in timer_init.\n");
+
+	sigemptyset(&sa.sa_mask);
+	if (sigaction(signo, &sa, NULL) == -1)
+	{
+		perror("sigaction install failed");
+		return -1;
+	}
+
+	/* Set and enable alarm */
+	se.sigev_notify = SIGEV_SIGNAL;
+	se.sigev_signo = signo;
+	return 0;
+}
+
+static int timer_set(timer_t timer, int count)
+{
+	struct itimerspec its;
+	its.it_interval.tv_sec = 0;
+	its.it_interval.tv_nsec = 0;
+	its.it_value.tv_sec = count;
+	its.it_value.tv_nsec = 0;
+
+	syslog(LOG_DEBUG, "in timer_set, count = %d.\n", count);
+
+	if (timer_settime(timer, 0, &its, NULL) == -1)
+	{
+		perror("timer settime failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int timer_add(timer_t *timer, int count)
+{
+	syslog(LOG_DEBUG, "in timer_add.\n");
+
+	if (timer_create(CLOCK_REALTIME, &se, timer) == -1)
+	{
+		perror("timer create failed");
+		return -1;
+	}
+
+	return timer_set(*timer, count);
+}
+
+static int timer_del(timer_t *timer)
+{
+	int rc = 0;
+
+	syslog(LOG_DEBUG, "in timer_del.\n");
+
+	rc = timer_delete(*timer);
+	*timer = 0;
+	return rc;
+}
+
int main(_unused int argc, char* const argv[])
{
	static struct in6_addr ifid = IN6ADDR_ANY_INIT;
@@ -404,6 +479,8 @@ int main(_unused int argc, char* const argv[])
	if (help || !ifname)
		return usage();

+	timer_init();
+
	signal(SIGIO, sighandler);
	signal(SIGHUP, sighandler);
	signal(SIGINT, sighandler);
@@ -666,6 +743,16 @@ static uint8_t* odhcp6c_resize_state(enum odhcp6c_state state, ssize_t len)

bool odhcp6c_signal_process(void)
{
+	/* To apply the RFC 8106, DNS RA options could have a lower lifetime
+	 * than MaxRtrAdvInterval. So the options could be expired between
+	 * two RA messages, and need to be processed here. */
+	if (signal_timer) {
+		signal_timer = false;
+		syslog(LOG_DEBUG, "timer arrived, expire invalid options...\n");
+		odhcp6c_expire();
+		script_call("ra-updated", 0, false);
+	}
+
	while (signal_io) {
		signal_io = false;

@@ -775,6 +862,9 @@ bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new,
	struct odhcp6c_entry *x = odhcp6c_find_entry(state, new);
	uint8_t *start = odhcp6c_get_state(state, &len);

+	syslog(LOG_ERR, "odhcp6c_update_entry state %d, valid %d, preferred %d",
+		   state, new->valid, new->preferred);
+
	if (x && x->valid > new->valid && new->valid < safe)
		new->valid = safe;

@@ -793,10 +883,21 @@ bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new,
			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;
-	} else if (x)
+
+			if (state == STATE_RA_DNS || state == STATE_RA_SEARCH)
+				timer_set(x->timer, new->valid);
+		} else {
+			if (state == STATE_RA_DNS || state == STATE_RA_SEARCH)
+				timer_add(&new->timer, new->valid);
+
+			if (odhcp6c_add_state(state, new, odhcp6c_entry_size(new)))
+				return false;
+		}
+	} else if (x) {
+		if (state == STATE_RA_DNS || state == STATE_RA_SEARCH)
+			timer_del(&x->timer);
		odhcp6c_remove_state(state, ((uint8_t*)x) - start, odhcp6c_entry_size(x));
+	}

	return true;
}
@@ -831,6 +932,9 @@ static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed)
			c->valid -= elapsed;

		if (!c->valid) {
+			syslog(LOG_DEBUG, "in odhcp6c_expire_list expire state %d", state);
+			if (state == STATE_RA_DNS || state == STATE_RA_SEARCH)
+				timer_del(&c->timer);
			odhcp6c_remove_state(state, ((uint8_t*)c) - start, odhcp6c_entry_size(c));
			start = odhcp6c_get_state(state, &len);
		} else
diff --git a/src/odhcp6c.h b/src/odhcp6c.h
index 14d0017..5a3d436 100644
--- a/src/odhcp6c.h
+++ b/src/odhcp6c.h
@@ -15,6 +15,7 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
+#include <time.h>
#include <netinet/in.h>

#define _unused __attribute__((unused))
@@ -345,6 +346,7 @@ enum odhcp6c_ia_mode {


struct odhcp6c_entry {
+	timer_t timer;
	struct in6_addr router;
	uint8_t auxlen;
	uint8_t length;
--
2.27.0




More information about the openwrt-devel mailing list