]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
RA: Rework temporary address management
authorRoy Marples <roy@marples.name>
Thu, 9 Apr 2020 15:16:37 +0000 (16:16 +0100)
committerRoy Marples <roy@marples.name>
Thu, 9 Apr 2020 15:16:37 +0000 (16:16 +0100)
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.

src/dhcp6.c
src/dhcpcd.c
src/ipv6.c
src/ipv6.h

index 42989d175c00937d2454565907f08fa77e2288d9..e59a5aebb3c3dac824fc3d09f41ba0abfede248a 100644 (file)
@@ -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);
index 90f02ba2f4c4b882e9484db6df858360689fc11e..b8f41e8b9b4783cb0c95966ce51553de82054b43 100644 (file)
@@ -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);
                }
index 51b4c9b783049780ac3fc98b8423802ee05c2b5c..fb52667b8235c5c3b130c993db6158be9271939d 100644 (file)
 #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 */
 
index 8ffc0896760281dccc057b40eb529b0ffe057070..0330b8e78856b6fd6468ce1f6af078f12ca7b9ba 100644 (file)
@@ -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) {}