[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