]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_mac.c
Merge pull request #12780 from opensourcerouting/spdx-license-id
[mirror_frr.git] / bgpd / bgp_mac.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * BGPd - Mac hash code
4 * Copyright (C) 2018 Cumulus Networks, Inc.
5 * Donald Sharp
6 */
7 #include <zebra.h>
8
9 #include <jhash.h>
10 #include <hash.h>
11 #include <prefix.h>
12 #include <memory.h>
13
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"
22
23 DEFINE_MTYPE_STATIC(BGPD, BSM, "Mac Hash Entry");
24 DEFINE_MTYPE_STATIC(BGPD, BSM_STRING, "Mac Hash Entry Intf String");
25
26 struct bgp_self_mac {
27 struct ethaddr macaddr;
28 struct list *ifp_list;
29 };
30
31 static unsigned int bgp_mac_hash_key_make(const void *data)
32 {
33 const struct bgp_self_mac *bsm = data;
34
35 return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead);
36 }
37
38 static bool bgp_mac_hash_cmp(const void *d1, const void *d2)
39 {
40 const struct bgp_self_mac *bsm1 = d1;
41 const struct bgp_self_mac *bsm2 = d2;
42
43 if (memcmp(&bsm1->macaddr, &bsm2->macaddr, ETH_ALEN) == 0)
44 return true;
45
46 return false;
47 }
48
49 void bgp_mac_init(void)
50 {
51 bm->self_mac_hash = hash_create(bgp_mac_hash_key_make, bgp_mac_hash_cmp,
52 "BGP MAC Hash");
53 }
54
55 static void bgp_mac_hash_free(void *data)
56 {
57 struct bgp_self_mac *bsm = data;
58
59 if (bsm->ifp_list)
60 list_delete(&bsm->ifp_list);
61
62 XFREE(MTYPE_BSM, bsm);
63 }
64
65 void bgp_mac_finish(void)
66 {
67 hash_clean(bm->self_mac_hash, bgp_mac_hash_free);
68 hash_free(bm->self_mac_hash);
69 }
70
71 static void bgp_mac_hash_interface_string_del(void *val)
72 {
73 char *data = val;
74
75 XFREE(MTYPE_BSM_STRING, data);
76 }
77
78 static void *bgp_mac_hash_alloc(void *p)
79 {
80 const struct bgp_self_mac *orig = p;
81 struct bgp_self_mac *bsm;
82
83 bsm = XCALLOC(MTYPE_BSM, sizeof(struct bgp_self_mac));
84 memcpy(&bsm->macaddr, &orig->macaddr, ETH_ALEN);
85
86 bsm->ifp_list = list_new();
87 bsm->ifp_list->del = bgp_mac_hash_interface_string_del;
88
89 return bsm;
90 }
91
92 struct bgp_mac_find_internal {
93 struct bgp_self_mac *bsm;
94 const char *ifname;
95 };
96
97 static void bgp_mac_find_ifp_internal(struct hash_bucket *bucket, void *arg)
98 {
99 struct bgp_mac_find_internal *bmfi = arg;
100 struct bgp_self_mac *bsm = bucket->data;
101 struct listnode *node;
102 char *name;
103
104 for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) {
105 if (strcmp(name, bmfi->ifname) == 0) {
106 bmfi->bsm = bsm;
107 return;
108 }
109 }
110 }
111
112 static struct bgp_self_mac *bgp_mac_find_interface_name(const char *ifname)
113 {
114 struct bgp_mac_find_internal bmfi;
115
116 bmfi.bsm = NULL;
117 bmfi.ifname = ifname;
118 hash_iterate(bm->self_mac_hash, bgp_mac_find_ifp_internal, &bmfi);
119
120 return bmfi.bsm;
121 }
122
123 static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer,
124 struct bgp_table *table,
125 struct ethaddr *macaddr)
126 {
127 struct bgp_dest *pdest, *dest;
128 struct bgp_path_info *pi;
129
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);
134
135 if (!sub)
136 continue;
137
138 for (dest = bgp_table_top(sub); dest;
139 dest = bgp_route_next(dest)) {
140 bool dest_affected;
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;
147
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,
151 ETH_ALEN)
152 == 0)
153 dest_affected = true;
154 else
155 dest_affected = false;
156
157 for (pi = dest->info; pi; pi = pi->next) {
158 if (pi->peer == peer)
159 break;
160 }
161
162 if (!pi)
163 continue;
164
165 /*
166 * If the mac address is not the same then
167 * we don't care and since we are looking
168 */
169 if ((memcmp(&pi->attr->rmac, macaddr, ETH_ALEN) != 0)
170 && !dest_affected)
171 continue;
172
173 if (pi->extra)
174 num_labels = pi->extra->num_labels;
175 if (num_labels)
176 label_pnt = &pi->extra->label[0];
177
178 prd.family = AF_UNSPEC;
179 prd.prefixlen = 64;
180 memcpy(&prd.val, pdest_p->u.val, 8);
181
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];
185
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));
192 zlog_debug(
193 "%s skip update of %s marked as removed",
194 peer->host, pfx_buf);
195 }
196 continue;
197 }
198
199 memcpy(&evpn, bgp_attr_get_evpn_overlay(pi->attr),
200 sizeof(evpn));
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);
205 }
206 }
207 }
208
209 static void bgp_mac_rescan_evpn_table(struct bgp *bgp, struct ethaddr *macaddr)
210 {
211 struct listnode *node;
212 struct peer *peer;
213 safi_t safi;
214 afi_t afi;
215
216 afi = AFI_L2VPN;
217 safi = SAFI_EVPN;
218 for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
219
220 if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
221 continue;
222
223 if (!peer_established(peer))
224 continue;
225
226 if (bgp_debug_update(peer, NULL, NULL, 1))
227 zlog_debug(
228 "Processing EVPN MAC interface change on peer %s %s",
229 peer->host,
230 CHECK_FLAG(peer->af_flags[afi][safi],
231 PEER_FLAG_SOFT_RECONFIG)
232 ? "(inbound, soft-reconfig)"
233 : "");
234
235 if (!bgp_soft_reconfig_in(peer, afi, safi)) {
236 struct bgp_table *table = bgp->rib[afi][safi];
237
238 bgp_process_mac_rescan_table(bgp, peer, table, macaddr);
239 }
240 }
241 }
242
243 static void bgp_mac_rescan_all_evpn_tables(struct ethaddr *macaddr)
244 {
245 struct listnode *node;
246 struct bgp *bgp;
247
248 for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
249 struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN];
250
251 if (table)
252 bgp_mac_rescan_evpn_table(bgp, macaddr);
253 }
254 }
255
256 static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname,
257 struct ethaddr *macaddr)
258 {
259 struct listnode *node = NULL;
260 char *name;
261
262 for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) {
263 if (strcmp(name, ifname) == 0)
264 break;
265 }
266
267 if (node) {
268 list_delete_node(bsm->ifp_list, node);
269 XFREE(MTYPE_BSM_STRING, name);
270 }
271
272 if (bsm->ifp_list->count == 0) {
273 struct ethaddr mac = *macaddr;
274
275 hash_release(bm->self_mac_hash, bsm);
276 list_delete(&bsm->ifp_list);
277 XFREE(MTYPE_BSM, bsm);
278
279 bgp_mac_rescan_all_evpn_tables(&mac);
280 }
281 }
282
283 void bgp_mac_add_mac_entry(struct interface *ifp)
284 {
285 struct bgp_self_mac lookup;
286 struct bgp_self_mac *bsm;
287 struct bgp_self_mac *old_bsm;
288 char *ifname;
289
290 memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN);
291 bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc);
292
293 /*
294 * Does this happen to be a move
295 */
296 old_bsm = bgp_mac_find_interface_name(ifp->name);
297 ifname = XSTRDUP(MTYPE_BSM_STRING, ifp->name);
298
299 if (bsm->ifp_list->count == 0) {
300
301 listnode_add(bsm->ifp_list, ifname);
302 if (old_bsm)
303 bgp_mac_remove_ifp_internal(old_bsm, ifname,
304 &old_bsm->macaddr);
305 } else {
306 /*
307 * If old mac address is the same as the new,
308 * then there is nothing to do here
309 */
310 if (old_bsm == bsm) {
311 XFREE(MTYPE_BSM_STRING, ifname);
312 return;
313 }
314
315 if (old_bsm)
316 bgp_mac_remove_ifp_internal(old_bsm, ifp->name,
317 &old_bsm->macaddr);
318
319 listnode_add(bsm->ifp_list, ifname);
320 }
321
322 bgp_mac_rescan_all_evpn_tables(&bsm->macaddr);
323 }
324
325 void bgp_mac_del_mac_entry(struct interface *ifp)
326 {
327 struct bgp_self_mac lookup;
328 struct bgp_self_mac *bsm;
329
330 memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN);
331 bsm = hash_lookup(bm->self_mac_hash, &lookup);
332 if (!bsm)
333 return;
334
335 /*
336 * Write code to allow old mac address to no-longer
337 * win if we happen to have received it from a peer.
338 */
339 bgp_mac_remove_ifp_internal(bsm, ifp->name, &bsm->macaddr);
340 }
341
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.
346 */
347 bool bgp_mac_exist(const struct ethaddr *mac)
348 {
349 struct bgp_self_mac lookup;
350 struct bgp_self_mac *bsm;
351 static uint8_t tmp [ETHER_ADDR_STRLEN] = {0};
352
353 if (memcmp(mac, &tmp, ETH_ALEN) == 0)
354 return false;
355
356 memcpy(&lookup.macaddr, mac, ETH_ALEN);
357 bsm = hash_lookup(bm->self_mac_hash, &lookup);
358 if (!bsm)
359 return false;
360
361 return true;
362 }
363
364 /* This API checks EVPN type-2 prefix and comapares
365 * mac against any of local assigned (SVIs) MAC
366 * address.
367 */
368 bool bgp_mac_entry_exists(const struct prefix *p)
369 {
370 const struct prefix_evpn *pevpn = (const struct prefix_evpn *)p;
371
372 if (pevpn->family != AF_EVPN)
373 return false;
374
375 if (pevpn->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
376 return false;
377
378 return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac);
379
380 return true;
381 }
382
383 static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg)
384 {
385 struct vty *vty = arg;
386 struct bgp_self_mac *bsm = bucket->data;
387 struct listnode *node;
388 char *name;
389 char buf_mac[ETHER_ADDR_STRLEN];
390
391 vty_out(vty, "Mac Address: %s ",
392 prefix_mac2str(&bsm->macaddr, buf_mac, sizeof(buf_mac)));
393
394 for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name))
395 vty_out(vty, "%s ", name);
396
397 vty_out(vty, "\n");
398 }
399
400 void bgp_mac_dump_table(struct vty *vty)
401 {
402 hash_iterate(bm->self_mac_hash, bgp_mac_show_mac_entry, vty);
403 }