From 65a9519fb295d8f5748fc69962be1666985011fe Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Thu, 4 Apr 2019 16:57:47 +0200 Subject: [PATCH] ndp: create ICMPv6 socket per interface 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 --- src/config.c | 1 + src/ndp.c | 124 +++++++++++++++++++++++++++++---------------------- src/odhcpd.h | 1 + 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/src/config.c b/src/config.c index 16f003b..64639c9 100644 --- a/src/config.c +++ b/src/config.c @@ -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); diff --git a/src/ndp.c b/src/ndp.c index f9f2495..58fe14f 100644 --- 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); } diff --git a/src/odhcpd.h b/src/odhcpd.h index 42d0954..97d2113 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -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; -- 2.25.1