1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2018 Cumulus Networks, Inc.
14 #include "bgpd/bgpd.h"
15 #include "bgpd/bgp_mac.h"
16 #include "bgpd/bgp_memory.h"
17 #include "bgpd/bgp_route.h"
18 #include "bgpd/bgp_packet.h"
19 #include "bgpd/bgp_rd.h"
20 #include "bgpd/bgp_debug.h"
21 #include "bgpd/bgp_evpn_private.h"
23 DEFINE_MTYPE_STATIC(BGPD
, BSM
, "Mac Hash Entry");
24 DEFINE_MTYPE_STATIC(BGPD
, BSM_STRING
, "Mac Hash Entry Intf String");
27 struct ethaddr macaddr
;
28 struct list
*ifp_list
;
31 static unsigned int bgp_mac_hash_key_make(const void *data
)
33 const struct bgp_self_mac
*bsm
= data
;
35 return jhash(&bsm
->macaddr
, ETH_ALEN
, 0xa5a5dead);
38 static bool bgp_mac_hash_cmp(const void *d1
, const void *d2
)
40 const struct bgp_self_mac
*bsm1
= d1
;
41 const struct bgp_self_mac
*bsm2
= d2
;
43 if (memcmp(&bsm1
->macaddr
, &bsm2
->macaddr
, ETH_ALEN
) == 0)
49 void bgp_mac_init(void)
51 bm
->self_mac_hash
= hash_create(bgp_mac_hash_key_make
, bgp_mac_hash_cmp
,
55 static void bgp_mac_hash_free(void *data
)
57 struct bgp_self_mac
*bsm
= data
;
60 list_delete(&bsm
->ifp_list
);
62 XFREE(MTYPE_BSM
, bsm
);
65 void bgp_mac_finish(void)
67 hash_clean(bm
->self_mac_hash
, bgp_mac_hash_free
);
68 hash_free(bm
->self_mac_hash
);
71 static void bgp_mac_hash_interface_string_del(void *val
)
75 XFREE(MTYPE_BSM_STRING
, data
);
78 static void *bgp_mac_hash_alloc(void *p
)
80 const struct bgp_self_mac
*orig
= p
;
81 struct bgp_self_mac
*bsm
;
83 bsm
= XCALLOC(MTYPE_BSM
, sizeof(struct bgp_self_mac
));
84 memcpy(&bsm
->macaddr
, &orig
->macaddr
, ETH_ALEN
);
86 bsm
->ifp_list
= list_new();
87 bsm
->ifp_list
->del
= bgp_mac_hash_interface_string_del
;
92 struct bgp_mac_find_internal
{
93 struct bgp_self_mac
*bsm
;
97 static void bgp_mac_find_ifp_internal(struct hash_bucket
*bucket
, void *arg
)
99 struct bgp_mac_find_internal
*bmfi
= arg
;
100 struct bgp_self_mac
*bsm
= bucket
->data
;
101 struct listnode
*node
;
104 for (ALL_LIST_ELEMENTS_RO(bsm
->ifp_list
, node
, name
)) {
105 if (strcmp(name
, bmfi
->ifname
) == 0) {
112 static struct bgp_self_mac
*bgp_mac_find_interface_name(const char *ifname
)
114 struct bgp_mac_find_internal bmfi
;
117 bmfi
.ifname
= ifname
;
118 hash_iterate(bm
->self_mac_hash
, bgp_mac_find_ifp_internal
, &bmfi
);
123 static void bgp_process_mac_rescan_table(struct bgp
*bgp
, struct peer
*peer
,
124 struct bgp_table
*table
,
125 struct ethaddr
*macaddr
)
127 struct bgp_dest
*pdest
, *dest
;
128 struct bgp_path_info
*pi
;
130 for (pdest
= bgp_table_top(table
); pdest
;
131 pdest
= bgp_route_next(pdest
)) {
132 struct bgp_table
*sub
= pdest
->info
;
133 const struct prefix
*pdest_p
= bgp_dest_get_prefix(pdest
);
138 for (dest
= bgp_table_top(sub
); dest
;
139 dest
= bgp_route_next(dest
)) {
141 const struct prefix
*p
= bgp_dest_get_prefix(dest
);
142 struct prefix_evpn
*pevpn
= (struct prefix_evpn
*)dest
;
143 struct prefix_rd prd
;
144 uint32_t num_labels
= 0;
145 mpls_label_t
*label_pnt
= NULL
;
146 struct bgp_route_evpn
*evpn
;
148 if (pevpn
->family
== AF_EVPN
149 && pevpn
->prefix
.route_type
== BGP_EVPN_MAC_IP_ROUTE
150 && memcmp(&p
->u
.prefix_evpn
.macip_addr
.mac
, macaddr
,
153 dest_affected
= true;
155 dest_affected
= false;
157 for (pi
= dest
->info
; pi
; pi
= pi
->next
) {
158 if (pi
->peer
== peer
)
166 * If the mac address is not the same then
167 * we don't care and since we are looking
169 if ((memcmp(&pi
->attr
->rmac
, macaddr
, ETH_ALEN
) != 0)
174 num_labels
= pi
->extra
->num_labels
;
176 label_pnt
= &pi
->extra
->label
[0];
178 prd
.family
= AF_UNSPEC
;
180 memcpy(&prd
.val
, pdest_p
->u
.val
, 8);
182 if (CHECK_FLAG(pi
->flags
, BGP_PATH_REMOVED
)) {
183 if (bgp_debug_update(peer
, p
, NULL
, 1)) {
184 char pfx_buf
[BGP_PRD_PATH_STRLEN
];
186 bgp_debug_rdpfxpath2str(
187 AFI_L2VPN
, SAFI_EVPN
, &prd
,
188 p
, label_pnt
, num_labels
,
189 pi
->addpath_rx_id
? 1 : 0,
190 pi
->addpath_rx_id
, NULL
,
191 pfx_buf
, sizeof(pfx_buf
));
193 "%s skip update of %s marked as removed",
194 peer
->host
, pfx_buf
);
199 memcpy(&evpn
, bgp_attr_get_evpn_overlay(pi
->attr
),
201 bgp_update(peer
, p
, pi
->addpath_rx_id
, pi
->attr
,
202 AFI_L2VPN
, SAFI_EVPN
, ZEBRA_ROUTE_BGP
,
203 BGP_ROUTE_NORMAL
, &prd
, label_pnt
,
204 num_labels
, 1, evpn
);
209 static void bgp_mac_rescan_evpn_table(struct bgp
*bgp
, struct ethaddr
*macaddr
)
211 struct listnode
*node
;
218 for (ALL_LIST_ELEMENTS_RO(bgp
->peer
, node
, peer
)) {
220 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
223 if (!peer_established(peer
))
226 if (bgp_debug_update(peer
, NULL
, NULL
, 1))
228 "Processing EVPN MAC interface change on peer %s %s",
230 CHECK_FLAG(peer
->af_flags
[afi
][safi
],
231 PEER_FLAG_SOFT_RECONFIG
)
232 ? "(inbound, soft-reconfig)"
235 if (!bgp_soft_reconfig_in(peer
, afi
, safi
)) {
236 struct bgp_table
*table
= bgp
->rib
[afi
][safi
];
238 bgp_process_mac_rescan_table(bgp
, peer
, table
, macaddr
);
243 static void bgp_mac_rescan_all_evpn_tables(struct ethaddr
*macaddr
)
245 struct listnode
*node
;
248 for (ALL_LIST_ELEMENTS_RO(bm
->bgp
, node
, bgp
)) {
249 struct bgp_table
*table
= bgp
->rib
[AFI_L2VPN
][SAFI_EVPN
];
252 bgp_mac_rescan_evpn_table(bgp
, macaddr
);
256 static void bgp_mac_remove_ifp_internal(struct bgp_self_mac
*bsm
, char *ifname
,
257 struct ethaddr
*macaddr
)
259 struct listnode
*node
= NULL
;
262 for (ALL_LIST_ELEMENTS_RO(bsm
->ifp_list
, node
, name
)) {
263 if (strcmp(name
, ifname
) == 0)
268 list_delete_node(bsm
->ifp_list
, node
);
269 XFREE(MTYPE_BSM_STRING
, name
);
272 if (bsm
->ifp_list
->count
== 0) {
273 struct ethaddr mac
= *macaddr
;
275 hash_release(bm
->self_mac_hash
, bsm
);
276 list_delete(&bsm
->ifp_list
);
277 XFREE(MTYPE_BSM
, bsm
);
279 bgp_mac_rescan_all_evpn_tables(&mac
);
283 void bgp_mac_add_mac_entry(struct interface
*ifp
)
285 struct bgp_self_mac lookup
;
286 struct bgp_self_mac
*bsm
;
287 struct bgp_self_mac
*old_bsm
;
290 memcpy(&lookup
.macaddr
, &ifp
->hw_addr
, ETH_ALEN
);
291 bsm
= hash_get(bm
->self_mac_hash
, &lookup
, bgp_mac_hash_alloc
);
294 * Does this happen to be a move
296 old_bsm
= bgp_mac_find_interface_name(ifp
->name
);
297 ifname
= XSTRDUP(MTYPE_BSM_STRING
, ifp
->name
);
299 if (bsm
->ifp_list
->count
== 0) {
301 listnode_add(bsm
->ifp_list
, ifname
);
303 bgp_mac_remove_ifp_internal(old_bsm
, ifname
,
307 * If old mac address is the same as the new,
308 * then there is nothing to do here
310 if (old_bsm
== bsm
) {
311 XFREE(MTYPE_BSM_STRING
, ifname
);
316 bgp_mac_remove_ifp_internal(old_bsm
, ifp
->name
,
319 listnode_add(bsm
->ifp_list
, ifname
);
322 bgp_mac_rescan_all_evpn_tables(&bsm
->macaddr
);
325 void bgp_mac_del_mac_entry(struct interface
*ifp
)
327 struct bgp_self_mac lookup
;
328 struct bgp_self_mac
*bsm
;
330 memcpy(&lookup
.macaddr
, &ifp
->hw_addr
, ETH_ALEN
);
331 bsm
= hash_lookup(bm
->self_mac_hash
, &lookup
);
336 * Write code to allow old mac address to no-longer
337 * win if we happen to have received it from a peer.
339 bgp_mac_remove_ifp_internal(bsm
, ifp
->name
, &bsm
->macaddr
);
342 /* This API checks MAC address against any of local
343 * assigned (SVIs) MAC address.
344 * An example: router-mac attribute in any of evpn update
345 * requires to compare against local mac.
347 bool bgp_mac_exist(const struct ethaddr
*mac
)
349 struct bgp_self_mac lookup
;
350 struct bgp_self_mac
*bsm
;
351 static uint8_t tmp
[ETHER_ADDR_STRLEN
] = {0};
353 if (memcmp(mac
, &tmp
, ETH_ALEN
) == 0)
356 memcpy(&lookup
.macaddr
, mac
, ETH_ALEN
);
357 bsm
= hash_lookup(bm
->self_mac_hash
, &lookup
);
364 /* This API checks EVPN type-2 prefix and comapares
365 * mac against any of local assigned (SVIs) MAC
368 bool bgp_mac_entry_exists(const struct prefix
*p
)
370 const struct prefix_evpn
*pevpn
= (const struct prefix_evpn
*)p
;
372 if (pevpn
->family
!= AF_EVPN
)
375 if (pevpn
->prefix
.route_type
!= BGP_EVPN_MAC_IP_ROUTE
)
378 return bgp_mac_exist(&p
->u
.prefix_evpn
.macip_addr
.mac
);
383 static void bgp_mac_show_mac_entry(struct hash_bucket
*bucket
, void *arg
)
385 struct vty
*vty
= arg
;
386 struct bgp_self_mac
*bsm
= bucket
->data
;
387 struct listnode
*node
;
389 char buf_mac
[ETHER_ADDR_STRLEN
];
391 vty_out(vty
, "Mac Address: %s ",
392 prefix_mac2str(&bsm
->macaddr
, buf_mac
, sizeof(buf_mac
)));
394 for (ALL_LIST_ELEMENTS_RO(bsm
->ifp_list
, node
, name
))
395 vty_out(vty
, "%s ", name
);
400 void bgp_mac_dump_table(struct vty
*vty
)
402 hash_iterate(bm
->self_mac_hash
, bgp_mac_show_mac_entry
, vty
);