X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fodhcpd.git;a=blobdiff_plain;f=src%2Fndp.c;h=97a471338ca843a71f6a126379b8071e93fbe5d1;hp=882487991f3c9d0f6d7545feebe75c03bf2f8683;hb=adc8f6269d82f526e225fd8d4b78388fc2da0659;hpb=8a615ad3c4f2318667630e2505888df09901320d diff --git a/src/ndp.c b/src/ndp.c index 8824879..97a4713 100644 --- a/src/ndp.c +++ b/src/ndp.c @@ -33,30 +33,32 @@ #include #include -#include "router.h" #include "dhcpv6.h" -#include "ndp.h" +#include "odhcpd.h" 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 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, - .handle_error = catch_rtnetlink, + .handle_error = catch_rtnl_err, .recv_msgs = handle_rtnl_event, }, .sock = NULL, + .sock_bufsize = 133120, }; // 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) { - int val = 256 * 1024; + int val = 2; 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); - 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); @@ -105,7 +107,6 @@ int init_ndp(void) return -1; } - val = 2; 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); } -static void dump_addr_table(void) +static void dump_addr6_table(void) { struct nl_msg *msg; struct ifaddrmsg ifa = { @@ -167,12 +168,33 @@ static void dump_addr_table(void) 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 ret = 0, procfd; + bool dump_neigh = false; char procbuf[64]; + 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); @@ -185,19 +207,15 @@ int setup_ndp_interface(struct interface *iface, bool enable) 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) {} - 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)); - return -1; + ret = -1; + goto out; } #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)); - return -1; + ret = -1; + goto out; } 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; - } else - close(procfd); + + ndp_rqs_addr6_dump(); + } 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) }; + 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); } - // 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; + 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); @@ -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 - char ipbuf[INET6_ADDRSTRLEN]; 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 @@ -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) { - 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); @@ -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; - 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; } @@ -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) { - addr[i].addr.s6_addr32[3] = 0; - 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; + 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; - odhcpd_setup_proxy_neigh(addr, c, c->ndp == RELAYD_RELAY ? add : false); + add = (c->ndp == RELAYD_RELAY ? add : false); + + if (odhcpd_setup_proxy_neigh(addr, c, add)) + syslog(LOG_DEBUG, "Failed to %s proxy neighbour entry %s%%%s", + add ? "add" : "delete", ipbuf, iface->ifname); + else + syslog(LOG_DEBUG, "%s proxy neighbour entry %s%%%s", + add ? "Added" : "Deleted", ipbuf, iface->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; @@ -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; + char ipbuf[INET6_ADDRSTRLEN]; 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; + 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) @@ -485,6 +526,10 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) 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) { @@ -522,8 +567,22 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) 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); }