Skip to content

Commit 5c732e0

Browse files
committed
advertise removed prefix delegation in RIO with lifetime 0
RFC 4191 section 4 states: ... When ceasing to be an advertising interface and sending Router Advertisements with a Router Lifetime of zero, the Router Advertisement SHOULD also set the Route Lifetime to zero in all Route Information Options. Since RIOs reflect the prefix delegations owned by the router, it is reasonable to assume that invalid RIOs should be sent along invalid PIOs required by RFC 7084 L-13. This extends the functionality introduced in commit 83e14f4 where removed addresses were advertised as invalid PIO in 3 consecutive RAs. Other related improvements introduced here: - further precaution has been taken in netlink.c to prevent possible overlapping between current list of interface IPv6 addresses and the list of invalid addresses - IPv6 route parsing was moved to netlink.c and was splitted in 2 functions; this allows dprefix caching in iface->addr6 list, necessary for generating invalid RIOs after prefix get deleted - all prefixes are advertised as invalid on odhcpd shutdown Signed-off-by: Alin Nastac <alin.nastac@gmail.com>
1 parent ca463da commit 5c732e0

File tree

4 files changed

+142
-85
lines changed

4 files changed

+142
-85
lines changed

src/netlink.c

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <netlink/attr.h>
2727

2828
#include <arpa/inet.h>
29+
#include <net/route.h>
2930
#include <libubox/list.h>
3031

3132
#include "odhcpd.h"
@@ -54,8 +55,15 @@ static struct event_socket rtnl_event = {
5455
.sock_bufsize = 133120,
5556
};
5657

57-
int netlink_init(void)
58+
static FILE *fp_ipv6_route = NULL;
59+
60+
int netlink_init(bool ipv6)
5861
{
62+
if (ipv6 && !(fp_ipv6_route = fopen("/proc/net/ipv6_route", "r"))) {
63+
syslog(LOG_ERR, "fopen(/proc/net/ipv6_route): %m");
64+
goto err;
65+
}
66+
5967
rtnl_socket = create_socket(NETLINK_ROUTE);
6068
if (!rtnl_socket) {
6169
syslog(LOG_ERR, "Unable to open nl socket: %m");
@@ -100,9 +108,67 @@ int netlink_init(void)
100108
rtnl_event.ev.uloop.fd = -1;
101109
}
102110

111+
if (fp_ipv6_route) {
112+
fclose(fp_ipv6_route);
113+
fp_ipv6_route = NULL;
114+
}
115+
103116
return -1;
104117
}
105118

119+
/* Detect whether a default IPv6 route exists */
120+
bool netlink_default_ipv6_route_exists()
121+
{
122+
bool found_default = false;
123+
char line[512], ifname[16];
124+
125+
if (!fp_ipv6_route)
126+
return false;
127+
rewind(fp_ipv6_route);
128+
129+
while (fgets(line, sizeof(line), fp_ipv6_route)) {
130+
if (sscanf(line, "00000000000000000000000000000000 00 "
131+
"%*s %*s %*s %*s %*s %*s %*s %15s", ifname) &&
132+
strcmp(ifname, "lo")) {
133+
found_default = true;
134+
break;
135+
}
136+
}
137+
138+
return found_default;
139+
}
140+
141+
/* Find the source prefixes of all IPv6 addresses */
142+
static void find_addr6_dprefix(struct odhcpd_ipaddr *n, ssize_t len)
143+
{
144+
struct odhcpd_ipaddr p = { .addr.in6 = IN6ADDR_ANY_INIT, .prefix = 0,
145+
.dprefix = 0, .preferred = 0, .valid = 0};
146+
char line[512];
147+
uint32_t rflags;
148+
149+
if (!fp_ipv6_route || len <= 0)
150+
return;
151+
rewind(fp_ipv6_route);
152+
153+
while (fgets(line, sizeof(line), fp_ipv6_route)) {
154+
if (sscanf(line, "%8" SCNx32 "%8" SCNx32 "%*8" SCNx32 "%*8" SCNx32 " %hhx %*s "
155+
"%*s 00000000000000000000000000000000 %*s %*s %*s %" SCNx32 " lo",
156+
&p.addr.in6.s6_addr32[0], &p.addr.in6.s6_addr32[1], &p.prefix, &rflags) &&
157+
p.prefix > 0 && (rflags & RTF_NONEXTHOP) && (rflags & RTF_REJECT)) {
158+
// Find source prefixes by scanning through unreachable-routes
159+
p.addr.in6.s6_addr32[0] = htonl(p.addr.in6.s6_addr32[0]);
160+
p.addr.in6.s6_addr32[1] = htonl(p.addr.in6.s6_addr32[1]);
161+
162+
for (ssize_t i = 0; i < len; ++i) {
163+
if (n[i].prefix <= 64 && n[i].prefix >= p.prefix &&
164+
!odhcpd_bmemcmp(&p.addr.in6, &n[i].addr.in6, p.prefix)) {
165+
n[i].dprefix = p.prefix;
166+
break;
167+
}
168+
}
169+
}
170+
}
171+
}
106172

107173
int netlink_add_netevent_handler(struct netevent_handler *handler)
108174
{
@@ -212,6 +278,20 @@ static void refresh_iface_addr6(int ifindex)
212278
}
213279

214280
if (change) {
281+
/*
282+
* Remove invalid prefixes that were added back to the interface.
283+
*/
284+
for (ssize_t i = 0; i < len; ++i) {
285+
size_t j = 0;
286+
while (j < iface->invalid_addr6_len) {
287+
if (addr[i].prefix <= iface->invalid_addr6[j].prefix &&
288+
odhcpd_bmemcmp(&addr[i].addr.in6, &iface->invalid_addr6[j].addr.in6, iface->invalid_addr6[j].prefix) == 0)
289+
odhcpd_del_intf_invalid_addr6(iface, j);
290+
else
291+
++j;
292+
}
293+
}
294+
215295
/*
216296
* Keep track on removed prefixes, so we could advertise them as invalid
217297
* for at least a couple of times.
@@ -756,6 +836,9 @@ ssize_t netlink_get_interface_addrs(int ifindex, bool v6, struct odhcpd_ipaddr *
756836
addr[i].valid += now;
757837
}
758838

839+
if (v6)
840+
find_addr6_dprefix(addr, ctxt.ret);
841+
759842
free:
760843
nlmsg_free(msg);
761844
out:

src/odhcpd.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ int main(int argc, char **argv)
7777
{
7878
openlog("odhcpd", LOG_PERROR | LOG_PID, LOG_DAEMON);
7979
int opt;
80+
bool ipv6 = ipv6_enabled();
8081

8182
while ((opt = getopt(argc, argv, "hl:")) != -1) {
8283
switch (opt) {
@@ -109,10 +110,10 @@ int main(int argc, char **argv)
109110
signal(SIGINT, sighandler);
110111
signal(SIGTERM, sighandler);
111112

112-
if (netlink_init())
113+
if (netlink_init(ipv6))
113114
return 4;
114115

115-
if (ipv6_enabled()) {
116+
if (ipv6) {
116117
if (router_init())
117118
return 4;
118119

src/odhcpd.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,26 @@ inline static struct dhcp_assignment *alloc_assignment(size_t extra_len)
399399
return a;
400400
}
401401

402+
inline static void odhcpd_del_intf_invalid_addr6(struct interface *iface, size_t idx)
403+
{
404+
if (idx + 1 < iface->invalid_addr6_len)
405+
memmove(&iface->invalid_addr6[idx], &iface->invalid_addr6[idx + 1],
406+
sizeof(*iface->invalid_addr6) * (iface->invalid_addr6_len - idx - 1));
407+
408+
iface->invalid_addr6_len--;
409+
410+
if (iface->invalid_addr6_len) {
411+
struct odhcpd_ipaddr *new_invalid_addr6 = realloc(iface->invalid_addr6,
412+
sizeof(*iface->invalid_addr6) * iface->invalid_addr6_len);
413+
414+
if (new_invalid_addr6)
415+
iface->invalid_addr6 = new_invalid_addr6;
416+
} else {
417+
free(iface->invalid_addr6);
418+
iface->invalid_addr6 = NULL;
419+
}
420+
}
421+
402422
// Exported main functions
403423
int odhcpd_register(struct odhcpd_event *event);
404424
int odhcpd_deregister(struct odhcpd_event *event);
@@ -452,6 +472,7 @@ void dhcpv6_ia_enum_addrs(struct interface *iface, struct dhcp_assignment *c, ti
452472
dhcpv6_binding_cb_handler_t func, void *arg);
453473
void dhcpv6_ia_write_statefile(void);
454474

475+
bool netlink_default_ipv6_route_exists();
455476
int netlink_add_netevent_handler(struct netevent_handler *hdlr);
456477
ssize_t netlink_get_interface_addrs(const int ifindex, bool v6,
457478
struct odhcpd_ipaddr **addrs);
@@ -468,7 +489,7 @@ void netlink_dump_neigh_table(const bool proxy);
468489
void netlink_dump_addr_table(const bool v6);
469490

470491
// Exported module initializers
471-
int netlink_init(void);
492+
int netlink_init(bool ipv6);
472493
int router_init(void);
473494
int dhcpv6_init(void);
474495
int ndp_init(void);

0 commit comments

Comments
 (0)