diff options
-rw-r--r-- | zebra/if_netlink.c | 54 | ||||
-rw-r--r-- | zebra/interface.c | 24 | ||||
-rw-r--r-- | zebra/interface.h | 2 |
3 files changed, 80 insertions, 0 deletions
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index cd602b3d8..4a37c14b9 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1015,6 +1015,54 @@ int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; } +/* helper function called by if_netlink_change + * to delete interfaces in case the interface moved + * to an other netns + */ +static void if_netlink_check_ifp_instance_consistency(uint16_t cmd, + struct interface *ifp, + ns_id_t ns_id) +{ + struct interface *old_ifp; + + /* + * look if interface name is also found on other netns + * - only if vrf backend is netns + * - do not concern lo interface + * - then remove previous one + * - for new link case, check found interface is not active + */ + if (!vrf_is_backend_netns() || + !strcmp(ifp->name, "lo")) + return; + old_ifp = if_lookup_by_name_not_ns(ns_id, ifp->name); + if (!old_ifp) + return; + if ((cmd == RTM_NEWLINK) + && (CHECK_FLAG(old_ifp->status, ZEBRA_INTERFACE_ACTIVE))) + return; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s %s(%u) %s VRF %u", + cmd == RTM_DELLINK ? + "RTM_DELLINK replaced by" : + "RTM_NEWLINK replaces", + ifp->name, + old_ifp->ifindex, + cmd == RTM_DELLINK ? + "in" : "from", + old_ifp->vrf_id); + /* the found interface replaces the current one + * remove it + */ + if (cmd == RTM_DELLINK) + if_delete(ifp); + else + if_delete(old_ifp); + /* the found interface is replaced by the current one + * suppress it + */ +} + int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, ns_id_t ns_id, int startup) { @@ -1175,6 +1223,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); + if_netlink_check_ifp_instance_consistency(RTM_NEWLINK, + ifp, ns_id); } else if (ifp->vrf_id != vrf_id) { /* VRF change for an interface. */ if (IS_ZEBRA_DEBUG_KERNEL) @@ -1242,6 +1292,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); + if_netlink_check_ifp_instance_consistency(RTM_NEWLINK, + ifp, ns_id); } } else { /* Delete interface notification from kernel */ @@ -1265,6 +1317,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, if (!IS_ZEBRA_IF_VRF(ifp)) if_delete_update(ifp); + if_netlink_check_ifp_instance_consistency(RTM_DELLINK, + ifp, ns_id); } return 0; diff --git a/zebra/interface.c b/zebra/interface.c index a011efd28..4f761a5e9 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -252,6 +252,30 @@ struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns, return NULL; } +/* this function must be used only if the vrf backend + * is a netns backend + */ +struct interface *if_lookup_by_name_not_ns(ns_id_t ns_id, + const char *ifname) +{ + struct interface *ifp; + struct ns *ns; + + RB_FOREACH (ns, ns_head, &ns_tree) { + if (ns->ns_id == ns_id) + continue; + /* if_delete_update has removed interface + * from zns->if_table + * so to look for interface, use the vrf list + */ + ifp = if_lookup_by_name(ifname, (vrf_id_t)ns->ns_id); + if (!ifp) + continue; + return ifp; + } + return NULL; +} + const char *ifindex2ifname_per_ns(struct zebra_ns *zns, unsigned int ifindex) { struct interface *ifp; diff --git a/zebra/interface.h b/zebra/interface.h index 7a776f5e3..fba3201c5 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -317,6 +317,8 @@ extern void zebra_if_init(void); extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *, uint32_t); extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *, const char *); +extern struct interface *if_lookup_by_name_not_ns(ns_id_t ns_id, + const char *ifname); extern struct interface *if_link_per_ns(struct zebra_ns *, struct interface *); extern const char *ifindex2ifname_per_ns(struct zebra_ns *, unsigned int); |