diff --git a/src/dhcpcd.8.in b/src/dhcpcd.8.in index 26d37772..73c13dbf 100644 --- a/src/dhcpcd.8.in +++ b/src/dhcpcd.8.in @@ -131,7 +131,7 @@ and .Nm is also an implementation of the IPv6 Privacy Extensions to AutoConf as specified in -.Li RFC 4941 . +.Li RFC 8981 . This feature needs to be enabled in the kernel and .Nm will start using it. @@ -876,8 +876,8 @@ RFC\ 951, RFC\ 1534, RFC\ 2104, RFC\ 2131, RFC\ 2132, RFC\ 2563, RFC\ 2855, RFC\ 3004, RFC\ 3118, RFC\ 3203, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397, RFC\ 3442, RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075, RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833, -RFC\ 4941, RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6355, -RFC\ 6603, RFC\ 6704, RFC\ 7217, RFC\ 7550, RFC\ 7844. +RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6355, +RFC\ 6603, RFC\ 6704, RFC\ 7217, RFC\ 7550, RFC\ 7844, RFC\ 8981. .Sh AUTHORS .An Roy Marples Aq Mt roy@marples.name .Sh BUGS diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 7e862b3e..f0818f02 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -860,7 +860,7 @@ dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags) * would normally as we maybe on a new network. */ ipv6nd_startexpire(ifp); #ifdef IPV6_MANAGETEMPADDR - /* RFC4941 Section 3.5 */ + /* RFC 8981 Section 3.6 */ ipv6_regentempaddrs(ifp); #endif #endif diff --git a/src/dhcpcd.conf.5.in b/src/dhcpcd.conf.5.in index b7d596e9..e800e577 100644 --- a/src/dhcpcd.conf.5.in +++ b/src/dhcpcd.conf.5.in @@ -733,7 +733,7 @@ The default is 180 seconds. .It Ic ssid Ar ssid Subsequent options are only parsed for this wireless .Ar ssid . -.It Ic slaac Ic hwaddr | Ic private | Ic token Ar token Op Ic temp | Ic temporary +.It Ic slaac Oo Ic hwaddr | Ic private | Ic token Ar token Oc Oo Ic temp | Ic temporary Oc Selects the interface identifier used for SLAAC generated IPv6 addresses. If .Ic private @@ -743,7 +743,7 @@ If is used then the token is combined with the prefix to make the final address. The .Ic temporary -directive will create a temporary address for the prefix as well. +directive will create a RFC 8981 temporary address for the prefix. .It Ic static Ar value Configures a static .Ar value . diff --git a/src/if-options.c b/src/if-options.c index 0ec71a43..9bb8ef03 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -2525,30 +2525,37 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, } if (strcmp(arg, "private") == 0 || strcmp(arg, "stableprivate") == 0 || - strcmp(arg, "stable") == 0) + strcmp(arg, "stable") == 0) { ifo->options |= DHCPCD_SLAACPRIVATE; - else + arg = np; + } else { ifo->options &= ~DHCPCD_SLAACPRIVATE; #ifdef INET6 - if (strcmp(arg, "token") == 0) { - if (np == NULL) { - logerrx("slaac token: no token specified"); - return -1; - } - arg = np; - np = strwhite(np); - if (np != NULL) { - *np++ = '\0'; - np = strskipwhite(np); - } - if (inet_pton(AF_INET6, arg, &ifo->token) != 1) { - logerrx("slaac token: invalid token"); - return -1; + if (strcmp(arg, "token") == 0) { + if (np == NULL) { + logerrx( + "slaac token: no token specified"); + return -1; + } + arg = np; + np = strwhite(np); + if (np != NULL) { + *np++ = '\0'; + np = strskipwhite(np); + } + if (inet_pton(AF_INET6, arg, &ifo->token) != + 1) { + logerrx("slaac token: invalid token"); + return -1; + } + arg = np; } - } #endif - if (np != NULL && - (strcmp(np, "temp") == 0 || strcmp(np, "temporary") == 0)) + } + + if (arg == NULL) + break; + if (strcmp(arg, "temp") == 0 || strcmp(arg, "temporary") == 0) ifo->options |= DHCPCD_SLAACTEMP; break; case O_BOOTP: diff --git a/src/ipv6.c b/src/ipv6.c index 80ee4ad3..6bf91fd6 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -123,6 +123,18 @@ #ifdef IPV6_MANAGETEMPADDR static void ipv6_regentempaddr(void *); + +/* RFC 8981 Section 3.8: + * 2 + (TEMP_IDGEN_RETRIES * DupAddrDetectTransmits * RetransTimer / 1000) */ +static inline uint32_t +ipv6_regen_advance(const struct interface *ifp) +{ + const struct rs_state *state = RS_CSTATE(ifp); + + if (state != NULL && state->retrans != 0) + return 2 + (TEMP_IDGEN_RETRIES * 1 * state->retrans / 1000); + return REGEN_ADVANCE; +} #endif int @@ -216,7 +228,7 @@ ipv6_reserved(const struct in6_addr *addr) id = be64dec(addr->s6_addr + sizeof(id)); if (id == 0) /* RFC4291 */ - return 1; + return true; for (i = 0; i < __arraycount(reslowhigh); i++) { r = &reslowhigh[i]; low = be64dec(r->low); @@ -367,7 +379,7 @@ ipv6_makestableprivate(struct in6_addr *addr, const struct in6_addr *prefix, return r; } -#ifdef IPV6_AF_TEMPORARY +#ifdef IPV6_MANAGETEMPADDR static int ipv6_maketemporaryaddress(struct in6_addr *addr, const struct in6_addr *prefix, int prefix_len, const struct interface *ifp) @@ -377,12 +389,22 @@ ipv6_maketemporaryaddress(struct in6_addr *addr, const struct in6_addr *prefix, if (ipv6_mask(&mask, prefix_len) == -1) return -1; - *addr = *prefix; again: - addr->s6_addr32[2] |= (arc4random() & ~mask.s6_addr32[2]); - addr->s6_addr32[3] |= (arc4random() & ~mask.s6_addr32[3]); - + *addr = *prefix; + addr->s6_addr32[0] = (addr->s6_addr32[0] & mask.s6_addr32[0]) | + (arc4random() & ~mask.s6_addr32[0]); + addr->s6_addr32[1] = (addr->s6_addr32[1] & mask.s6_addr32[1]) | + (arc4random() & ~mask.s6_addr32[1]); + addr->s6_addr32[2] = (addr->s6_addr32[2] & mask.s6_addr32[2]) | + (arc4random() & ~mask.s6_addr32[2]); + addr->s6_addr32[3] = (addr->s6_addr32[3] & mask.s6_addr32[3]) | + (arc4random() & ~mask.s6_addr32[3]); + + /* RFC 8981 Section 3.3.1 Step 3: + * The resulting IID MUST be compared against the reserved IPv6 IIDs + * and against those IIDs already employed in an address of the same + * network interface and the same network prefix. */ TAILQ_FOREACH(ifpn, ifp->ctx->ifaces, next) { if (ipv6_iffindaddr(ifpn, addr, 0) != NULL) break; @@ -482,7 +504,7 @@ ipv6_makeaddr(struct in6_addr *addr, struct interface *ifp, return -1; } -#ifdef IPV6_AF_TEMPORARY +#ifdef IPV6_MANAGETEMPADDR if (flags & IPV6_AF_TEMPORARY) return ipv6_maketemporaryaddress(addr, prefix, prefix_len, ifp); #else @@ -755,12 +777,7 @@ ipv6_addaddr1(struct ipv6_addr *ia, struct timespec *now) loglevel = ia->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG; logmessage(loglevel, "%s: adding %saddress %s", ifp->name, -#ifdef IPV6_AF_TEMPORARY - ia->flags & IPV6_AF_TEMPORARY ? "temporary " : "", -#else - "", -#endif - ia->saddr); + ia->flags & IPV6_AF_TEMPORARY ? "temporary " : "", ia->saddr); if (ia->prefix_pltime == ND6_INFINITE_LIFETIME && ia->prefix_vltime == ND6_INFINITE_LIFETIME) logdebugx("%s: pltime infinity, vltime infinity", ifp->name); @@ -768,7 +785,7 @@ ipv6_addaddr1(struct ipv6_addr *ia, struct timespec *now) logdebugx("%s: pltime infinity, vltime %" PRIu32 " seconds", ifp->name, ia->prefix_vltime); else if (ia->prefix_vltime == ND6_INFINITE_LIFETIME) - logdebugx("%s: pltime %" PRIu32 "seconds, vltime infinity", + logdebugx("%s: pltime %" PRIu32 " seconds, vltime infinity", ifp->name, ia->prefix_pltime); else logdebugx("%s: pltime %" PRIu32 " seconds, vltime %" PRIu32 @@ -784,12 +801,20 @@ ipv6_addaddr1(struct ipv6_addr *ia, struct timespec *now) } #ifdef IPV6_MANAGETEMPADDR - /* RFC4941 Section 3.4 */ + /* RFC 8981 Section 3.5 */ if (ia->flags & IPV6_AF_TEMPORARY && - ia->prefix_pltime > REGEN_ADVANCE && ia->prefix_vltime && - ifp->options->options & DHCPCD_SLAACTEMP) - eloop_timeout_add_sec(ifp->ctx->eloop, - ia->prefix_pltime - REGEN_ADVANCE, ipv6_regentempaddr, ia); + ifp->options->options & DHCPCD_SLAACTEMP) { + uint32_t regen_advance = ipv6_regen_advance(ifp); + + if (!(ia->flags & IPV6_AF_TEMPFAILED) && + ia->prefix_pltime > regen_advance && ia->prefix_vltime) + eloop_timeout_add_sec(ifp->ctx->eloop, + ia->prefix_pltime - regen_advance, + ipv6_regentempaddr, ia); + else + eloop_timeout_delete(ifp->ctx->eloop, + ipv6_regentempaddr, ia); + } #endif /* Restore real pltime and vltime */ @@ -1211,7 +1236,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, int cmd, struct if_head *ifs, ia->prefix_pltime = 0; ia->prefix_vltime = ND6_INFINITE_LIFETIME; } - /* This is a minor regression against RFC 4941 + /* This is a minor regression against RFC 8981 * because the kernel only knows when the * lifetimes were last updated, not when the * address was initially created. @@ -1604,7 +1629,7 @@ ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr, else if (ia->flags & IPV6_AF_AUTOCONF) { ia->prefix = *addr; iaf = ipv6nd_iffindprefix(ifp, addr, prefix_len); - if (iaf != NULL) + if (iaf != NULL && !(ia->flags & IPV6_AF_TEMPORARY)) memcpy(&ia->addr, &iaf->addr, sizeof(ia->addr)); else { ia->dadcounter = ipv6_makeaddr(&ia->addr, ifp, @@ -1894,52 +1919,37 @@ ipv6_handleifa_addrs(int cmd, struct ipv6_addrhead *addrs, } #ifdef IPV6_MANAGETEMPADDR +/* RFC 8981 Section 3.4 Step 7 */ static void -ipv6_regen_desync(struct interface *ifp, bool force) +ipv6_tempdadcallback(void *arg) { - struct ipv6_state *state; - unsigned int max; - - state = IPV6_STATE(ifp); + struct ipv6_addr *ia = arg, *ta; + struct timespec tv; - /* RFC4941 Section 5 states that DESYNC_FACTOR must never be - * greater than TEMP_VALID_LIFETIME - REGEN_ADVANCE. - * I believe this is an error and it should be never be greater than - * TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE. */ - max = TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE; - if (state->desync_factor && !force && state->desync_factor < max) + if (!(ia->addr_flags & IN6_IFF_DUPLICATED)) return; - if (state->desync_factor == 0) - state->desync_factor = arc4random_uniform( - MIN(MAX_DESYNC_FACTOR, max)); - max = TEMP_PREFERRED_LIFETIME - state->desync_factor - REGEN_ADVANCE; - eloop_timeout_add_sec(ifp->ctx->eloop, max, ipv6_regentempaddrs, ifp); -} -/* RFC4941 Section 3.3.7 */ -static void -ipv6_tempdadcallback(void *arg) -{ - struct ipv6_addr *ia = arg; - - if (ia->addr_flags & IN6_IFF_DUPLICATED) { - struct ipv6_addr *ia1; - struct timespec tv; + /* + * The address is duplicated. + * Don't delete it here as it will still be used after the callback. + * Duplicate addresses in other IPv6 protocols are not deleted. + */ + eloop_timeout_delete(ia->iface->ctx->eloop, ipv6_regentempaddr, ia); - if (++ia->dadcounter == TEMP_IDGEN_RETRIES) { - logerrx("%s: too many duplicate temporary addresses", - ia->iface->name); - return; - } - clock_gettime(CLOCK_MONOTONIC, &tv); - if ((ia1 = ipv6_createtempaddr(ia, &tv)) == NULL) - logerr(__func__); - else - ia1->dadcounter = ia->dadcounter; - ipv6_deleteaddr(ia); - if (ia1) - ipv6_addaddr(ia1, &ia1->acquired); + if (++ia->dadcounter >= TEMP_IDGEN_RETRIES) { + logerrx("%s: %s: too many duplicate temporary addresses", + ia->iface->name, ia->saddr); + ipv6nd_prefix_setflags(ia, IPV6_AF_TEMPFAILED); + return; } + + clock_gettime(CLOCK_MONOTONIC, &tv); + if ((ta = ipv6_createtempaddr(ia, &tv)) == NULL) + logerr(__func__); + else + ta->dadcounter = ia->dadcounter; + if (ta != NULL) + ipv6_addaddr(ta, &ta->acquired); } struct ipv6_addr * @@ -1948,6 +1958,7 @@ ipv6_createtempaddr(struct ipv6_addr *ia0, const struct timespec *now) struct ipv6_state *state; struct interface *ifp = ia0->iface; struct ipv6_addr *ia; + uint32_t regen_advance, max_desync; ia = ipv6_newaddr(ifp, &ia0->prefix, ia0->prefix_len, IPV6_AF_AUTOCONF | IPV6_AF_TEMPORARY); @@ -1957,21 +1968,32 @@ ipv6_createtempaddr(struct ipv6_addr *ia0, const struct timespec *now) ia->dadcallback = ipv6_tempdadcallback; ia->created = ia->acquired = now ? *now : ia0->acquired; - /* Ensure desync is still valid */ - ipv6_regen_desync(ifp, false); - - /* RFC4941 Section 3.3.4 */ - state = IPV6_STATE(ia->iface); + /* RFC 8981 Section 3.4 Step 4 and Section 3.8: + * DESYNC_FACTOR is computed each time a temporary address is + * generated, and is associated with the corresponding address. + * It MUST be smaller than (TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE). */ + regen_advance = ipv6_regen_advance(ifp); + max_desync = TEMP_PREFERRED_LIFETIME - regen_advance - 1; + if (max_desync > MAX_DESYNC_FACTOR) + max_desync = MAX_DESYNC_FACTOR; + ia->desync_factor = arc4random_uniform(max_desync + 1); + + /* RFC 8981 Section 3.4 Step 4 */ ia->prefix_pltime = MIN(ia0->prefix_pltime, - TEMP_PREFERRED_LIFETIME - state->desync_factor); + TEMP_PREFERRED_LIFETIME - ia->desync_factor); ia->prefix_vltime = MIN(ia0->prefix_vltime, TEMP_VALID_LIFETIME); - if (ia->prefix_pltime <= REGEN_ADVANCE || + + /* RFC 8981 Section 3.4 Step 5: + * A temporary address is created only if this calculated preferred + * lifetime is greater than REGEN_ADVANCE time units. */ + if (ia->prefix_pltime <= regen_advance || ia->prefix_pltime > ia0->prefix_vltime) { errno = EINVAL; free(ia); return NULL; } + state = IPV6_STATE(ia->iface); TAILQ_INSERT_TAIL(&state->addrs, ia, next); return ia; } @@ -1981,8 +2003,10 @@ ipv6_settemptime(struct ipv6_addr *ia, int flags) { struct ipv6_state *state; struct ipv6_addr *ap, *first; + uint32_t regen_advance; state = IPV6_STATE(ia->iface); + regen_advance = ipv6_regen_advance(ia->iface); first = NULL; TAILQ_FOREACH_REVERSE(ap, &state->addrs, ipv6_addrhead, next) { if (ap->flags & IPV6_AF_TEMPORARY && ap->prefix_pltime && @@ -1994,7 +2018,7 @@ ipv6_settemptime(struct ipv6_addr *ia, int flags) elapsed = eloop_timespec_diff(&ia->acquired, &ap->acquired, NULL); if (ap->prefix_pltime <= elapsed || - ap->prefix_pltime - elapsed < REGEN_ADVANCE) + ap->prefix_pltime - elapsed < regen_advance) continue; return ap; @@ -2007,20 +2031,17 @@ ipv6_settemptime(struct ipv6_addr *ia, int flags) elapsed = eloop_timespec_diff(&ia->acquired, &ap->created, NULL); - /* RFC4941 Section 3.4 + /* RFC 8981 Section 3.5 * Deprecated prefix, deprecate the temporary address */ if (ia->prefix_pltime == 0) { ap->prefix_pltime = 0; goto valid; } - /* Ensure desync is still valid */ - ipv6_regen_desync(ap->iface, false); - - /* RFC4941 Section 3.3.2 + /* RFC 8981 Section 3.4 Steps 1 & 2 * Extend temporary times, but ensure that they * never last beyond the system limit. */ - limit = TEMP_PREFERRED_LIFETIME - state->desync_factor; + limit = TEMP_PREFERRED_LIFETIME - ap->desync_factor; if (elapsed >= limit) ap->prefix_pltime = 0; else { @@ -2107,14 +2128,13 @@ ipv6_regentempaddrs(void *arg) if (state == NULL) return; - ipv6_regen_desync(ifp, true); - clock_gettime(CLOCK_MONOTONIC, &tv); /* Mark addresses for regen so we don't infinite loop. */ TAILQ_FOREACH(ia, &state->addrs, next) { if (ia->flags & IPV6_AF_TEMPORARY && - ia->flags & IPV6_AF_ADDED && !(ia->flags & IPV6_AF_STALE)) + ia->flags & IPV6_AF_ADDED && + !(ia->flags & (IPV6_AF_STALE | IPV6_AF_TEMPFAILED))) ia->flags |= IPV6_AF_REGEN; else ia->flags &= ~IPV6_AF_REGEN; diff --git a/src/ipv6.h b/src/ipv6.h index 1b78532f..a038abee 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -58,12 +58,13 @@ #define ND6_INFINITE_LIFETIME ((uint32_t)~0) #endif -/* RFC4941 constants */ -#define TEMP_VALID_LIFETIME 604800 /* 1 week */ +/* RFC 8981 constants */ +#define TEMP_VALID_LIFETIME 172800 /* 2 days */ #define TEMP_PREFERRED_LIFETIME 86400 /* 1 day */ #define REGEN_ADVANCE 5 /* seconds */ -#define MAX_DESYNC_FACTOR 600 /* 10 minutes */ -#define TEMP_IDGEN_RETRIES 3 +#define MAX_DESYNC_FACTOR \ + (TEMP_PREFERRED_LIFETIME * 4 / 10) /* 0.4 * TEMP_PREFERRED_LIFETIME */ +#define TEMP_IDGEN_RETRIES 3 /* RFC7217 constants */ #define IDGEN_RETRIES 3 @@ -208,6 +209,9 @@ struct ipv6_addr { #ifdef ALIAS_ADDR char alias[IF_NAMESIZE]; #endif +#ifdef IPV6_MANAGETEMPADDR + uint32_t desync_factor; +#endif }; #define IPV6_AF_ONLINK (1U << 0) @@ -227,9 +231,8 @@ struct ipv6_addr { #define IPV6_AF_REGEN (1U << 14) #define IPV6_AF_ROUTER (1U << 15) #define IPV6_AF_ADVERTISED (1U << 16) -#ifdef IPV6_MANAGETEMPADDR -#define IPV6_AF_TEMPORARY (1U << 17) -#endif +#define IPV6_AF_TEMPORARY (1U << 17) +#define IPV6_AF_TEMPFAILED (1U << 18) struct ll_callback { TAILQ_ENTRY(ll_callback) next; @@ -241,10 +244,6 @@ TAILQ_HEAD(ll_callback_head, ll_callback); struct ipv6_state { struct ipv6_addrhead addrs; struct ll_callback_head ll_callbacks; - -#ifdef IPV6_MANAGETEMPADDR - uint32_t desync_factor; -#endif }; #define IPV6_STATE(ifp) ((struct ipv6_state *)(ifp)->if_data[IF_DATA_IPV6]) diff --git a/src/ipv6nd.c b/src/ipv6nd.c index 5f881915..f14e14e0 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -58,6 +58,7 @@ #include "ipv6nd.h" #include "logerr.h" #include "privsep.h" +#include "queue.h" #include "route.h" #include "script.h" @@ -920,6 +921,24 @@ ipv6nd_dadcallback(void *arg) } } +/* Sets the flags for the prefix found on each RA for the interface */ +void +ipv6nd_prefix_setflags(struct ipv6_addr *ia, unsigned int flags) +{ + struct ra *rap; + struct ipv6_addr *pfx; + + TAILQ_FOREACH(rap, ia->iface->ctx->ra_routers, next) { + if (rap->iface != ia->iface) + continue; + TAILQ_FOREACH(pfx, &rap->addrs, next) { + if (ia->prefix_len == pfx->prefix_len && + IN6_ARE_ADDR_EQUAL(&ia->prefix, &pfx->prefix)) + pfx->flags |= flags; + } + } +} + static struct ipv6_addr * ipv6nd_findmarkstale(struct ra *rap, struct ipv6_addr *ia, bool mark) { @@ -1223,7 +1242,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, const struct sockaddr_in6 *from, has_address = false; rap->mtu = 0; -#ifdef IPV6_AF_TEMPORARY +#ifdef IPV6_MANAGETEMPADDR ipv6_markaddrsstale(ifp, IPV6_AF_TEMPORARY); #endif TAILQ_FOREACH(ia, &rap->addrs, next) { @@ -1379,8 +1398,9 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, const struct sockaddr_in6 *from, has_address = true; #ifdef IPV6_MANAGETEMPADDR - /* RFC4941 Section 3.3.3 */ + /* RFC 8981 Section 3.4 */ if (ia->flags & IPV6_AF_AUTOCONF && + !(ia->flags & IPV6_AF_TEMPFAILED) && ia->iface->options->options & DHCPCD_SLAACTEMP && IA6_CANAUTOCONF(ia)) { if (!new_ia) { @@ -1729,9 +1749,7 @@ ipv6nd_env(FILE *fp, const struct interface *ifp) j = 0; TAILQ_FOREACH(ia, &rap->addrs, next) { if (!(ia->flags & IPV6_AF_AUTOCONF) || -#ifdef IPV6_AF_TEMPORARY ia->flags & IPV6_AF_TEMPORARY || -#endif !(ia->flags & IPV6_AF_ADDED) || ia->prefix_vltime == 0) continue; diff --git a/src/ipv6nd.h b/src/ipv6nd.h index 63799411..92f9a3b2 100644 --- a/src/ipv6nd.h +++ b/src/ipv6nd.h @@ -131,6 +131,7 @@ struct ipv6_addr *ipv6nd_iffindprefix(struct interface *, const struct in6_addr *, uint8_t); ssize_t ipv6nd_free(struct interface *); void ipv6nd_expirera(void *arg); +void ipv6nd_prefix_setflags(struct ipv6_addr *, unsigned int); bool ipv6nd_hasralifetime(const struct interface *, bool); #define ipv6nd_hasra(i) ipv6nd_hasralifetime((i), false) bool ipv6nd_hasradhcp(const struct interface *, bool);