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 6 : DEFINE_MTYPE_STATIC(BGPD, BSM, "Mac Hash Entry");
37 6 : 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 19 : static unsigned int bgp_mac_hash_key_make(const void *data)
45 : {
46 19 : const struct bgp_self_mac *bsm = data;
47 :
48 19 : return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead);
49 : }
50 :
51 13 : static bool bgp_mac_hash_cmp(const void *d1, const void *d2)
52 : {
53 13 : const struct bgp_self_mac *bsm1 = d1;
54 13 : const struct bgp_self_mac *bsm2 = d2;
55 :
56 13 : if (memcmp(&bsm1->macaddr, &bsm2->macaddr, ETH_ALEN) == 0)
57 13 : return true;
58 :
59 : return false;
60 : }
61 :
62 2 : void bgp_mac_init(void)
63 : {
64 2 : bm->self_mac_hash = hash_create(bgp_mac_hash_key_make, bgp_mac_hash_cmp,
65 : "BGP MAC Hash");
66 2 : }
67 :
68 4 : static void bgp_mac_hash_free(void *data)
69 : {
70 4 : struct bgp_self_mac *bsm = data;
71 :
72 4 : if (bsm->ifp_list)
73 4 : list_delete(&bsm->ifp_list);
74 :
75 4 : XFREE(MTYPE_BSM, bsm);
76 4 : }
77 :
78 2 : void bgp_mac_finish(void)
79 : {
80 2 : hash_clean(bm->self_mac_hash, bgp_mac_hash_free);
81 2 : hash_free(bm->self_mac_hash);
82 2 : }
83 :
84 4 : static void bgp_mac_hash_interface_string_del(void *val)
85 : {
86 4 : char *data = val;
87 :
88 4 : XFREE(MTYPE_BSM_STRING, data);
89 4 : }
90 :
91 5 : static void *bgp_mac_hash_alloc(void *p)
92 : {
93 5 : const struct bgp_self_mac *orig = p;
94 5 : struct bgp_self_mac *bsm;
95 :
96 5 : bsm = XCALLOC(MTYPE_BSM, sizeof(struct bgp_self_mac));
97 5 : memcpy(&bsm->macaddr, &orig->macaddr, ETH_ALEN);
98 :
99 5 : bsm->ifp_list = list_new();
100 5 : bsm->ifp_list->del = bgp_mac_hash_interface_string_del;
101 :
102 5 : return bsm;
103 : }
104 :
105 : struct bgp_mac_find_internal {
106 : struct bgp_self_mac *bsm;
107 : const char *ifname;
108 : };
109 :
110 28 : static void bgp_mac_find_ifp_internal(struct hash_bucket *bucket, void *arg)
111 : {
112 28 : struct bgp_mac_find_internal *bmfi = arg;
113 28 : struct bgp_self_mac *bsm = bucket->data;
114 28 : struct listnode *node;
115 28 : char *name;
116 :
117 68 : for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) {
118 23 : if (strcmp(name, bmfi->ifname) == 0) {
119 11 : bmfi->bsm = bsm;
120 11 : return;
121 : }
122 : }
123 : }
124 :
125 16 : static struct bgp_self_mac *bgp_mac_find_interface_name(const char *ifname)
126 : {
127 16 : struct bgp_mac_find_internal bmfi;
128 :
129 16 : bmfi.bsm = NULL;
130 16 : bmfi.ifname = ifname;
131 16 : hash_iterate(bm->self_mac_hash, bgp_mac_find_ifp_internal, &bmfi);
132 :
133 16 : 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 6 : static void bgp_mac_rescan_evpn_table(struct bgp *bgp, struct ethaddr *macaddr)
223 : {
224 6 : struct listnode *node;
225 6 : struct peer *peer;
226 6 : safi_t safi;
227 6 : afi_t afi;
228 :
229 6 : afi = AFI_L2VPN;
230 6 : safi = SAFI_EVPN;
231 18 : for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
232 :
233 6 : if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
234 0 : continue;
235 :
236 6 : if (!peer_established(peer))
237 6 : 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 6 : }
255 :
256 6 : static void bgp_mac_rescan_all_evpn_tables(struct ethaddr *macaddr)
257 : {
258 6 : struct listnode *node;
259 6 : struct bgp *bgp;
260 :
261 18 : for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
262 6 : struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN];
263 :
264 6 : if (table)
265 6 : bgp_mac_rescan_evpn_table(bgp, macaddr);
266 : }
267 6 : }
268 :
269 1 : static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname,
270 : struct ethaddr *macaddr)
271 : {
272 1 : struct listnode *node = NULL;
273 1 : char *name;
274 :
275 2 : for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) {
276 1 : if (strcmp(name, ifname) == 0)
277 : break;
278 : }
279 :
280 1 : if (node) {
281 1 : list_delete_node(bsm->ifp_list, node);
282 1 : XFREE(MTYPE_BSM_STRING, name);
283 : }
284 :
285 1 : if (bsm->ifp_list->count == 0) {
286 1 : struct ethaddr mac = *macaddr;
287 :
288 1 : hash_release(bm->self_mac_hash, bsm);
289 1 : list_delete(&bsm->ifp_list);
290 1 : XFREE(MTYPE_BSM, bsm);
291 :
292 1 : bgp_mac_rescan_all_evpn_tables(&mac);
293 : }
294 1 : }
295 :
296 16 : void bgp_mac_add_mac_entry(struct interface *ifp)
297 : {
298 16 : struct bgp_self_mac lookup;
299 16 : struct bgp_self_mac *bsm;
300 16 : struct bgp_self_mac *old_bsm;
301 16 : char *ifname;
302 :
303 16 : memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN);
304 16 : bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc);
305 :
306 : /*
307 : * Does this happen to be a move
308 : */
309 16 : old_bsm = bgp_mac_find_interface_name(ifp->name);
310 16 : ifname = XSTRDUP(MTYPE_BSM_STRING, ifp->name);
311 :
312 16 : if (bsm->ifp_list->count == 0) {
313 :
314 5 : listnode_add(bsm->ifp_list, ifname);
315 5 : 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 11 : if (old_bsm == bsm) {
324 11 : XFREE(MTYPE_BSM_STRING, ifname);
325 11 : return;
326 : }
327 :
328 0 : if (old_bsm)
329 0 : bgp_mac_remove_ifp_internal(old_bsm, ifp->name,
330 : &old_bsm->macaddr);
331 :
332 0 : listnode_add(bsm->ifp_list, ifname);
333 : }
334 :
335 5 : bgp_mac_rescan_all_evpn_tables(&bsm->macaddr);
336 : }
337 :
338 2 : void bgp_mac_del_mac_entry(struct interface *ifp)
339 : {
340 2 : struct bgp_self_mac lookup;
341 2 : struct bgp_self_mac *bsm;
342 :
343 2 : memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN);
344 2 : bsm = hash_lookup(bm->self_mac_hash, &lookup);
345 2 : if (!bsm)
346 1 : 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 1 : 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 4 : bool bgp_mac_exist(const struct ethaddr *mac)
361 : {
362 4 : struct bgp_self_mac lookup;
363 4 : struct bgp_self_mac *bsm;
364 4 : static uint8_t tmp [ETHER_ADDR_STRLEN] = {0};
365 :
366 4 : 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 4 : bool bgp_mac_entry_exists(const struct prefix *p)
382 : {
383 4 : const struct prefix_evpn *pevpn = (const struct prefix_evpn *)p;
384 :
385 4 : 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 : }
|