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