Coverage Report

Created: 2025-08-26 06:20

/src/frr/lib/distribute.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* Distribute list functions
3
 * Copyright (C) 1998, 1999 Kunihiro Ishiguro
4
 */
5
6
#include <zebra.h>
7
8
#include "hash.h"
9
#include "if.h"
10
#include "filter.h"
11
#include "command.h"
12
#include "distribute.h"
13
#include "memory.h"
14
15
DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE_CTX, "Distribute ctx");
16
DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE, "Distribute list");
17
DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE_IFNAME, "Dist-list ifname");
18
DEFINE_MTYPE_STATIC(LIB, DISTRIBUTE_NAME, "Dist-list name");
19
20
static struct list *dist_ctx_list;
21
22
static struct distribute *distribute_new(void)
23
0
{
24
0
  return XCALLOC(MTYPE_DISTRIBUTE, sizeof(struct distribute));
25
0
}
26
27
/* Free distribute object. */
28
static void distribute_free(struct distribute *dist)
29
0
{
30
0
  int i = 0;
31
32
0
  XFREE(MTYPE_DISTRIBUTE_IFNAME, dist->ifname);
33
34
0
  for (i = 0; i < DISTRIBUTE_MAX; i++) {
35
0
    XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[i]);
36
0
  }
37
38
0
  for (i = 0; i < DISTRIBUTE_MAX; i++) {
39
0
    XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[i]);
40
0
  }
41
42
0
  XFREE(MTYPE_DISTRIBUTE, dist);
43
0
}
44
45
static void distribute_free_if_empty(struct distribute_ctx *ctx,
46
             struct distribute *dist)
47
0
{
48
0
  int i;
49
50
0
  for (i = 0; i < DISTRIBUTE_MAX; i++)
51
0
    if (dist->list[i] != NULL || dist->prefix[i] != NULL)
52
0
      return;
53
54
0
  hash_release(ctx->disthash, dist);
55
0
  distribute_free(dist);
56
0
}
57
58
/* Lookup interface's distribute list. */
59
struct distribute *distribute_lookup(struct distribute_ctx *ctx,
60
             const char *ifname)
61
0
{
62
0
  struct distribute key;
63
0
  struct distribute *dist;
64
65
  /* temporary reference */
66
0
  key.ifname = (ifname) ? XSTRDUP(MTYPE_DISTRIBUTE_IFNAME, ifname) : NULL;
67
68
0
  dist = hash_lookup(ctx->disthash, &key);
69
70
0
  XFREE(MTYPE_DISTRIBUTE_IFNAME, key.ifname);
71
72
0
  return dist;
73
0
}
74
75
void distribute_list_add_hook(struct distribute_ctx *ctx,
76
            void (*func)(struct distribute_ctx *ctx,
77
             struct distribute *))
78
0
{
79
0
  ctx->distribute_add_hook = func;
80
0
}
81
82
void distribute_list_delete_hook(struct distribute_ctx *ctx,
83
         void (*func)(struct distribute_ctx *ctx,
84
                struct distribute *))
85
0
{
86
0
  ctx->distribute_delete_hook = func;
87
0
}
88
89
static void *distribute_hash_alloc(struct distribute *arg)
90
0
{
91
0
  struct distribute *dist;
92
93
0
  dist = distribute_new();
94
0
  if (arg->ifname)
95
0
    dist->ifname = XSTRDUP(MTYPE_DISTRIBUTE_IFNAME, arg->ifname);
96
0
  else
97
0
    dist->ifname = NULL;
98
0
  return dist;
99
0
}
100
101
/* Make new distribute list and push into hash. */
102
static struct distribute *distribute_get(struct distribute_ctx *ctx,
103
           const char *ifname)
104
0
{
105
0
  struct distribute key;
106
0
  struct distribute *ret;
107
108
  /* temporary reference */
109
0
  key.ifname = (ifname) ? XSTRDUP(MTYPE_DISTRIBUTE_IFNAME, ifname) : NULL;
110
111
0
  ret = hash_get(ctx->disthash, &key,
112
0
           (void *(*)(void *))distribute_hash_alloc);
113
114
0
  XFREE(MTYPE_DISTRIBUTE_IFNAME, key.ifname);
115
116
0
  return ret;
117
0
}
118
119
static unsigned int distribute_hash_make(const void *arg)
120
0
{
121
0
  const struct distribute *dist = arg;
122
123
0
  return dist->ifname ? string_hash_make(dist->ifname) : 0;
124
0
}
125
126
/* If two distribute-list have same value then return 1 else return
127
   0. This function is used by hash package. */
128
static bool distribute_cmp(const struct distribute *dist1,
129
        const struct distribute *dist2)
130
0
{
131
0
  if (dist1->ifname && dist2->ifname)
132
0
    if (strcmp(dist1->ifname, dist2->ifname) == 0)
133
0
      return true;
134
0
  if (!dist1->ifname && !dist2->ifname)
135
0
    return true;
136
0
  return false;
137
0
}
138
139
/* Set access-list name to the distribute list. */
140
static void distribute_list_set(struct distribute_ctx *ctx,
141
        const char *ifname, enum distribute_type type,
142
        const char *alist_name)
143
0
{
144
0
  struct distribute *dist;
145
146
0
  dist = distribute_get(ctx, ifname);
147
148
0
  XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]);
149
0
  dist->list[type] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, alist_name);
150
151
  /* Apply this distribute-list to the interface. */
152
0
  (ctx->distribute_add_hook)(ctx, dist);
153
0
}
154
155
/* Unset distribute-list.  If matched distribute-list exist then
156
   return 1. */
157
static int distribute_list_unset(struct distribute_ctx *ctx,
158
         const char *ifname,
159
         enum distribute_type type,
160
         const char *alist_name)
161
0
{
162
0
  struct distribute *dist;
163
164
0
  dist = distribute_lookup(ctx, ifname);
165
0
  if (!dist)
166
0
    return 0;
167
168
0
  if (!dist->list[type])
169
0
    return 0;
170
0
  if (strcmp(dist->list[type], alist_name) != 0)
171
0
    return 0;
172
173
0
  XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]);
174
175
  /* Apply this distribute-list to the interface. */
176
0
  (ctx->distribute_delete_hook)(ctx, dist);
177
178
  /* If all dist are NULL, then free distribute list. */
179
0
  distribute_free_if_empty(ctx, dist);
180
0
  return 1;
181
0
}
182
183
/* Set access-list name to the distribute list. */
184
static void distribute_list_prefix_set(struct distribute_ctx *ctx,
185
               const char *ifname,
186
               enum distribute_type type,
187
               const char *plist_name)
188
0
{
189
0
  struct distribute *dist;
190
191
0
  dist = distribute_get(ctx, ifname);
192
193
0
  XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]);
194
0
  dist->prefix[type] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, plist_name);
195
196
  /* Apply this distribute-list to the interface. */
197
0
  (ctx->distribute_add_hook)(ctx, dist);
198
0
}
199
200
/* Unset distribute-list.  If matched distribute-list exist then
201
   return 1. */
202
static int distribute_list_prefix_unset(struct distribute_ctx *ctx,
203
          const char *ifname,
204
          enum distribute_type type,
205
          const char *plist_name)
206
0
{
207
0
  struct distribute *dist;
208
209
0
  dist = distribute_lookup(ctx, ifname);
210
0
  if (!dist)
211
0
    return 0;
212
213
0
  if (!dist->prefix[type])
214
0
    return 0;
215
0
  if (strcmp(dist->prefix[type], plist_name) != 0)
216
0
    return 0;
217
218
0
  XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]);
219
220
  /* Apply this distribute-list to the interface. */
221
0
  (ctx->distribute_delete_hook)(ctx, dist);
222
223
  /* If all dist are NULL, then free distribute list. */
224
0
  distribute_free_if_empty(ctx, dist);
225
0
  return 1;
226
0
}
227
228
static enum distribute_type distribute_direction(const char *dir, bool v4)
229
0
{
230
0
  if (dir[0] == 'i') {
231
0
    if (v4)
232
0
      return DISTRIBUTE_V4_IN;
233
0
    else
234
0
      return DISTRIBUTE_V6_IN;
235
0
  } else if (dir[0] == 'o') {
236
0
    if (v4)
237
0
      return DISTRIBUTE_V4_OUT;
238
0
    else
239
0
      return DISTRIBUTE_V6_OUT;
240
0
  }
241
242
0
  assert(!"Expecting in or out only, fix your code");
243
244
0
  __builtin_unreachable();
245
0
}
246
247
int distribute_list_parser(bool prefix, bool v4, const char *dir,
248
         const char *list, const char *ifname)
249
0
{
250
0
  enum distribute_type type = distribute_direction(dir, v4);
251
0
  struct distribute_ctx *ctx = listnode_head(dist_ctx_list);
252
253
0
  void (*distfn)(struct distribute_ctx *, const char *,
254
0
           enum distribute_type, const char *) =
255
0
    prefix ? &distribute_list_prefix_set : &distribute_list_set;
256
257
0
  distfn(ctx, ifname, type, list);
258
259
0
  return CMD_SUCCESS;
260
0
}
261
262
int distribute_list_no_parser(struct vty *vty, bool prefix, bool v4,
263
            const char *dir, const char *list,
264
            const char *ifname)
265
0
{
266
0
  enum distribute_type type = distribute_direction(dir, v4);
267
0
  struct distribute_ctx *ctx = listnode_head(dist_ctx_list);
268
0
  int ret;
269
270
0
  int (*distfn)(struct distribute_ctx *, const char *,
271
0
          enum distribute_type, const char *) =
272
0
    prefix ? &distribute_list_prefix_unset : &distribute_list_unset;
273
274
275
0
  ret = distfn(ctx, ifname, type, list);
276
0
  if (!ret) {
277
0
    vty_out(vty, "distribute list doesn't exist\n");
278
0
    return CMD_WARNING_CONFIG_FAILED;
279
0
  }
280
281
0
  return CMD_SUCCESS;
282
0
}
283
284
static int distribute_print(struct vty *vty, char *tab[], int is_prefix,
285
          enum distribute_type type, int has_print)
286
0
{
287
0
  if (tab[type]) {
288
0
    vty_out(vty, "%s %s%s", has_print ? "," : "",
289
0
      is_prefix ? "(prefix-list) " : "", tab[type]);
290
0
    return 1;
291
0
  }
292
0
  return has_print;
293
0
}
294
295
int config_show_distribute(struct vty *vty, struct distribute_ctx *dist_ctxt)
296
0
{
297
0
  unsigned int i;
298
0
  int has_print = 0;
299
0
  struct hash_bucket *mp;
300
0
  struct distribute *dist;
301
302
  /* Output filter configuration. */
303
0
  dist = distribute_lookup(dist_ctxt, NULL);
304
0
  vty_out(vty, "  Outgoing update filter list for all interface is");
305
0
  has_print = 0;
306
0
  if (dist) {
307
0
    has_print = distribute_print(vty, dist->list, 0,
308
0
               DISTRIBUTE_V4_OUT, has_print);
309
0
    has_print = distribute_print(vty, dist->prefix, 1,
310
0
               DISTRIBUTE_V4_OUT, has_print);
311
0
    has_print = distribute_print(vty, dist->list, 0,
312
0
               DISTRIBUTE_V6_OUT, has_print);
313
0
    has_print = distribute_print(vty, dist->prefix, 1,
314
0
               DISTRIBUTE_V6_OUT, has_print);
315
0
  }
316
0
  if (has_print)
317
0
    vty_out(vty, "\n");
318
0
  else
319
0
    vty_out(vty, " not set\n");
320
321
0
  for (i = 0; i < dist_ctxt->disthash->size; i++)
322
0
    for (mp = dist_ctxt->disthash->index[i]; mp; mp = mp->next) {
323
0
      dist = mp->data;
324
0
      if (dist->ifname) {
325
0
        vty_out(vty, "    %s filtered by",
326
0
          dist->ifname);
327
0
        has_print = 0;
328
0
        has_print = distribute_print(vty, dist->list, 0,
329
0
                   DISTRIBUTE_V4_OUT,
330
0
                   has_print);
331
0
        has_print = distribute_print(
332
0
          vty, dist->prefix, 1, DISTRIBUTE_V4_OUT,
333
0
          has_print);
334
0
        has_print = distribute_print(vty, dist->list, 0,
335
0
                   DISTRIBUTE_V6_OUT,
336
0
                   has_print);
337
0
        has_print = distribute_print(
338
0
          vty, dist->prefix, 1, DISTRIBUTE_V6_OUT,
339
0
          has_print);
340
0
        if (has_print)
341
0
          vty_out(vty, "\n");
342
0
        else
343
0
          vty_out(vty, " nothing\n");
344
0
      }
345
0
    }
346
347
348
  /* Input filter configuration. */
349
0
  dist = distribute_lookup(dist_ctxt, NULL);
350
0
  vty_out(vty, "  Incoming update filter list for all interface is");
351
0
  has_print = 0;
352
0
  if (dist) {
353
0
    has_print = distribute_print(vty, dist->list, 0,
354
0
               DISTRIBUTE_V4_IN, has_print);
355
0
    has_print = distribute_print(vty, dist->prefix, 1,
356
0
               DISTRIBUTE_V4_IN, has_print);
357
0
    has_print = distribute_print(vty, dist->list, 0,
358
0
               DISTRIBUTE_V6_IN, has_print);
359
0
    has_print = distribute_print(vty, dist->prefix, 1,
360
0
               DISTRIBUTE_V6_IN, has_print);
361
0
  }
362
0
  if (has_print)
363
0
    vty_out(vty, "\n");
364
0
  else
365
0
    vty_out(vty, " not set\n");
366
367
0
  for (i = 0; i < dist_ctxt->disthash->size; i++)
368
0
    for (mp = dist_ctxt->disthash->index[i]; mp; mp = mp->next) {
369
0
      dist = mp->data;
370
0
      if (dist->ifname) {
371
0
        vty_out(vty, "    %s filtered by",
372
0
          dist->ifname);
373
0
        has_print = 0;
374
0
        has_print = distribute_print(vty, dist->list, 0,
375
0
                   DISTRIBUTE_V4_IN,
376
0
                   has_print);
377
0
        has_print = distribute_print(
378
0
          vty, dist->prefix, 1, DISTRIBUTE_V4_IN,
379
0
          has_print);
380
0
        has_print = distribute_print(vty, dist->list, 0,
381
0
                   DISTRIBUTE_V6_IN,
382
0
                   has_print);
383
0
        has_print = distribute_print(
384
0
          vty, dist->prefix, 1, DISTRIBUTE_V6_IN,
385
0
          has_print);
386
0
        if (has_print)
387
0
          vty_out(vty, "\n");
388
0
        else
389
0
          vty_out(vty, " nothing\n");
390
0
      }
391
0
    }
392
0
  return 0;
393
0
}
394
395
/* Configuration write function. */
396
int config_write_distribute(struct vty *vty,
397
          struct distribute_ctx *dist_ctxt)
398
0
{
399
0
  unsigned int i;
400
0
  int j;
401
0
  int output, v6;
402
0
  struct hash_bucket *mp;
403
0
  int write = 0;
404
405
0
  for (i = 0; i < dist_ctxt->disthash->size; i++)
406
0
    for (mp = dist_ctxt->disthash->index[i]; mp; mp = mp->next) {
407
0
      struct distribute *dist;
408
409
0
      dist = mp->data;
410
411
0
      for (j = 0; j < DISTRIBUTE_MAX; j++)
412
0
        if (dist->list[j]) {
413
0
          output = j == DISTRIBUTE_V4_OUT
414
0
             || j == DISTRIBUTE_V6_OUT;
415
0
          v6 = j == DISTRIBUTE_V6_IN
416
0
               || j == DISTRIBUTE_V6_OUT;
417
0
          vty_out(vty,
418
0
            " %sdistribute-list %s %s %s\n",
419
0
            v6 ? "ipv6 " : "",
420
0
            dist->list[j],
421
0
            output ? "out" : "in",
422
0
            dist->ifname ? dist->ifname
423
0
                   : "");
424
0
          write++;
425
0
        }
426
427
0
      for (j = 0; j < DISTRIBUTE_MAX; j++)
428
0
        if (dist->prefix[j]) {
429
0
          output = j == DISTRIBUTE_V4_OUT
430
0
             || j == DISTRIBUTE_V6_OUT;
431
0
          v6 = j == DISTRIBUTE_V6_IN
432
0
               || j == DISTRIBUTE_V6_OUT;
433
0
          vty_out(vty,
434
0
            " %sdistribute-list prefix %s %s %s\n",
435
0
            v6 ? "ipv6 " : "",
436
0
            dist->prefix[j],
437
0
            output ? "out" : "in",
438
0
            dist->ifname ? dist->ifname
439
0
                   : "");
440
0
          write++;
441
0
        }
442
0
    }
443
0
  return write;
444
0
}
445
446
void distribute_list_delete(struct distribute_ctx **ctx)
447
0
{
448
0
  hash_clean_and_free(&(*ctx)->disthash,
449
0
          (void (*)(void *))distribute_free);
450
451
0
  if (dist_ctx_list) {
452
0
    listnode_delete(dist_ctx_list, *ctx);
453
0
    if (list_isempty(dist_ctx_list))
454
0
      list_delete(&dist_ctx_list);
455
0
  }
456
457
0
  XFREE(MTYPE_DISTRIBUTE_CTX, (*ctx));
458
0
}
459
460
/* Initialize distribute list container */
461
struct distribute_ctx *distribute_list_ctx_create(struct vrf *vrf)
462
0
{
463
0
  struct distribute_ctx *ctx;
464
465
0
  ctx = XCALLOC(MTYPE_DISTRIBUTE_CTX, sizeof(struct distribute_ctx));
466
0
  ctx->vrf = vrf;
467
0
  ctx->disthash = hash_create(
468
0
    distribute_hash_make,
469
0
    (bool (*)(const void *, const void *))distribute_cmp, NULL);
470
0
  if (!dist_ctx_list)
471
0
    dist_ctx_list = list_new();
472
0
  listnode_add(dist_ctx_list, ctx);
473
0
  return ctx;
474
0
}