X-Git-Url: http://git.archive.openwrt.org/?a=blobdiff_plain;f=src%2Fndp.c;h=ec990ed5dff89006cfd1d50dcca3351616431e7f;hb=a827fcad451996f1a7043b479d8c6162174b2038;hp=b4767ea618eebc81f156f0ac77145e22851488d3;hpb=94afe3bf59b7ad6f3f7a4d0de609412b88387586;p=project%2Fodhcpd.git diff --git a/src/ndp.c b/src/ndp.c index b4767ea..ec990ed 100644 --- a/src/ndp.c +++ b/src/ndp.c @@ -92,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, RTNLGRP_LINK, 0)) goto err; odhcpd_register(&rtnl_event.ev); @@ -186,13 +187,13 @@ int setup_ndp_interface(struct interface *iface, bool enable) close(iface->ndp_event.uloop.fd); iface->ndp_event.uloop.fd = -1; - if (!enable || iface->ndp != RELAYD_RELAY) + if (!enable || iface->ndp != MODE_RELAY) if (write(procfd, "0\n", 2) < 0) {} dump_neigh = true; } - if (enable && iface->ndp == RELAYD_RELAY) { + if (enable && iface->ndp == MODE_RELAY) { if (write(procfd, "1\n", 2) < 0) {} int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6)); @@ -285,7 +286,7 @@ static void handle_solicit(void *addr, void *data, size_t len, // Don't process solicit messages on non relay interfaces // Don't forward any non-DAD solicitation for external ifaces // TODO: check if we should even forward DADs for them - if (iface->ndp != RELAYD_RELAY || (iface->external && !ns_is_dad)) + if (iface->ndp != MODE_RELAY || (iface->external && !ns_is_dad)) return; if (len < sizeof(*ip6) + sizeof(*req)) @@ -305,7 +306,7 @@ static void handle_solicit(void *addr, void *data, size_t len, struct interface *c; list_for_each_entry(c, &interfaces, head) - if (iface != c && c->ndp == RELAYD_RELAY && + if (iface != c && c->ndp == MODE_RELAY && (ns_is_dad || !c->external)) ping6(&req->nd_ns_target, c); } @@ -324,6 +325,25 @@ static void setup_route(struct in6_addr *addr, struct interface *iface, bool add } // Check address update +static void check_addr_updates(struct interface *iface) +{ + struct odhcpd_ipaddr *addr = NULL; + ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, false, &addr); + + if (len < 0) + return; + + 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; + + free(iface->addr4); + iface->addr4 = addr; + iface->addr4_len = len; +} + +// Check v6 address update static void check_addr6_updates(struct interface *iface) { struct odhcpd_ipaddr *addr = NULL; @@ -347,10 +367,8 @@ static void check_addr6_updates(struct interface *iface) iface->ia_addr = addr; iface->ia_addr_len = len; - if (change) - dhcpv6_ia_postupdate(iface); - if (change) { + dhcpv6_ia_postupdate(iface); syslog(LOG_INFO, "Raising SIGUSR1 due to address change on %s", iface->ifname); raise(SIGUSR1); } @@ -364,10 +382,10 @@ static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *ifa inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)); list_for_each_entry(c, &interfaces, head) { - if (iface == c || (c->ndp != RELAYD_RELAY && !add)) + if (iface == c || (c->ndp != MODE_RELAY && !add)) continue; - bool neigh_add = (c->ndp == RELAYD_RELAY ? add : false); + bool neigh_add = (c->ndp == MODE_RELAY ? add : false); if (odhcpd_setup_proxy_neigh(addr, c, neigh_add)) syslog(LOG_DEBUG, "Failed to %s proxy neighbour entry %s%%%s", @@ -391,12 +409,35 @@ 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_NEWLINK: { + struct ifinfomsg *ifi = nlmsg_data(hdr); + struct nlattr *nla[__IFLA_MAX]; + + if (!nlmsg_valid_hdr(hdr, sizeof(*ifi)) || + ifi->ifi_family != AF_UNSPEC) + return NL_SKIP; + + nlmsg_parse(hdr, sizeof(struct ifinfomsg), nla, __IFLA_MAX - 1, NULL); + if (!nla[IFLA_IFNAME]) + return NL_SKIP; + + struct interface *iface = odhcpd_get_interface_by_name(nla_data(nla[IFLA_IFNAME])); + if (!iface) + return NL_SKIP; + + if (iface->ifindex != ifi->ifi_index) { + iface->ifindex = ifi->ifi_index; + check_addr_updates(iface); + } + break; + } + case RTM_NEWROUTE: case RTM_DELROUTE: { struct rtmsg *rtm = nlmsg_data(hdr); @@ -409,7 +450,7 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) syslog(LOG_INFO, "Raising SIGUSR1 due to default route change"); raise(SIGUSR1); } - return NL_OK; + break; } case RTM_NEWADDR: @@ -420,7 +461,8 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) 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); @@ -428,28 +470,42 @@ 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; - inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)); - syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr", - ipbuf, iface->ifname); + addr6 = nla_data(nla[IFA_ADDRESS]); + if (!addr6 || IN6_IS_ADDR_LINKLOCAL(addr6) || + IN6_IS_ADDR_MULTICAST(addr6)) + return NL_SKIP; - check_addr6_updates(iface); + inet_ntop(AF_INET6, addr6, ipbuf, sizeof(ipbuf)); + syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr", + ipbuf, iface->ifname); - if (iface->ndp != RELAYD_RELAY) - break; + check_addr6_updates(iface); - /* handle the relay logic below */ - setup_addr_for_relaying(addr, iface, add); + if (iface->ndp != MODE_RELAY) + break; - if (!add) - dump_neigh_table(false); + /* 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; } @@ -465,27 +521,27 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) return NL_SKIP; iface = odhcpd_get_interface_by_index(ndm->ndm_ifindex); - if (!iface || iface->ndp != RELAYD_RELAY) + if (!iface || iface->ndp != MODE_RELAY) return (iface ? NL_OK : NL_SKIP); nlmsg_parse(hdr, sizeof(*ndm), nla, __NDA_MAX - 1, NULL); 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, addr, ipbuf, sizeof(ipbuf)); - syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newneigh" : "delneigh", + 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); } @@ -497,8 +553,8 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) 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);