]>
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 | ||
89c73443 DS |
245 | if (bgp_debug_update(peer, NULL, NULL, 1)) |
246 | zlog_debug( | |
247 | "Processing EVPN MAC interface change on peer %s %s", | |
248 | peer->host, | |
249 | CHECK_FLAG(peer->af_flags[afi][safi], | |
250 | PEER_FLAG_SOFT_RECONFIG) | |
251 | ? "(inbound, soft-reconfig)" | |
252 | : ""); | |
253 | ||
254 | if (!bgp_soft_reconfig_in(peer, afi, safi)) { | |
4e802e66 DS |
255 | struct bgp_table *table = bgp->rib[afi][safi]; |
256 | ||
78869ebf | 257 | bgp_process_mac_rescan_table(bgp, peer, table, macaddr); |
4e802e66 DS |
258 | } |
259 | } | |
260 | } | |
261 | ||
78869ebf | 262 | static void bgp_mac_rescan_all_evpn_tables(struct ethaddr *macaddr) |
4e802e66 DS |
263 | { |
264 | struct listnode *node; | |
265 | struct bgp *bgp; | |
266 | ||
267 | for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { | |
268 | struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN]; | |
269 | ||
270 | if (table) | |
78869ebf | 271 | bgp_mac_rescan_evpn_table(bgp, macaddr); |
4e802e66 DS |
272 | } |
273 | } | |
274 | ||
78869ebf DS |
275 | static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname, |
276 | struct ethaddr *macaddr) | |
4e802e66 DS |
277 | { |
278 | struct listnode *node = NULL; | |
6a69ac51 DS |
279 | char *name; |
280 | ||
281 | for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) { | |
282 | if (strcmp(name, ifname) == 0) | |
283 | break; | |
284 | } | |
285 | ||
286 | if (node) { | |
287 | list_delete_node(bsm->ifp_list, node); | |
288 | XFREE(MTYPE_BSM_STRING, name); | |
289 | } | |
290 | ||
291 | if (bsm->ifp_list->count == 0) { | |
c86e8d64 DS |
292 | struct ethaddr mac = *macaddr; |
293 | ||
6a69ac51 DS |
294 | hash_release(bm->self_mac_hash, bsm); |
295 | list_delete(&bsm->ifp_list); | |
296 | XFREE(MTYPE_BSM, bsm); | |
297 | ||
c86e8d64 | 298 | bgp_mac_rescan_all_evpn_tables(&mac); |
6a69ac51 DS |
299 | } |
300 | } | |
301 | ||
302 | void bgp_mac_add_mac_entry(struct interface *ifp) | |
303 | { | |
304 | struct bgp_self_mac lookup; | |
305 | struct bgp_self_mac *bsm; | |
306 | struct bgp_self_mac *old_bsm; | |
307 | char *ifname; | |
308 | ||
309 | memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); | |
310 | bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc); | |
311 | ||
312 | /* | |
313 | * Does this happen to be a move | |
314 | */ | |
315 | old_bsm = bgp_mac_find_interface_name(ifp->name); | |
316 | ifname = XSTRDUP(MTYPE_BSM_STRING, ifp->name); | |
317 | ||
318 | if (bsm->ifp_list->count == 0) { | |
319 | ||
320 | listnode_add(bsm->ifp_list, ifname); | |
321 | if (old_bsm) | |
78869ebf DS |
322 | bgp_mac_remove_ifp_internal(old_bsm, ifname, |
323 | &old_bsm->macaddr); | |
6a69ac51 DS |
324 | } else { |
325 | /* | |
326 | * If old mac address is the same as the new, | |
327 | * then there is nothing to do here | |
328 | */ | |
308000ee DS |
329 | if (old_bsm == bsm) { |
330 | XFREE(MTYPE_BSM_STRING, ifname); | |
6a69ac51 | 331 | return; |
308000ee | 332 | } |
6a69ac51 DS |
333 | |
334 | if (old_bsm) | |
78869ebf DS |
335 | bgp_mac_remove_ifp_internal(old_bsm, ifp->name, |
336 | &old_bsm->macaddr); | |
6a69ac51 DS |
337 | |
338 | listnode_add(bsm->ifp_list, ifname); | |
339 | } | |
340 | ||
78869ebf | 341 | bgp_mac_rescan_all_evpn_tables(&bsm->macaddr); |
6a69ac51 DS |
342 | } |
343 | ||
344 | void bgp_mac_del_mac_entry(struct interface *ifp) | |
345 | { | |
346 | struct bgp_self_mac lookup; | |
347 | struct bgp_self_mac *bsm; | |
348 | ||
349 | memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); | |
350 | bsm = hash_lookup(bm->self_mac_hash, &lookup); | |
351 | if (!bsm) | |
352 | return; | |
353 | ||
354 | /* | |
355 | * Write code to allow old mac address to no-longer | |
356 | * win if we happen to have received it from a peer. | |
357 | */ | |
78869ebf | 358 | bgp_mac_remove_ifp_internal(bsm, ifp->name, &bsm->macaddr); |
6a69ac51 DS |
359 | } |
360 | ||
eee353c5 CS |
361 | /* This API checks MAC address against any of local |
362 | * assigned (SVIs) MAC address. | |
363 | * An example: router-mac attribute in any of evpn update | |
364 | * requires to compare against local mac. | |
365 | */ | |
5a1ae2c2 | 366 | bool bgp_mac_exist(const struct ethaddr *mac) |
4e802e66 | 367 | { |
4e802e66 DS |
368 | struct bgp_self_mac lookup; |
369 | struct bgp_self_mac *bsm; | |
eee353c5 CS |
370 | static uint8_t tmp [ETHER_ADDR_STRLEN] = {0}; |
371 | ||
372 | if (memcmp(mac, &tmp, ETH_ALEN) == 0) | |
373 | return false; | |
374 | ||
375 | memcpy(&lookup.macaddr, mac, ETH_ALEN); | |
376 | bsm = hash_lookup(bm->self_mac_hash, &lookup); | |
377 | if (!bsm) | |
378 | return false; | |
379 | ||
380 | return true; | |
381 | } | |
382 | ||
383 | /* This API checks EVPN type-2 prefix and comapares | |
384 | * mac against any of local assigned (SVIs) MAC | |
385 | * address. | |
386 | */ | |
5a1ae2c2 | 387 | bool bgp_mac_entry_exists(const struct prefix *p) |
eee353c5 | 388 | { |
5a1ae2c2 | 389 | const struct prefix_evpn *pevpn = (const struct prefix_evpn *)p; |
4e802e66 DS |
390 | |
391 | if (pevpn->family != AF_EVPN) | |
392 | return false; | |
393 | ||
394 | if (pevpn->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) | |
395 | return false; | |
396 | ||
eee353c5 | 397 | return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac); |
4e802e66 DS |
398 | |
399 | return true; | |
400 | } | |
401 | ||
e3b78da8 | 402 | static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg) |
48ecf8f5 DS |
403 | { |
404 | struct vty *vty = arg; | |
e3b78da8 | 405 | struct bgp_self_mac *bsm = bucket->data; |
48ecf8f5 DS |
406 | struct listnode *node; |
407 | char *name; | |
408 | char buf_mac[ETHER_ADDR_STRLEN]; | |
409 | ||
410 | vty_out(vty, "Mac Address: %s ", | |
411 | prefix_mac2str(&bsm->macaddr, buf_mac, sizeof(buf_mac))); | |
412 | ||
413 | for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) | |
414 | vty_out(vty, "%s ", name); | |
415 | ||
416 | vty_out(vty, "\n"); | |
417 | } | |
418 | ||
419 | void bgp_mac_dump_table(struct vty *vty) | |
420 | { | |
421 | hash_iterate(bm->self_mac_hash, bgp_mac_show_mac_entry, vty); | |
422 | } |