]>
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"); | |
6a69ac51 | 37 | DEFINE_MTYPE_STATIC(BGPD, BSM_STRING, "Mac Hash Entry Interface 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 DS |
139 | { |
140 | struct bgp_node *prn, *rn; | |
141 | struct bgp_path_info *pi; | |
4e802e66 DS |
142 | |
143 | for (prn = bgp_table_top(table); prn; prn = bgp_route_next(prn)) { | |
144 | struct bgp_table *sub = prn->info; | |
145 | ||
146 | if (!sub) | |
147 | continue; | |
148 | ||
149 | for (rn = bgp_table_top(sub); rn; rn = bgp_route_next(rn)) { | |
78869ebf DS |
150 | bool rn_affected; |
151 | struct prefix_evpn *pevpn = (struct prefix_evpn *)&rn->p; | |
4e802e66 DS |
152 | struct prefix_rd prd; |
153 | uint32_t num_labels = 0; | |
154 | mpls_label_t *label_pnt = NULL; | |
155 | struct bgp_route_evpn evpn; | |
156 | ||
78869ebf DS |
157 | if (pevpn->family == AF_EVPN && |
158 | pevpn->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && | |
159 | memcmp(&rn->p.u.prefix_evpn.macip_addr.mac, | |
160 | macaddr, ETH_ALEN) == 0) | |
161 | rn_affected = true; | |
162 | else | |
163 | rn_affected = false; | |
164 | ||
4e802e66 DS |
165 | for (pi = rn->info; pi; pi = pi->next) { |
166 | if (pi->peer == peer) | |
167 | break; | |
168 | } | |
169 | ||
170 | if (!pi) | |
171 | continue; | |
172 | ||
78869ebf DS |
173 | /* |
174 | * If the mac address is not the same then | |
175 | * we don't care and since we are looking | |
176 | */ | |
177 | if ((memcmp(&pi->attr->rmac, macaddr, ETH_ALEN) != 0) && | |
178 | !rn_affected) | |
179 | continue; | |
180 | ||
4e802e66 DS |
181 | if (pi->extra) |
182 | num_labels = pi->extra->num_labels; | |
183 | if (num_labels) | |
184 | label_pnt = &pi->extra->label[0]; | |
185 | ||
186 | prd.family = AF_UNSPEC; | |
187 | prd.prefixlen = 64; | |
188 | memcpy(&prd.val, &prn->p.u.val, 8); | |
189 | ||
c869b3f8 CS |
190 | if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { |
191 | if (bgp_debug_update(peer, &rn->p, NULL, 1)) { | |
192 | char pfx_buf[BGP_PRD_PATH_STRLEN]; | |
193 | ||
194 | bgp_debug_rdpfxpath2str( | |
195 | AFI_L2VPN, SAFI_EVPN, &prd, | |
196 | &rn->p, label_pnt, num_labels, | |
197 | pi->addpath_rx_id ? 1 : 0, | |
198 | pi->addpath_rx_id, pfx_buf, | |
199 | sizeof(pfx_buf)); | |
200 | zlog_debug( | |
201 | "%s skip update of %s marked as removed", | |
202 | peer->host, pfx_buf); | |
203 | } | |
204 | continue; | |
205 | } | |
206 | ||
4e802e66 DS |
207 | memcpy(&evpn, &pi->attr->evpn_overlay, sizeof(evpn)); |
208 | int32_t ret = bgp_update(peer, &rn->p, | |
209 | pi->addpath_rx_id, | |
210 | pi->attr, AFI_L2VPN, SAFI_EVPN, | |
211 | ZEBRA_ROUTE_BGP, | |
212 | BGP_ROUTE_NORMAL, &prd, | |
213 | label_pnt, num_labels, | |
214 | 1, &evpn); | |
215 | ||
216 | if (ret < 0) | |
217 | bgp_unlock_node(rn); | |
218 | } | |
219 | } | |
220 | } | |
221 | ||
78869ebf | 222 | static void bgp_mac_rescan_evpn_table(struct bgp *bgp, struct ethaddr *macaddr) |
6a69ac51 DS |
223 | { |
224 | struct listnode *node; | |
4e802e66 DS |
225 | struct peer *peer; |
226 | safi_t safi; | |
227 | afi_t afi; | |
228 | ||
229 | afi = AFI_L2VPN; | |
230 | safi = SAFI_EVPN; | |
231 | for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { | |
232 | ||
233 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) | |
234 | continue; | |
235 | ||
236 | if (peer->status != Established) | |
237 | continue; | |
238 | ||
239 | if (CHECK_FLAG(peer->af_flags[afi][safi], | |
240 | PEER_FLAG_SOFT_RECONFIG)) { | |
241 | if (bgp_debug_update(peer, NULL, NULL, 1)) | |
242 | zlog_debug("Processing EVPN MAC interface change on peer %s (inbound, soft-reconfig)", | |
243 | peer->host); | |
244 | ||
245 | bgp_soft_reconfig_in(peer, afi, safi); | |
246 | } else { | |
247 | struct bgp_table *table = bgp->rib[afi][safi]; | |
248 | ||
249 | if (bgp_debug_update(peer, NULL, NULL, 1)) | |
250 | zlog_debug("Processing EVPN MAC interface change on peer %s", | |
251 | peer->host); | |
78869ebf | 252 | bgp_process_mac_rescan_table(bgp, peer, table, macaddr); |
4e802e66 DS |
253 | } |
254 | } | |
255 | } | |
256 | ||
78869ebf | 257 | static void bgp_mac_rescan_all_evpn_tables(struct ethaddr *macaddr) |
4e802e66 DS |
258 | { |
259 | struct listnode *node; | |
260 | struct bgp *bgp; | |
261 | ||
262 | for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) { | |
263 | struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN]; | |
264 | ||
265 | if (table) | |
78869ebf | 266 | bgp_mac_rescan_evpn_table(bgp, macaddr); |
4e802e66 DS |
267 | } |
268 | } | |
269 | ||
78869ebf DS |
270 | static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname, |
271 | struct ethaddr *macaddr) | |
4e802e66 DS |
272 | { |
273 | struct listnode *node = NULL; | |
6a69ac51 DS |
274 | char *name; |
275 | ||
276 | for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) { | |
277 | if (strcmp(name, ifname) == 0) | |
278 | break; | |
279 | } | |
280 | ||
281 | if (node) { | |
282 | list_delete_node(bsm->ifp_list, node); | |
283 | XFREE(MTYPE_BSM_STRING, name); | |
284 | } | |
285 | ||
286 | if (bsm->ifp_list->count == 0) { | |
287 | hash_release(bm->self_mac_hash, bsm); | |
288 | list_delete(&bsm->ifp_list); | |
289 | XFREE(MTYPE_BSM, bsm); | |
290 | ||
78869ebf | 291 | bgp_mac_rescan_all_evpn_tables(macaddr); |
6a69ac51 DS |
292 | } |
293 | } | |
294 | ||
295 | void bgp_mac_add_mac_entry(struct interface *ifp) | |
296 | { | |
297 | struct bgp_self_mac lookup; | |
298 | struct bgp_self_mac *bsm; | |
299 | struct bgp_self_mac *old_bsm; | |
300 | char *ifname; | |
301 | ||
302 | memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); | |
303 | bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc); | |
304 | ||
305 | /* | |
306 | * Does this happen to be a move | |
307 | */ | |
308 | old_bsm = bgp_mac_find_interface_name(ifp->name); | |
309 | ifname = XSTRDUP(MTYPE_BSM_STRING, ifp->name); | |
310 | ||
311 | if (bsm->ifp_list->count == 0) { | |
312 | ||
313 | listnode_add(bsm->ifp_list, ifname); | |
314 | if (old_bsm) | |
78869ebf DS |
315 | bgp_mac_remove_ifp_internal(old_bsm, ifname, |
316 | &old_bsm->macaddr); | |
6a69ac51 DS |
317 | } else { |
318 | /* | |
319 | * If old mac address is the same as the new, | |
320 | * then there is nothing to do here | |
321 | */ | |
308000ee DS |
322 | if (old_bsm == bsm) { |
323 | XFREE(MTYPE_BSM_STRING, ifname); | |
6a69ac51 | 324 | return; |
308000ee | 325 | } |
6a69ac51 DS |
326 | |
327 | if (old_bsm) | |
78869ebf DS |
328 | bgp_mac_remove_ifp_internal(old_bsm, ifp->name, |
329 | &old_bsm->macaddr); | |
6a69ac51 DS |
330 | |
331 | listnode_add(bsm->ifp_list, ifname); | |
332 | } | |
333 | ||
78869ebf | 334 | bgp_mac_rescan_all_evpn_tables(&bsm->macaddr); |
6a69ac51 DS |
335 | } |
336 | ||
337 | void bgp_mac_del_mac_entry(struct interface *ifp) | |
338 | { | |
339 | struct bgp_self_mac lookup; | |
340 | struct bgp_self_mac *bsm; | |
341 | ||
342 | memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); | |
343 | bsm = hash_lookup(bm->self_mac_hash, &lookup); | |
344 | if (!bsm) | |
345 | return; | |
346 | ||
347 | /* | |
348 | * Write code to allow old mac address to no-longer | |
349 | * win if we happen to have received it from a peer. | |
350 | */ | |
78869ebf | 351 | bgp_mac_remove_ifp_internal(bsm, ifp->name, &bsm->macaddr); |
6a69ac51 DS |
352 | } |
353 | ||
eee353c5 CS |
354 | /* This API checks MAC address against any of local |
355 | * assigned (SVIs) MAC address. | |
356 | * An example: router-mac attribute in any of evpn update | |
357 | * requires to compare against local mac. | |
358 | */ | |
359 | bool bgp_mac_exist(struct ethaddr *mac) | |
4e802e66 | 360 | { |
4e802e66 DS |
361 | struct bgp_self_mac lookup; |
362 | struct bgp_self_mac *bsm; | |
eee353c5 CS |
363 | static uint8_t tmp [ETHER_ADDR_STRLEN] = {0}; |
364 | ||
365 | if (memcmp(mac, &tmp, ETH_ALEN) == 0) | |
366 | return false; | |
367 | ||
368 | memcpy(&lookup.macaddr, mac, ETH_ALEN); | |
369 | bsm = hash_lookup(bm->self_mac_hash, &lookup); | |
370 | if (!bsm) | |
371 | return false; | |
372 | ||
373 | return true; | |
374 | } | |
375 | ||
376 | /* This API checks EVPN type-2 prefix and comapares | |
377 | * mac against any of local assigned (SVIs) MAC | |
378 | * address. | |
379 | */ | |
380 | bool bgp_mac_entry_exists(struct prefix *p) | |
381 | { | |
382 | struct prefix_evpn *pevpn = (struct prefix_evpn *)p; | |
4e802e66 DS |
383 | |
384 | if (pevpn->family != AF_EVPN) | |
385 | return false; | |
386 | ||
387 | if (pevpn->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) | |
388 | return false; | |
389 | ||
eee353c5 | 390 | return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac); |
4e802e66 DS |
391 | |
392 | return true; | |
393 | } | |
394 | ||
e3b78da8 | 395 | static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg) |
48ecf8f5 DS |
396 | { |
397 | struct vty *vty = arg; | |
e3b78da8 | 398 | struct bgp_self_mac *bsm = bucket->data; |
48ecf8f5 DS |
399 | struct listnode *node; |
400 | char *name; | |
401 | char buf_mac[ETHER_ADDR_STRLEN]; | |
402 | ||
403 | vty_out(vty, "Mac Address: %s ", | |
404 | prefix_mac2str(&bsm->macaddr, buf_mac, sizeof(buf_mac))); | |
405 | ||
406 | for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) | |
407 | vty_out(vty, "%s ", name); | |
408 | ||
409 | vty_out(vty, "\n"); | |
410 | } | |
411 | ||
412 | void bgp_mac_dump_table(struct vty *vty) | |
413 | { | |
414 | hash_iterate(bm->self_mac_hash, bgp_mac_show_mac_entry, vty); | |
415 | } |