]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Default SLAAC to use RFC7271 addresses.
authorRoy Marples <roy@marples.name>
Wed, 4 Jun 2014 20:27:40 +0000 (20:27 +0000)
committerRoy Marples <roy@marples.name>
Wed, 4 Jun 2014 20:27:40 +0000 (20:27 +0000)
Create an IPv6 link-local address if non exists at startup using the
configured SLAAC method.

dhcpcd.8.in
dhcpcd.c
dhcpcd.conf.5.in
if-options.c
if-options.h
ipv6.c
ipv6.h
ipv6nd.c

index 29285f1ba94521609ba4b7e5e00ddd533d81c289..de2a827b2fe5b0e2cafe088a133bb0508515f0c8 100644 (file)
@@ -75,7 +75,7 @@
 .Sh DESCRIPTION
 .Nm
 is an implementation of the DHCP client specified in
-.Li RFC 2131 .
+.%R RFC 2131 .
 .Nm
 gets the host information
 .Po
@@ -103,27 +103,20 @@ to renew early.
 .Pp
 .Nm
 is also an implementation of the BOOTP client specified in
-.Li RFC 951 .
+.%R RFC 951 .
 .Pp
 .Nm
 is also an implementation of the IPv6 Router Solicitor as specified in
-.Li RFC 4861
+.%R RFC 4861
 and
-.Li RFC 6106 .
-.Nm
-can optionally handle address and route management itself,
-and will do so by default if Router Solicitation is disabled in the kernel
-or if stable private addresses are enabled in the configuration.
-If
-.Nm
-is managing routes,
-.Nm
-sends Neighbor Solicitions to each advertising router periodically and will
-expire the ones that do not respond.
+.%R RFC 6106
+and will generate stable private Interface Identifiers for SLAAC
+as specified in
+.%R RFC 7212 .
 .Pp
 .Nm
 is also an implemenation of the DHCPv6 client as specified in
-.Li RFC 3315 .
+.%R RFC 3315 .
 By default,
 .Nm
 only starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement.
@@ -221,7 +214,7 @@ instead of the default
 .Pa @SCRIPT@ .
 .It Fl D , Fl Fl duid
 Generate an
-.Li RFC 4361
+.%R RFC 4361
 compliant clientid.
 This requires persistent storage and not all DHCP servers work with it so it
 is not enabled by default.
@@ -269,7 +262,7 @@ are disable, none, ptr and both.
 itself never does any DNS updates.
 .Nm
 encodes the FQDN hostname as specified in
-.Li RFC1035 .
+.%R RFC1035 .
 .It Fl f , Fl Fl config Ar file
 Specify a config to load instead of
 .Pa @SYSCONFDIR@/dhcpcd.conf .
index 482764d41bec049023c9717d4713c3d0deb51336..c89922a36d8560dfeecb085d3adfc0db3763d149 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -447,6 +447,8 @@ configure_interface1(struct interface *ifp)
        if (!(ifo->auth.options & DHCPCD_AUTH_SEND))
                ifo->auth.options &= ~DHCPCD_AUTH_REQUIRE;
 
+       if (ifo->options & DHCPCD_SLAACPRIVATE)
+               ifo->options |= DHCPCD_IPV6RA_OWN;
 }
 
 int
@@ -602,6 +604,10 @@ dhcpcd_startinterface(void *arg)
                }
        }
 
+       if (ifo->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) {
+               syslog(LOG_ERR, "%s: ipv6_start: %m", ifp->name);
+               ifo->options &= DHCPCD_IPV6;
+       }
        if (ifo->options & DHCPCD_IPV6) {
                if (ifo->options & DHCPCD_IPV6RS &&
                    !(ifo->options & DHCPCD_INFORM))
index e61b771ace36b1f173cae62a7fcdbdeac9468590..845bce43eaf31d8ac0a37ca8068a74876ce57706 100644 (file)
@@ -453,9 +453,11 @@ instead of the default
 .It Ic ssid Ar ssid
 Subsequent options are only parsed for this wireless
 .Ar ssid .
-.It Ic stableprivate
-Configure stable private IPv6 addresses as per RFC7217 for SLAAC instead of
-ones generated from hardware addresses for SLAAC.
+.It Ic slaac Op Ar hwaddr | Ar private
+Selects the interface identifier used for SLAAC generated IPv6 addresses.
+.Ar private
+is the default and complies with
+.%R RFC 7217.
 .It Ic static Ar value
 Configures a static
 .Ar value .
index b3f255b02d4e232f7c050032e5e044a4a4148471..344760881f26f61775daa12b821394bbb0c0bc5a 100644 (file)
@@ -91,7 +91,7 @@
 #define O_IPV4                 O_BASE + 32
 #define O_IPV6                 O_BASE + 33
 #define O_CONTROLGRP           O_BASE + 34
-#define O_STABLEPRIVATE                O_BASE + 35
+#define O_SLAAC                        O_BASE + 35
 
 const struct option cf_options[] = {
        {"background",      no_argument,       NULL, 'b'},
@@ -176,7 +176,7 @@ const struct option cf_options[] = {
        {"dhcp6",           no_argument,       NULL, O_DHCP6},
        {"nodhcp6",         no_argument,       NULL, O_NODHCP6},
        {"controlgroup",    required_argument, NULL, O_CONTROLGRP},
-       {"stableprivate",   no_argument,       NULL, O_STABLEPRIVATE},
+       {"slaac",           required_argument, NULL, O_SLAAC},
        {NULL,              0,                 NULL, '\0'}
 };
 
@@ -1837,10 +1837,13 @@ err_sla:
                ctx->control_group = grp->gr_gid;
 #endif
                break;
-       case O_STABLEPRIVATE:
-               ifo->options |= DHCPCD_STABLEPRIVATE;
-               /* This option implies that we steal SLAAC from the kernel */
-               ifo->options |= DHCPCD_IPV6RA_OWN;
+       case O_SLAAC:
+               if (strcmp(arg, "private") == 0 ||
+                   strcmp(arg, "stableprivate") == 0 ||
+                   strcmp(arg, "stable") == 0)
+                       ifo->options |= DHCPCD_SLAACPRIVATE;
+               else
+                       ifo->options &= ~DHCPCD_SLAACPRIVATE;
                break;
        default:
                return 0;
@@ -1949,6 +1952,7 @@ read_config(struct dhcpcd_ctx *ctx,
 #endif
 #ifdef INET6
        ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS | DHCPCD_IPV6RA_REQRDNSS;
+       ifo->options |= DHCPCD_SLAACPRIVATE;
        ifo->options |= DHCPCD_DHCP6;
 #endif
        ifo->timeout = DEFAULT_TIMEOUT;
index 4288479bd7c62c3b51a4fbab4b0101bf845d0fb2..b5cf156ab848704e88101af1dc915381def45f85 100644 (file)
 #define DHCPCD_IAID                    (1ULL << 48)
 #define DHCPCD_DHCP                    (1ULL << 49)
 #define DHCPCD_DHCP6                   (1ULL << 50)
-#define DHCPCD_STABLEPRIVATE           (1ULL << 51)
+#define DHCPCD_SLAACPRIVATE            (1ULL << 51)
 
 extern const struct option cf_options[];
 
diff --git a/ipv6.c b/ipv6.c
index 25ebd65a783a80c87c56bb546d12e695d98221f7..f9a79ae8a3377a49b34857c0abb1659a76c4c183 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -393,7 +393,7 @@ ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp,
                return -1;
        }
 
-       if (ifp->options->options & DHCPCD_STABLEPRIVATE) {
+       if (ifp->options->options & DHCPCD_SLAACPRIVATE) {
                if (ifp->ctx->secret_len == 0) {
                        if (ipv6_readsecret(ifp->ctx) == -1)
                                return -1;
@@ -878,6 +878,122 @@ ipv6_free_ll_callbacks(struct interface *ifp)
        }
 }
 
+static struct ipv6_addr *
+ipv6_newlinklocal(struct interface *ifp)
+{
+       struct ipv6_addr *ap;
+
+       ap = calloc(1, sizeof(*ap));
+       if (ap != NULL) {
+               ap->iface = ifp;
+               ap->prefix.s6_addr32[0] = htonl(0xfe800000);
+               ap->prefix.s6_addr32[1] = 0;
+               ap->prefix_len = 64;
+               ap->dadcounter = 0;
+               ap->prefix_pltime = ND6_INFINITE_LIFETIME;
+               ap->prefix_vltime = ND6_INFINITE_LIFETIME;
+               ap->flags = IPV6_AF_NEW;
+               ap->addr_flags = IN6_IFF_TENTATIVE;
+       }
+       return ap;
+}
+
+static const uint8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static const uint8_t allone[8] =
+    { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static int
+ipv6_addlinklocal(struct interface *ifp)
+{
+       struct ipv6_state *state;
+       struct ipv6_addr *ap;
+       int dadcounter;
+
+       if (ipv6_linklocal(ifp))
+               return 0;
+
+       /* Check sanity before malloc */
+       if (!(ifp->options->options & DHCPCD_SLAACPRIVATE)) {
+               switch (ifp->family) {
+               case ARPHRD_ETHER:
+                       /* Check for a valid hardware address */
+                       if (ifp->hwlen != 6 & ifp->hwlen != 8) {
+                               errno = ENOTSUP;
+                               return -1;
+                       }
+                       if (memcmp(ifp->hwaddr, allzero, ifp->hwlen) == 0 ||
+                           memcmp(ifp->hwaddr, allone, ifp->hwlen) == 0)
+                       {
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       break;
+               default:
+                       errno = ENOTSUP;
+                       return -1;
+               }
+       }
+
+       state = ipv6_getstate(ifp);
+       if (state == NULL)
+               return -1;
+
+       ap = ipv6_newlinklocal(ifp);
+       if (ap == NULL)
+               return -1;
+
+       if (ifp->options->options & DHCPCD_SLAACPRIVATE) {
+               dadcounter = 0;
+               if (ipv6_makestableprivate(&ap->addr,
+                       &ap->prefix, ap->prefix_len, ifp, &dadcounter) == -1)
+               {
+                       free(ap);
+                       return -1;
+               }
+               ap->dadcounter = dadcounter;
+       } else {
+               memcpy(ap->addr.s6_addr, ap->prefix.s6_addr, ap->prefix_len);
+               switch (ifp->family) {
+               case ARPHRD_ETHER:
+                       if (ifp->hwlen == 6) {
+                               ap->addr.s6_addr[ 8] = ifp->hwaddr[0];
+                               ap->addr.s6_addr[ 9] = ifp->hwaddr[1];
+                               ap->addr.s6_addr[10] = ifp->hwaddr[2];
+                               ap->addr.s6_addr[11] = 0xff;
+                               ap->addr.s6_addr[12] = 0xfe;
+                               ap->addr.s6_addr[13] = ifp->hwaddr[3];
+                               ap->addr.s6_addr[14] = ifp->hwaddr[4];
+                               ap->addr.s6_addr[15] = ifp->hwaddr[5];
+                       } else if (ifp->hwlen == 8)
+                               memcpy(&ap->addr.s6_addr[8], ifp->hwaddr, 8);
+                       break;
+               }
+
+               /* Sanity check: g bit must not indciate "group" */
+               if (EUI64_GROUP(&ap->addr)) {
+                       free(ap);
+                       errno = EINVAL;
+                       return -1;
+               }
+               EUI64_TO_IFID(&ap->addr);
+       }
+
+       inet_ntop(AF_INET6, &ap->addr, ap->saddr, sizeof(ap->saddr));
+       TAILQ_INSERT_TAIL(&state->addrs, ap, next);
+       ipv6_addaddr(ap);
+       return 1;
+}
+
+/* Ensure the interface has a link-local address */
+int
+ipv6_start(struct interface *ifp)
+{
+
+       if (ipv6_linklocal(ifp) == NULL && ipv6_addlinklocal(ifp) == -1)
+               return -1;
+       return 0;
+}
+
 void
 ipv6_free(struct interface *ifp)
 {
diff --git a/ipv6.h b/ipv6.h
index 82a74b379fa63dfa623228157e85cf4d26abae7c..0b860e2fec7b61867463b5f46dbcb93ce56e5f0b 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
 #define ROUNDUP8(a)  (1 + (((a) - 1) |  7))
 #define ROUNDUP16(a) (1 + (((a) - 1) | 16))
 
+#define EUI64_GBIT             0x01
+#define EUI64_UBIT             0x02
+#define EUI64_TO_IFID(in6)     do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
+#define EUI64_GROUP(in6)       ((in6)->s6_addr[8] & EUI64_GBIT)
+
 #ifndef ND6_INFINITE_LIFETIME
 #  define ND6_INFINITE_LIFETIME                ((uint32_t)~0)
 #endif
@@ -189,6 +194,7 @@ const struct ipv6_addr *ipv6_findaddr(const struct interface *,
 #define ipv6_linklocal(ifp) (ipv6_findaddr((ifp), NULL))
 int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
 void ipv6_free_ll_callbacks(struct interface *);
+int ipv6_start(struct interface *);
 void ipv6_free(struct interface *);
 void ipv6_ctxfree(struct dhcpcd_ctx *);
 int ipv6_removesubnet(struct interface *, struct ipv6_addr *);
@@ -196,6 +202,7 @@ void ipv6_buildroutes(struct dhcpcd_ctx *);
 
 #else
 #define ipv6_init(a) NULL
+#define ipv6_start(a) (-1)
 #define ipv6_free_ll_callbacks(a)
 #define ipv6_free(a)
 #define ipv6_ctxfree(a)
index 0ed143043b39e6e229865488290dae96f257b2cb..f75115290c6d5453eea85a20a366a69b15f67a2e 100644 (file)
--- a/ipv6nd.c
+++ b/ipv6nd.c
@@ -603,7 +603,7 @@ ipv6nd_dadcallback(void *arg)
                 * 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 &&
+               if (ifp->options->options & DHCPCD_SLAACPRIVATE &&
                    ap->dadcounter < IDGEN_RETRIES)
                {
                        syslog(LOG_INFO, "%s: deleting address %s",