From 239b26f932e5b78828dce8f7fe628c12b7ddbd9f Mon Sep 17 00:00:00 2001 From: Sharath Ramamurthy Date: Tue, 27 Jul 2021 14:28:59 +0530 Subject: [PATCH] zebra: multiple vlan aware bridge data structure and related changes Multiple vlan aware bridge data structure changes and its corresponding bridge handling changes. A new vlan-table is maintained for each bridge which records the zebra_l2_bridge_vlan entry. zebra_l2_bridge_vlan maps vlan to access_bd associated to this bridge. Existing zebra_evpn_access_bd structure is vlan aware which is now modified to be (vlan, bridge) aware. Whenever a new access_bd is instantiated, a corresponding entry is also recorded in the zebra l2 bridge for the vlan. When the access_bd is dereferenced or whenever a bridge is deleted, the association is cleaned up. Signed-off-by: Sharath Ramamurthy --- zebra/subdir.am | 1 + zebra/zebra_evpn_mh.c | 165 ++++++++++++---- zebra/zebra_evpn_mh.h | 11 +- zebra/zebra_l2_bridge_if.c | 384 +++++++++++++++++++++++++++++++++++++ zebra/zebra_l2_bridge_if.h | 75 ++++++++ zebra/zebra_vty.c | 30 ++- 6 files changed, 616 insertions(+), 50 deletions(-) create mode 100644 zebra/zebra_l2_bridge_if.c create mode 100644 zebra/zebra_l2_bridge_if.h diff --git a/zebra/subdir.am b/zebra/subdir.am index 104980218d..4e159c2017 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -66,6 +66,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_errors.c \ zebra/zebra_gr.c \ zebra/zebra_l2.c \ + zebra/zebra_l2_bridge_if.c \ zebra/zebra_evpn.c \ zebra/zebra_evpn_mac.c \ zebra/zebra_evpn_neigh.c \ diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index a2a0952165..4bba98c5a0 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -41,6 +41,7 @@ #include "zebra/if_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_l2.h" +#include "zebra/zebra_l2_bridge_if.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" @@ -522,7 +523,7 @@ static unsigned int zebra_evpn_acc_vl_hash_keymake(const void *p) { const struct zebra_evpn_access_bd *acc_bd = p; - return jhash_1word(acc_bd->vid, 0); + return jhash_2words(acc_bd->vid, acc_bd->bridge_ifindex, 0); } /* Compare two VLAN based broadcast domains */ @@ -537,16 +538,19 @@ static bool zebra_evpn_acc_vl_cmp(const void *p1, const void *p2) if (acc_bd1 == NULL || acc_bd2 == NULL) return false; - return (acc_bd1->vid == acc_bd2->vid); + return ((acc_bd1->vid == acc_bd2->vid) + && (acc_bd1->bridge_ifindex == acc_bd2->bridge_ifindex)); } /* Lookup VLAN based broadcast domain */ -static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid) +struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid, + struct interface *br_if) { struct zebra_evpn_access_bd *acc_bd; struct zebra_evpn_access_bd tmp; tmp.vid = vid; + tmp.bridge_ifindex = br_if->ifindex; acc_bd = hash_lookup(zmh_info->evpn_vlan_table, &tmp); return acc_bd; @@ -562,11 +566,13 @@ zebra_evpn_acc_vl_new(vlanid_t vid, struct interface *br_if) struct interface *vlan_if; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d add", vid); + zlog_debug("access vlan %d bridge %s add", vid, br_if->name); acc_bd = XCALLOC(MTYPE_ZACC_BD, sizeof(struct zebra_evpn_access_bd)); acc_bd->vid = vid; + acc_bd->bridge_ifindex = br_if->ifindex; + acc_bd->bridge_zif = (struct zebra_if *)br_if->info; /* Initialize the mbr list */ acc_bd->mbr_zifs = list_new(); @@ -575,14 +581,12 @@ zebra_evpn_acc_vl_new(vlanid_t vid, struct interface *br_if) (void)hash_get(zmh_info->evpn_vlan_table, acc_bd, hash_alloc_intern); /* check if an svi exists for the vlan */ - if (br_if) { - vlan_if = zvni_map_to_svi(vid, br_if); - if (vlan_if) { - if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("vlan %d SVI %s set", vid, - vlan_if->name); - acc_bd->vlan_zif = vlan_if->info; - } + vlan_if = zvni_map_to_svi(vid, br_if); + if (vlan_if) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("vlan %d bridge %s SVI %s set", vid, + br_if->name, vlan_if->name); + acc_bd->vlan_zif = vlan_if->info; } return acc_bd; } @@ -623,10 +627,27 @@ static void zebra_evpn_acc_bd_free_on_deref(struct zebra_evpn_access_bd *acc_bd) if (!list_isempty(acc_bd->mbr_zifs) || acc_bd->vxlan_zif) return; + /* Remove this access_bd from bridge hash table */ + zebra_l2_bridge_if_vlan_access_bd_deref(acc_bd); + /* if there are no references free the EVI */ zebra_evpn_acc_vl_free(acc_bd); } +static struct zebra_evpn_access_bd * +zebra_evpn_acc_bd_alloc_on_ref(vlanid_t vid, struct interface *br_if) +{ + struct zebra_evpn_access_bd *acc_bd = NULL; + + assert(br_if && br_if->info); + acc_bd = zebra_evpn_acc_vl_new(vid, br_if); + if (acc_bd) + /* Add this access_bd to bridge hash table */ + zebra_l2_bridge_if_vlan_access_bd_ref(acc_bd); + + return acc_bd; +} + /* called when a SVI is goes up/down */ void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, struct zebra_if *br_zif, bool is_up) @@ -647,14 +668,14 @@ void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, return; vid = vlan_zif->l2info.vl.vid; - acc_bd = zebra_evpn_acc_vl_find(vid); + acc_bd = zebra_evpn_acc_vl_find(vid, tmp_br_zif->ifp); if (!acc_bd) return; if (is_up) { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("vlan %d SVI %s set", vid, - vlan_zif->ifp->name); + zlog_debug("vlan %d bridge %s SVI %s set", vid, + tmp_br_zif->ifp->name, vlan_zif->ifp->name); acc_bd->vlan_zif = vlan_zif; if (acc_bd->zevpn) @@ -662,7 +683,8 @@ void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, acc_bd->zevpn); } else if (acc_bd->vlan_zif) { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("vlan %d SVI clear", vid); + zlog_debug("vlan %d bridge %s SVI clear", vid, + tmp_br_zif->ifp->name); acc_bd->vlan_zif = NULL; if (acc_bd->zevpn && acc_bd->zevpn->mac_table) zebra_evpn_mac_svi_del(vlan_zif->ifp, acc_bd->zevpn); @@ -687,8 +709,9 @@ static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, struct listnode *node; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d l2-vni %u set", - acc_bd->vid, zevpn ? zevpn->vni : 0); + zlog_debug("access vlan %d bridge %s l2-vni %u set", + acc_bd->vid, acc_bd->bridge_zif->ifp->name, + zevpn ? zevpn->vni : 0); for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif)) { if (!zif->es_info.es) @@ -717,6 +740,7 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id, vni_t old_vni; struct zebra_evpn_access_bd *acc_bd; struct zebra_evpn *old_zevpn; + struct interface *br_if; if (!vid) return; @@ -724,10 +748,14 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id, if (!vni_id) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + br_if = vxlan_zif->brslave_info.br_if; + + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (!acc_bd) - acc_bd = zebra_evpn_acc_vl_new(vid, - vxlan_zif->brslave_info.br_if); + acc_bd = zebra_evpn_acc_bd_alloc_on_ref(vid, br_if); old_vni = acc_bd->vni; @@ -745,8 +773,6 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id, if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("access vlan %d vni %u ref", acc_bd->vid, vni_id); - zlog_err("access vlan %d vni %u ref", acc_bd->vid, vni_id); - if (old_zevpn) zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn); @@ -758,6 +784,7 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id, void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, struct zebra_if *vxlan_zif) { + struct interface *br_if; struct zebra_evpn_access_bd *acc_bd; if (!vid) @@ -766,7 +793,11 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, if (!vni_id) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + br_if = vxlan_zif->brslave_info.br_if; + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (!acc_bd) return; @@ -775,7 +806,8 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d vni %u deref", acc_bd->vid, vni_id); + zlog_debug("access vlan %d bridge %s vni %u deref", acc_bd->vid, + br_if->name, vni_id); if (acc_bd->zevpn) zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, acc_bd->zevpn); @@ -788,10 +820,37 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, vni_t vni_id, zebra_evpn_acc_bd_free_on_deref(acc_bd); } +/* handle BridgeIf<->AccessBD cleanup */ +void zebra_evpn_access_bd_bridge_cleanup(vlanid_t vid, struct interface *br_if, + struct zebra_evpn_access_bd *acc_bd) +{ + struct zebra_evpn *zevpn; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access bd vlan %d bridge %s cleanup", acc_bd->vid, + br_if->name); + + zevpn = acc_bd->zevpn; + if (zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, zevpn); + + /* cleanup resources maintained against the ES */ + list_delete_all_node(acc_bd->mbr_zifs); + + acc_bd->zevpn = NULL; + acc_bd->vxlan_zif = NULL; + acc_bd->vni = 0; + acc_bd->bridge_zif = NULL; + + /* if there are no other references the access_bd can be freed */ + zebra_evpn_acc_bd_free_on_deref(acc_bd); +} + /* handle EVPN add/del */ void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, bool set) { + struct interface *br_if; struct zebra_vxlan_vni *vni; struct zebra_evpn_access_bd *acc_bd; @@ -803,7 +862,11 @@ void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, if (!vni) return; - acc_bd = zebra_evpn_acc_vl_find(vni->access_vlan); + br_if = zif->brslave_info.br_if; + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vni->access_vlan, br_if); if (!acc_bd) return; @@ -825,21 +888,26 @@ void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, /* handle addition of new VLAN members */ void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif) { + struct interface *br_if; struct zebra_evpn_access_bd *acc_bd; if (!vid) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + br_if = zif->brslave_info.br_if; + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (!acc_bd) - acc_bd = zebra_evpn_acc_vl_new(vid, zif->brslave_info.br_if); + acc_bd = zebra_evpn_acc_bd_alloc_on_ref(vid, br_if); if (listnode_lookup(acc_bd->mbr_zifs, zif)) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d mbr %s ref", - vid, zif->ifp->name); + zlog_debug("access vlan %d bridge %s mbr %s ref", vid, + br_if->name, zif->ifp->name); listnode_add(acc_bd->mbr_zifs, zif); if (acc_bd->zevpn && zif->es_info.es) @@ -849,13 +917,18 @@ void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif) /* handle deletion of VLAN members */ void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif) { + struct interface *br_if; struct zebra_evpn_access_bd *acc_bd; struct listnode *node; if (!vid) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + br_if = zif->brslave_info.br_if; + if (!br_if) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (!acc_bd) return; @@ -864,8 +937,8 @@ void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d mbr %s deref", - vid, zif->ifp->name); + zlog_debug("access vlan %d bridge %s mbr %s deref", vid, + br_if->name, zif->ifp->name); list_delete_node(acc_bd->mbr_zifs, node); @@ -932,14 +1005,19 @@ static void zebra_evpn_acc_vl_show_entry_detail(struct vty *vty, if (json) { zebra_evpn_acc_vl_json_fill(acc_bd, json, true); } else { - vty_out(vty, "VLAN: %u\n", acc_bd->vid); + vty_out(vty, "VLAN: %s.%u\n", acc_bd->bridge_zif->ifp->name, + acc_bd->vid); vty_out(vty, " VxLAN Interface: %s\n", acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-"); vty_out(vty, " SVI: %s\n", acc_bd->vlan_zif ? acc_bd->vlan_zif->ifp->name : "-"); - vty_out(vty, " L2-VNI: %d\n", - acc_bd->zevpn ? acc_bd->zevpn->vni : 0); + if (acc_bd->zevpn) + vty_out(vty, " L2-VNI: %d\n", acc_bd->zevpn->vni); + else { + vty_out(vty, " L2-VNI: 0\n"); + vty_out(vty, " L3-VNI: %d\n", acc_bd->vni); + } vty_out(vty, " Member Count: %d\n", listcount(acc_bd->mbr_zifs)); vty_out(vty, " Members: \n"); @@ -955,7 +1033,8 @@ static void zebra_evpn_acc_vl_show_entry(struct vty *vty, if (json) { zebra_evpn_acc_vl_json_fill(acc_bd, json, false); } else { - vty_out(vty, "%-5u %-15s %-8d %-15s %u\n", acc_bd->vid, + vty_out(vty, "%-5s.%-5u %-15s %-8d %-15s %u\n", + acc_bd->bridge_zif->ifp->name, acc_bd->vid, acc_bd->vlan_zif ? acc_bd->vlan_zif->ifp->name : "-", acc_bd->zevpn ? acc_bd->zevpn->vni : 0, acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-", @@ -993,7 +1072,7 @@ void zebra_evpn_acc_vl_show(struct vty *vty, bool uj) wctx.detail = false; if (!uj) - vty_out(vty, "%-5s %-15s %-8s %-15s %s\n", "VLAN", "SVI", + vty_out(vty, "%-12s %-15s %-8s %-15s %s\n", "VLAN", "SVI", "L2-VNI", "VXLAN-IF", "# Members"); hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash, @@ -1022,7 +1101,8 @@ void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj) vty_json(vty, json_array); } -void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid) +void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid, + struct interface *br_if) { json_object *json = NULL; struct zebra_evpn_access_bd *acc_bd; @@ -1030,12 +1110,13 @@ void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid) if (uj) json = json_object_new_object(); - acc_bd = zebra_evpn_acc_vl_find(vid); + acc_bd = zebra_evpn_acc_vl_find(vid, br_if); if (acc_bd) { zebra_evpn_acc_vl_show_entry_detail(vty, acc_bd, json); } else { if (!json) - vty_out(vty, "VLAN %u not present\n", vid); + vty_out(vty, "VLAN %s.%u not present\n", br_if->name, + vid); } if (uj) @@ -1992,7 +2073,7 @@ static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) return; bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { - acc_bd = zebra_evpn_acc_vl_find(vid); + acc_bd = zebra_evpn_acc_vl_find(vid, zif->brslave_info.br_if); if (acc_bd->zevpn) zebra_evpn_local_es_evi_add(es, acc_bd->zevpn); } diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 2c6e4924f6..12a8569504 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -179,6 +179,9 @@ struct zebra_evpn_es_vtep { struct zebra_evpn_access_bd { vlanid_t vid; + ifindex_t bridge_ifindex; + struct zebra_if *bridge_zif; /* associated bridge */ + vni_t vni; /* vni associated with the vxlan device */ struct zebra_if *vxlan_zif; /* vxlan device */ /* list of members associated with the BD i.e. (potential) ESs */ @@ -348,9 +351,12 @@ extern void zebra_evpn_interface_init(void); extern int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp); extern void zebra_evpn_acc_vl_show(struct vty *vty, bool uj); extern void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj); -extern void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid); extern void zebra_evpn_if_es_print(struct vty *vty, json_object *json, struct zebra_if *zif); +extern struct zebra_evpn_access_bd * +zebra_evpn_acc_vl_find(vlanid_t vid, struct interface *br_if); +extern void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid, + struct interface *br_if); extern void zebra_evpn_es_cleanup(void); extern int zebra_evpn_mh_mac_holdtime_update(struct vty *vty, uint32_t duration, bool set_default); @@ -376,6 +382,9 @@ extern void zebra_evpn_l2_nh_show(struct vty *vty, bool uj); extern void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, struct zebra_if *br_zif, bool is_up); extern void zebra_evpn_acc_bd_svi_mac_add(struct interface *vlan_if); +extern void +zebra_evpn_access_bd_bridge_cleanup(vlanid_t vid, struct interface *br_if, + struct zebra_evpn_access_bd *acc_bd); extern void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es, struct interface *ifp, bool bypass); extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS); diff --git a/zebra/zebra_l2_bridge_if.c b/zebra/zebra_l2_bridge_if.c new file mode 100644 index 0000000000..6dc3e93bb0 --- /dev/null +++ b/zebra/zebra_l2_bridge_if.c @@ -0,0 +1,384 @@ +/* + * Zebra L2 bridge interface handling + * + * Copyright (C) 2021 Cumulus Networks, Inc. + * Sharath Ramamurthy + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include + +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" +#ifdef GNU_LINUX +#include +#endif + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_l2_bridge_if.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_vxlan_if.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" +#include "zebra/zebra_router.h" + +static unsigned int zebra_l2_bridge_vlan_hash_keymake(const void *p) +{ + const struct zebra_l2_bridge_vlan *bvlan; + + bvlan = (const struct zebra_l2_bridge_vlan *)p; + return jhash(&bvlan->vid, sizeof(bvlan->vid), 0); +} + +static bool zebra_l2_bridge_vlan_hash_cmp(const void *p1, const void *p2) +{ + const struct zebra_l2_bridge_vlan *bv1; + const struct zebra_l2_bridge_vlan *bv2; + + bv1 = (const struct zebra_l2_bridge_vlan *)p1; + bv2 = (const struct zebra_l2_bridge_vlan *)p2; + + return (bv1->vid == bv2->vid); +} + +static int zebra_l2_bridge_if_vlan_walk_callback(struct hash_bucket *bucket, + void *ctxt) +{ + int ret; + struct zebra_l2_bridge_vlan *bvlan; + struct zebra_l2_bridge_if_ctx *ctx; + + bvlan = (struct zebra_l2_bridge_vlan *)bucket->data; + ctx = (struct zebra_l2_bridge_if_ctx *)ctxt; + + ret = ctx->func(ctx->zif, bvlan, ctx->arg); + return ret; +} + +static void zebra_l2_bridge_if_vlan_iterate_callback(struct hash_bucket *bucket, + void *ctxt) +{ + struct zebra_l2_bridge_vlan *bvlan; + struct zebra_l2_bridge_if_ctx *ctx; + + bvlan = (struct zebra_l2_bridge_vlan *)bucket->data; + ctx = (struct zebra_l2_bridge_if_ctx *)ctxt; + + ctx->func(ctx->zif, bvlan, ctx->arg); +} + +static int zebra_l2_bridge_if_vlan_clean(struct zebra_if *zif, + struct zebra_l2_bridge_vlan *bvlan, + void *ctxt) +{ + struct zebra_evpn_access_bd *acc_bd; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("access vlan %d bridge %s cleanup", bvlan->vid, + zif->ifp->name); + + acc_bd = zebra_evpn_acc_vl_find(bvlan->vid, zif->ifp); + if (acc_bd) + zebra_evpn_access_bd_bridge_cleanup(bvlan->vid, zif->ifp, + acc_bd); + + bvlan->access_bd = NULL; + return 0; +} + +static void zebra_l2_bridge_vlan_free(void *arg) +{ + struct zebra_l2_bridge_vlan *bvl; + + bvl = (struct zebra_l2_bridge_vlan *)arg; + XFREE(MTYPE_TMP, bvl); +} + +static void *zebra_l2_bridge_vlan_alloc(void *p) +{ + struct zebra_l2_bridge_vlan *bvlan; + const struct zebra_l2_bridge_vlan *bvl; + + bvl = (const struct zebra_l2_bridge_vlan *)p; + bvlan = XCALLOC(MTYPE_TMP, sizeof(*bvlan)); + bvlan->vid = bvl->vid; + bvlan->access_bd = bvl->access_bd; + + return (void *)bvlan; +} + +static void zebra_l2_bridge_vlan_table_destroy(struct hash *vlan_table) +{ + if (vlan_table) { + hash_clean(vlan_table, zebra_l2_bridge_vlan_free); + hash_free(vlan_table); + } +} + +static struct hash *zebra_l2_bridge_vlan_table_create(void) +{ + return hash_create(zebra_l2_bridge_vlan_hash_keymake, + zebra_l2_bridge_vlan_hash_cmp, + "Zebra L2 Bridge Vlan Table"); +} + +static void zebra_l2_bridge_if_vlan_table_destroy(struct zebra_if *zif) +{ + struct zebra_l2_bridge_if *br; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + zebra_l2_bridge_if_vlan_iterate(zif, zebra_l2_bridge_if_vlan_clean, + NULL); + zebra_l2_bridge_vlan_table_destroy(br->vlan_table); + br->vlan_table = NULL; +} + +static int zebra_l2_bridge_if_vlan_table_create(struct zebra_if *zif) +{ + struct zebra_l2_bridge_if *br; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + if (!br->vlan_table) { + br->vlan_table = zebra_l2_bridge_vlan_table_create(); + if (!br->vlan_table) + return ENOMEM; + } + + return 0; +} + +static int zebra_l2_bridge_if_vlan_del(struct interface *ifp, vlanid_t vid) +{ + struct zebra_if *zif; + struct zebra_l2_bridge_if *br; + struct zebra_l2_bridge_vlan bvl; + struct zebra_l2_bridge_vlan *bvlan; + + zif = (struct zebra_if *)ifp->info; + memset(&bvl, 0, sizeof(bvl)); + bvl.vid = vid; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + bvlan = hash_release(br->vlan_table, &bvl); + + if (bvlan) + zebra_l2_bridge_vlan_free(bvlan); + + return 0; +} + +static int zebra_l2_bridge_if_vlan_update(struct interface *ifp, + struct zebra_l2_bridge_vlan *bvl, + int chgflags) +{ + struct zebra_if *zif; + struct zebra_l2_bridge_vlan *bvlan; + + zif = (struct zebra_if *)ifp->info; + bvlan = zebra_l2_bridge_if_vlan_find(zif, bvl->vid); + + if (chgflags & ZEBRA_BRIDGEIF_ACCESS_BD_CHANGE) + bvlan->access_bd = bvl->access_bd; + + if (!bvlan->access_bd) + return zebra_l2_bridge_if_vlan_del(ifp, bvl->vid); + + return 0; +} + +static int zebra_l2_bridge_if_vlan_add(struct interface *ifp, + struct zebra_l2_bridge_vlan *bvlan) +{ + struct zebra_if *zif; + struct zebra_l2_bridge_if *br; + + zif = (struct zebra_if *)ifp->info; + br = BRIDGE_FROM_ZEBRA_IF(zif); + hash_get(br->vlan_table, (void *)bvlan, zebra_l2_bridge_vlan_alloc); + + return 0; +} + +struct zebra_l2_bridge_vlan * +zebra_l2_bridge_if_vlan_find(const struct zebra_if *zif, vlanid_t vid) +{ + const struct zebra_l2_bridge_if *br; + struct zebra_l2_bridge_vlan *bvl; + struct zebra_l2_bridge_vlan bvlan; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + memset(&bvlan, 0, sizeof(bvlan)); + bvlan.vid = vid; + bvl = (struct zebra_l2_bridge_vlan *)hash_lookup(br->vlan_table, + (void *)&bvlan); + + /* TODO: For debugging. Remove later */ + if (bvl) + assert(bvl->vid == vid); + + return bvl; +} + +vni_t zebra_l2_bridge_if_vni_find(const struct zebra_if *zif, vlanid_t vid) +{ + vni_t vni_id = 0; + struct zebra_l2_bridge_vlan *bvlan; + + bvlan = zebra_l2_bridge_if_vlan_find(zif, vid); + if (bvlan && bvlan->access_bd && bvlan->access_bd->vni) + vni_id = bvlan->access_bd->vni; + + return vni_id; +} + +void zebra_l2_bridge_if_vlan_iterate(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_l2_bridge_vlan *, + void *), + void *arg) +{ + struct zebra_l2_bridge_if *br; + struct zebra_l2_bridge_if_ctx ctx; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + memset(&ctx, 0, sizeof(ctx)); + ctx.zif = zif; + ctx.func = func; + ctx.arg = arg; + hash_iterate(br->vlan_table, zebra_l2_bridge_if_vlan_iterate_callback, + &ctx); +} + +void zebra_l2_bridge_if_vlan_walk(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_l2_bridge_vlan *, + void *), + void *arg) +{ + struct zebra_l2_bridge_if *br; + struct zebra_l2_bridge_if_ctx ctx; + + br = BRIDGE_FROM_ZEBRA_IF(zif); + memset(&ctx, 0, sizeof(ctx)); + ctx.zif = zif; + ctx.func = func; + ctx.arg = arg; + hash_walk(br->vlan_table, zebra_l2_bridge_if_vlan_walk_callback, &ctx); +} + +int zebra_l2_bridge_if_vlan_access_bd_deref(struct zebra_evpn_access_bd *bd) +{ + int chgflags = 0; + struct zebra_if *zif; + struct zebra_l2_bridge_vlan bvl; + struct zebra_l2_bridge_vlan *bvlan; + + zif = bd->bridge_zif; + if (!zif) + return -1; + + bvlan = zebra_l2_bridge_if_vlan_find(zif, bd->vid); + if (!bvlan) + return 0; + + memset(&bvl, 0, sizeof(bvl)); + bvl.vid = bd->vid; + bvl.access_bd = NULL; + chgflags = ZEBRA_BRIDGEIF_ACCESS_BD_CHANGE; + return zebra_l2_bridge_if_vlan_update(zif->ifp, &bvl, chgflags); +} + +int zebra_l2_bridge_if_vlan_access_bd_ref(struct zebra_evpn_access_bd *bd) +{ + int chgflags = 0; + struct zebra_if *zif; + struct zebra_l2_bridge_vlan bvl; + struct zebra_l2_bridge_vlan *bvlan; + + zif = bd->bridge_zif; + if (!zif) + return -1; + + if (!bd->vid) + return -1; + + memset(&bvl, 0, sizeof(bvl)); + bvl.vid = bd->vid; + bvl.access_bd = bd; + + bvlan = zebra_l2_bridge_if_vlan_find(zif, bd->vid); + if (!bvlan) { + return zebra_l2_bridge_if_vlan_add(zif->ifp, &bvl); + } + + chgflags = ZEBRA_BRIDGEIF_ACCESS_BD_CHANGE; + return zebra_l2_bridge_if_vlan_update(zif->ifp, &bvl, chgflags); +} + +int zebra_l2_bridge_if_cleanup(struct interface *ifp) +{ + struct zebra_if *zif; + + if (!IS_ZEBRA_IF_BRIDGE(ifp)) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("bridge %s cleanup", ifp->name); + + zif = (struct zebra_if *)ifp->info; + zebra_l2_bridge_if_vlan_table_destroy(zif); + return 0; +} + +int zebra_l2_bridge_if_del(struct interface *ifp) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("bridge %s delete", ifp->name); + + return zebra_l2_bridge_if_cleanup(ifp); +} + +int zebra_l2_bridge_if_add(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_l2_bridge_if *br; + + zif = (struct zebra_if *)ifp->info; + br = BRIDGE_FROM_ZEBRA_IF(zif); + br->br_zif = (struct zebra_if *)ifp->info; + zebra_l2_bridge_if_vlan_table_create(zif); + return 0; +} diff --git a/zebra/zebra_l2_bridge_if.h b/zebra/zebra_l2_bridge_if.h new file mode 100644 index 0000000000..734ecfdcf1 --- /dev/null +++ b/zebra/zebra_l2_bridge_if.h @@ -0,0 +1,75 @@ +/* + * Zebra L2 bridge interface data structures and definitions + * These are public definitions referenced by other files. + * Copyright (C) 2021 Cumulus Networks, Inc. + * Sharath Ramamurthy + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_L2_BRIDGE_IF_H +#define _ZEBRA_L2_BRIDGE_IF_H + +#include +#include + +#include "linklist.h" +#include "if.h" +#include "vlan.h" +#include "vxlan.h" + +#include "lib/json.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zserv.h" + +#include "zebra/zebra_dplane.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Bridge interface change flags of interest. */ +#define ZEBRA_BRIDGEIF_ACCESS_BD_CHANGE (1 << 0) + +extern struct zebra_l2_bridge_vlan * +zebra_l2_bridge_if_vlan_find(const struct zebra_if *zif, vlanid_t vid); +extern vni_t zebra_l2_bridge_if_vni_find(const struct zebra_if *zif, + vlanid_t vid); +extern void zebra_l2_bridge_if_vlan_iterate( + struct zebra_if *zif, + int (*func)(struct zebra_if *zif, struct zebra_l2_bridge_vlan *, + void *), + void *arg); +extern void +zebra_l2_bridge_if_vlan_walk(struct zebra_if *zif, + int (*func)(struct zebra_if *zif, + struct zebra_l2_bridge_vlan *, void *), + void *arg); +extern int +zebra_l2_bridge_if_vlan_access_bd_deref(struct zebra_evpn_access_bd *bd); +extern int +zebra_l2_bridge_if_vlan_access_bd_ref(struct zebra_evpn_access_bd *bd); +extern int zebra_l2_bridge_if_del(struct interface *ifp); +extern int zebra_l2_bridge_if_add(struct interface *ifp); +extern int zebra_l2_bridge_if_cleanup(struct interface *ifp); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_L2_BRIDGE_IF_H */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index d96ee4890f..88feae984b 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -3016,20 +3016,36 @@ DEFPY(show_evpn_es_evi, return CMD_SUCCESS; } -DEFPY(show_evpn_access_vlan, - show_evpn_access_vlan_cmd, - "show evpn access-vlan [(1-4094)$vid | detail$detail] [json$json]", +DEFPY(show_evpn_access_vlan, show_evpn_access_vlan_cmd, + "show evpn access-vlan [IFNAME$if_name (1-4094)$vid | detail$detail] [json$json]", SHOW_STR "EVPN\n" "Access VLANs\n" "VLAN ID\n" - "Detailed information\n" - JSON_STR) + "Detailed information\n" JSON_STR) { bool uj = !!json; - if (vid) { - zebra_evpn_acc_vl_show_vid(vty, uj, vid); + if (if_name && vid) { + bool found = false; + struct vrf *vrf; + struct interface *ifp; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (if_name) { + ifp = if_lookup_by_name(if_name, vrf->vrf_id); + if (ifp) { + zebra_evpn_acc_vl_show_vid(vty, uj, vid, + ifp); + found = true; + break; + } + } + } + if (!found) { + vty_out(vty, "%% Can't find interface %s\n", if_name); + return CMD_WARNING; + } } else { if (detail) zebra_evpn_acc_vl_show_detail(vty, uj); -- 2.39.5