X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fodhcpd.git;a=blobdiff_plain;f=src%2Fndp.c;h=44884be7395f83aa8c90c50d1f20eea393475c14;hp=150d3600920f9faf9cfb03c3684b0003c0dcfadf;hb=98b41e5b07b28b438fb0be45ba294a932f4c849b;hpb=0850938d569998bf4a570d834e73aeba08646f61;ds=sidebyside diff --git a/src/ndp.c b/src/ndp.c index 150d360..44884be 100644 --- a/src/ndp.c +++ b/src/ndp.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -32,9 +33,9 @@ static void handle_solicit(void *addr, void *data, size_t len, - struct interface *iface); + struct interface *iface, void *dest); static void handle_rtnetlink(void *addr, void *data, size_t len, - struct interface *iface); + struct interface *iface, void *dest); static struct ndp_neighbor* find_neighbor(struct in6_addr *addr, bool strict); static void modify_neighbor(struct in6_addr *addr, struct interface *iface, bool add); @@ -90,27 +91,12 @@ int init_ndp(void) send(rtnl_event.uloop.fd, &req2, sizeof(req2), MSG_DONTWAIT); odhcpd_register(&rtnl_event); - - // Create socket for intercepting NDP - int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, - htons(ETH_P_ALL)); // ETH_P_ALL for ingress + egress - if (sock < 0) { - syslog(LOG_ERR, "Unable to open packet socket: %s", - strerror(errno)); - return -1; - } - - 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; - } - - ndp_event.uloop.fd = sock; - odhcpd_register(&ndp_event); - // Open ICMPv6 socket ping_socket = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); + if (ping_socket < 0) { + syslog(LOG_ERR, "Unable to open raw socket: %s", strerror(errno)); + return -1; + } int val = 2; setsockopt(ping_socket, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); @@ -167,6 +153,7 @@ int setup_ndp_interface(struct interface *iface, bool enable) memcpy(entry, iface->static_ndp, iface->static_ndp_len); for (entry = strtok_r(entry, " ", &saveptr); entry; entry = strtok_r(NULL, " ", &saveptr)) { + char *sep; struct ndp_neighbor *n = malloc(sizeof(*n)); if (!n) { syslog(LOG_ERR, "Malloc failed for static NDP-prefix %s", entry); @@ -176,18 +163,59 @@ int setup_ndp_interface(struct interface *iface, bool enable) n->iface = iface; n->timeout = 0; - char ipbuf[INET6_ADDRSTRLEN]; - if (sscanf(entry, "%45s/%hhu", ipbuf, &n->len) < 2 - || n->len > 128 || inet_pton(AF_INET6, ipbuf, &n->addr) != 1) { + sep = strchr(entry, '/'); + if (!sep) { + free(n); syslog(LOG_ERR, "Invalid static NDP-prefix %s", entry); return -1; } + + *sep = 0; + n->len = atoi(sep + 1); + if (inet_pton(AF_INET6, entry, &n->addr) != 1 || n->len > 128) { + free(n); + syslog(LOG_ERR, "Invalid static NDP-prefix %s/%s", entry, sep + 1); + return -1; + } list_add(&n->head, &neighbors); } } } + bool enable_packet = false; + struct interface *i; + list_for_each_entry(i, &interfaces, head) { + if (i == iface && !enable) + continue; + + if (i->ndp == RELAYD_RELAY) + enable_packet = true; + } + + if (enable_packet && ndp_event.uloop.fd < 0) { + // Create socket for intercepting NDP + int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + htons(ETH_P_ALL)); // ETH_P_ALL for ingress + egress + if (sock < 0) { + syslog(LOG_ERR, "Unable to open packet socket: %s", + strerror(errno)); + return -1; + } + + 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; + } + + ndp_event.uloop.fd = sock; + odhcpd_register(&ndp_event); + } else if (!enable_packet && ndp_event.uloop.fd >= 0) { + close(ndp_event.uloop.fd); + ndp_event.uloop.fd = -1; + } + return 0; } @@ -210,7 +238,7 @@ static ssize_t ping6(struct in6_addr *addr, // Handle solicitations static void handle_solicit(void *addr, void *data, size_t len, - struct interface *iface) + struct interface *iface, _unused void *dest) { struct ip6_hdr *ip6 = data; struct nd_neighbor_solicit *req = (struct nd_neighbor_solicit*)&ip6[1]; @@ -234,7 +262,7 @@ static void handle_solicit(void *addr, void *data, size_t len, char ipbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &req->nd_ns_target, ipbuf, sizeof(ipbuf)); - syslog(LOG_NOTICE, "Got a NS for %s", ipbuf); + syslog(LOG_DEBUG, "Got a NS for %s", ipbuf); uint8_t mac[6]; odhcpd_get_mac(iface, mac); @@ -246,7 +274,7 @@ static void handle_solicit(void *addr, void *data, size_t len, struct ndp_neighbor *n = find_neighbor(&req->nd_ns_target, false); if (n && (n->iface || abs(n->timeout - now) < 5)) { - syslog(LOG_NOTICE, "%s is on %s", ipbuf, + syslog(LOG_DEBUG, "%s is on %s", ipbuf, (n->iface) ? n->iface->ifname : ""); if (!n->iface || n->iface == iface) return; @@ -364,41 +392,12 @@ static void free_neighbor(struct ndp_neighbor *n) --neighbor_count; } - -static bool match_neighbor(struct ndp_neighbor *n, struct in6_addr *addr) -{ - if (n->len <= 32) - return ntohl(n->addr.s6_addr32[0]) >> (32 - n->len) == - ntohl(addr->s6_addr32[0]) >> (32 - n->len); - - if (n->addr.s6_addr32[0] != addr->s6_addr32[0]) - return false; - - if (n->len <= 64) - return ntohl(n->addr.s6_addr32[1]) >> (64 - n->len) == - ntohl(addr->s6_addr32[1]) >> (64 - n->len); - - if (n->addr.s6_addr32[1] != addr->s6_addr32[1]) - return false; - - if (n->len <= 96) - return ntohl(n->addr.s6_addr32[2]) >> (96 - n->len) == - ntohl(addr->s6_addr32[2]) >> (96 - n->len); - - if (n->addr.s6_addr32[2] != addr->s6_addr32[2]) - return false; - - return ntohl(n->addr.s6_addr32[3]) >> (128 - n->len) == - ntohl(addr->s6_addr32[3]) >> (128 - n->len); -} - - static struct ndp_neighbor* find_neighbor(struct in6_addr *addr, bool strict) { time_t now = time(NULL); struct ndp_neighbor *n, *e; list_for_each_entry_safe(n, e, &neighbors, head) { - if ((!strict && match_neighbor(n, addr)) || + if ((!strict && !odhcpd_bmemcmp(&n->addr, addr, n->len)) || (n->len == 128 && IN6_ARE_ADDR_EQUAL(&n->addr, addr))) return n; @@ -451,7 +450,7 @@ static void modify_neighbor(struct in6_addr *addr, // Handler for neighbor cache entries from the kernel. This is our source // to learn and unlearn hosts on interfaces. static void handle_rtnetlink(_unused void *addr, void *data, size_t len, - _unused struct interface *iface) + _unused struct interface *iface, _unused void *dest) { for (struct nlmsghdr *nh = data; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {