summaryrefslogtreecommitdiff
path: root/sysdep
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2022-07-26 18:45:20 +0200
committerOndrej Zajicek <santiago@crfreenet.org>2022-07-26 18:45:20 +0200
commitddb1bdf2819ce69248d5a51e71d803f13548b217 (patch)
treee1903f8339058875f6e5526a75724126b3eadfd0 /sysdep
parent722daa950046a7ad307fd7aca8e0506f30b3d000 (diff)
Netlink: Restrict route replace for IPv6
Seems like the previous patch was too optimistic, as route replace is still broken even in Linux 4.19 LTS (but fixed in Linux 5.10 LTS) for: ip route add 2001:db8::/32 via fe80::1 dev eth0 ip route replace 2001:db8::/32 dev eth0 It ends with two routes instead of just the second. The issue is limited to direct and special type (e.g. unreachable) routes, the patch restricts route replace for cases when the new route is a regular route (with a next hop address).
Diffstat (limited to 'sysdep')
-rw-r--r--sysdep/linux/netlink.c28
1 files changed, 27 insertions, 1 deletions
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index e41eb32d..12bacd35 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -1459,12 +1459,38 @@ done:
return nl_exchange(&r->h, (op == NL_OP_DELETE));
}
+static inline int
+nl_allow_replace(struct krt_proto *p, rte *new)
+{
+ /*
+ * We use NL_OP_REPLACE for IPv4, it has an issue with not checking for
+ * matching rtm_protocol, but that is OK when dedicated priority is used.
+ *
+ * For IPv6, the NL_OP_REPLACE is still broken even in Linux 4.19 LTS
+ * (although it seems to be fixed in Linux 5.10 LTS) for sequence:
+ *
+ * ip route add 2001:db8::/32 via fe80::1 dev eth0
+ * ip route replace 2001:db8::/32 dev eth0
+ *
+ * (it ends with two routes instead of replacing the first by the second one)
+ *
+ * Replacing with direct and special type (e.g. unreachable) routes does not
+ * work, but replacing with regular routes work reliably
+ */
+
+ if (krt_ipv4(p))
+ return 1;
+
+ rta *a = new->attrs;
+ return (a->dest == RTD_UNICAST) && ipa_nonzero(a->nh.gw);
+}
+
void
krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
{
int err = 0;
- if (old && new)
+ if (old && new && nl_allow_replace(p, new))
{
err = nl_send_route(p, new, NL_OP_REPLACE);
}