From: Roy Marples Date: Thu, 9 Apr 2020 15:16:37 +0000 (+0100) Subject: RA: Rework temporary address management X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4d4f630416a1ed712abb045ec2da92f8b898add4;p=thirdparty%2Fdhcpcd.git RA: Rework temporary address management Vastly simpfly how they are generated and managed. Temporary address generation now uses pure random numbers rather than MD5ing over a random secret as arc4random should be random enough. This change reflects RFC 4941bis. --- diff --git a/src/dhcp6.c b/src/dhcp6.c index 42989d17..e59a5aeb 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -2708,7 +2708,7 @@ dhcp6_ifdelegateaddr(struct interface *ifp, struct ipv6_addr *prefix, vl |= sla->suffix; be64enc(daddr.s6_addr + 8, vl); } else { - dadcounter = ipv6_makeaddr(&daddr, ifp, &addr, pfxlen); + dadcounter = ipv6_makeaddr(&daddr, ifp, &addr, pfxlen, 0); if (dadcounter == -1) { logerrx("%s: error adding slaac to prefix_len %d", ifp->name, pfxlen); diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 90f02ba2..b8f41e8b 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -782,7 +782,7 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags, ipv6nd_startexpire(ifp); #endif /* RFC4941 Section 3.5 */ - ipv6_gentempifid(ifp); + ipv6_regentempaddrs(ifp); #endif dhcpcd_startinterface(ifp); } diff --git a/src/ipv6.c b/src/ipv6.c index 51b4c9b7..fb52667b 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -121,10 +121,7 @@ #endif #ifdef IPV6_MANAGETEMPADDR -static void ipv6_regentempifid(void *); static void ipv6_regentempaddr(void *); -#else -#define ipv6_regentempifid(a) {} #endif int @@ -181,7 +178,7 @@ ipv6_readsecret(struct dhcpcd_ctx *ctx) } /* Ensure that only the dhcpcd user can read the secret. - * Write permission is also denied as chaning it would remove + * Write permission is also denied as changing it would remove * it's stability. */ if ((fp = fopen(SECRET, "w")) == NULL || chmod(SECRET, S_IRUSR) == -1) @@ -217,7 +214,7 @@ static const struct reslowhigh { { 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } }; -static int +static bool ipv6_reserved(const struct in6_addr *addr) { uint64_t id, low, high; @@ -227,37 +224,42 @@ ipv6_reserved(const struct in6_addr *addr) id = be64dec(addr->s6_addr + sizeof(id)); if (id == 0) /* RFC4291 */ return 1; - for (i = 0; i < sizeof(reslowhigh) / sizeof(reslowhigh[0]); i++) { + for (i = 0; i < __arraycount(reslowhigh); i++) { r = &reslowhigh[i]; low = be64dec(r->low); high = be64dec(r->high); if (id >= low && id <= high) - return 1; + return true; } - return 0; + return false; } /* RFC7217 */ static int -ipv6_makestableprivate1(struct in6_addr *addr, - const struct in6_addr *prefix, int prefix_len, +ipv6_makestableprivate1(struct dhcpcd_ctx *ctx, + struct in6_addr *addr, const struct in6_addr *prefix, int prefix_len, const unsigned char *netiface, size_t netiface_len, const unsigned char *netid, size_t netid_len, unsigned short vlanid, - uint32_t *dad_counter, - const unsigned char *secret, size_t secret_len) + uint32_t *dad_counter) { unsigned char buf[2048], *p, digest[SHA256_DIGEST_LENGTH]; size_t len, l; - SHA256_CTX ctx; + SHA256_CTX sha_ctx; if (prefix_len < 0 || prefix_len > 120) { errno = EINVAL; return -1; } + if (ctx->secret_len == 0) { + if (ipv6_readsecret(ctx) == -1) + return -1; + } + l = (size_t)(ROUNDUP8(prefix_len) / NBBY); - len = l + netiface_len + netid_len + sizeof(*dad_counter) + secret_len; + len = l + netiface_len + netid_len + sizeof(*dad_counter) + + ctx->secret_len; if (vlanid != 0) len += sizeof(vlanid); if (len > sizeof(buf)) { @@ -282,15 +284,15 @@ ipv6_makestableprivate1(struct in6_addr *addr, } memcpy(p, dad_counter, sizeof(*dad_counter)); p += sizeof(*dad_counter); - memcpy(p, secret, secret_len); + memcpy(p, ctx->secret, ctx->secret_len); /* Make an address using the digest of the above. * RFC7217 Section 5.1 states that we shouldn't use MD5. * Pity as we use that for HMAC-MD5 which is still deemed OK. * SHA-256 is recommended */ - SHA256_Init(&ctx); - SHA256_Update(&ctx, buf, len); - SHA256_Final(digest, &ctx); + SHA256_Init(&sha_ctx); + SHA256_Update(&sha_ctx, buf, len); + SHA256_Final(digest, &sha_ctx); p = addr->s6_addr; memcpy(p, prefix, l); @@ -320,29 +322,50 @@ ipv6_makestableprivate(struct in6_addr *addr, uint32_t dad; int r; - if (ifp->ctx->secret_len == 0) { - if (ipv6_readsecret(ifp->ctx) == -1) - return -1; - } - dad = (uint32_t)*dad_counter; /* For our implementation, we shall set the hardware address * as the interface identifier */ - r = ipv6_makestableprivate1(addr, prefix, prefix_len, + r = ipv6_makestableprivate1(ifp->ctx, addr, prefix, prefix_len, ifp->hwaddr, ifp->hwlen, ifp->ssid, ifp->ssid_len, - ifp->vlanid, &dad, - ifp->ctx->secret, ifp->ctx->secret_len); + ifp->vlanid, &dad); if (r == 0) *dad_counter = (int)dad; return r; } +static int +ipv6_maketemporaryaddress(struct in6_addr *addr, + const struct in6_addr *prefix, int prefix_len, + const struct interface *ifp) +{ + struct in6_addr mask; + struct interface *ifpn; + + 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]); + + TAILQ_FOREACH(ifpn, ifp->ctx->ifaces, next) { + if (ipv6_iffindaddr(ifpn, addr, 0) != NULL) + break; + } + if (ifpn != NULL) + goto again; + if (ipv6_reserved(addr)) + goto again; + return 0; +} + int ipv6_makeaddr(struct in6_addr *addr, struct interface *ifp, - const struct in6_addr *prefix, int prefix_len) + const struct in6_addr *prefix, int prefix_len, unsigned int flags) { const struct ipv6_addr *ap; int dad; @@ -352,6 +375,13 @@ ipv6_makeaddr(struct in6_addr *addr, struct interface *ifp, return -1; } +#ifdef IPV6_AF_TEMPORARY + if (flags & IPV6_AF_TEMPORARY) + return ipv6_maketemporaryaddress(addr, prefix, prefix_len, ifp); +#else + UNUSED(flags); +#endif + if (ifp->options->options & DHCPCD_SLAACPRIVATE) { dad = 0; if (ipv6_makestableprivate(addr, @@ -1052,11 +1082,6 @@ ipv6_getstate(struct interface *ifp) } TAILQ_INIT(&state->addrs); TAILQ_INIT(&state->ll_callbacks); - - /* Regenerate new ids */ - if (ifp->options && - ip6_use_tempaddr(ifp->name)) - ipv6_regentempifid(ifp); } return state; } @@ -1523,7 +1548,9 @@ ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr, /* If adding a new DHCP / RA derived address, check current flags * from an existing address. */ - if (flags & IPV6_AF_AUTOCONF) + if (tempaddr) + iaf = NULL; + else if (flags & IPV6_AF_AUTOCONF) iaf = ipv6nd_iffindprefix(ifp, addr, prefix_len); else iaf = ipv6_iffindaddr(ifp, addr, 0); @@ -1551,14 +1578,15 @@ ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr, if (prefix_len == 128) goto makepfx; - else if (ia->flags & IPV6_AF_AUTOCONF && !tempaddr) { + else if (ia->flags & IPV6_AF_AUTOCONF) { ia->prefix = *addr; if (iaf != NULL) memcpy(&ia->addr, &iaf->addr, sizeof(ia->addr)); else { ia->dadcounter = ipv6_makeaddr(&ia->addr, ifp, &ia->prefix, - ia->prefix_len); + ia->prefix_len, + ia->flags); if (ia->dadcounter == -1) goto err; } @@ -1736,12 +1764,6 @@ ipv6_start(struct interface *ifp) if (ipv6_tryaddlinklocal(ifp) == -1) return -1; - if (IPV6_CSTATE(ifp)) { - /* Regenerate new ids */ - if (ip6_use_tempaddr(ifp->name)) - ipv6_regentempifid(ifp); - } - return 0; } @@ -1838,31 +1860,8 @@ ipv6_handleifa_addrs(int cmd, } #ifdef IPV6_MANAGETEMPADDR -static const struct ipv6_addr * -ipv6_findaddrid(struct dhcpcd_ctx *ctx, uint8_t *addr) -{ - const struct interface *ifp; - const struct ipv6_state *state; - const struct ipv6_addr *ia; - - TAILQ_FOREACH(ifp, ctx->ifaces, next) { - if ((state = IPV6_CSTATE(ifp))) { - TAILQ_FOREACH(ia, &state->addrs, next) { - if (memcmp(&ia->addr.s6_addr[8], addr, 8) == 0) - return ia; - } - } - } - return NULL; -} - -static const uint8_t nullid[8]; -static const uint8_t anycastid[8] = { - 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 }; -static const uint8_t isatapid[4] = { 0x00, 0x00, 0x5e, 0xfe }; - static void -ipv6_regen_desync(struct interface *ifp, int force) +ipv6_regen_desync(struct interface *ifp, bool force) { struct ipv6_state *state; unsigned int max, pref; @@ -1871,7 +1870,7 @@ ipv6_regen_desync(struct interface *ifp, int force) /* 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 greateter than + * I believe this is an error and it should be never be greater than * TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE. */ pref = (unsigned int)ip6_temp_preferred_lifetime(ifp->name); max = pref - REGEN_ADVANCE; @@ -1881,63 +1880,7 @@ ipv6_regen_desync(struct interface *ifp, int force) state->desync_factor = arc4random_uniform(MIN(MAX_DESYNC_FACTOR, max)); max = pref - state->desync_factor - REGEN_ADVANCE; - eloop_timeout_add_sec(ifp->ctx->eloop, max, ipv6_regentempifid, ifp); -} - -void -ipv6_gentempifid(struct interface *ifp) -{ - struct ipv6_state *state; - MD5_CTX md5; - uint8_t seed[16], digest[16]; - int retry; - - if ((state = IPV6_STATE(ifp)) == NULL) - return; - - retry = 0; - if (memcmp(nullid, state->randomseed0, sizeof(nullid)) == 0) { - uint32_t r; - - r = arc4random(); - memcpy(seed, &r, sizeof(r)); - r = arc4random(); - memcpy(seed + sizeof(r), &r, sizeof(r)); - } else - memcpy(seed, state->randomseed0, sizeof(state->randomseed0)); - - memcpy(seed + sizeof(state->randomseed0), - state->randomseed1, sizeof(state->randomseed1)); - -again: - MD5Init(&md5); - MD5Update(&md5, seed, sizeof(seed)); - MD5Final(digest, &md5); - - /* RFC4941 Section 3.2.1.1 - * Take the left-most 64bits and set bit 6 to zero */ - memcpy(state->randomid, digest, sizeof(state->randomid)); - state->randomid[0] = (uint8_t)(state->randomid[0] & ~EUI64_UBIT); - - /* RFC4941 Section 3.2.1.4 - * Reject reserved or existing id's */ - if (memcmp(nullid, state->randomid, sizeof(nullid)) == 0 || - (memcmp(anycastid, state->randomid, 7) == 0 && - (anycastid[7] & state->randomid[7]) == anycastid[7]) || - memcmp(isatapid, state->randomid, sizeof(isatapid)) == 0 || - ipv6_findaddrid(ifp->ctx, state->randomid)) - { - if (++retry < GEN_TEMPID_RETRY_MAX) { - memcpy(seed, digest + 8, 8); - goto again; - } - memset(state->randomid, 0, sizeof(state->randomid)); - } - - /* RFC4941 Section 3.2.1.6 - * Save the right-most 64bits of the digest */ - memcpy(state->randomseed0, digest + 8, - sizeof(state->randomseed0)); + eloop_timeout_add_sec(ifp->ctx->eloop, max, ipv6_regentempaddrs, ifp); } /* RFC4941 Section 3.3.7 */ @@ -1970,75 +1913,27 @@ struct ipv6_addr * ipv6_createtempaddr(struct ipv6_addr *ia0, const struct timespec *now) { struct ipv6_state *state; - const struct ipv6_state *cstate; - int genid; - struct in6_addr addr, mask; - uint32_t randid[2]; - const struct interface *ifp; - const struct ipv6_addr *ap; + struct interface *ifp = ia0->iface; struct ipv6_addr *ia; - uint32_t i, trylimit; - - trylimit = TEMP_IDGEN_RETRIES; - state = IPV6_STATE(ia0->iface); - genid = 0; - - addr = ia0->addr; - ipv6_mask(&mask, ia0->prefix_len); - /* clear the old ifid */ - for (i = 0; i < 4; i++) - addr.s6_addr32[i] &= mask.s6_addr32[i]; - -again: - if (memcmp(state->randomid, nullid, sizeof(nullid)) == 0) - genid = 1; - if (genid) { - memcpy(state->randomseed1, &ia0->addr.s6_addr[8], - sizeof(state->randomseed1)); - ipv6_gentempifid(ia0->iface); - if (memcmp(state->randomid, nullid, sizeof(nullid)) == 0) { - errno = EFAULT; - return NULL; - } - } - memcpy(&randid[0], state->randomid, sizeof(randid[0])); - memcpy(&randid[1], state->randomid + sizeof(randid[1]), - sizeof(randid[2])); - addr.s6_addr32[2] |= randid[0] & ~mask.s6_addr32[2]; - addr.s6_addr32[3] |= randid[1] & ~mask.s6_addr32[3]; - - /* Ensure we don't already have it */ - TAILQ_FOREACH(ifp, ia0->iface->ctx->ifaces, next) { - cstate = IPV6_CSTATE(ifp); - if (cstate) { - TAILQ_FOREACH(ap, &cstate->addrs, next) { - if (IN6_ARE_ADDR_EQUAL(&ap->addr, &addr)) { - if (--trylimit == 0) { - errno = EEXIST; - return NULL; - } - genid = 1; - goto again; - } - } - } - } + uint32_t i; - ia = ipv6_newaddr(ia0->iface, &addr, ia0->prefix_len, + ia = ipv6_newaddr(ifp, &ia0->prefix, ia0->prefix_len, IPV6_AF_AUTOCONF | IPV6_AF_TEMPORARY); - /* Must be made tentative, for our DaD to work */ - ia->addr_flags = IN6_IFF_TENTATIVE; + if (ia == NULL) + return NULL; + ia->dadcallback = ipv6_tempdadcallback; ia->created = ia->acquired = now ? *now : ia0->acquired; /* Ensure desync is still valid */ - ipv6_regen_desync(ia->iface, 0); + ipv6_regen_desync(ifp, false); /* RFC4941 Section 3.3.4 */ - i = (uint32_t)ip6_temp_preferred_lifetime(ia0->iface->name) - + state = IPV6_STATE(ia->iface); + i = (uint32_t)ip6_temp_preferred_lifetime(ifp->name) - state->desync_factor; ia->prefix_pltime = MIN(ia0->prefix_pltime, i); - i = (uint32_t)ip6_temp_valid_lifetime(ia0->iface->name); + i = (uint32_t)ip6_temp_valid_lifetime(ifp->name); ia->prefix_vltime = MIN(ia0->prefix_vltime, i); if (ia->prefix_pltime <= REGEN_ADVANCE || ia->prefix_pltime > ia0->prefix_vltime) @@ -2089,7 +1984,7 @@ ipv6_settemptime(struct ipv6_addr *ia, int flags) } /* Ensure desync is still valid */ - ipv6_regen_desync(ap->iface, 0); + ipv6_regen_desync(ap->iface, false); /* RFC4941 Section 3.3.2 * Extend temporary times, but ensure that they @@ -2147,31 +2042,44 @@ ipv6_addtempaddrs(struct interface *ifp, const struct timespec *now) } static void -ipv6_regentempaddr(void *arg) +ipv6_regentempaddr0(struct ipv6_addr *ia, struct timespec *tv) { - struct ipv6_addr *ia = arg, *ia1; - struct timespec tv; + struct ipv6_addr *ia1; logdebugx("%s: regen temp addr %s", ia->iface->name, ia->saddr); - clock_gettime(CLOCK_MONOTONIC, &tv); - ia1 = ipv6_createtempaddr(ia, &tv); + ia1 = ipv6_createtempaddr(ia, tv); if (ia1) - ipv6_addaddr(ia1, &tv); + ipv6_addaddr(ia1, tv); else logerr(__func__); } static void -ipv6_regentempifid(void *arg) +ipv6_regentempaddr(void *arg) +{ + struct timespec tv; + + clock_gettime(CLOCK_MONOTONIC, &tv); + ipv6_regentempaddr0(arg, &tv); +} + +void +ipv6_regentempaddrs(void *arg) { struct interface *ifp = arg; + struct timespec tv; struct ipv6_state *state; + struct ipv6_addr *ia; - state = IPV6_STATE(ifp); - if (memcmp(state->randomid, nullid, sizeof(state->randomid))) - ipv6_gentempifid(ifp); + ipv6_regen_desync(ifp, true); - ipv6_regen_desync(ifp, 1); + clock_gettime(CLOCK_MONOTONIC, &tv); + state = IPV6_STATE(ifp); + TAILQ_FOREACH(ia, &state->addrs, next) { + if (ia->flags & IPV6_AF_TEMPORARY && + !(ia->flags & IPV6_AF_STALE)) + ipv6_regentempaddr0(ia, &tv); + } } #endif /* IPV6_MANAGETEMPADDR */ diff --git a/src/ipv6.h b/src/ipv6.h index 8ffc0896..0330b8e7 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -59,9 +59,7 @@ #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 GEN_TEMPID_RETRY_MAX 5 /* RFC7217 constants */ #define IDGEN_RETRIES 3 @@ -245,9 +243,6 @@ struct ipv6_state { #ifdef IPV6_MANAGETEMPADDR uint32_t desync_factor; - uint8_t randomseed0[8]; /* upper 64 bits of MD5 digest */ - uint8_t randomseed1[8]; /* lower 64 bits */ - uint8_t randomid[8]; #endif }; @@ -259,11 +254,10 @@ struct ipv6_state { int ipv6_init(struct dhcpcd_ctx *); -int ipv6_makestableprivate(struct in6_addr *addr, - const struct in6_addr *prefix, int prefix_len, - const struct interface *ifp, int *dad_counter); +int ipv6_makestableprivate(struct in6_addr *, + const struct in6_addr *, int, const struct interface *, int *); int ipv6_makeaddr(struct in6_addr *, struct interface *, - const struct in6_addr *, int); + const struct in6_addr *, int, unsigned int); int ipv6_mask(struct in6_addr *, int); uint8_t ipv6_prefixlen(const struct in6_addr *); int ipv6_userprefix( const struct in6_addr *, short prefix_len, @@ -301,11 +295,11 @@ void ipv6_freedrop(struct interface *, int); #define ipv6_drop(ifp) ipv6_freedrop((ifp), 2) #ifdef IPV6_MANAGETEMPADDR -void ipv6_gentempifid(struct interface *); struct ipv6_addr *ipv6_createtempaddr(struct ipv6_addr *, const struct timespec *); struct ipv6_addr *ipv6_settemptime(struct ipv6_addr *, int); void ipv6_addtempaddrs(struct interface *, const struct timespec *); +void ipv6_regentempaddrs(void *); #else #define ipv6_gentempifid(a) {} #define ipv6_settempstale(a) {}