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