ubus: display accept reconf status for DHCPv6 assignments
[project/odhcpd.git] / src / ndp.c
index 94b7e96..e0e865f 100644 (file)
--- 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, 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,7 +409,7 @@ 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];
@@ -420,7 +438,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 +447,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", true ? "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 +498,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));
+               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 +530,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);