]>
Commit | Line | Data |
---|---|---|
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 | { | |
24 | return XCALLOC(MTYPE_DISTRIBUTE, sizeof(struct distribute)); | |
25 | } | |
26 | ||
27 | /* Free distribute object. */ | |
28 | static 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 | ||
45 | static 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. */ | |
59 | struct 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 | ||
75 | void 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 | ||
82 | void 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 | ||
89 | static 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. */ | |
102 | static 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 | ||
119 | static 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. */ | |
128 | static 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. */ | |
140 | static 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. */ | |
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 | { | |
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. */ | |
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 | { | |
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. */ | |
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 | { | |
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 | ||
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 | ||
247 | int 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 | ||
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 | { | |
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 | ||
284 | static 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 | ||
295 | int 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. */ | |
396 | int 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 | ||
446 | void 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 */ | |
461 | struct 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 | } |