diff options
-rw-r--r-- | src/dhcpv6-ia.c | 64 | ||||
-rw-r--r-- | src/dhcpv6.h | 13 |
2 files changed, 57 insertions, 20 deletions
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 847ede4..04d92fe 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -40,14 +40,16 @@ (addrs)[(i)].prefix > 64) static void free_dhcpv6_assignment(struct dhcpv6_assignment *c); -static void reconf_timer(struct uloop_timeout *event); -static struct uloop_timeout reconf_event = {.cb = reconf_timer}; +static void stop_reconf(struct dhcpv6_assignment *a); +static void valid_until_cb(struct uloop_timeout *event); + +static struct uloop_timeout valid_until_timeout = {.cb = valid_until_cb}; static uint32_t serial = 0; static uint8_t statemd5[16]; int dhcpv6_ia_init(void) { - uloop_timeout_set(&reconf_event, 2000); + uloop_timeout_set(&valid_until_timeout, 1000); return 0; } @@ -120,6 +122,7 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable) } if (a->head.next) { + a->iface = iface; if (lease->hostname[0]) { free(a->hostname); a->hostname = strdup(lease->hostname); @@ -141,6 +144,9 @@ static void free_dhcpv6_assignment(struct dhcpv6_assignment *c) if (c->head.next) list_del(&c->head); + if (c->reconf_cnt) + stop_reconf(c); + free(c->managed); free(c->hostname); free(c); @@ -170,7 +176,7 @@ static size_t get_preferred_addr(const struct odhcpd_ipaddr *addrs, const size_t return m; } -static int send_reconf(struct interface *iface, struct dhcpv6_assignment *assign) +static int send_reconf(struct dhcpv6_assignment *assign) { struct { struct dhcpv6_client_header hdr; @@ -202,6 +208,7 @@ static int send_reconf(struct interface *iface, struct dhcpv6_assignment *assign .clid_len = htons(assign->clid_len), .clid_data = {0}, }; + struct interface *iface = assign->iface; odhcpd_get_mac(iface, reconf_msg.mac); memcpy(reconf_msg.clid_data, assign->clid_data, assign->clid_len); @@ -662,6 +669,36 @@ void dhcpv6_ia_preupdate(struct interface *iface) apply_lease(iface, c, false); } +static void reconf_timeout_cb(struct uloop_timeout *event) +{ + struct dhcpv6_assignment *a = container_of(event, struct dhcpv6_assignment, reconf_timer); + + if (a->reconf_cnt > 0 && a->reconf_cnt < DHCPV6_REC_MAX_RC) { + send_reconf(a); + uloop_timeout_set(&a->reconf_timer, + DHCPV6_REC_TIMEOUT << a->reconf_cnt); + a->reconf_cnt++; + } else + stop_reconf(a); +} + +static void start_reconf(struct dhcpv6_assignment *a) +{ + uloop_timeout_set(&a->reconf_timer, + DHCPV6_REC_TIMEOUT << a->reconf_cnt); + a->reconf_timer.cb = reconf_timeout_cb; + a->reconf_cnt++; + + send_reconf(a); +} + +static void stop_reconf(struct dhcpv6_assignment *a) +{ + uloop_timeout_cancel(&a->reconf_timer); + a->reconf_cnt = 0; + a->reconf_timer.cb = NULL; +} + void dhcpv6_ia_postupdate(struct interface *iface) { if (iface->dhcpv6 != RELAYD_SERVER) @@ -697,16 +734,14 @@ void dhcpv6_ia_postupdate(struct interface *iface) apply_lease(iface, c, true); if (c->accept_reconf && c->reconf_cnt == 0) { - c->reconf_cnt = 1; - c->reconf_sent = now; - send_reconf(iface, c); + start_reconf(c); /* Leave all other assignments of that client alone */ struct dhcpv6_assignment *a; list_for_each_entry(a, &iface->ia_assignments, head) if (a != c && a->clid_len == c->clid_len && !memcmp(a->clid_data, c->clid_data, a->clid_len)) - c->reconf_cnt = INT_MAX; + a->reconf_cnt = INT_MAX; } } @@ -722,7 +757,7 @@ void dhcpv6_ia_postupdate(struct interface *iface) dhcpv6_write_statefile(); } -static void reconf_timer(struct uloop_timeout *event) +static void valid_until_cb(struct uloop_timeout *event) { time_t now = odhcpd_time(); struct interface *iface; @@ -737,15 +772,10 @@ static void reconf_timer(struct uloop_timeout *event) (a->length == 128 && a->clid_len == 0)) free_dhcpv6_assignment(a); - } else if (a->reconf_cnt > 0 && a->reconf_cnt < 8 && - now > a->reconf_sent + (1 << a->reconf_cnt)) { - ++a->reconf_cnt; - a->reconf_sent = now; - send_reconf(iface, a); } } } - uloop_timeout_set(event, 2000); + uloop_timeout_set(event, 1000); } static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, @@ -1124,8 +1154,7 @@ ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, a->clid_len = clid_len; a->iaid = ia->iaid; a->peer = *addr; - a->reconf_cnt = 0; - a->reconf_sent = 0; + stop_reconf(a); break; } } @@ -1152,6 +1181,7 @@ ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, /* Set valid time to current time indicating */ /* assignment is not having infinite lifetime */ a->valid_until = now; + a->iface = iface; if (first) memcpy(a->key, first->key, sizeof(a->key)); diff --git a/src/dhcpv6.h b/src/dhcpv6.h index e58cb69..de815cf 100644 --- a/src/dhcpv6.h +++ b/src/dhcpv6.h @@ -76,6 +76,9 @@ #define DHCPV6_HOP_COUNT_LIMIT 32 +#define DHCPV6_REC_TIMEOUT 2000 /* msec */ +#define DHCPV6_REC_MAX_RC 8 + struct dhcpv6_client_header { uint8_t msg_type; uint8_t transaction_id[3]; @@ -139,17 +142,21 @@ struct dhcpv6_ia_addr { struct dhcpv6_assignment { struct list_head head; + struct interface *iface; + struct sockaddr_in6 peer; time_t valid_until; - time_t reconf_sent; + + struct uloop_timeout reconf_timer; + bool accept_reconf; int reconf_cnt; - char *hostname; uint8_t key[16]; + + char *hostname; uint32_t assigned; uint32_t iaid; uint8_t mac[6]; uint8_t length; // length == 128 -> IA_NA, length <= 64 -> IA_PD - bool accept_reconf; struct odhcpd_ipaddr *managed; ssize_t managed_size; |