Coverage Report

Created: 2025-11-09 06:11

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