ndp: create ICMPv6 socket per interface
authorHans Dedecker <dedeckeh@gmail.com>
Thu, 4 Apr 2019 14:57:47 +0000 (16:57 +0200)
committerHans Dedecker <dedeckeh@gmail.com>
Fri, 5 Apr 2019 09:52:30 +0000 (11:52 +0200)
Get rid of the global raw ICMPv6 socket by creating a raw ICMPv6 ping socket
per interface. This fixes an open raw ICMPv6 socket in case all ndp interfaces
are configured as disabled.

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
src/config.c
src/ndp.c
src/odhcpd.h

index 16f003be86b40eef9cf9cad18d6d459d29b39919..64639c91c43c46648205e8b9e80c2b59fb82279c 100644 (file)
@@ -433,6 +433,7 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
                iface->router_event.uloop.fd = -1;
                iface->dhcpv6_event.uloop.fd = -1;
                iface->ndp_event.uloop.fd = -1;
+               iface->ndp_ping_fd = -1;
                iface->dhcpv4_event.uloop.fd = -1;
                set_interface_defaults(iface);
 
index f9f24954fc559f6dbe2212d8ccee4ebf6c7151bd..58fe14fca0d997a8907b849e17aabe19ebb09b07 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
@@ -39,8 +39,6 @@ static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *ifa
 static void handle_solicit(void *addr, void *data, size_t len,
                struct interface *iface, void *dest);
 
-static int ping_socket = -1;
-
 /* Filter ICMPv6 messages of type neighbor soliciation */
 static struct sock_filter bpf[] = {
        BPF_STMT(BPF_LD | BPF_B | BPF_ABS, offsetof(struct ip6_hdr, ip6_nxt)),
@@ -57,55 +55,11 @@ static struct netevent_handler ndp_netevent_handler = { .cb = ndp_netevent_cb, }
 /* Initialize NDP-proxy */
 int ndp_init(void)
 {
-       struct icmp6_filter filt;
-       int val = 2, ret = 0;
-
-       /* Open ICMPv6 socket */
-       ping_socket = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
-       if (ping_socket < 0) {
-               syslog(LOG_ERR, "socket(AF_INET6): %m");
-               ret = -1;
-               goto out;
-       }
+       int ret = 0;
 
-       if (setsockopt(ping_socket, IPPROTO_RAW, IPV6_CHECKSUM,
-                               &val, sizeof(val)) < 0) {
-               syslog(LOG_ERR, "setsockopt(IPV6_CHECKSUM): %m");
+       if (netlink_add_netevent_handler(&ndp_netevent_handler) < 0) {
+               syslog(LOG_ERR, "Failed to add ndp netevent handler");
                ret = -1;
-               goto out;
-       }
-
-       /* This is required by RFC 4861 */
-       val = 255;
-       if (setsockopt(ping_socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
-                               &val, sizeof(val)) < 0) {
-               syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_HOPS): %m");
-               ret = -1;
-               goto out;
-       }
-
-       if (setsockopt(ping_socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
-                               &val, sizeof(val)) < 0) {
-               syslog(LOG_ERR, "setsockopt(IPV6_UNICAST_HOPS): %m");
-               ret = -1;
-               goto out;
-       }
-
-       /* Filter all packages, we only want to send */
-       ICMP6_FILTER_SETBLOCKALL(&filt);
-       if (setsockopt(ping_socket, IPPROTO_ICMPV6, ICMP6_FILTER,
-                               &filt, sizeof(filt)) < 0) {
-               syslog(LOG_ERR, "setsockopt(ICMP6_FILTER): %m");
-               ret = -1;
-               goto out;
-       }
-
-       netlink_add_netevent_handler(&ndp_netevent_handler);
-
-out:
-       if (ret < 0 && ping_socket >= 0) {
-               close(ping_socket);
-               ping_socket = -1;
        }
 
        return ret;
@@ -125,6 +79,11 @@ int ndp_setup_interface(struct interface *iface, bool enable)
                goto out;
        }
 
+       if (iface->ndp_ping_fd >= 0) {
+               close(iface->ndp_ping_fd);
+               iface->ndp_ping_fd = -1;
+       }
+
        if (iface->ndp_event.uloop.fd >= 0) {
                uloop_fd_delete(&iface->ndp_event.uloop);
                close(iface->ndp_event.uloop.fd);
@@ -139,9 +98,59 @@ int ndp_setup_interface(struct interface *iface, bool enable)
        if (enable && iface->ndp == MODE_RELAY) {
                struct sockaddr_ll ll;
                struct packet_mreq mreq;
+               struct icmp6_filter filt;
+               int val = 2;
 
                if (write(procfd, "1\n", 2) < 0) {}
 
+               /* Open ICMPv6 socket */
+               iface->ndp_ping_fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
+               if (iface->ndp_ping_fd < 0) {
+                       syslog(LOG_ERR, "socket(AF_INET6): %m");
+                       ret = -1;
+                       goto out;
+               }
+
+               if (setsockopt(iface->ndp_ping_fd, SOL_SOCKET, SO_BINDTODEVICE,
+                              iface->ifname, strlen(iface->ifname)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(SO_BINDTODEVICE): %m");
+                       ret = -1;
+                       goto out;
+               }
+
+               if (setsockopt(iface->ndp_ping_fd, IPPROTO_RAW, IPV6_CHECKSUM,
+                               &val, sizeof(val)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(IPV6_CHECKSUM): %m");
+                       ret = -1;
+                       goto out;
+               }
+
+               /* This is required by RFC 4861 */
+               val = 255;
+               if (setsockopt(iface->ndp_ping_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+                              &val, sizeof(val)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_HOPS): %m");
+                       ret = -1;
+                       goto out;
+               }
+
+               if (setsockopt(iface->ndp_ping_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+                              &val, sizeof(val)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(IPV6_UNICAST_HOPS): %m");
+                       ret = -1;
+                       goto out;
+               }
+
+               /* Filter all packages, we only want to send */
+               ICMP6_FILTER_SETBLOCKALL(&filt);
+               if (setsockopt(iface->ndp_ping_fd, IPPROTO_ICMPV6, ICMP6_FILTER,
+                              &filt, sizeof(filt)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(ICMP6_FILTER): %m");
+                       ret = -1;
+                       goto out;
+               }
+
+
                iface->ndp_event.uloop.fd = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
                if (iface->ndp_event.uloop.fd < 0) {
                        syslog(LOG_ERR, "socket(AF_PACKET): %m");
@@ -203,9 +212,16 @@ int ndp_setup_interface(struct interface *iface, bool enable)
                netlink_dump_neigh_table(true);
 
  out:
-       if (ret < 0 && iface->ndp_event.uloop.fd >= 0) {
-               close(iface->ndp_event.uloop.fd);
-               iface->ndp_event.uloop.fd = -1;
+       if (ret < 0) {
+               if (iface->ndp_event.uloop.fd >= 0) {
+                       close(iface->ndp_event.uloop.fd);
+                       iface->ndp_event.uloop.fd = -1;
+               }
+
+               if (iface->ndp_ping_fd >= 0) {
+                       close(iface->ndp_ping_fd);
+                       iface->ndp_ping_fd = -1;
+               }
        }
 
        if (procfd >= 0)
@@ -264,7 +280,7 @@ static void ndp_netevent_cb(unsigned long event, struct netevent_handler_info *i
 static void ping6(struct in6_addr *addr,
                const struct interface *iface)
 {
-       struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *addr, .sin6_scope_id = iface->ifindex, };
+       struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *addr , };
        struct icmp6_hdr echo = { .icmp6_type = ICMP6_ECHO_REQUEST };
        struct iovec iov = { .iov_base = &echo, .iov_len = sizeof(echo) };
        char ipbuf[INET6_ADDRSTRLEN];
@@ -273,7 +289,7 @@ static void ping6(struct in6_addr *addr,
        syslog(LOG_NOTICE, "Pinging for %s on %s", ipbuf, iface->name);
 
        netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, true);
-       odhcpd_send(ping_socket, &dest, &iov, 1, iface);
+       odhcpd_send(iface->ndp_ping_fd, &dest, &iov, 1, iface);
        netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, false);
 }
 
index 42d0954baef64b1446a74385caf4255f5d5c81b8..97d2113571326b06c287d49ee6dee38843ad39bf 100644 (file)
@@ -218,6 +218,7 @@ struct interface {
 
        // NDP runtime data
        struct odhcpd_event ndp_event;
+       int ndp_ping_fd;
 
        // IPv4 runtime data
        struct odhcpd_ipaddr *addr4;