]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Support RTM_GETNEIGH on Linux as well as the polling for NUD changes on BSD.
authorRoy Marples <roy@marples.name>
Thu, 8 May 2014 23:35:30 +0000 (23:35 +0000)
committerRoy Marples <roy@marples.name>
Thu, 8 May 2014 23:35:30 +0000 (23:35 +0000)
Fixes [bb6153d18b].

if-bsd.c
if-linux.c
if.h
ipv6nd.c
ipv6nd.h

index bc4cd6e0fec6bd19670d4f8b5056cd35fc3055cb..bff1252f4010ebf0b9bb5c9949a39a97092653fd 100644 (file)
--- 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
index 2ee561f774d67d7443ccef8df175e3f51ecbf643..0b7547da66590b268f3fe08615ecd197ad7ded76 100644 (file)
@@ -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 ddf10fc2c880b6eed848cf8b0b0d36e5cecd82c5..d8e8304b954204f8df157e70e38c83f0fa60982b 100644 (file)
--- a/if.h
+++ b/if.h
 # 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
 
index 07df8ab6e310ea2e86e3186ac6061ce4dcf15c27..da3817b62f251c64a14dcd40d6844b3d461ba9b8 100644 (file)
--- 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
index 222cacf35decc9a845998746f9b93cdc030b9ecb..9dbbdf63495d82b17cf8a04721f2b26425b4435f 100644 (file)
--- 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)