]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
DHCPv6: listen on all IPv6 addresses for active interface
authorRoy Marples <roy@marples.name>
Wed, 25 Oct 2017 13:56:23 +0000 (14:56 +0100)
committerRoy Marples <roy@marples.name>
Wed, 25 Oct 2017 13:56:23 +0000 (14:56 +0100)
Simplify the process of selecting addresses to listen for DHCPv6
replies on by listening on all addresses for active interfaces
when not in master mode.

Always send from a socket not bound to an address to ensure
unicast in non master mode works fine. The downside of this approach
is that we no longer send from the DHCPv6 client port, but this
seems to work fine, at least with ISC DHCPd.

src/dhcp6.c
src/ipv6.c
src/ipv6.h

index 7505ce0c6d408e17bd3675c3527e5619c7f79f4b..206d2fa3dd03bd386853809400ea4e1ce85a5ef6 100644 (file)
@@ -1358,11 +1358,6 @@ dhcp6_dadcallback(void *arg)
        if (!wascompleted) {
                ifp = ia->iface;
 
-               /* If not in master mode, we need to listen on each address
-                * so we can receive unicast a message. */
-               if (ia->dhcp6_fd == -1 && !(ifp->ctx->options & DHCPCD_MASTER))
-                       dhcp6_listen(ifp->ctx, ia);
-
                state = D6_STATE(ifp);
                if (state->state == DH6S_BOUND ||
                    state->state == DH6S_DELEGATED)
@@ -1710,35 +1705,6 @@ dhcp6_leaseextend(struct interface *ifp)
        }
 }
 
-static void
-dhcp6_addaddrs(struct interface *ifp)
-{
-       struct dhcp6_state *state = D6_STATE(ifp);
-       struct ipv6_addr *ia;
-
-       ipv6_addaddrs(&state->addrs);
-
-       /* If not in master mode, we need to listen on each address
-        * so we can receive unicast a message. */
-       if (ifp->ctx->options & DHCPCD_MASTER)
-               return;
-
-       state = D6_STATE(ifp);
-       TAILQ_FOREACH(ia, &state->addrs, next) {
-               if (ia->prefix_vltime == 0 ||
-                   ia->flags & IPV6_AF_STALE ||
-                   ia->addr_flags & IN6_IFF_NOTUSEABLE)
-                       continue;
-               if (ia->dhcp6_fd != -1)
-                       continue;
-               ia->dhcp6_fd = dhcp6_listen(ifp->ctx, ia);
-               if (ia->dhcp6_fd == -1)
-                       continue;
-               eloop_event_add(ifp->ctx->eloop,
-                   ia->dhcp6_fd, dhcp6_recvaddr, ia);
-       }
-}
-
 static void
 dhcp6_startexpire(void *arg)
 {
@@ -2736,7 +2702,10 @@ dhcp6_delegate_prefix(struct interface *ifp)
                                break;
                }
                if (k && !carrier_warned) {
-                       dhcp6_addaddrs(ifd);
+                       struct dhcp6_state *s = D6_STATE(ifd);
+
+                       ipv6_addaddrs(&s->addrs);
+
                        /*
                         * Can't add routes here because that will trigger
                         * interface sorting which may break the current
@@ -3040,7 +3009,7 @@ dhcp6_bind(struct interface *ifp, const char *op)
                        eloop_timeout_add_sec(ifp->ctx->eloop,
                            (time_t)state->expire, dhcp6_startdiscover, ifp);
 
-               dhcp6_addaddrs(ifp);
+               ipv6_addaddrs(&state->addrs);
                dhcp6_deprecateaddrs(&state->addrs);
 
                if (state->state == DH6S_INFORMED)
@@ -3481,10 +3450,13 @@ dhcp6_listen(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
 #ifdef BSD
        sa.sin6_len = sizeof(sa);
 #endif
+
        if (ia != NULL) {
                memcpy(&sa.sin6_addr, &ia->addr, sizeof(sa.sin6_addr));
                sa.sin6_scope_id = ia->iface->index;
-       }
+       } else if (!(ctx->options & DHCPCD_MASTER))
+               /* This socket is only used for sending. */
+               return s;
 
        if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1)
                goto errexit;
@@ -3494,12 +3466,11 @@ dhcp6_listen(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
                if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
                    &n, sizeof(n)) == -1)
                        goto errexit;
-       }
-
-       if (ia != NULL) {
+       } else {
                ia->dhcp6_fd = s;
                eloop_event_add(ctx->eloop, s, dhcp6_recvaddr, ia);
        }
+
        return s;
 
 errexit:
@@ -3512,28 +3483,13 @@ errexit:
 static int
 dhcp6_open(struct dhcpcd_ctx *ctx)
 {
-       struct ipv6_addr *ia = NULL;
-       int s;
 
-       if (!(ctx->options & DHCPCD_MASTER)) {
-               /* Bind to the link-local address to allow more than one
-                * DHCPv6 client to work. */
-               struct interface *ifp;
+       /* Open an unbound socket to send from. */
+       ctx->dhcp6_fd = dhcp6_listen(ctx, NULL);
+       if (ctx->dhcp6_fd != -1 && (ctx->options & DHCPCD_MASTER))
+               eloop_event_add(ctx->eloop, ctx->dhcp6_fd, dhcp6_recvctx, ctx);
 
-               TAILQ_FOREACH(ifp, ctx->ifaces, next) {
-                       if (!ifp->active)
-                               continue;
-                       ia = ipv6_linklocal(ifp);
-                       if (ia != NULL)
-                               break;
-               }
-       }
-       s = dhcp6_listen(ctx, ia);
-       ctx->dhcp6_fd = s;
-       if (s != -1 && ia == NULL)
-               eloop_event_add(ctx->eloop, s, dhcp6_recvctx, ctx);
-
-       return s;
+       return ctx->dhcp6_fd;
 }
 
 #ifndef SMALL
@@ -3809,6 +3765,14 @@ dhcp6_handleifa(int cmd, struct ipv6_addr *ia)
 {
        struct dhcp6_state *state;
 
+       /* If not running in master mode, listen to this address */
+       if (cmd == RTM_NEWADDR &&
+           !(ia->addr_flags & IN6_IFF_NOTUSEABLE) &&
+           ia->iface->active == IF_ACTIVE_USER &&
+           !(ia->iface->ctx->options & DHCPCD_MASTER) &&
+           ia->dhcp6_fd == -1)
+               dhcp6_listen(ia->iface->ctx, ia);
+
        if ((state = D6_STATE(ia->iface)) != NULL)
                ipv6_handleifa_addrs(cmd, &state->addrs, ia);
 }
index c98a8f34eca2b5886b5852e1ad7d821ee64d1926..b05f1d67f242eb40408526553de35cd63b958799 100644 (file)
@@ -1103,26 +1103,10 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
                break;
        case RTM_NEWADDR:
                if (ia == NULL) {
-                       char buf[INET6_ADDRSTRLEN];
-                       const char *cbp;
-
-                       if ((ia = calloc(1, sizeof(*ia))) == NULL) {
-                               logerr(__func__);
-                               break;
-                       }
+                       ia = ipv6_newaddr(ifp, addr, prefix_len, 0);
 #ifdef ALIAS_ADDR
                        strlcpy(ia->alias, ifname, sizeof(ia->alias));
 #endif
-                       ia->iface = ifp;
-                       ia->addr = *addr;
-                       ia->prefix_len = prefix_len;
-                       ipv6_makeprefix(&ia->prefix, &ia->addr,
-                           ia->prefix_len);
-                       cbp = inet_ntop(AF_INET6, &addr->s6_addr,
-                           buf, sizeof(buf));
-                       if (cbp)
-                               snprintf(ia->saddr, sizeof(ia->saddr),
-                                   "%s/%d", cbp, prefix_len);
                        if (if_getlifetime6(ia) == -1) {
                                /* No support or address vanished.
                                 * Either way, just set a deprecated
@@ -1190,10 +1174,8 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
        }
 
        if (ia != NULL) {
-               if (!IN6_IS_ADDR_LINKLOCAL(&ia->addr)) {
-                       ipv6nd_handleifa(cmd, ia);
-                       dhcp6_handleifa(cmd, ia);
-               }
+               ipv6nd_handleifa(cmd, ia);
+               dhcp6_handleifa(cmd, ia);
 
                /* Done with the ia now, so free it. */
                if (cmd == RTM_DELADDR)
@@ -1453,8 +1435,8 @@ ipv6_tryaddlinklocal(struct interface *ifp)
 }
 
 struct ipv6_addr *
-ipv6_newaddr(struct interface *ifp, struct in6_addr *addr, uint8_t prefix_len,
-    unsigned int flags)
+ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr,
+    uint8_t prefix_len, unsigned int flags)
 {
        struct ipv6_addr *ia;
        char buf[INET6_ADDRSTRLEN];
index 8fca8a71f737b08316999982e421b85722117404..156691b5e6849fcf04664bdf9bbccd5e58e12033 100644 (file)
@@ -250,7 +250,7 @@ struct ipv6_addr *ipv6_findmaskaddr(struct dhcpcd_ctx *,
     const struct in6_addr *);
 #define ipv6_linklocal(ifp) ipv6_iffindaddr((ifp), NULL, IN6_IFF_NOTUSEABLE)
 int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
-struct ipv6_addr *ipv6_newaddr(struct interface *, struct in6_addr *, uint8_t,
+struct ipv6_addr *ipv6_newaddr(struct interface *, const struct in6_addr *, uint8_t,
     unsigned int);
 void ipv6_freeaddr(struct ipv6_addr *);
 void ipv6_freedrop(struct interface *, int);