summaryrefslogtreecommitdiffstats
path: root/src/ipv6.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2019-01-05 11:42:12 +0000
committerRoy Marples <roy@marples.name>2019-01-05 11:42:12 +0000
commit2f2db8dcf1a3acd87accc71951cae044118edcbf (patch)
tree3d5b6596e4c07295d673ce00abb1ee266bbfedeb /src/ipv6.c
parent7bcb18539956b473fb90a89f7f42d030fbe5ea6c (diff)
downloaddhcpcd-2f2db8dcf1a3acd87accc71951cae044118edcbf.tar.xz
ip6: Implement IPv6 address sharing
This allows the same IPv6 address to exist on more than one interface. Whenever dhcpcd address an IPv6 address, it will advertise it along with the hardware address of the preferred interface. This is heavliy reliant on the kernel supporting this as it's the kernel that handle the Duplicate Address Detection. In a nutshell it needs to support RFC 7527 and ignore NA packets from any hardware address the host owns. Currently the only known kernel that fully supports this is NetBSD-8.99.27
Diffstat (limited to 'src/ipv6.c')
-rw-r--r--src/ipv6.c54
1 files changed, 14 insertions, 40 deletions
diff --git a/src/ipv6.c b/src/ipv6.c
index 49315d14..38cb06d8 100644
--- a/src/ipv6.c
+++ b/src/ipv6.c
@@ -626,32 +626,19 @@ ipv6_deleteaddr(struct ipv6_addr *ia)
break;
}
}
+
+ /* Advertise the address if it exists on another interface. */
+ ipv6nd_advertise(ia);
}
static int
ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
{
struct interface *ifp;
- struct ipv6_state *state;
- struct ipv6_addr *ia2;
uint32_t pltime, vltime;
+ bool vltime_was_zero;
__printflike(1, 2) void (*logfunc)(const char *, ...);
- /* Ensure no other interface has this address */
- TAILQ_FOREACH(ifp, ia->iface->ctx->ifaces, next) {
- if (ifp == ia->iface)
- continue;
- state = IPV6_STATE(ifp);
- if (state == NULL)
- continue;
- TAILQ_FOREACH(ia2, &state->addrs, next) {
- if (IN6_ARE_ADDR_EQUAL(&ia2->addr, &ia->addr)) {
- ipv6_deleteaddr(ia2);
- break;
- }
- }
- }
-
/* Remember the interface of the address. */
ifp = ia->iface;
@@ -714,7 +701,7 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
" seconds",
ifp->name, ia->prefix_pltime, ia->prefix_vltime);
-
+ vltime_was_zero = ia->prefix_vltime == 0;
if (if_address6(RTM_NEWADDR, ia) == -1) {
logerr(__func__);
/* Restore real pltime and vltime */
@@ -778,6 +765,10 @@ ipv6_addaddr1(struct ipv6_addr *ia, const struct timespec *now)
}
#endif
+ /* Re-advertise the preferred address to be safe. */
+ if (!vltime_was_zero)
+ ipv6nd_advertise(ia);
+
return 0;
}
@@ -909,7 +900,7 @@ ipv6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr, unsigned int
ssize_t
ipv6_addaddrs(struct ipv6_addrhead *addrs)
{
- struct ipv6_addr *ap, *apn, *apf;
+ struct ipv6_addr *ap, *apn;
ssize_t i;
struct timespec now;
@@ -935,27 +926,6 @@ ipv6_addaddrs(struct ipv6_addrhead *addrs)
} else if (!(ap->flags & IPV6_AF_STALE) &&
!IN6_IS_ADDR_UNSPECIFIED(&ap->addr))
{
- apf = ipv6_findaddr(ap->iface->ctx,
- &ap->addr, IPV6_AF_ADDED);
- if (apf && apf->iface != ap->iface) {
- if (apf->iface->metric <= ap->iface->metric) {
- loginfox("%s: preferring %s on %s",
- ap->iface->name,
- ap->saddr,
- apf->iface->name);
- continue;
- }
- loginfox("%s: preferring %s on %s",
- apf->iface->name,
- ap->saddr,
- ap->iface->name);
- if (if_address6(RTM_DELADDR, apf) == -1 &&
- errno != EADDRNOTAVAIL && errno != ENXIO)
- logerr(__func__);
- apf->flags &=
- ~(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED);
- } else if (apf)
- apf->flags &= ~IPV6_AF_ADDED;
if (ap->flags & IPV6_AF_NEW)
i++;
if (!timespecisset(&now))
@@ -989,6 +959,7 @@ ipv6_freeaddr(struct ipv6_addr *ia)
}
eloop_q_timeout_delete(ia->iface->ctx->eloop, 0, NULL, ia);
+ free(ia->na);
free(ia);
}
@@ -1108,6 +1079,9 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
case RTM_DELADDR:
if (ia != NULL) {
TAILQ_REMOVE(&state->addrs, ia, next);
+ /* Advertise the address if it exists on
+ * another interface. */
+ ipv6nd_advertise(ia);
/* We'll free it at the end of the function. */
}
break;