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