Coverage Report

Created: 2026-01-01 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/zebra/table_manager.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* zebra table Manager for routing table identifier management
3
 * Copyright (C) 2018 6WIND
4
 */
5
6
#include "zebra.h"
7
8
#include <stdio.h>
9
#include <string.h>
10
#include <sys/types.h>
11
12
#include "lib/log.h"
13
#include "lib/memory.h"
14
#include "lib/table.h"
15
#include "lib/network.h"
16
#include "lib/stream.h"
17
#include "lib/zclient.h"
18
#include "lib/libfrr.h"
19
#include "lib/vrf.h"
20
21
#include "zebra/zserv.h"
22
#include "zebra/zebra_vrf.h"
23
#include "zebra/label_manager.h" /* for NO_PROTO */
24
#include "zebra/table_manager.h"
25
#include "zebra/zebra_errors.h"
26
27
/* routing table identifiers
28
 *
29
 */
30
#if !defined(GNU_LINUX)
31
/* BSD systems
32
 */
33
#else
34
/* Linux Systems
35
 */
36
0
#define RT_TABLE_ID_LOCAL                  255
37
#define RT_TABLE_ID_MAIN                   254
38
#define RT_TABLE_ID_DEFAULT                253
39
0
#define RT_TABLE_ID_COMPAT                 252
40
#define RT_TABLE_ID_UNSPEC                 0
41
#endif /* !def(GNU_LINUX) */
42
0
#define RT_TABLE_ID_UNRESERVED_MIN         1
43
0
#define RT_TABLE_ID_UNRESERVED_MAX         0xffffffff
44
45
DEFINE_MGROUP(TABLE_MGR, "Table Manager");
46
2
DEFINE_MTYPE_STATIC(TABLE_MGR, TM_CHUNK, "Table Manager Chunk");
47
2
DEFINE_MTYPE_STATIC(TABLE_MGR, TM_TABLE, "Table Manager Context");
48
2
49
2
static void delete_table_chunk(void *val)
50
2
{
51
0
  XFREE(MTYPE_TM_CHUNK, val);
52
0
}
53
54
/**
55
 * Init table manager
56
 */
57
void table_manager_enable(struct zebra_vrf *zvrf)
58
1
{
59
60
1
  if (zvrf->tbl_mgr)
61
0
    return;
62
1
  if (!vrf_is_backend_netns()
63
1
      && strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) {
64
0
    struct zebra_vrf *def = zebra_vrf_lookup_by_id(VRF_DEFAULT);
65
66
0
    if (def)
67
0
      zvrf->tbl_mgr = def->tbl_mgr;
68
0
    return;
69
0
  }
70
1
  zvrf->tbl_mgr = XCALLOC(MTYPE_TM_TABLE, sizeof(struct table_manager));
71
1
  zvrf->tbl_mgr->lc_list = list_new();
72
1
  zvrf->tbl_mgr->lc_list->del = delete_table_chunk;
73
1
}
74
75
/**
76
 * Core function, assigns table chunks
77
 *
78
 * It first searches through the list to check if there's one available
79
 * (previously released). Otherwise it creates and assigns a new one
80
 *
81
 * @param proto Daemon protocol of client, to identify the owner
82
 * @param instance Instance, to identify the owner
83
 * @para size Size of the table chunk
84
 * @return Pointer to the assigned table chunk
85
 */
86
struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
87
                 uint32_t size,
88
                 struct zebra_vrf *zvrf)
89
0
{
90
0
  struct table_manager_chunk *tmc;
91
0
  struct listnode *node;
92
0
  uint32_t start;
93
0
  bool manual_conf = false;
94
95
0
  if (!zvrf)
96
0
    return NULL;
97
98
  /* first check if there's one available */
99
0
  for (ALL_LIST_ELEMENTS_RO(zvrf->tbl_mgr->lc_list, node, tmc)) {
100
0
    if (tmc->proto == NO_PROTO
101
0
        && tmc->end - tmc->start + 1 == size) {
102
0
      tmc->proto = proto;
103
0
      tmc->instance = instance;
104
0
      return tmc;
105
0
    }
106
0
  }
107
  /* otherwise create a new one */
108
0
  tmc = XCALLOC(MTYPE_TM_CHUNK, sizeof(struct table_manager_chunk));
109
110
0
  if (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end)
111
0
    manual_conf = true;
112
  /* table RT IDs range are [1;252] and [256;0xffffffff]
113
   * - check if the requested range can be within the first range,
114
   * otherwise elect second one
115
   * - TODO : vrf-lites have their own table identifier.
116
   * In that case, table_id should be removed from the table range.
117
   */
118
0
  if (list_isempty(zvrf->tbl_mgr->lc_list)) {
119
0
    if (!manual_conf)
120
0
      start = RT_TABLE_ID_UNRESERVED_MIN;
121
0
    else
122
0
      start = zvrf->tbl_mgr->start;
123
0
  } else
124
0
    start = ((struct table_manager_chunk *)listgetdata(
125
0
         listtail(zvrf->tbl_mgr->lc_list)))
126
0
        ->end
127
0
      + 1;
128
129
0
  if (!manual_conf) {
130
131
#if !defined(GNU_LINUX)
132
/* BSD systems
133
 */
134
#else
135
/* Linux Systems
136
 */
137
    /* if not enough room space between MIN and COMPAT,
138
     * then begin after LOCAL
139
     */
140
0
    if (start < RT_TABLE_ID_COMPAT
141
0
        && (size > RT_TABLE_ID_COMPAT - RT_TABLE_ID_UNRESERVED_MIN))
142
0
      start = RT_TABLE_ID_LOCAL + 1;
143
0
#endif /* !def(GNU_LINUX) */
144
0
    tmc->start = start;
145
0
    if (RT_TABLE_ID_UNRESERVED_MAX - size + 1 < start) {
146
0
      flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS,
147
0
         "Reached max table id. Start/Size %u/%u",
148
0
         start, size);
149
0
      XFREE(MTYPE_TM_CHUNK, tmc);
150
0
      return NULL;
151
0
    }
152
0
  } else {
153
0
    tmc->start = start;
154
0
    if (zvrf->tbl_mgr->end - size + 1 < start) {
155
0
      flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS,
156
0
         "Reached max table id. Start/Size %u/%u",
157
0
         start, size);
158
0
      XFREE(MTYPE_TM_CHUNK, tmc);
159
0
      return NULL;
160
0
    }
161
0
  }
162
0
  tmc->end = tmc->start + size - 1;
163
0
  tmc->proto = proto;
164
0
  tmc->instance = instance;
165
0
  listnode_add(zvrf->tbl_mgr->lc_list, tmc);
166
167
0
  return tmc;
168
0
}
169
170
/**
171
 * Core function, release no longer used table chunks
172
 *
173
 * @param proto Daemon protocol of client, to identify the owner
174
 * @param instance Instance, to identify the owner
175
 * @param start First table RT ID of the chunk
176
 * @param end Last table RT ID of the chunk
177
 * @return 0 on success, -1 otherwise
178
 */
179
int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start,
180
      uint32_t end, struct zebra_vrf *zvrf)
181
0
{
182
0
  struct listnode *node;
183
0
  struct table_manager_chunk *tmc;
184
0
  int ret = -1;
185
0
  struct table_manager *tbl_mgr;
186
187
0
  if (!zvrf)
188
0
    return -1;
189
190
0
  tbl_mgr = zvrf->tbl_mgr;
191
0
  if (!tbl_mgr)
192
0
    return ret;
193
  /* check that size matches */
194
0
  zlog_debug("Releasing table chunk: %u - %u", start, end);
195
  /* find chunk and disown */
196
0
  for (ALL_LIST_ELEMENTS_RO(tbl_mgr->lc_list, node, tmc)) {
197
0
    if (tmc->start != start)
198
0
      continue;
199
0
    if (tmc->end != end)
200
0
      continue;
201
0
    if (tmc->proto != proto || tmc->instance != instance) {
202
0
      flog_err(EC_ZEBRA_TM_DAEMON_MISMATCH,
203
0
         "%s: Daemon mismatch!!", __func__);
204
0
      continue;
205
0
    }
206
0
    tmc->proto = NO_PROTO;
207
0
    tmc->instance = 0;
208
0
    ret = 0;
209
0
    break;
210
0
  }
211
0
  if (ret != 0)
212
0
    flog_err(EC_ZEBRA_TM_UNRELEASED_CHUNK,
213
0
       "%s: Table chunk not released!!", __func__);
214
215
0
  return ret;
216
0
}
217
218
/**
219
 * Release table chunks from a client.
220
 *
221
 * Called on client disconnection or reconnection. It only releases chunks
222
 * with empty keep value.
223
 *
224
 * @param client the client to release chunks from
225
 * @return Number of chunks released
226
 */
227
int release_daemon_table_chunks(struct zserv *client)
228
308
{
229
308
  uint8_t proto = client->proto;
230
308
  uint16_t instance = client->instance;
231
308
  struct listnode *node;
232
308
  struct table_manager_chunk *tmc;
233
308
  int count = 0;
234
308
  int ret;
235
308
  struct vrf *vrf;
236
308
  struct zebra_vrf *zvrf;
237
238
308
  RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
239
308
    zvrf = vrf->info;
240
241
308
    if (!zvrf)
242
0
      continue;
243
308
    if (!vrf_is_backend_netns() && vrf->vrf_id != VRF_DEFAULT)
244
0
      continue;
245
308
    for (ALL_LIST_ELEMENTS_RO(zvrf->tbl_mgr->lc_list, node, tmc)) {
246
0
      if (tmc->proto == proto && tmc->instance == instance) {
247
0
        ret = release_table_chunk(
248
0
          tmc->proto, tmc->instance, tmc->start,
249
0
          tmc->end, zvrf);
250
0
        if (ret == 0)
251
0
          count++;
252
0
      }
253
0
    }
254
308
  }
255
308
  zlog_debug("%s: Released %d table chunks", __func__, count);
256
257
308
  return count;
258
308
}
259
260
static void table_range_add(struct zebra_vrf *zvrf, uint32_t start,
261
          uint32_t end)
262
0
{
263
0
  if (!zvrf->tbl_mgr)
264
0
    return;
265
0
  zvrf->tbl_mgr->start = start;
266
0
  zvrf->tbl_mgr->end = end;
267
0
}
268
269
void table_manager_disable(struct zebra_vrf *zvrf)
270
0
{
271
0
  if (!zvrf->tbl_mgr)
272
0
    return;
273
0
  if (!vrf_is_backend_netns()
274
0
      && strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) {
275
0
    zvrf->tbl_mgr = NULL;
276
0
    return;
277
0
  }
278
0
  list_delete(&zvrf->tbl_mgr->lc_list);
279
0
  XFREE(MTYPE_TM_TABLE, zvrf->tbl_mgr);
280
0
  zvrf->tbl_mgr = NULL;
281
0
}
282
283
int table_manager_range(struct vty *vty, bool add, struct zebra_vrf *zvrf,
284
      const char *start_table_str, const char *end_table_str)
285
0
{
286
0
  uint32_t start;
287
0
  uint32_t end;
288
289
0
  if (add) {
290
0
    if (!start_table_str || !end_table_str) {
291
0
      vty_out(vty, "%% Labels not specified\n");
292
0
      return CMD_WARNING_CONFIG_FAILED;
293
0
    }
294
0
    start = atoi(start_table_str);
295
0
    end = atoi(end_table_str);
296
0
    if (end < start) {
297
0
      vty_out(vty, "%% End table is less than Start table\n");
298
0
      return CMD_WARNING_CONFIG_FAILED;
299
0
    }
300
301
#if !defined(GNU_LINUX)
302
/* BSD systems
303
 */
304
#else
305
    /* Linux Systems
306
     */
307
0
    if ((start >= RT_TABLE_ID_COMPAT && start <= RT_TABLE_ID_LOCAL)
308
0
        || (end >= RT_TABLE_ID_COMPAT
309
0
      && end <= RT_TABLE_ID_LOCAL)) {
310
0
      vty_out(vty, "%% Values forbidden in range [%u;%u]\n",
311
0
        RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL);
312
0
      return CMD_WARNING_CONFIG_FAILED;
313
0
    }
314
0
    if (start < RT_TABLE_ID_COMPAT && end > RT_TABLE_ID_LOCAL) {
315
0
      vty_out(vty,
316
0
        "%% Range overlaps range [%u;%u] forbidden\n",
317
0
        RT_TABLE_ID_COMPAT, RT_TABLE_ID_LOCAL);
318
0
      return CMD_WARNING_CONFIG_FAILED;
319
0
    }
320
0
#endif
321
0
    if (zvrf->tbl_mgr
322
0
        && ((zvrf->tbl_mgr->start && zvrf->tbl_mgr->start != start)
323
0
      || (zvrf->tbl_mgr->end && zvrf->tbl_mgr->end != end))) {
324
0
      vty_out(vty,
325
0
        "%% New range will be taken into account at restart\n");
326
0
    }
327
0
    table_range_add(zvrf, start, end);
328
0
  } else
329
0
    table_range_add(zvrf, 0, 0);
330
0
  return CMD_SUCCESS;
331
0
}