summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);
}