From 14ddb3d9c42a9845d1a7443b9231f92bc80b4f2d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 24 Sep 2019 10:44:58 +0200 Subject: [PATCH 01/21] zebra: across network namespace, vxlan remembers the link information the link information of vxlan interface is populated in layer 2 information, as well as in layer 2 vxlan information. This information will be used later to collect vnis that are in other network namespaces, but where bgp evpn is enabled on main network namespaces, and those vnis have the link information in that namespace. Signed-off-by: Philippe Guibert --- zebra/if_netlink.c | 35 +++++++++++++++++++++++++++++++---- zebra/interface.c | 11 +++++++++++ zebra/zebra_l2.h | 4 ++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 429bb968a5..1f9a5db597 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -482,6 +482,7 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, struct rtattr *attr[IFLA_VXLAN_MAX + 1]; vni_t vni_in_msg; struct in_addr vtep_ip_in_msg; + ifindex_t ifindex_link; memset(vxl_info, 0, sizeof(*vxl_info)); memset(attr, 0, sizeof(attr)); @@ -510,6 +511,15 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, *(struct in_addr *)RTA_DATA(attr[IFLA_VXLAN_GROUP]); } + if (!attr[IFLA_VXLAN_LINK]) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("IFLA_VXLAN_LINK missing " + "from VXLAN IF message"); + } else { + ifindex_link = + *(ifindex_t *)RTA_DATA(attr[IFLA_VXLAN_LINK]); + vxl_info->ifindex_link = ifindex_link; + } return 0; } @@ -519,7 +529,8 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, * its members. Likewise, for VxLAN interface. */ static void netlink_interface_update_l2info(struct interface *ifp, - struct rtattr *link_data, int add) + struct rtattr *link_data, int add, + ns_id_t link_nsid) { if (!link_data) return; @@ -538,7 +549,12 @@ static void netlink_interface_update_l2info(struct interface *ifp, struct zebra_l2info_vxlan vxlan_info; netlink_extract_vxlan_info(link_data, &vxlan_info); + vxlan_info.link_nsid = link_nsid; zebra_l2_vxlanif_add_update(ifp, &vxlan_info, add); + if (link_nsid != NS_UNKNOWN && + vxlan_info.ifindex_link) + zebra_if_update_link(ifp, vxlan_info.ifindex_link, + link_nsid); } } @@ -622,6 +638,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t link_ifindex = IFINDEX_INTERNAL; ifindex_t bond_ifindex = IFINDEX_INTERNAL; struct zebra_if *zif; + ns_id_t link_nsid = ns_id; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -705,6 +722,9 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); + if (tb[IFLA_LINK_NETNSID]) + link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + /* Add interface. * We add by index first because in some cases such as the master * interface, we have the index before we have the name. Fixing @@ -749,7 +769,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Extract and save L2 interface information, take additional actions. */ - netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], 1); + netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], + 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) @@ -1168,6 +1189,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t link_ifindex = IFINDEX_INTERNAL; uint8_t old_hw_addr[INTERFACE_HWADDR_MAX]; struct zebra_if *zif; + ns_id_t link_nsid = ns_id; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -1235,6 +1257,9 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); + if (tb[IFLA_LINK_NETNSID]) + link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + if (tb[IFLA_IFALIAS]) { desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); } @@ -1319,7 +1344,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Extract and save L2 interface information, take * additional actions. */ netlink_interface_update_l2info( - ifp, linkinfo[IFLA_INFO_DATA], 1); + ifp, linkinfo[IFLA_INFO_DATA], + 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); @@ -1421,7 +1447,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Extract and save L2 interface information, take * additional actions. */ netlink_interface_update_l2info( - ifp, linkinfo[IFLA_INFO_DATA], 0); + ifp, linkinfo[IFLA_INFO_DATA], + 0, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); diff --git a/zebra/interface.c b/zebra/interface.c index 9a248ba5d1..b2a309be9e 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1488,6 +1488,17 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) if (vxlan_info->mcast_grp.s_addr != INADDR_ANY) vty_out(vty, " Mcast Group %s", inet_ntoa(vxlan_info->mcast_grp)); + if (vxlan_info->ifindex_link && + (vxlan_info->link_nsid != NS_UNKNOWN)) { + struct interface *ifp; + + ifp = if_lookup_by_index_per_ns( + zebra_ns_lookup(vxlan_info->link_nsid), + vxlan_info->ifindex_link); + vty_out(vty, " Link Interface %s", + ifp == NULL ? "Unknown" : + ifp->name); + } vty_out(vty, "\n"); } diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index d9f0eec3f8..23875331f7 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -55,6 +55,10 @@ struct zebra_l2info_vxlan { struct in_addr vtep_ip; /* Local tunnel IP */ vlanid_t access_vlan; /* Access VLAN - for VLAN-aware bridge. */ struct in_addr mcast_grp; + ifindex_t ifindex_link; /* Interface index of interface + * linked with VXLAN + */ + ns_id_t link_nsid; }; struct zebra_l2info_bondslave { From 28254125d06f65cc4344b6156eec76a37ec6aede Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 26 Sep 2019 18:49:59 +0200 Subject: [PATCH 02/21] zebra: importation of bgp evpn rt5 from vni with other netns With vrf-lite mechanisms, it is possible to create layer 3 vnis by creating a bridge interface in default vr, by creating a vxlan interface that is attached to that bridge interface, then by moving the vxlan interface to the wished vrf. With vrf-netns mechanism, it is slightly different since bridged interfaces can not be separated in different network namespaces. To make it work, the setup consists in : - creating a vxlan interface on default vrf. - move the vxlan interface to the wished vrf ( with an other netns) - create a bridge interface in the wished vrf - attach the vxlan interface to that bridged interface from that point, if BGP is enabled to advertise vnis in default vrf, then vxlan interfaces are discovered appropriately in other vrfs, provided that the link interface still resides in the vrf where l2vpn is advertised. to import ipv4 entries from a separate vrf, into the l2vpn, the configuration of vni in the dedicated vrf + the advertisement of ipv4 entries in bgp vrf will import the entries in the bgp l2vpn. the modification consists in parsing the vxlan interfaces in all network namespaces, where the link resides in the same network namespace as the bgp core instance where bgp l2vpn is enabled. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 2 +- lib/netns_other.c | 2 +- lib/ns.h | 2 ++ zebra/zebra_ns.c | 22 ++++++++++++ zebra/zebra_ns.h | 8 +++++ zebra/zebra_vxlan.c | 88 ++++++++++++++++++++++++++++++++++++--------- 6 files changed, 106 insertions(+), 18 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 98f359401e..09a42b850b 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -51,7 +51,7 @@ static struct ns *ns_lookup_name_internal(const char *name); RB_GENERATE(ns_head, ns, entry, ns_compare) -static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); static struct ns *default_ns; static int ns_current_ns_fd; diff --git a/lib/netns_other.c b/lib/netns_other.c index 740d2b621e..b0aae4f8df 100644 --- a/lib/netns_other.c +++ b/lib/netns_other.c @@ -34,7 +34,7 @@ static inline int ns_compare(const struct ns *ns, const struct ns *ns2); RB_GENERATE(ns_head, ns, entry, ns_compare) -static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); static inline int ns_compare(const struct ns *a, const struct ns *b) { diff --git a/lib/ns.h b/lib/ns.h index 20e0a38e3b..1963b8a359 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -71,6 +71,8 @@ struct ns { RB_HEAD(ns_head, ns); RB_PROTOTYPE(ns_head, ns, entry, ns_compare) +extern struct ns_head ns_tree; + /* * API for managing NETNS. eg from zebra daemon * one want to manage the list of NETNS, etc... diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 4e51437337..4e23ca2f03 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -229,3 +229,25 @@ int zebra_ns_config_write(struct vty *vty, struct ns *ns) vty_out(vty, " netns %s\n", ns->name); return 0; } + +void zebra_ns_list_walk(int (*exec_for_each_zns)(struct zebra_ns *zns, + void *param_in, + void **param_out), + void *param_in, + void **param_out) +{ + struct ns *ns; + struct zebra_ns *zns; + int ret; + + RB_FOREACH (ns, ns_head, &ns_tree) { + zns = (struct zebra_ns *)ns->info; + if (!zns && ns->ns_id == NS_DEFAULT) + zns = zebra_ns_lookup(ns->ns_id); + if (!zns) + continue; + ret = exec_for_each_zns(zns, param_in, param_out); + if (ret == ZNS_WALK_STOP) + return; + } +} diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index dc79a83db0..fa2fd47c25 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -72,6 +72,14 @@ int zebra_ns_final_shutdown(struct ns *ns); int zebra_ns_config_write(struct vty *vty, struct ns *ns); +#define ZNS_WALK_CONTINUE 0 +#define ZNS_WALK_STOP 1 +void zebra_ns_list_walk(int (*exec_for_each_zns)(struct zebra_ns *zns, + void *param_in, + void **param_out), + void *param_in, + void **param_out); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index d85f48e570..863eef17a3 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -4115,18 +4115,20 @@ static int zvni_send_del_to_client(vni_t vni) return zserv_send_message(client, s); } -/* - * Build the VNI hash table by going over the VxLAN interfaces. This - * is called when EVPN (advertise-all-vni) is enabled. - */ -static void zvni_build_hash_table(void) +static int zvni_build_hash_table_zns(struct zebra_ns *zns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { - struct zebra_ns *zns; struct route_node *rn; struct interface *ifp; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return ZNS_WALK_STOP; /* Walk VxLAN interfaces and create VNI hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { vni_t vni; zebra_vni_t *zvni = NULL; @@ -4143,7 +4145,15 @@ static void zvni_build_hash_table(void) vxl = &zif->l2info.vxl; vni = vxl->vni; - + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vni); + continue; + } /* L3-VNI and L2-VNI are handled seperately */ zl3vni = zl3vni_lookup(vni); if (zl3vni) { @@ -4212,7 +4222,7 @@ static void zvni_build_hash_table(void) zlog_debug( "Failed to add VNI hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return; + return ZNS_WALK_CONTINUE; } if (zvni->local_vtep_ip.s_addr != @@ -4249,6 +4259,19 @@ static void zvni_build_hash_table(void) } } } + return ZNS_WALK_CONTINUE; +} + +/* + * Build the VNI hash table by going over the VxLAN interfaces. This + * is called when EVPN (advertise-all-vni) is enabled. + */ + +static void zvni_build_hash_table(void) +{ + zebra_ns_list_walk(zvni_build_hash_table_zns, + (void *)NULL, + (void **)NULL); } /* @@ -5033,14 +5056,21 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni) return 0; } -struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +static int zl3vni_map_to_vxlan_if_zns(struct zebra_ns *zns, + void *_zl3vni, + void **_pifp) { - struct zebra_ns *zns = NULL; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)_zl3vni; struct route_node *rn = NULL; struct interface *ifp = NULL; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return ZNS_WALK_STOP; /* loop through all vxlan-interface */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { struct zebra_if *zif = NULL; @@ -5055,13 +5085,39 @@ struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) continue; vxl = &zif->l2info.vxl; - if (vxl->vni == zl3vni->vni) { - zl3vni->local_vtep_ip = vxl->vtep_ip; - return ifp; + if (vxl->vni != zl3vni->vni) + continue; + + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vxl->vni); + continue; } + + + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (_pifp) + *_pifp = (void *)ifp; + return ZNS_WALK_STOP; } - return NULL; + return ZNS_WALK_CONTINUE; +} + +struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +{ + struct interface **p_ifp; + struct interface *ifp = NULL; + + p_ifp = &ifp; + + zebra_ns_list_walk(zl3vni_map_to_vxlan_if_zns, + (void *)zl3vni, (void **)p_ifp); + return ifp; } struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) From 6fe516bd4b85569b3b8b4bcc2910afc5569aa026 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 27 Sep 2019 11:17:20 +0200 Subject: [PATCH 03/21] zebra: zvni_from_svi() adaptation for other network namespaces other network namespaces are parsed because bridge interface can be bridged with vxlan interfaces with a link in the default vrf that hosts l2vpn. Signed-off-by: Philippe Guibert --- zebra/zebra_vxlan.c | 109 +++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 863eef17a3..232f455432 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3575,51 +3575,35 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp, return zvni; } -/* - * Map SVI and associated bridge to a VNI. This is invoked upon getting - * neighbor notifications, to see if they are of interest. - */ -static zebra_vni_t *zvni_from_svi(struct interface *ifp, - struct interface *br_if) +struct zvni_from_svi_param { + struct interface *br_if; + struct zebra_if *zif; + uint8_t bridge_vlan_aware; + vlanid_t vid; +}; + +static int zvni_from_svi_zns(struct zebra_ns *zns, + void *_in_param, + void **_p_zvni) { - struct zebra_ns *zns; struct route_node *rn; + struct interface *br_if; + zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni; + zebra_vni_t *zvni; struct interface *tmp_if = NULL; struct zebra_if *zif; - struct zebra_l2info_bridge *br; struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - vlanid_t vid = 0; - zebra_vni_t *zvni; + struct zvni_from_svi_param *in_param = + (struct zvni_from_svi_param *)_in_param; int found = 0; - if (!br_if) - return NULL; - - /* Make sure the linked interface is a bridge. */ - if (!IS_ZEBRA_IF_BRIDGE(br_if)) - return NULL; - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; + if (!in_param) + return ZNS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; assert(zif); - br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - if (bridge_vlan_aware) { - struct zebra_l2info_vlan *vl; - if (!IS_ZEBRA_IF_VLAN(ifp)) - return NULL; - - zif = ifp->info; - assert(zif); - vl = &zif->l2info.vl; - vid = vl->vid; - } - - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; if (!tmp_if) @@ -3634,16 +3618,67 @@ static zebra_vni_t *zvni_from_svi(struct interface *ifp, if (zif->brslave_info.br_if != br_if) continue; - if (!bridge_vlan_aware || vxl->access_vlan == vid) { + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == !in_param->vid) { found = 1; break; } } if (!found) - return NULL; + return ZNS_WALK_CONTINUE; zvni = zvni_lookup(vxl->vni); + if (p_zvni) + *p_zvni = zvni; + return ZNS_WALK_STOP; +} + +/* + * Map SVI and associated bridge to a VNI. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + */ +static zebra_vni_t *zvni_from_svi(struct interface *ifp, + struct interface *br_if) +{ + struct zebra_l2info_bridge *br; + zebra_vni_t *zvni = NULL; + zebra_vni_t **p_zvni; + struct zebra_if *zif; + struct zvni_from_svi_param in_param; + + if (!br_if) + return NULL; + + /* Make sure the linked interface is a bridge. */ + if (!IS_ZEBRA_IF_BRIDGE(br_if)) + return NULL; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = 0; + + if (in_param.bridge_vlan_aware) { + struct zebra_l2info_vlan *vl; + + if (!IS_ZEBRA_IF_VLAN(ifp)) + return NULL; + + zif = ifp->info; + assert(zif); + vl = &zif->l2info.vl; + in_param.vid = vl->vid; + } + + in_param.br_if = br_if; + in_param.zif = zif; + p_zvni = &zvni; + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + zebra_ns_list_walk(zvni_from_svi_zns, (void *)&in_param, + (void **)p_zvni); return zvni; } From 2a9dccb6475bfc11af2b855c4c8ff9e500ba21f4 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 27 Sep 2019 15:45:42 +0200 Subject: [PATCH 04/21] zebra: zvni_map_to_svi() adaptation for other network namespaces the function is called with all the network namespaces. Signed-off-by: Philippe Guibert --- zebra/zebra_vxlan.c | 76 ++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 232f455432..126e2c2bc3 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3682,6 +3682,42 @@ static zebra_vni_t *zvni_from_svi(struct interface *ifp, return zvni; } +static int zvni_map_to_svi_zns(struct zebra_ns *zns, + void *_in_param, + void **_p_ifp) +{ + struct route_node *rn; + struct zvni_from_svi_param *in_param = + (struct zvni_from_svi_param *)_in_param; + struct zebra_l2info_vlan *vl; + struct interface *tmp_if = NULL; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct zebra_if *zif; + + if (!in_param) + return ZNS_WALK_STOP; + + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VLAN + || zif->link != in_param->br_if) + continue; + vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; + + if (vl->vid == in_param->vid) { + if (p_ifp) + *p_ifp = tmp_if; + return ZNS_WALK_STOP; + } + } + return ZNS_WALK_CONTINUE; +} + /* Map to SVI on bridge corresponding to specified VLAN. This can be one * of two cases: * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface @@ -3691,15 +3727,11 @@ static zebra_vni_t *zvni_from_svi(struct interface *ifp, */ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { - struct zebra_ns *zns; - struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zebra_l2info_vlan *vl; - uint8_t bridge_vlan_aware; - int found = 0; - + struct zvni_from_svi_param in_param; + struct interface **p_ifp; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; @@ -3708,33 +3740,19 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) zif = br_if->info; assert(zif); br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - + in_param.bridge_vlan_aware = br->vlan_aware; /* Check oper status of the SVI. */ - if (!bridge_vlan_aware) + if (!in_param.bridge_vlan_aware) return if_is_operative(br_if) ? br_if : NULL; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = NULL; + p_ifp = &tmp_if; /* Identify corresponding VLAN interface. */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VLAN - || zif->link != br_if) - continue; - vl = &zif->l2info.vl; - - if (vl->vid == vid) { - found = 1; - break; - } - } - - return found ? tmp_if : NULL; + zebra_ns_list_walk(zvni_map_to_svi_zns, (void *)&in_param, + (void **)p_ifp); + return tmp_if; } /* Map to MAC-VLAN interface corresponding to specified SVI interface. From fc3141393ad95651d31fccd144b5c029d00e5f3a Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 27 Sep 2019 15:12:27 +0200 Subject: [PATCH 05/21] zebra: display interface slave type interface slave type is displayed. Signed-off-by: Philippe Guibert --- zebra/interface.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/zebra/interface.c b/zebra/interface.c index b2a309be9e..9d1f70609b 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1236,6 +1236,23 @@ static void nbr_connected_dump_vty(struct vty *vty, vty_out(vty, "\n"); } +static const char *zebra_zifslavetype_2str(zebra_slave_iftype_t zif_slave_type) +{ + switch (zif_slave_type) { + case ZEBRA_IF_SLAVE_BRIDGE: + return "Bridge"; + case ZEBRA_IF_SLAVE_VRF: + return "Vrf"; + case ZEBRA_IF_SLAVE_BOND: + return "Bond"; + case ZEBRA_IF_SLAVE_OTHER: + return "Other"; + case ZEBRA_IF_SLAVE_NONE: + return "None"; + } + return "None"; +} + static const char *zebra_ziftype_2str(zebra_iftype_t zif_type) { switch (zif_type) { @@ -1463,6 +1480,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " Interface Type %s\n", zebra_ziftype_2str(zebra_if->zif_type)); + vty_out(vty, " Interface Slave Type %s\n", + zebra_zifslavetype_2str(zebra_if->zif_slave_type)); + if (IS_ZEBRA_IF_BRIDGE(ifp)) { struct zebra_l2info_bridge *bridge_info; From 7c990878f20efff335c1211deda3ec50071ae2b5 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 1 Oct 2019 11:07:13 +0200 Subject: [PATCH 06/21] zebra: map vxlan interface to bridge interface with correct ns id an incoming bridge index has been found, that is linked with vxlan interface, and the search for that bridge interface is done. In vrf-lite, the search is done across the same default namespace, because bridge and vxlan may not be in the same vrf. But this behaviour is wrong when using vrf netns backend, as the bridge and the vxlan have to be in the same vrf ( hence in the same network namespace). To comply with that, use the netnamespace of the vxlan interface. Like that, the appropriate nsid is passed as parameter, and consequently, the search is correct, and the mac address passed to BGP will be ok too. Signed-off-by: Philippe Guibert --- zebra/zebra_l2.c | 14 ++++++++++---- zebra/zebra_l2.h | 3 ++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index e549d80a5c..d12fb3e30d 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -83,12 +83,14 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) } /* Public functions */ -void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave) +void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns) { struct interface *br_if; /* TODO: Handle change of master */ - br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + assert(zns); + br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(zns->ns_id), br_slave->bridge_ifindex); if (br_if) br_slave->br_if = br_if; @@ -241,19 +243,23 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, { struct zebra_if *zif; ifindex_t old_bridge_ifindex; + struct zebra_vrf *zvrf; zif = ifp->info; assert(zif); + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) + return; + old_bridge_ifindex = zif->brslave_info.bridge_ifindex; if (old_bridge_ifindex == bridge_ifindex) return; zif->brslave_info.bridge_ifindex = bridge_ifindex; - /* Set up or remove link with master */ if (bridge_ifindex != IFINDEX_INTERNAL) { - zebra_l2_map_slave_to_bridge(&zif->brslave_info); + zebra_l2_map_slave_to_bridge(&zif->brslave_info, zvrf->zns); /* In the case of VxLAN, invoke the handler for EVPN. */ if (zif->zif_type == ZEBRA_IF_VXLAN) zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 23875331f7..3e8de79951 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -81,7 +81,8 @@ union zebra_l2if_info { #define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) ((zif)->l2info.br.vlan_aware == 1) -extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave); +extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns); extern void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave); extern void From 97c9e7533bd22029ac19838c043cfca82d2f6eb3 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 2 Oct 2019 12:14:13 +0200 Subject: [PATCH 07/21] zebra, lib: add an internal API to get relative default nsid in other ns as remind, the netns identifiers are local to a namespace. that is to say that for instance, a vrf will have a netns id value in one netns, and have an other netns id value in one other netns. There is a need for zebra daemon to collect some cross information, like the LINK_NETNSID information from interfaces having link layer in an other network namespace. For that, it is needed to have a global overview instead of a relative overview per namespace. The first brick of this change is an API that sticks to netlink API, that uses NETNSA_TARGET_NSID. from a given vrf vrfX, and a new vrf created vrfY, the API returns the value of nsID from vrfX, inside the new vrf vrfY. The brick also gets the ns id value of default namespace in each other namespace. An additional value in ns.h is offered, that permits to retrieve the default namespace context. Signed-off-by: Philippe Guibert --- include/linux/net_namespace.h | 1 + lib/netns_linux.c | 6 +++ lib/ns.h | 1 + zebra/zebra_netns_id.c | 90 +++++++++++++++++++++++++++++++++++ zebra/zebra_netns_id.h | 2 + zebra/zebra_netns_notify.c | 12 ++++- 6 files changed, 111 insertions(+), 1 deletion(-) diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h index 0187c74d88..0ed9dd61d3 100644 --- a/include/linux/net_namespace.h +++ b/include/linux/net_namespace.h @@ -16,6 +16,7 @@ enum { NETNSA_NSID, NETNSA_PID, NETNSA_FD, + NETNSA_TARGET_NSID, __NETNSA_MAX, }; diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 09a42b850b..bb66e8938f 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -590,3 +590,9 @@ ns_id_t ns_get_default_id(void) return default_ns->ns_id; return NS_DEFAULT_INTERNAL; } + +struct ns *ns_get_default(void) +{ + return default_ns; +} + diff --git a/lib/ns.h b/lib/ns.h index 1963b8a359..c594cbfac9 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -177,6 +177,7 @@ extern struct ns *ns_lookup_name(const char *name); extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); extern void ns_disable(struct ns *ns); +extern struct ns *ns_get_default(void); #ifdef __cplusplus } diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 77a9a7c368..eca0961b99 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -314,11 +314,101 @@ ns_id_t zebra_ns_id_get(const char *netnspath) return return_nsid; } +/* if nsid is not default one, get relative default ns for the new ns + * for instance, if default ns is 0 and a new ns "vrf1" id is 1, + * in ns "vrf1", the default ns is not known. using GETNSID with + * two parameters: NETNS_NSID, and NETNS_TARGET_NSID will help + * in identifying that value + */ +ns_id_t zebra_ns_id_get_relative_value(ns_id_t ns_read, ns_id_t target_nsid) +{ + struct sockaddr_nl snl; + int sock, ret; + unsigned int seq; + ns_id_t return_nsid = NS_UNKNOWN; + char buf[NETLINK_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int len; + + if (ns_read == NS_UNKNOWN || target_nsid == NS_UNKNOWN) + return return_nsid; + + /* netlink socket */ + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", + sock, safe_strerror(errno)); + return NS_UNKNOWN; + } + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTNLGRP_NSID; + snl.nl_pid = 0; /* AUTO PID */ + ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); + if (ret < 0) { + flog_err_sys(EC_LIB_SOCKET, + "netlink( %u) socket() bind error: %s", sock, + safe_strerror(errno)); + close(sock); + return NS_UNKNOWN; + } + + /* message to send to netlink : GETNSID */ + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_GETNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_read); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_TARGET_NSID, target_nsid); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = + (struct nlmsgerr + *)((char *)nlh + + NETLINK_ALIGN(sizeof( + struct + nlmsghdr))); + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + break; + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN( + nlh->nlmsg_len)); + } while (len != 0 && ret == 0); + + return return_nsid; +} + #else ns_id_t zebra_ns_id_get(const char *netnspath) { return zebra_ns_id_get_fallback(netnspath); } + +ns_id_t zebra_ns_id_get_relative_value(ns_id_t ns_read, ns_id_t target_nsid) +{ + return NS_UNKNOWN; +} #endif /* ! defined(HAVE_NETLINK) */ #ifdef HAVE_NETNS diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index 7a5f6851f4..215d627b7b 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -26,6 +26,8 @@ extern "C" { extern ns_id_t zebra_ns_id_get(const char *netnspath); extern ns_id_t zebra_ns_id_get_default(void); +extern ns_id_t zebra_ns_id_get_relative_value(ns_id_t ns_read, + ns_id_t target_nsid); #ifdef __cplusplus } diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index c5d11f1837..105886bb67 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -72,7 +72,8 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) char *netnspath = ns_netns_pathname(NULL, name); struct vrf *vrf; int ret; - ns_id_t ns_id, ns_id_external; + ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN; + struct ns *default_ns; if (netnspath == NULL) return; @@ -82,6 +83,15 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) } if (ns_id == NS_UNKNOWN) return; + default_ns = ns_get_default(); + if (default_ns && default_ns->internal_ns_id != ns_id) { + frr_with_privs(&zserv_privs) { + ns_id_relative = + zebra_ns_id_get_relative_value( + default_ns->internal_ns_id, + ns_id); + } + } ns_id_external = ns_map_nsid_with_external(ns_id, true); /* if VRF with NS ID already present */ vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external); From 9d3555e06ccc68fe37e0a00100029ac4bad8dee2 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 2 Oct 2019 13:34:40 +0200 Subject: [PATCH 08/21] zebra, lib: store relative default ns id in each namespace to be able to retrieve the network namespace identifier for each namespace, the ns id is stored in each ns context. For default namespace, the netns id is the same as that value. Signed-off-by: Philippe Guibert --- lib/ns.h | 5 +++++ lib/vrf.c | 8 ++++++-- lib/vrf.h | 2 +- zebra/zebra_netns_notify.c | 4 +++- zebra/zebra_ns.c | 4 ++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/ns.h b/lib/ns.h index c594cbfac9..188161e11a 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -53,6 +53,11 @@ struct ns { /* Identifier, mapped on the NSID value */ ns_id_t internal_ns_id; + /* Identifier, value of NSID of default netns, + * relative value in that local netns + */ + ns_id_t relative_default_ns; + /* Name */ char *name; diff --git a/lib/vrf.c b/lib/vrf.c index 6bd577fce1..9df5d19516 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -652,7 +652,8 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, } int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, - ns_id_t ns_id, ns_id_t internal_ns_id) + ns_id_t ns_id, ns_id_t internal_ns_id, + ns_id_t rel_def_ns_id) { struct ns *ns = NULL; @@ -700,6 +701,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, } ns = ns_get_created(ns, pathname, ns_id); ns->internal_ns_id = internal_ns_id; + ns->relative_default_ns = rel_def_ns_id; ns->vrf_ctxt = (void *)vrf; vrf->ns_ctxt = (void *)ns; /* update VRF netns NAME */ @@ -797,7 +799,9 @@ DEFUN_NOSH (vrf_netns, frr_with_privs(vrf_daemon_privs) { ret = vrf_netns_handler_create(vty, vrf, pathname, - NS_UNKNOWN, NS_UNKNOWN); + NS_UNKNOWN, + NS_UNKNOWN, + NS_UNKNOWN); } return ret; } diff --git a/lib/vrf.h b/lib/vrf.h index 83ed16b48e..a8514d74ed 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -315,7 +315,7 @@ extern int vrf_handler_create(struct vty *vty, const char *name, */ extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, ns_id_t ext_ns_id, - ns_id_t ns_id); + ns_id_t ns_id, ns_id_t rel_def_ns_id); /* used internally to enable or disable VRF. * Notify a change in the VRF ID of the VRF diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 105886bb67..0e2c1684d5 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -109,7 +109,9 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) } frr_with_privs(&zserv_privs) { ret = vrf_netns_handler_create(NULL, vrf, netnspath, - ns_id_external, ns_id); + ns_id_external, + ns_id, + ns_id_relative); } if (ret != CMD_SUCCESS) { flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 4e23ca2f03..13864cd429 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -183,12 +183,16 @@ int zebra_ns_init(const char *optional_default_name) struct ns *default_ns; ns_id_t ns_id; ns_id_t ns_id_external; + struct ns *ns; frr_with_privs(&zserv_privs) { ns_id = zebra_ns_id_get_default(); } ns_id_external = ns_map_nsid_with_external(ns_id, true); ns_init_management(ns_id_external, ns_id); + ns = ns_get_default(); + if (ns) + ns->relative_default_ns = ns_id; default_ns = ns_lookup(ns_get_default_id()); if (!default_ns) { From b6ebab34f664ba1cc9479fc1287f127c12077509 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 2 Oct 2019 13:37:11 +0200 Subject: [PATCH 09/21] zebra, lib: new API to get absolute netns val from relative netns val when receiving a netlink API for an interface in a namespace, this interface may come with LINK_NSID value, which means that the interface has its link in an other namespace. Unfortunately, the link_nsid value is self to that namespace, and there is a need to know what is its associated nsid value from the default namespace point of view. The information collected previously on each namespace, can then be compared with that value to check if the link belongs to the default namespace or not. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 18 ++++++++++++++++++ lib/ns.h | 1 + zebra/if_netlink.c | 9 ++++++--- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index bb66e8938f..82e0c4d8e0 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -584,6 +584,24 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) return ret; } +/* if relative link_nsid matches default netns, + * then return default absolute netns value + * otherwise, return NS_UNKNOWN + */ +ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid) +{ + struct ns *ns; + + ns = ns_lookup(ns_id_reference); + if (!ns) + return NS_UNKNOWN; + if (ns->relative_default_ns != link_nsid) + return NS_UNKNOWN; + ns = ns_get_default(); + assert(ns); + return ns->ns_id; +} + ns_id_t ns_get_default_id(void) { if (default_ns) diff --git a/lib/ns.h b/lib/ns.h index 188161e11a..0dfd977429 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -181,6 +181,7 @@ extern struct ns *ns_lookup_name(const char *name); */ extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); +extern ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid); extern void ns_disable(struct ns *ns); extern struct ns *ns_get_default(void); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 1f9a5db597..782de87a75 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -722,8 +722,10 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - if (tb[IFLA_LINK_NETNSID]) + if (tb[IFLA_LINK_NETNSID]) { link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } /* Add interface. * We add by index first because in some cases such as the master @@ -1257,9 +1259,10 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - if (tb[IFLA_LINK_NETNSID]) + if (tb[IFLA_LINK_NETNSID]) { link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); - + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } if (tb[IFLA_IFALIAS]) { desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); } From b5b453a2d6af58692bee0e256fe1dffe99824801 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 3 Oct 2019 17:11:37 +0200 Subject: [PATCH 10/21] zebra: bridge layer2 information records ns_id where bridge is when working with vrf netns backend, two bridges interfaces may have the same bridge interface index, but not the same namespace. because in vrf netns backend mode, a bridge slave always belong to the same network namespace, then a check with the namespace id and the ns id of the bridge interface permits to resolve correctly the interface pointer. The problem could occur if a same index of two bridge interfaces can be found on two different namespaces. Signed-off-by: Philippe Guibert --- zebra/if_netlink.c | 8 +++++--- zebra/zebra_l2.c | 18 +++++++++++++++--- zebra/zebra_l2.h | 4 +++- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 782de87a75..55bcda8182 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -774,7 +774,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) - zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); + zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); @@ -1351,7 +1351,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } else if (ifp->vrf_id != vrf_id) { @@ -1454,7 +1455,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 0, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index d12fb3e30d..a214494492 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -53,7 +53,13 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) { struct vrf *vrf; struct interface *ifp; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + zvrf = zebra_vrf_lookup_by_id(br_if->vrf_id); + assert(zvrf); + zns = zvrf->zns; + assert(zns); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES (vrf, ifp) { struct zebra_if *zif; @@ -72,7 +78,8 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) br_slave = &zif->brslave_info; if (link) { - if (br_slave->bridge_ifindex == br_if->ifindex) + if (br_slave->bridge_ifindex == br_if->ifindex && + br_slave->ns_id == zns->ns_id) br_slave->br_if = br_if; } else { if (br_slave->br_if == br_if) @@ -239,10 +246,12 @@ void zebra_l2_vxlanif_del(struct interface *ifp) * from a bridge before it can be mapped to another bridge. */ void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex) + ifindex_t bridge_ifindex, + ns_id_t ns_id) { struct zebra_if *zif; ifindex_t old_bridge_ifindex; + ns_id_t old_ns_id; struct zebra_vrf *zvrf; zif = ifp->info; @@ -253,9 +262,12 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, return; old_bridge_ifindex = zif->brslave_info.bridge_ifindex; - if (old_bridge_ifindex == bridge_ifindex) + old_ns_id = zif->brslave_info.ns_id; + if (old_bridge_ifindex == bridge_ifindex && + old_ns_id == zif->brslave_info.ns_id) return; + zif->brslave_info.ns_id = ns_id; zif->brslave_info.bridge_ifindex = bridge_ifindex; /* Set up or remove link with master */ if (bridge_ifindex != IFINDEX_INTERNAL) { diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 3e8de79951..a3c780ee09 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -37,6 +37,7 @@ extern "C" { struct zebra_l2info_brslave { ifindex_t bridge_ifindex; /* Bridge Master */ struct interface *br_if; /* Pointer to master */ + ns_id_t ns_id; /* network namespace where bridge is */ }; /* zebra L2 interface information - bridge interface */ @@ -102,7 +103,8 @@ extern void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, vlanid_t access_vlan); extern void zebra_l2_vxlanif_del(struct interface *ifp); extern void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex); + ifindex_t bridge_ifindex, + ns_id_t ns_id); extern void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex); From 388d5b438e22cddc6740e362763c0922edbb242a Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 9 Oct 2019 11:34:46 +0200 Subject: [PATCH 11/21] zebra: add ns_id attribute to mac structure this information is necessary for local information, because the interface associated to the mac address is stored with its ifindex, and the ifindex may not be enough to get to the right interface when it comes with multiple network namespaces. Signed-off-by: Philippe Guibert --- zebra/zebra_vxlan.c | 21 ++++++++++++++++++--- zebra/zebra_vxlan_private.h | 1 + 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 126e2c2bc3..a3f70d585c 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -1194,7 +1194,7 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) ifindex_t ifindex; ifindex = mac->fwd_info.local.ifindex; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); ifp = if_lookup_by_index_per_ns(zns, ifindex); if (!ifp) return; @@ -1276,7 +1276,7 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) ifindex_t ifindex; ifindex = mac->fwd_info.local.ifindex; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); ifp = if_lookup_by_index_per_ns(zns, ifindex); if (!ifp) return; @@ -1368,7 +1368,7 @@ static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt) if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) return; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); ifindex = mac->fwd_info.local.ifindex; ifp = if_lookup_by_index_per_ns(zns, ifindex); if (!ifp) // unexpected @@ -2731,7 +2731,12 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, zebra_mac_t *mac = NULL; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_vrf *zvrf; + ns_id_t local_ns_id = NS_DEFAULT; + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; zif = zvni->vxlan_if->info; if (!zif) return -1; @@ -2756,6 +2761,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vxl->access_vlan; n = zvni_neigh_lookup(zvni, ip); @@ -8127,6 +8133,11 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, bool upd_neigh = false; bool is_dup_detect = false; struct in_addr vtep_ip = {.s_addr = 0}; + ns_id_t local_ns_id = NS_DEFAULT; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. @@ -8179,6 +8190,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, } SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; if (sticky) SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); @@ -8203,6 +8215,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, */ if (mac_sticky == sticky && mac->fwd_info.local.ifindex == ifp->ifindex + && mac->fwd_info.local.ns_id == local_ns_id && mac->fwd_info.local.vid == vid) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( @@ -8227,6 +8240,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) || @@ -8264,6 +8278,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; if (sticky) SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 0a46fb2075..d2b02df2ad 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -310,6 +310,7 @@ struct zebra_mac_t_ { union { struct { ifindex_t ifindex; + ns_id_t ns_id; vlanid_t vid; } local; From f8ed2c5420106314a940cb67264494e0110fc4c0 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 27 Sep 2019 15:46:22 +0200 Subject: [PATCH 12/21] zebra: fdb and neighbor table are read for all zns all network namespaces are read so as to collect interesting fdb and neighbor tables for EVPN. Signed-off-by: Philippe Guibert --- zebra/zebra_vxlan.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index a3f70d585c..9447048706 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -9744,6 +9744,21 @@ stream_failure: return; } +static int macfdb_read_zns(struct zebra_ns *zns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + macfdb_read(zns); + return ZNS_WALK_CONTINUE; +} + +static int neigh_read_zns(struct zebra_ns *zns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + neigh_read(zns); + return ZNS_WALK_CONTINUE; +} /* * Handle message from client to learn (or stop learning) about VNIs and MACs. @@ -9793,10 +9808,10 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) NULL); /* Read the MAC FDB */ - macfdb_read(zvrf->zns); + zebra_ns_list_walk(macfdb_read_zns, NULL, NULL); /* Read neighbors */ - neigh_read(zvrf->zns); + zebra_ns_list_walk(neigh_read_zns, NULL, NULL); } else { /* Cleanup VTEPs for all VNIs - uninstall from * kernel and free entries. From db81d18647bbd81a2c335620c9a03e32e4a5b2be Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 9 Oct 2019 13:59:37 +0200 Subject: [PATCH 13/21] zebra: zvni_map_to_vlan() adaptation for all namespaces this change is needed when a MAC/IP entry is learned by zebra, and the entry happens to be in a different namespace. So that the entry be active, the correct vni match has to be found. Signed-off-by: Philippe Guibert --- zebra/zebra_vxlan.c | 76 +++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 9447048706..0703c028e8 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3528,32 +3528,37 @@ static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr) 0 /* seq */, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_DEL); } -/* - * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC - * notifications, to see if they are of interest. - */ -static zebra_vni_t *zvni_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid) +struct zvni_from_svi_param { + struct interface *br_if; + struct zebra_if *zif; + uint8_t bridge_vlan_aware; + vlanid_t vid; +}; + +static int zvni_map_vlan_zns(struct zebra_ns *zns, + void *_in_param, + void **_p_zvni) { - struct zebra_ns *zns; struct route_node *rn; + struct interface *br_if; + zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni; + zebra_vni_t *zvni; struct interface *tmp_if = NULL; struct zebra_if *zif; - struct zebra_l2info_bridge *br; struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - zebra_vni_t *zvni; + struct zvni_from_svi_param *in_param = + (struct zvni_from_svi_param *)_in_param; int found = 0; - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; + if (!in_param) + return ZNS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; assert(zif); - br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; + assert(br_if); /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; if (!tmp_if) @@ -3568,25 +3573,48 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp, if (zif->brslave_info.br_if != br_if) continue; - if (!bridge_vlan_aware || vxl->access_vlan == vid) { + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == in_param->vid) { found = 1; break; } } - if (!found) - return NULL; + return ZNS_WALK_CONTINUE; zvni = zvni_lookup(vxl->vni); - return zvni; + if (p_zvni) + *p_zvni = zvni; + return ZNS_WALK_STOP; } -struct zvni_from_svi_param { - struct interface *br_if; +/* + * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC + * notifications, to see if they are of interest. + */ +static zebra_vni_t *zvni_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid) +{ struct zebra_if *zif; - uint8_t bridge_vlan_aware; - vlanid_t vid; -}; + struct zebra_l2info_bridge *br; + zebra_vni_t **p_zvni; + zebra_vni_t *zvni = NULL; + struct zvni_from_svi_param in_param; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = zif; + p_zvni = &zvni; + + zebra_ns_list_walk(zvni_map_vlan_zns, (void *)&in_param, + (void **)p_zvni); + return zvni; +} static int zvni_from_svi_zns(struct zebra_ns *zns, void *_in_param, From a2342a241253c41b798845cae155b4caab4bcda5 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 11 Oct 2019 14:11:13 +0200 Subject: [PATCH 14/21] bgpd: evpn nexthop can be changed by default There can be cases where evpn traffic is not meshed across various endpoints, but sent to a central pe. For this situation, remove the nexthop unchanged default behaviour for bgp evpn. Also add route reflector commands to bgp evpn node. Signed-off-by: Philippe Guibert --- bgpd/bgp_vty.c | 4 ++++ bgpd/bgpd.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9890a3f071..51a9684235 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -16234,6 +16234,10 @@ void bgp_vty_init(void) &no_neighbor_route_reflector_client_cmd); install_element(BGP_EVPN_NODE, &neighbor_route_reflector_client_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_route_reflector_client_cmd); + install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_cmd); + install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_cmd); + install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_force_cmd); + install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_force_cmd); /* "neighbor route-server" commands.*/ install_element(BGP_NODE, &neighbor_route_server_client_hidden_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index cf6335d373..faee7dad4a 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1227,10 +1227,6 @@ struct peer *peer_new(struct bgp *bgp) peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; } - /* set nexthop-unchanged for l2vpn evpn by default */ - SET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN], - PEER_FLAG_NEXTHOP_UNCHANGED); - SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); /* Initialize per peer bgp GR FSM */ From 3acc394bc5e5c225e9258fd0d57a6cebea0c0ccd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 25 Oct 2019 14:25:00 +0200 Subject: [PATCH 15/21] zebra: when parsing local entry against dad, retrieve config when duplicate address detection is observed, some incrementation, some timing mechanisms need to be done. For that the main evpn configuration is retrieved. Until now, the VRF that was storing the dad config parameters was the same VRF that hosted the VXLAN interface. With netns backend, this is not true, as the VXLAN interface is in the same VRF as the bridge interface. The modification takes same definition as in BGP, that is to say that there is a single bgp evpn instance, and this is that instance that will give the correct config settings. Signed-off-by: Philippe Guibert --- zebra/zebra_vxlan.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 0703c028e8..6a4c01bd5b 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -5680,7 +5680,7 @@ static void process_remote_macip_add(vni_t vni, return; } - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) return; @@ -8189,11 +8189,10 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, return -1; } - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) { if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" No Vrf found for vrf_id: %d", - zvni->vxlan_if->vrf_id); + zlog_debug(" No Evpn Global Vrf found"); return -1; } From 40424548910887f3bbbf544ce964d3b736048ae5 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 28 Oct 2019 12:02:17 +0100 Subject: [PATCH 16/21] zebra: local mac entries populated in correct netnamespace MAC address neighbours are read on the same netnamespace where the vxlan interface is. Signed-off-by: Philippe Guibert --- zebra/zebra_vxlan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 6a4c01bd5b..4d2dc85b76 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3987,6 +3987,7 @@ static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac) static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp) { struct zebra_ns *zns; + struct zebra_vrf *zvrf; struct zebra_if *zif; struct interface *vlan_if; struct zebra_l2info_vxlan *vxl; @@ -3994,7 +3995,10 @@ static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp) zif = ifp->info; vxl = &zif->l2info.vxl; - zns = zebra_ns_lookup(NS_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(zvni->vrf_id); + if (!zvrf || !zvrf->zns) + return; + zns = zvrf->zns; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( From c4d466c830083e8ba58881d7ad03a90f6baf0754 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 20 Dec 2019 17:51:37 +0100 Subject: [PATCH 17/21] lib, zebra: reuse and adapt ns_list walk functionality the walk routine is used by vxlan service to identify some contexts in each specific network namespace, when vrf netns backend is used. that walk mechanism is extended with some additional paramters to the walk routine. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 16 ++++++-- lib/netns_other.c | 2 +- lib/ns.h | 11 +++-- zebra/main.c | 4 +- zebra/zebra_ns.c | 35 +++++----------- zebra/zebra_ns.h | 17 +++----- zebra/zebra_vxlan.c | 98 +++++++++++++++++++++++++-------------------- 7 files changed, 93 insertions(+), 90 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 82e0c4d8e0..e1c0159fc5 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -51,7 +51,7 @@ static struct ns *ns_lookup_name_internal(const char *name); RB_GENERATE(ns_head, ns, entry, ns_compare) -struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); static struct ns *default_ns; static int ns_current_ns_fd; @@ -379,12 +379,20 @@ struct ns *ns_lookup(ns_id_t ns_id) return ns_lookup_internal(ns_id); } -void ns_walk_func(int (*func)(struct ns *)) +void ns_walk_func(int (*func)(struct ns *, + void *param_in, + void **param_out), + void *param_in, + void **param_out) { struct ns *ns = NULL; + int ret; - RB_FOREACH (ns, ns_head, &ns_tree) - func(ns); + RB_FOREACH (ns, ns_head, &ns_tree) { + ret = func(ns, param_in, param_out); + if (ret == NS_WALK_STOP) + return; + } } const char *ns_get_name(struct ns *ns) diff --git a/lib/netns_other.c b/lib/netns_other.c index b0aae4f8df..740d2b621e 100644 --- a/lib/netns_other.c +++ b/lib/netns_other.c @@ -34,7 +34,7 @@ static inline int ns_compare(const struct ns *ns, const struct ns *ns2); RB_GENERATE(ns_head, ns, entry, ns_compare) -struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); static inline int ns_compare(const struct ns *a, const struct ns *b) { diff --git a/lib/ns.h b/lib/ns.h index 0dfd977429..286ff5b295 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -76,8 +76,6 @@ struct ns { RB_HEAD(ns_head, ns); RB_PROTOTYPE(ns_head, ns, entry, ns_compare) -extern struct ns_head ns_tree; - /* * API for managing NETNS. eg from zebra daemon * one want to manage the list of NETNS, etc... @@ -127,7 +125,14 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id); extern char *ns_netns_pathname(struct vty *vty, const char *name); /* Parse and execute a function on all the NETNS */ -extern void ns_walk_func(int (*func)(struct ns *)); +#define NS_WALK_CONTINUE 0 +#define NS_WALK_STOP 1 + +extern void ns_walk_func(int (*func)(struct ns *, + void *, + void **), + void *param_in, + void **param_out); /* API to get the NETNS name, from the ns pointer */ extern const char *ns_get_name(struct ns *ns); diff --git a/zebra/main.c b/zebra/main.c index e230a744f6..f447e9aa07 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -177,7 +177,7 @@ static void sigint(void) vrf_terminate(); rtadv_terminate(); - ns_walk_func(zebra_ns_early_shutdown); + ns_walk_func(zebra_ns_early_shutdown, NULL, NULL); zebra_ns_notify_close(); access_list_reset(); @@ -208,7 +208,7 @@ int zebra_finalize(struct thread *dummy) zlog_info("Zebra final shutdown"); /* Final shutdown of ns resources */ - ns_walk_func(zebra_ns_final_shutdown); + ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); /* Stop dplane thread and finish any cleanup */ zebra_dplane_shutdown(); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 13864cd429..6462daf687 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -153,20 +153,25 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) /* During zebra shutdown, do partial cleanup while the async dataplane * is still running. */ -int zebra_ns_early_shutdown(struct ns *ns) +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; if (zns == NULL) return 0; - return zebra_ns_disable_internal(zns, false); + zebra_ns_disable_internal(zns, false); + return NS_WALK_CONTINUE; } /* During zebra shutdown, do final cleanup * after all dataplane work is complete. */ -int zebra_ns_final_shutdown(struct ns *ns) +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; @@ -175,7 +180,7 @@ int zebra_ns_final_shutdown(struct ns *ns) kernel_terminate(zns, true); - return 0; + return NS_WALK_CONTINUE; } int zebra_ns_init(const char *optional_default_name) @@ -233,25 +238,3 @@ int zebra_ns_config_write(struct vty *vty, struct ns *ns) vty_out(vty, " netns %s\n", ns->name); return 0; } - -void zebra_ns_list_walk(int (*exec_for_each_zns)(struct zebra_ns *zns, - void *param_in, - void **param_out), - void *param_in, - void **param_out) -{ - struct ns *ns; - struct zebra_ns *zns; - int ret; - - RB_FOREACH (ns, ns_head, &ns_tree) { - zns = (struct zebra_ns *)ns->info; - if (!zns && ns->ns_id == NS_DEFAULT) - zns = zebra_ns_lookup(ns->ns_id); - if (!zns) - continue; - ret = exec_for_each_zns(zns, param_in, param_out); - if (ret == ZNS_WALK_STOP) - return; - } -} diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index fa2fd47c25..f7d1f40782 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -67,19 +67,14 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(const char *optional_default_name); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disabled(struct ns *ns); -int zebra_ns_early_shutdown(struct ns *ns); -int zebra_ns_final_shutdown(struct ns *ns); - +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); int zebra_ns_config_write(struct vty *vty, struct ns *ns); -#define ZNS_WALK_CONTINUE 0 -#define ZNS_WALK_STOP 1 -void zebra_ns_list_walk(int (*exec_for_each_zns)(struct zebra_ns *zns, - void *param_in, - void **param_out), - void *param_in, - void **param_out); - #ifdef __cplusplus } #endif diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 4d2dc85b76..244b398e47 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3535,10 +3535,11 @@ struct zvni_from_svi_param { vlanid_t vid; }; -static int zvni_map_vlan_zns(struct zebra_ns *zns, - void *_in_param, - void **_p_zvni) +static int zvni_map_vlan_ns(struct ns *ns, + void *_in_param, + void **_p_zvni) { + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *br_if; zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni; @@ -3551,7 +3552,7 @@ static int zvni_map_vlan_zns(struct zebra_ns *zns, int found = 0; if (!in_param) - return ZNS_WALK_STOP; + return NS_WALK_STOP; br_if = in_param->br_if; zif = in_param->zif; assert(zif); @@ -3580,12 +3581,12 @@ static int zvni_map_vlan_zns(struct zebra_ns *zns, } } if (!found) - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; zvni = zvni_lookup(vxl->vni); if (p_zvni) *p_zvni = zvni; - return ZNS_WALK_STOP; + return NS_WALK_STOP; } /* @@ -3611,15 +3612,17 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp, in_param.zif = zif; p_zvni = &zvni; - zebra_ns_list_walk(zvni_map_vlan_zns, (void *)&in_param, - (void **)p_zvni); + ns_walk_func(zvni_map_vlan_ns, + (void *)&in_param, + (void **)p_zvni); return zvni; } -static int zvni_from_svi_zns(struct zebra_ns *zns, - void *_in_param, - void **_p_zvni) +static int zvni_from_svi_ns(struct ns *ns, + void *_in_param, + void **_p_zvni) { + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *br_if; zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni; @@ -3632,7 +3635,7 @@ static int zvni_from_svi_zns(struct zebra_ns *zns, int found = 0; if (!in_param) - return ZNS_WALK_STOP; + return NS_WALK_STOP; br_if = in_param->br_if; zif = in_param->zif; assert(zif); @@ -3660,12 +3663,12 @@ static int zvni_from_svi_zns(struct zebra_ns *zns, } if (!found) - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; zvni = zvni_lookup(vxl->vni); if (p_zvni) *p_zvni = zvni; - return ZNS_WALK_STOP; + return NS_WALK_STOP; } /* @@ -3711,15 +3714,17 @@ static zebra_vni_t *zvni_from_svi(struct interface *ifp, in_param.zif = zif; p_zvni = &zvni; /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - zebra_ns_list_walk(zvni_from_svi_zns, (void *)&in_param, - (void **)p_zvni); + ns_walk_func(zvni_from_svi_ns, + (void *)&in_param, + (void **)p_zvni); return zvni; } -static int zvni_map_to_svi_zns(struct zebra_ns *zns, - void *_in_param, - void **_p_ifp) +static int zvni_map_to_svi_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) { + struct zebra_ns *zns = ns->info; struct route_node *rn; struct zvni_from_svi_param *in_param = (struct zvni_from_svi_param *)_in_param; @@ -3729,7 +3734,7 @@ static int zvni_map_to_svi_zns(struct zebra_ns *zns, struct zebra_if *zif; if (!in_param) - return ZNS_WALK_STOP; + return NS_WALK_STOP; /* TODO: Optimize with a hash. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { @@ -3746,10 +3751,10 @@ static int zvni_map_to_svi_zns(struct zebra_ns *zns, if (vl->vid == in_param->vid) { if (p_ifp) *p_ifp = tmp_if; - return ZNS_WALK_STOP; + return NS_WALK_STOP; } } - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; } /* Map to SVI on bridge corresponding to specified VLAN. This can be one @@ -3784,8 +3789,9 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) in_param.zif = NULL; p_ifp = &tmp_if; /* Identify corresponding VLAN interface. */ - zebra_ns_list_walk(zvni_map_to_svi_zns, (void *)&in_param, - (void **)p_ifp); + ns_walk_func(zvni_map_to_svi_ns, + (void *)&in_param, + (void **)p_ifp); return tmp_if; } @@ -4206,10 +4212,11 @@ static int zvni_send_del_to_client(vni_t vni) return zserv_send_message(client, s); } -static int zvni_build_hash_table_zns(struct zebra_ns *zns, +static int zvni_build_hash_table_ns(struct ns *ns, void *param_in __attribute__((unused)), void **param_out __attribute__((unused))) { + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *ifp; struct zebra_vrf *zvrf; @@ -4217,7 +4224,7 @@ static int zvni_build_hash_table_zns(struct zebra_ns *zns, zvrf = zebra_vrf_get_evpn(); if (!zvrf) - return ZNS_WALK_STOP; + return NS_WALK_STOP; /* Walk VxLAN interfaces and create VNI hash. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { @@ -4313,7 +4320,7 @@ static int zvni_build_hash_table_zns(struct zebra_ns *zns, zlog_debug( "Failed to add VNI hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; } if (zvni->local_vtep_ip.s_addr != @@ -4350,7 +4357,7 @@ static int zvni_build_hash_table_zns(struct zebra_ns *zns, } } } - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; } /* @@ -4360,9 +4367,9 @@ static int zvni_build_hash_table_zns(struct zebra_ns *zns, static void zvni_build_hash_table(void) { - zebra_ns_list_walk(zvni_build_hash_table_zns, - (void *)NULL, - (void **)NULL); + ns_walk_func(zvni_build_hash_table_ns, + (void *)NULL, + (void **)NULL); } /* @@ -5147,10 +5154,11 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni) return 0; } -static int zl3vni_map_to_vxlan_if_zns(struct zebra_ns *zns, +static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, void *_zl3vni, void **_pifp) { + struct zebra_ns *zns = ns->info; zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)_zl3vni; struct route_node *rn = NULL; struct interface *ifp = NULL; @@ -5159,7 +5167,7 @@ static int zl3vni_map_to_vxlan_if_zns(struct zebra_ns *zns, zvrf = zebra_vrf_get_evpn(); if (!zvrf) - return ZNS_WALK_STOP; + return NS_WALK_STOP; /* loop through all vxlan-interface */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { @@ -5193,10 +5201,10 @@ static int zl3vni_map_to_vxlan_if_zns(struct zebra_ns *zns, zl3vni->local_vtep_ip = vxl->vtep_ip; if (_pifp) *_pifp = (void *)ifp; - return ZNS_WALK_STOP; + return NS_WALK_STOP; } - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; } struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) @@ -5206,8 +5214,8 @@ struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) p_ifp = &ifp; - zebra_ns_list_walk(zl3vni_map_to_vxlan_if_zns, - (void *)zl3vni, (void **)p_ifp); + ns_walk_func(zl3vni_map_to_vxlan_if_ns, + (void *)zl3vni, (void **)p_ifp); return ifp; } @@ -9775,20 +9783,24 @@ stream_failure: return; } -static int macfdb_read_zns(struct zebra_ns *zns, +static int macfdb_read_ns(struct ns *ns, void *_in_param __attribute__((unused)), void **out_param __attribute__((unused))) { + struct zebra_ns *zns = ns->info; + macfdb_read(zns); - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; } -static int neigh_read_zns(struct zebra_ns *zns, +static int neigh_read_ns(struct ns *ns, void *_in_param __attribute__((unused)), void **out_param __attribute__((unused))) { + struct zebra_ns *zns = ns->info; + neigh_read(zns); - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; } /* @@ -9839,10 +9851,10 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) NULL); /* Read the MAC FDB */ - zebra_ns_list_walk(macfdb_read_zns, NULL, NULL); + ns_walk_func(macfdb_read_ns, NULL, NULL); /* Read neighbors */ - zebra_ns_list_walk(neigh_read_zns, NULL, NULL); + ns_walk_func(neigh_read_ns, NULL, NULL); } else { /* Cleanup VTEPs for all VNIs - uninstall from * kernel and free entries. From ee9633ed87f0ff5da1373a42d6c044f0a682c8d3 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 3 Jan 2020 19:03:18 +0100 Subject: [PATCH 18/21] bgpd: sanity check when updating nexthop from bgp to zebra nexthop may sometimes not be passed through zebra, like for onlink messages. Signed-off-by: Philippe Guibert --- bgpd/bgp_zebra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index cca3f4aaa3..b7c255f16f 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1143,7 +1143,8 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->ifindex = 0; } } - api_nh->gate.ipv6 = *nexthop; + if (nexthop) + api_nh->gate.ipv6 = *nexthop; return true; } From de0ebb25404fe984f084a0d57b7f873618423876 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 6 Jan 2020 17:39:17 +0100 Subject: [PATCH 19/21] zebra: dynamically detect vxlan link interfaces in other netns this is used when parsing the newly network namespaces. actually, to track the link of some interfaces like vxlan interfaces, both link index and link nsid are necessary. if a vxlan interface is moved to a new netns, the link information is in the default network namespace, then LINK_NSID is the value of the netns by default in the new netns. That value of the default netns in the new netns is not known, because the system does not automatically assign an NSID of default network namespace in the new netns. Now a new NSID of default netns, seen from that new netns, is created. This permits to store at netns creation the default netns relative value for further usage. Because the default netns value is set from the new netns perspective, it is not needed anymore to use the NETNSA_TARGET_NSID attribute only available in recent kernels. Signed-off-by: Philippe Guibert --- zebra/zebra_netns_id.c | 131 ++++++++----------------------------- zebra/zebra_netns_id.h | 4 +- zebra/zebra_netns_notify.c | 21 +++--- 3 files changed, 39 insertions(+), 117 deletions(-) diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index eca0961b99..0d86421b9e 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -159,27 +159,34 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) return ns_id; } -ns_id_t zebra_ns_id_get(const char *netnspath) +/* fd_param = -1 is ignored. + * netnspath set to null is ignored. + * one of the 2 params is mandatory. netnspath is looked in priority + */ +ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) { int ns_id = -1; struct sockaddr_nl snl; - int fd, sock, ret; + int fd = -1, sock, ret; unsigned int seq; ns_id_t return_nsid = NS_UNKNOWN; /* netns path check */ - if (!netnspath) + if (!netnspath && fd_param == -1) return NS_UNKNOWN; - fd = open(netnspath, O_RDONLY); - if (fd == -1) - return NS_UNKNOWN; - + if (netnspath) { + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + } else if (fd_param != -1) + fd = fd_param; /* netlink socket */ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", sock, safe_strerror(errno)); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } memset(&snl, 0, sizeof(snl)); @@ -192,7 +199,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) socket() bind error: %s", sock, safe_strerror(errno)); close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } @@ -214,7 +222,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -258,7 +267,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) recvfrom() error 2 when reading: %s", fd, safe_strerror(errno)); close(sock); - close(fd); + if (fd_param == -1) + close(fd); if (errno == ENOTSUP) { zlog_debug("NEWNSID locally generated"); return zebra_ns_id_get_fallback(netnspath); @@ -278,7 +288,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (fd_param == -1) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -309,106 +320,18 @@ ns_id_t zebra_ns_id_get(const char *netnspath) } while (len != 0 && ret == 0); } - close(fd); + if (fd_param == -1) + close(fd); close(sock); return return_nsid; } -/* if nsid is not default one, get relative default ns for the new ns - * for instance, if default ns is 0 and a new ns "vrf1" id is 1, - * in ns "vrf1", the default ns is not known. using GETNSID with - * two parameters: NETNS_NSID, and NETNS_TARGET_NSID will help - * in identifying that value - */ -ns_id_t zebra_ns_id_get_relative_value(ns_id_t ns_read, ns_id_t target_nsid) -{ - struct sockaddr_nl snl; - int sock, ret; - unsigned int seq; - ns_id_t return_nsid = NS_UNKNOWN; - char buf[NETLINK_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; - struct rtgenmsg *rt; - int len; - - if (ns_read == NS_UNKNOWN || target_nsid == NS_UNKNOWN) - return return_nsid; - - /* netlink socket */ - sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (sock < 0) { - flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", - sock, safe_strerror(errno)); - return NS_UNKNOWN; - } - memset(&snl, 0, sizeof(snl)); - snl.nl_family = AF_NETLINK; - snl.nl_groups = RTNLGRP_NSID; - snl.nl_pid = 0; /* AUTO PID */ - ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); - if (ret < 0) { - flog_err_sys(EC_LIB_SOCKET, - "netlink( %u) socket() bind error: %s", sock, - safe_strerror(errno)); - close(sock); - return NS_UNKNOWN; - } - - /* message to send to netlink : GETNSID */ - memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); - nlh = initiate_nlh(buf, &seq, RTM_GETNSID); - rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); - nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); - rt->rtgen_family = AF_UNSPEC; - - addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_read); - addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_TARGET_NSID, target_nsid); - - ret = send_receive(sock, nlh, seq, buf); - if (ret < 0) { - close(sock); - return NS_UNKNOWN; - } - nlh = (struct nlmsghdr *)buf; - len = ret; - ret = 0; - do { - if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { - return_nsid = extract_nsid(nlh, buf); - if (return_nsid != NS_UNKNOWN) - break; - } else if (nlh->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = - (struct nlmsgerr - *)((char *)nlh - + NETLINK_ALIGN(sizeof( - struct - nlmsghdr))); - if (err->error < 0) - errno = -err->error; - else - errno = err->error; - break; - } - len = len - NETLINK_ALIGN(nlh->nlmsg_len); - nlh = (struct nlmsghdr *)((char *)nlh - + NETLINK_ALIGN( - nlh->nlmsg_len)); - } while (len != 0 && ret == 0); - - return return_nsid; -} - #else -ns_id_t zebra_ns_id_get(const char *netnspath) +ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused))) { return zebra_ns_id_get_fallback(netnspath); } -ns_id_t zebra_ns_id_get_relative_value(ns_id_t ns_read, ns_id_t target_nsid) -{ - return NS_UNKNOWN; -} #endif /* ! defined(HAVE_NETLINK) */ #ifdef HAVE_NETNS @@ -444,7 +367,7 @@ ns_id_t zebra_ns_id_get_default(void) return NS_DEFAULT_INTERNAL; } close(fd); - return zebra_ns_id_get((char *)NS_DEFAULT_NAME); + return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1); #else /* HAVE_NETNS */ return NS_DEFAULT_INTERNAL; #endif /* !HAVE_NETNS */ diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index 215d627b7b..dd9eab18e0 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -24,10 +24,8 @@ extern "C" { #endif -extern ns_id_t zebra_ns_id_get(const char *netnspath); +extern ns_id_t zebra_ns_id_get(const char *netnspath, int fd); extern ns_id_t zebra_ns_id_get_default(void); -extern ns_id_t zebra_ns_id_get_relative_value(ns_id_t ns_read, - ns_id_t target_nsid); #ifdef __cplusplus } diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 0e2c1684d5..72e4fd0055 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -79,19 +79,10 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) return; frr_with_privs(&zserv_privs) { - ns_id = zebra_ns_id_get(netnspath); + ns_id = zebra_ns_id_get(netnspath, -1); } if (ns_id == NS_UNKNOWN) return; - default_ns = ns_get_default(); - if (default_ns && default_ns->internal_ns_id != ns_id) { - frr_with_privs(&zserv_privs) { - ns_id_relative = - zebra_ns_id_get_relative_value( - default_ns->internal_ns_id, - ns_id); - } - } ns_id_external = ns_map_nsid_with_external(ns_id, true); /* if VRF with NS ID already present */ vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external); @@ -107,6 +98,16 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) ns_map_nsid_with_external(ns_id, false); return; } + + default_ns = ns_get_default(); + + /* force kernel ns_id creation in that new vrf */ + frr_with_privs(&zserv_privs) { + ns_switch_to_netns(netnspath); + ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd); + ns_switchback_to_initial(); + } + frr_with_privs(&zserv_privs) { ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id_external, From 89b97c33d7a6d9dc427d56fea52fa27334dde81d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 6 Feb 2020 09:32:21 +0100 Subject: [PATCH 20/21] doc: add some documentation about bgp evpn netns support a linux configuration gives some explanation on how to set up an evpn overlay in network namespaces. Signed-off-by: Philippe Guibert --- doc/user/bgp.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 84be461cce..7c86cac11c 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2391,6 +2391,27 @@ the same behavior of using same next-hop and RMAC values. Enables or disables advertise-pip feature, specifiy system-IP and/or system-MAC parameters. +Support with VRF network namespace backend +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It is possible to separate overlay networks contained in VXLAN interfaces from +underlay networks by using VRFs. VRF-lite and VRF-netns backends can be used for +that. In the latter case, this is necessary to set both bridge and vxlan interface +on the same network namespace, as below example illustrates: + +.. code-block:: shell + + # linux shell + ip netns add vrf1 + ip link add name vxlan101 type vxlan id 101 dstport 4789 dev eth0 local 10.1.1.1 + ip link set dev vxlan101 netns vrf1 + ip netns exec vrf1 ip link set dev lo up + ip netns exec vrf1 brctl addbr bridge101 + ip netns exec vrf1 brctl addif bridge101 vxlan101 + +This makes possible to separate not only layer 3 networks like VRF-lite networks. +Also, VRF netns based make possible to separate layer 2 networks on separate VRF +instances. + .. _bgp-cisco-compatibility: Cisco Compatibility From bf69e212fd053af3298fc3cba38458b396467849 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 6 Feb 2020 09:33:21 +0100 Subject: [PATCH 21/21] zebra: support for macvlan interfaces mac vlan interfaces are supported in different network namespaces. Signed-off-by: Philippe Guibert --- zebra/zebra_vxlan.c | 71 +++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 244b398e47..998c035656 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3530,6 +3530,7 @@ static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr) struct zvni_from_svi_param { struct interface *br_if; + struct interface *svi_if; struct zebra_if *zif; uint8_t bridge_vlan_aware; vlanid_t vid; @@ -3795,16 +3796,51 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) return tmp_if; } +static int zvni_map_to_macvlan_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct zvni_from_svi_param *in_param = + (struct zvni_from_svi_param *)_in_param; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + + if (!in_param) + return NS_WALK_STOP; + + /* Identify corresponding VLAN interface. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + continue; + zif = tmp_if->info; + + if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) + continue; + + if (zif->link == in_param->svi_if) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; + } + } + + return NS_WALK_CONTINUE; +} + /* Map to MAC-VLAN interface corresponding to specified SVI interface. */ static struct interface *zvni_map_to_macvlan(struct interface *br_if, struct interface *svi_if) { - struct zebra_ns *zns; - struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; - int found = 0; + struct interface **p_ifp; + struct zvni_from_svi_param in_param; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) @@ -3819,28 +3855,19 @@ static struct interface *zvni_map_to_macvlan(struct interface *br_if, zif = br_if->info; assert(zif); + in_param.vid = 0; + in_param.br_if = br_if; + in_param.zif = NULL; + in_param.svi_if = svi_if; + p_ifp = &tmp_if; + /* Identify corresponding VLAN interface. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - - if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) - continue; - - if (zif->link == svi_if) { - found = 1; - break; - } - } - - return found ? tmp_if : NULL; + ns_walk_func(zvni_map_to_macvlan_ns, + (void *)&in_param, + (void **)p_ifp); + return tmp_if; } - /* * Install remote MAC into the forwarding plane. */