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