X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=zebra%2Fzebra_evpn_mh.c;h=334dde3b101230d4243435e8e8c95ed205d8c8fd;hb=2e1ea892220dccb8a4c72e438cb3bbd4cac22b2b;hp=98120accfdc2e5fa7c22afa071aae4725a9cea2e;hpb=63e357a82c55fb750aed1a65d8e8107338f32329;p=mirror_frr.git diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 98120accf..334dde3b1 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -1,20 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Zebra EVPN multihoming code * * Copyright (C) 2019 Cumulus Networks, Inc. * Anuradha Karuppiah - * - * 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 @@ -41,12 +30,13 @@ #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" +#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_evpn.h" #include "zebra/zebra_evpn_mac.h" -#include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_router.h" #include "zebra/zebra_evpn_mh.h" #include "zebra/zebra_nhg.h" @@ -481,6 +471,7 @@ void zebra_evpn_update_all_es(struct zebra_evpn *zevpn) struct interface *vlan_if; struct interface *vxlan_if; struct zebra_if *vxlan_zif; + struct zebra_vxlan_vni *vni; /* the EVPN is now elgible as a base for EVPN-MH */ if (zebra_evpn_send_to_client_ok(zevpn)) @@ -497,11 +488,15 @@ void zebra_evpn_update_all_es(struct zebra_evpn *zevpn) vxlan_zif = vxlan_if->info; if (if_is_operative(vxlan_if) && vxlan_zif->brslave_info.br_if) { - vlan_if = zvni_map_to_svi( - vxlan_zif->l2info.vxl.access_vlan, - vxlan_zif->brslave_info.br_if); - if (vlan_if) - zebra_evpn_acc_bd_svi_mac_add(vlan_if); + vni = zebra_vxlan_if_vni_find(vxlan_zif, zevpn->vni); + /* VLAN-VNI mappings may not exist */ + if (vni) { + vlan_if = zvni_map_to_svi( + vni->access_vlan, + vxlan_zif->brslave_info.br_if); + if (vlan_if) + zebra_evpn_acc_bd_svi_mac_add(vlan_if); + } } } } @@ -520,7 +515,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 */ @@ -535,16 +530,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; @@ -560,11 +558,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(); @@ -573,14 +573,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; } @@ -621,16 +619,32 @@ 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) { struct zebra_evpn_access_bd *acc_bd; - struct zebra_l2info_bridge *br; uint16_t vid; struct zebra_if *tmp_br_zif = br_zif; @@ -641,20 +655,19 @@ void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, tmp_br_zif = vlan_zif->link->info; } - br = &tmp_br_zif->l2info.br; /* ignore vlan unaware bridges */ - if (!br->vlan_aware) + if (!IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(tmp_br_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 +675,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 +701,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) @@ -711,33 +726,44 @@ static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, } /* handle VLAN->VxLAN_IF association */ -void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) +void zebra_evpn_vl_vxl_ref(uint16_t vid, vni_t vni_id, + struct zebra_if *vxlan_zif) { + vni_t old_vni; struct zebra_evpn_access_bd *acc_bd; - struct zebra_if *old_vxlan_zif; struct zebra_evpn *old_zevpn; + struct interface *br_if; if (!vid) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + if (!vni_id) + return; + + 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_vxlan_zif = acc_bd->vxlan_zif; - acc_bd->vxlan_zif = vxlan_zif; - if (vxlan_zif == old_vxlan_zif) + old_vni = acc_bd->vni; + + if (vni_id == old_vni) return; + acc_bd->vni = vni_id; + acc_bd->vxlan_zif = vxlan_zif; + old_zevpn = acc_bd->zevpn; - acc_bd->zevpn = zebra_evpn_lookup(vxlan_zif->l2info.vxl.vni); + acc_bd->zevpn = zebra_evpn_lookup(vni_id); if (acc_bd->zevpn == old_zevpn) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d vni %u ref", - acc_bd->vid, vxlan_zif->l2info.vxl.vni); + zlog_debug("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); @@ -747,30 +773,66 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) } /* handle VLAN->VxLAN_IF deref */ -void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif) +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) return; - acc_bd = zebra_evpn_acc_vl_find(vid); + if (!vni_id) + return; + + 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; /* clear vxlan_if only if it matches */ - if (acc_bd->vxlan_zif != vxlan_zif) + if (acc_bd->vni != vni_id) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("access vlan %d vni %u deref", - acc_bd->vid, vxlan_zif->l2info.vxl.vni); + 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); acc_bd->zevpn = NULL; acc_bd->vxlan_zif = NULL; + acc_bd->vni = 0; + + /* if there are no other references the access_bd can be freed */ + 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); @@ -780,15 +842,23 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif) void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, bool set) { - struct zebra_l2info_vxlan *vxl; + struct interface *br_if; + struct zebra_vxlan_vni *vni; struct zebra_evpn_access_bd *acc_bd; if (!zif) return; /* locate access_bd associated with the vxlan device */ - vxl = &zif->l2info.vxl; - acc_bd = zebra_evpn_acc_vl_find(vxl->access_vlan); + vni = zebra_vxlan_if_vni_find(zif, zevpn->vni); + if (!vni) + return; + + 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; @@ -810,21 +880,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) @@ -834,13 +909,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; @@ -849,8 +929,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); @@ -917,14 +997,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"); @@ -940,7 +1025,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 : "-", @@ -978,7 +1064,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, @@ -1007,7 +1093,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; @@ -1015,12 +1102,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) @@ -1977,7 +2065,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); } @@ -1986,9 +2074,10 @@ static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) static void zebra_evpn_flush_local_mac(struct zebra_mac *mac, struct interface *ifp) { + vlanid_t vid; struct zebra_if *zif; struct interface *br_ifp; - vlanid_t vid; + struct zebra_vxlan_vni *vni; zif = ifp->info; br_ifp = zif->brslave_info.br_if; @@ -1997,7 +2086,8 @@ static void zebra_evpn_flush_local_mac(struct zebra_mac *mac, if (mac->zevpn->vxlan_if) { zif = mac->zevpn->vxlan_if->info; - vid = zif->l2info.vxl.access_vlan; + vni = zebra_vxlan_if_vni_find(zif, mac->zevpn->vni); + vid = vni->access_vlan; } else { vid = 0; } @@ -2759,6 +2849,12 @@ bool zebra_evpn_is_if_es_capable(struct zebra_if *zif) if (zif->zif_type == ZEBRA_IF_BOND) return true; + /* relax the checks to allow config to be applied in zebra + * before interface is rxed from the kernel + */ + if (zif->ifp->ifindex == IFINDEX_INTERNAL) + return true; + /* XXX: allow swpX i.e. a regular ethernet port to be an ES link too */ return false; }