cmake: enforce additonal compiler checks
[project/odhcpd.git] / src / ndp.c
index 64a6c1c5ce40fcf1face31391943bc59f36eb6a0..dfbb1115c2126cf67bdb7c08674a5355fa84750c 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
@@ -71,6 +71,8 @@ int ndp_setup_interface(struct interface *iface, bool enable)
        bool dump_neigh = false;
        char procbuf[64];
 
+       enable = enable && (iface->ndp == MODE_RELAY);
+
        snprintf(procbuf, sizeof(procbuf), "/proc/sys/net/ipv6/conf/%s/proxy_ndp", iface->ifname);
        procfd = open(procbuf, O_WRONLY);
 
@@ -89,13 +91,13 @@ int ndp_setup_interface(struct interface *iface, bool enable)
                close(iface->ndp_event.uloop.fd);
                iface->ndp_event.uloop.fd = -1;
 
-               if (!enable || iface->ndp != MODE_RELAY)
+               if (!enable)
                        if (write(procfd, "0\n", 2) < 0) {}
 
                dump_neigh = true;
        }
 
-       if (enable && iface->ndp == MODE_RELAY) {
+       if (enable) {
                struct sockaddr_ll ll;
                struct packet_mreq mreq;
                struct icmp6_filter filt;
@@ -286,13 +288,40 @@ static void ping6(struct in6_addr *addr,
        char ipbuf[INET6_ADDRSTRLEN];
 
        inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
-       syslog(LOG_NOTICE, "Pinging for %s on %s", ipbuf, iface->name);
+       syslog(LOG_DEBUG, "Pinging for %s on %s", ipbuf, iface->name);
 
        netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, true);
        odhcpd_send(iface->ndp_ping_fd, &dest, &iov, 1, iface);
        netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, false);
 }
 
+/* Send a Neighbor Advertisement. */
+static void send_na(struct in6_addr *to_addr,
+               const struct interface *iface, struct in6_addr *for_addr,
+               const uint8_t *mac)
+{
+       struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *to_addr };
+       char pbuf[sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + 6];
+       struct nd_neighbor_advert *adv = (struct nd_neighbor_advert*)pbuf;
+       struct nd_opt_hdr *opt = (struct nd_opt_hdr*) &pbuf[sizeof(struct nd_neighbor_advert)];
+       struct iovec iov = { .iov_base = &pbuf, .iov_len = sizeof(pbuf) };
+       char ipbuf[INET6_ADDRSTRLEN];
+
+       memset(pbuf, 0, sizeof(pbuf));
+       adv->nd_na_hdr = (struct icmp6_hdr) {
+               .icmp6_type = ND_NEIGHBOR_ADVERT,
+               .icmp6_dataun.icmp6_un_data32 = { ND_NA_FLAG_SOLICITED }
+       };
+       adv->nd_na_target = *for_addr;
+       *opt = (struct nd_opt_hdr) { .nd_opt_type = ND_OPT_TARGET_LINKADDR, .nd_opt_len = 1 };
+       memcpy(&pbuf[sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr)], mac, 6);
+
+       inet_ntop(AF_INET6, to_addr, ipbuf, sizeof(ipbuf));
+       syslog(LOG_DEBUG, "Answering NS to %s on %s", ipbuf, iface->ifname);
+
+       odhcpd_send(iface->ndp_ping_fd, &dest, &iov, 1, iface);
+}
+
 /* Handle solicitations */
 static void handle_solicit(void *addr, void *data, size_t len,
                struct interface *iface, _unused void *dest)
@@ -333,6 +362,17 @@ static void handle_solicit(void *addr, void *data, size_t len,
                                (ns_is_dad || !c->external))
                        ping6(&req->nd_ns_target, c);
        }
+
+       /* Catch global-addressed NS and answer them manually.
+        * The kernel won't answer these and cannot route them either. */
+       if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
+                       IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
+               bool is_proxy_neigh = netlink_get_interface_proxy_neigh(iface->ifindex,
+                               &req->nd_ns_target) == 1;
+
+               if (is_proxy_neigh)
+                       send_na(&ip6->ip6_src, iface, &req->nd_ns_target, mac);
+       }
 }
 
 /* Use rtnetlink to modify kernel routes */
@@ -341,7 +381,7 @@ static void setup_route(struct in6_addr *addr, struct interface *iface, bool add
        char ipbuf[INET6_ADDRSTRLEN];
 
        inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
-       syslog(LOG_NOTICE, "%s about %s%s on %s",
+       syslog(LOG_DEBUG, "%s about %s%s on %s",
                        (add) ? "Learning" : "Forgetting",
                        iface->learn_routes ? "proxy routing for " : "",
                        ipbuf, iface->name);