diff options
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/slaacd/engine.c | 992 | ||||
-rw-r--r-- | sbin/slaacd/frontend.c | 113 | ||||
-rw-r--r-- | sbin/slaacd/slaacd.c | 12 | ||||
-rw-r--r-- | sbin/slaacd/slaacd.h | 12 |
4 files changed, 580 insertions, 549 deletions
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 <florian@openbsd.org> @@ -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) { @@ -1029,6 +961,326 @@ free_ra(struct radv *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) { struct slaacd_iface *iface; @@ -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,45 +2342,13 @@ 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) { struct imsg_propose_rdns rdns; @@ -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 <florian@openbsd.org> @@ -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 <florian@openbsd.org> @@ -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 <florian@openbsd.org> @@ -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; |