From: Roy Marples Date: Thu, 8 May 2014 23:35:30 +0000 (+0000) Subject: Support RTM_GETNEIGH on Linux as well as the polling for NUD changes on BSD. X-Git-Tag: v6.4.0~56 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=72c37f5f56ea5ca3c46bfc4c646be7ed1f1ef95c;p=thirdparty%2Fdhcpcd.git Support RTM_GETNEIGH on Linux as well as the polling for NUD changes on BSD. Fixes [bb6153d18b]. --- diff --git a/if-bsd.c b/if-bsd.c index bc4cd6e0..bff1252f 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -975,7 +975,7 @@ eexit: int if_nd6reachable(const char *ifname, struct in6_addr *addr) { - int s, r; + int s, flags; struct in6_nbrinfo nbi; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) @@ -985,21 +985,22 @@ if_nd6reachable(const char *ifname, struct in6_addr *addr) strlcpy(nbi.ifname, ifname, sizeof(nbi.ifname)); nbi.addr = *addr; if (ioctl(s, SIOCGNBRINFO_IN6, &nbi) == -1) - r = -1; + flags = -1; else { + flags = 0; switch(nbi.state) { case ND6_LLINFO_REACHABLE: case ND6_LLINFO_STALE: case ND6_LLINFO_DELAY: case ND6_LLINFO_PROBE: - r = 1; + flags |= IPV6ND_REACHABLE; break; - default: - r = 0; } + if (nbi.isrouter) + flags |= IPV6ND_ROUTER; } close(s); - return r; + return flags; } void diff --git a/if-linux.c b/if-linux.c index 2ee561f7..0b7547da 100644 --- a/if-linux.c +++ b/if-linux.c @@ -75,6 +75,7 @@ #include "if.h" #include "ipv4.h" #include "ipv6.h" +#include "ipv6nd.h" #define bpf_insn sock_filter #define BPF_SKIPTYPE @@ -275,7 +276,7 @@ if_openlinksocket(void) snl.nl_groups |= RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR; #endif #ifdef INET6 - snl.nl_groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR; + snl.nl_groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_NEIGH; #endif return _open_link_socket(&snl); @@ -557,6 +558,50 @@ handle_rename(struct dhcpcd_ctx *ctx, unsigned int ifindex, const char *ifname) return 0; } +#ifdef INET6 +static int +link_neigh(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) +{ + struct ndmsg *r; + struct rtattr *rta; + size_t len; + struct in6_addr addr6; + int flags; + + if (nlm->nlmsg_type != RTM_NEWNEIGH && nlm->nlmsg_type != RTM_DELNEIGH) + return 0; + if (nlm->nlmsg_len < sizeof(*r)) + return -1; + + r = NLMSG_DATA(nlm); + rta = (struct rtattr *)RTM_RTA(r); + len = RTM_PAYLOAD(nlm); + if (r->ndm_family == AF_INET6) { + flags = 0; + if (r->ndm_flags & NTF_ROUTER) + flags |= IPV6ND_ROUTER; + if (nlm->nlmsg_type == RTM_NEWNEIGH && + r->ndm_state & + (NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE | + NUD_PERMANENT)) + flags |= IPV6ND_REACHABLE; + memset(&addr6, 0, sizeof(addr6)); + while (RTA_OK(rta, len)) { + switch (rta->rta_type) { + case NDA_DST: + memcpy(&addr6.s6_addr, RTA_DATA(rta), + sizeof(addr6.s6_addr)); + break; + } + rta = RTA_NEXT(rta, len); + } + ipv6nd_neighbour(ctx, &addr6, flags); + } + + return 1; +} +#endif + static int link_netlink(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) { @@ -573,6 +618,11 @@ link_netlink(struct dhcpcd_ctx *ctx, struct nlmsghdr *nlm) r = link_addr(ctx, nlm); if (r != 0) return r; +#ifdef INET6 + r = link_neigh(ctx, nlm); + if (r != 0) + return r; +#endif if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK) return 0; @@ -1163,14 +1213,6 @@ if_addrflags6(const char *ifname, const struct in6_addr *addr) return -1; } -int -if_nd6reachable(__unused const char *ifname, __unused struct in6_addr *addr) -{ - - /* Assume reachable until I work out how to obtain reachability */ - return 1; -} - static const char *prefix = "/proc/sys/net/ipv6/conf"; void diff --git a/if.h b/if.h index ddf10fc2..d8e8304b 100644 --- a/if.h +++ b/if.h @@ -48,6 +48,13 @@ # endif #endif +/* Neighbour reachability and router updates */ +#ifndef HAVE_RTM_GETNEIGH +# ifdef __linux__ +# define HAVE_RTM_GETNEIGH +# endif +#endif + #define EUI64_ADDR_LEN 8 #define INFINIBAND_ADDR_LEN 20 diff --git a/ipv6nd.c b/ipv6nd.c index 07df8ab6..da3817b6 100644 --- a/ipv6nd.c +++ b/ipv6nd.c @@ -316,40 +316,70 @@ ipv6nd_sendrsprobe(void *arg) syslog(LOG_WARNING, "%s: no IPv6 Routers available", ifp->name); } +static void +ipv6nd_reachable(struct ra *rap, int flags) +{ + + if (flags & IPV6ND_REACHABLE) { + if (rap->lifetime && rap->expired) { + syslog(LOG_INFO, "%s: %s is reachable again", + rap->iface->name, rap->sfrom); + rap->expired = 0; + ipv6_buildroutes(rap->iface->ctx); + /* XXX Not really an RA */ + script_runreason(rap->iface, "ROUTERADVERT"); + } + } else { + /* Any error means it's really gone from the kernel + * neighbour database */ + if (rap->lifetime && !rap->expired) { + syslog(LOG_WARNING, + "%s: %s is unreachable, expiring it", + rap->iface->name, rap->sfrom); + rap->expired = 1; + ipv6_buildroutes(rap->iface->ctx); + /* XXX Not really an RA */ + script_runreason(rap->iface, "ROUTERADVERT"); + } + } +} + +#ifdef HAVE_RTM_GETNEIGH +void +ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags) +{ + struct ra *rap; + + TAILQ_FOREACH(rap, ctx->ipv6->ra_routers, next) { + if (IN6_ARE_ADDR_EQUAL(&rap->from, addr)) { + ipv6nd_reachable(rap, flags); + break; + } + } +} + +#else + static void ipv6nd_checkreachablerouters(void *arg) { struct dhcpcd_ctx *ctx = arg; struct ra *rap; + int flags; TAILQ_FOREACH(rap, ctx->ipv6->ra_routers, next) { - if (if_nd6reachable(rap->iface->name, &rap->from) == 1) { - if (rap->lifetime && rap->expired) { - syslog(LOG_INFO, "%s: %s is reachable again", - rap->iface->name, rap->sfrom); - rap->expired = 0; - ipv6_buildroutes(ctx); - /* XXX Not really an RA */ - script_runreason(rap->iface, "ROUTERADVERT"); - } - } else { - /* Any error means it's really gone from the kernel - * neighbour database */ - if (rap->lifetime && !rap->expired) { - syslog(LOG_WARNING, - "%s: %s is unreachable, expiring it", - rap->iface->name, rap->sfrom); - rap->expired = 1; - ipv6_buildroutes(ctx); - /* XXX Not really an RA */ - script_runreason(rap->iface, "ROUTERADVERT"); - } + flags = if_nd6reachable(rap->iface->name, &rap->from); + if (flags == -1) { + /* An error occured, so it's unreachable */ + flags = 0; } + ipv6nd_reachable(rap, flags); } eloop_timeout_add_sec(ctx->eloop, ND6REACHABLE_TIMER, ipv6nd_checkreachablerouters, ctx); } +#endif static void ipv6nd_free_opts(struct ra *rap) @@ -390,9 +420,11 @@ void ipv6nd_freedrop_ra(struct ra *rap, int drop) eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap); if (!drop) TAILQ_REMOVE(rap->iface->ctx->ipv6->ra_routers, rap, next); +#ifndef HAVE_RTM_GETNEIGH if (TAILQ_FIRST(rap->iface->ctx->ipv6->ra_routers) == NULL) eloop_timeout_delete(rap->iface->ctx->eloop, ipv6nd_checkreachablerouters, rap->iface->ctx); +#endif ipv6_freedrop_addrs(&rap->addrs, drop, NULL); ipv6nd_free_opts(rap); free(rap->data); @@ -983,8 +1015,10 @@ handle_flag: /* Expire should be called last as the rap object could be destroyed */ ipv6nd_expirera(ifp); +#ifndef HAVE_RTM_GETNEIGH /* Start our reachability tests now */ ipv6nd_checkreachablerouters(ifp->ctx); +#endif } int diff --git a/ipv6nd.h b/ipv6nd.h index 222cacf3..9dbbdf63 100644 --- a/ipv6nd.h +++ b/ipv6nd.h @@ -78,6 +78,9 @@ struct rs_state { #define RETRANS_TIMER 1000 /* milliseconds */ #define DELAY_FIRST_PROBE_TIME 5 /* seconds */ +#define IPV6ND_REACHABLE (1 << 0) +#define IPV6ND_ROUTER (1 << 1) + #ifdef INET6 void ipv6nd_startrs(struct interface *); ssize_t ipv6nd_env(char **, const char *, const struct interface *); @@ -91,6 +94,10 @@ int ipv6nd_has_ra(const struct interface *); void ipv6nd_handleifa(struct dhcpcd_ctx *, int, const char *, const struct in6_addr *, int); void ipv6nd_drop(struct interface *); + +#ifdef HAVE_RTM_GETNEIGH +void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, int); +#endif #else #define ipv6nd_startrs(a) {} #define ipv6nd_addrexists(a, b) (0)