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