From 07509878e37b7f6ce75e4168e450cee3b36f24e9 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 26 Sep 2019 18:49:59 +0200 Subject: [PATCH 01/24] 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 | 89 +++++++++++++++++++++++++++++++++++++-------- 6 files changed, 107 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 13a58bc34a..7079184a34 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -846,18 +846,21 @@ static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) return zebra_evpn_del(zevpn); } -/* - * Build the VNI hash table by going over the VxLAN interfaces. This - * is called when EVPN (advertise-all-vni) is enabled. - */ -static void zevpn_build_hash_table(void) + +static int zevpn_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 EVPN hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { vni_t vni; zebra_evpn_t *zevpn = NULL; @@ -874,7 +877,15 @@ static void zevpn_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) { @@ -943,7 +954,7 @@ static void zevpn_build_hash_table(void) zlog_debug( "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return; + return ZNS_WALK_CONTINUE; } if (zevpn->local_vtep_ip.s_addr != @@ -985,6 +996,19 @@ static void zevpn_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 zevpn_build_hash_table(void) +{ + zebra_ns_list_walk(zevpn_build_hash_table_zns, + (void *)NULL, + (void **)NULL); } /* @@ -1617,14 +1641,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; @@ -1639,13 +1670,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 9d277b8c5239963bc6037f014e6c5fda5fa1c646 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 27 Sep 2019 11:17:20 +0200 Subject: [PATCH 02/24] 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_evpn.c | 109 ++++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 73df93258e..b704c32c5c 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -675,51 +675,35 @@ zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, return zevpn; } -/* - * Map SVI and associated bridge to an EVPN. This is invoked upon getting - * neighbor notifications, to see if they are of interest. - */ -zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, - struct interface *br_if) +struct zevpn_from_svi_param { + struct interface *br_if; + struct zebra_if *zif; + uint8_t bridge_vlan_aware; + vlanid_t vid; +}; + +static int zebra_evpn_from_svi_zns(struct zebra_ns *zns, + void *_in_param, + void **_p_zevpn) { - struct zebra_ns *zns; struct route_node *rn; + struct interface *br_if; + zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; + zebra_evpn_t *zevpn; 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_evpn_t *zevpn; + struct zevpn_from_svi_param *in_param = + (struct zevpn_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) @@ -734,16 +718,67 @@ zebra_evpn_t *zebra_evpn_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; zevpn = zebra_evpn_lookup(vxl->vni); + if (p_zevpn) + *p_zevpn = zevpn; + return ZNS_WALK_STOP; +} + +/* + * Map SVI and associated bridge to an EVPN. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, + struct interface *br_if) +{ + struct zebra_l2info_bridge *br; + zebra_evpn_t *zevpn = NULL; + zebra_evpn_t **p_zevpn; + struct zebra_if *zif; + struct zevpn_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_zevpn = &zevpn; + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + zebra_ns_list_walk(zebra_evpn_from_svi_zns, (void *)&in_param, + (void **)p_zevpn); return zevpn; } From a237058f59d46af115dafa82c5ca1026418e0518 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 27 Sep 2019 15:45:42 +0200 Subject: [PATCH 03/24] 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_evpn.c | 13 ++------ zebra/zebra_evpn.h | 8 +++++ zebra/zebra_vxlan.c | 76 ++++++++++++++++++++++++++++----------------- 3 files changed, 58 insertions(+), 39 deletions(-) diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index b704c32c5c..eb9f99f2dd 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -675,13 +675,6 @@ zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, return zevpn; } -struct zevpn_from_svi_param { - struct interface *br_if; - struct zebra_if *zif; - uint8_t bridge_vlan_aware; - vlanid_t vid; -}; - static int zebra_evpn_from_svi_zns(struct zebra_ns *zns, void *_in_param, void **_p_zevpn) @@ -693,8 +686,8 @@ static int zebra_evpn_from_svi_zns(struct zebra_ns *zns, struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl = NULL; - struct zevpn_from_svi_param *in_param = - (struct zevpn_from_svi_param *)_in_param; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; int found = 0; if (!in_param) @@ -745,7 +738,7 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, zebra_evpn_t *zevpn = NULL; zebra_evpn_t **p_zevpn; struct zebra_if *zif; - struct zevpn_from_svi_param in_param; + struct zebra_from_svi_param in_param; if (!br_if) return NULL; diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index 3b6a5b21e8..7b08a3f485 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -123,6 +123,14 @@ struct zebra_evpn_t_ { struct list *local_es_evi_list; }; +/* for parsing evpn and vni contexts */ +struct zebra_from_svi_param { + struct interface *br_if; + struct zebra_if *zif; + uint8_t bridge_vlan_aware; + vlanid_t vid; +}; + struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); static inline struct interface *zevpn_map_to_svi(zebra_evpn_t *zevpn) diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 7079184a34..5a58794378 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -782,6 +782,42 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) vty_out(vty, "\n"); } +static int zvni_map_to_svi_ns(struct zebra_ns *zns, + void *_in_param, + void **_p_ifp) +{ + struct route_node *rn; + struct zebra_from_svi_param *in_param = + (struct zebra_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 @@ -791,15 +827,11 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) */ 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 zebra_from_svi_param in_param; + struct interface **p_ifp; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; @@ -808,33 +840,19 @@ 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_ns, (void *)&in_param, + (void **)p_ifp); + return tmp_if; } static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) From d0738ba2bbdf764ba484f17ecf08f25b11403409 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 27 Sep 2019 15:12:27 +0200 Subject: [PATCH 04/24] 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 b824e313ec..d29f61450e 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1242,6 +1242,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) { @@ -1469,6 +1486,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 9771da714e63ba050f13a3f1fa2cdee464e97da1 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 1 Oct 2019 11:07:13 +0200 Subject: [PATCH 05/24] 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 1758c8f96a..d8674beb41 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -84,12 +84,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; @@ -252,19 +254,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 2735d915ec..79d2e4feca 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 2d4e4d39b776c0a389712be6cbb0b28cf7aa33ad Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 2 Oct 2019 12:14:13 +0200 Subject: [PATCH 06/24] 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 8de4daf439..61d2ebe184 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -315,11 +315,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 ec7681bf23..db620cf220 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 20f4b2b0719e68e1f78d8e1a8f73d5f3c2de2418 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 2 Oct 2019 13:34:40 +0200 Subject: [PATCH 07/24] 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 2a3ce2a315..20e08b03d8 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -653,7 +653,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 */ @@ -795,7 +797,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 db620cf220..521053484c 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 b1cc23b2cdaf70b279dfe53aa8ded6e2e6d3b1fd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 2 Oct 2019 13:37:11 +0200 Subject: [PATCH 08/24] 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 | 20 ++++++++++++++++++++ lib/ns.h | 1 + zebra/if_netlink.c | 9 ++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index bb66e8938f..0109b3db6a 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -584,6 +584,26 @@ 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 5cd3e69299..c3eab60148 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -798,8 +798,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 @@ -1349,9 +1351,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 a6e0edf28046a091344fad654b3c37aa07d403c0 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 3 Oct 2019 17:11:37 +0200 Subject: [PATCH 09/24] 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 c3eab60148..90a08bbd6c 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -850,7 +850,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); @@ -1442,7 +1442,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) { @@ -1543,7 +1544,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 d8674beb41..417056ecb0 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -54,7 +54,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; @@ -73,7 +79,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) @@ -250,10 +257,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; @@ -264,9 +273,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 79d2e4feca..f3b15c7770 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 47c589295583bc2094429ee8df61ae8c97461b13 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 9 Oct 2019 11:34:46 +0200 Subject: [PATCH 10/24] 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_evpn_mac.c | 16 +++++++++++++++- zebra/zebra_evpn_mac.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index b9cc02a276..6fc141efbb 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -249,7 +249,7 @@ static void zebra_evpn_mac_get_access_info(zebra_mac_t *mac, struct zebra_ns *zns; *vid = mac->fwd_info.local.vid; - zns = zebra_ns_lookup(NS_DEFAULT); + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); *ifpP = if_lookup_by_index_per_ns(zns, mac->fwd_info.local.ifindex); } @@ -1610,6 +1610,12 @@ static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, { struct zebra_if *zif = ifp->info; bool es_change; + ns_id_t local_ns_id = NS_DEFAULT; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); @@ -1618,6 +1624,7 @@ static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, if (!mac->es) { /* if es is set fwd_info is not-relevant/taped-out */ mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; mac->fwd_info.local.vid = vid; } @@ -2204,6 +2211,12 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, { char buf[ETHER_ADDR_STRLEN]; zebra_mac_t *mac; + ns_id_t local_ns_id = NS_DEFAULT; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { @@ -2223,6 +2236,7 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, 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 = vlan_id; *macp = mac; diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index 39aaf1fb30..f9ca81445f 100644 --- a/zebra/zebra_evpn_mac.h +++ b/zebra/zebra_evpn_mac.h @@ -91,6 +91,7 @@ struct zebra_mac_t_ { union { struct { ifindex_t ifindex; + ns_id_t ns_id; vlanid_t vid; } local; From 6265fbe55cdfb63d40ad2f9393fe014927f34717 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 27 Sep 2019 15:46:22 +0200 Subject: [PATCH 11/24] 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 5a58794378..a0046e6031 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -5522,6 +5522,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. @@ -5574,10 +5589,10 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) zebra_evpn_gw_macip_add_for_evpn_hash, 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 EVPNs - uninstall from * kernel and free entries. From 9609fab712c41a7e92dd3f51a4089bf852cce5a7 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 9 Oct 2019 13:59:37 +0200 Subject: [PATCH 12/24] 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_evpn.c | 65 +++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index eb9f99f2dd..da4ba4f705 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -622,32 +622,30 @@ void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, return; } -/* - * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC - * notifications, to see if they are of interest. - */ -zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid) +static int zebra_evpn_map_vlan_zns(struct zebra_ns *zns, + void *_in_param, + void **_p_zevpn) { - struct zebra_ns *zns; struct route_node *rn; + struct interface *br_if; + zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; + zebra_evpn_t *zevpn; 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_evpn_t *zevpn; + struct zebra_from_svi_param *in_param = + (struct zebra_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) @@ -662,16 +660,47 @@ zebra_evpn_t *zebra_evpn_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; zevpn = zebra_evpn_lookup(vxl->vni); + if (p_zevpn) + *p_zevpn = zevpn; + return ZNS_WALK_STOP; +} + +/* + * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC + * notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid) +{ + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + zebra_evpn_t **p_zevpn; + zebra_evpn_t *zevpn = NULL; + struct zebra_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_zevpn = &zevpn; + + zebra_ns_list_walk(zebra_evpn_map_vlan_zns, + (void *)&in_param, + (void **)p_zevpn); return zevpn; } From 6179d0d1258b0119d2a8fd2aa7a5cf8ca54a754a Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 11 Oct 2019 14:11:13 +0200 Subject: [PATCH 13/24] 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, add the configuration knobs to force nexthop attribute. Upon that change, nexthop unchanged attribute is automatically disabled. Signed-off-by: Philippe Guibert --- bgpd/bgp_vty.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index b2a795b97d..d9c78b262b 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -16446,6 +16446,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); From d6bf8f136da0e1afce0ef6c9c7a232a30851742c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 25 Oct 2019 14:25:00 +0200 Subject: [PATCH 14/24] 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_evpn.c | 2 +- zebra/zebra_vxlan.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index da4ba4f705..d78ee57af3 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -1372,7 +1372,7 @@ void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, } } - zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + zvrf = zebra_vrf_get_evpn(); if (!zvrf) return; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index a0046e6031..6332854e73 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -4062,11 +4062,10 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, return -1; } - zvrf = vrf_info_lookup(zevpn->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", - zevpn->vxlan_if->vrf_id); + zlog_debug(" No Evpn Global Vrf found"); return -1; } From b5fde6fd5c41b99ff205b1139490db49acab1667 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 28 Oct 2019 12:02:17 +0100 Subject: [PATCH 15/24] 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_evpn.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index d78ee57af3..542f36156e 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -869,6 +869,7 @@ void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) { struct zebra_ns *zns; + struct zebra_vrf *zvrf; struct zebra_if *zif; struct interface *vlan_if; struct zebra_l2info_vxlan *vxl; @@ -876,7 +877,10 @@ void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) zif = ifp->info; vxl = &zif->l2info.vxl; - zns = zebra_ns_lookup(NS_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(zevpn->vrf_id); + if (!zvrf || !zvrf->zns) + return; + zns = zvrf->zns; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( From 2961d0601c7d168eed5bbe8e95f80a642f9cb4b5 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 20 Dec 2019 17:51:37 +0100 Subject: [PATCH 16/24] 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_evpn.c | 36 +++++++++++----------- zebra/zebra_ns.c | 35 ++++++---------------- zebra/zebra_ns.h | 17 ++++------- zebra/zebra_vxlan.c | 73 +++++++++++++++++++++++++-------------------- 8 files changed, 97 insertions(+), 97 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 0109b3db6a..e8d549b4e0 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 64746f7166..2b97e915fb 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -187,7 +187,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(); @@ -218,7 +218,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_evpn.c b/zebra/zebra_evpn.c index 542f36156e..0f5e77ac65 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -622,10 +622,11 @@ void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, return; } -static int zebra_evpn_map_vlan_zns(struct zebra_ns *zns, - void *_in_param, - void **_p_zevpn) +static int zebra_evpn_map_vlan_ns(struct ns *ns, + void *_in_param, + void **_p_zevpn) { + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *br_if; zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; @@ -638,7 +639,7 @@ static int zebra_evpn_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); @@ -667,12 +668,12 @@ static int zebra_evpn_map_vlan_zns(struct zebra_ns *zns, } } if (!found) - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; zevpn = zebra_evpn_lookup(vxl->vni); if (p_zevpn) *p_zevpn = zevpn; - return ZNS_WALK_STOP; + return NS_WALK_STOP; } /* @@ -698,16 +699,17 @@ zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, in_param.zif = zif; p_zevpn = &zevpn; - zebra_ns_list_walk(zebra_evpn_map_vlan_zns, - (void *)&in_param, - (void **)p_zevpn); + ns_walk_func(zebra_evpn_map_vlan_ns, + (void *)&in_param, + (void **)p_zevpn); return zevpn; } -static int zebra_evpn_from_svi_zns(struct zebra_ns *zns, - void *_in_param, - void **_p_zevpn) +static int zebra_evpn_from_svi_ns(struct ns *ns, + void *_in_param, + void **_p_zevpn) { + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *br_if; zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; @@ -720,7 +722,7 @@ static int zebra_evpn_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); @@ -748,12 +750,12 @@ static int zebra_evpn_from_svi_zns(struct zebra_ns *zns, } if (!found) - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; zevpn = zebra_evpn_lookup(vxl->vni); if (p_zevpn) *p_zevpn = zevpn; - return ZNS_WALK_STOP; + return NS_WALK_STOP; } /* @@ -799,8 +801,8 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, in_param.zif = zif; p_zevpn = &zevpn; /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - zebra_ns_list_walk(zebra_evpn_from_svi_zns, (void *)&in_param, - (void **)p_zevpn); + ns_walk_func(zebra_evpn_from_svi_ns, (void *)&in_param, + (void **)p_zevpn); return zevpn; } 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 6332854e73..61498973e9 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -782,10 +782,11 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) vty_out(vty, "\n"); } -static int zvni_map_to_svi_ns(struct zebra_ns *zns, +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 zebra_from_svi_param *in_param = (struct zebra_from_svi_param *)_in_param; @@ -795,7 +796,7 @@ static int zvni_map_to_svi_ns(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)) { @@ -812,10 +813,10 @@ static int zvni_map_to_svi_ns(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 @@ -850,8 +851,8 @@ 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_ns, (void *)&in_param, - (void **)p_ifp); + ns_walk_func(zvni_map_to_svi_ns, (void *)&in_param, + (void **)p_ifp); return tmp_if; } @@ -865,10 +866,11 @@ static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) return zebra_evpn_del(zevpn); } -static int zevpn_build_hash_table_zns(struct zebra_ns *zns, +static int zevpn_build_hash_table_zns(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; @@ -876,7 +878,7 @@ static int zevpn_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 EVPN hash. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { @@ -972,7 +974,7 @@ static int zevpn_build_hash_table_zns(struct zebra_ns *zns, zlog_debug( "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; } if (zevpn->local_vtep_ip.s_addr != @@ -1014,7 +1016,7 @@ static int zevpn_build_hash_table_zns(struct zebra_ns *zns, } } } - return ZNS_WALK_CONTINUE; + return NS_WALK_CONTINUE; } /* @@ -1024,9 +1026,9 @@ static int zevpn_build_hash_table_zns(struct zebra_ns *zns, static void zevpn_build_hash_table(void) { - zebra_ns_list_walk(zevpn_build_hash_table_zns, - (void *)NULL, - (void **)NULL); + ns_walk_func(zevpn_build_hash_table_zns, + (void *)NULL, + (void **)NULL); } /* @@ -1659,10 +1661,11 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni) return 0; } -static int zl3vni_map_to_vxlan_if_zns(struct zebra_ns *zns, - void *_zl3vni, - void **_pifp) +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; @@ -1671,7 +1674,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)) { @@ -1705,10 +1708,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) @@ -1718,8 +1721,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; } @@ -5521,20 +5524,24 @@ 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, +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 NS_WALK_CONTINUE; +} + +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; } /* @@ -5588,10 +5595,10 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) zebra_evpn_gw_macip_add_for_evpn_hash, 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 EVPNs - uninstall from * kernel and free entries. From c2ca3e25a625756eb33fc794de7b1504762cabc6 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 3 Jan 2020 19:03:18 +0100 Subject: [PATCH 17/24] 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 87936f1dd6..639500fafc 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1144,7 +1144,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 289b0f0dfd7112a2832812db3e5def10e82098e6 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 6 Jan 2020 17:39:17 +0100 Subject: [PATCH 18/24] 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 61d2ebe184..79121bb086 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); @@ -279,7 +289,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; @@ -310,106 +321,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 @@ -445,7 +368,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 521053484c..995fa6fb5a 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 ee9d0f0976cf43084facabfcbb7addedcd02de06 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Sat, 15 Aug 2020 07:54:26 -0700 Subject: [PATCH 19/24] [netns 19/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 | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 99dfee60eb..6dc931e608 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2475,6 +2475,26 @@ 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, it is necessary to set both bridge and vxlan interface +in 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 it 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-debugging: From a1ce03e1fd7280799b6433ae51f5af57a5cacf5e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 6 Feb 2020 09:33:21 +0100 Subject: [PATCH 20/24] zebra: support for macvlan interfaces mac vlan interfaces are supported in different network namespaces. Signed-off-by: Philippe Guibert --- zebra/zebra_evpn.c | 69 ++++++++++++++++++++++++++++++++-------------- zebra/zebra_evpn.h | 1 + 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 0f5e77ac65..80124f92b3 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -806,16 +806,51 @@ zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, return zevpn; } +static int zvni_map_to_macvlan_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct zebra_from_svi_param *in_param = + (struct zebra_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. */ struct interface *zebra_evpn_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 zebra_from_svi_param in_param; /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) @@ -830,25 +865,17 @@ struct interface *zebra_evpn_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; } /* diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h index 7b08a3f485..27392ec85c 100644 --- a/zebra/zebra_evpn.h +++ b/zebra/zebra_evpn.h @@ -126,6 +126,7 @@ struct zebra_evpn_t_ { /* for parsing evpn and vni contexts */ struct zebra_from_svi_param { struct interface *br_if; + struct interface *svi_if; struct zebra_if *zif; uint8_t bridge_vlan_aware; vlanid_t vid; From d4b10c1b7455457c9bb857937b2bebabef0aa329 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 3 Mar 2020 10:45:34 +0100 Subject: [PATCH 21/24] topotests: add bgp_evpn_rt5 test with vrf netns backend this test checks connectivity between a vrf-lite device and a vrf-netns device. this ensures that evpn serice is importing appropriate evpn rt5 entries in the correct vrf. Signed-off-by: Philippe Guibert --- tests/topotests/bgp_evpn_rt5/__init__.py | 0 tests/topotests/bgp_evpn_rt5/r1/bgpd.conf | 26 ++ tests/topotests/bgp_evpn_rt5/r1/zebra.conf | 22 ++ tests/topotests/bgp_evpn_rt5/r2/bgpd.conf | 27 +++ tests/topotests/bgp_evpn_rt5/r2/zebra.conf | 18 ++ tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py | 227 ++++++++++++++++++ 6 files changed, 320 insertions(+) create mode 100644 tests/topotests/bgp_evpn_rt5/__init__.py create mode 100644 tests/topotests/bgp_evpn_rt5/r1/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_rt5/r1/zebra.conf create mode 100644 tests/topotests/bgp_evpn_rt5/r2/bgpd.conf create mode 100644 tests/topotests/bgp_evpn_rt5/r2/zebra.conf create mode 100755 tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py diff --git a/tests/topotests/bgp_evpn_rt5/__init__.py b/tests/topotests/bgp_evpn_rt5/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf new file mode 100644 index 0000000000..9237682067 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf @@ -0,0 +1,26 @@ +debug bgp neighbor-events +debug bgp updates +debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.21 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.41 remote-as 65000 + neighbor 192.168.100.41 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.41 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r1-vrf-101 + bgp router-id 192.168.102.21 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.102.21/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r1/zebra.conf b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf new file mode 100644 index 0000000000..f5eaab1953 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf @@ -0,0 +1,22 @@ +log stdout + +hostname r1 +password zebra + +debug zebra vxlan +debug zebra kernel +debug zebra dplane +debug zebra rib +log stdout +vrf r1-vrf-101 + vni 101 + exit-vrf +! +interface r1-eth0 + ip address 192.168.100.21/24 +! +interface loop101 vrf r1-vrf-101 + ip address 192.168.102.21/32 +! + + diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf new file mode 100644 index 0000000000..6dcacd288d --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf @@ -0,0 +1,27 @@ +debug bgp neighbor-events +debug bgp updates +debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.41 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.21 peer-group + neighbor 192.168.100.21 remote-as 65000 + neighbor 192.168.100.21 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.21 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r2-vrf-101 + bgp router-id 192.168.101.41 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.101.41/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r2/zebra.conf b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf new file mode 100644 index 0000000000..e5f962d254 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf @@ -0,0 +1,18 @@ +log stdout + +hostname r2 +password zebra + +debug zebra vxlan + +vrf r2-vrf-101 + vni 101 + exit-vrf +! +interface loop101 vrf r2-vrf-101 + ip address 192.168.101.41/32 +! +interface r2-eth0 + ip address 192.168.100.41/24 +! + diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py new file mode 100755 index 0000000000..086dcd8cc6 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python + +# +# test_bgp_evpn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_evpn.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +l3mdev_accept = 0 + +class BGPEVPNTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + tgen.add_router('r1') + tgen.add_router('r2') + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r1']) + + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + global l3mdev_accept + + tgen = Topogen(BGPEVPNTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + l3mdev_accept = 1 + logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) + + # create VRF vrf-101 on R1 and R2 + # create loop101 + cmds_vrflite = ['sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept), + 'ip link add {}-vrf-101 type vrf table 101', + 'ip ru add oif {}-vrf-101 table 101', + 'ip ru add iif {}-vrf-101 table 101', + 'ip link set dev {}-vrf-101 up', + 'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept), + 'ip link add loop101 type dummy', + 'ip link set dev loop101 master {}-vrf-101', + 'ip link set dev loop101 up'] + cmds_netns = ['ip netns add {}-vrf-101', + 'ip link add loop101 type dummy', + 'ip link set dev loop101 netns {}-vrf-101', + 'ip netns exec {}-vrf-101 ip link set dev loop101 up'] + + cmds_r2 = [ # config routing 101 + 'ip link add name bridge-101 up type bridge stp_state 0', + 'ip link set bridge-101 master {}-vrf-101', + 'ip link set dev bridge-101 up', + 'ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.100.41', + 'brctl addif bridge-101 vxlan-101', + 'ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off'] + + cmds_r1_netns_method3 = ['ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21', + 'ip link set dev vxlan-{1} netns {0}-vrf-{1}', + 'ip netns exec {0}-vrf-{1} ip li set dev lo up', + 'ip netns exec {0}-vrf-{1} brctl addbr bridge-{1}', + 'ip netns exec {0}-vrf-{1} brctl addif bridge-{1} vxlan-{1}', + 'ip netns exec {0}-vrf-{1} ip link set bridge-{1} up', + 'ip netns exec {0}-vrf-{1} ip link set vxlan-{1} up'] + + router = tgen.gears['r1'] + for cmd in cmds_netns: + logger.info('cmd to r1: '+cmd); + output = router.run(cmd.format('r1')) + logger.info('result: '+output); + + router = tgen.gears['r2'] + for cmd in cmds_vrflite: + logger.info('cmd to r2: '+cmd.format('r2')); + output = router.run(cmd.format('r2')) + logger.info('result: '+output); + + for cmd in cmds_r2: + logger.info('cmd to r2: '+cmd.format('r2')); + output = router.run(cmd.format('r2')) + logger.info('result: '+output); + + router = tgen.gears['r1'] + bridge_id = '101' + for cmd in cmds_r1_netns_method3: + logger.info('cmd to r1: '+cmd.format('r1', bridge_id)); + output = router.run(cmd.format('r1', bridge_id)) + logger.info('result: '+output); + router = tgen.gears['r1'] + + for rname, router in router_list.iteritems(): + if rname == 'r1': + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)), + '--vrfwnetns -o vrf0' + ) + else: + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + cmds_rx_netns = ['ip netns del {}-vrf-101'] + + router = tgen.gears['r1'] + for cmd in cmds_rx_netns: + logger.info('cmd to r1: '+cmd.format('r1')); + output = router.run(cmd.format('r1')) + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + topotest.sleep(4, 'waiting 4 seconds for bgp convergence') + # Check IPv4/IPv6 routing tables. + output = tgen.gears['r1'].vtysh_cmd('show bgp l2vpn evpn', isjson=False) + logger.info('==== result from show bgp l2vpn evpn') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp l2vpn evpn route detail', isjson=False) + logger.info('==== result from show bgp l2vpn evpn route detail') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp vrf r1-vrf-101 ipv4', isjson=False) + logger.info('==== result from show bgp vrf r1-vrf-101 ipv4') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp vrf r1-vrf-101', isjson=False) + logger.info('==== result from show bgp vrf r1-vrf-101 ') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show ip route vrf r1-vrf-101', isjson=False) + logger.info('==== result from show ip route vrf r1-vrf-101') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn vni detail', isjson=False) + logger.info('==== result from show evpn vni detail') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn next-hops vni all', isjson=False) + logger.info('==== result from show evpn next-hops vni all') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn rmac vni all', isjson=False) + logger.info('==== result from show evpn next-hops vni all') + logger.info(output) + # Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn) + pingrouter = tgen.gears['r1'] + logger.info('Check Ping IPv4 from R1(r1-vrf-101) to R2(r2-vrf-101 = 192.168.101.41)') + output = pingrouter.run('ip netns exec r1-vrf-101 ping 192.168.101.41 -f -c 1000') + logger.info(output) + if '1000 packets transmitted, 1000 received' not in output: + assertmsg = 'expected ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) should be ok' + assert 0, assertmsg + else: + logger.info('Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK') + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From f54b51035b89eafc04df3f2b225da6d33f20d6b3 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 5 Jun 2020 15:59:43 +0200 Subject: [PATCH 22/24] topotests: bgp_evpn_rt5 test uses ip link instead of bridge-utils bridge-utils package is not available on tested distributions. use ip link instead. Signed-off-by: Philippe Guibert --- tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index 086dcd8cc6..bb0b2c9e0b 100755 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -100,14 +100,14 @@ def setup_module(mod): 'ip link set bridge-101 master {}-vrf-101', 'ip link set dev bridge-101 up', 'ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.100.41', - 'brctl addif bridge-101 vxlan-101', + 'ip link set dev vxlan-101 master bridge-101', 'ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off'] cmds_r1_netns_method3 = ['ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21', 'ip link set dev vxlan-{1} netns {0}-vrf-{1}', 'ip netns exec {0}-vrf-{1} ip li set dev lo up', - 'ip netns exec {0}-vrf-{1} brctl addbr bridge-{1}', - 'ip netns exec {0}-vrf-{1} brctl addif bridge-{1} vxlan-{1}', + 'ip netns exec {0}-vrf-{1} ip link add name bridge-{1} up type bridge stp_state 0', + 'ip netns exec {0}-vrf-{1} ip link set dev vxlan-{1} master bridge-{1}', 'ip netns exec {0}-vrf-{1} ip link set bridge-{1} up', 'ip netns exec {0}-vrf-{1} ip link set vxlan-{1} up'] From 370fb8f819edda7e1a740ed2b4ffdb7fe86b46bb Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 9 Jun 2020 16:49:51 +0200 Subject: [PATCH 23/24] test: topotest bgp_evpn_rt5 runs on kernel > 4.18 if kernel is less than 4.18, then test will not run. Signed-off-by: Philippe Guibert --- tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index bb0b2c9e0b..69ef7e9fae 100755 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -47,6 +47,7 @@ from lib.topolog import logger from mininet.topo import Topo l3mdev_accept = 0 +krel = '' class BGPEVPNTopo(Topo): "Test topology builder" @@ -70,12 +71,18 @@ class BGPEVPNTopo(Topo): def setup_module(mod): "Sets up the pytest environment" global l3mdev_accept + global krel tgen = Topogen(BGPEVPNTopo, mod.__name__) tgen.start_topology() router_list = tgen.routers() + krel = platform.release() + if topotest.version_cmp(krel, '4.18') < 0: + logger.info('BGP EVPN RT5 NETNS tests will not run (have kernel "{}", but it requires 4.18)'.format(krel)) + return pytest.skip('Skipping BGP EVPN RT5 NETNS Test. Kernel not supported') + l3mdev_accept = 1 logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) From b0610ff7271eb72b69d50e6850ad9f3fb84dd62d Mon Sep 17 00:00:00 2001 From: Pat Ruddy Date: Wed, 10 Jun 2020 14:39:37 +0100 Subject: [PATCH 24/24] bgp: remove duplicate command installs [no_]neighbor_nexthop_self_cmd & [no_]neighbor_nexthop_self_force_cmd have duplicate install_element actions on the EVPN_NODE. This causes duplicate command log errors which are caught by topotests. Remove these. Signed-off-by: Pat Ruddy --- bgpd/bgp_vty.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index d9c78b262b..b2a795b97d 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -16446,10 +16446,6 @@ 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);