This fixes RTM address handling on FreeBSD and OpenBSD.
Instead of listening for duplicate address messages for
FreeBSD and OpenBSD simply poll for the address flag changing.
This means less code to maintain and hopefully less error-prone.
Remove dead code which send a DAD as this is, and should,
be done entirely in the kernel.
# Work with cpu-kernel-os, ie Debian
case "$VENDOR" in
- linux|kfreebsd) OS=$VENDOR; VENDOR= ;;
+ linux*|kfreebsd*) OS=$VENDOR; VENDOR= ;;
esac
# Special case
case "$OS" in
- gnu) OS=hurd;; # No HURD support as yet
+ gnu*) OS=hurd;; # No HURD support as yet
esac
fi
CFLAGS+= -Wdeclaration-after-statement
EOF
case "$OS" in
- openbsd) ;; # OpenBSD has many redundant decs in system headers
+ openbsd*) ;; # OpenBSD has many redundant decs in system headers
*) echo "CFLAGS+= -Wredundant-decls" >>$CONFIG_MK;;
esac
fi
fi
case "$OS" in
-linux)
+linux*)
echo "CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=700" >>$CONFIG_MK
if [ -z "$INET" -o "$INET" = yes ]; then
echo "DHCPCD_SRCS+= lpf.c" >>$CONFIG_MK
echo "DHCPCD_SRCS+= platform-linux.c" >>$CONFIG_MK
echo "LDADD+= -lrt -ldl" >>$CONFIG_MK
;;
-kfreebsd)
+kfreebsd*)
echo "CPPFLAGS+= -D_GNU_SOURCE" >>$CONFIG_MK
if [ -z "$INET" -o "$INET" = yes ]; then
echo "DHCPCD_SRCS+= bpf.c" >>$CONFIG_MK
EOF
if $XCC _posix_spawn.c -o _posix_spawn 2>/dev/null; then
case "$OS" in
- openbsd) printf "broken OpenBSD ... "; POSIX_SPAWN=no;;
+ openbsd*) printf "broken OpenBSD ... "; POSIX_SPAWN=no;;
*) POSIX_SPAWN=yes;;
esac
else
int wascompleted;
wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
- ipv6nd_cancelprobeaddr(ap);
ap->flags |= IPV6_AF_DADCOMPLETED;
if (ap->flags & IPV6_AF_DUPLICATED)
/* XXX FIXME
* We should decline the address */
syslog(LOG_WARNING, "%s: DAD detected %s",
ap->iface->name, ap->saddr);
-#ifdef IPV6_SEND_DAD
- else
- ipv6_addaddr(ap);
-#endif
if (!wascompleted) {
ifp = ap->iface;
}
if (k && !carrier_warned) {
ifd_state = D6_STATE(ifd);
- ipv6nd_probeaddrs(&ifd_state->addrs);
+ ipv6_addaddrs(&ifd_state->addrs);
}
}
syslog(LOG_INFO, "%s: adding delegated prefixes", ifp->name);
state = D6_STATE(ifp);
state->state = DH6S_DELEGATED;
- ipv6nd_probeaddrs(&state->addrs);
+ ipv6_addaddrs(&state->addrs);
ipv6_buildroutes(ifp->ctx);
}
return k;
if (ifp->options->ia_type == D6_OPTION_IA_PD)
dhcp6_delegate_prefix(ifp);
- ipv6nd_probeaddrs(&state->addrs);
+ ipv6_addaddrs(&state->addrs);
if (state->state == DH6S_INFORMED)
syslog(has_new ? LOG_INFO : LOG_DEBUG,
"%s: refresh in %"PRIu32" seconds",
char *bp = rtm.buffer;
size_t l;
int s, retval;
- const struct ipv6_addr_l *lla;
+ const struct ipv6_addr *lla;
if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
return -1;
#ifdef INET
struct rt rt;
#endif
-#if defined(INET6) && !defined(LISTEN_DAD)
+#ifdef INET6
struct in6_addr ia6;
struct sockaddr_in6 *sin6;
int ifa_flags;
&rt.dest, &rt.net, &rt.gate);
break;
#endif
-#if defined(INET6) && !defined(LISTEN_DAD)
+#ifdef INET6
case AF_INET6:
sin6 = (struct sockaddr_in6*)(void *)
rti_info[RTAX_IFA];
if (ifs == NULL)
ifs = ctx->ifaces;
- if (ifs == NULL)
+ if (ifs == NULL) {
+ errno = ESRCH;
return;
- if (addr->s_addr == INADDR_ANY)
+ }
+ if (addr->s_addr == INADDR_ANY) {
+ errno = EINVAL;
return;
+ }
TAILQ_FOREACH(ifp, ifs, next) {
if (strcmp(ifp->name, ifname) == 0)
break;
}
- if (ifp == NULL)
+ if (ifp == NULL) {
+ errno = ESRCH;
return;
-
+ }
state = ipv4_getstate(ifp);
- if (state == NULL)
+ if (state == NULL) {
+ errno = ENOENT;
return;
+ }
+
ap = ipv4_findaddr(ifp, addr, net);
if (type == RTM_NEWADDR && ap == NULL) {
ap = malloc(sizeof(*ap));
/* Match Linux defines to BSD */
# define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)
# define IN6_IFF_DUPLICATED IFA_F_DADFAILED
+# define IN6_IFF_DETACHED 0
#else
# include <net/if.h>
#ifdef __FreeBSD__ /* Needed so that including netinet6/in6_var.h works */
#include "ipv6.h"
#include "ipv6nd.h"
+#ifdef IPV6_POLLADDRFLAG
+# warning kernel does not report IPv6 address flag changes
+# warning polling tentative address flags periodically instead
+#endif
+
+#define IN6_IFF_NOTUSEABLE \
+ (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED)
+
/* Hackery at it's finest. */
#ifndef s6_addr32
# define s6_addr32 __u6_addr.__u6_addr32
#endif
-#define EUI64_GBIT 0x01
-#define EUI64_UBIT 0x02
-#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } \
- while (/*CONSTCOND*/ 0)
-#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT)
-
-
struct ipv6_ctx *
ipv6_init(struct dhcpcd_ctx *dhcpcd_ctx)
{
ctx->nd_fd = -1;
ctx->dhcp_fd = -1;
+#ifdef IPV6_POLLADDRFLAG
+ if (!ctx->polladdr_warned) {
+ syslog(LOG_WARNING,
+ "kernel does not report IPv6 address flag changes");
+ syslog(LOG_WARNING,
+ "polling tentative address flags periodically instead");
+ ctx->polladdr_warned = 1;
+ }
+#endif
+
dhcpcd_ctx->ipv6 = ctx;
return ctx;
}
ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp,
const struct in6_addr *prefix, int prefix_len)
{
- const struct ipv6_addr_l *ap;
-#if 0
- static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- static u_int8_t allone[8] =
- { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-#endif
+ const struct ipv6_addr *ap;
if (prefix_len < 0 || prefix_len > 64) {
errno = EINVAL;
return -1;
}
-
- memcpy(addr, prefix, sizeof(*prefix));
-
- /* Try and make the address from the first local-link address */
- ap = ipv6_linklocal(ifp);
- if (ap) {
- addr->s6_addr32[2] = ap->addr.s6_addr32[2];
- addr->s6_addr32[3] = ap->addr.s6_addr32[3];
- return 0;
- }
-
- /* Because we delay a few functions until we get a local-link address
- * there is little point in the below code.
- * It exists in-case we need to create local-link addresses
- * ourselves, but then we would need to be able to send RFC
- * conformant DAD requests.
- * See ipv6ns.c for why we need the kernel to do this. */
- errno = ENOENT;
- return -1;
-
-#if 0
- /* Make an EUI64 based off our hardware address */
- switch (ifp->family) {
- case ARPHRD_ETHER:
- /* Check for a valid hardware address */
- if (ifp->hwlen != 8 && ifp->hwlen != 6) {
- errno = ENOTSUP;
- return -1;
- }
- if (memcmp(ifp->hwaddr, allzero, ifp->hwlen) == 0 ||
- memcmp(ifp->hwaddr, allone, ifp->hwlen) == 0)
- {
- errno = EINVAL;
- return -1;
- }
-
- /* make a EUI64 address */
- if (ifp->hwlen == 8)
- memcpy(&addr->s6_addr[8], ifp->hwaddr, 8);
- else if (ifp->hwlen == 6) {
- addr->s6_addr[8] = ifp->hwaddr[0];
- addr->s6_addr[9] = ifp->hwaddr[1];
- addr->s6_addr[10] = ifp->hwaddr[2];
- addr->s6_addr[11] = 0xff;
- addr->s6_addr[12] = 0xfe;
- addr->s6_addr[13] = ifp->hwaddr[3];
- addr->s6_addr[14] = ifp->hwaddr[4];
- addr->s6_addr[15] = ifp->hwaddr[5];
- }
- break;
- default:
- errno = ENOTSUP;
- return -1;
- }
-
- /* sanity check: g bit must not indicate "group" */
- if (EUI64_GROUP(addr)) {
- errno = EINVAL;
- return -1;
- }
-
- EUI64_TO_IFID(addr);
-
- /* sanity check: ifid must not be all zero, avoid conflict with
- * subnet router anycast */
- if ((addr->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 &&
- memcmp(&addr->s6_addr[9], allzero, 7) == 0)
- {
- errno = EINVAL;
+ if ((ap = ipv6_linklocal(ifp)) == NULL) {
+ /* We delay a few functions until we get a local-link address
+ * so this should never be hit. */
+ errno = ENOENT;
return -1;
}
+ /* Make the address from the first local-link address */
+ memcpy(addr, prefix, sizeof(*prefix));
+ addr->s6_addr32[2] = ap->addr.s6_addr32[2];
+ addr->s6_addr32[3] = ap->addr.s6_addr32[3];
return 0;
-#endif
}
int
return 0;
}
-#ifdef LISTEN_DAD
+#ifdef IPV6_POLLADDRFLAG
void
ipv6_checkaddrflags(void *arg)
{
syslog(LOG_DEBUG,
"%s: vltime %"PRIu32" seconds, pltime %"PRIu32" seconds",
ap->iface->name, ap->prefix_vltime, ap->prefix_pltime);
+
+#ifdef IPV6_POLLADDRFLAG
+ eloop_timeout_delete(ap->iface->ctx->eloop,
+ ipv6_checkaddrflags, ap);
+ if (!(ap->flags & IPV6_AF_DADCOMPLETED)) {
+ struct timeval tv;
+
+ ms_to_tv(&tv, RETRANS_TIMER / 2);
+ eloop_timeout_add_tv(ap->iface->ctx->eloop,
+ &tv, ipv6_checkaddrflags, ap);
+ }
+#endif
+
return 0;
}
+ssize_t
+ipv6_addaddrs(struct ipv6_addrhead *addrs)
+{
+ struct ipv6_addr *ap, *apn;
+ ssize_t i;
+
+ i = 0;
+ TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
+ if (ap->prefix_vltime == 0) {
+ TAILQ_REMOVE(addrs, ap, next);
+ if (ap->flags & IPV6_AF_ADDED) {
+ syslog(LOG_INFO, "%s: deleting address %s",
+ ap->iface->name, ap->saddr);
+ i++;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr) &&
+ del_address6(ap) == -1 &&
+ errno != EADDRNOTAVAIL && errno != ENXIO)
+ syslog(LOG_ERR, "del_address6 %m");
+ }
+ eloop_q_timeout_delete(ap->iface->ctx->eloop,
+ 0, NULL, ap);
+ free(ap);
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr)) {
+ if (ap->flags & IPV6_AF_NEW)
+ i++;
+ ipv6_addaddr(ap);
+ }
+ }
+
+ return i;
+}
+
+
void
ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop,
const struct interface *ifd)
{
struct ipv6_addr *ap, *apn;
-#ifdef LISTEN_DAD
- struct ipv6_state *state;
- struct ipv6_addr_l *lap;
-#endif
TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
if (ifd && ap->delegating_iface != ifd)
if (del_address6(ap) == -1 &&
errno != EADDRNOTAVAIL && errno != ENXIO)
syslog(LOG_ERR, "del_address6 %m");
-
-#ifdef LISTEN_DAD
- /* Remove it from our internal state as we cannot
- * reliably listen to RTM messages */
- state = IPV6_STATE(ap->iface);
- if (state) {
- TAILQ_FOREACH(lap, &state->addrs, next) {
- if (IN6_ARE_ADDR_EQUAL(&lap->addr,
- &ap->addr))
- {
- TAILQ_REMOVE(&state->addrs,
- lap, next);
- free(lap);
- break;
- }
- }
- }
-#endif
}
free(ap);
}
{
struct interface *ifp;
struct ipv6_state *state;
- struct ipv6_addr_l *ap;
+ struct ipv6_addr *ap;
struct ll_callback *cb;
#if 0
ifname, cmd, buf, flags);
#endif
- /* Safety, remove tentative addresses */
- if (cmd == RTM_NEWADDR) {
- if (flags & (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED))
- cmd = RTM_DELADDR;
-#ifdef IN6_IFF_DETACHED
- if (flags & IN6_IFF_DETACHED)
- cmd = RTM_DELADDR;
-#endif
- }
-
if (ifs == NULL)
ifs = ctx->ifaces;
if (ifs == NULL) {
errno = ESRCH;
return;
}
-
state = ipv6_getstate(ifp);
- if (state == NULL)
+ if (state == NULL) {
+ errno = ENOENT;
return;
+ }
if (!IN6_IS_ADDR_LINKLOCAL(addr)) {
ipv6nd_handleifa(ctx, cmd, ifname, addr, flags);
dhcp6_handleifa(ctx, cmd, ifname, addr, flags);
}
- /* We don't care about duplicated addresses, so remove them */
- if (flags & IN6_IFF_DUPLICATED)
- cmd = RTM_DELADDR;
-
TAILQ_FOREACH(ap, &state->addrs, next) {
if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
break;
case RTM_NEWADDR:
if (ap == NULL) {
ap = calloc(1, sizeof(*ap));
+ ap->iface = ifp;
memcpy(ap->addr.s6_addr, addr->s6_addr,
sizeof(ap->addr.s6_addr));
TAILQ_INSERT_TAIL(&state->addrs,
ap, next);
+ }
+ ap->addr_flags = flags;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&ap->addr)) {
+#ifdef IPV6_POLLADDRFLAG
+ if (ap->addr_flags & IN6_IFF_TENTATIVE) {
+ struct timeval tv;
- if (IN6_IS_ADDR_LINKLOCAL(&ap->addr)) {
+ ms_to_tv(&tv, RETRANS_TIMER / 2);
+ eloop_timeout_add_tv(ap->iface->ctx->eloop,
+ &tv, ipv6_checkaddrflags, ap);
+ break;
+ }
+#endif
+
+ if (!(ap->addr_flags & IN6_IFF_NOTUSEABLE)) {
/* Now run any callbacks.
* Typically IPv6RS or DHCPv6 */
- while ((cb =
- TAILQ_FIRST(&state->ll_callbacks)))
+ while ((cb = TAILQ_FIRST(&state->ll_callbacks)))
{
TAILQ_REMOVE(&state->ll_callbacks,
cb, next);
}
}
-const struct ipv6_addr_l *
-ipv6_linklocal(const struct interface *ifp)
-{
- const struct ipv6_state *state;
- const struct ipv6_addr_l *ap;
-
- state = IPV6_CSTATE(ifp);
- if (state) {
- TAILQ_FOREACH(ap, &state->addrs, next) {
- if (IN6_IS_ADDR_LINKLOCAL(&ap->addr))
- return ap;
- }
- }
- return NULL;
-}
-
-const struct ipv6_addr_l *
+const struct ipv6_addr *
ipv6_findaddr(const struct interface *ifp, const struct in6_addr *addr)
{
const struct ipv6_state *state;
- const struct ipv6_addr_l *ap;
+ const struct ipv6_addr *ap;
state = IPV6_CSTATE(ifp);
if (state) {
TAILQ_FOREACH(ap, &state->addrs, next) {
- if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
- return ap;
+ if (addr == NULL) {
+ if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) &&
+ !(ap->addr_flags & IN6_IFF_NOTUSEABLE))
+ return ap;
+ } else {
+ if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
+ return ap;
+ }
}
}
return NULL;
ipv6_free(struct interface *ifp)
{
struct ipv6_state *state;
- struct ipv6_addr_l *ap;
+ struct ipv6_addr *ap;
if (ifp) {
ipv6_free_ll_callbacks(ifp);
/*
* BSD kernels don't inform userland of DAD results.
- * Also, for RTM_NEWADDR messages the address flags could be
- * undefined leading to false positive duplicate address errors.
- * As such we listen for duplicate addresses on the wire and
- * wait the maxium possible length of time as dictated by the DAD transmission
- * counter and RFC timings.
* See the discussion here:
* http://mail-index.netbsd.org/tech-net/2013/03/15/msg004019.html
*/
# include <sys/param.h>
#endif
#ifdef BSD
-# define LISTEN_DAD
+# define IPV6_POLLADDRFLAG
#endif
/* This was fixed in NetBSD */
#ifdef __NetBSD_Prereq__
# if __NetBSD_Prereq__(6, 99, 20)
-# undef LISTEN_DAD
+# undef IPV6_POLLADDRFLAG
# endif
#endif
uint32_t prefix_vltime;
uint32_t prefix_pltime;
struct in6_addr addr;
+ int addr_flags;
short flags;
char saddr[INET6_ADDRSTRLEN];
uint8_t iaid[4];
};
TAILQ_HEAD(rt6_head, rt6);
-struct ipv6_addr_l {
- TAILQ_ENTRY(ipv6_addr_l) next;
- struct in6_addr addr;
-};
-
-TAILQ_HEAD(ipv6_addr_l_head, ipv6_addr_l);
-
struct ll_callback {
TAILQ_ENTRY(ll_callback) next;
void (*callback)(void *);
TAILQ_HEAD(ll_callback_head, ll_callback);
struct ipv6_state {
- struct ipv6_addr_l_head addrs;
+ struct ipv6_addrhead addrs;
struct ll_callback_head ll_callbacks;
};
const char *sfrom;
int nd_fd;
-#ifdef IPV6_SEND_DAD
- int unspec_fd;
-#endif
-#ifdef LISTEN_DAD
- uint8_t dad_warned;
+#ifdef IPV6_POLLADDRFLAG
+ uint8_t polladdr_warned;
#endif
struct ra_head *ra_routers;
struct rt6_head *routes;
uint64_t user_number, struct in6_addr *result, short result_len);
void ipv6_checkaddrflags(void *);
int ipv6_addaddr(struct ipv6_addr *);
+ssize_t ipv6_addaddrs(struct ipv6_addrhead *addrs);
void ipv6_freedrop_addrs(struct ipv6_addrhead *, int,
const struct interface *);
void ipv6_handleifa(struct dhcpcd_ctx *ctx, int, struct if_head *,
const char *, const struct in6_addr *, int);
int ipv6_handleifa_addrs(int, struct ipv6_addrhead *,
const struct in6_addr *, int);
-const struct ipv6_addr_l *ipv6_linklocal(const struct interface *);
-const struct ipv6_addr_l *ipv6_findaddr(const struct interface *,
+const struct ipv6_addr *ipv6_findaddr(const struct interface *,
const struct in6_addr *);
+#define ipv6_linklocal(ifp) (ipv6_findaddr((ifp), NULL))
int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
void ipv6_free_ll_callbacks(struct interface *);
void ipv6_free(struct interface *);
#include "ipv6nd.h"
#include "script.h"
-#if defined(LISTEN_DAD) && defined(INET6)
-# warning kernel does not report DAD results to userland
-# warning listening to duplicated addresses on the wire
-#endif
-
/* Debugging Router Solicitations is a lot of spam, so disable it */
//#define DEBUG_RS
goto eexit;
#endif
-#ifdef LISTEN_DAD
- if (!ctx->dad_warned) {
- syslog(LOG_WARNING,
- "kernel does not report DAD results to userland");
- syslog(LOG_WARNING,
- "warning listening to duplicated addresses on the wire");
- ctx->dad_warned = 1;
- }
-#endif
-
return ctx->nd_fd;
eexit:
int wascompleted, found;
wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
- ipv6nd_cancelprobeaddr(ap);
ap->flags |= IPV6_AF_DADCOMPLETED;
if (ap->flags & IPV6_AF_DUPLICATED)
/* No idea what how to try and make another address :( */
syslog(LOG_WARNING, "%s: DAD detected %s",
ap->iface->name, ap->saddr);
-#ifdef IPV6_SEND_DAD
- else
- ipv6_addaddr(ap);
-#endif
if (!wascompleted) {
ifp = ap->iface;
script_runreason(ifp, "TEST");
goto handle_flag;
}
- ipv6nd_probeaddrs(&rap->addrs);
+ ipv6_addaddrs(&rap->addrs);
ipv6_buildroutes(ifp->ctx);
/* We will get run by the expire function */
next = lt;
}
- /* Addresses are expired in ipv6ns_probeaddrs
+ /* Addresses are expired in ipv6_addaddrs
* so that DHCPv6 addresses can be removed also. */
TAILQ_FOREACH_SAFE(rao, &rap->options, next, raon) {
if (rap->expired) {
&tv, ipv6nd_proberouter, rap);
}
-#ifdef LISTEN_DAD
-void
-ipv6nd_cancelprobeaddr(struct ipv6_addr *ap)
-{
-
- eloop_timeout_delete(ap->iface->ctx->eloop, ipv6nd_probeaddr, ap);
- if (ap->dadcallback)
- eloop_timeout_delete(ap->iface->ctx->eloop, ap->dadcallback,ap);
-}
-
-#endif
-
-void
-ipv6nd_probeaddr(void *arg)
-{
- struct ipv6_addr *ap = arg;
-#ifdef IPV6_SEND_DAD
- struct nd_neighbor_solicit *ns;
- struct nd_opt_hdr *nd;
- struct sockaddr_in6 dst;
- struct cmsghdr *cm;
- struct in6_pktinfo pi;
- int hoplimit = HOPLIMIT;
- struct timeval tv, rtv;
-#else
-#ifdef LISTEN_DAD
- struct timeval tv;
-#endif
-#endif
-
- if (ap->dadcallback &&
- ((ap->flags & IPV6_AF_NEW) == 0 ||
- ap->nsprobes >= ap->iface->options->dadtransmits))
- {
-#ifdef IPV6_SEND_DAD
- ap->dadcallback(ap);
-#else
- if (!(ap->flags & IPV6_AF_AUTOCONF) ||
- ap->iface->options->options & DHCPCD_IPV6RA_OWN)
- ipv6_addaddr(ap);
-#endif
- return;
- }
-
- if (ipv6nd_open(ap->iface->ctx) == -1) {
- syslog(LOG_ERR, "%s: ipv6nd_open: %m", __func__);
- return;
- }
-
- ap->flags &= ~IPV6_AF_DADCOMPLETED;
-
-#ifdef IPV6_SEND_DAD
- if (!ap->ns) {
- ap->nslen = sizeof(*ns) + ROUNDUP8(ap->iface->hwlen + 2);
- ap->ns = calloc(1, ap->nslen);
- if (ap->ns == NULL) {
- syslog(LOG_ERR, "%s: %m", __func__);
- return;
- }
- ns = (struct nd_neighbor_solicit *)(void *)ap->ns;
- ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
- //ns->nd_ns_cksum = 0;
- //ns->nd_ns_code = 0;
- //ns->nd_ns_reserved = 0;
- ns->nd_ns_target = ap->addr;
- nd = (struct nd_opt_hdr *)(ap->ns + sizeof(*ns));
- nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
- nd->nd_opt_len = (ROUNDUP8(ap->iface->hwlen + 2)) >> 3;
- memcpy(nd + 1, ap->iface->hwaddr, ap->iface->hwlen);
- }
-
- memset(&dst, 0, sizeof(dst));
- dst.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
- dst.sin6_len = sizeof(dst);
-#endif
- dst.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
- dst.sin6_addr.s6_addr16[1] = 0;
- dst.sin6_addr.s6_addr32[1] = 0;
- dst.sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
- dst.sin6_addr.s6_addr32[3] = ap->addr.s6_addr32[3];
- dst.sin6_addr.s6_addr[12] = 0xff;
-
- //memcpy(&dst.sin6_addr, &ap->addr, sizeof(dst.sin6_addr));
- dst.sin6_scope_id = ap->iface->index;
-
- ctx = ap->iface->ctx->ipv6;
- ctx->sndhdr.msg_name = (caddr_t)&dst;
- ctx->sndhdr.msg_iov[0].iov_base = ap->ns;
- ctx->sndhdr.msg_iov[0].iov_len = ap->nslen;
-
- /* Set the outbound interface */
- cm = CMSG_FIRSTHDR(&ctx->sndhdr);
- if (cm == NULL) /* unlikely */
- return;
- cm->cmsg_level = IPPROTO_IPV6;
- cm->cmsg_type = IPV6_PKTINFO;
- cm->cmsg_len = CMSG_LEN(sizeof(pi));
- memset(&pi, 0, sizeof(pi));
- pi.ipi6_ifindex = ap->iface->index;
- memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
-
- /* Hop limit */
- cm = CMSG_NXTHDR(&sndhdr, cm);
- if (cm == NULL) /* unlikely */
- return;
- cm->cmsg_level = IPPROTO_IPV6;
- cm->cmsg_type = IPV6_HOPLIMIT;
- cm->cmsg_len = CMSG_LEN(sizeof(hoplimit));
- memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit));
-
-#ifdef DEBUG_NS
- syslog(LOG_INFO, "%s: sending IPv6 NS for %s",
- ap->iface->name, ap->saddr);
- if (ap->dadcallback == NULL)
- syslog(LOG_WARNING, "%s: no callback!", ap->iface->name);
-#endif
- if (sendmsg(unspec_sock, &sndhdr, 0) == -1) {
- syslog(LOG_ERR, "%s: %s: sendmsg: %m",
- ap->iface->name, __func__);
- return;
- }
-
- if (ap->dadcallback) {
- ms_to_tv(&tv, RETRANS_TIMER);
- ms_to_tv(&rtv, MIN_RANDOM_FACTOR);
- timeradd(&tv, &rtv, &tv);
- rtv.tv_sec = 0;
- rtv.tv_usec = arc4random() %
- (MAX_RANDOM_FACTOR_U - MIN_RANDOM_FACTOR_U);
- timeradd(&tv, &rtv, &tv);
-
- eloop_timeout_add_tv(ap->iface->ctx->eloop, &tv,
- ++(ap->nsprobes) < ap->iface->options->dadtransmits ?
- ipv6nd_probeaddr : ap->dadcallback,
- ap);
- }
-#else /* IPV6_SEND_DAD */
-
- if (!(ap->flags & IPV6_AF_AUTOCONF) ||
- ap->iface->options->options & DHCPCD_IPV6RA_OWN)
- ipv6_addaddr(ap);
-
-#ifdef LISTEN_DAD
- /* Let the kernel handle DAD.
- * We don't know the timings, so just poll the address flags */
- if (ap->dadcallback) {
- ms_to_tv(&tv, RETRANS_TIMER / 2);
- eloop_timeout_add_tv(ap->iface->ctx->eloop,
- &tv, ipv6_checkaddrflags, ap);
- }
-#endif
-#endif /* IPV6_SEND_DAD */
-}
-
-ssize_t
-ipv6nd_probeaddrs(struct ipv6_addrhead *addrs)
-{
- struct ipv6_addr *ap, *apn;
- ssize_t i;
-
- i = 0;
- TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
- if (ap->prefix_vltime == 0) {
- TAILQ_REMOVE(addrs, ap, next);
- if (ap->flags & IPV6_AF_ADDED) {
- syslog(LOG_INFO, "%s: deleting address %s",
- ap->iface->name, ap->saddr);
- i++;
- if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr) &&
- del_address6(ap) == -1 &&
- errno != EADDRNOTAVAIL && errno != ENXIO)
- syslog(LOG_ERR, "del_address6 %m");
- }
- eloop_q_timeout_delete(ap->iface->ctx->eloop,
- 0, NULL, ap);
- free(ap);
- } else if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr)) {
- ipv6nd_probeaddr(ap);
- if (ap->flags & IPV6_AF_NEW)
- i++;
- }
- }
-
- return i;
-}
-
void
ipv6nd_proberouter(void *arg)
{
struct nd_neighbor_advert *nd_na;
struct ra *rap;
int is_router, is_solicited;
-#ifdef DEBUG_NS
- int found;
-#endif
struct timeval tv;
-#ifdef LISTEN_DAD
- struct dhcp6_state *d6state;
- struct ipv6_addr *ap;
-#endif
-
if ((size_t)len < sizeof(struct nd_neighbor_advert)) {
syslog(LOG_ERR, "IPv6 NA packet too short from %s", ctx->sfrom);
return;
return;
}
-#ifdef DEBUG_NS
- found = 0;
-#endif
TAILQ_FOREACH(rap, ctx->ra_routers, next) {
- if (rap->iface != ifp)
- continue;
- if (memcmp(rap->from.s6_addr, nd_na->nd_na_target.s6_addr,
+ if (rap->iface == ifp &&
+ memcmp(rap->from.s6_addr, nd_na->nd_na_target.s6_addr,
sizeof(rap->from.s6_addr)) == 0)
break;
-#ifdef LISTEN_DAD
- TAILQ_FOREACH(ap, &rap->addrs, next) {
- if (memcmp(ap->addr.s6_addr,
- nd_na->nd_na_target.s6_addr,
- sizeof(ap->addr.s6_addr)) == 0)
- {
- ap->flags |= IPV6_AF_DUPLICATED;
- if (ap->dadcallback)
- ap->dadcallback(ap);
-#ifdef DEBUG_NS
- found++;
-#endif
- }
- }
-#endif
}
if (rap == NULL) {
-#ifdef LISTEN_DAD
- d6state = D6_STATE(ifp);
- if (d6state) {
- TAILQ_FOREACH(ap, &d6state->addrs, next) {
- if (memcmp(ap->addr.s6_addr,
- nd_na->nd_na_target.s6_addr,
- sizeof(ap->addr.s6_addr)) == 0)
- {
- ap->flags |= IPV6_AF_DUPLICATED;
- if (ap->dadcallback)
- ap->dadcallback(ap);
-#ifdef DEBUG_NS
- found++;
-#endif
- }
- }
- }
-#endif
-
#ifdef DEBUG_NS
- if (found == 0)
- syslog(LOG_DEBUG, "%s: unexpected NA from %s",
- ifp->name, ctx->sfrom);
+ syslog(LOG_DEBUG, "%s: unexpected NA from s",
+ ifp->name, ctx->sfrom);
#endif
return;
}
const char *, const struct in6_addr *, int);
void ipv6nd_drop(struct interface *);
-void ipv6nd_probeaddr(void *);
-ssize_t ipv6nd_probeaddrs(struct ipv6_addrhead *);
void ipv6nd_proberouter(void *);
void ipv6nd_cancelproberouter(struct ra *);
-#ifdef LISTEN_DAD
-void ipv6nd_cancelprobeaddr(struct ipv6_addr *);
-#else
-#define ipv6nd_cancelprobeaddr(a)
-#endif
-
#else
#define ipv6nd_startrs(a) {}
#define ipv6nd_addrexists(a, b) (0)