Handle duplicate address detection failures.
authorflorian <florian@openbsd.org>
Mon, 23 Jul 2018 06:14:14 +0000 (06:14 +0000)
committerflorian <florian@openbsd.org>
Mon, 23 Jul 2018 06:14:14 +0000 (06:14 +0000)
We get notified when duplication is detected on the route socket. For
privacy addresses simply generate a new random address. If we have
soii enabled increase the dad counter on the prefix and generate a new
address. For eui64 addresses nothing can be done.

sbin/slaacd/engine.c
sbin/slaacd/frontend.c
sbin/slaacd/slaacd.c
sbin/slaacd/slaacd.h

index 961e1b1..b2955ea 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: engine.c,v 1.27 2018/06/20 14:55:29 florian Exp $     */
+/*     $OpenBSD: engine.c,v 1.28 2018/07/23 06:14:14 florian Exp $     */
 
 /*
  * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
@@ -106,6 +106,7 @@ enum proposal_state {
        PROPOSAL_CONFIGURED,
        PROPOSAL_NEARLY_EXPIRED,
        PROPOSAL_WITHDRAWN,
+       PROPOSAL_DUPLICATED,
 };
 
 const char* proposal_state_name[] = {
@@ -114,6 +115,7 @@ const char* proposal_state_name[] = {
        "CONFIGURED",
        "NEARLY_EXPIRED",
        "WITHDRAWN",
+       "DUPLICATED",
 };
 
 const char* rpref_name[] = {
@@ -130,6 +132,7 @@ struct radv_prefix {
        int                     autonomous;
        uint32_t                vltime;
        uint32_t                pltime;
+       int                     dad_counter;
 };
 
 struct radv_rdns {
@@ -261,11 +264,10 @@ struct dfr_proposal       *find_dfr_proposal_by_id(struct slaacd_iface *,
                             int64_t);
 struct dfr_proposal    *find_dfr_proposal_by_gw(struct slaacd_iface *,
                             struct sockaddr_in6 *);
-void                    find_prefix(struct slaacd_iface *, struct
-                            address_proposal *, struct radv **, struct
-                            radv_prefix **);
+struct radv_prefix     *find_prefix(struct radv *, struct radv_prefix *);
 int                     engine_imsg_compose_main(int, pid_t, void *, uint16_t);
 uint32_t                real_lifetime(struct timespec *, uint32_t);
+void                    merge_dad_couters(struct radv *, struct radv *);
 
 struct imsgev          *iev_frontend;
 struct imsgev          *iev_main;
@@ -392,6 +394,8 @@ engine_dispatch_frontend(int fd, short event, void *bula)
        struct dfr_proposal             *dfr_proposal = NULL;
        struct imsg_del_addr             del_addr;
        struct imsg_del_route            del_route;
+       struct imsg_dup_addr             dup_addr;
+       struct timeval                   tv;
        ssize_t                          n;
        int                              shut = 0;
 #ifndef        SMALL
@@ -546,6 +550,31 @@ engine_dispatch_frontend(int fd, short event, void *bula)
                                start_probe(iface);
                        }
                        break;
+               case IMSG_DUP_ADDRESS:
+                       if (imsg.hdr.len != IMSG_HEADER_SIZE +
+                           sizeof(dup_addr))
+                               fatal("%s: IMSG_DUP_ADDRESS wrong length: %d",
+                                   __func__, imsg.hdr.len);
+                       memcpy(&dup_addr, imsg.data, sizeof(dup_addr));
+                       iface = get_slaacd_iface_by_id(dup_addr.if_index);
+                       if (iface == NULL) {
+                               log_debug("IMSG_DUP_ADDRESS: unknown interface"
+                                   ", ignoring");
+                               break;
+                       }
+
+                       addr_proposal = find_address_proposal_by_addr(iface,
+                           &dup_addr.addr);
+
+                       if (addr_proposal) {
+                               /* XXX should we inform netcfgd? */
+                               addr_proposal->state = PROPOSAL_DUPLICATED;
+                               tv.tv_sec = 0;
+                               tv.tv_usec = arc4random_uniform(1000000);
+                               addr_proposal->next_timeout = 0;
+                               evtimer_add(&addr_proposal->timer, &tv);
+                       }
+                       break;
                default:
                        log_debug("%s: unexpected imsg %d", __func__,
                            imsg.hdr.type);
@@ -1261,7 +1290,6 @@ gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct
 {
        SHA2_CTX ctx;
        struct in6_addr iid;
-       int dad_counter = 0; /* XXX not used */
        int i;
        u_int8_t digest[SHA512_DIGEST_LENGTH];
 
@@ -1293,7 +1321,8 @@ gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct
                    sizeof(prefix->prefix));
                SHA512Update(&ctx, &iface->hw_address,
                    sizeof(iface->hw_address));
-               SHA512Update(&ctx, &dad_counter, sizeof(dad_counter));
+               SHA512Update(&ctx, &prefix->dad_counter,
+                   sizeof(prefix->dad_counter));
                SHA512Update(&ctx, addr_proposal->soiikey,
                    sizeof(addr_proposal->soiikey));
                SHA512Final(digest, &ctx);
@@ -1601,13 +1630,16 @@ void update_iface_ra(struct slaacd_iface *iface, struct radv *ra)
        struct address_proposal *addr_proposal;
        struct dfr_proposal     *dfr_proposal, *tmp;
        uint32_t                 remaining_lifetime;
-       int                      found, found_privacy;
+       int                      found, found_privacy, duplicate_found;
        const char              *hbuf;
 
        if ((old_ra = find_ra(iface, &ra->from)) == NULL)
                LIST_INSERT_HEAD(&iface->radvs, ra, entries);
        else {
                LIST_REPLACE(old_ra, ra, entries);
+
+               merge_dad_couters(old_ra, ra);
+
                free_ra(old_ra);
        }
        if (ra->router_lifetime == 0) {
@@ -1674,6 +1706,8 @@ void update_iface_ra(struct slaacd_iface *iface, struct radv *ra)
                                continue;
                        found = 0;
                        found_privacy = 0;
+                       duplicate_found = 0;
+
                        LIST_FOREACH(addr_proposal, &iface->addr_proposals,
                            entries) {
                                if (prefix->prefix_len ==
@@ -1699,7 +1733,9 @@ void update_iface_ra(struct slaacd_iface *iface, struct radv *ra)
                                         * expires
                                         */
                                        if (addr_proposal->state !=
-                                           PROPOSAL_NEARLY_EXPIRED)
+                                           PROPOSAL_NEARLY_EXPIRED &&
+                                           addr_proposal->state !=
+                                           PROPOSAL_DUPLICATED)
                                                found_privacy = 1;
 
                                        if (!iface->autoconfprivacy)
@@ -1715,6 +1751,12 @@ void update_iface_ra(struct slaacd_iface *iface, struct radv *ra)
                                        continue;
                                }
 
+                               if (addr_proposal->state ==
+                                   PROPOSAL_DUPLICATED) {
+                                       duplicate_found = 1;
+                                       continue;
+                               }
+
                                found = 1;
 
                                remaining_lifetime =
@@ -1751,6 +1793,12 @@ void update_iface_ra(struct slaacd_iface *iface, struct radv *ra)
                                }
                        }
 
+                       if (!found && duplicate_found && iface->soii) {
+                               prefix->dad_counter++;
+                               log_debug("%s dad_counter: %d",
+                                    __func__, prefix->dad_counter);
+                       }
+
                        if (!found &&
                            (iface->soii || prefix->prefix_len <= 64))
                                /* new proposal */
@@ -2117,6 +2165,13 @@ address_proposal_timeout(int fd, short events, void *arg)
                log_debug("%s: scheduling new timeout in %llds.%06ld",
                    __func__, tv.tv_sec, tv.tv_usec);
                break;
+       case PROPOSAL_DUPLICATED:
+               engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION,
+                   0, &addr_proposal->if_index,
+                   sizeof(addr_proposal->if_index));
+               log_debug("%s: address duplicated",
+                   __func__);
+               break;
        default:
                log_debug("%s: unhandled state: %s", __func__,
                    proposal_state_name[addr_proposal->state]);
@@ -2304,32 +2359,19 @@ find_dfr_proposal_by_gw(struct slaacd_iface *iface, struct sockaddr_in6
 }
 
 
-/* XXX currently unused */
-void
-find_prefix(struct slaacd_iface *iface, struct address_proposal *addr_proposal,
-    struct radv **result_ra, struct radv_prefix **result_prefix)
+struct radv_prefix *
+find_prefix(struct radv *ra, struct radv_prefix *prefix)
 {
-       struct radv             *ra;
-       struct radv_prefix      *prefix;
-       uint32_t                 lifetime, max_lifetime = 0;
+       struct radv_prefix      *result;
 
-       *result_ra = NULL;
-       *result_prefix = NULL;
 
-       LIST_FOREACH(ra, &iface->radvs, entries) {
-               LIST_FOREACH(prefix, &ra->prefixes, entries) {
-                       if (memcmp(&prefix->prefix, &addr_proposal->prefix,
-                           sizeof(addr_proposal->prefix)) != 0)
-                               continue;
-                       lifetime = real_lifetime(&ra->uptime,
-                           prefix->vltime);
-                       if (lifetime > max_lifetime) {
-                               max_lifetime = lifetime;
-                               *result_ra = ra;
-                               *result_prefix = prefix;
-                       }
-               }
+       LIST_FOREACH(result, &ra->prefixes, entries) {
+               if (memcmp(&result->prefix, &prefix->prefix,
+                   sizeof(prefix->prefix)) == 0 && result->prefix_len ==
+                   prefix->prefix_len)
+                       return (result);
        }
+       return (NULL);
 }
 
 uint32_t
@@ -2350,3 +2392,17 @@ real_lifetime(struct timespec *received_uptime, uint32_t ltime)
 
        return (remaining);
 }
+
+void
+merge_dad_couters(struct radv *old_ra, struct radv *new_ra)
+{
+
+       struct radv_prefix      *old_prefix, *new_prefix;
+
+       LIST_FOREACH(old_prefix, &old_ra->prefixes, entries) {
+               if (!old_prefix->dad_counter)
+                       continue;
+               if ((new_prefix = find_prefix(new_ra, old_prefix)) != NULL)
+                       new_prefix->dad_counter = old_prefix->dad_counter;
+       }
+}
index 96d69b8..cdbe7d1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: frontend.c,v 1.20 2018/06/06 14:08:28 florian Exp $   */
+/*     $OpenBSD: frontend.c,v 1.21 2018/07/23 06:14:14 florian Exp $   */
 
 /*
  * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
@@ -73,6 +73,7 @@ void           get_lladdr(char *, struct ether_addr *, struct sockaddr_in6 *);
 void            send_solicitation(uint32_t);
 #ifndef        SMALL
 void            update_autoconf_addresses(uint32_t, char*);
+const char     *flags_to_str(int);
 #endif /* SMALL */
 
 struct imsgev                  *iev_main;
@@ -592,6 +593,31 @@ update_autoconf_addresses(uint32_t if_index, char* if_name)
        }
        freeifaddrs(ifap);
 }
+
+const char*
+flags_to_str(int flags)
+{
+       static char     buf[sizeof(" anycast tentative duplicated detached "
+                           "deprecated autoconf autoconfprivacy")];
+
+       buf[0] = '\0';
+       if (flags & IN6_IFF_ANYCAST)
+               (void)strlcat(buf, " anycast", sizeof(buf));
+       if (flags & IN6_IFF_TENTATIVE)
+               (void)strlcat(buf, " tentative", sizeof(buf));
+       if (flags & IN6_IFF_DUPLICATED)
+               (void)strlcat(buf, " duplicated", sizeof(buf));
+       if (flags & IN6_IFF_DETACHED)
+               (void)strlcat(buf, " detached", sizeof(buf));
+       if (flags & IN6_IFF_DEPRECATED)
+               (void)strlcat(buf, " deprecated", sizeof(buf));
+       if (flags & IN6_IFF_AUTOCONF)
+               (void)strlcat(buf, " autoconf", sizeof(buf));
+       if (flags & IN6_IFF_PRIVACY)
+               (void)strlcat(buf, " autoconfprivacy", sizeof(buf));
+
+       return (buf);
+}
 #endif /* SMALL */
 
 void
@@ -673,7 +699,10 @@ handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
        struct imsg_proposal_ack         proposal_ack;
        struct imsg_del_addr             del_addr;
        struct imsg_del_route            del_route;
+       struct imsg_dup_addr             dup_addr;
        struct sockaddr_rtlabel         *rl;
+       struct sockaddr_in6             *sin6;
+       struct in6_ifreq                 ifr6;
        struct in6_addr                 *in6;
        int64_t                          id, pid;
        int                              xflags, if_index;
@@ -728,6 +757,42 @@ handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
                            ifm->ifm_index);
                }
                break;
+       case RTM_CHGADDRATTR:
+               ifm = (struct if_msghdr *)rtm;
+               if_name = if_indextoname(ifm->ifm_index, ifnamebuf);
+               if (rtm->rtm_addrs & RTA_IFA && rti_info[RTAX_IFA]->sa_family
+                   == AF_INET6) {
+                       sin6 = (struct sockaddr_in6 *) rti_info[RTAX_IFA];
+
+                       if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+                               break;
+
+                       memset(&ifr6, 0, sizeof(ifr6));
+                       (void) strlcpy(ifr6.ifr_name, if_name,
+                           sizeof(ifr6.ifr_name));
+                       memcpy(&ifr6.ifr_addr, sin6, sizeof(ifr6.ifr_addr));
+
+                       if (ioctl(ioctlsock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6)
+                           < 0) {
+                               log_warn("SIOCGIFAFLAG_IN6");
+                               break;
+                       }
+
+#ifndef        SMALL
+                       log_debug("RTM_CHGADDRATTR: %s -%s",
+                           sin6_to_str(sin6),
+                           flags_to_str(ifr6.ifr_ifru.ifru_flags6));
+#endif /* SMALL */
+
+                       if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED) {
+                               dup_addr.if_index = ifm->ifm_index;
+                               dup_addr.addr = *sin6;
+                               frontend_imsg_compose_engine(IMSG_DUP_ADDRESS,
+                                   0, 0, &dup_addr, sizeof(dup_addr));
+                       }
+
+               }
+               break;
        case RTM_DELETE:
                ifm = (struct if_msghdr *)rtm;
                if ((rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY | RTA_LABEL)) !=
index 6778659..633864b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: slaacd.c,v 1.25 2018/07/09 07:41:15 florian Exp $     */
+/*     $OpenBSD: slaacd.c,v 1.26 2018/07/23 06:14:14 florian Exp $     */
 
 /*
  * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
@@ -275,7 +275,7 @@ main(int argc, char *argv[])
 
        rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_NEWADDR) |
            ROUTE_FILTER(RTM_DELADDR) | ROUTE_FILTER(RTM_PROPOSAL) |
-           ROUTE_FILTER(RTM_DELETE);
+           ROUTE_FILTER(RTM_DELETE) | ROUTE_FILTER(RTM_CHGADDRATTR);
        if (setsockopt(frontend_routesock, PF_ROUTE, ROUTE_MSGFILTER,
            &rtfilter, sizeof(rtfilter)) < 0)
                fatal("setsockopt(ROUTE_MSGFILTER)");
index 910be91..1659c56 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: slaacd.h,v 1.17 2018/07/09 07:41:15 florian Exp $     */
+/*     $OpenBSD: slaacd.h,v 1.18 2018/07/23 06:14:14 florian Exp $     */
 
 /*
  * Copyright (c) 2017 Florian Obser <florian@openbsd.org>
@@ -74,6 +74,7 @@ enum imsg_type {
        IMSG_FAKE_ACK,
        IMSG_CONFIGURE_DFR,
        IMSG_WITHDRAW_DFR,
+       IMSG_DUP_ADDRESS,
 };
 
 enum {
@@ -202,6 +203,11 @@ struct imsg_ra {
        uint8_t                 packet[1500];
 };
 
+struct imsg_dup_addr {
+       uint32_t                if_index;
+       struct sockaddr_in6     addr;
+};
+
 /* slaacd.c */
 void           imsg_event_add(struct imsgev *);
 int            imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t,