#endif
#ifdef IPV6_MANAGETEMPADDR
-static void ipv6_regentempifid(void *);
static void ipv6_regentempaddr(void *);
-#else
-#define ipv6_regentempifid(a) {}
#endif
int
}
/* 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)
{ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }
};
-static int
+static bool
ipv6_reserved(const struct in6_addr *addr)
{
uint64_t id, low, high;
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)) {
}
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);
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;
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,
}
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;
}
/* 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);
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;
}
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;
}
}
#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;
/* 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;
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 */
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)
}
/* 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
}
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 */