summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
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) {