diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2022-07-26 18:45:20 +0200 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2022-07-26 18:45:20 +0200 |
commit | ddb1bdf2819ce69248d5a51e71d803f13548b217 (patch) | |
tree | e1903f8339058875f6e5526a75724126b3eadfd0 /sysdep/linux | |
parent | 722daa950046a7ad307fd7aca8e0506f30b3d000 (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/linux')
-rw-r--r-- | sysdep/linux/netlink.c | 28 |
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); } |