X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fodhcpd.git;a=blobdiff_plain;f=src%2Fndp.c;h=33dde872f73e801ac8db8a1255aa186136fa02df;hp=586658846f87b1367d0dda63823d1e558fbe6c40;hb=94e65ee0aecf0749e64fb29b4532c614e7be86a8;hpb=68ee0b59040625d2ab5830b1c1cc8eca2da0dcf1 diff --git a/src/ndp.c b/src/ndp.c index 5866588..33dde87 100644 --- a/src/ndp.c +++ b/src/ndp.c @@ -33,9 +33,8 @@ #include #include -#include "router.h" #include "dhcpv6.h" -#include "ndp.h" +#include "odhcpd.h" struct event_socket { struct odhcpd_event ev; @@ -93,9 +92,10 @@ int init_ndp(void) nl_socket_modify_cb(rtnl_event.sock, NL_CB_VALID, NL_CB_CUSTOM, cb_rtnl_valid, NULL); - // Receive IPv6 address, IPv6 routes and neighbor events - if (nl_socket_add_memberships(rtnl_event.sock, RTNLGRP_IPV6_IFADDR, - RTNLGRP_IPV6_ROUTE, RTNLGRP_NEIGH, 0)) + // Receive IPv4 address, IPv6 address, IPv6 routes and neighbor events + if (nl_socket_add_memberships(rtnl_event.sock, RTNLGRP_IPV4_IFADDR, + RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE, + RTNLGRP_NEIGH, 0)) goto err; odhcpd_register(&rtnl_event.ev); @@ -150,11 +150,11 @@ static void dump_neigh_table(const bool proxy) nlmsg_free(msg); } -static void dump_addr_table(void) +static void dump_addr_table(bool v6) { struct nl_msg *msg; struct ifaddrmsg ifa = { - .ifa_family = AF_INET6, + .ifa_family = v6 ? AF_INET6 : AF_INET, }; msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP); @@ -193,10 +193,6 @@ 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) {} @@ -264,13 +260,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) @@ -278,6 +277,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); @@ -296,11 +297,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 @@ -315,49 +314,47 @@ 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); } -// compare prefixes -static int prefixcmp(const void *va, const void *vb) -{ - const struct odhcpd_ipaddr *a = va, *b = vb; - 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; -} - // Check address update static void check_addr_updates(struct interface *iface) { - struct odhcpd_ipaddr addr[RELAYD_MAX_ADDRS] = {{IN6ADDR_ANY_INIT, 0, 0, 0, 0}}; - time_t now = odhcpd_time(); - ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, addr, ARRAY_SIZE(addr)); + struct odhcpd_ipaddr *addr = NULL; + ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, false, &addr); if (len < 0) return; - qsort(addr, len, sizeof(*addr), prefixcmp); + bool change = len != (ssize_t)iface->addr4_len; + for (ssize_t i = 0; !change && i < len; ++i) + if (addr[i].addr.in.s_addr != iface->addr4[i].addr.in.s_addr) + change = true; - for (int i = 0; i < len; ++i) { - addr[i].addr.s6_addr32[3] = 0; + free(iface->addr4); + iface->addr4 = addr; + iface->addr4_len = len; +} - if (addr[i].preferred < UINT32_MAX - now) - addr[i].preferred += now; +// Check v6 address update +static void check_addr6_updates(struct interface *iface) +{ + struct odhcpd_ipaddr *addr = NULL; + ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, true, &addr); - if (addr[i].valid < UINT32_MAX - now) - addr[i].valid += now; - } + if (len < 0) + return; bool change = len != (ssize_t)iface->ia_addr_len; for (ssize_t i = 0; !change && i < len; ++i) - if (!IN6_ARE_ADDR_EQUAL(&addr[i].addr, &iface->ia_addr[i].addr) || + if (!IN6_ARE_ADDR_EQUAL(&addr[i].addr.in6, &iface->ia_addr[i].addr.in6) || (addr[i].preferred > 0) != (iface->ia_addr[i].preferred > 0) || addr[i].valid < iface->ia_addr[i].valid || addr[i].preferred < iface->ia_addr[i].preferred) @@ -366,45 +363,39 @@ static void check_addr_updates(struct interface *iface) if (change) dhcpv6_ia_preupdate(iface); - memcpy(iface->ia_addr, addr, len * sizeof(*addr)); + free(iface->ia_addr); + iface->ia_addr = addr; iface->ia_addr_len = len; - if (change) - dhcpv6_ia_postupdate(iface, now); - if (change) { + dhcpv6_ia_postupdate(iface); syslog(LOG_INFO, "Raising SIGUSR1 due to address change on %s", iface->ifname); raise(SIGUSR1); } } -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); - } -} - -void setup_ping6(struct in6_addr *addr, struct interface *iface) -{ - struct interface *c; - - list_for_each_entry(c, &interfaces, head) { - if (iface == c || c->ndp != RELAYD_RELAY || - c->external == true) - continue; + bool neigh_add = (c->ndp == RELAYD_RELAY ? add : false); - ping6(addr, c); + 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); } } -static struct in6_addr last_solicited; - static void handle_rtnl_event(struct odhcpd_event *e) { struct event_socket *ev_sock = container_of(e, struct event_socket, ev); @@ -418,9 +409,10 @@ static void handle_rtnl_event(struct odhcpd_event *e) static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) { struct nlmsghdr *hdr = nlmsg_hdr(msg); - struct in6_addr *addr = NULL; + struct in6_addr *addr6 = NULL; struct interface *iface = NULL; bool add = false; + char ipbuf[INET6_ADDRSTRLEN]; switch (hdr->nlmsg_type) { case RTM_NEWROUTE: @@ -440,12 +432,14 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) case RTM_NEWADDR: add = true; + /* fall through */ case RTM_DELADDR: { struct ifaddrmsg *ifa = nlmsg_data(hdr); struct nlattr *nla[__IFA_MAX]; if (!nlmsg_valid_hdr(hdr, sizeof(*ifa)) || - ifa->ifa_family != AF_INET6) + (ifa->ifa_family != AF_INET6 && + ifa->ifa_family != AF_INET)) return NL_SKIP; iface = odhcpd_get_interface_by_index(ifa->ifa_index); @@ -453,29 +447,48 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) return NL_SKIP; nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL); - if (!nla[IFA_ADDRESS]) - return NL_SKIP; - addr = nla_data(nla[IFA_ADDRESS]); - if (!addr || IN6_IS_ADDR_LINKLOCAL(addr) || - IN6_IS_ADDR_MULTICAST(addr)) - return NL_SKIP; + if (ifa->ifa_family == AF_INET6) { + if (!nla[IFA_ADDRESS]) + return NL_SKIP; - check_addr_updates(iface); + addr6 = nla_data(nla[IFA_ADDRESS]); + if (!addr6 || IN6_IS_ADDR_LINKLOCAL(addr6) || + IN6_IS_ADDR_MULTICAST(addr6)) + return NL_SKIP; - if (iface->ndp != RELAYD_RELAY) - break; + inet_ntop(AF_INET6, addr6, ipbuf, sizeof(ipbuf)); + syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr", + ipbuf, iface->ifname); - /* handle the relay logic below */ - setup_addr_for_relaying(addr, iface, add); + check_addr6_updates(iface); - if (!add) - dump_neigh_table(false); + if (iface->ndp != RELAYD_RELAY) + break; + + /* handle the relay logic below */ + setup_addr_for_relaying(addr6, iface, add); + + if (!add) + dump_neigh_table(false); + } else { + if (!nla[IFA_LOCAL]) + return NL_SKIP; + + struct in_addr *addr = nla_data(nla[IFA_ADDRESS]); + + inet_ntop(AF_INET, addr, ipbuf, sizeof(ipbuf)); + syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr", + ipbuf, iface->ifname); + + check_addr_updates(iface); + } break; } case RTM_NEWNEIGH: add = true; + /* fall through */ case RTM_DELNEIGH: { struct ndmsg *ndm = nlmsg_data(hdr); struct nlattr *nla[__NDA_MAX]; @@ -492,16 +505,20 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) if (!nla[NDA_DST]) return NL_SKIP; - addr = nla_data(nla[NDA_DST]); - if (!addr || IN6_IS_ADDR_LINKLOCAL(addr) || - IN6_IS_ADDR_MULTICAST(addr)) + addr6 = nla_data(nla[NDA_DST]); + if (!addr6 || IN6_IS_ADDR_LINKLOCAL(addr6) || + IN6_IS_ADDR_MULTICAST(addr6)) return NL_SKIP; + inet_ntop(AF_INET6, addr6, 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) { - odhcpd_setup_proxy_neigh(addr, iface, false); - setup_route(addr, iface, false); + odhcpd_setup_proxy_neigh(addr6, iface, false); + setup_route(addr6, iface, false); dump_neigh_table(false); } @@ -510,17 +527,11 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) if (add && !(ndm->ndm_state & (NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE | - NUD_PERMANENT | NUD_NOARP))) { - if (!IN6_ARE_ADDR_EQUAL(&last_solicited, addr)) { - last_solicited = *addr; - setup_ping6(addr, iface); - } - + NUD_PERMANENT | NUD_NOARP))) return NL_OK; - } - setup_addr_for_relaying(addr, iface, add); - setup_route(addr, iface, add); + setup_addr_for_relaying(addr6, iface, add); + setup_route(addr6, iface, add); if (!add) dump_neigh_table(false); @@ -547,7 +558,7 @@ static void catch_rtnl_err(struct odhcpd_event *e, int error) if (nl_socket_set_buffer_size(ev_sock->sock, ev_sock->sock_bufsize, 0)) goto err; - dump_addr_table(); + dump_addr_table(true); return; err: