Coverage Report

Created: 2025-10-23 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/zebra/zebra_neigh.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Zebra neighbor table management
4
 *
5
 * Copyright (C) 2021 Nvidia
6
 * Anuradha Karuppiah
7
 */
8
9
#include <zebra.h>
10
11
#include "command.h"
12
#include "hash.h"
13
#include "if.h"
14
#include "jhash.h"
15
#include "linklist.h"
16
#include "log.h"
17
#include "memory.h"
18
#include "prefix.h"
19
#include "stream.h"
20
#include "table.h"
21
22
#include "zebra/zebra_router.h"
23
#include "zebra/debug.h"
24
#include "zebra/interface.h"
25
#include "zebra/rib.h"
26
#include "zebra/rt.h"
27
#include "zebra/rt_netlink.h"
28
#include "zebra/zebra_errors.h"
29
#include "zebra/interface.h"
30
#include "zebra/zebra_neigh.h"
31
#include "zebra/zebra_pbr.h"
32
33
2
DEFINE_MTYPE_STATIC(ZEBRA, ZNEIGH_INFO, "Zebra neigh table");
34
2
DEFINE_MTYPE_STATIC(ZEBRA, ZNEIGH_ENT, "Zebra neigh entry");
35
2
36
2
static int zebra_neigh_rb_cmp(const struct zebra_neigh_ent *n1,
37
2
            const struct zebra_neigh_ent *n2)
38
2
{
39
0
  if (n1->ifindex < n2->ifindex)
40
0
    return -1;
41
42
0
  if (n1->ifindex > n2->ifindex)
43
0
    return 1;
44
45
0
  if (n1->ip.ipa_type < n2->ip.ipa_type)
46
0
    return -1;
47
48
0
  if (n1->ip.ipa_type > n2->ip.ipa_type)
49
0
    return 1;
50
51
0
  if (n1->ip.ipa_type == AF_INET) {
52
0
    if (n1->ip.ipaddr_v4.s_addr < n2->ip.ipaddr_v4.s_addr)
53
0
      return -1;
54
55
0
    if (n1->ip.ipaddr_v4.s_addr > n2->ip.ipaddr_v4.s_addr)
56
0
      return 1;
57
58
0
    return 0;
59
0
  }
60
61
0
  return memcmp(&n1->ip.ipaddr_v6, &n2->ip.ipaddr_v6, IPV6_MAX_BYTELEN);
62
0
}
63
RB_GENERATE(zebra_neigh_rb_head, zebra_neigh_ent, rb_node, zebra_neigh_rb_cmp);
64
65
static struct zebra_neigh_ent *zebra_neigh_find(ifindex_t ifindex,
66
            struct ipaddr *ip)
67
0
{
68
0
  struct zebra_neigh_ent tmp;
69
70
0
  tmp.ifindex = ifindex;
71
0
  memcpy(&tmp.ip, ip, sizeof(*ip));
72
0
  return RB_FIND(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, &tmp);
73
0
}
74
75
static struct zebra_neigh_ent *
76
zebra_neigh_new(ifindex_t ifindex, struct ipaddr *ip, struct ethaddr *mac)
77
0
{
78
0
  struct zebra_neigh_ent *n;
79
80
0
  n = XCALLOC(MTYPE_ZNEIGH_ENT, sizeof(struct zebra_neigh_ent));
81
82
0
  memcpy(&n->ip, ip, sizeof(*ip));
83
0
  n->ifindex = ifindex;
84
0
  if (mac) {
85
0
    memcpy(&n->mac, mac, sizeof(*mac));
86
0
    n->flags |= ZEBRA_NEIGH_ENT_ACTIVE;
87
0
  }
88
89
  /* Add to rb_tree */
90
0
  if (RB_INSERT(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, n)) {
91
0
    XFREE(MTYPE_ZNEIGH_ENT, n);
92
0
    return NULL;
93
0
  }
94
95
  /* Initialise the pbr rule list */
96
0
  n->pbr_rule_list = list_new();
97
0
  listset_app_node_mem(n->pbr_rule_list);
98
99
0
  if (IS_ZEBRA_DEBUG_NEIGH)
100
0
    zlog_debug("zebra neigh new if %d %pIA %pEA", n->ifindex,
101
0
         &n->ip, &n->mac);
102
103
0
  return n;
104
0
}
105
106
static void zebra_neigh_pbr_rules_update(struct zebra_neigh_ent *n)
107
0
{
108
0
  struct zebra_pbr_rule *rule;
109
0
  struct listnode *node;
110
111
0
  for (ALL_LIST_ELEMENTS_RO(n->pbr_rule_list, node, rule))
112
0
    dplane_pbr_rule_update(rule, rule);
113
0
}
114
115
static void zebra_neigh_free(struct zebra_neigh_ent *n)
116
0
{
117
0
  if (listcount(n->pbr_rule_list)) {
118
    /* if rules are still using the neigh mark it as inactive and
119
     * update the dataplane
120
     */
121
0
    if (n->flags & ZEBRA_NEIGH_ENT_ACTIVE) {
122
0
      n->flags &= ~ZEBRA_NEIGH_ENT_ACTIVE;
123
0
      memset(&n->mac, 0, sizeof(n->mac));
124
0
    }
125
0
    zebra_neigh_pbr_rules_update(n);
126
0
    return;
127
0
  }
128
0
  if (IS_ZEBRA_DEBUG_NEIGH)
129
0
    zlog_debug("zebra neigh free if %d %pIA %pEA", n->ifindex,
130
0
         &n->ip, &n->mac);
131
132
  /* cleanup resources maintained against the neigh */
133
0
  list_delete(&n->pbr_rule_list);
134
135
0
  RB_REMOVE(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, n);
136
137
0
  XFREE(MTYPE_ZNEIGH_ENT, n);
138
0
}
139
140
/* kernel neigh del */
141
void zebra_neigh_del(struct interface *ifp, struct ipaddr *ip)
142
0
{
143
0
  struct zebra_neigh_ent *n;
144
145
0
  if (IS_ZEBRA_DEBUG_NEIGH)
146
0
    zlog_debug("zebra neigh del if %s/%d %pIA", ifp->name,
147
0
         ifp->ifindex, ip);
148
149
0
  n = zebra_neigh_find(ifp->ifindex, ip);
150
0
  if (!n)
151
0
    return;
152
0
  zebra_neigh_free(n);
153
0
}
154
155
/* kernel neigh add */
156
void zebra_neigh_add(struct interface *ifp, struct ipaddr *ip,
157
         struct ethaddr *mac)
158
0
{
159
0
  struct zebra_neigh_ent *n;
160
161
0
  if (IS_ZEBRA_DEBUG_NEIGH)
162
0
    zlog_debug("zebra neigh add if %s/%d %pIA %pEA", ifp->name,
163
0
         ifp->ifindex, ip, mac);
164
165
0
  n = zebra_neigh_find(ifp->ifindex, ip);
166
0
  if (n) {
167
0
    if (!memcmp(&n->mac, mac, sizeof(*mac)))
168
0
      return;
169
170
0
    memcpy(&n->mac, mac, sizeof(*mac));
171
0
    n->flags |= ZEBRA_NEIGH_ENT_ACTIVE;
172
173
    /* update rules linked to the neigh */
174
0
    zebra_neigh_pbr_rules_update(n);
175
0
  } else {
176
0
    zebra_neigh_new(ifp->ifindex, ip, mac);
177
0
  }
178
0
}
179
180
void zebra_neigh_deref(struct zebra_pbr_rule *rule)
181
0
{
182
0
  struct zebra_neigh_ent *n = rule->action.neigh;
183
184
0
  if (IS_ZEBRA_DEBUG_NEIGH)
185
0
    zlog_debug("zebra neigh deref if %d %pIA by pbr rule %u",
186
0
         n->ifindex, &n->ip, rule->rule.seq);
187
188
0
  rule->action.neigh = NULL;
189
  /* remove rule from the list and free if it is inactive */
190
0
  list_delete_node(n->pbr_rule_list, &rule->action.neigh_listnode);
191
0
  if (!(n->flags & ZEBRA_NEIGH_ENT_ACTIVE))
192
0
    zebra_neigh_free(n);
193
0
}
194
195
/* XXX - this needs to work with evpn's neigh read */
196
static void zebra_neigh_read_on_first_ref(void)
197
0
{
198
0
  static bool neigh_read_done;
199
200
0
  if (!neigh_read_done) {
201
0
    neigh_read(zebra_ns_lookup(NS_DEFAULT));
202
0
    neigh_read_done = true;
203
0
  }
204
0
}
205
206
void zebra_neigh_ref(int ifindex, struct ipaddr *ip,
207
         struct zebra_pbr_rule *rule)
208
0
{
209
0
  struct zebra_neigh_ent *n;
210
211
0
  if (IS_ZEBRA_DEBUG_NEIGH)
212
0
    zlog_debug("zebra neigh ref if %d %pIA by pbr rule %u", ifindex,
213
0
         ip, rule->rule.seq);
214
215
0
  zebra_neigh_read_on_first_ref();
216
0
  n = zebra_neigh_find(ifindex, ip);
217
0
  if (!n)
218
0
    n = zebra_neigh_new(ifindex, ip, NULL);
219
220
  /* link the pbr entry to the neigh */
221
0
  if (rule->action.neigh == n)
222
0
    return;
223
224
0
  if (rule->action.neigh)
225
0
    zebra_neigh_deref(rule);
226
227
0
  rule->action.neigh = n;
228
0
  listnode_init(&rule->action.neigh_listnode, rule);
229
0
  listnode_add(n->pbr_rule_list, &rule->action.neigh_listnode);
230
0
}
231
232
static void zebra_neigh_show_one(struct vty *vty, struct zebra_neigh_ent *n)
233
0
{
234
0
  char mac_buf[ETHER_ADDR_STRLEN];
235
0
  char ip_buf[INET6_ADDRSTRLEN];
236
0
  struct interface *ifp;
237
238
0
  ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
239
0
          n->ifindex);
240
0
  ipaddr2str(&n->ip, ip_buf, sizeof(ip_buf));
241
0
  prefix_mac2str(&n->mac, mac_buf, sizeof(mac_buf));
242
0
  vty_out(vty, "%-20s %-30s %-18s %u\n", ifp ? ifp->name : "-", ip_buf,
243
0
    mac_buf, listcount(n->pbr_rule_list));
244
0
}
245
246
void zebra_neigh_show(struct vty *vty)
247
0
{
248
0
  struct zebra_neigh_ent *n;
249
250
0
  vty_out(vty, "%-20s %-30s %-18s %s\n", "Interface", "Neighbor", "MAC",
251
0
    "#Rules");
252
0
  RB_FOREACH (n, zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree)
253
0
    zebra_neigh_show_one(vty, n);
254
0
}
255
256
void zebra_neigh_init(void)
257
1
{
258
1
  zneigh_info = XCALLOC(MTYPE_ZNEIGH_INFO, sizeof(*zrouter.neigh_info));
259
1
  RB_INIT(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree);
260
1
}
261
262
void zebra_neigh_terminate(void)
263
0
{
264
0
  struct zebra_neigh_ent *n, *next;
265
266
0
  if (!zrouter.neigh_info)
267
0
    return;
268
269
0
  RB_FOREACH_SAFE (n, zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree,
270
0
       next)
271
0
    zebra_neigh_free(n);
272
  XFREE(MTYPE_ZNEIGH_INFO, zneigh_info);
273
0
}