summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Obser <florian@cvs.openbsd.org>2018-07-23 06:14:15 +0000
committerFlorian Obser <florian@cvs.openbsd.org>2018-07-23 06:14:15 +0000
commit7a2a049403bb80e50a5be89d5ff8e2c2345b89b0 (patch)
tree536f2870a22835df31739331b0dfadbb2667c059
parent9ca8cb0a669109357766c425d569d5f96d7f7030 (diff)
Handle duplicate address detection failures.
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.
-rw-r--r--sbin/slaacd/engine.c116
-rw-r--r--sbin/slaacd/frontend.c67
-rw-r--r--sbin/slaacd/slaacd.c4
-rw-r--r--sbin/slaacd/slaacd.h8
4 files changed, 161 insertions, 34 deletions
diff --git a/sbin/slaacd/engine.c b/sbin/slaacd/engine.c
index 961e1b115b6..b2955ea2ff5 100644
--- a/sbin/slaacd/engine.c
+++ b/sbin/slaacd/engine.c
@@ -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;
+ }
+}
diff --git a/sbin/slaacd/frontend.c b/sbin/slaacd/frontend.c
index 96d69b8b661..cdbe7d11b5b 100644
--- a/sbin/slaacd/frontend.c
+++ b/sbin/slaacd/frontend.c
@@ -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)) !=
diff --git a/sbin/slaacd/slaacd.c b/sbin/slaacd/slaacd.c
index 6778659fa10..633864bf566 100644
--- a/sbin/slaacd/slaacd.c
+++ b/sbin/slaacd/slaacd.c
@@ -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)");
diff --git a/sbin/slaacd/slaacd.h b/sbin/slaacd/slaacd.h
index 910be91e687..1659c564abf 100644
--- a/sbin/slaacd/slaacd.h
+++ b/sbin/slaacd/slaacd.h
@@ -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,