char *cp;
struct sockaddr *sa, *rti_info[RTAX_MAX];
struct sockaddr_dl *sdl;
+ struct sockaddr_in6 *sin;
+ struct ipv6_addr *ia;
cp = (char *)(void *)(rtm + 1);
sa = (struct sockaddr *)(void *)cp;
sdl = (struct sockaddr_dl *)(void *)rti_info[RTAX_IFP];
rt->iface = if_findsdl(ctx, sdl);
} else if (rtm->rtm_addrs & RTA_GATEWAY) {
- sdl = (struct sockaddr_dl *)(void *)rti_info[RTAX_GATEWAY];
- if (sdl->sdl_family == AF_LINK)
+ sa = rti_info[RTAX_GATEWAY];
+ switch (sa->sa_family) {
+ case AF_LINK:
+ sdl = (void *)sa;
rt->iface = if_findsdl(ctx, sdl);
+ break;
+ case AF_INET6:
+ sin = (void *)sa;
+ if ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))
+ rt->iface = ia->iface;
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ logger(ctx, LOG_ERR, "%s: %m", __func__);
+ return -1;
+ }
}
/* If we don't have an interface and it's a host route, it maybe
if (rt->iface == NULL &&
!(~rtm->rtm_flags & (RTF_HOST | RTF_GATEWAY)))
{
- struct ipv6_addr *ia;
-
if ((ia = ipv6_findaddr(ctx, &rt->dest, 0)))
rt->iface = ia->iface;
}
return NULL;
}
+static struct ipv6_addr *
+ipv6_iffindmaskaddr(const struct interface *ifp, const struct in6_addr *addr)
+{
+ struct ipv6_state *state;
+ struct ipv6_addr *ap;
+ struct in6_addr mask;
+
+ state = IPV6_STATE(ifp);
+ if (state) {
+ TAILQ_FOREACH(ap, &state->addrs, next) {
+ if (ipv6_mask(&mask, ap->prefix_len) == -1)
+ continue;
+ if (IN6_ARE_MASKED_ADDR_EQUAL(&ap->addr, addr, &mask))
+ return ap;
+ }
+ }
+ return NULL;
+}
+
+struct ipv6_addr *
+ipv6_findmaskaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr)
+{
+ struct interface *ifp;
+ struct ipv6_addr *ap;
+
+ TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+ ap = ipv6_iffindmaskaddr(ifp, addr);
+ if (ap != NULL)
+ return ap;
+ }
+ return NULL;
+}
+
int
ipv6_addlinklocalcallback(struct interface *ifp,
void (*callback)(void *), void *arg)
#define IDGEN_RETRIES 3
#define IDGEN_DELAY 1 /* second */
+#ifndef IN6_ARE_MASKED_ADDR_EQUAL
+#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \
+ (((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \
+ (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \
+ (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \
+ (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 )
+#endif
+
/*
* BSD kernels don't inform userland of DAD results.
* See the discussion here:
short);
struct ipv6_addr *ipv6_findaddr(struct dhcpcd_ctx *,
const struct in6_addr *, short);
+struct ipv6_addr *ipv6_findmaskaddr(struct dhcpcd_ctx *,
+ const struct in6_addr *);
#define ipv6_linklocal(ifp) ipv6_iffindaddr((ifp), NULL)
int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
void ipv6_freeaddr(struct ipv6_addr *);