summaryrefslogtreecommitdiffstats
path: root/ipv6.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2014-06-04 20:27:40 +0000
committerRoy Marples <roy@marples.name>2014-06-04 20:27:40 +0000
commit1aeaf0e7517692a31fe347ea4cedc44409f021dd (patch)
tree09b609f9ad2e1b0b91d02bc97101a8a48422c3c9 /ipv6.c
parentbe844cae6d927e1f8f132f0fc94bcab1565b0fe8 (diff)
downloaddhcpcd-1aeaf0e7517692a31fe347ea4cedc44409f021dd.tar.xz
Default SLAAC to use RFC7271 addresses.
Create an IPv6 link-local address if non exists at startup using the configured SLAAC method.
Diffstat (limited to 'ipv6.c')
-rw-r--r--ipv6.c118
1 files changed, 117 insertions, 1 deletions
diff --git a/ipv6.c b/ipv6.c
index 25ebd65a..f9a79ae8 100644
--- 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)
{