treewide: rework handling of netlink events
authorHans Dedecker <dedeckeh@gmail.com>
Tue, 17 Oct 2017 14:23:35 +0000 (16:23 +0200)
committerHans Dedecker <dedeckeh@gmail.com>
Thu, 9 Nov 2017 14:25:06 +0000 (15:25 +0100)
Rework the handling of netlink events by letting the different
modules ndp, ra, dhcpv6 and dhcpv4 install netevent handlers.
The installed netevent handlers are called by the netlink logic
passing an event indication together with event data.
Each netevent handler implements its own event logic; this
makes the code more modular and less complex by moving all
netlink code to netlink.c
While at it rename ia_addr and ia_addr_len into addr6 and
addr6_len respectively

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
src/config.c
src/dhcpv4.c
src/dhcpv6-ia.c
src/dhcpv6.c
src/dhcpv6.h
src/ndp.c
src/netlink.c
src/odhcpd.c
src/odhcpd.h
src/router.c

index 2f51d3396045ac581e0e52115a0e944719de08c5..7e23eb28893ed8e1c9af4656261f539ff5e4592e 100644 (file)
@@ -243,7 +243,7 @@ static void close_interface(struct interface *iface)
 
        clean_interface(iface);
        free(iface->addr4);
-       free(iface->ia_addr);
+       free(iface->addr6);
        free(iface->ifname);
        free(iface);
 }
@@ -450,10 +450,10 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
 
        if (get_addrs) {
                ssize_t len = netlink_get_interface_addrs(iface->ifindex,
-                                               true, &iface->ia_addr);
+                                               true, &iface->addr6);
 
                if (len > 0)
-                       iface->ia_addr_len = len;
+                       iface->addr6_len = len;
 
                len = netlink_get_interface_addrs(iface->ifindex,
                                                false, &iface->addr4);
index c8be632def5877922f250d8c86f92477416a6477..166582e94ebe2fe971c97c457b0f7ad2a7a1a61b 100644 (file)
 #include "dhcpv4.h"
 #include "dhcpv6.h"
 
+static void dhcpv4_netevent_cb(unsigned long event, struct netevent_handler_info *info);
 static int setup_dhcpv4_addresses(struct interface *iface);
 static void update_static_assignments(struct interface *iface);
 static void valid_until_cb(struct uloop_timeout *event);
+static void handle_addrlist_change(struct interface *iface);
 static void free_dhcpv4_assignment(struct dhcpv4_assignment *a);
 static void dhcpv4_fr_start(struct dhcpv4_assignment *a);
 static void dhcpv4_fr_stop(struct dhcpv4_assignment *a);
@@ -48,6 +50,7 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                uint32_t *leasetime, const char *hostname, const size_t hostname_len,
                const bool accept_fr_nonce, bool *incl_fr_opt, uint32_t *fr_serverid);
 
+static struct netevent_handler dhcpv4_netevent_handler = { .cb = dhcpv4_netevent_cb, };
 static struct uloop_timeout valid_until_timeout = {.cb = valid_until_cb};
 static uint32_t serial = 0;
 
@@ -61,6 +64,8 @@ struct odhcpd_ref_ip {
 int dhcpv4_init(void)
 {
        uloop_timeout_set(&valid_until_timeout, 1000);
+       netlink_add_netevent_handler(&dhcpv4_netevent_handler);
+
        return 0;
 }
 
@@ -126,6 +131,26 @@ int dhcpv4_setup_interface(struct interface *iface, bool enable)
        return 0;
 }
 
+
+static void dhcpv4_netevent_cb(unsigned long event, struct netevent_handler_info *info)
+{
+       struct interface *iface = info->iface;
+
+       if (!iface || iface->dhcpv4 == MODE_DISABLED)
+               return;
+
+       switch (event) {
+       case NETEV_IFINDEX_CHANGE:
+               dhcpv4_setup_interface(iface, true);
+               break;
+       case NETEV_ADDRLIST_CHANGE:
+               handle_addrlist_change(iface);
+               break;
+       default:
+               break;
+       }
+}
+
 static struct dhcpv4_assignment *find_assignment_by_hwaddr(struct interface *iface, const uint8_t *hwaddr)
 {
        struct dhcpv4_assignment *a;
@@ -293,7 +318,7 @@ static void decr_ref_cnt_ip(struct odhcpd_ref_ip **ptr, struct interface *iface)
        struct odhcpd_ref_ip *ip = *ptr;
 
        if (--ip->ref_cnt == 0) {
-               netlink_setup_addr(&ip->addr, iface, false, false);
+               netlink_setup_addr(&ip->addr, iface->ifindex, false, false);
 
                list_del(&ip->head);
                free(ip);
@@ -344,11 +369,8 @@ static void valid_until_cb(struct uloop_timeout *event)
        uloop_timeout_set(event, 1000);
 }
 
-void dhcpv4_addr_update(struct interface *iface)
+static void handle_addrlist_change(struct interface *iface)
 {
-       if (iface->dhcpv4 == MODE_DISABLED)
-               return;
-
        struct odhcpd_ipaddr ip;
        struct odhcpd_ref_ip *a;
        struct dhcpv4_assignment *c;
@@ -373,7 +395,7 @@ void dhcpv4_addr_update(struct interface *iface)
 
        a = list_first_entry(&iface->dhcpv4_fr_ips, struct odhcpd_ref_ip, head);
 
-       if (netlink_setup_addr(&a->addr, iface, false, true)) {
+       if (netlink_setup_addr(&a->addr, iface->ifindex, false, true)) {
                syslog(LOG_ERR, "Failed to add ip address");
                return;
        }
index 6568c69446655d8922aeb4ae74a0923ad8df2a26..533af009c9b47eb6476a42a055fe78810da74688 100644 (file)
     ((iface)->dhcpv6_assignall || (i) == (m) || \
      (addrs)[(i)].prefix > 64)
 
+static void dhcpv6_netevent_cb(unsigned long event, struct netevent_handler_info *info);
 static void free_dhcpv6_assignment(struct dhcpv6_assignment *c);
+static void handle_addrlist_change(struct netevent_handler_info *info);
+static void start_reconf(struct dhcpv6_assignment *a);
 static void stop_reconf(struct dhcpv6_assignment *a);
 static void valid_until_cb(struct uloop_timeout *event);
 
+static struct netevent_handler dhcpv6_netevent_handler = { .cb = dhcpv6_netevent_cb, };
 static struct uloop_timeout valid_until_timeout = {.cb = valid_until_cb};
 static uint32_t serial = 0;
 static uint8_t statemd5[16];
@@ -50,6 +54,9 @@ static uint8_t statemd5[16];
 int dhcpv6_ia_init(void)
 {
        uloop_timeout_set(&valid_until_timeout, 1000);
+
+       netlink_add_netevent_handler(&dhcpv6_netevent_handler);
+
        return 0;
 }
 
@@ -134,6 +141,24 @@ int dhcpv6_setup_ia_interface(struct interface *iface, bool enable)
        return 0;
 }
 
+
+static void dhcpv6_netevent_cb(unsigned long event, struct netevent_handler_info *info)
+{
+       struct interface *iface = info->iface;
+
+       if (!iface || iface->dhcpv6 != MODE_SERVER)
+               return;
+
+       switch (event) {
+       case NETEV_ADDR6LIST_CHANGE:
+               handle_addrlist_change(info);
+               break;
+       default:
+               break;
+       }
+}
+
+
 static void free_dhcpv6_assignment(struct dhcpv6_assignment *c)
 {
        if (c->managed_sock.fd.registered) {
@@ -243,8 +268,8 @@ static int send_reconf(struct dhcpv6_assignment *assign)
 void dhcpv6_enum_ia_addrs(struct interface *iface, struct dhcpv6_assignment *c,
                                time_t now, dhcpv6_binding_cb_handler_t func, void *arg)
 {
-       struct odhcpd_ipaddr *addrs = (c->managed) ? c->managed : iface->ia_addr;
-       size_t addrlen = (c->managed) ? (size_t)c->managed_size : iface->ia_addr_len;
+       struct odhcpd_ipaddr *addrs = (c->managed) ? c->managed : iface->addr6;
+       size_t addrlen = (c->managed) ? (size_t)c->managed_size : iface->addr6_len;
        size_t m = get_preferred_addr(addrs, addrlen);
 
        for (size_t i = 0; i < addrlen; ++i) {
@@ -441,24 +466,29 @@ void dhcpv6_write_statefile(void)
        }
 }
 
-
-static void apply_lease(struct interface *iface, struct dhcpv6_assignment *a, bool add)
+static void __apply_lease(struct interface *iface, struct dhcpv6_assignment *a,
+               struct odhcpd_ipaddr *addrs, ssize_t addr_len, bool add)
 {
-       if (a->length > 64 || a->managed_size < 0)
+       if (a->length > 64)
                return;
 
-       struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->ia_addr;
-       size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->ia_addr_len;
-
-       for (size_t i = 0; i < addrlen; ++i) {
+       for (ssize_t i = 0; i < addr_len; ++i) {
                struct in6_addr prefix = addrs[i].addr.in6;
                prefix.s6_addr32[1] |= htonl(a->assigned);
                prefix.s6_addr32[2] = prefix.s6_addr32[3] = 0;
                netlink_setup_route(&prefix, (a->managed_size) ? addrs[i].prefix : a->length,
-                               iface, &a->peer.sin6_addr, 1024, add);
+                               iface->ifindex, &a->peer.sin6_addr, 1024, add);
        }
 }
 
+static void apply_lease(struct interface *iface, struct dhcpv6_assignment *a, bool add)
+{
+       struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->addr6;
+       ssize_t addrlen = (a->managed) ? a->managed_size : (ssize_t)iface->addr6_len;
+
+       __apply_lease(iface, a, addrs, addrlen, add);
+}
+
 /* More data was received from TCP connection */
 static void managed_handle_pd_data(struct ustream *s, _unused int bytes_new)
 {
@@ -577,7 +607,7 @@ static bool assign_pd(struct interface *iface, struct dhcpv6_assignment *assign)
                }
 
                return false;
-       } else if (iface->ia_addr_len < 1)
+       } else if (iface->addr6_len < 1)
                return false;
 
        /* Try honoring the hint first */
@@ -655,74 +685,33 @@ static bool assign_na(struct interface *iface, struct dhcpv6_assignment *assign)
        return false;
 }
 
-void dhcpv6_ia_preupdate(struct interface *iface)
+static void handle_addrlist_change(struct netevent_handler_info *info)
 {
-       if (iface->dhcpv6 != MODE_SERVER)
-               return;
-
-       struct dhcpv6_assignment *c, *border = list_last_entry(
+       struct interface *iface = info->iface;
+       struct dhcpv6_assignment *c, *d, *border = list_last_entry(
                        &iface->ia_assignments, struct dhcpv6_assignment, head);
+       time_t now = odhcpd_time();
+       int minprefix = -1;
 
        list_for_each_entry(c, &iface->ia_assignments, head)
                if (c != border && iface->ra_managed == RA_MANAGED_NO_MFLAG
                                && (c->flags & OAF_BOUND))
-                       apply_lease(iface, c, false);
-}
-
-static void reconf_timeout_cb(struct uloop_timeout *event)
-{
-       struct dhcpv6_assignment *a = container_of(event, struct dhcpv6_assignment, reconf_timer);
-
-       if (a->reconf_cnt > 0 && a->reconf_cnt < DHCPV6_REC_MAX_RC) {
-               send_reconf(a);
-               uloop_timeout_set(&a->reconf_timer,
-                                       DHCPV6_REC_TIMEOUT << a->reconf_cnt);
-               a->reconf_cnt++;
-       } else
-               stop_reconf(a);
-}
-
-static void start_reconf(struct dhcpv6_assignment *a)
-{
-       uloop_timeout_set(&a->reconf_timer,
-                               DHCPV6_REC_TIMEOUT << a->reconf_cnt);
-       a->reconf_timer.cb = reconf_timeout_cb;
-       a->reconf_cnt++;
-
-       send_reconf(a);
-}
-
-static void stop_reconf(struct dhcpv6_assignment *a)
-{
-       uloop_timeout_cancel(&a->reconf_timer);
-       a->reconf_cnt = 0;
-       a->reconf_timer.cb = NULL;
-}
-
-void dhcpv6_ia_postupdate(struct interface *iface)
-{
-       if (iface->dhcpv6 != MODE_SERVER)
-               return;
-
-       time_t now = odhcpd_time();
-       int minprefix = -1;
-       for (size_t i = 0; i < iface->ia_addr_len; ++i) {
-               if (iface->ia_addr[i].preferred > (uint32_t)now &&
-                               iface->ia_addr[i].prefix < 64 &&
-                               iface->ia_addr[i].prefix > minprefix)
-                       minprefix = iface->ia_addr[i].prefix;
+                       __apply_lease(iface, c, info->addrs_old.addrs,
+                                       info->addrs_old.len, false);
+
+       for (size_t i = 0; i < iface->addr6_len; ++i) {
+               if (iface->addr6[i].preferred > (uint32_t)now &&
+                               iface->addr6[i].prefix < 64 &&
+                               iface->addr6[i].prefix > minprefix)
+                       minprefix = iface->addr6[i].prefix;
        }
 
-       struct dhcpv6_assignment *border = list_last_entry(
-                       &iface->ia_assignments, struct dhcpv6_assignment, head);
-
        if (minprefix > 32 && minprefix <= 64)
                border->assigned = 1U << (64 - minprefix);
        else
                border->assigned = 0;
 
        struct list_head reassign = LIST_HEAD_INIT(reassign);
-       struct dhcpv6_assignment *c, *d;
        list_for_each_entry_safe(c, d, &iface->ia_assignments, head) {
                if (c->clid_len == 0 || (!INFINITE_VALID(c->valid_until) && c->valid_until < now) ||
                                c->managed_size)
@@ -757,6 +746,36 @@ void dhcpv6_ia_postupdate(struct interface *iface)
        dhcpv6_write_statefile();
 }
 
+static void reconf_timeout_cb(struct uloop_timeout *event)
+{
+       struct dhcpv6_assignment *a = container_of(event, struct dhcpv6_assignment, reconf_timer);
+
+       if (a->reconf_cnt > 0 && a->reconf_cnt < DHCPV6_REC_MAX_RC) {
+               send_reconf(a);
+               uloop_timeout_set(&a->reconf_timer,
+                                       DHCPV6_REC_TIMEOUT << a->reconf_cnt);
+               a->reconf_cnt++;
+       } else
+               stop_reconf(a);
+}
+
+static void start_reconf(struct dhcpv6_assignment *a)
+{
+       uloop_timeout_set(&a->reconf_timer,
+                               DHCPV6_REC_TIMEOUT << a->reconf_cnt);
+       a->reconf_timer.cb = reconf_timeout_cb;
+       a->reconf_cnt++;
+
+       send_reconf(a);
+}
+
+static void stop_reconf(struct dhcpv6_assignment *a)
+{
+       uloop_timeout_cancel(&a->reconf_timer);
+       a->reconf_cnt = 0;
+       a->reconf_timer.cb = NULL;
+}
+
 static void valid_until_cb(struct uloop_timeout *event)
 {
        time_t now = odhcpd_time();
@@ -810,8 +829,8 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
                        uint32_t pref = leasetime;
                        uint32_t valid = leasetime;
 
-                       struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->ia_addr;
-                       size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->ia_addr_len;
+                       struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->addr6;
+                       size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->addr6_len;
                        size_t m = get_preferred_addr(addrs, addrlen);
 
                        for (size_t i = 0; i < addrlen; ++i) {
@@ -904,8 +923,8 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
 
                                bool found = false;
                                if (a) {
-                                       struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->ia_addr;
-                                       size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->ia_addr_len;
+                                       struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->addr6;
+                                       size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->addr6_len;
 
                                        for (size_t i = 0; i < addrlen; ++i) {
                                                if (!valid_addr(&addrs[i], now))
@@ -1200,7 +1219,7 @@ ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
                                }
                        }
 
-                       if (!assigned || iface->ia_addr_len == 0)
+                       if (!assigned || iface->addr6_len == 0)
                                /* Set error status */
                                status = (is_pd) ? DHCPV6_STATUS_NOPREFIXAVAIL : DHCPV6_STATUS_NOADDRSAVAIL;
                        else if (assigned && !first && hdr->msg_type != DHCPV6_MSG_REBIND) {
index b94e84dae42601b5cbbc6f571787b4aacb591a37..3edde955984954d3c03ecae6418f19b2978ced29 100644 (file)
@@ -483,17 +483,17 @@ static struct odhcpd_ipaddr *relay_link_address(struct interface *iface)
        struct odhcpd_ipaddr *addr = NULL;
        time_t now = odhcpd_time();
 
-       for (size_t i = 0; i < iface->ia_addr_len; i++) {
-               if (iface->ia_addr[i].valid <= (uint32_t)now)
+       for (size_t i = 0; i < iface->addr6_len; i++) {
+               if (iface->addr6[i].valid <= (uint32_t)now)
                        continue;
 
-               if (iface->ia_addr[i].preferred > (uint32_t)now) {
-                       addr = &iface->ia_addr[i];
+               if (iface->addr6[i].preferred > (uint32_t)now) {
+                       addr = &iface->addr6[i];
                        break;
                }
 
-               if (!addr || (iface->ia_addr[i].valid > addr->valid))
-                       addr = &iface->ia_addr[i];
+               if (!addr || (iface->addr6[i].valid > addr->valid))
+                       addr = &iface->addr6[i];
        }
 
        return addr;
index fe05ae5e1a0e9650387cb49748eefa3f3b0e6086..08dac6c7282558b080124b2b923f28677e687f24 100644 (file)
@@ -196,5 +196,3 @@ int dhcpv6_setup_ia_interface(struct interface *iface, bool enable);
 void dhcpv6_enum_ia_addrs(struct interface *iface, struct dhcpv6_assignment *c, time_t now,
                                dhcpv6_binding_cb_handler_t func, void *arg);
 void dhcpv6_write_statefile(void);
-void dhcpv6_ia_preupdate(struct interface *iface);
-void dhcpv6_ia_postupdate(struct interface *iface);
index 460d5fc9e8edbacb089174fa8b179546becd1990..a9dbd9a635ce30214ad7697147254ce565874a4a 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
 #include <netinet/icmp6.h>
 #include <netpacket/packet.h>
 
-#include <linux/rtnetlink.h>
 #include <linux/filter.h>
-
-#include <netlink/msg.h>
-#include <netlink/socket.h>
-#include <netlink/attr.h>
+#include <linux/neighbour.h>
 
 #include "dhcpv6.h"
 #include "odhcpd.h"
 
-struct event_socket {
-       struct odhcpd_event ev;
-       struct nl_sock *sock;
-       int sock_bufsize;
-};
 
+static void ndp_netevent_cb(unsigned long event, struct netevent_handler_info *info);
+static void setup_route(struct in6_addr *addr, struct interface *iface, bool add);
+static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *iface, bool add);
 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_rtnl_err(struct odhcpd_event *e, int error);
 
 static int ping_socket = -1;
-static struct event_socket rtnl_event = {
-       .ev = {
-               .uloop = {.fd = - 1, },
-               .handle_dgram = NULL,
-               .handle_error = catch_rtnl_err,
-               .recv_msgs = handle_rtnl_event,
-       },
-       .sock = NULL,
-       .sock_bufsize = 133120,
-};
 
 // Filter ICMPv6 messages of type neighbor soliciation
 static struct sock_filter bpf[] = {
@@ -71,35 +52,13 @@ static struct sock_filter bpf[] = {
        BPF_STMT(BPF_RET | BPF_K, 0),
 };
 static const struct sock_fprog bpf_prog = {sizeof(bpf) / sizeof(*bpf), bpf};
-
+static struct netevent_handler ndp_netevent_handler = { .cb = ndp_netevent_cb, };
 
 // Initialize NDP-proxy
 int ndp_init(void)
 {
        int val = 2;
 
-       rtnl_event.sock = netlink_create_socket(NETLINK_ROUTE);
-       if (!rtnl_event.sock)
-               goto err;
-
-       rtnl_event.ev.uloop.fd = nl_socket_get_fd(rtnl_event.sock);
-
-       if (nl_socket_set_buffer_size(rtnl_event.sock, rtnl_event.sock_bufsize, 0))
-               goto err;
-
-       nl_socket_disable_seq_check(rtnl_event.sock);
-
-       nl_socket_modify_cb(rtnl_event.sock, NL_CB_VALID, NL_CB_CUSTOM,
-                       cb_rtnl_valid, NULL);
-
-       // 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);
-
        // Open ICMPv6 socket
        ping_socket = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
        if (ping_socket < 0) {
@@ -119,53 +78,9 @@ int ndp_init(void)
        ICMP6_FILTER_SETBLOCKALL(&filt);
        setsockopt(ping_socket, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt));
 
-       return 0;
-
-err:
-       if (rtnl_event.sock) {
-               nl_socket_free(rtnl_event.sock);
-               rtnl_event.sock = NULL;
-               rtnl_event.ev.uloop.fd = -1;
-       }
-
-       return -1;
-}
+       netlink_add_netevent_handler(&ndp_netevent_handler);
 
-static void dump_neigh_table(const bool proxy)
-{
-       struct nl_msg *msg;
-       struct ndmsg ndm = {
-               .ndm_family = AF_INET6,
-               .ndm_flags = proxy ? NTF_PROXY : 0,
-       };
-
-       msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
-       if (!msg)
-               return;
-
-       nlmsg_append(msg, &ndm, sizeof(ndm), 0);
-
-       nl_send_auto_complete(rtnl_event.sock, msg);
-
-       nlmsg_free(msg);
-}
-
-static void dump_addr_table(bool v6)
-{
-       struct nl_msg *msg;
-       struct ifaddrmsg ifa = {
-               .ifa_family = v6 ? AF_INET6 : AF_INET,
-       };
-
-       msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP);
-       if (!msg)
-               return;
-
-       nlmsg_append(msg, &ifa, sizeof(ifa), 0);
-
-       nl_send_auto_complete(rtnl_event.sock, msg);
-
-       nlmsg_free(msg);
+       return 0;
 }
 
 int ndp_setup_interface(struct interface *iface, bool enable)
@@ -236,13 +151,13 @@ int ndp_setup_interface(struct interface *iface, bool enable)
 
                // If we already were enabled dump is unnecessary, if not do dump
                if (!dump_neigh)
-                       dump_neigh_table(false);
+                       netlink_dump_neigh_table(false);
                else
                        dump_neigh = false;
        }
 
        if (dump_neigh)
-               dump_neigh_table(true);
+               netlink_dump_neigh_table(true);
 
 out:
        if (procfd >= 0)
@@ -251,6 +166,48 @@ out:
        return ret;
 }
 
+static void ndp_netevent_cb(unsigned long event, struct netevent_handler_info *info)
+{
+       struct interface *iface = info->iface;
+       bool add = true;
+
+       if (!iface || iface->ndp == MODE_DISABLED)
+               return;
+
+       switch (event) {
+       case NETEV_ADDR6_DEL:
+               add = false;
+               netlink_dump_neigh_table(false);
+       case NETEV_ADDR6_ADD:
+               setup_addr_for_relaying(&info->addr.in6, iface, add);
+               break;
+       case NETEV_NEIGH6_DEL:
+               add = false;
+       case NETEV_NEIGH6_ADD:
+               if (info->neigh.flags & NTF_PROXY) {
+                       if (add) {
+                               netlink_setup_proxy_neigh(&info->neigh.dst.in6, iface->ifindex, false);
+                               setup_route(&info->neigh.dst.in6, iface, false);
+                               netlink_dump_neigh_table(false);
+                       }
+                       break;
+               }
+
+               if (add &&
+                   !(info->neigh.state &
+                     (NUD_REACHABLE|NUD_STALE|NUD_DELAY|NUD_PROBE|NUD_PERMANENT|NUD_NOARP)))
+                       break;
+
+               setup_addr_for_relaying(&info->neigh.dst.in6, iface, add);
+               setup_route(&info->neigh.dst.in6, iface, add);
+
+               if (!add)
+                       netlink_dump_neigh_table(false);
+               break;
+       default:
+               break;
+       }
+}
 
 // Send an ICMP-ECHO. This is less for actually pinging but for the
 // neighbor cache to be kept up-to-date.
@@ -265,9 +222,9 @@ static void ping6(struct in6_addr *addr,
        inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
        syslog(LOG_NOTICE, "Pinging for %s%%%s", ipbuf, iface->ifname);
 
-       netlink_setup_route(addr, 128, iface, NULL, 128, true);
+       netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, true);
        odhcpd_send(ping_socket, &dest, &iov, 1, iface);
-       netlink_setup_route(addr, 128, iface, NULL, 128, false);
+       netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, false);
 }
 
 // Handle solicitations
@@ -317,64 +274,13 @@ static void setup_route(struct in6_addr *addr, struct interface *iface, bool add
        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);
+       syslog(LOG_NOTICE, "%s about %s%s%%%s",
+                       (add) ? "Learning" : "Forgetting",
+                       iface->learn_routes ? "proxy routing for " : "",
+                       ipbuf, iface->ifname);
 
        if (iface->learn_routes)
-               netlink_setup_route(addr, 128, iface, NULL, 1024, add);
-}
-
-// Check address update
-static void check_addr_updates(struct interface *iface)
-{
-       struct odhcpd_ipaddr *addr = NULL;
-       ssize_t len = netlink_get_interface_addrs(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;
-
-       if (change)
-               dhcpv4_addr_update(iface);
-}
-
-// Check v6 address update
-static void check_addr6_updates(struct interface *iface)
-{
-       struct odhcpd_ipaddr *addr = NULL;
-       ssize_t len = netlink_get_interface_addrs(iface->ifindex, true, &addr);
-
-       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.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)
-                       change = true;
-
-       if (change)
-               dhcpv6_ia_preupdate(iface);
-
-       free(iface->ia_addr);
-       iface->ia_addr = addr;
-       iface->ia_addr_len = len;
-
-       if (change) {
-               dhcpv6_ia_postupdate(iface);
-               syslog(LOG_INFO, "Raising SIGUSR1 due to address change on %s", iface->ifname);
-               raise(SIGUSR1);
-       }
+               netlink_setup_route(addr, 128, iface->ifindex, NULL, 1024, add);
 }
 
 static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *iface, bool add)
@@ -390,7 +296,7 @@ static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *ifa
 
                bool neigh_add = (c->ndp == MODE_RELAY ? add : false);
 
-               if (netlink_setup_proxy_neigh(addr, c, neigh_add))
+               if (netlink_setup_proxy_neigh(addr, c->ifindex, neigh_add))
                        syslog(LOG_DEBUG, "Failed to %s proxy neighbour entry %s%%%s",
                                neigh_add ? "add" : "delete", ipbuf, c->ifname);
                else
@@ -398,195 +304,3 @@ static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *ifa
                                neigh_add ? "Added" : "Deleted", ipbuf, c->ifname);
        }
 }
-
-static void handle_rtnl_event(struct odhcpd_event *e)
-{
-       struct event_socket *ev_sock = container_of(e, struct event_socket, ev);
-
-       nl_recvmsgs_default(ev_sock->sock);
-}
-
-
-// Handler for neighbor cache entries from the kernel. This is our source
-// to learn and unlearn hosts on interfaces.
-static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
-{
-       struct nlmsghdr *hdr = nlmsg_hdr(msg);
-       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);
-
-               if (!nlmsg_valid_hdr(hdr, sizeof(*rtm)) ||
-                               rtm->rtm_family != AF_INET6)
-                       return NL_SKIP;
-
-               if (rtm->rtm_dst_len == 0) {
-                       syslog(LOG_INFO, "Raising SIGUSR1 due to default route change");
-                       raise(SIGUSR1);
-               }
-               break;
-       }
-
-       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_INET))
-                       return NL_SKIP;
-
-               iface = odhcpd_get_interface_by_index(ifa->ifa_index);
-               if (!iface)
-                       return NL_SKIP;
-
-               nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
-
-               if (ifa->ifa_family == AF_INET6) {
-                       if (!nla[IFA_ADDRESS])
-                               return NL_SKIP;
-
-                       addr6 = nla_data(nla[IFA_ADDRESS]);
-                       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", add ? "newaddr" : "deladdr",
-                               ipbuf, iface->ifname);
-
-                       check_addr6_updates(iface);
-
-                       if (iface->ndp != MODE_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];
-
-               if (!nlmsg_valid_hdr(hdr, sizeof(*ndm)) ||
-                               ndm->ndm_family != AF_INET6)
-                       return NL_SKIP;
-
-               iface = odhcpd_get_interface_by_index(ndm->ndm_ifindex);
-               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;
-
-               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) {
-                               netlink_setup_proxy_neigh(addr6, iface, false);
-                               setup_route(addr6, iface, false);
-                               dump_neigh_table(false);
-                       }
-
-                       return NL_OK;
-               }
-
-               if (add && !(ndm->ndm_state &
-                               (NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE |
-                                NUD_PERMANENT | NUD_NOARP)))
-                       return NL_OK;
-
-               setup_addr_for_relaying(addr6, iface, add);
-               setup_route(addr6, iface, add);
-
-               if (!add)
-                       dump_neigh_table(false);
-               break;
-       }
-
-       default:
-               return NL_SKIP;
-       }
-
-       return NL_OK;
-}
-
-static void catch_rtnl_err(struct odhcpd_event *e, int error)
-{
-       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_addr_table(true);
-       return;
-
-err:
-       odhcpd_deregister(e);
-}
index 485fd57e100972be883abaa7259818fdd1dd9442..ca9376e5c7ac06007b0f50c8b50fe73cc5ecbb4a 100644 (file)
 #include <netlink/socket.h>
 #include <netlink/attr.h>
 
+#include <arpa/inet.h>
+#include <libubox/list.h>
+
 #include "odhcpd.h"
 
-static struct nl_sock *rtnl_socket = NULL;
+struct event_socket {
+       struct odhcpd_event ev;
+       struct nl_sock *sock;
+       int sock_bufsize;
+};
 
+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 struct nl_sock *create_socket(int protocol);
+
+static struct nl_sock *rtnl_socket = NULL;
+struct list_head netevent_handler_list = LIST_HEAD_INIT(netevent_handler_list);
+static struct event_socket rtnl_event = {
+       .ev = {
+               .uloop = {.fd = - 1, },
+               .handle_dgram = NULL,
+               .handle_error = catch_rtnl_err,
+               .recv_msgs = handle_rtnl_event,
+       },
+       .sock = NULL,
+       .sock_bufsize = 133120,
+};
 
 int netlink_init(void)
 {
-       if (!(rtnl_socket = netlink_create_socket(NETLINK_ROUTE))) {
+       rtnl_socket = create_socket(NETLINK_ROUTE);
+       if (!rtnl_socket) {
                syslog(LOG_ERR, "Unable to open nl socket: %s", strerror(errno));
-               return -1;
+               goto err;
+       }
+
+       rtnl_event.sock = create_socket(NETLINK_ROUTE);
+       if (!rtnl_event.sock) {
+               syslog(LOG_ERR, "Unable to open nl event socket: %s", strerror(errno));
+               goto err;
+       }
+
+       rtnl_event.ev.uloop.fd = nl_socket_get_fd(rtnl_event.sock);
+
+       if (nl_socket_set_buffer_size(rtnl_event.sock, rtnl_event.sock_bufsize, 0))
+               goto err;
+
+       nl_socket_disable_seq_check(rtnl_event.sock);
+
+       nl_socket_modify_cb(rtnl_event.sock, NL_CB_VALID, NL_CB_CUSTOM,
+                       cb_rtnl_valid, NULL);
+
+       // 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);
+
+       return 0;
+
+err:
+       if (rtnl_socket) {
+               nl_socket_free(rtnl_socket);
+               rtnl_socket = NULL;
        }
 
+       if (rtnl_event.sock) {
+               nl_socket_free(rtnl_event.sock);
+               rtnl_event.sock = NULL;
+               rtnl_event.ev.uloop.fd = -1;
+       }
+
+       return -1;
+}
+
+
+int netlink_add_netevent_handler(struct netevent_handler *handler)
+{
+       if (!handler->cb)
+               return -1;
+
+       list_add(&handler->head, &netevent_handler_list);
+
        return 0;
 }
 
+static void call_netevent_handler_list(unsigned long event, struct netevent_handler_info *info)
+{
+       struct netevent_handler *handler;
 
-struct nl_sock *netlink_create_socket(int protocol)
+       list_for_each_entry(handler, &netevent_handler_list, head)
+               handler->cb(event, info);
+}
+
+static void handle_rtnl_event(struct odhcpd_event *e)
+{
+       struct event_socket *ev_sock = container_of(e, struct event_socket, ev);
+
+       nl_recvmsgs_default(ev_sock->sock);
+}
+
+static void refresh_iface_addr4(struct netevent_handler_info *event_info)
+{
+       struct odhcpd_ipaddr *addr = NULL;
+       struct interface *iface = event_info->iface;
+       ssize_t len = netlink_get_interface_addrs(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;
+
+       event_info->addrs_old.addrs = iface->addr4;
+       event_info->addrs_old.len = iface->addr4_len;
+
+       iface->addr4 = addr;
+       iface->addr4_len = len;
+
+       if (change)
+               call_netevent_handler_list(NETEV_ADDRLIST_CHANGE, event_info);
+
+       free(event_info->addrs_old.addrs);
+}
+
+static void refresh_iface_addr6(struct netevent_handler_info *event_info)
+{
+       struct odhcpd_ipaddr *addr = NULL;
+       struct interface *iface = event_info->iface;
+       ssize_t len = netlink_get_interface_addrs(iface->ifindex, true, &addr);
+
+       if (len < 0)
+               return;
+
+       bool change = len != (ssize_t)iface->addr6_len;
+       for (ssize_t i = 0; !change && i < len; ++i)
+               if (!IN6_ARE_ADDR_EQUAL(&addr[i].addr.in6, &iface->addr6[i].addr.in6) ||
+                               (addr[i].preferred > 0) != (iface->addr6[i].preferred > 0) ||
+                               addr[i].valid < iface->addr6[i].valid ||
+                               addr[i].preferred < iface->addr6[i].preferred)
+                       change = true;
+
+       event_info->addrs_old.addrs = iface->addr6;
+       event_info->addrs_old.len = iface->addr6_len;
+
+       iface->addr6 = addr;
+       iface->addr6_len = len;
+
+       if (change)
+               call_netevent_handler_list(NETEV_ADDR6LIST_CHANGE, event_info);
+
+       free(event_info->addrs_old.addrs);
+}
+
+// Handler for neighbor cache entries from the kernel. This is our source
+// to learn and unlearn hosts on interfaces.
+static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg)
+{
+       struct nlmsghdr *hdr = nlmsg_hdr(msg);
+       struct netevent_handler_info event_info;
+       bool add = false;
+       char ipbuf[INET6_ADDRSTRLEN];
+
+       memset(&event_info, 0, sizeof(event_info));
+       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(*ifi), nla, __IFLA_MAX - 1, NULL);
+               if (!nla[IFLA_IFNAME])
+                       return NL_SKIP;
+
+               event_info.iface = odhcpd_get_interface_by_name(nla_get_string(nla[IFLA_IFNAME]));
+               if (!event_info.iface)
+                       return NL_SKIP;
+
+               if (event_info.iface->ifindex != ifi->ifi_index) {
+                       event_info.iface->ifindex = ifi->ifi_index;
+                       call_netevent_handler_list(NETEV_IFINDEX_CHANGE, &event_info);
+               }
+               break;
+       }
+
+       case RTM_NEWROUTE:
+               add = true;
+               /* fall through */
+       case RTM_DELROUTE: {
+               struct rtmsg *rtm = nlmsg_data(hdr);
+               struct nlattr *nla[__RTA_MAX];
+
+               if (!nlmsg_valid_hdr(hdr, sizeof(*rtm)) ||
+                               rtm->rtm_family != AF_INET6)
+                       return NL_SKIP;
+
+               nlmsg_parse(hdr, sizeof(*rtm), nla, __RTA_MAX - 1, NULL);
+
+               event_info.rt.dst_len = rtm->rtm_dst_len;
+               if (nla[RTA_DST])
+                       nla_memcpy(&event_info.rt.dst, nla[RTA_DST],
+                                       sizeof(&event_info.rt.dst));
+
+               if (nla[RTA_OIF])
+                       event_info.iface = odhcpd_get_interface_by_index(nla_get_u32(nla[RTA_OIF]));
+
+               if (nla[RTA_GATEWAY])
+                       nla_memcpy(&event_info.rt.gateway, nla[RTA_GATEWAY],
+                                       sizeof(&event_info.rt.gateway));
+
+               call_netevent_handler_list(add ? NETEV_ROUTE6_ADD : NETEV_ROUTE6_DEL,
+                                       &event_info);
+               break;
+       }
+
+       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_INET))
+                       return NL_SKIP;
+
+               event_info.iface = odhcpd_get_interface_by_index(ifa->ifa_index);
+               if (!event_info.iface)
+                       return NL_SKIP;
+
+               nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
+
+               if (ifa->ifa_family == AF_INET6) {
+                       if (!nla[IFA_ADDRESS])
+                               return NL_SKIP;
+
+                       nla_memcpy(&event_info.addr, nla[IFA_ADDRESS], sizeof(event_info.addr));
+
+                       if (IN6_IS_ADDR_LINKLOCAL(&event_info.addr) ||
+                           IN6_IS_ADDR_MULTICAST(&event_info.addr))
+                               return NL_SKIP;
+
+                       inet_ntop(AF_INET6, &event_info.addr, ipbuf, sizeof(ipbuf));
+                       syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr",
+                               ipbuf, event_info.iface->ifname);
+
+                       call_netevent_handler_list(add ? NETEV_ADDR6_ADD : NETEV_ADDR6_DEL,
+                                                       &event_info);
+
+                       refresh_iface_addr6(&event_info);
+               } else {
+                       if (!nla[IFA_LOCAL])
+                               return NL_SKIP;
+
+                       nla_memcpy(&event_info.addr, nla[IFA_LOCAL], sizeof(event_info.addr));
+
+                       inet_ntop(AF_INET, &event_info.addr, ipbuf, sizeof(ipbuf));
+                       syslog(LOG_DEBUG, "Netlink %s %s%%%s", add ? "newaddr" : "deladdr",
+                               ipbuf, event_info.iface->ifname);
+
+                       call_netevent_handler_list(add ? NETEV_ADDR_ADD : NETEV_ADDR_DEL,
+                                                       &event_info);
+
+                       refresh_iface_addr4(&event_info);
+               }
+               break;
+       }
+
+       case RTM_NEWNEIGH:
+               add = true;
+               /* fall through */
+       case RTM_DELNEIGH: {
+               struct ndmsg *ndm = nlmsg_data(hdr);
+               struct nlattr *nla[__NDA_MAX];
+
+               if (!nlmsg_valid_hdr(hdr, sizeof(*ndm)) ||
+                               ndm->ndm_family != AF_INET6)
+                       return NL_SKIP;
+
+               event_info.iface = odhcpd_get_interface_by_index(ndm->ndm_ifindex);
+               if (!event_info.iface)
+                       return NL_SKIP;
+
+               nlmsg_parse(hdr, sizeof(*ndm), nla, __NDA_MAX - 1, NULL);
+               if (!nla[NDA_DST])
+                       return NL_SKIP;
+
+               nla_memcpy(&event_info.neigh.dst, nla[NDA_DST], sizeof(event_info.neigh.dst));
+
+               if (IN6_IS_ADDR_LINKLOCAL(&event_info.neigh.dst) ||
+                   IN6_IS_ADDR_MULTICAST(&event_info.neigh.dst))
+                       return NL_SKIP;
+
+               inet_ntop(AF_INET6, &event_info.neigh.dst, ipbuf, sizeof(ipbuf));
+               syslog(LOG_DEBUG, "Netlink %s %s%%%s", true ? "newneigh" : "delneigh",
+                       ipbuf, event_info.iface->ifname);
+
+               event_info.neigh.state = ndm->ndm_state;
+               event_info.neigh.flags = ndm->ndm_flags;
+
+               call_netevent_handler_list(add ? NETEV_NEIGH6_ADD : NETEV_NEIGH6_DEL,
+                                               &event_info);
+               break;
+       }
+
+       default:
+               return NL_SKIP;
+       }
+
+       return NL_OK;
+}
+
+static void catch_rtnl_err(struct odhcpd_event *e, int error)
+{
+       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;
+
+       netlink_dump_addr_table(true);
+       return;
+
+err:
+       odhcpd_deregister(e);
+}
+
+static struct nl_sock *create_socket(int protocol)
 {
        struct nl_sock *nl_sock;
 
@@ -252,7 +577,7 @@ out:
 
 
 int netlink_setup_route(const struct in6_addr *addr, const int prefixlen,
-               const struct interface *iface, const struct in6_addr *gw,
+               const int ifindex, const struct in6_addr *gw,
                const uint32_t metric, const bool add)
 {
        struct nl_msg *msg;
@@ -275,7 +600,7 @@ int netlink_setup_route(const struct in6_addr *addr, const int prefixlen,
        nlmsg_append(msg, &rtm, sizeof(rtm), 0);
 
        nla_put(msg, RTA_DST, sizeof(*addr), addr);
-       nla_put_u32(msg, RTA_OIF, iface->ifindex);
+       nla_put_u32(msg, RTA_OIF, ifindex);
        nla_put_u32(msg, RTA_PRIORITY, metric);
 
        if (gw)
@@ -292,13 +617,13 @@ int netlink_setup_route(const struct in6_addr *addr, const int prefixlen,
 
 
 int netlink_setup_proxy_neigh(const struct in6_addr *addr,
-               const struct interface *iface, const bool add)
+               const int ifindex, const bool add)
 {
        struct nl_msg *msg;
        struct ndmsg ndm = {
                .ndm_family = AF_INET6,
                .ndm_flags = NTF_PROXY,
-               .ndm_ifindex = iface->ifindex,
+               .ndm_ifindex = ifindex,
        };
        int ret = 0, flags = NLM_F_REQUEST;
 
@@ -324,8 +649,7 @@ int netlink_setup_proxy_neigh(const struct in6_addr *addr,
 
 
 int netlink_setup_addr(struct odhcpd_ipaddr *addr,
-               const struct interface *iface, const bool v6,
-               const bool add)
+               const int ifindex, const bool v6, const bool add)
 {
        struct nl_msg *msg;
        struct ifaddrmsg ifa = {
@@ -333,7 +657,7 @@ int netlink_setup_addr(struct odhcpd_ipaddr *addr,
                .ifa_prefixlen = addr->prefix,
                .ifa_flags = 0,
                .ifa_scope = 0,
-               .ifa_index = iface->ifindex, };
+               .ifa_index = ifindex, };
        int ret = 0, flags = NLM_F_REQUEST;
 
        if (add)
@@ -390,3 +714,40 @@ int netlink_setup_addr(struct odhcpd_ipaddr *addr,
 
        return nl_wait_for_ack(rtnl_socket);
 }
+
+void netlink_dump_neigh_table(const bool proxy)
+{
+       struct nl_msg *msg;
+       struct ndmsg ndm = {
+               .ndm_family = AF_INET6,
+               .ndm_flags = proxy ? NTF_PROXY : 0,
+       };
+
+       msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
+       if (!msg)
+               return;
+
+       nlmsg_append(msg, &ndm, sizeof(ndm), 0);
+
+       nl_send_auto_complete(rtnl_event.sock, msg);
+
+       nlmsg_free(msg);
+}
+
+void netlink_dump_addr_table(const bool v6)
+{
+       struct nl_msg *msg;
+       struct ifaddrmsg ifa = {
+               .ifa_family = v6 ? AF_INET6 : AF_INET,
+       };
+
+       msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP);
+       if (!msg)
+               return;
+
+       nlmsg_append(msg, &ifa, sizeof(ifa), 0);
+
+       nl_send_auto_complete(rtnl_event.sock, msg);
+
+       nlmsg_free(msg);
+}
index 5f871518e218e2f875202876c4e43891842d6202..4972aa2b44c1054f6348f11b4eb035d025c4a6d3 100644 (file)
@@ -224,8 +224,8 @@ int odhcpd_get_interface_dns_addr(const struct interface *iface, struct in6_addr
        time_t now = odhcpd_time();
        ssize_t m = -1;
 
-       for (size_t i = 0; i < iface->ia_addr_len; ++i) {
-               if (iface->ia_addr[i].valid <= (uint32_t)now)
+       for (size_t i = 0; i < iface->addr6_len; ++i) {
+               if (iface->addr6[i].valid <= (uint32_t)now)
                        continue;
 
                if (m < 0) {
@@ -233,24 +233,24 @@ int odhcpd_get_interface_dns_addr(const struct interface *iface, struct in6_addr
                        continue;
                }
 
-               if (iface->ia_addr[m].preferred >= (uint32_t)now &&
-                               iface->ia_addr[i].preferred < (uint32_t)now)
+               if (iface->addr6[m].preferred >= (uint32_t)now &&
+                               iface->addr6[i].preferred < (uint32_t)now)
                        continue;
 
-               if (IN6_IS_ADDR_ULA(&iface->ia_addr[i].addr.in6)) {
-                       if (!IN6_IS_ADDR_ULA(&iface->ia_addr[m].addr.in6)) {
+               if (IN6_IS_ADDR_ULA(&iface->addr6[i].addr.in6)) {
+                       if (!IN6_IS_ADDR_ULA(&iface->addr6[m].addr.in6)) {
                                m = i;
                                continue;
                        }
-               } else if (IN6_IS_ADDR_ULA(&iface->ia_addr[m].addr.in6))
+               } else if (IN6_IS_ADDR_ULA(&iface->addr6[m].addr.in6))
                        continue;
 
-               if (iface->ia_addr[i].preferred > iface->ia_addr[m].preferred)
+               if (iface->addr6[i].preferred > iface->addr6[m].preferred)
                        m = i;
        }
 
        if (m >= 0) {
-               *addr = iface->ia_addr[m].addr.in6;
+               *addr = iface->addr6[m].addr.in6;
                return 0;
        }
 
index 0ae36c3e65e92b592da02edf06de54c425c4a18d..a49ab57986b31b8a632b50c9a1b4b46385afb9e8 100644 (file)
@@ -71,6 +71,46 @@ union if_addr {
        struct in6_addr in6;
 };
 
+struct netevent_handler_info {
+       struct interface *iface;
+       union {
+               struct {
+                       union if_addr dst;
+                       uint8_t dst_len;
+                       union if_addr gateway;
+               } rt;
+               struct {
+                       union if_addr dst;
+                       uint16_t state;
+                       uint8_t flags;
+               } neigh;
+               struct {
+                       struct odhcpd_ipaddr *addrs;
+                       size_t len;
+               } addrs_old;
+               union if_addr addr;
+       };
+};
+
+enum netevents {
+       NETEV_IFINDEX_CHANGE,
+       NETEV_ADDR_ADD,
+       NETEV_ADDR_DEL,
+       NETEV_ADDRLIST_CHANGE,
+       NETEV_ADDR6_ADD,
+       NETEV_ADDR6_DEL,
+       NETEV_ADDR6LIST_CHANGE,
+       NETEV_ROUTE6_ADD,
+       NETEV_ROUTE6_DEL,
+       NETEV_NEIGH6_ADD,
+       NETEV_NEIGH6_DEL,
+};
+
+struct netevent_handler {
+       struct list_head head;
+       void (*cb) (unsigned long event, struct netevent_handler_info *info);
+};
+
 struct odhcpd_ipaddr {
        union if_addr addr;
        uint8_t prefix;
@@ -126,8 +166,8 @@ struct interface {
        const char *name;
 
        // IPv6 runtime data
-       struct odhcpd_ipaddr *ia_addr;
-       size_t ia_addr_len;
+       struct odhcpd_ipaddr *addr6;
+       size_t addr6_len;
 
        // RA runtime data
        struct uloop_timeout timer_rs;
@@ -251,8 +291,6 @@ bool odhcpd_bitlen2netmask(bool v6, unsigned int bits, void *mask);
 
 int config_parse_interface(void *data, size_t len, const char *iname, bool overwrite);
 
-void dhcpv4_addr_update(struct interface *iface);
-
 #ifdef WITH_UBUS
 int ubus_init(void);
 const char* ubus_get_ifname(const char *name);
@@ -260,18 +298,18 @@ void ubus_apply_network(void);
 bool ubus_has_prefix(const char *name, const char *ifname);
 #endif
 
-struct nl_sock *netlink_create_socket(int protocol);
-ssize_t netlink_get_interface_addrs(int ifindex, bool v6,
+int netlink_add_netevent_handler(struct netevent_handler *hdlr);
+ssize_t netlink_get_interface_addrs(const int ifindex, bool v6,
                struct odhcpd_ipaddr **addrs);
 int netlink_setup_route(const struct in6_addr *addr, const int prefixlen,
-               const struct interface *iface, const struct in6_addr *gw,
+               const int ifindex, const struct in6_addr *gw,
                const uint32_t metric, const bool add);
 int netlink_setup_proxy_neigh(const struct in6_addr *addr,
-               const struct interface *iface, const bool add);
+               const int ifindex, const bool add);
 int netlink_setup_addr(struct odhcpd_ipaddr *addr,
-               const struct interface *iface, const bool v6,
-               const bool add);
-
+               const int ifindex, const bool v6, const bool add);
+void netlink_dump_neigh_table(const bool proxy);
+void netlink_dump_addr_table(const bool v6);
 
 // Exported module initializers
 int netlink_init(void);
index b50df3a962995e5e7d65b03a12accbb474374baa..c35cd12e7d30c6fd0dc2f71888362e7ffa759533 100644 (file)
@@ -33,12 +33,14 @@ static void forward_router_advertisement(uint8_t *data, size_t len);
 static void handle_icmpv6(void *addr, void *data, size_t len,
                struct interface *iface, void *dest);
 static void trigger_router_advert(struct uloop_timeout *event);
-static void sigusr1_refresh(int signal);
+static void router_netevent_cb(unsigned long event, struct netevent_handler_info *info);
 
 static struct odhcpd_event router_event = {.uloop = {.fd = -1}, .handle_dgram = handle_icmpv6, };
+static struct netevent_handler router_netevent_handler = { .cb = router_netevent_cb, };
 
 static FILE *fp_route = NULL;
 
+
 #define TIME_LEFT(t1, now) ((t1) != UINT32_MAX ? (t1) - (now) : UINT32_MAX)
 
 int router_init(void)
@@ -83,7 +85,8 @@ int router_init(void)
                syslog(LOG_ERR, "Failed to open routing table: %s",
                                strerror(errno));
 
-       signal(SIGUSR1, sigusr1_refresh);
+       netlink_add_netevent_handler(&router_netevent_handler);
+
        return 0;
 }
 
@@ -129,15 +132,32 @@ int router_setup_interface(struct interface *iface, bool enable)
 }
 
 
-// Signal handler to resend all RDs
-static void sigusr1_refresh(_unused int signal)
+static void router_netevent_cb(unsigned long event, struct netevent_handler_info *info)
 {
        struct interface *iface;
-       list_for_each_entry(iface, &interfaces, head)
-               if (iface->ra == MODE_SERVER && !iface->master)
+
+       switch (event) {
+       case NETEV_ROUTE6_ADD:
+       case NETEV_ROUTE6_DEL:
+               if (info->rt.dst_len)
+                       break;
+
+               list_for_each_entry(iface, &interfaces, head) {
+                       if (iface->ra == MODE_SERVER && !iface->master)
+                               uloop_timeout_set(&iface->timer_rs, 1000);
+               }
+               break;
+       case NETEV_ADDR6LIST_CHANGE:
+               iface = info->iface;
+               if (iface && iface->ra == MODE_SERVER && !iface->master)
                        uloop_timeout_set(&iface->timer_rs, 1000);
+               break;
+       default:
+               break;
+       }
 }
 
+
 static bool router_icmpv6_valid(struct sockaddr_in6 *source, uint8_t *data, size_t len)
 {
        struct icmp6_hdr *hdr = (struct icmp6_hdr *)data;
@@ -320,11 +340,11 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
 
        // If not shutdown
        if (iface->timer_rs.cb) {
-               size_t size = sizeof(*addrs) * iface->ia_addr_len;
+               size_t size = sizeof(*addrs) * iface->addr6_len;
                addrs = alloca(size);
-               memcpy(addrs, iface->ia_addr, size);
+               memcpy(addrs, iface->addr6, size);
 
-               ipcnt = iface->ia_addr_len;
+               ipcnt = iface->addr6_len;
 
                // Check default route
                if (iface->default_router) {