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