From ddb1bdf2819ce69248d5a51e71d803f13548b217 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 26 Jul 2022 18:45:20 +0200 Subject: [PATCH] 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). --- sysdep/linux/netlink.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) 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); }