ndp: fix adding proxy neighbor entries
[project/odhcpd.git] / src / ndp.c
index 8824879..49aea1c 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
 #include <netlink/socket.h>
 #include <netlink/attr.h>
 
 #include <netlink/socket.h>
 #include <netlink/attr.h>
 
-#include "router.h"
 #include "dhcpv6.h"
 #include "dhcpv6.h"
-#include "ndp.h"
+#include "odhcpd.h"
 
 struct event_socket {
        struct odhcpd_event ev;
        struct nl_sock *sock;
 
 struct event_socket {
        struct odhcpd_event ev;
        struct nl_sock *sock;
+       int sock_bufsize;
 };
 
 static void handle_solicit(void *addr, void *data, size_t len,
                struct interface *iface, void *dest);
 static void handle_rtnl_event(struct odhcpd_event *ev);
 static int cb_rtnl_valid(struct nl_msg *msg, void *arg);
 };
 
 static void handle_solicit(void *addr, void *data, size_t len,
                struct interface *iface, void *dest);
 static void handle_rtnl_event(struct odhcpd_event *ev);
 static int cb_rtnl_valid(struct nl_msg *msg, void *arg);
-static void catch_rtnetlink(int error);
+static void catch_rtnl_err(struct odhcpd_event *e, int error);
 
 
+static int addr6_dump_rqs = 0;
 static int ping_socket = -1;
 static struct event_socket rtnl_event = {
        .ev = {
                .uloop = {.fd = - 1, },
                .handle_dgram = NULL,
 static int ping_socket = -1;
 static struct event_socket rtnl_event = {
        .ev = {
                .uloop = {.fd = - 1, },
                .handle_dgram = NULL,
-               .handle_error = catch_rtnetlink,
+               .handle_error = catch_rtnl_err,
                .recv_msgs = handle_rtnl_event,
        },
        .sock = NULL,
                .recv_msgs = handle_rtnl_event,
        },
        .sock = NULL,
+       .sock_bufsize = 133120,
 };
 
 // Filter ICMPv6 messages of type neighbor soliciation
 };
 
 // Filter ICMPv6 messages of type neighbor soliciation
@@ -75,7 +77,7 @@ static const struct sock_fprog bpf_prog = {sizeof(bpf) / sizeof(*bpf), bpf};
 // Initialize NDP-proxy
 int init_ndp(void)
 {
 // Initialize NDP-proxy
 int init_ndp(void)
 {
-       int val = 256 * 1024;
+       int val = 2;
 
        rtnl_event.sock = odhcpd_create_nl_socket(NETLINK_ROUTE);
        if (!rtnl_event.sock)
 
        rtnl_event.sock = odhcpd_create_nl_socket(NETLINK_ROUTE);
        if (!rtnl_event.sock)
@@ -83,7 +85,7 @@ int init_ndp(void)
 
        rtnl_event.ev.uloop.fd = nl_socket_get_fd(rtnl_event.sock);
 
 
        rtnl_event.ev.uloop.fd = nl_socket_get_fd(rtnl_event.sock);
 
-       if (nl_socket_set_buffer_size(rtnl_event.sock, val, 0))
+       if (nl_socket_set_buffer_size(rtnl_event.sock, rtnl_event.sock_bufsize, 0))
                goto err;
 
        nl_socket_disable_seq_check(rtnl_event.sock);
                goto err;
 
        nl_socket_disable_seq_check(rtnl_event.sock);
@@ -105,7 +107,6 @@ int init_ndp(void)
                        return -1;
        }
 
                        return -1;
        }
 
-       val = 2;
        setsockopt(ping_socket, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
 
        // This is required by RFC 4861
        setsockopt(ping_socket, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
 
        // This is required by RFC 4861
@@ -149,7 +150,7 @@ static void dump_neigh_table(const bool proxy)
        nlmsg_free(msg);
 }
 
        nlmsg_free(msg);
 }
 
-static void dump_addr_table(void)
+static void dump_addr6_table(void)
 {
        struct nl_msg *msg;
        struct ifaddrmsg ifa = {
 {
        struct nl_msg *msg;
        struct ifaddrmsg ifa = {
@@ -167,12 +168,33 @@ static void dump_addr_table(void)
        nlmsg_free(msg);
 }
 
        nlmsg_free(msg);
 }
 
+void ndp_handle_addr6_dump(void)
+{
+       if (!addr6_dump_rqs)
+               return;
+
+       dump_addr6_table();
+       addr6_dump_rqs = 0;
+}
+
+inline void ndp_rqs_addr6_dump(void)
+{
+       addr6_dump_rqs++;
+}
+
 int setup_ndp_interface(struct interface *iface, bool enable)
 {
 int setup_ndp_interface(struct interface *iface, bool enable)
 {
+       int ret = 0, procfd;
+       bool dump_neigh = false;
        char procbuf[64];
        char procbuf[64];
+
        snprintf(procbuf, sizeof(procbuf), "/proc/sys/net/ipv6/conf/%s/proxy_ndp", iface->ifname);
        snprintf(procbuf, sizeof(procbuf), "/proc/sys/net/ipv6/conf/%s/proxy_ndp", iface->ifname);
-       int procfd = open(procbuf, O_WRONLY);
-       bool dump_neigh = false;
+       procfd = open(procbuf, O_WRONLY);
+
+       if (procfd < 0) {
+               ret = -1;
+               goto out;
+       }
 
        if (iface->ndp_event.uloop.fd > 0) {
                uloop_fd_delete(&iface->ndp_event.uloop);
 
        if (iface->ndp_event.uloop.fd > 0) {
                uloop_fd_delete(&iface->ndp_event.uloop);
@@ -185,19 +207,15 @@ int setup_ndp_interface(struct interface *iface, bool enable)
                dump_neigh = true;
        }
 
                dump_neigh = true;
        }
 
-       if (enable && (iface->ra == RELAYD_SERVER ||
-                       iface->dhcpv6 == RELAYD_SERVER || iface->ndp == RELAYD_RELAY))
-               dump_addr_table();
-
        if (enable && iface->ndp == RELAYD_RELAY) {
                if (write(procfd, "1\n", 2) < 0) {}
        if (enable && iface->ndp == RELAYD_RELAY) {
                if (write(procfd, "1\n", 2) < 0) {}
-               close(procfd);
 
                int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
                if (sock < 0) {
                        syslog(LOG_ERR, "Unable to open packet socket: %s",
                                        strerror(errno));
 
                int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
                if (sock < 0) {
                        syslog(LOG_ERR, "Unable to open packet socket: %s",
                                        strerror(errno));
-                       return -1;
+                       ret = -1;
+                       goto out;
                }
 
 #ifdef PACKET_RECV_TYPE
                }
 
 #ifdef PACKET_RECV_TYPE
@@ -208,7 +226,8 @@ int setup_ndp_interface(struct interface *iface, bool enable)
                if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
                                &bpf_prog, sizeof(bpf_prog))) {
                        syslog(LOG_ERR, "Failed to set BPF: %s", strerror(errno));
                if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
                                &bpf_prog, sizeof(bpf_prog))) {
                        syslog(LOG_ERR, "Failed to set BPF: %s", strerror(errno));
-                       return -1;
+                       ret = -1;
+                       goto out;
                }
 
                struct sockaddr_ll ll = {
                }
 
                struct sockaddr_ll ll = {
@@ -234,13 +253,18 @@ int setup_ndp_interface(struct interface *iface, bool enable)
                        dump_neigh_table(false);
                else
                        dump_neigh = false;
                        dump_neigh_table(false);
                else
                        dump_neigh = false;
-       } else
-               close(procfd);
+
+               ndp_rqs_addr6_dump();
+       }
 
        if (dump_neigh)
                dump_neigh_table(true);
 
 
        if (dump_neigh)
                dump_neigh_table(true);
 
-       return 0;
+out:
+       if (procfd >= 0)
+               close(procfd);
+
+       return ret;
 }
 
 
 }
 
 
@@ -252,13 +276,16 @@ static void ping6(struct in6_addr *addr,
        struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *addr, .sin6_scope_id = iface->ifindex, };
        struct icmp6_hdr echo = { .icmp6_type = ICMP6_ECHO_REQUEST };
        struct iovec iov = { .iov_base = &echo, .iov_len = sizeof(echo) };
        struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *addr, .sin6_scope_id = iface->ifindex, };
        struct icmp6_hdr echo = { .icmp6_type = ICMP6_ECHO_REQUEST };
        struct iovec iov = { .iov_base = &echo, .iov_len = sizeof(echo) };
+       char ipbuf[INET6_ADDRSTRLEN];
+
+       inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
+       syslog(LOG_NOTICE, "Pinging for %s%%%s", ipbuf, iface->ifname);
 
        odhcpd_setup_route(addr, 128, iface, NULL, 128, true);
        odhcpd_send(ping_socket, &dest, &iov, 1, iface);
        odhcpd_setup_route(addr, 128, iface, NULL, 128, false);
 }
 
 
        odhcpd_setup_route(addr, 128, iface, NULL, 128, true);
        odhcpd_send(ping_socket, &dest, &iov, 1, iface);
        odhcpd_setup_route(addr, 128, iface, NULL, 128, false);
 }
 
-
 // Handle solicitations
 static void handle_solicit(void *addr, void *data, size_t len,
                struct interface *iface, _unused void *dest)
 // Handle solicitations
 static void handle_solicit(void *addr, void *data, size_t len,
                struct interface *iface, _unused void *dest)
@@ -266,6 +293,8 @@ static void handle_solicit(void *addr, void *data, size_t len,
        struct ip6_hdr *ip6 = data;
        struct nd_neighbor_solicit *req = (struct nd_neighbor_solicit*)&ip6[1];
        struct sockaddr_ll *ll = addr;
        struct ip6_hdr *ip6 = data;
        struct nd_neighbor_solicit *req = (struct nd_neighbor_solicit*)&ip6[1];
        struct sockaddr_ll *ll = addr;
+       char ipbuf[INET6_ADDRSTRLEN];
+       uint8_t mac[6];
 
        // Solicitation is for duplicate address detection
        bool ns_is_dad = IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src);
 
        // Solicitation is for duplicate address detection
        bool ns_is_dad = IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src);
@@ -284,11 +313,9 @@ static void handle_solicit(void *addr, void *data, size_t len,
                        IN6_IS_ADDR_MULTICAST(&req->nd_ns_target))
                return; // Invalid target
 
                        IN6_IS_ADDR_MULTICAST(&req->nd_ns_target))
                return; // Invalid target
 
-       char ipbuf[INET6_ADDRSTRLEN];
        inet_ntop(AF_INET6, &req->nd_ns_target, ipbuf, sizeof(ipbuf));
        inet_ntop(AF_INET6, &req->nd_ns_target, ipbuf, sizeof(ipbuf));
-       syslog(LOG_DEBUG, "Got a NS for %s", ipbuf);
+       syslog(LOG_DEBUG, "Got a NS for %s%%%s", ipbuf, iface->ifname);
 
 
-       uint8_t mac[6];
        odhcpd_get_mac(iface, mac);
        if (!memcmp(ll->sll_addr, mac, sizeof(mac)))
                return; // Looped back
        odhcpd_get_mac(iface, mac);
        if (!memcmp(ll->sll_addr, mac, sizeof(mac)))
                return; // Looped back
@@ -303,10 +330,11 @@ static void handle_solicit(void *addr, void *data, size_t len,
 // Use rtnetlink to modify kernel routes
 static void setup_route(struct in6_addr *addr, struct interface *iface, bool add)
 {
 // Use rtnetlink to modify kernel routes
 static void setup_route(struct in6_addr *addr, struct interface *iface, bool add)
 {
-       char namebuf[INET6_ADDRSTRLEN];
-       inet_ntop(AF_INET6, addr, namebuf, sizeof(namebuf));
-       syslog(LOG_NOTICE, "%s about %s on %s",
-                       (add) ? "Learned" : "Forgot", namebuf, iface->ifname);
+       char ipbuf[INET6_ADDRSTRLEN];
+
+       inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
+       syslog(LOG_NOTICE, "%s about %s%%%s",
+                       (add) ? "Learned" : "Forgot", ipbuf, iface->ifname);
 
        if (iface->learn_routes)
                odhcpd_setup_route(addr, 128, iface, NULL, 1024, add);
 
        if (iface->learn_routes)
                odhcpd_setup_route(addr, 128, iface, NULL, 1024, add);
@@ -316,8 +344,8 @@ static void setup_route(struct in6_addr *addr, struct interface *iface, bool add
 static int prefixcmp(const void *va, const void *vb)
 {
        const struct odhcpd_ipaddr *a = va, *b = vb;
 static int prefixcmp(const void *va, const void *vb)
 {
        const struct odhcpd_ipaddr *a = va, *b = vb;
-       uint32_t a_pref = ((a->addr.s6_addr[0] & 0xfe) != 0xfc) ? a->preferred : 1;
-       uint32_t b_pref = ((b->addr.s6_addr[0] & 0xfe) != 0xfc) ? b->preferred : 1;
+       uint32_t a_pref = IN6_IS_ADDR_ULA(&a->addr) ? 1 : a->preferred;
+       uint32_t b_pref = IN6_IS_ADDR_ULA(&b->addr) ? 1 : b->preferred;
        return (a_pref < b_pref) ? 1 : (a_pref > b_pref) ? -1 : 0;
 }
 
        return (a_pref < b_pref) ? 1 : (a_pref > b_pref) ? -1 : 0;
 }
 
@@ -334,8 +362,6 @@ static void check_addr_updates(struct interface *iface)
        qsort(addr, len, sizeof(*addr), prefixcmp);
 
        for (int i = 0; i < len; ++i) {
        qsort(addr, len, sizeof(*addr), prefixcmp);
 
        for (int i = 0; i < len; ++i) {
-               addr[i].addr.s6_addr32[3] = 0;
-
                if (addr[i].preferred < UINT32_MAX - now)
                        addr[i].preferred += now;
 
                if (addr[i].preferred < UINT32_MAX - now)
                        addr[i].preferred += now;
 
@@ -366,19 +392,29 @@ static void check_addr_updates(struct interface *iface)
        }
 }
 
        }
 }
 
-void setup_addr_for_relaying(struct in6_addr *addr, struct interface *iface, bool add)
+static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *iface, bool add)
 {
        struct interface *c;
 {
        struct interface *c;
+       char ipbuf[INET6_ADDRSTRLEN];
+
+       inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
 
        list_for_each_entry(c, &interfaces, head) {
                if (iface == c || (c->ndp != RELAYD_RELAY && !add))
                        continue;
 
 
        list_for_each_entry(c, &interfaces, head) {
                if (iface == c || (c->ndp != RELAYD_RELAY && !add))
                        continue;
 
-               odhcpd_setup_proxy_neigh(addr, c, c->ndp == RELAYD_RELAY ? add : false);
+               bool neigh_add = (c->ndp == RELAYD_RELAY ? add : false);
+
+               if (odhcpd_setup_proxy_neigh(addr, c, neigh_add))
+                       syslog(LOG_DEBUG, "Failed to %s proxy neighbour entry %s%%%s",
+                               neigh_add ? "add" : "delete", ipbuf, c->ifname);
+               else
+                       syslog(LOG_DEBUG, "%s proxy neighbour entry %s%%%s",
+                               neigh_add ? "Added" : "Deleted", ipbuf, c->ifname);
        }
 }
 
        }
 }
 
-void setup_ping6(struct in6_addr *addr, struct interface *iface)
+static void setup_ping6(struct in6_addr *addr, struct interface *iface)
 {
        struct interface *c;
 
 {
        struct interface *c;
 
@@ -409,6 +445,7 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
        struct in6_addr *addr = NULL;
        struct interface *iface = NULL;
        bool add = false;
        struct in6_addr *addr = NULL;
        struct interface *iface = NULL;
        bool add = false;
+       char ipbuf[INET6_ADDRSTRLEN];
 
        switch (hdr->nlmsg_type) {
        case RTM_NEWROUTE:
 
        switch (hdr->nlmsg_type) {
        case RTM_NEWROUTE:
@@ -449,6 +486,10 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
                                IN6_IS_ADDR_MULTICAST(addr))
                        return NL_SKIP;
 
                                IN6_IS_ADDR_MULTICAST(addr))
                        return NL_SKIP;
 
+               inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
+               syslog(LOG_DEBUG, "Netlink %s %s%%%s", true ? "newaddr" : "deladdr",
+                       ipbuf, iface->ifname);
+
                check_addr_updates(iface);
 
                if (iface->ndp != RELAYD_RELAY)
                check_addr_updates(iface);
 
                if (iface->ndp != RELAYD_RELAY)
@@ -485,6 +526,10 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
                                IN6_IS_ADDR_MULTICAST(addr))
                        return NL_SKIP;
 
                                IN6_IS_ADDR_MULTICAST(addr))
                        return NL_SKIP;
 
+               inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
+               syslog(LOG_DEBUG, "Netlink %s %s%%%s", true ? "newneigh" : "delneigh",
+                       ipbuf, iface->ifname);
+
                if (ndm->ndm_flags & NTF_PROXY) {
                        /* Dump and flush proxy entries */
                        if (hdr->nlmsg_type == RTM_NEWNEIGH) {
                if (ndm->ndm_flags & NTF_PROXY) {
                        /* Dump and flush proxy entries */
                        if (hdr->nlmsg_type == RTM_NEWNEIGH) {
@@ -522,8 +567,22 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
        return NL_OK;
 }
 
        return NL_OK;
 }
 
-static void catch_rtnetlink(int error)
+static void catch_rtnl_err(struct odhcpd_event *e, int error)
 {
 {
-       if (error == ENOBUFS)
-               dump_addr_table();
+       struct event_socket *ev_sock = container_of(e, struct event_socket, ev);
+
+       if (error != ENOBUFS)
+               goto err;
+
+       /* Double netlink event buffer size */
+       ev_sock->sock_bufsize *= 2;
+
+       if (nl_socket_set_buffer_size(ev_sock->sock, ev_sock->sock_bufsize, 0))
+               goto err;
+
+       dump_addr6_table();
+       return;
+
+err:
+       odhcpd_deregister(e);
 }
 }