summaryrefslogtreecommitdiffstats
path: root/ipv6nd.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2014-06-02 14:48:33 +0000
committerRoy Marples <roy@marples.name>2014-06-02 14:48:33 +0000
commit3ed12ab824919fda7cfae98b6779841e8651984e (patch)
treeb9ed0e502741515f1c216a489a7e468835445bfb /ipv6nd.c
parentdecaba353a0eb82531431eda011a5a4f7ec6f30e (diff)
downloaddhcpcd-3ed12ab824919fda7cfae98b6779841e8651984e.tar.xz
Implement Stable Private Addresses for SLAAC as per RFC7217.
Add a SHA256 implementation by Collin Percival if one in libc/libmd not found.
Diffstat (limited to 'ipv6nd.c')
-rw-r--r--ipv6nd.c63
1 files changed, 59 insertions, 4 deletions
diff --git a/ipv6nd.c b/ipv6nd.c
index a277889e..0e6f7a95 100644
--- a/ipv6nd.c
+++ b/ipv6nd.c
@@ -572,23 +572,78 @@ ipv6nd_scriptrun(struct ra *rap)
}
static void
+ipv6nd_addaddr(void *arg)
+{
+ struct ipv6_addr *ap = arg;
+
+ ipv6_addaddr(ap);
+}
+
+static void
ipv6nd_dadcallback(void *arg)
{
struct ipv6_addr *ap = arg, *rapap;
struct interface *ifp;
struct ra *rap;
int wascompleted, found;
+ struct timeval tv;
+ char buf[INET6_ADDRSTRLEN];
+ const char *p;
+ ifp = ap->iface;
wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
ap->flags |= IPV6_AF_DADCOMPLETED;
- if (ap->flags & IPV6_AF_DUPLICATED)
- /* No idea what how to try and make another address :( */
+ if (ap->flags & IPV6_AF_DUPLICATED) {
+ ap->dadcounter++;
syslog(LOG_WARNING, "%s: DAD detected %s",
ap->iface->name, ap->saddr);
- if (!wascompleted) {
- ifp = ap->iface;
+ /* Try and make another stable private address.
+ * Because ap->dadcounter is always increamented,
+ * a different address is generated. */
+ /* XXX Cache DAD counter per prefix/id/ssid? */
+ if (ifp->options->options & DHCPCD_STABLEPRIVATE &&
+ ap->dadcounter < IDGEN_RETRIES)
+ {
+ syslog(LOG_INFO, "%s: deleting address %s",
+ ifp->name, ap->saddr);
+ if (if_deladdress6(ap) == -1 &&
+ errno != EADDRNOTAVAIL && errno != ENXIO)
+ syslog(LOG_ERR, "if_deladdress6: %m");
+ if (ipv6_makestableprivate(&ap->addr,
+ &ap->prefix, ap->prefix_len,
+ ifp->options->iaid, sizeof(ifp->options->iaid),
+ ifp->ssid, strlen(ifp->ssid),
+ ap->dadcounter,
+ ifp->ctx->secret, ifp->ctx->secret_len) == -1)
+ {
+ syslog(LOG_ERR,
+ "%s: ipv6_makestableprivate: %m",
+ ifp->name);
+ return;
+ }
+ ap->flags &= ~(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED);
+ ap->flags |= IPV6_AF_NEW;
+ p = inet_ntop(AF_INET6, ap->addr.s6_addr,
+ buf, sizeof(buf));
+ if (p)
+ snprintf(ap->saddr,
+ sizeof(ap->saddr),
+ "%s/%d",
+ p, ap->prefix_len);
+ else
+ ap->saddr[0] = '\0';
+ tv.tv_sec = 0;
+ tv.tv_usec = (suseconds_t)arc4random_uniform(
+ IDGEN_DELAY * USECINSEC);
+ timernorm(&tv);
+ eloop_timeout_add_tv(ifp->ctx->eloop, &tv,
+ ipv6nd_addaddr, ap);
+ return;
+ }
+ }
+ if (!wascompleted) {
TAILQ_FOREACH(rap, ifp->ctx->ipv6->ra_routers, next) {
if (rap->iface != ifp)
continue;