diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2024-05-30 16:12:15 +0200 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2024-05-30 16:12:15 +0200 |
commit | 00b139bd25c77b401d2065283cb970d9d8c1aa02 (patch) | |
tree | b5e2cd308eeb9bb94ad68b602cea37a5efba28b2 | |
parent | a5b4c21d8138bb533b02a60973b1ff4feedae456 (diff) |
Kernel: Do not use route replace when krt_metric differs
The krt_metric is a part of the primary key, so it cannot differ for
route replace operation.
Thanks to Leif Jakob for the bugreport.
-rw-r--r-- | sysdep/linux/netlink.c | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 6b4fc80c..533cc268 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1465,9 +1465,25 @@ done: } static inline int -nl_allow_replace(struct krt_proto *p, rte *new) +nl_allow_replace(struct krt_proto *p, rte *new, rte *old) { /* + * In kernel routing tables, (net, metric) is the primary key. Therefore, we + * can use NL_OP_REPLACE only if the new and and the old route have the same + * kernel metric, otherwise the replace would just add the new route while + * keep the old one. + */ + + if ((p->af != AF_MPLS) && (KRT_CF->sys.metric == 0)) + { + uint new_metric = ea_get_int(new->attrs->eattrs, EA_KRT_METRIC, 0); + uint old_metric = ea_get_int(old->attrs->eattrs, EA_KRT_METRIC, 0); + + if (new_metric != old_metric) + return 0; + } + + /* * 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. * @@ -1495,7 +1511,7 @@ krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old) { int err = 0; - if (old && new && nl_allow_replace(p, new)) + if (old && new && nl_allow_replace(p, new, old)) { err = nl_send_route(p, new, NL_OP_REPLACE); } |