From: florian Date: Tue, 12 Jul 2022 16:54:59 +0000 (+0000) Subject: Rewrite state machine in the style of dhcpleased(8). X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=5a030e60546510c38a8eb1913bca7311063bdaf6;p=openbsd Rewrite state machine in the style of dhcpleased(8). It is less cluttered, easier to reason about and fixes some bugs in passing that would have been difficult in the old state machine. Stale IPv6 addresses, default routes and nameservers are now correctly removed when moving from one IPv6 enabled network to another IPv6 enabled network. Default routes and nameservers correctly expire when they are not refreshed and nameservers are updated when router advertisements change the nameserver option. Testing & input caspar@ Putting it in now to get wider testing and shake out bugs, discussed with deraadt@ at r2k22. --- diff --git a/sbin/slaacd/engine.c b/sbin/slaacd/engine.c index db6d619abf1..f12a8db5385 100644 --- a/sbin/slaacd/engine.c +++ b/sbin/slaacd/engine.c @@ -1,4 +1,4 @@ -/* $OpenBSD: engine.c,v 1.80 2022/06/28 13:35:42 florian Exp $ */ +/* $OpenBSD: engine.c,v 1.81 2022/07/12 16:54:59 florian Exp $ */ /* * Copyright (c) 2017 Florian Obser @@ -100,23 +100,13 @@ enum if_state { IF_DOWN, - IF_DELAY, - IF_PROBE, - IF_IDLE, - IF_DEAD, -}; - -const char* if_state_name[] = { - "IF_DOWN", - "IF_DELAY", - "IF_PROBE", - "IF_IDLE", - "IF_DEAD", + IF_INIT, + IF_BOUND, }; enum proposal_state { + PROPOSAL_IF_DOWN, PROPOSAL_NOT_CONFIGURED, - PROPOSAL_SENT, PROPOSAL_CONFIGURED, PROPOSAL_NEARLY_EXPIRED, PROPOSAL_WITHDRAWN, @@ -124,16 +114,6 @@ enum proposal_state { PROPOSAL_STALE, }; -const char* proposal_state_name[] = { - "NOT_CONFIGURED", - "SENT", - "CONFIGURED", - "NEARLY_EXPIRED", - "WITHDRAWN", - "DUPLICATED", - "STALE", -}; - const char* rpref_name[] = { "Low", "Medium", @@ -181,12 +161,13 @@ struct address_proposal { struct event timer; int64_t id; enum proposal_state state; - time_t next_timeout; + struct timeval timo; struct timespec created; struct timespec when; struct timespec uptime; uint32_t if_index; struct ether_addr hw_address; + struct sockaddr_in6 from; struct sockaddr_in6 addr; struct in6_addr mask; struct in6_addr prefix; @@ -204,7 +185,7 @@ struct dfr_proposal { struct event timer; int64_t id; enum proposal_state state; - time_t next_timeout; + struct timeval timo; struct timespec when; struct timespec uptime; uint32_t if_index; @@ -219,7 +200,7 @@ struct rdns_proposal { struct event timer; int64_t id; enum proposal_state state; - time_t next_timeout; + struct timeval timo; struct timespec when; struct timespec uptime; uint32_t if_index; @@ -234,6 +215,8 @@ struct slaacd_iface { LIST_ENTRY(slaacd_iface) entries; enum if_state state; struct event timer; + struct timeval timo; + struct timespec last_sol; int probes; uint32_t if_index; uint32_t rdomain; @@ -264,12 +247,19 @@ void engine_showinfo_ctl(struct imsg *, uint32_t); void debug_log_ra(struct imsg_ra *); int in6_mask2prefixlen(struct in6_addr *); #endif /* SMALL */ -void deprecate_all_proposals(struct slaacd_iface *); -void send_rdns_withdraw(struct slaacd_iface *); struct slaacd_iface *get_slaacd_iface_by_id(uint32_t); void remove_slaacd_iface(uint32_t); void free_ra(struct radv *); +void iface_state_transition(struct slaacd_iface *, enum + if_state); +void addr_proposal_state_transition(struct + address_proposal *, enum proposal_state); +void dfr_proposal_state_transition(struct dfr_proposal *, + enum proposal_state); +void rdns_proposal_state_transition(struct rdns_proposal *, + enum proposal_state); void engine_update_iface(struct imsg_ifinfo *); +void request_solicitation(struct slaacd_iface *); void parse_ra(struct slaacd_iface *, struct imsg_ra *); void gen_addr(struct slaacd_iface *, struct radv_prefix *, struct address_proposal *, int); @@ -277,7 +267,6 @@ void gen_address_proposal(struct slaacd_iface *, struct radv *, struct radv_prefix *, int); void free_address_proposal(struct address_proposal *); void withdraw_addr(struct address_proposal *); -void timeout_from_lifetime(struct address_proposal *); void configure_address(struct address_proposal *); void in6_prefixlen2mask(struct in6_addr *, int len); void gen_dfr_proposal(struct slaacd_iface *, struct @@ -289,15 +278,14 @@ void update_iface_ra_rdns(struct slaacd_iface *, struct radv *); void gen_rdns_proposal(struct slaacd_iface *, struct radv *); -void propose_rdns(struct rdns_proposal *); void free_rdns_proposal(struct rdns_proposal *); +void withdraw_rdns(struct rdns_proposal *); void compose_rdns_proposal(uint32_t, int); void update_iface_ra(struct slaacd_iface *, struct radv *); void update_iface_ra_dfr(struct slaacd_iface *, struct radv *); void update_iface_ra_prefix(struct slaacd_iface *, struct radv *, struct radv_prefix *prefix); -void start_probe(struct slaacd_iface *); void address_proposal_timeout(int, short, void *); void dfr_proposal_timeout(int, short, void *); void rdns_proposal_timeout(int, short, void *); @@ -309,7 +297,7 @@ struct dfr_proposal *find_dfr_proposal_by_gw(struct slaacd_iface *, struct sockaddr_in6 *); struct rdns_proposal *find_rdns_proposal_by_gw(struct slaacd_iface *, struct sockaddr_in6 *); -struct radv_prefix *find_prefix(struct radv *, struct radv_prefix *); +struct radv_prefix *find_prefix(struct radv *, struct in6_addr *, uint8_t); int engine_imsg_compose_main(int, pid_t, void *, uint16_t); uint32_t real_lifetime(struct timespec *, uint32_t); void merge_dad_couters(struct radv *, struct radv *); @@ -318,6 +306,33 @@ static struct imsgev *iev_frontend; static struct imsgev *iev_main; int64_t proposal_id; + +#define CASE(x) case x : return #x + +static const char* +if_state_name(enum if_state ifs) +{ + switch (ifs) { + CASE(IF_DOWN); + CASE(IF_INIT); + CASE(IF_BOUND); + } +} + +static const char* +proposal_state_name(enum proposal_state ps) +{ + switch (ps) { + CASE(PROPOSAL_IF_DOWN); + CASE(PROPOSAL_NOT_CONFIGURED); + CASE(PROPOSAL_CONFIGURED); + CASE(PROPOSAL_NEARLY_EXPIRED); + CASE(PROPOSAL_WITHDRAWN); + CASE(PROPOSAL_DUPLICATED); + CASE(PROPOSAL_STALE); + } +} + void engine_sig_handler(int sig, short event, void *arg) { @@ -441,7 +456,6 @@ engine_dispatch_frontend(int fd, short event, void *bula) struct imsg_del_addr del_addr; struct imsg_del_route del_route; struct imsg_dup_addr dup_addr; - struct timeval tv; ssize_t n; int shut = 0; #ifndef SMALL @@ -499,7 +513,13 @@ engine_dispatch_frontend(int fd, short event, void *bula) __func__, IMSG_DATA_SIZE(imsg)); memcpy(&ra, imsg.data, sizeof(ra)); iface = get_slaacd_iface_by_id(ra.if_index); - if (iface != NULL) + + /* + * Ignore unsolicitated router advertisements + * if we think the interface is still down. + * Otherwise we confuse the state machine. + */ + if (iface != NULL && iface->state != IF_DOWN) parse_ra(iface, &ra); break; case IMSG_CTL_SEND_SOLICITATION: @@ -512,10 +532,10 @@ engine_dispatch_frontend(int fd, short event, void *bula) if (iface == NULL) log_warnx("requested to send solicitation on " "non-autoconf interface: %u", if_index); - else - engine_imsg_compose_frontend( - IMSG_CTL_SEND_SOLICITATION, imsg.hdr.pid, - &iface->if_index, sizeof(iface->if_index)); + else { + iface->last_sol.tv_sec = 0; /* no rate limit */ + request_solicitation(iface); + } break; case IMSG_DEL_ADDRESS: if (IMSG_DATA_SIZE(imsg) != sizeof(del_addr)) @@ -531,8 +551,14 @@ engine_dispatch_frontend(int fd, short event, void *bula) addr_proposal = find_address_proposal_by_addr(iface, &del_addr.addr); - - free_address_proposal(addr_proposal); + /* + * If it's in state PROPOSAL_WITHDRAWN we just + * deleted it ourself but want to keep it around + * so we can renew it + */ + if (addr_proposal && addr_proposal->state != + PROPOSAL_WITHDRAWN) + free_address_proposal(addr_proposal); break; case IMSG_DEL_ROUTE: if (IMSG_DATA_SIZE(imsg) != sizeof(del_route)) @@ -552,7 +578,6 @@ engine_dispatch_frontend(int fd, short event, void *bula) if (dfr_proposal) { dfr_proposal->state = PROPOSAL_WITHDRAWN; free_dfr_proposal(dfr_proposal); - start_probe(iface); } break; case IMSG_DUP_ADDRESS: @@ -570,13 +595,9 @@ engine_dispatch_frontend(int fd, short event, void *bula) addr_proposal = find_address_proposal_by_addr(iface, &dup_addr.addr); - if (addr_proposal) { - addr_proposal->state = PROPOSAL_DUPLICATED; - tv.tv_sec = 0; - tv.tv_usec = arc4random_uniform(1000000); - addr_proposal->next_timeout = 0; - evtimer_add(&addr_proposal->timer, &tv); - } + if (addr_proposal) + addr_proposal_state_transition(addr_proposal, + PROPOSAL_DUPLICATED); break; case IMSG_REPROPOSE_RDNS: LIST_FOREACH (iface, &slaacd_interfaces, entries) @@ -608,10 +629,6 @@ engine_dispatch_main(int fd, short event, void *bula) struct imsg_ifinfo imsg_ifinfo; ssize_t n; int shut = 0; - struct slaacd_iface *iface; - struct imsg_addrinfo imsg_addrinfo; - struct address_proposal *addr_proposal = NULL; - size_t i; if (event & EV_READ) { if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) @@ -669,65 +686,6 @@ engine_dispatch_main(int fd, short event, void *bula) memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo)); engine_update_iface(&imsg_ifinfo); break; -#ifndef SMALL - case IMSG_UPDATE_ADDRESS: - if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_addrinfo)) - fatalx("%s: IMSG_UPDATE_ADDRESS wrong length: " - "%lu", __func__, IMSG_DATA_SIZE(imsg)); - - memcpy(&imsg_addrinfo, imsg.data, - sizeof(imsg_addrinfo)); - - iface = get_slaacd_iface_by_id(imsg_addrinfo.if_index); - if (iface == NULL) - break; - - log_debug("%s: IMSG_UPDATE_ADDRESS", __func__); - - addr_proposal = find_address_proposal_by_addr(iface, - &imsg_addrinfo.addr); - if (addr_proposal) - break; - - if ((addr_proposal = calloc(1, - sizeof(*addr_proposal))) == NULL) - fatal("calloc"); - evtimer_set(&addr_proposal->timer, - address_proposal_timeout, addr_proposal); - addr_proposal->id = ++proposal_id; - addr_proposal->state = PROPOSAL_CONFIGURED; - addr_proposal->vltime = imsg_addrinfo.vltime; - addr_proposal->pltime = imsg_addrinfo.pltime; - - timeout_from_lifetime(addr_proposal); - - /* leave created 0, we don't know when it was created */ - if (clock_gettime(CLOCK_REALTIME, &addr_proposal->when)) - fatal("clock_gettime"); - if (clock_gettime(CLOCK_MONOTONIC, - &addr_proposal->uptime)) - fatal("clock_gettime"); - addr_proposal->if_index = imsg_addrinfo.if_index; - memcpy(&addr_proposal->hw_address, &iface->hw_address, - sizeof(addr_proposal->hw_address)); - addr_proposal->addr = imsg_addrinfo.addr; - addr_proposal->mask = imsg_addrinfo.mask; - addr_proposal->prefix = addr_proposal->addr.sin6_addr; - - for (i = 0; i < sizeof(addr_proposal->prefix.s6_addr) / - sizeof(addr_proposal->prefix.s6_addr[0]); i++) - addr_proposal->prefix.s6_addr[i] &= - addr_proposal->mask.s6_addr[i]; - - addr_proposal->temporary = imsg_addrinfo.temporary; - addr_proposal->prefix_len = - in6_mask2prefixlen(&addr_proposal->mask); - - LIST_INSERT_HEAD(&iface->addr_proposals, - addr_proposal, entries); - - break; -#endif /* SMALL */ default: log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type); @@ -824,11 +782,11 @@ send_interface_info(struct slaacd_iface *iface, pid_t pid) memset(&cei_addr_proposal, 0, sizeof(cei_addr_proposal)); cei_addr_proposal.id = addr_proposal->id; if(strlcpy(cei_addr_proposal.state, - proposal_state_name[addr_proposal->state], + proposal_state_name(addr_proposal->state), sizeof(cei_addr_proposal.state)) >= sizeof(cei_addr_proposal.state)) log_warnx("truncated state name"); - cei_addr_proposal.next_timeout = addr_proposal->next_timeout; + cei_addr_proposal.next_timeout = addr_proposal->timo.tv_sec; cei_addr_proposal.when = addr_proposal->when; cei_addr_proposal.uptime = addr_proposal->uptime; memcpy(&cei_addr_proposal.addr, &addr_proposal->addr, sizeof( @@ -853,11 +811,11 @@ send_interface_info(struct slaacd_iface *iface, pid_t pid) memset(&cei_dfr_proposal, 0, sizeof(cei_dfr_proposal)); cei_dfr_proposal.id = dfr_proposal->id; if(strlcpy(cei_dfr_proposal.state, - proposal_state_name[dfr_proposal->state], + proposal_state_name(dfr_proposal->state), sizeof(cei_dfr_proposal.state)) >= sizeof(cei_dfr_proposal.state)) log_warnx("truncated state name"); - cei_dfr_proposal.next_timeout = dfr_proposal->next_timeout; + cei_dfr_proposal.next_timeout = dfr_proposal->timo.tv_sec; cei_dfr_proposal.when = dfr_proposal->when; cei_dfr_proposal.uptime = dfr_proposal->uptime; memcpy(&cei_dfr_proposal.addr, &dfr_proposal->addr, sizeof( @@ -882,11 +840,11 @@ send_interface_info(struct slaacd_iface *iface, pid_t pid) memset(&cei_rdns_proposal, 0, sizeof(cei_rdns_proposal)); cei_rdns_proposal.id = rdns_proposal->id; if(strlcpy(cei_rdns_proposal.state, - proposal_state_name[rdns_proposal->state], + proposal_state_name(rdns_proposal->state), sizeof(cei_rdns_proposal.state)) >= sizeof(cei_rdns_proposal.state)) log_warnx("truncated state name"); - cei_rdns_proposal.next_timeout = rdns_proposal->next_timeout; + cei_rdns_proposal.next_timeout = rdns_proposal->timo.tv_sec; cei_rdns_proposal.when = rdns_proposal->when; cei_rdns_proposal.uptime = rdns_proposal->uptime; memcpy(&cei_rdns_proposal.from, &rdns_proposal->from, sizeof( @@ -927,32 +885,6 @@ engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index) #endif /* SMALL */ -void -deprecate_all_proposals(struct slaacd_iface *iface) -{ - struct address_proposal *addr_proposal; - - log_debug("%s: iface: %d", __func__, iface->if_index); - - LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries) { - addr_proposal->pltime = 0; - configure_address(addr_proposal); - addr_proposal->state = PROPOSAL_NEARLY_EXPIRED; - } -} - -void -send_rdns_withdraw(struct slaacd_iface *iface) -{ - struct rdns_proposal *rdns_proposal; - - while(!LIST_EMPTY(&iface->rdns_proposals)) { - rdns_proposal = LIST_FIRST(&iface->rdns_proposals); - free_rdns_proposal(rdns_proposal); - } - compose_rdns_proposal(iface->if_index, iface->rdomain); -} - struct slaacd_iface* get_slaacd_iface_by_id(uint32_t if_index) { @@ -1028,6 +960,326 @@ free_ra(struct radv *ra) free(ra); } +void +iface_state_transition(struct slaacd_iface *iface, enum if_state new_state) +{ + enum if_state old_state = iface->state; + struct address_proposal *addr_proposal; + struct dfr_proposal *dfr_proposal; + struct rdns_proposal *rdns_proposal; + char ifnamebuf[IF_NAMESIZE], *if_name; + + iface->state = new_state; + + switch (new_state) { + case IF_DOWN: + if (old_state != IF_DOWN) { + LIST_FOREACH (addr_proposal, &iface->addr_proposals, + entries) + addr_proposal_state_transition(addr_proposal, + PROPOSAL_IF_DOWN); + LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, + entries) + dfr_proposal_state_transition(dfr_proposal, + PROPOSAL_IF_DOWN); + LIST_FOREACH (rdns_proposal, &iface->rdns_proposals, + entries) + rdns_proposal_state_transition(rdns_proposal, + PROPOSAL_IF_DOWN); + } + + /* nothing else to do until interface comes back up */ + iface->timo.tv_sec = -1; + break; + case IF_INIT: + switch (old_state) { + case IF_INIT: + iface->probes++; + break; + case IF_DOWN: + LIST_FOREACH (addr_proposal, &iface->addr_proposals, + entries) + addr_proposal_state_transition(addr_proposal, + PROPOSAL_WITHDRAWN); + LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, + entries) + dfr_proposal_state_transition(dfr_proposal, + PROPOSAL_WITHDRAWN); + LIST_FOREACH (rdns_proposal, &iface->rdns_proposals, + entries) + rdns_proposal_state_transition(rdns_proposal, + PROPOSAL_WITHDRAWN); + default: + iface->probes = 0; + } + if (iface->probes < MAX_RTR_SOLICITATIONS) { + iface->timo.tv_sec = RTR_SOLICITATION_INTERVAL; + request_solicitation(iface); + } else + /* no router available, stop probing */ + iface->timo.tv_sec = -1; + break; + case IF_BOUND: + iface->timo.tv_sec = -1; + break; + } + + if_name = if_indextoname(iface->if_index, ifnamebuf); + log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ? + "?" : if_name, if_state_name(old_state), if_state_name(new_state), + iface->timo.tv_sec); + + if (iface->timo.tv_sec == -1) { + if (evtimer_pending(&iface->timer, NULL)) + evtimer_del(&iface->timer); + } else + evtimer_add(&iface->timer, &iface->timo); +} + +void addr_proposal_state_transition(struct address_proposal *addr_proposal, + enum proposal_state new_state) +{ + enum proposal_state old_state = addr_proposal->state; + struct slaacd_iface *iface; + uint32_t lifetime; + char ifnamebuf[IF_NAMESIZE], *if_name; + + addr_proposal->state = new_state; + + if ((iface = get_slaacd_iface_by_id(addr_proposal->if_index)) == NULL) + return; + + switch (addr_proposal->state) { + case PROPOSAL_IF_DOWN: + if (old_state == PROPOSAL_IF_DOWN) { + withdraw_addr(addr_proposal); + addr_proposal->timo.tv_sec = -1; + } else { + addr_proposal->timo.tv_sec = + real_lifetime(&addr_proposal->uptime, + addr_proposal->vltime); + } + break; + case PROPOSAL_NOT_CONFIGURED: + break; + case PROPOSAL_CONFIGURED: + lifetime = real_lifetime(&addr_proposal->uptime, + addr_proposal->pltime); + if (lifetime == 0) + lifetime = real_lifetime(&addr_proposal->uptime, + addr_proposal->vltime); + if (lifetime > MAX_RTR_SOLICITATIONS * + (RTR_SOLICITATION_INTERVAL + 1)) + addr_proposal->timo.tv_sec = lifetime - + MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; + else + addr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; + break; + case PROPOSAL_NEARLY_EXPIRED: + lifetime = real_lifetime(&addr_proposal->uptime, + addr_proposal->pltime); + if (lifetime == 0) + lifetime = real_lifetime(&addr_proposal->uptime, + addr_proposal->vltime); + if (lifetime > MAX_RTR_SOLICITATIONS * + (RTR_SOLICITATION_INTERVAL + 1)) + addr_proposal->timo.tv_sec = lifetime - + MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; + else + addr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; + request_solicitation(iface); + break; + case PROPOSAL_WITHDRAWN: + withdraw_addr(addr_proposal); + addr_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS * + RTR_SOLICITATION_INTERVAL; + break; + case PROPOSAL_DUPLICATED: + addr_proposal->timo.tv_sec = 0; + break; + case PROPOSAL_STALE: + addr_proposal->timo.tv_sec = 0; /* remove immediately */ + break; + } + + if_name = if_indextoname(addr_proposal->if_index, ifnamebuf); + log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ? + "?" : if_name, proposal_state_name(old_state), + proposal_state_name(new_state), + addr_proposal->timo.tv_sec); + + if (addr_proposal->timo.tv_sec == -1) { + if (evtimer_pending(&addr_proposal->timer, NULL)) + evtimer_del(&addr_proposal->timer); + } else + evtimer_add(&addr_proposal->timer, &addr_proposal->timo); +} + +void dfr_proposal_state_transition(struct dfr_proposal *dfr_proposal, + enum proposal_state new_state) +{ + enum proposal_state old_state = dfr_proposal->state; + struct slaacd_iface *iface; + uint32_t lifetime; + char ifnamebuf[IF_NAMESIZE], *if_name; + + dfr_proposal->state = new_state; + + if ((iface = get_slaacd_iface_by_id(dfr_proposal->if_index)) == NULL) + return; + + switch (dfr_proposal->state) { + case PROPOSAL_IF_DOWN: + if (old_state == PROPOSAL_IF_DOWN) { + withdraw_dfr(dfr_proposal); + dfr_proposal->timo.tv_sec = -1; + } else { + dfr_proposal->timo.tv_sec = + real_lifetime(&dfr_proposal->uptime, + dfr_proposal->router_lifetime); + } + break; + case PROPOSAL_NOT_CONFIGURED: + break; + case PROPOSAL_CONFIGURED: + lifetime = real_lifetime(&dfr_proposal->uptime, + dfr_proposal->router_lifetime); + if (lifetime > MAX_RTR_SOLICITATIONS * + (RTR_SOLICITATION_INTERVAL + 1)) + dfr_proposal->timo.tv_sec = lifetime - + MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; + else + dfr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; + break; + case PROPOSAL_NEARLY_EXPIRED: + lifetime = real_lifetime(&dfr_proposal->uptime, + dfr_proposal->router_lifetime); + if (lifetime > MAX_RTR_SOLICITATIONS * + (RTR_SOLICITATION_INTERVAL + 1)) + dfr_proposal->timo.tv_sec = lifetime - + MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; + else + dfr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; + request_solicitation(iface); + break; + case PROPOSAL_WITHDRAWN: + withdraw_dfr(dfr_proposal); + dfr_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS * + RTR_SOLICITATION_INTERVAL; + break; + case PROPOSAL_STALE: + dfr_proposal->timo.tv_sec = 0; /* remove immediately */ + break; + case PROPOSAL_DUPLICATED: + fatalx("invalid dfr state: PROPOSAL_DUPLICATED"); + break; + } + + if_name = if_indextoname(dfr_proposal->if_index, ifnamebuf); + log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ? + "?" : if_name, proposal_state_name(old_state), + proposal_state_name(new_state), + dfr_proposal->timo.tv_sec); + + if (dfr_proposal->timo.tv_sec == -1) { + if (evtimer_pending(&dfr_proposal->timer, NULL)) + evtimer_del(&dfr_proposal->timer); + } else + evtimer_add(&dfr_proposal->timer, &dfr_proposal->timo); + +} + +void rdns_proposal_state_transition(struct rdns_proposal *rdns_proposal, + enum proposal_state new_state) +{ + enum proposal_state old_state = rdns_proposal->state; + struct slaacd_iface *iface; + uint32_t lifetime; + char ifnamebuf[IF_NAMESIZE], *if_name; + + rdns_proposal->state = new_state; + + if ((iface = get_slaacd_iface_by_id(rdns_proposal->if_index)) == NULL) + return; + + switch (rdns_proposal->state) { + case PROPOSAL_IF_DOWN: + if (old_state == PROPOSAL_IF_DOWN) { + withdraw_rdns(rdns_proposal); + rdns_proposal->timo.tv_sec = -1; + } else { + rdns_proposal->timo.tv_sec = + real_lifetime(&rdns_proposal->uptime, + rdns_proposal->rdns_lifetime); + } + break; + case PROPOSAL_NOT_CONFIGURED: + break; + case PROPOSAL_CONFIGURED: + lifetime = real_lifetime(&rdns_proposal->uptime, + rdns_proposal->rdns_lifetime); + if (lifetime > MAX_RTR_SOLICITATIONS * + (RTR_SOLICITATION_INTERVAL + 1)) + rdns_proposal->timo.tv_sec = lifetime - + MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; + else + rdns_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; + break; + case PROPOSAL_NEARLY_EXPIRED: + lifetime = real_lifetime(&rdns_proposal->uptime, + rdns_proposal->rdns_lifetime); + if (lifetime > MAX_RTR_SOLICITATIONS * + (RTR_SOLICITATION_INTERVAL + 1)) + rdns_proposal->timo.tv_sec = lifetime - + MAX_RTR_SOLICITATIONS * RTR_SOLICITATION_INTERVAL; + else + rdns_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL; + request_solicitation(iface); + break; + case PROPOSAL_WITHDRAWN: + withdraw_rdns(rdns_proposal); + rdns_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS * + RTR_SOLICITATION_INTERVAL; + break; + case PROPOSAL_STALE: + rdns_proposal->timo.tv_sec = 0; /* remove immediately */ + break; + case PROPOSAL_DUPLICATED: + fatalx("invalid rdns state: PROPOSAL_DUPLICATED"); + break; + } + + if_name = if_indextoname(rdns_proposal->if_index, ifnamebuf); + log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ? + "?" : if_name, proposal_state_name(old_state), + proposal_state_name(new_state), + rdns_proposal->timo.tv_sec); + + if (rdns_proposal->timo.tv_sec == -1) { + if (evtimer_pending(&rdns_proposal->timer, NULL)) + evtimer_del(&rdns_proposal->timer); + } else + evtimer_add(&rdns_proposal->timer, &rdns_proposal->timo); +} + +void +request_solicitation(struct slaacd_iface *iface) +{ + struct timespec now, diff, sol_delay = {RTR_SOLICITATION_INTERVAL, 0}; + + clock_gettime(CLOCK_MONOTONIC, &now); + timespecsub(&now, &iface->last_sol, &diff); + if (timespeccmp(&diff, &sol_delay, <)) { + log_warnx("last solicitation less then %d seconds ago", + RTR_SOLICITATION_INTERVAL); + return; + } + + iface->last_sol = now; + engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, 0, + &iface->if_index, sizeof(iface->if_index)); +} + void engine_update_iface(struct imsg_ifinfo *imsg_ifinfo) { @@ -1039,6 +1291,7 @@ engine_update_iface(struct imsg_ifinfo *imsg_ifinfo) if ((iface = calloc(1, sizeof(*iface))) == NULL) fatal("calloc"); iface->state = IF_DOWN; + iface->timo.tv_usec = arc4random_uniform(1000000); evtimer_set(&iface->timer, iface_timeout, iface); iface->if_index = imsg_ifinfo->if_index; iface->rdomain = imsg_ifinfo->rdomain; @@ -1106,16 +1359,10 @@ engine_update_iface(struct imsg_ifinfo *imsg_ifinfo) return; if (iface->running && LINK_STATE_IS_UP(iface->link_state)) - start_probe(iface); + iface_state_transition(iface, IF_INIT); - else { - /* XXX correct state transition */ - send_rdns_withdraw(iface); - deprecate_all_proposals(iface); - iface->state = IF_DOWN; - if (evtimer_pending(&iface->timer, NULL)) - evtimer_del(&iface->timer); - } + else + iface_state_transition(iface, IF_DOWN); } void @@ -1318,7 +1565,6 @@ parse_ra(struct slaacd_iface *iface, struct imsg_ra *ra) p += nd_opt_hdr->nd_opt_len * 8 - 2; } update_iface_ra(iface, radv); - iface->state = IF_IDLE; return; err: @@ -1646,25 +1892,24 @@ update_iface_ra_dfr(struct slaacd_iface *iface, struct radv *ra) return; } - if (real_lifetime(&dfr_proposal->uptime, dfr_proposal->router_lifetime) - > ra->router_lifetime) { - log_warnx("ignoring router advertisement lowering router " - "lifetime"); - return; - } - dfr_proposal->when = ra->when; dfr_proposal->uptime = ra->uptime; dfr_proposal->router_lifetime = ra->router_lifetime; log_debug("%s, dfr state: %s, rl: %d", __func__, - proposal_state_name[dfr_proposal->state], + proposal_state_name(dfr_proposal->state), real_lifetime(&dfr_proposal->uptime, dfr_proposal->router_lifetime)); switch (dfr_proposal->state) { case PROPOSAL_CONFIGURED: case PROPOSAL_NEARLY_EXPIRED: + /* routes do not expire in the kernel, update timeout */ + dfr_proposal_state_transition(dfr_proposal, + PROPOSAL_CONFIGURED); + break; + case PROPOSAL_IF_DOWN: + case PROPOSAL_WITHDRAWN: log_debug("updating dfr"); configure_dfr(dfr_proposal); break; @@ -1744,6 +1989,7 @@ update_iface_ra_prefix(struct slaacd_iface *iface, struct radv *ra, found = 1; } + addr_proposal->from = ra->from; addr_proposal->when = ra->when; addr_proposal->uptime = ra->uptime; @@ -1758,11 +2004,13 @@ update_iface_ra_prefix(struct slaacd_iface *iface, struct radv *ra, } log_debug("%s, addr state: %s", __func__, - proposal_state_name[addr_proposal->state]); + proposal_state_name(addr_proposal->state)); switch (addr_proposal->state) { case PROPOSAL_CONFIGURED: case PROPOSAL_NEARLY_EXPIRED: + case PROPOSAL_IF_DOWN: + case PROPOSAL_WITHDRAWN: log_debug("updating address"); configure_address(addr_proposal); break; @@ -1800,37 +2048,63 @@ void update_iface_ra_rdns(struct slaacd_iface *iface, struct radv *ra) { struct rdns_proposal *rdns_proposal; + struct radv_rdns *radv_rdns; + struct in6_addr rdns[MAX_RDNS_COUNT]; + int rdns_count; rdns_proposal = find_rdns_proposal_by_gw(iface, &ra->from); if (!rdns_proposal) { /* new proposal */ - gen_rdns_proposal(iface, ra); + if (!LIST_EMPTY(&ra->rdns_servers)) + gen_rdns_proposal(iface, ra); return; } - if (real_lifetime(&rdns_proposal->uptime, rdns_proposal->rdns_lifetime) - > ra->rdns_lifetime) { - /* XXX check RFC */ - log_warnx("ignoring router advertisement lowering rdns " - "lifetime"); + rdns_count = 0; + memset(&rdns, 0, sizeof(rdns)); + LIST_FOREACH(radv_rdns, &ra->rdns_servers, entries) { + memcpy(&rdns[rdns_count++], + &radv_rdns->rdns, sizeof(struct in6_addr)); + if (rdns_proposal->rdns_count == MAX_RDNS_COUNT) + break; + } + + if (rdns_count == 0) { + free_rdns_proposal(rdns_proposal); return; } + if (rdns_proposal->rdns_count != rdns_count || + memcmp(&rdns_proposal->rdns, &rdns, sizeof(rdns)) != 0) { + memcpy(&rdns_proposal->rdns, &rdns, sizeof(rdns)); + rdns_proposal->rdns_count = rdns_count; + rdns_proposal->state = PROPOSAL_NOT_CONFIGURED; + } rdns_proposal->when = ra->when; rdns_proposal->uptime = ra->uptime; rdns_proposal->rdns_lifetime = ra->rdns_lifetime; log_debug("%s, rdns state: %s, rl: %d", __func__, - proposal_state_name[rdns_proposal->state], + proposal_state_name(rdns_proposal->state), real_lifetime(&rdns_proposal->uptime, rdns_proposal->rdns_lifetime)); switch (rdns_proposal->state) { - case PROPOSAL_SENT: + case PROPOSAL_CONFIGURED: case PROPOSAL_NEARLY_EXPIRED: + /* rdns are not expired by the kernel, update timeout */ + rdns_proposal_state_transition(rdns_proposal, + PROPOSAL_CONFIGURED); + break; + case PROPOSAL_IF_DOWN: + case PROPOSAL_WITHDRAWN: + case PROPOSAL_NOT_CONFIGURED: log_debug("updating rdns"); - propose_rdns(rdns_proposal); + rdns_proposal_state_transition(rdns_proposal, + PROPOSAL_CONFIGURED); + compose_rdns_proposal(rdns_proposal->if_index, + rdns_proposal->rdomain); break; default: log_debug("%s: iface %d: %s", __func__, iface->if_index, @@ -1839,39 +2113,12 @@ update_iface_ra_rdns(struct slaacd_iface *iface, struct radv *ra) } } -void -timeout_from_lifetime(struct address_proposal *addr_proposal) -{ - struct timeval tv; - time_t lifetime; - - addr_proposal->next_timeout = 0; - - if (addr_proposal->pltime > MAX_RTR_SOLICITATIONS * - (RTR_SOLICITATION_INTERVAL + 1)) - lifetime = addr_proposal->pltime; - else - lifetime = addr_proposal->vltime; - - if (lifetime > MAX_RTR_SOLICITATIONS * - (RTR_SOLICITATION_INTERVAL + 1)) { - addr_proposal->next_timeout = lifetime - MAX_RTR_SOLICITATIONS * - (RTR_SOLICITATION_INTERVAL + 1); - tv.tv_sec = addr_proposal->next_timeout; - tv.tv_usec = arc4random_uniform(1000000); - evtimer_add(&addr_proposal->timer, &tv); - log_debug("%s: %d, scheduling new timeout in %llds.%06ld", - __func__, addr_proposal->if_index, tv.tv_sec, tv.tv_usec); - } -} void configure_address(struct address_proposal *addr_proposal) { struct imsg_configure_address address; - - timeout_from_lifetime(addr_proposal); - addr_proposal->state = PROPOSAL_CONFIGURED; + struct slaacd_iface *iface; log_debug("%s: %d", __func__, addr_proposal->if_index); @@ -1885,6 +2132,10 @@ configure_address(struct address_proposal *addr_proposal) engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address, sizeof(address)); + + if ((iface = get_slaacd_iface_by_id(addr_proposal->if_index)) != NULL) + iface_state_transition(iface, IF_BOUND); + addr_proposal_state_transition(addr_proposal, PROPOSAL_CONFIGURED); } void @@ -1899,13 +2150,16 @@ gen_address_proposal(struct slaacd_iface *iface, struct radv *ra, struct addr_proposal->id = ++proposal_id; evtimer_set(&addr_proposal->timer, address_proposal_timeout, addr_proposal); - addr_proposal->next_timeout = 1; + addr_proposal->timo.tv_sec = 1; + addr_proposal->timo.tv_usec = arc4random_uniform(1000000); addr_proposal->state = PROPOSAL_NOT_CONFIGURED; if (clock_gettime(CLOCK_MONOTONIC, &addr_proposal->created)) fatal("clock_gettime"); addr_proposal->when = ra->when; addr_proposal->uptime = ra->uptime; addr_proposal->if_index = iface->if_index; + memcpy(&addr_proposal->from, &ra->from, + sizeof(addr_proposal->from)); memcpy(&addr_proposal->hw_address, &iface->hw_address, sizeof(addr_proposal->hw_address)); memcpy(&addr_proposal->soiikey, &iface->soiikey, @@ -1987,7 +2241,8 @@ gen_dfr_proposal(struct slaacd_iface *iface, struct radv *ra) dfr_proposal->id = ++proposal_id; evtimer_set(&dfr_proposal->timer, dfr_proposal_timeout, dfr_proposal); - dfr_proposal->next_timeout = 1; + dfr_proposal->timo.tv_sec = 1; + dfr_proposal->timo.tv_usec = arc4random_uniform(1000000); dfr_proposal->state = PROPOSAL_NOT_CONFIGURED; dfr_proposal->when = ra->when; dfr_proposal->uptime = ra->uptime; @@ -2009,39 +2264,17 @@ void configure_dfr(struct dfr_proposal *dfr_proposal) { struct imsg_configure_dfr dfr; - struct timeval tv; - enum proposal_state prev_state; - - if (dfr_proposal->router_lifetime > MAX_RTR_SOLICITATIONS * - (RTR_SOLICITATION_INTERVAL + 1)) { - dfr_proposal->next_timeout = dfr_proposal->router_lifetime - - MAX_RTR_SOLICITATIONS * (RTR_SOLICITATION_INTERVAL + 1); - tv.tv_sec = dfr_proposal->next_timeout; - tv.tv_usec = arc4random_uniform(1000000); - evtimer_add(&dfr_proposal->timer, &tv); - log_debug("%s: %d, scheduling new timeout in %llds.%06ld", - __func__, dfr_proposal->if_index, tv.tv_sec, tv.tv_usec); - } else - dfr_proposal->next_timeout = 0; - - prev_state = dfr_proposal->state; - - dfr_proposal->state = PROPOSAL_CONFIGURED; log_debug("%s: %d", __func__, dfr_proposal->if_index); - if (prev_state == PROPOSAL_CONFIGURED || prev_state == - PROPOSAL_NEARLY_EXPIRED) { - /* nothing to do here, routes do not expire in the kernel */ - return; - } - dfr.if_index = dfr_proposal->if_index; dfr.rdomain = dfr_proposal->rdomain; memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr)); dfr.router_lifetime = dfr_proposal->router_lifetime; engine_imsg_compose_main(IMSG_CONFIGURE_DFR, 0, &dfr, sizeof(dfr)); + + dfr_proposal_state_transition(dfr_proposal, PROPOSAL_CONFIGURED); } void @@ -2091,7 +2324,8 @@ gen_rdns_proposal(struct slaacd_iface *iface, struct radv *ra) rdns_proposal->id = ++proposal_id; evtimer_set(&rdns_proposal->timer, rdns_proposal_timeout, rdns_proposal); - rdns_proposal->next_timeout = 1; + rdns_proposal->timo.tv_sec = 1; + rdns_proposal->timo.tv_usec = arc4random_uniform(1000000); rdns_proposal->state = PROPOSAL_NOT_CONFIGURED; rdns_proposal->when = ra->when; rdns_proposal->uptime = ra->uptime; @@ -2108,44 +2342,12 @@ gen_rdns_proposal(struct slaacd_iface *iface, struct radv *ra) } LIST_INSERT_HEAD(&iface->rdns_proposals, rdns_proposal, entries); - propose_rdns(rdns_proposal); + compose_rdns_proposal(iface->if_index, iface->rdomain); hbuf = sin6_to_str(&rdns_proposal->from); log_debug("%s: iface %d: %s", __func__, iface->if_index, hbuf); } -void -propose_rdns(struct rdns_proposal *rdns_proposal) -{ - struct timeval tv; - enum proposal_state prev_state; - - if (rdns_proposal->rdns_lifetime > MAX_RTR_SOLICITATIONS * - (RTR_SOLICITATION_INTERVAL + 1)) { - rdns_proposal->next_timeout = rdns_proposal->rdns_lifetime - - MAX_RTR_SOLICITATIONS * (RTR_SOLICITATION_INTERVAL + 1); - tv.tv_sec = rdns_proposal->next_timeout; - tv.tv_usec = arc4random_uniform(1000000); - evtimer_add(&rdns_proposal->timer, &tv); - log_debug("%s: %d, scheduling new timeout in %llds.%06ld", - __func__, rdns_proposal->if_index, tv.tv_sec, tv.tv_usec); - } else - rdns_proposal->next_timeout = 0; - - prev_state = rdns_proposal->state; - - rdns_proposal->state = PROPOSAL_SENT; - - log_debug("%s: %d", __func__, rdns_proposal->if_index); - - if (prev_state == PROPOSAL_SENT || prev_state == - PROPOSAL_NEARLY_EXPIRED) { - /* nothing to do here rDNS proposals do not expire */ - return; - } - compose_rdns_proposal(rdns_proposal->if_index, rdns_proposal->rdomain); -} - void compose_rdns_proposal(uint32_t if_index, int rdomain) { @@ -2160,6 +2362,11 @@ compose_rdns_proposal(uint32_t if_index, int rdomain) if ((iface = get_slaacd_iface_by_id(if_index)) != NULL) { LIST_FOREACH(rdns_proposal, &iface->rdns_proposals, entries) { + if (rdns_proposal->state == PROPOSAL_WITHDRAWN || + rdns_proposal->state == PROPOSAL_STALE) + continue; + rdns_proposal_state_transition(rdns_proposal, + PROPOSAL_CONFIGURED); for (i = 0; i < rdns_proposal->rdns_count && rdns.rdns_count < MAX_RDNS_COUNT; i++) { rdns.rdns[rdns.rdns_count++] = @@ -2179,31 +2386,36 @@ free_rdns_proposal(struct rdns_proposal *rdns_proposal) LIST_REMOVE(rdns_proposal, entries); evtimer_del(&rdns_proposal->timer); + switch (rdns_proposal->state) { + case PROPOSAL_CONFIGURED: + case PROPOSAL_NEARLY_EXPIRED: + case PROPOSAL_STALE: + withdraw_rdns(rdns_proposal); + break; + default: + break; + } free(rdns_proposal); } void -start_probe(struct slaacd_iface *iface) +withdraw_rdns(struct rdns_proposal *rdns_proposal) { - struct timeval tv; - - iface->state = IF_DELAY; - iface->probes = 0; - - tv.tv_sec = 0; - tv.tv_usec = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY_USEC); + log_debug("%s: %d", __func__, rdns_proposal->if_index); - log_debug("%s: iface %d: sleeping for %ldusec", __func__, - iface->if_index, tv.tv_usec); + rdns_proposal->state = PROPOSAL_WITHDRAWN; - evtimer_add(&iface->timer, &tv); + /* we have to re-propose all rdns servers, minus one */ + compose_rdns_proposal(rdns_proposal->if_index, rdns_proposal->rdomain); } void address_proposal_timeout(int fd, short events, void *arg) { struct address_proposal *addr_proposal; - struct timeval tv; + struct slaacd_iface *iface = NULL; + struct radv *ra = NULL; + struct radv_prefix *prefix = NULL; const char *hbuf; addr_proposal = (struct address_proposal *)arg; @@ -2211,67 +2423,53 @@ address_proposal_timeout(int fd, short events, void *arg) hbuf = sin6_to_str(&addr_proposal->addr); log_debug("%s: iface %d: %s [%s], priv: %s", __func__, addr_proposal->if_index, hbuf, - proposal_state_name[addr_proposal->state], + proposal_state_name(addr_proposal->state), addr_proposal->temporary ? "y" : "n"); switch (addr_proposal->state) { + case PROPOSAL_IF_DOWN: + addr_proposal_state_transition(addr_proposal, PROPOSAL_STALE); + break; case PROPOSAL_CONFIGURED: - log_debug("PROPOSAL_CONFIGURED timeout: id: %lld, temporary: " - "%s", addr_proposal->id, addr_proposal->temporary ? - "y" : "n"); - - addr_proposal->next_timeout = 1; - addr_proposal->state = PROPOSAL_NEARLY_EXPIRED; - - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_add(&addr_proposal->timer, &tv); - + addr_proposal_state_transition(addr_proposal, + PROPOSAL_NEARLY_EXPIRED); break; case PROPOSAL_NEARLY_EXPIRED: - log_debug("%s: rl: %d", __func__, - real_lifetime(&addr_proposal->uptime, - addr_proposal->vltime)); - /* - * we should have gotten a RTM_DELADDR from the kernel, - * in case we missed it, delete to not waste memory - */ if (real_lifetime(&addr_proposal->uptime, - addr_proposal->vltime) == 0) { - evtimer_del(&addr_proposal->timer); - free_address_proposal(addr_proposal); - log_debug("%s: removing address proposal", __func__); - break; - } - - engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, - 0, &addr_proposal->if_index, - sizeof(addr_proposal->if_index)); - - if (addr_proposal->temporary) { - addr_proposal->next_timeout = 0; - break; /* just let it expire */ - } - - tv.tv_sec = addr_proposal->next_timeout; - tv.tv_usec = arc4random_uniform(1000000); - addr_proposal->next_timeout *= 2; - evtimer_add(&addr_proposal->timer, &tv); - log_debug("%s: scheduling new timeout in %llds.%06ld", - __func__, tv.tv_sec, tv.tv_usec); + addr_proposal->vltime) > 0) + addr_proposal_state_transition(addr_proposal, + PROPOSAL_NEARLY_EXPIRED); + else + addr_proposal_state_transition(addr_proposal, + PROPOSAL_STALE); break; case PROPOSAL_DUPLICATED: - engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, - 0, &addr_proposal->if_index, - sizeof(addr_proposal->if_index)); - log_debug("%s: address duplicated", - __func__); + iface = get_slaacd_iface_by_id(addr_proposal->if_index); + if (iface != NULL) + ra = find_ra(iface, &addr_proposal->from); + if (ra != NULL) + prefix = find_prefix(ra, &addr_proposal->prefix, + addr_proposal->prefix_len); + if (prefix != NULL) { + if (!addr_proposal->temporary) { + prefix->dad_counter++; + gen_address_proposal(iface, ra, prefix, 0); + } else + gen_address_proposal(iface, ra, prefix, 1); + } + addr_proposal_state_transition(addr_proposal, PROPOSAL_STALE); break; case PROPOSAL_STALE: + free_address_proposal(addr_proposal); + addr_proposal = NULL; + break; + case PROPOSAL_WITHDRAWN: + free_address_proposal(addr_proposal); + addr_proposal = NULL; break; default: log_debug("%s: unhandled state: %s", __func__, - proposal_state_name[addr_proposal->state]); + proposal_state_name(addr_proposal->state)); } } @@ -2279,48 +2477,43 @@ void dfr_proposal_timeout(int fd, short events, void *arg) { struct dfr_proposal *dfr_proposal; - struct timeval tv; const char *hbuf; dfr_proposal = (struct dfr_proposal *)arg; hbuf = sin6_to_str(&dfr_proposal->addr); log_debug("%s: iface %d: %s [%s]", __func__, dfr_proposal->if_index, - hbuf, proposal_state_name[dfr_proposal->state]); + hbuf, proposal_state_name(dfr_proposal->state)); switch (dfr_proposal->state) { + case PROPOSAL_IF_DOWN: + dfr_proposal_state_transition(dfr_proposal, PROPOSAL_STALE); + break; case PROPOSAL_CONFIGURED: - log_debug("PROPOSAL_CONFIGURED timeout: id: %lld", - dfr_proposal->id); - - dfr_proposal->next_timeout = 1; - dfr_proposal->state = PROPOSAL_NEARLY_EXPIRED; - - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_add(&dfr_proposal->timer, &tv); - + dfr_proposal_state_transition(dfr_proposal, + PROPOSAL_NEARLY_EXPIRED); break; case PROPOSAL_NEARLY_EXPIRED: if (real_lifetime(&dfr_proposal->uptime, - dfr_proposal->router_lifetime) == 0) { - free_dfr_proposal(dfr_proposal); - log_debug("%s: removing dfr proposal", __func__); - break; - } - engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, - 0, &dfr_proposal->if_index, - sizeof(dfr_proposal->if_index)); - tv.tv_sec = dfr_proposal->next_timeout; - tv.tv_usec = arc4random_uniform(1000000); - dfr_proposal->next_timeout *= 2; - evtimer_add(&dfr_proposal->timer, &tv); - log_debug("%s: scheduling new timeout in %llds.%06ld", - __func__, tv.tv_sec, tv.tv_usec); + dfr_proposal->router_lifetime) > 0) + dfr_proposal_state_transition(dfr_proposal, + PROPOSAL_NEARLY_EXPIRED); + else + dfr_proposal_state_transition(dfr_proposal, + PROPOSAL_STALE); break; + case PROPOSAL_STALE: + free_dfr_proposal(dfr_proposal); + dfr_proposal = NULL; + break; + case PROPOSAL_WITHDRAWN: + free_dfr_proposal(dfr_proposal); + dfr_proposal = NULL; + break; + default: log_debug("%s: unhandled state: %s", __func__, - proposal_state_name[dfr_proposal->state]); + proposal_state_name(dfr_proposal->state)); } } @@ -2328,50 +2521,43 @@ void rdns_proposal_timeout(int fd, short events, void *arg) { struct rdns_proposal *rdns_proposal; - struct timeval tv; const char *hbuf; rdns_proposal = (struct rdns_proposal *)arg; hbuf = sin6_to_str(&rdns_proposal->from); log_debug("%s: iface %d: %s [%s]", __func__, rdns_proposal->if_index, - hbuf, proposal_state_name[rdns_proposal->state]); + hbuf, proposal_state_name(rdns_proposal->state)); switch (rdns_proposal->state) { - case PROPOSAL_SENT: - log_debug("PROPOSAL_SENT timeout: id: %lld", - rdns_proposal->id); - - rdns_proposal->next_timeout = 1; - rdns_proposal->state = PROPOSAL_NEARLY_EXPIRED; - - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_add(&rdns_proposal->timer, &tv); - + case PROPOSAL_IF_DOWN: + rdns_proposal_state_transition(rdns_proposal, PROPOSAL_STALE); + break; + case PROPOSAL_CONFIGURED: + rdns_proposal_state_transition(rdns_proposal, + PROPOSAL_NEARLY_EXPIRED); break; case PROPOSAL_NEARLY_EXPIRED: if (real_lifetime(&rdns_proposal->uptime, - rdns_proposal->rdns_lifetime) == 0) { - free_rdns_proposal(rdns_proposal); - log_debug("%s: removing rdns proposal", __func__); - compose_rdns_proposal(rdns_proposal->if_index, - rdns_proposal->rdomain); - break; - } - engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, - 0, &rdns_proposal->if_index, - sizeof(rdns_proposal->if_index)); - tv.tv_sec = rdns_proposal->next_timeout; - tv.tv_usec = arc4random_uniform(1000000); - rdns_proposal->next_timeout *= 2; - evtimer_add(&rdns_proposal->timer, &tv); - log_debug("%s: scheduling new timeout in %llds.%06ld", - __func__, tv.tv_sec, tv.tv_usec); + rdns_proposal->rdns_lifetime) > 0) + rdns_proposal_state_transition(rdns_proposal, + PROPOSAL_NEARLY_EXPIRED); + else + rdns_proposal_state_transition(rdns_proposal, + PROPOSAL_STALE); + break; + case PROPOSAL_STALE: + free_rdns_proposal(rdns_proposal); + rdns_proposal = NULL; + break; + case PROPOSAL_WITHDRAWN: + free_rdns_proposal(rdns_proposal); + rdns_proposal = NULL; break; + default: log_debug("%s: unhandled state: %s", __func__, - proposal_state_name[rdns_proposal->state]); + proposal_state_name(rdns_proposal->state)); } } @@ -2379,48 +2565,17 @@ void iface_timeout(int fd, short events, void *arg) { struct slaacd_iface *iface = (struct slaacd_iface *)arg; - struct timeval tv; - struct address_proposal *addr_proposal; - struct dfr_proposal *dfr_proposal; - struct rdns_proposal *rdns_proposal; log_debug("%s[%d]: %s", __func__, iface->if_index, - if_state_name[iface->state]); + if_state_name(iface->state)); switch (iface->state) { - case IF_DELAY: - case IF_PROBE: - iface->state = IF_PROBE; - engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, 0, - &iface->if_index, sizeof(iface->if_index)); - if (++iface->probes >= MAX_RTR_SOLICITATIONS) { - iface->state = IF_DEAD; - tv.tv_sec = 0; - } else - tv.tv_sec = RTR_SOLICITATION_INTERVAL; - tv.tv_usec = arc4random_uniform(1000000); - evtimer_add(&iface->timer, &tv); - break; - case IF_DEAD: - while(!LIST_EMPTY(&iface->addr_proposals)) { - addr_proposal = LIST_FIRST(&iface->addr_proposals); - addr_proposal->state = PROPOSAL_STALE; - free_address_proposal(addr_proposal); - } - while(!LIST_EMPTY(&iface->dfr_proposals)) { - dfr_proposal = LIST_FIRST(&iface->dfr_proposals); - dfr_proposal->state = PROPOSAL_STALE; - free_dfr_proposal(dfr_proposal); - } - while(!LIST_EMPTY(&iface->rdns_proposals)) { - rdns_proposal = LIST_FIRST(&iface->rdns_proposals); - rdns_proposal->state = PROPOSAL_STALE; - free_rdns_proposal(rdns_proposal); - } - compose_rdns_proposal(iface->if_index, iface->rdomain); - break; case IF_DOWN: - case IF_IDLE: + fatalx("%s: timeout in wrong state IF_DOWN", __func__); + break; + case IF_INIT: + iface_state_transition(iface, IF_INIT); + break; default: break; } @@ -2483,15 +2638,15 @@ find_rdns_proposal_by_gw(struct slaacd_iface *iface, struct sockaddr_in6 } struct radv_prefix * -find_prefix(struct radv *ra, struct radv_prefix *prefix) +find_prefix(struct radv *ra, struct in6_addr *prefix, uint8_t prefix_len) { struct radv_prefix *result; LIST_FOREACH(result, &ra->prefixes, entries) { - if (memcmp(&result->prefix, &prefix->prefix, - sizeof(prefix->prefix)) == 0 && result->prefix_len == - prefix->prefix_len) + if (memcmp(&result->prefix, prefix, + sizeof(result->prefix)) == 0 && result->prefix_len == + prefix_len) return (result); } return (NULL); @@ -2525,7 +2680,8 @@ merge_dad_couters(struct radv *old_ra, struct radv *new_ra) LIST_FOREACH(old_prefix, &old_ra->prefixes, entries) { if (!old_prefix->dad_counter) continue; - if ((new_prefix = find_prefix(new_ra, old_prefix)) != NULL) + if ((new_prefix = find_prefix(new_ra, &old_prefix->prefix, + old_prefix->prefix_len)) != NULL) new_prefix->dad_counter = old_prefix->dad_counter; } } diff --git a/sbin/slaacd/frontend.c b/sbin/slaacd/frontend.c index 43105684daa..24cf5c71cfc 100644 --- a/sbin/slaacd/frontend.c +++ b/sbin/slaacd/frontend.c @@ -1,4 +1,4 @@ -/* $OpenBSD: frontend.c,v 1.63 2022/03/21 16:25:47 florian Exp $ */ +/* $OpenBSD: frontend.c,v 1.64 2022/07/12 16:54:59 florian Exp $ */ /* * Copyright (c) 2017 Florian Obser @@ -96,7 +96,6 @@ void unref_icmp6ev(struct iface *); void set_icmp6sock(int, int); void send_solicitation(uint32_t); #ifndef SMALL -void update_autoconf_addresses(uint32_t, char*); const char *flags_to_str(int); #endif /* SMALL */ @@ -613,103 +612,6 @@ update_iface(uint32_t if_index, char* if_name) } #ifndef SMALL -void -update_autoconf_addresses(uint32_t if_index, char* if_name) -{ - struct in6_ifreq ifr6; - struct imsg_addrinfo imsg_addrinfo; - struct ifaddrs *ifap, *ifa; - struct in6_addrlifetime *lifetime; - struct sockaddr_in6 *sin6; - time_t t; - int xflags; - - if ((xflags = get_xflags(if_name)) == -1) - return; - - if (!(xflags & (IFXF_AUTOCONF6 | IFXF_AUTOCONF6TEMP))) - return; - - memset(&imsg_addrinfo, 0, sizeof(imsg_addrinfo)); - imsg_addrinfo.if_index = if_index; - - if (getifaddrs(&ifap) != 0) - fatal("getifaddrs"); - - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { - if (strcmp(if_name, ifa->ifa_name) != 0) - continue; - if (ifa->ifa_addr == NULL) - continue; - - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) - continue; - - log_debug("%s: IP: %s", __func__, sin6_to_str(sin6)); - imsg_addrinfo.addr = *sin6; - - memset(&ifr6, 0, sizeof(ifr6)); - strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name)); - memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); - - if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6) == -1) { - log_warn("SIOCGIFAFLAG_IN6"); - continue; - } - - if (!(ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_AUTOCONF | - IN6_IFF_TEMPORARY))) - continue; - - imsg_addrinfo.temporary = ifr6.ifr_ifru.ifru_flags6 & - IN6_IFF_TEMPORARY ? 1 : 0; - - memset(&ifr6, 0, sizeof(ifr6)); - strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name)); - memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); - - if (ioctl(ioctlsock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) == - -1) { - log_warn("SIOCGIFNETMASK_IN6"); - continue; - } - - imsg_addrinfo.mask = ((struct sockaddr_in6 *)&ifr6.ifr_addr) - ->sin6_addr; - - memset(&ifr6, 0, sizeof(ifr6)); - strlcpy(ifr6.ifr_name, if_name, sizeof(ifr6.ifr_name)); - memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr)); - lifetime = &ifr6.ifr_ifru.ifru_lifetime; - - if (ioctl(ioctlsock, SIOCGIFALIFETIME_IN6, (caddr_t)&ifr6) == - -1) { - log_warn("SIOCGIFALIFETIME_IN6"); - continue; - } - - imsg_addrinfo.vltime = ND6_INFINITE_LIFETIME; - imsg_addrinfo.pltime = ND6_INFINITE_LIFETIME; - t = time(NULL); - - if (lifetime->ia6t_preferred) - imsg_addrinfo.pltime = lifetime->ia6t_preferred < t ? 0 - : lifetime->ia6t_preferred - t; - - if (lifetime->ia6t_expire) - imsg_addrinfo.vltime = lifetime->ia6t_expire < t ? 0 : - lifetime->ia6t_expire - t; - - frontend_imsg_compose_main(IMSG_UPDATE_ADDRESS, 0, - &imsg_addrinfo, sizeof(imsg_addrinfo)); - - } - freeifaddrs(ifap); -} - const char* flags_to_str(int flags) { @@ -751,12 +653,8 @@ frontend_startup(void) fatalx("if_nameindex"); for(ifnidx = ifnidxp; ifnidx->if_index !=0 && ifnidx->if_name != NULL; - ifnidx++) { + ifnidx++) update_iface(ifnidx->if_index, ifnidx->if_name); -#ifndef SMALL - update_autoconf_addresses(ifnidx->if_index, ifnidx->if_name); -#endif /* SMALL */ - } if_freenameindex(ifnidxp); } @@ -831,16 +729,13 @@ handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) xflags = get_xflags(if_name); if (xflags == -1 || !(xflags & (IFXF_AUTOCONF6 | IFXF_AUTOCONF6TEMP))) { - log_debug("RTM_IFINFO: %s(%d) no(longer) autoconf6", if_name, - if_index); + log_debug("RTM_IFINFO: %s(%d) no(longer) autoconf6", + if_name, if_index); frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, &if_index, sizeof(if_index)); remove_iface(if_index); } else { update_iface(if_index, if_name); -#ifndef SMALL - update_autoconf_addresses(if_index, if_name); -#endif /* SMALL */ } break; case RTM_IFANNOUNCE: diff --git a/sbin/slaacd/slaacd.c b/sbin/slaacd/slaacd.c index e167f58411c..e149479833c 100644 --- a/sbin/slaacd/slaacd.c +++ b/sbin/slaacd/slaacd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: slaacd.c,v 1.64 2021/08/24 14:56:06 florian Exp $ */ +/* $OpenBSD: slaacd.c,v 1.65 2022/07/12 16:54:59 florian Exp $ */ /* * Copyright (c) 2017 Florian Obser @@ -381,7 +381,6 @@ main_dispatch_frontend(int fd, short event, void *bula) int shut = 0; int rdomain; #ifndef SMALL - struct imsg_addrinfo imsg_addrinfo; int verbose; #endif /* SMALL */ @@ -423,15 +422,6 @@ main_dispatch_frontend(int fd, short event, void *bula) memcpy(&verbose, imsg.data, sizeof(verbose)); log_setverbose(verbose); break; - case IMSG_UPDATE_ADDRESS: - if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_addrinfo)) - fatalx("%s: IMSG_UPDATE_ADDRESS wrong length: " - "%lu", __func__, IMSG_DATA_SIZE(imsg)); - memcpy(&imsg_addrinfo, imsg.data, - sizeof(imsg_addrinfo)); - main_imsg_compose_engine(IMSG_UPDATE_ADDRESS, 0, - &imsg_addrinfo, sizeof(imsg_addrinfo)); - break; #endif /* SMALL */ case IMSG_UPDATE_IF: if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_ifinfo)) diff --git a/sbin/slaacd/slaacd.h b/sbin/slaacd/slaacd.h index d7880fb7c35..2844f4a63b7 100644 --- a/sbin/slaacd/slaacd.h +++ b/sbin/slaacd/slaacd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: slaacd.h,v 1.37 2022/06/28 09:21:58 florian Exp $ */ +/* $OpenBSD: slaacd.h,v 1.38 2022/07/12 16:55:00 florian Exp $ */ /* * Copyright (c) 2017 Florian Obser @@ -51,7 +51,6 @@ enum imsg_type { IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSALS, IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSAL, IMSG_CTL_END, - IMSG_UPDATE_ADDRESS, #endif /* SMALL */ IMSG_PROPOSE_RDNS, IMSG_REPROPOSE_RDNS, @@ -158,15 +157,6 @@ struct ctl_engine_info_rdns_proposal { #endif /* SMALL */ -struct imsg_addrinfo { - uint32_t if_index; - struct sockaddr_in6 addr; - struct in6_addr mask; - int temporary; - uint32_t vltime; - uint32_t pltime; -}; - struct imsg_propose_rdns { uint32_t if_index; int rdomain;