summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorHans Dedecker <dedeckeh@gmail.com>2017-10-17 16:23:35 +0200
committerHans Dedecker <dedeckeh@gmail.com>2017-11-09 15:25:06 +0100
commitcf29925b29a4e4e38ab88573f12ec075eacb3d22 (patch)
tree50824b4e52d0b15592b58383e61a53334fbed545 /src
parent24cdc1b59f00a065dd1cf0a04145ca6aaf6f23f1 (diff)
treewide: rework handling of netlink events
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>
Diffstat (limited to 'src')
-rw-r--r--src/config.c6
-rw-r--r--src/dhcpv4.c34
-rw-r--r--src/dhcpv6-ia.c159
-rw-r--r--src/dhcpv6.c12
-rw-r--r--src/dhcpv6.h2
-rw-r--r--src/ndp.c404
-rw-r--r--src/netlink.c383
-rw-r--r--src/odhcpd.c18
-rw-r--r--src/odhcpd.h60
-rw-r--r--src/router.c38
10 files changed, 644 insertions, 472 deletions
diff --git a/src/config.c b/src/config.c
index 2f51d33..7e23eb2 100644
--- a/src/config.c
+++ b/src/config.c
@@ -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);
diff --git a/src/dhcpv4.c b/src/dhcpv4.c
index c8be632..166582e 100644
--- a/src/dhcpv4.c
+++ b/src/dhcpv4.c
@@ -35,9 +35,11 @@
#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;
}
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
index 6568c69..533af00 100644
--- a/src/dhcpv6-ia.c
+++ b/src/dhcpv6-ia.c
@@ -39,10 +39,14 @@
((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) {
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index b94e84d..3edde95 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -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;
diff --git a/src/dhcpv6.h b/src/dhcpv6.h
index fe05ae5..08dac6c 100644
--- a/src/dhcpv6.h
+++ b/src/dhcpv6.h
@@ -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);
diff --git a/src/ndp.c b/src/ndp.c
index 460d5fc..a9dbd9a 100644
--- a/src/ndp.c
+++ b/src/ndp.c
@@ -26,39 +26,20 @@
#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);
-}
diff --git a/src/netlink.c b/src/netlink.c
index 485fd57..ca9376e 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -25,23 +25,348 @@
#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);
+}
diff --git a/src/odhcpd.c b/src/odhcpd.c
index 5f87151..4972aa2 100644
--- a/src/odhcpd.c
+++ b/src/odhcpd.c
@@ -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;
}
diff --git a/src/odhcpd.h b/src/odhcpd.h
index 0ae36c3..a49ab57 100644
--- a/src/odhcpd.h
+++ b/src/odhcpd.h
@@ -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);
diff --git a/src/router.c b/src/router.c
index b50df3a..c35cd12 100644
--- a/src/router.c
+++ b/src/router.c
@@ -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) {