summaryrefslogtreecommitdiffhomepage
path: root/src/netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/netlink.c')
-rw-r--r--src/netlink.c56
1 files changed, 56 insertions, 0 deletions
diff --git a/src/netlink.c b/src/netlink.c
index 39f6245..63a0f8b 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -205,10 +205,66 @@ static void refresh_iface_addr6(int ifindex)
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].prefix != iface->addr6[i].prefix ||
(addr[i].preferred > (uint32_t)now) != (iface->addr6[i].preferred > (uint32_t)now) ||
addr[i].valid < iface->addr6[i].valid || addr[i].preferred < iface->addr6[i].preferred)
change = true;
}
+
+ if (change) {
+ /*
+ * Keep track on removed prefixes, so we could advertise them as invalid
+ * for at least a couple of times.
+ *
+ * RFC7084 ยง 4.3 :
+ * L-13: If the delegated prefix changes, i.e., the current prefix is
+ * replaced with a new prefix without any overlapping time
+ * period, then the IPv6 CE router MUST immediately advertise the
+ * old prefix with a Preferred Lifetime of zero and a Valid
+ * Lifetime of either a) zero or b) the lower of the current
+ * Valid Lifetime and two hours (which must be decremented in
+ * real time) in a Router Advertisement message as described in
+ * Section 5.5.3, (e) of [RFC4862].
+ */
+
+ for (size_t i = 0; i < iface->addr6_len; ++i) {
+ bool removed = true;
+
+ if (iface->addr6[i].valid <= (uint32_t)now)
+ continue;
+
+ for (ssize_t j = 0; removed && j < len; ++j) {
+ size_t plen = min(addr[j].prefix, iface->addr6[i].prefix);
+
+ if (odhcpd_bmemcmp(&addr[j].addr.in6, &iface->addr6[i].addr.in6, plen) == 0)
+ removed = false;
+ }
+
+ for (size_t j = 0; removed && j < iface->invalid_addr6_len; ++j) {
+ size_t plen = min(iface->invalid_addr6[j].prefix, iface->addr6[i].prefix);
+
+ if (odhcpd_bmemcmp(&iface->invalid_addr6[j].addr.in6, &iface->addr6[i].addr.in6, plen) == 0)
+ removed = false;
+ }
+
+ if (removed) {
+ size_t pos = iface->invalid_addr6_len;
+ struct odhcpd_ipaddr *new_invalid_addr6 = realloc(iface->invalid_addr6,
+ sizeof(*iface->invalid_addr6) * (pos + 1));
+
+ if (!new_invalid_addr6)
+ break;
+
+ iface->invalid_addr6 = new_invalid_addr6;
+ iface->invalid_addr6_len++;
+ memcpy(&iface->invalid_addr6[pos], &iface->addr6[i], sizeof(*iface->invalid_addr6));
+ iface->invalid_addr6[pos].valid = iface->invalid_addr6[pos].preferred = (uint32_t)now;
+
+ if (iface->invalid_addr6[pos].prefix < 64)
+ iface->invalid_addr6[pos].prefix = 64;
+ }
+ }
+ }
}
iface->addr6 = addr;