diff options
| -rw-r--r-- | bgpd/bgp_attr.c | 2 | ||||
| -rw-r--r-- | bgpd/bgp_attr.h | 8 | ||||
| -rw-r--r-- | bgpd/bgp_mac.c | 354 | ||||
| -rw-r--r-- | bgpd/bgp_mac.h | 41 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 14 | ||||
| -rw-r--r-- | bgpd/bgp_vty.c | 22 | ||||
| -rw-r--r-- | bgpd/bgp_zebra.c | 9 | ||||
| -rw-r--r-- | bgpd/bgpd.c | 3 | ||||
| -rw-r--r-- | bgpd/bgpd.h | 3 | ||||
| -rw-r--r-- | bgpd/subdir.am | 2 |
10 files changed, 448 insertions, 10 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b990e99bda..03f31eddfc 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -392,7 +392,7 @@ static bool overlay_index_same(const struct attr *a1, const struct attr *a2) if (!a1 && !a2) return true; return !memcmp(&(a1->evpn_overlay), &(a2->evpn_overlay), - sizeof(struct overlay_index)); + sizeof(struct bgp_route_evpn)); } /* Unknown transit attribute. */ diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 47a4182fee..6d5c647b21 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -92,12 +92,6 @@ struct bgp_tea_options { #endif -/* Overlay Index Info */ -struct overlay_index { - struct eth_segment_id eth_s_id; - union gw_addr gw_ip; -}; - enum pta_type { PMSI_TNLTYPE_NO_INFO = 0, PMSI_TNLTYPE_RSVP_TE_P2MP, @@ -204,7 +198,7 @@ struct attr { struct bgp_attr_encap_subtlv *vnc_subtlvs; /* VNC-specific */ #endif /* EVPN */ - struct overlay_index evpn_overlay; + struct bgp_route_evpn evpn_overlay; /* EVPN MAC Mobility sequence number, if any. */ uint32_t mm_seqnum; diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c new file mode 100644 index 0000000000..24f93e4373 --- /dev/null +++ b/bgpd/bgp_mac.c @@ -0,0 +1,354 @@ +/* + * BGPd - Mac hash code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> + +#include <jhash.h> +#include <hash.h> +#include <prefix.h> +#include <memory.h> + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_mac.h" +#include "bgpd/bgp_memory.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_evpn_private.h" + +DEFINE_MTYPE_STATIC(BGPD, BSM, "Mac Hash Entry"); +DEFINE_MTYPE_STATIC(BGPD, BSM_STRING, "Mac Hash Entry Interface String"); + +struct bgp_self_mac { + struct ethaddr macaddr; + struct list *ifp_list; +}; + +static unsigned int bgp_mac_hash_key_make(void *data) +{ + struct bgp_self_mac *bsm = data; + + return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead); +} + +static bool bgp_mac_hash_cmp(const void *d1, const void *d2) +{ + const struct bgp_self_mac *bsm1 = d1; + const struct bgp_self_mac *bsm2 = d2; + + if (memcmp(&bsm1->macaddr, &bsm2->macaddr, ETH_ALEN) == 0) + return true; + + return false; +} + +void bgp_mac_init(void) +{ + bm->self_mac_hash = hash_create(bgp_mac_hash_key_make, bgp_mac_hash_cmp, + "BGP MAC Hash"); +} + +static void bgp_mac_hash_free(void *data) +{ + struct bgp_self_mac *bsm = data; + + if (bsm->ifp_list) + list_delete(&bsm->ifp_list); + + XFREE(MTYPE_BSM, bsm); +} + +void bgp_mac_finish(void) +{ + hash_clean(bm->self_mac_hash, bgp_mac_hash_free); + hash_free(bm->self_mac_hash); +} + +static void bgp_mac_hash_interface_string_del(void *val) +{ + char *data = val; + + XFREE(MTYPE_BSM_STRING, data); +} + +static void *bgp_mac_hash_alloc(void *p) +{ + const struct bgp_self_mac *orig = p; + struct bgp_self_mac *bsm; + + bsm = XCALLOC(MTYPE_BSM, sizeof(struct bgp_self_mac)); + memcpy(&bsm->macaddr, &orig->macaddr, ETH_ALEN); + + bsm->ifp_list = list_new(); + bsm->ifp_list->del = bgp_mac_hash_interface_string_del; + + return bsm; +} + +struct bgp_mac_find_internal { + struct bgp_self_mac *bsm; + const char *ifname; +}; + +static void bgp_mac_find_ifp_internal(struct hash_backet *backet, void *arg) +{ + struct bgp_mac_find_internal *bmfi = arg; + struct bgp_self_mac *bsm = backet->data; + struct listnode *node; + char *name; + + for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) { + if (strcmp(name, bmfi->ifname) == 0) { + bmfi->bsm = bsm; + return; + } + } +} + +static struct bgp_self_mac *bgp_mac_find_interface_name(const char *ifname) +{ + struct bgp_mac_find_internal bmfi; + + bmfi.bsm = NULL; + bmfi.ifname = ifname; + hash_iterate(bm->self_mac_hash, bgp_mac_find_ifp_internal, &bmfi); + + return bmfi.bsm; +} + +static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, + struct bgp_table *table) +{ + struct bgp_node *prn, *rn; + struct bgp_path_info *pi; + uint32_t count = 0; + + for (prn = bgp_table_top(table); prn; prn = bgp_route_next(prn)) { + struct bgp_table *sub = prn->info; + + if (!sub) + continue; + + for (rn = bgp_table_top(sub); rn; rn = bgp_route_next(rn)) { + struct prefix_rd prd; + uint32_t num_labels = 0; + mpls_label_t *label_pnt = NULL; + struct bgp_route_evpn evpn; + + count++; + for (pi = rn->info; pi; pi = pi->next) { + if (pi->peer == peer) + break; + } + + if (!pi) + continue; + + if (pi->extra) + num_labels = pi->extra->num_labels; + if (num_labels) + label_pnt = &pi->extra->label[0]; + + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(&prd.val, &prn->p.u.val, 8); + + memcpy(&evpn, &pi->attr->evpn_overlay, sizeof(evpn)); + int32_t ret = bgp_update(peer, &rn->p, + pi->addpath_rx_id, + pi->attr, AFI_L2VPN, SAFI_EVPN, + ZEBRA_ROUTE_BGP, + BGP_ROUTE_NORMAL, &prd, + label_pnt, num_labels, + 1, &evpn); + + if (ret < 0) + bgp_unlock_node(rn); + } + } +} + +static void bgp_mac_rescan_evpn_table(struct bgp *bgp) +{ + struct listnode *node; + struct peer *peer; + safi_t safi; + afi_t afi; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + continue; + + if (peer->status != Established) + continue; + + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SOFT_RECONFIG)) { + if (bgp_debug_update(peer, NULL, NULL, 1)) + zlog_debug("Processing EVPN MAC interface change on peer %s (inbound, soft-reconfig)", + peer->host); + + bgp_soft_reconfig_in(peer, afi, safi); + } else { + struct bgp_table *table = bgp->rib[afi][safi]; + + if (bgp_debug_update(peer, NULL, NULL, 1)) + zlog_debug("Processing EVPN MAC interface change on peer %s", + peer->host); + bgp_process_mac_rescan_table(bgp, peer, table); + } + } +} + +static void bgp_mac_rescan_all_evpn_tables(void) +{ + struct listnode *node; + struct bgp *bgp; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { + struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN]; + + if (table) + bgp_mac_rescan_evpn_table(bgp); + } +} + +static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname) +{ + struct listnode *node = NULL; + char *name; + + for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) { + if (strcmp(name, ifname) == 0) + break; + } + + if (node) { + list_delete_node(bsm->ifp_list, node); + XFREE(MTYPE_BSM_STRING, name); + } + + if (bsm->ifp_list->count == 0) { + hash_release(bm->self_mac_hash, bsm); + list_delete(&bsm->ifp_list); + XFREE(MTYPE_BSM, bsm); + + bgp_mac_rescan_all_evpn_tables(); + } +} + +void bgp_mac_add_mac_entry(struct interface *ifp) +{ + struct bgp_self_mac lookup; + struct bgp_self_mac *bsm; + struct bgp_self_mac *old_bsm; + char *ifname; + + memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); + bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc); + + /* + * Does this happen to be a move + */ + old_bsm = bgp_mac_find_interface_name(ifp->name); + ifname = XSTRDUP(MTYPE_BSM_STRING, ifp->name); + + if (bsm->ifp_list->count == 0) { + + listnode_add(bsm->ifp_list, ifname); + if (old_bsm) + bgp_mac_remove_ifp_internal(old_bsm, ifname); + } else { + /* + * If old mac address is the same as the new, + * then there is nothing to do here + */ + if (old_bsm == bsm) + return; + + if (old_bsm) + bgp_mac_remove_ifp_internal(old_bsm, ifp->name); + + listnode_add(bsm->ifp_list, ifname); + } + + bgp_mac_rescan_all_evpn_tables(); +} + +void bgp_mac_del_mac_entry(struct interface *ifp) +{ + struct bgp_self_mac lookup; + struct bgp_self_mac *bsm; + + memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); + bsm = hash_lookup(bm->self_mac_hash, &lookup); + if (!bsm) + return; + + /* + * Write code to allow old mac address to no-longer + * win if we happen to have received it from a peer. + */ + bgp_mac_remove_ifp_internal(bsm, ifp->name); +} + +bool bgp_mac_entry_exists(struct prefix *p) +{ + struct prefix_evpn *pevpn = (struct prefix_evpn *)p; + struct bgp_self_mac lookup; + struct bgp_self_mac *bsm; + + if (pevpn->family != AF_EVPN) + return false; + + if (pevpn->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) + return false; + + memcpy(&lookup.macaddr, &p->u.prefix_evpn.macip_addr.mac, ETH_ALEN); + bsm = hash_lookup(bm->self_mac_hash, &lookup); + if (!bsm) + return false; + + return true; +} + +static void bgp_mac_show_mac_entry(struct hash_backet *backet, void *arg) +{ + struct vty *vty = arg; + struct bgp_self_mac *bsm = backet->data; + struct listnode *node; + char *name; + char buf_mac[ETHER_ADDR_STRLEN]; + + vty_out(vty, "Mac Address: %s ", + prefix_mac2str(&bsm->macaddr, buf_mac, sizeof(buf_mac))); + + for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) + vty_out(vty, "%s ", name); + + vty_out(vty, "\n"); +} + +void bgp_mac_dump_table(struct vty *vty) +{ + hash_iterate(bm->self_mac_hash, bgp_mac_show_mac_entry, vty); +} diff --git a/bgpd/bgp_mac.h b/bgpd/bgp_mac.h new file mode 100644 index 0000000000..1dd987ef12 --- /dev/null +++ b/bgpd/bgp_mac.h @@ -0,0 +1,41 @@ +/* + * BGPd - Mac hash header + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __BGP_MAC_H__ +#define __BGP_MAC_H__ + +void bgp_mac_init(void); +void bgp_mac_finish(void); + +/* + * Functions to add/delete the mac entry from the appropriate + * bgp hash's. Additionally to do some additional processing + * to allow the win/loss to be processed. + */ +void bgp_mac_add_mac_entry(struct interface *ifp); +void bgp_mac_del_mac_entry(struct interface *ifp); + +void bgp_mac_dump_table(struct vty *vty); + +/* + * Function to lookup the prefix and see if we have a matching mac + */ +bool bgp_mac_entry_exists(struct prefix *p); + +#endif diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 31cd3d1f05..3561674700 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -66,6 +66,7 @@ #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_label.h" #include "bgpd/bgp_addpath.h" +#include "bgpd/bgp_mac.h" #if ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -3057,6 +3058,11 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, goto filtered; } + if (bgp_mac_entry_exists(p)) { + reason = "self mac;"; + goto filtered; + } + attr_new = bgp_attr_intern(&new_attr); /* If the update is implicit withdraw. */ @@ -3778,16 +3784,22 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, bgp_node_get_bgp_path_info(rn); uint32_t num_labels = 0; mpls_label_t *label_pnt = NULL; + struct bgp_route_evpn evpn; if (pi && pi->extra) num_labels = pi->extra->num_labels; if (num_labels) label_pnt = &pi->extra->label[0]; + if (pi) + memcpy(&evpn, &pi->attr->evpn_overlay, + sizeof(evpn)); + else + memset(&evpn, 0, sizeof(evpn)); ret = bgp_update(peer, &rn->p, ain->addpath_rx_id, ain->attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, label_pnt, - num_labels, 1, NULL); + num_labels, 1, &evpn); if (ret < 0) { bgp_unlock_node(rn); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 2a4421aa54..cf1421f5a6 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -37,6 +37,7 @@ #include "frrstr.h" #include "bgpd/bgpd.h" +#include "bgpd/bgp_attr_evpn.h" #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_aspath.h" @@ -62,6 +63,7 @@ #include "bgpd/bgp_io.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_addpath.h" +#include "bgpd/bgp_mac.h" static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); @@ -7244,13 +7246,15 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name, /* one clear bgp command to rule them all */ DEFUN (clear_ip_bgp_all, clear_ip_bgp_all_cmd, - "clear [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] <*|A.B.C.D|X:X::X:X|WORD|(1-4294967295)|external|peer-group WORD> [<soft [<in|out>]|in [prefix-filter]|out>]", + "clear [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6|l2vpn> [<unicast|multicast|vpn|labeled-unicast|flowspec|evpn>]] <*|A.B.C.D|X:X::X:X|WORD|(1-4294967295)|external|peer-group WORD> [<soft [<in|out>]|in [prefix-filter]|out>]", CLEAR_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR + "Address Family\n" BGP_SAFI_WITH_LABEL_HELP_STR + "Address Family modifier\n" "Clear all peers\n" "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" @@ -7574,6 +7578,18 @@ DEFUN (show_bgp_vrfs, return CMD_SUCCESS; } +DEFUN (show_bgp_mac_hash, + show_bgp_mac_hash_cmd, + "show bgp mac hash", + SHOW_STR + BGP_STR + "Mac Address\n" + "Mac Address database\n") +{ + bgp_mac_dump_table(vty); + + return CMD_SUCCESS; +} static void show_tip_entry(struct hash_backet *backet, void *args) { @@ -12999,6 +13015,8 @@ void bgp_vty_init(void) &neighbor_soft_reconfiguration_cmd); install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element(BGP_EVPN_NODE, &neighbor_soft_reconfiguration_cmd); + install_element(BGP_EVPN_NODE, &no_neighbor_soft_reconfiguration_cmd); /* "neighbor attribute-unchanged" commands. */ install_element(BGP_NODE, &neighbor_attr_unchanged_hidden_cmd); @@ -13832,6 +13850,8 @@ void bgp_vty_init(void) /* "show bgp martian next-hop" */ install_element(VIEW_NODE, &show_bgp_martian_nexthop_db_cmd); + install_element(VIEW_NODE, &show_bgp_mac_hash_cmd); + /* "show [ip] bgp views" commands. */ install_element(VIEW_NODE, &show_bgp_views_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 66d3333739..3c4b219466 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -59,6 +59,7 @@ #include "bgpd/bgp_labelpool.h" #include "bgpd/bgp_pbr.h" #include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_mac.h" /* All information about zebra. */ struct zclient *zclient = NULL; @@ -221,6 +222,8 @@ static int bgp_interface_add(int command, struct zclient *zclient, if (!bgp) return 0; + bgp_mac_add_mac_entry(ifp); + bgp_update_interface_nbrs(bgp, ifp, ifp); return 0; } @@ -245,6 +248,8 @@ static int bgp_interface_delete(int command, struct zclient *zclient, if (bgp) bgp_update_interface_nbrs(bgp, ifp, NULL); + bgp_mac_del_mac_entry(ifp); + if_set_index(ifp, IFINDEX_INTERNAL); return 0; } @@ -267,6 +272,8 @@ static int bgp_interface_up(int command, struct zclient *zclient, if (!ifp) return 0; + bgp_mac_add_mac_entry(ifp); + if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("Rx Intf up VRF %u IF %s", vrf_id, ifp->name); @@ -300,6 +307,8 @@ static int bgp_interface_down(int command, struct zclient *zclient, if (!ifp) return 0; + bgp_mac_del_mac_entry(ifp); + if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("Rx Intf down VRF %u IF %s", vrf_id, ifp->name); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 7a3afbd18d..5b8ceb0541 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -87,6 +87,7 @@ #include "bgpd/bgp_pbr.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_mac.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information"); @@ -7775,6 +7776,7 @@ void bgp_master_init(struct thread_master *master) bgp_process_queue_init(); + bgp_mac_init(); /* init the rd id space. assign 0th index in the bitfield, so that we start with id 1 @@ -7964,4 +7966,5 @@ void bgp_terminate(void) if (bm->t_rmap_update) BGP_TIMER_OFF(bm->t_rmap_update); + bgp_mac_finish(); } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index f28ca9fa0b..484fc105e8 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -122,6 +122,9 @@ struct bgp_master { /* Listener address */ char *address; + /* The Mac table */ + struct hash *self_mac_hash; + /* BGP start time. */ time_t start_time; diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 7d7d3ca189..7dd1d73f69 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -72,6 +72,7 @@ bgpd_libbgp_a_SOURCES = \ bgpd/bgp_label.c \ bgpd/bgp_labelpool.c \ bgpd/bgp_lcommunity.c \ + bgpd/bgp_mac.c \ bgpd/bgp_memory.c \ bgpd/bgp_mpath.c \ bgpd/bgp_mplsvpn.c \ @@ -145,6 +146,7 @@ noinst_HEADERS += \ bgpd/bgp_label.h \ bgpd/bgp_labelpool.h \ bgpd/bgp_lcommunity.h \ + bgpd/bgp_mac.h \ bgpd/bgp_memory.h \ bgpd/bgp_mpath.h \ bgpd/bgp_mplsvpn.h \ |
