]>
Commit | Line | Data |
---|---|---|
48ecf8f5 DS |
1 | /* |
2 | * BGPd - Mac hash code | |
3 | * Copyright (C) 2018 Cumulus Networks, Inc. | |
4 | * Donald Sharp | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the Free | |
8 | * Software Foundation; either version 2 of the License, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; see the file COPYING; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | #include <zebra.h> | |
21 | ||
22 | #include <jhash.h> | |
23 | #include <hash.h> | |
24 | #include <prefix.h> | |
25 | #include <memory.h> | |
26 | ||
27 | #include "bgpd/bgpd.h" | |
28 | #include "bgpd/bgp_mac.h" | |
29 | #include "bgpd/bgp_memory.h" | |
30 | #include "bgpd/bgp_route.h" | |
31 | #include "bgpd/bgp_packet.h" | |
c869b3f8 | 32 | #include "bgpd/bgp_rd.h" |
48ecf8f5 | 33 | #include "bgpd/bgp_debug.h" |
4e802e66 | 34 | #include "bgpd/bgp_evpn_private.h" |
48ecf8f5 DS |
35 | |
36 | DEFINE_MTYPE_STATIC(BGPD, BSM, "Mac Hash Entry"); | |
2a99175f | 37 | DEFINE_MTYPE_STATIC(BGPD, BSM_STRING, "Mac Hash Entry Intf String"); |
48ecf8f5 DS |
38 | |
39 | struct bgp_self_mac { | |
40 | struct ethaddr macaddr; | |
41 | struct list *ifp_list; | |
42 | }; | |
43 | ||
d8b87afe | 44 | static unsigned int bgp_mac_hash_key_make(const void *data) |
48ecf8f5 | 45 | { |
d8b87afe | 46 | const struct bgp_self_mac *bsm = data; |
48ecf8f5 DS |
47 | |
48 | return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead); | |
49 | } | |
50 | ||
51 | static bool bgp_mac_hash_cmp(const void *d1, const void *d2) | |
52 | { | |
53 | const struct bgp_self_mac *bsm1 = d1; | |
54 | const struct bgp_self_mac *bsm2 = d2; | |
55 | ||
56 | if (memcmp(&bsm1->macaddr, &bsm2->macaddr, ETH_ALEN) == 0) | |
57 | return true; | |
58 | ||
59 | return false; | |
60 | } | |
61 | ||
62 | void bgp_mac_init(void) | |
63 | { | |
64 | bm->self_mac_hash = hash_create(bgp_mac_hash_key_make, bgp_mac_hash_cmp, | |
65 | "BGP MAC Hash"); | |
66 | } | |
67 | ||
68 | static void bgp_mac_hash_free(void *data) | |
69 | { | |
70 | struct bgp_self_mac *bsm = data; | |
71 | ||
4e802e66 DS |
72 | if (bsm->ifp_list) |
73 | list_delete(&bsm->ifp_list); | |
74 | ||
48ecf8f5 DS |
75 | XFREE(MTYPE_BSM, bsm); |
76 | } | |
77 | ||
78 | void bgp_mac_finish(void) | |
79 | { | |
80 | hash_clean(bm->self_mac_hash, bgp_mac_hash_free); | |
81 | hash_free(bm->self_mac_hash); | |
82 | } | |
83 | ||
6a69ac51 DS |
84 | static void bgp_mac_hash_interface_string_del(void *val) |
85 | { | |
86 | char *data = val; | |
87 | ||
88 | XFREE(MTYPE_BSM_STRING, data); | |
89 | } | |
90 | ||
91 | static void *bgp_mac_hash_alloc(void *p) | |
92 | { | |
93 | const struct bgp_self_mac *orig = p; | |
94 | struct bgp_self_mac *bsm; | |
95 | ||
96 | bsm = XCALLOC(MTYPE_BSM, sizeof(struct bgp_self_mac)); | |
97 | memcpy(&bsm->macaddr, &orig->macaddr, ETH_ALEN); | |
98 | ||
99 | bsm->ifp_list = list_new(); | |
100 | bsm->ifp_list->del = bgp_mac_hash_interface_string_del; | |
101 | ||
102 | return bsm; | |
103 | } | |
104 | ||
105 | struct bgp_mac_find_internal { | |
106 | struct bgp_self_mac *bsm; | |
107 | const char *ifname; | |
108 | }; | |
109 | ||
e3b78da8 | 110 | static void bgp_mac_find_ifp_internal(struct hash_bucket *bucket, void *arg) |
6a69ac51 DS |
111 | { |
112 | struct bgp_mac_find_internal *bmfi = arg; | |
e3b78da8 | 113 | struct bgp_self_mac *bsm = bucket->data; |
6a69ac51 DS |
114 | struct listnode *node; |
115 | char *name; | |
116 | ||
117 | for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) { | |
118 | if (strcmp(name, bmfi->ifname) == 0) { | |
119 | bmfi->bsm = bsm; | |
120 | return; | |
121 | } | |
122 | } | |
123 | } | |
124 | ||
125 | static struct bgp_self_mac *bgp_mac_find_interface_name(const char *ifname) | |
126 | { | |
127 | struct bgp_mac_find_internal bmfi; | |
128 | ||
129 | bmfi.bsm = NULL; | |
130 | bmfi.ifname = ifname; | |
131 | hash_iterate(bm->self_mac_hash, bgp_mac_find_ifp_internal, &bmfi); | |
132 | ||
133 | return bmfi.bsm; | |
134 | } | |
135 | ||
4e802e66 | 136 | static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, |
78869ebf DS |
137 | struct bgp_table *table, |
138 | struct ethaddr *macaddr) | |
4e802e66 | 139 | { |
9bcb3eef | 140 | struct bgp_dest *pdest, *dest; |
4e802e66 | 141 | struct bgp_path_info *pi; |
4e802e66 | 142 | |
9bcb3eef DS |
143 | for (pdest = bgp_table_top(table); pdest; |
144 | pdest = bgp_route_next(pdest)) { | |
145 | struct bgp_table *sub = pdest->info; | |
146 | const struct prefix *pdest_p = bgp_dest_get_prefix(pdest); | |
4e802e66 DS |
147 | |
148 | if (!sub) | |
149 | continue; | |
150 | ||
9bcb3eef DS |
151 | for (dest = bgp_table_top(sub); dest; |
152 | dest = bgp_route_next(dest)) { | |
153 | bool dest_affected; | |
154 | const struct prefix *p = bgp_dest_get_prefix(dest); | |
155 | struct prefix_evpn *pevpn = (struct prefix_evpn *)dest; | |
4e802e66 DS |
156 | struct prefix_rd prd; |
157 | uint32_t num_labels = 0; | |
158 | mpls_label_t *label_pnt = NULL; | |
6c924775 | 159 | struct bgp_route_evpn *evpn; |
4e802e66 | 160 | |
9bcb3eef DS |
161 | if (pevpn->family == AF_EVPN |
162 | && pevpn->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE | |
163 | && memcmp(&p->u.prefix_evpn.macip_addr.mac, macaddr, | |
164 | ETH_ALEN) | |
165 | == 0) | |
166 | dest_affected = true; | |
78869ebf | 167 | else |
9bcb3eef | 168 | dest_affected = false; |
78869ebf | 169 | |
9bcb3eef | 170 | for (pi = dest->info; pi; pi = pi->next) { |
4e802e66 DS |
171 | if (pi->peer == peer) |
172 | break; | |
173 | } | |
174 | ||
175 | if (!pi) | |
176 | continue; | |
177 | ||
78869ebf DS |
178 | /* |
179 | * If the mac address is not the same then | |
180 | * we don't care and since we are looking | |
181 | */ | |
9bcb3eef DS |
182 | if ((memcmp(&pi->attr->rmac, macaddr, ETH_ALEN) != 0) |
183 | && !dest_affected) | |
78869ebf DS |
184 | continue; |
185 | ||
4e802e66 DS |
186 | if (pi->extra) |
187 | num_labels = pi->extra->num_labels; | |
188 | if (num_labels) | |
189 | label_pnt = &pi->extra->label[0]; | |
190 | ||
191 | prd.family = AF_UNSPEC; | |
192 | prd.prefixlen = 64; | |
9bcb3eef | 193 | memcpy(&prd.val, pdest_p->u.val, 8); |
4e802e66 | 194 | |
c869b3f8 | 195 | if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { |
b54892e0 | 196 | if (bgp_debug_update(peer, p, NULL, 1)) { |
c869b3f8 CS |
197 | char pfx_buf[BGP_PRD_PATH_STRLEN]; |
198 | ||
199 | bgp_debug_rdpfxpath2str( | |
200 | AFI_L2VPN, SAFI_EVPN, &prd, | |
b54892e0 | 201 | p, label_pnt, num_labels, |
c869b3f8 | 202 | pi->addpath_rx_id ? 1 : 0, |
6c995628 AD |
203 | pi->addpath_rx_id, NULL, |
204 | pfx_buf, sizeof(pfx_buf)); | |
c869b3f8 CS |
205 | zlog_debug( |
206 | "%s skip update of %s marked as removed", | |
207 | peer->host, pfx_buf); | |
208 | } | |
209 | continue; | |
210 | } | |
211 | ||
6c924775 DS |
212 | memcpy(&evpn, bgp_attr_get_evpn_overlay(pi->attr), |
213 | sizeof(evpn)); | |
b54892e0 | 214 | int32_t ret = bgp_update(peer, p, |
4e802e66 DS |
215 | pi->addpath_rx_id, |
216 | pi->attr, AFI_L2VPN, SAFI_EVPN, | |
217 | ZEBRA_ROUTE_BGP, | |
218 | BGP_ROUTE_NORMAL, &prd, | |
219 | label_pnt, num_labels, | |
6c924775 | 220 | 1, evpn); |
4e802e66 DS |
221 | |
222 | if (ret < 0) | |
9bcb3eef | 223 | bgp_dest_unlock_node(dest); |
4e802e66 DS |
224 | } |
225 | } | |
226 | } | |
227 | ||
78869ebf | 228 | static void bgp_mac_rescan_evpn_table(struct bgp *bgp, struct ethaddr *macaddr) |
6a69ac51 DS |
229 | { |
230 | struct listnode *node; | |
4e802e66 DS |
231 | struct peer *peer; |
232 | safi_t safi; | |
233 | afi_t afi; | |
234 | ||
235 | afi = AFI_L2VPN; | |
236 | safi = SAFI_EVPN; | |
237 | for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { | |
238 | ||
239 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) | |
240 | continue; | |
241 | ||
feb17238 | 242 | if (!peer_established(peer)) |
4e802e66 DS |
243 | continue; |
244 | ||
245 | if (CHECK_FLAG(peer->af_flags[afi][safi], | |
246 | PEER_FLAG_SOFT_RECONFIG)) { | |
247 | if (bgp_debug_update(peer, NULL, NULL, 1)) | |
248 | zlog_debug("Processing EVPN MAC interface change on peer %s (inbound, soft-reconfig)", | |
249 | peer->host); | |
250 | ||
251 | bgp_soft_reconfig_in(peer, afi, safi); | |
252 | } else { | |
253 | struct bgp_table *table = bgp->rib[afi][safi]; | |
254 | ||
255 | if (bgp_debug_update(peer, NULL, NULL, 1)) | |
256 | zlog_debug("Processing EVPN MAC interface change on peer %s", | |
257 | peer->host); | |
78869ebf | 258 | bgp_process_mac_rescan_table(bgp, peer, table, macaddr); |
4e802e66 DS |
259 | } |
260 | } | |
261 | } | |
262 | ||
78869ebf | 263 | static void bgp_mac_rescan_all_evpn_tables(struct ethaddr *macaddr) |
4e802e66 DS |
264 | { |
265 | struct listnode *node; | |
266 | struct bgp *bgp; | |
267 | ||
268 | for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { | |
269 | struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN]; | |
270 | ||
271 | if (table) | |
78869ebf | 272 | bgp_mac_rescan_evpn_table(bgp, macaddr); |
4e802e66 DS |
273 | } |
274 | } | |
275 | ||
78869ebf DS |
276 | static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname, |
277 | struct ethaddr *macaddr) | |
4e802e66 DS |
278 | { |
279 | struct listnode *node = NULL; | |
6a69ac51 DS |
280 | char *name; |
281 | ||
282 | for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) { | |
283 | if (strcmp(name, ifname) == 0) | |
284 | break; | |
285 | } | |
286 | ||
287 | if (node) { | |
288 | list_delete_node(bsm->ifp_list, node); | |
289 | XFREE(MTYPE_BSM_STRING, name); | |
290 | } | |
291 | ||
292 | if (bsm->ifp_list->count == 0) { | |
c86e8d64 DS |
293 | struct ethaddr mac = *macaddr; |
294 | ||
6a69ac51 DS |
295 | hash_release(bm->self_mac_hash, bsm); |
296 | list_delete(&bsm->ifp_list); | |
297 | XFREE(MTYPE_BSM, bsm); | |
298 | ||
c86e8d64 | 299 | bgp_mac_rescan_all_evpn_tables(&mac); |
6a69ac51 DS |
300 | } |
301 | } | |
302 | ||
303 | void bgp_mac_add_mac_entry(struct interface *ifp) | |
304 | { | |
305 | struct bgp_self_mac lookup; | |
306 | struct bgp_self_mac *bsm; | |
307 | struct bgp_self_mac *old_bsm; | |
308 | char *ifname; | |
309 | ||
310 | memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); | |
311 | bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc); | |
312 | ||
313 | /* | |
314 | * Does this happen to be a move | |
315 | */ | |
316 | old_bsm = bgp_mac_find_interface_name(ifp->name); | |
317 | ifname = XSTRDUP(MTYPE_BSM_STRING, ifp->name); | |
318 | ||
319 | if (bsm->ifp_list->count == 0) { | |
320 | ||
321 | listnode_add(bsm->ifp_list, ifname); | |
322 | if (old_bsm) | |
78869ebf DS |
323 | bgp_mac_remove_ifp_internal(old_bsm, ifname, |
324 | &old_bsm->macaddr); | |
6a69ac51 DS |
325 | } else { |
326 | /* | |
327 | * If old mac address is the same as the new, | |
328 | * then there is nothing to do here | |
329 | */ | |
308000ee DS |
330 | if (old_bsm == bsm) { |
331 | XFREE(MTYPE_BSM_STRING, ifname); | |
6a69ac51 | 332 | return; |
308000ee | 333 | } |
6a69ac51 DS |
334 | |
335 | if (old_bsm) | |
78869ebf DS |
336 | bgp_mac_remove_ifp_internal(old_bsm, ifp->name, |
337 | &old_bsm->macaddr); | |
6a69ac51 DS |
338 | |
339 | listnode_add(bsm->ifp_list, ifname); | |
340 | } | |
341 | ||
78869ebf | 342 | bgp_mac_rescan_all_evpn_tables(&bsm->macaddr); |
6a69ac51 DS |
343 | } |
344 | ||
345 | void bgp_mac_del_mac_entry(struct interface *ifp) | |
346 | { | |
347 | struct bgp_self_mac lookup; | |
348 | struct bgp_self_mac *bsm; | |
349 | ||
350 | memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); | |
351 | bsm = hash_lookup(bm->self_mac_hash, &lookup); | |
352 | if (!bsm) | |
353 | return; | |
354 | ||
355 | /* | |
356 | * Write code to allow old mac address to no-longer | |
357 | * win if we happen to have received it from a peer. | |
358 | */ | |
78869ebf | 359 | bgp_mac_remove_ifp_internal(bsm, ifp->name, &bsm->macaddr); |
6a69ac51 DS |
360 | } |
361 | ||
eee353c5 CS |
362 | /* This API checks MAC address against any of local |
363 | * assigned (SVIs) MAC address. | |
364 | * An example: router-mac attribute in any of evpn update | |
365 | * requires to compare against local mac. | |
366 | */ | |
5a1ae2c2 | 367 | bool bgp_mac_exist(const struct ethaddr *mac) |
4e802e66 | 368 | { |
4e802e66 DS |
369 | struct bgp_self_mac lookup; |
370 | struct bgp_self_mac *bsm; | |
eee353c5 CS |
371 | static uint8_t tmp [ETHER_ADDR_STRLEN] = {0}; |
372 | ||
373 | if (memcmp(mac, &tmp, ETH_ALEN) == 0) | |
374 | return false; | |
375 | ||
376 | memcpy(&lookup.macaddr, mac, ETH_ALEN); | |
377 | bsm = hash_lookup(bm->self_mac_hash, &lookup); | |
378 | if (!bsm) | |
379 | return false; | |
380 | ||
381 | return true; | |
382 | } | |
383 | ||
384 | /* This API checks EVPN type-2 prefix and comapares | |
385 | * mac against any of local assigned (SVIs) MAC | |
386 | * address. | |
387 | */ | |
5a1ae2c2 | 388 | bool bgp_mac_entry_exists(const struct prefix *p) |
eee353c5 | 389 | { |
5a1ae2c2 | 390 | const struct prefix_evpn *pevpn = (const struct prefix_evpn *)p; |
4e802e66 DS |
391 | |
392 | if (pevpn->family != AF_EVPN) | |
393 | return false; | |
394 | ||
395 | if (pevpn->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) | |
396 | return false; | |
397 | ||
eee353c5 | 398 | return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac); |
4e802e66 DS |
399 | |
400 | return true; | |
401 | } | |
402 | ||
e3b78da8 | 403 | static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg) |
48ecf8f5 DS |
404 | { |
405 | struct vty *vty = arg; | |
e3b78da8 | 406 | struct bgp_self_mac *bsm = bucket->data; |
48ecf8f5 DS |
407 | struct listnode *node; |
408 | char *name; | |
409 | char buf_mac[ETHER_ADDR_STRLEN]; | |
410 | ||
411 | vty_out(vty, "Mac Address: %s ", | |
412 | prefix_mac2str(&bsm->macaddr, buf_mac, sizeof(buf_mac))); | |
413 | ||
414 | for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) | |
415 | vty_out(vty, "%s ", name); | |
416 | ||
417 | vty_out(vty, "\n"); | |
418 | } | |
419 | ||
420 | void bgp_mac_dump_table(struct vty *vty) | |
421 | { | |
422 | hash_iterate(bm->self_mac_hash, bgp_mac_show_mac_entry, vty); | |
423 | } |