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