]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Support the DHCPv6 unicast option.
authorRoy Marples <roy@marples.name>
Wed, 5 Jun 2013 10:37:45 +0000 (10:37 +0000)
committerRoy Marples <roy@marples.name>
Wed, 5 Jun 2013 10:37:45 +0000 (10:37 +0000)
dhcp6.c
dhcp6.h

diff --git a/dhcp6.c b/dhcp6.c
index d3706b95a20ce6b7064208eb83b80785469def2a..026cc6c384d0a9657fe0f80134e5b51ab955ab0b 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -69,7 +69,9 @@
 //#define VENDOR_SPLIT
 
 static int sock = -1;
-static struct sockaddr_in6 alldhcp, from;
+static struct in6_addr in6addr_linklocal_alldhcp =
+    IN6ADDR_LINKLOCAL_ALLDHCP_INIT;
+static struct sockaddr_in6 from;
 static struct msghdr sndhdr;
 static struct iovec sndiov[2];
 static unsigned char *sndbuf;
@@ -158,15 +160,6 @@ dhcp6_init(void)
        atexit(dhcp6_cleanup);
 #endif
 
-       memset(&alldhcp, 0, sizeof(alldhcp));
-       alldhcp.sin6_family = AF_INET6;
-       alldhcp.sin6_port = htons(DHCP6_SERVER_PORT);
-#ifdef SIN6_LEN
-       alldhcp.sin6_len = sizeof(alldhcp);
-#endif
-       if (inet_pton(AF_INET6, ALLDHCP, &alldhcp.sin6_addr.s6_addr) != 1)
-               return -1;
-
        len = CMSG_SPACE(sizeof(struct in6_pktinfo));
        sndbuf = calloc(1, len);
        if (sndbuf == NULL)
@@ -358,7 +351,7 @@ dhcp6_makemessage(struct interface *ifp)
        struct dhcp6_state *state;
        struct dhcp6_message *m;
        struct dhcp6_option *o, *so;
-       const struct dhcp6_option *si;
+       const struct dhcp6_option *si, *unicast;
        ssize_t len, ml;
        size_t l;
        uint8_t u8;
@@ -386,7 +379,7 @@ dhcp6_makemessage(struct interface *ifp)
                                len += sizeof(*u16);
                }
                if (len == 0)
-                       len = sizeof(*u16) * 2;
+                       len = sizeof(*u16) * 3;
                len += sizeof(*o);
        }
 
@@ -451,6 +444,8 @@ dhcp6_makemessage(struct interface *ifp)
                return -1;
 
        state->send_len = len;
+       unicast = NULL;
+       /* Depending on state, get the unicast address */
        switch(state->state) {
                break;
        case DH6S_INIT: /* FALLTHROUGH */
@@ -459,6 +454,7 @@ dhcp6_makemessage(struct interface *ifp)
                break;
        case DH6S_REQUEST:
                state->send->type = DHCP6_REQUEST;
+               unicast = dhcp6_getoption(D6_OPTION_UNICAST, m, ml);
                break;
        case DH6S_CONFIRM:
                state->send->type = DHCP6_CONFIRM;
@@ -468,12 +464,14 @@ dhcp6_makemessage(struct interface *ifp)
                break;
        case DH6S_RENEW:
                state->send->type = DHCP6_RENEW;
+               unicast = dhcp6_getoption(D6_OPTION_UNICAST, m, ml);
                break;
        case DH6S_INFORM:
                state->send->type = DHCP6_INFORMATION_REQ;
                break;
        case DH6S_RELEASE:
                state->send->type = DHCP6_RELEASE;
+               unicast = dhcp6_getoption(D6_OPTION_UNICAST, m, ml);
                break;
        default:
                errno = EINVAL;
@@ -482,6 +480,13 @@ dhcp6_makemessage(struct interface *ifp)
                return -1;
        }
 
+       /* If we found a unicast option, copy it to our state for sending */
+       if (unicast && ntohs(unicast->len) == sizeof(state->unicast.s6_addr))
+               memcpy(&state->unicast.s6_addr, D6_COPTION_DATA(unicast),
+                   sizeof(state->unicast.s6_addr));
+       else
+               state->unicast = in6addr_any;
+
        dhcp6_newxid(ifp, state->send);
 
        o = D6_FIRST_OPTION(state->send);
@@ -578,7 +583,8 @@ dhcp6_makemessage(struct interface *ifp)
                if (o->len == 0) {
                        *u16++ = htons(D6_OPTION_DNS_SERVERS);
                        *u16++ = htons(D6_OPTION_DOMAIN_LIST);
-                       o->len = sizeof(*u16) * 2;
+                       *u16++ = htons(D6_OPTION_UNICAST);
+                       o->len = sizeof(*u16) * 3;
                }
                o->len = htons(o->len);
        }
@@ -638,11 +644,36 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
        double rnd;
        suseconds_t ms;
        uint8_t neg;
+       const char *broad_uni;
+
+       memset(&to, 0, sizeof(to));
+       to.sin6_family = AF_INET6;
+       to.sin6_port = htons(DHCP6_SERVER_PORT);
+#ifdef SIN6_LEN
+       to.sin6_len = sizeof(to);
+#endif
 
        state = D6_STATE(ifp);
+       /* We need to ensure we have sufficient scope to unicast the address */
+       /* XXX FIXME: We should check any added addresses we have like from
+        * a Router Advertisement */
+       if (!IN6_IS_ADDR_UNSPECIFIED(&state->unicast) &&
+           state->state == DH6S_REQUEST &&
+           (!IN6_IS_ADDR_LINKLOCAL(&state->unicast) || !ipv6_linklocal(ifp)))
+               state->unicast = in6addr_any;
+
+       if (IN6_IS_ADDR_UNSPECIFIED(&state->unicast)) {
+               to.sin6_addr = in6addr_linklocal_alldhcp;
+               broad_uni = "broadcasting";
+       } else {
+               to.sin6_addr = state->unicast;
+               broad_uni = "unicasting";
+       }
+
        if (!callback)
-               syslog(LOG_DEBUG, "%s: sending %s with xid 0x%02x%02x%02x",
+               syslog(LOG_DEBUG, "%s: %s %s with xid 0x%02x%02x%02x",
                    ifp->name,
+                   broad_uni,
                    dhcp6_get_op(state->send->type),
                    state->send->xid[0],
                    state->send->xid[1],
@@ -687,9 +718,11 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
                }
 
                syslog(LOG_DEBUG,
-                   "%s: sending %s (xid 0x%02x%02x%02x),"
+                   "%s: %s %s (xid 0x%02x%02x%02x),"
                    " next in %0.2f seconds",
-                   ifp->name, dhcp6_get_op(state->send->type),
+                   ifp->name,
+                   broad_uni,
+                   dhcp6_get_op(state->send->type),
                    state->send->xid[0],
                    state->send->xid[1],
                    state->send->xid[2],
@@ -699,7 +732,6 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
        /* Update the elapsed time */
        dhcp6_updateelapsed(ifp, state->send, state->send_len);
 
-       to = alldhcp;
        to.sin6_scope_id = ifp->index;
        sndhdr.msg_name = (caddr_t)&to;
        sndhdr.msg_iov[0].iov_base = (caddr_t)state->send;
diff --git a/dhcp6.h b/dhcp6.h
index 151c3f5eb4a9309ef388d641ea8d1115dd729e11..7189145d1b4f8f5cb56c6a2dbaff5715307159a1 100644 (file)
--- a/dhcp6.h
+++ b/dhcp6.h
@@ -30,7 +30,9 @@
 
 #include "dhcpcd.h"
 
-#define ALLDHCP "ff02::1:2"
+#define IN6ADDR_LINKLOCAL_ALLDHCP_INIT \
+       {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+           0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 }}}
 
 /* UDP port numbers for DHCP */
 #define DHCP6_CLIENT_PORT      546
@@ -177,6 +179,7 @@ struct dhcp6_state {
        uint32_t renew;
        uint32_t rebind;
        uint32_t expire;
+       struct in6_addr unicast;
        struct ipv6_addrhead addrs;
        uint32_t lowpl;
        uint32_t sla;