ndp: use IPv4 address list when comparing IPv4 addresses
[project/odhcpd.git] / src / ndp.c
index e201ac9..33dde87 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
@@ -33,9 +33,8 @@
 #include <netlink/socket.h>
 #include <netlink/attr.h>
 
 #include <netlink/socket.h>
 #include <netlink/attr.h>
 
-#include "router.h"
 #include "dhcpv6.h"
 #include "dhcpv6.h"
-#include "ndp.h"
+#include "odhcpd.h"
 
 struct event_socket {
        struct odhcpd_event ev;
 
 struct event_socket {
        struct odhcpd_event ev;
@@ -49,7 +48,6 @@ static void handle_rtnl_event(struct odhcpd_event *ev);
 static int cb_rtnl_valid(struct nl_msg *msg, void *arg);
 static void catch_rtnl_err(struct odhcpd_event *e, int error);
 
 static int cb_rtnl_valid(struct nl_msg *msg, void *arg);
 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 = {
 static int ping_socket = -1;
 static struct event_socket rtnl_event = {
        .ev = {
@@ -94,9 +92,10 @@ int init_ndp(void)
        nl_socket_modify_cb(rtnl_event.sock, NL_CB_VALID, NL_CB_CUSTOM,
                        cb_rtnl_valid, NULL);
 
        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);
                goto err;
 
        odhcpd_register(&rtnl_event.ev);
@@ -151,11 +150,11 @@ static void dump_neigh_table(const bool proxy)
        nlmsg_free(msg);
 }
 
        nlmsg_free(msg);
 }
 
-static void dump_addr6_table(void)
+static void dump_addr_table(bool v6)
 {
        struct nl_msg *msg;
        struct ifaddrmsg ifa = {
 {
        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);
        };
 
        msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP);
@@ -169,20 +168,6 @@ static void dump_addr6_table(void)
        nlmsg_free(msg);
 }
 
        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;
 int setup_ndp_interface(struct interface *iface, bool enable)
 {
        int ret = 0, procfd;
@@ -254,8 +239,6 @@ int setup_ndp_interface(struct interface *iface, bool enable)
                        dump_neigh_table(false);
                else
                        dump_neigh = false;
                        dump_neigh_table(false);
                else
                        dump_neigh = false;
-
-               ndp_rqs_addr6_dump();
        }
 
        if (dump_neigh)
        }
 
        if (dump_neigh)
@@ -341,40 +324,37 @@ static void setup_route(struct in6_addr *addr, struct interface *iface, bool add
                odhcpd_setup_route(addr, 128, iface, NULL, 1024, add);
 }
 
                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)
 {
 // 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;
 
 
        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)
 
        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)
                                (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)
@@ -383,19 +363,18 @@ static void check_addr_updates(struct interface *iface)
        if (change)
                dhcpv6_ia_preupdate(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;
 
        iface->ia_addr_len = len;
 
-       if (change)
-               dhcpv6_ia_postupdate(iface, now);
-
        if (change) {
        if (change) {
+               dhcpv6_ia_postupdate(iface);
                syslog(LOG_INFO, "Raising SIGUSR1 due to address change on %s", iface->ifname);
                raise(SIGUSR1);
        }
 }
 
                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];
 {
        struct interface *c;
        char ipbuf[INET6_ADDRSTRLEN];
@@ -406,32 +385,17 @@ void setup_addr_for_relaying(struct in6_addr *addr, struct interface *iface, boo
                if (iface == c || (c->ndp != RELAYD_RELAY && !add))
                        continue;
 
                if (iface == c || (c->ndp != RELAYD_RELAY && !add))
                        continue;
 
-               add = (c->ndp == RELAYD_RELAY ? add : false);
+               bool neigh_add = (c->ndp == RELAYD_RELAY ? add : false);
 
 
-               if (odhcpd_setup_proxy_neigh(addr, c, add))
+               if (odhcpd_setup_proxy_neigh(addr, c, neigh_add))
                        syslog(LOG_DEBUG, "Failed to %s proxy neighbour entry %s%%%s",
                        syslog(LOG_DEBUG, "Failed to %s proxy neighbour entry %s%%%s",
-                               add ? "add" : "delete", ipbuf, iface->ifname);
+                               neigh_add ? "add" : "delete", ipbuf, c->ifname);
                else
                        syslog(LOG_DEBUG, "%s proxy neighbour entry %s%%%s",
                else
                        syslog(LOG_DEBUG, "%s proxy neighbour entry %s%%%s",
-                               add ? "Added" : "Deleted", ipbuf, iface->ifname);
+                               neigh_add ? "Added" : "Deleted", ipbuf, c->ifname);
        }
 }
 
        }
 }
 
-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;
-
-               ping6(addr, c);
-       }
-}
-
-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);
 static void handle_rtnl_event(struct odhcpd_event *e)
 {
        struct event_socket *ev_sock = container_of(e, struct event_socket, ev);
@@ -445,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);
 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];
        struct interface *iface = NULL;
        bool add = false;
        char ipbuf[INET6_ADDRSTRLEN];
@@ -468,12 +432,14 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
 
        case RTM_NEWADDR:
                add = true;
 
        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)) ||
        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);
                        return NL_SKIP;
 
                iface = odhcpd_get_interface_by_index(ifa->ifa_index);
@@ -481,33 +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);
                        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_addr_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 != RELAYD_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;
        }
 
        case RTM_NEWNEIGH:
                add = true;
                break;
        }
 
        case RTM_NEWNEIGH:
                add = true;
+               /* fall through */
        case RTM_DELNEIGH: {
                struct ndmsg *ndm = nlmsg_data(hdr);
                struct nlattr *nla[__NDA_MAX];
        case RTM_DELNEIGH: {
                struct ndmsg *ndm = nlmsg_data(hdr);
                struct nlattr *nla[__NDA_MAX];
@@ -524,20 +505,20 @@ static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
                if (!nla[NDA_DST])
                        return NL_SKIP;
 
                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;
 
                        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) {
                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);
                        }
 
                                dump_neigh_table(false);
                        }
 
@@ -546,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 |
 
                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;
                        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);
 
                if (!add)
                        dump_neigh_table(false);
@@ -583,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;
 
        if (nl_socket_set_buffer_size(ev_sock->sock, ev_sock->sock_bufsize, 0))
                goto err;
 
-       dump_addr6_table();
+       dump_addr_table(true);
        return;
 
 err:
        return;
 
 err: