]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
718e3744 | 2 | /* Distribute list functions |
3 | * Copyright (C) 1998, 1999 Kunihiro Ishiguro | |
718e3744 | 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 | ||
bf8d3d6a DL |
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"); | |
4a1ab8e4 | 19 | |
c17faa4b | 20 | static struct list *dist_ctx_list; |
6b0655a2 | 21 | |
d62a17ae | 22 | static struct distribute *distribute_new(void) |
718e3744 | 23 | { |
d62a17ae | 24 | return XCALLOC(MTYPE_DISTRIBUTE, sizeof(struct distribute)); |
718e3744 | 25 | } |
26 | ||
27 | /* Free distribute object. */ | |
d62a17ae | 28 | static void distribute_free(struct distribute *dist) |
718e3744 | 29 | { |
d62a17ae | 30 | int i = 0; |
ee5bb561 | 31 | |
0a22ddfb | 32 | XFREE(MTYPE_DISTRIBUTE_IFNAME, dist->ifname); |
718e3744 | 33 | |
0a22ddfb QY |
34 | for (i = 0; i < DISTRIBUTE_MAX; i++) { |
35 | XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[i]); | |
36 | } | |
718e3744 | 37 | |
0a22ddfb QY |
38 | for (i = 0; i < DISTRIBUTE_MAX; i++) { |
39 | XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[i]); | |
40 | } | |
718e3744 | 41 | |
d62a17ae | 42 | XFREE(MTYPE_DISTRIBUTE, dist); |
718e3744 | 43 | } |
44 | ||
03a38493 PG |
45 | static void distribute_free_if_empty(struct distribute_ctx *ctx, |
46 | struct distribute *dist) | |
ee5bb561 | 47 | { |
d62a17ae | 48 | int i; |
ee5bb561 | 49 | |
d62a17ae | 50 | for (i = 0; i < DISTRIBUTE_MAX; i++) |
51 | if (dist->list[i] != NULL || dist->prefix[i] != NULL) | |
52 | return; | |
ee5bb561 | 53 | |
03a38493 | 54 | hash_release(ctx->disthash, dist); |
d62a17ae | 55 | distribute_free(dist); |
ee5bb561 MB |
56 | } |
57 | ||
718e3744 | 58 | /* Lookup interface's distribute list. */ |
03a38493 PG |
59 | struct distribute *distribute_lookup(struct distribute_ctx *ctx, |
60 | const char *ifname) | |
718e3744 | 61 | { |
d62a17ae | 62 | struct distribute key; |
63 | struct distribute *dist; | |
718e3744 | 64 | |
d62a17ae | 65 | /* temporary reference */ |
66 | key.ifname = (ifname) ? XSTRDUP(MTYPE_DISTRIBUTE_IFNAME, ifname) : NULL; | |
718e3744 | 67 | |
03a38493 | 68 | dist = hash_lookup(ctx->disthash, &key); |
3a7c85d1 | 69 | |
0a22ddfb | 70 | XFREE(MTYPE_DISTRIBUTE_IFNAME, key.ifname); |
24873f0c | 71 | |
d62a17ae | 72 | return dist; |
718e3744 | 73 | } |
74 | ||
03a38493 PG |
75 | void distribute_list_add_hook(struct distribute_ctx *ctx, |
76 | void (*func)(struct distribute_ctx *ctx, | |
77 | struct distribute *)) | |
718e3744 | 78 | { |
03a38493 | 79 | ctx->distribute_add_hook = func; |
718e3744 | 80 | } |
81 | ||
03a38493 PG |
82 | void distribute_list_delete_hook(struct distribute_ctx *ctx, |
83 | void (*func)(struct distribute_ctx *ctx, | |
84 | struct distribute *)) | |
718e3744 | 85 | { |
03a38493 | 86 | ctx->distribute_delete_hook = func; |
718e3744 | 87 | } |
88 | ||
d62a17ae | 89 | static void *distribute_hash_alloc(struct distribute *arg) |
718e3744 | 90 | { |
d62a17ae | 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; | |
718e3744 | 99 | } |
100 | ||
101 | /* Make new distribute list and push into hash. */ | |
03a38493 PG |
102 | static struct distribute *distribute_get(struct distribute_ctx *ctx, |
103 | const char *ifname) | |
718e3744 | 104 | { |
d62a17ae | 105 | struct distribute key; |
106 | struct distribute *ret; | |
107 | ||
108 | /* temporary reference */ | |
109 | key.ifname = (ifname) ? XSTRDUP(MTYPE_DISTRIBUTE_IFNAME, ifname) : NULL; | |
718e3744 | 110 | |
03a38493 | 111 | ret = hash_get(ctx->disthash, &key, |
d62a17ae | 112 | (void *(*)(void *))distribute_hash_alloc); |
24873f0c | 113 | |
0a22ddfb | 114 | XFREE(MTYPE_DISTRIBUTE_IFNAME, key.ifname); |
3a7c85d1 | 115 | |
d62a17ae | 116 | return ret; |
718e3744 | 117 | } |
118 | ||
d8b87afe | 119 | static unsigned int distribute_hash_make(const void *arg) |
718e3744 | 120 | { |
d62a17ae | 121 | const struct distribute *dist = arg; |
718e3744 | 122 | |
d62a17ae | 123 | return dist->ifname ? string_hash_make(dist->ifname) : 0; |
718e3744 | 124 | } |
125 | ||
126 | /* If two distribute-list have same value then return 1 else return | |
127 | 0. This function is used by hash package. */ | |
74df8d6d | 128 | static bool distribute_cmp(const struct distribute *dist1, |
d62a17ae | 129 | const struct distribute *dist2) |
718e3744 | 130 | { |
d62a17ae | 131 | if (dist1->ifname && dist2->ifname) |
132 | if (strcmp(dist1->ifname, dist2->ifname) == 0) | |
74df8d6d | 133 | return true; |
d62a17ae | 134 | if (!dist1->ifname && !dist2->ifname) |
74df8d6d DS |
135 | return true; |
136 | return false; | |
718e3744 | 137 | } |
6b0655a2 | 138 | |
718e3744 | 139 | /* Set access-list name to the distribute list. */ |
03a38493 PG |
140 | static void distribute_list_set(struct distribute_ctx *ctx, |
141 | const char *ifname, enum distribute_type type, | |
d62a17ae | 142 | const char *alist_name) |
718e3744 | 143 | { |
d62a17ae | 144 | struct distribute *dist; |
718e3744 | 145 | |
03a38493 | 146 | dist = distribute_get(ctx, ifname); |
718e3744 | 147 | |
0a22ddfb | 148 | XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]); |
d62a17ae | 149 | dist->list[type] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, alist_name); |
718e3744 | 150 | |
d62a17ae | 151 | /* Apply this distribute-list to the interface. */ |
03a38493 | 152 | (ctx->distribute_add_hook)(ctx, dist); |
718e3744 | 153 | } |
154 | ||
155 | /* Unset distribute-list. If matched distribute-list exist then | |
156 | return 1. */ | |
03a38493 PG |
157 | static int distribute_list_unset(struct distribute_ctx *ctx, |
158 | const char *ifname, | |
159 | enum distribute_type type, | |
d62a17ae | 160 | const char *alist_name) |
718e3744 | 161 | { |
d62a17ae | 162 | struct distribute *dist; |
718e3744 | 163 | |
03a38493 | 164 | dist = distribute_lookup(ctx, ifname); |
d62a17ae | 165 | if (!dist) |
166 | return 0; | |
718e3744 | 167 | |
d62a17ae | 168 | if (!dist->list[type]) |
169 | return 0; | |
170 | if (strcmp(dist->list[type], alist_name) != 0) | |
171 | return 0; | |
718e3744 | 172 | |
d62a17ae | 173 | XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]); |
718e3744 | 174 | |
d62a17ae | 175 | /* Apply this distribute-list to the interface. */ |
03a38493 | 176 | (ctx->distribute_delete_hook)(ctx, dist); |
718e3744 | 177 | |
d62a17ae | 178 | /* If all dist are NULL, then free distribute list. */ |
03a38493 | 179 | distribute_free_if_empty(ctx, dist); |
d62a17ae | 180 | return 1; |
718e3744 | 181 | } |
182 | ||
183 | /* Set access-list name to the distribute list. */ | |
03a38493 PG |
184 | static void distribute_list_prefix_set(struct distribute_ctx *ctx, |
185 | const char *ifname, | |
d62a17ae | 186 | enum distribute_type type, |
187 | const char *plist_name) | |
718e3744 | 188 | { |
d62a17ae | 189 | struct distribute *dist; |
718e3744 | 190 | |
03a38493 | 191 | dist = distribute_get(ctx, ifname); |
718e3744 | 192 | |
0a22ddfb | 193 | XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]); |
d62a17ae | 194 | dist->prefix[type] = XSTRDUP(MTYPE_DISTRIBUTE_NAME, plist_name); |
718e3744 | 195 | |
d62a17ae | 196 | /* Apply this distribute-list to the interface. */ |
03a38493 | 197 | (ctx->distribute_add_hook)(ctx, dist); |
718e3744 | 198 | } |
199 | ||
200 | /* Unset distribute-list. If matched distribute-list exist then | |
201 | return 1. */ | |
03a38493 PG |
202 | static int distribute_list_prefix_unset(struct distribute_ctx *ctx, |
203 | const char *ifname, | |
d62a17ae | 204 | enum distribute_type type, |
205 | const char *plist_name) | |
718e3744 | 206 | { |
d62a17ae | 207 | struct distribute *dist; |
718e3744 | 208 | |
03a38493 | 209 | dist = distribute_lookup(ctx, ifname); |
d62a17ae | 210 | if (!dist) |
211 | return 0; | |
718e3744 | 212 | |
d62a17ae | 213 | if (!dist->prefix[type]) |
214 | return 0; | |
215 | if (strcmp(dist->prefix[type], plist_name) != 0) | |
216 | return 0; | |
718e3744 | 217 | |
d62a17ae | 218 | XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]); |
718e3744 | 219 | |
d62a17ae | 220 | /* Apply this distribute-list to the interface. */ |
03a38493 | 221 | (ctx->distribute_delete_hook)(ctx, dist); |
718e3744 | 222 | |
d62a17ae | 223 | /* If all dist are NULL, then free distribute list. */ |
03a38493 | 224 | distribute_free_if_empty(ctx, dist); |
d62a17ae | 225 | return 1; |
718e3744 | 226 | } |
227 | ||
df19c76a DS |
228 | static 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 | ||
458133db DS |
247 | int distribute_list_parser(bool prefix, bool v4, const char *dir, |
248 | const char *list, const char *ifname) | |
df19c76a DS |
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 | ||
458133db DS |
262 | int distribute_list_no_parser(struct vty *vty, bool prefix, bool v4, |
263 | const char *dir, const char *list, | |
264 | const char *ifname) | |
df19c76a DS |
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 | } | |
d62a17ae | 280 | |
281 | return CMD_SUCCESS; | |
fb23cf4a | 282 | } |
d62a17ae | 283 | |
d62a17ae | 284 | static int distribute_print(struct vty *vty, char *tab[], int is_prefix, |
285 | enum distribute_type type, int has_print) | |
ee5bb561 | 286 | { |
d62a17ae | 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; | |
ee5bb561 MB |
293 | } |
294 | ||
03a38493 | 295 | int config_show_distribute(struct vty *vty, struct distribute_ctx *dist_ctxt) |
718e3744 | 296 | { |
d62a17ae | 297 | unsigned int i; |
298 | int has_print = 0; | |
e3b78da8 | 299 | struct hash_bucket *mp; |
d62a17ae | 300 | struct distribute *dist; |
301 | ||
302 | /* Output filter configuration. */ | |
03a38493 | 303 | dist = distribute_lookup(dist_ctxt, NULL); |
d62a17ae | 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 | ||
03a38493 PG |
321 | for (i = 0; i < dist_ctxt->disthash->size; i++) |
322 | for (mp = dist_ctxt->disthash->index[i]; mp; mp = mp->next) { | |
d62a17ae | 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. */ | |
03a38493 | 349 | dist = distribute_lookup(dist_ctxt, NULL); |
d62a17ae | 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 | ||
03a38493 PG |
367 | for (i = 0; i < dist_ctxt->disthash->size; i++) |
368 | for (mp = dist_ctxt->disthash->index[i]; mp; mp = mp->next) { | |
d62a17ae | 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; | |
718e3744 | 393 | } |
394 | ||
395 | /* Configuration write function. */ | |
03a38493 PG |
396 | int config_write_distribute(struct vty *vty, |
397 | struct distribute_ctx *dist_ctxt) | |
718e3744 | 398 | { |
d62a17ae | 399 | unsigned int i; |
400 | int j; | |
401 | int output, v6; | |
e3b78da8 | 402 | struct hash_bucket *mp; |
d62a17ae | 403 | int write = 0; |
404 | ||
03a38493 PG |
405 | for (i = 0; i < dist_ctxt->disthash->size; i++) |
406 | for (mp = dist_ctxt->disthash->index[i]; mp; mp = mp->next) { | |
d62a17ae | 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; | |
718e3744 | 444 | } |
445 | ||
03a38493 | 446 | void distribute_list_delete(struct distribute_ctx **ctx) |
718e3744 | 447 | { |
03a38493 PG |
448 | if ((*ctx)->disthash) { |
449 | hash_clean((*ctx)->disthash, (void (*)(void *))distribute_free); | |
450 | } | |
451 | if (!dist_ctx_list) | |
452 | dist_ctx_list = list_new(); | |
453 | listnode_delete(dist_ctx_list, *ctx); | |
454 | if (list_isempty(dist_ctx_list)) | |
455 | list_delete(&dist_ctx_list); | |
456 | XFREE(MTYPE_DISTRIBUTE_CTX, (*ctx)); | |
718e3744 | 457 | } |
458 | ||
03a38493 PG |
459 | /* Initialize distribute list container */ |
460 | struct distribute_ctx *distribute_list_ctx_create(struct vrf *vrf) | |
718e3744 | 461 | { |
03a38493 PG |
462 | struct distribute_ctx *ctx; |
463 | ||
464 | ctx = XCALLOC(MTYPE_DISTRIBUTE_CTX, sizeof(struct distribute_ctx)); | |
465 | ctx->vrf = vrf; | |
466 | ctx->disthash = hash_create( | |
d62a17ae | 467 | distribute_hash_make, |
74df8d6d | 468 | (bool (*)(const void *, const void *))distribute_cmp, NULL); |
03a38493 PG |
469 | if (!dist_ctx_list) |
470 | dist_ctx_list = list_new(); | |
471 | listnode_add(dist_ctx_list, ctx); | |
472 | return ctx; | |
473 | } |