3 * Copyright (C) 2018 Cumulus Networks, Inc.
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)
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
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
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"
35 DEFINE_MTYPE_STATIC(BGPD
, BSM
, "Mac Hash Entry");
36 DEFINE_MTYPE_STATIC(BGPD
, BSM_STRING
, "Mac Hash Entry Interface String");
39 struct ethaddr macaddr
;
40 struct list
*ifp_list
;
43 static unsigned int bgp_mac_hash_key_make(const void *data
)
45 const struct bgp_self_mac
*bsm
= data
;
47 return jhash(&bsm
->macaddr
, ETH_ALEN
, 0xa5a5dead);
50 static bool bgp_mac_hash_cmp(const void *d1
, const void *d2
)
52 const struct bgp_self_mac
*bsm1
= d1
;
53 const struct bgp_self_mac
*bsm2
= d2
;
55 if (memcmp(&bsm1
->macaddr
, &bsm2
->macaddr
, ETH_ALEN
) == 0)
61 void bgp_mac_init(void)
63 bm
->self_mac_hash
= hash_create(bgp_mac_hash_key_make
, bgp_mac_hash_cmp
,
67 static void bgp_mac_hash_free(void *data
)
69 struct bgp_self_mac
*bsm
= data
;
72 list_delete(&bsm
->ifp_list
);
74 XFREE(MTYPE_BSM
, bsm
);
77 void bgp_mac_finish(void)
79 hash_clean(bm
->self_mac_hash
, bgp_mac_hash_free
);
80 hash_free(bm
->self_mac_hash
);
83 static void bgp_mac_hash_interface_string_del(void *val
)
87 XFREE(MTYPE_BSM_STRING
, data
);
90 static void *bgp_mac_hash_alloc(void *p
)
92 const struct bgp_self_mac
*orig
= p
;
93 struct bgp_self_mac
*bsm
;
95 bsm
= XCALLOC(MTYPE_BSM
, sizeof(struct bgp_self_mac
));
96 memcpy(&bsm
->macaddr
, &orig
->macaddr
, ETH_ALEN
);
98 bsm
->ifp_list
= list_new();
99 bsm
->ifp_list
->del
= bgp_mac_hash_interface_string_del
;
104 struct bgp_mac_find_internal
{
105 struct bgp_self_mac
*bsm
;
109 static void bgp_mac_find_ifp_internal(struct hash_bucket
*bucket
, void *arg
)
111 struct bgp_mac_find_internal
*bmfi
= arg
;
112 struct bgp_self_mac
*bsm
= bucket
->data
;
113 struct listnode
*node
;
116 for (ALL_LIST_ELEMENTS_RO(bsm
->ifp_list
, node
, name
)) {
117 if (strcmp(name
, bmfi
->ifname
) == 0) {
124 static struct bgp_self_mac
*bgp_mac_find_interface_name(const char *ifname
)
126 struct bgp_mac_find_internal bmfi
;
129 bmfi
.ifname
= ifname
;
130 hash_iterate(bm
->self_mac_hash
, bgp_mac_find_ifp_internal
, &bmfi
);
135 static void bgp_process_mac_rescan_table(struct bgp
*bgp
, struct peer
*peer
,
136 struct bgp_table
*table
,
137 struct ethaddr
*macaddr
)
139 struct bgp_node
*prn
, *rn
;
140 struct bgp_path_info
*pi
;
143 for (prn
= bgp_table_top(table
); prn
; prn
= bgp_route_next(prn
)) {
144 struct bgp_table
*sub
= prn
->info
;
149 for (rn
= bgp_table_top(sub
); rn
; rn
= bgp_route_next(rn
)) {
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
;
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)
166 for (pi
= rn
->info
; pi
; pi
= pi
->next
) {
167 if (pi
->peer
== peer
)
175 * If the mac address is not the same then
176 * we don't care and since we are looking
178 if ((memcmp(&pi
->attr
->rmac
, macaddr
, ETH_ALEN
) != 0) &&
183 num_labels
= pi
->extra
->num_labels
;
185 label_pnt
= &pi
->extra
->label
[0];
187 prd
.family
= AF_UNSPEC
;
189 memcpy(&prd
.val
, &prn
->p
.u
.val
, 8);
191 memcpy(&evpn
, &pi
->attr
->evpn_overlay
, sizeof(evpn
));
192 int32_t ret
= bgp_update(peer
, &rn
->p
,
194 pi
->attr
, AFI_L2VPN
, SAFI_EVPN
,
196 BGP_ROUTE_NORMAL
, &prd
,
197 label_pnt
, num_labels
,
206 static void bgp_mac_rescan_evpn_table(struct bgp
*bgp
, struct ethaddr
*macaddr
)
208 struct listnode
*node
;
215 for (ALL_LIST_ELEMENTS_RO(bgp
->peer
, node
, peer
)) {
217 if (CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
))
220 if (peer
->status
!= Established
)
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)",
229 bgp_soft_reconfig_in(peer
, afi
, safi
);
231 struct bgp_table
*table
= bgp
->rib
[afi
][safi
];
233 if (bgp_debug_update(peer
, NULL
, NULL
, 1))
234 zlog_debug("Processing EVPN MAC interface change on peer %s",
236 bgp_process_mac_rescan_table(bgp
, peer
, table
, macaddr
);
241 static void bgp_mac_rescan_all_evpn_tables(struct ethaddr
*macaddr
)
243 struct listnode
*node
;
246 for (ALL_LIST_ELEMENTS_RO(bm
->bgp
, node
, bgp
)) {
247 struct bgp_table
*table
= bgp
->rib
[AFI_L2VPN
][SAFI_EVPN
];
250 bgp_mac_rescan_evpn_table(bgp
, macaddr
);
254 static void bgp_mac_remove_ifp_internal(struct bgp_self_mac
*bsm
, char *ifname
,
255 struct ethaddr
*macaddr
)
257 struct listnode
*node
= NULL
;
260 for (ALL_LIST_ELEMENTS_RO(bsm
->ifp_list
, node
, name
)) {
261 if (strcmp(name
, ifname
) == 0)
266 list_delete_node(bsm
->ifp_list
, node
);
267 XFREE(MTYPE_BSM_STRING
, name
);
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
);
275 bgp_mac_rescan_all_evpn_tables(macaddr
);
279 void bgp_mac_add_mac_entry(struct interface
*ifp
)
281 struct bgp_self_mac lookup
;
282 struct bgp_self_mac
*bsm
;
283 struct bgp_self_mac
*old_bsm
;
286 memcpy(&lookup
.macaddr
, &ifp
->hw_addr
, ETH_ALEN
);
287 bsm
= hash_get(bm
->self_mac_hash
, &lookup
, bgp_mac_hash_alloc
);
290 * Does this happen to be a move
292 old_bsm
= bgp_mac_find_interface_name(ifp
->name
);
293 ifname
= XSTRDUP(MTYPE_BSM_STRING
, ifp
->name
);
295 if (bsm
->ifp_list
->count
== 0) {
297 listnode_add(bsm
->ifp_list
, ifname
);
299 bgp_mac_remove_ifp_internal(old_bsm
, ifname
,
303 * If old mac address is the same as the new,
304 * then there is nothing to do here
310 bgp_mac_remove_ifp_internal(old_bsm
, ifp
->name
,
313 listnode_add(bsm
->ifp_list
, ifname
);
316 bgp_mac_rescan_all_evpn_tables(&bsm
->macaddr
);
319 void bgp_mac_del_mac_entry(struct interface
*ifp
)
321 struct bgp_self_mac lookup
;
322 struct bgp_self_mac
*bsm
;
324 memcpy(&lookup
.macaddr
, &ifp
->hw_addr
, ETH_ALEN
);
325 bsm
= hash_lookup(bm
->self_mac_hash
, &lookup
);
330 * Write code to allow old mac address to no-longer
331 * win if we happen to have received it from a peer.
333 bgp_mac_remove_ifp_internal(bsm
, ifp
->name
, &bsm
->macaddr
);
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.
341 bool bgp_mac_exist(struct ethaddr
*mac
)
343 struct bgp_self_mac lookup
;
344 struct bgp_self_mac
*bsm
;
345 static uint8_t tmp
[ETHER_ADDR_STRLEN
] = {0};
347 if (memcmp(mac
, &tmp
, ETH_ALEN
) == 0)
350 memcpy(&lookup
.macaddr
, mac
, ETH_ALEN
);
351 bsm
= hash_lookup(bm
->self_mac_hash
, &lookup
);
358 /* This API checks EVPN type-2 prefix and comapares
359 * mac against any of local assigned (SVIs) MAC
362 bool bgp_mac_entry_exists(struct prefix
*p
)
364 struct prefix_evpn
*pevpn
= (struct prefix_evpn
*)p
;
366 if (pevpn
->family
!= AF_EVPN
)
369 if (pevpn
->prefix
.route_type
!= BGP_EVPN_MAC_IP_ROUTE
)
372 return bgp_mac_exist(&p
->u
.prefix_evpn
.macip_addr
.mac
);
377 static void bgp_mac_show_mac_entry(struct hash_bucket
*bucket
, void *arg
)
379 struct vty
*vty
= arg
;
380 struct bgp_self_mac
*bsm
= bucket
->data
;
381 struct listnode
*node
;
383 char buf_mac
[ETHER_ADDR_STRLEN
];
385 vty_out(vty
, "Mac Address: %s ",
386 prefix_mac2str(&bsm
->macaddr
, buf_mac
, sizeof(buf_mac
)));
388 for (ALL_LIST_ELEMENTS_RO(bsm
->ifp_list
, node
, name
))
389 vty_out(vty
, "%s ", name
);
394 void bgp_mac_dump_table(struct vty
*vty
)
396 hash_iterate(bm
->self_mac_hash
, bgp_mac_show_mac_entry
, vty
);