router: also send RAs to every known neighbor
authorSteven Barth <steven@midlink.org>
Tue, 7 Jul 2015 14:01:13 +0000 (16:01 +0200)
committerSteven Barth <steven@midlink.org>
Tue, 7 Jul 2015 14:01:13 +0000 (16:01 +0200)
src/ndp.c
src/odhcpd.c
src/odhcpd.h
src/router.c

index 027e9ab..a6f8427 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
@@ -356,7 +356,7 @@ static void handle_rtnetlink(_unused void *addr, void *data, size_t len,
 
                // Data to retrieve
                size_t rta_offset = (is_route) ? sizeof(*rtm) : (is_addr) ?
-                               sizeof(*ifa) : sizeof(*ndm);
+                               sizeof(struct ifaddrmsg) : sizeof(*ndm);
                uint16_t atype = (is_route) ? RTA_DST : (is_addr) ? IFA_ADDRESS : NDA_DST;
                ssize_t alen = NLMSG_PAYLOAD(nh, rta_offset);
                struct in6_addr *addr = NULL;
index aad5b37..3fb3009 100644 (file)
@@ -194,6 +194,61 @@ ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest,
 }
 
 
+int odhcpd_iterate_interface_neighbors(const struct interface *iface,
+               void(*cb_neigh)(const struct in6_addr *addr,
+                               const struct interface *iface, void *data), void *data)
+{
+       struct {
+               struct nlmsghdr nhm;
+               struct ndmsg ndm;
+       } req = {{sizeof(req), RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP,
+                       ++rtnl_seq, 0}, {AF_INET6, 0, 0, iface->ifindex, 0, 0, 0}};
+
+       if (send(rtnl_socket, &req, sizeof(req), 0) < (ssize_t)sizeof(req))
+               return -1;
+
+       uint8_t buf[8192];
+       ssize_t len = 0;
+
+       for (struct nlmsghdr *nhm = NULL; ; nhm = NLMSG_NEXT(nhm, len)) {
+               while (len < 0 || !NLMSG_OK(nhm, (size_t)len)) {
+                       len = recv(rtnl_socket, buf, sizeof(buf), 0);
+                       nhm = (struct nlmsghdr*)buf;
+                       if (len < 0 || !NLMSG_OK(nhm, (size_t)len)) {
+                               if (errno == EINTR)
+                                       continue;
+                               else
+                                       return -1;
+                       }
+               }
+
+               if (nhm->nlmsg_type != RTM_NEWNEIGH)
+                       break;
+
+               struct ndmsg *ndm = NLMSG_DATA(nhm);
+               if (ndm->ndm_ifindex != iface->ifindex ||
+                               (ndm->ndm_state & NUD_FAILED))
+                       continue;
+
+               struct rtattr *rta = (struct rtattr*)&ndm[1];
+               size_t alen = NLMSG_PAYLOAD(nhm, sizeof(*ndm));
+
+               while (RTA_OK(rta, alen)) {
+                       if (rta->rta_type == NDA_DST) {
+                               // TODO
+                               cb_neigh(NULL, iface, data);
+                               break;
+                       } else {
+                               rta = RTA_NEXT(rta, alen);
+                       }
+               }
+
+       }
+
+       return 0;
+}
+
+
 // Detect an IPV6-address currently assigned to the given interface
 ssize_t odhcpd_get_interface_addresses(int ifindex,
                struct odhcpd_ipaddr *addrs, size_t cnt)
index 5944661..dcd2d14 100644 (file)
@@ -202,6 +202,10 @@ void odhcpd_hexlify(char *dst, const uint8_t *src, size_t len);
 int odhcpd_bmemcmp(const void *av, const void *bv, size_t bits);
 void odhcpd_bmemcpy(void *av, const void *bv, size_t bits);
 
+int odhcpd_iterate_interface_neighbors(const struct interface *iface,
+               void(*cb_neigh)(const struct in6_addr *addr,
+                               const struct interface *iface, void *data), void *data);
+
 int config_parse_interface(void *data, size_t len, const char *iname, bool overwrite);
 
 #ifdef WITH_UBUS
index f942d8f..718f170 100644 (file)
@@ -37,6 +37,7 @@ static void sigusr1_refresh(int signal);
 static struct odhcpd_event router_event = {{.fd = -1}, handle_icmpv6};
 
 static FILE *fp_route = NULL;
+#define RA_IOV_LEN 6
 
 
 int init_router(void)
@@ -205,6 +206,18 @@ static bool parse_routes(struct odhcpd_ipaddr *n, ssize_t len)
        return found_default;
 }
 
+// Unicsat RAs
+static void send_neigh_ra(const struct in6_addr *addr,
+               const struct interface *iface, void *data)
+{
+       struct sockaddr_in6 dest = {
+               .sin6_family = AF_INET6,
+               .sin6_addr = *addr,
+               .sin6_scope_id = iface->ifindex,
+       };
+       odhcpd_send(router_event.uloop.fd, &dest, data, RA_IOV_LEN, iface);
+}
+
 
 // Router Advert server mode
 static uint64_t send_router_advert(struct interface *iface, const struct in6_addr *from)
@@ -443,7 +456,8 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
                .data = {0, 0, maxival >> 24, maxival >> 16, maxival >> 8, maxival}
        };
 
-       struct iovec iov[] = {{&adv, (uint8_t*)&adv.prefix[cnt] - (uint8_t*)&adv},
+       struct iovec iov[RA_IOV_LEN] = {
+                       {&adv, (uint8_t*)&adv.prefix[cnt] - (uint8_t*)&adv},
                        {&routes, routes_cnt * sizeof(*routes)},
                        {&dns, (dns_cnt) ? sizeof(dns) : 0},
                        {dns_addr, dns_cnt * sizeof(*dns_addr)},
@@ -457,6 +471,7 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
        odhcpd_send(router_event.uloop.fd,
                        &dest, iov, ARRAY_SIZE(iov), iface);
 
+       odhcpd_iterate_interface_neighbors(iface, send_neigh_ra, iov);
        return msecs;
 }