diff options
| author | Roy Marples <roy@marples.name> | 2019-01-05 11:42:12 +0000 |
|---|---|---|
| committer | Roy Marples <roy@marples.name> | 2019-01-05 11:42:12 +0000 |
| commit | 2f2db8dcf1a3acd87accc71951cae044118edcbf (patch) | |
| tree | 3d5b6596e4c07295d673ce00abb1ee266bbfedeb /src/ipv6.c | |
| parent | 7bcb18539956b473fb90a89f7f42d030fbe5ea6c (diff) | |
| download | dhcpcd-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.c | 54 |
1 files changed, 14 insertions, 40 deletions
@@ -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; |
