]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
718e3744 | 2 | /* Route filtering function. |
3 | * Copyright (C) 1998, 1999 Kunihiro Ishiguro | |
718e3744 | 4 | */ |
5 | ||
6 | #include <zebra.h> | |
7 | ||
8 | #include "prefix.h" | |
9 | #include "filter.h" | |
10 | #include "memory.h" | |
11 | #include "command.h" | |
12 | #include "sockunion.h" | |
13 | #include "buffer.h" | |
fbf5d033 | 14 | #include "log.h" |
518f0eb1 | 15 | #include "routemap.h" |
b85120bc | 16 | #include "libfrr.h" |
1d3c4b66 | 17 | #include "northbound_cli.h" |
068ab901 | 18 | #include "json.h" |
718e3744 | 19 | |
bf8d3d6a DL |
20 | DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST, "Access List"); |
21 | DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST_STR, "Access List Str"); | |
22 | DEFINE_MTYPE_STATIC(LIB, ACCESS_FILTER, "Access Filter"); | |
d62a17ae | 23 | |
b34fd35d | 24 | /* Static structure for mac access_list's master. */ |
d37ba549 | 25 | static struct access_master access_master_mac = { |
d37ba549 MK |
26 | {NULL, NULL}, |
27 | NULL, | |
28 | NULL, | |
29 | }; | |
30 | ||
718e3744 | 31 | /* Static structure for IPv4 access_list's master. */ |
d62a17ae | 32 | static struct access_master access_master_ipv4 = { |
d62a17ae | 33 | {NULL, NULL}, |
34 | NULL, | |
35 | NULL, | |
718e3744 | 36 | }; |
37 | ||
718e3744 | 38 | /* Static structure for IPv6 access_list's master. */ |
d62a17ae | 39 | static struct access_master access_master_ipv6 = { |
d62a17ae | 40 | {NULL, NULL}, |
41 | NULL, | |
42 | NULL, | |
718e3744 | 43 | }; |
6b0655a2 | 44 | |
d62a17ae | 45 | static struct access_master *access_master_get(afi_t afi) |
718e3744 | 46 | { |
d62a17ae | 47 | if (afi == AFI_IP) |
48 | return &access_master_ipv4; | |
49 | else if (afi == AFI_IP6) | |
50 | return &access_master_ipv6; | |
d37ba549 MK |
51 | else if (afi == AFI_L2VPN) |
52 | return &access_master_mac; | |
d62a17ae | 53 | return NULL; |
718e3744 | 54 | } |
55 | ||
56 | /* Allocate new filter structure. */ | |
4cf24501 | 57 | struct filter *filter_new(void) |
718e3744 | 58 | { |
9f5dc319 | 59 | return XCALLOC(MTYPE_ACCESS_FILTER, sizeof(struct filter)); |
718e3744 | 60 | } |
61 | ||
d62a17ae | 62 | static void filter_free(struct filter *filter) |
718e3744 | 63 | { |
d62a17ae | 64 | XFREE(MTYPE_ACCESS_FILTER, filter); |
718e3744 | 65 | } |
66 | ||
67 | /* Return string of filter_type. */ | |
d62a17ae | 68 | static const char *filter_type_str(struct filter *filter) |
69 | { | |
70 | switch (filter->type) { | |
71 | case FILTER_PERMIT: | |
72 | return "permit"; | |
d62a17ae | 73 | case FILTER_DENY: |
74 | return "deny"; | |
d62a17ae | 75 | case FILTER_DYNAMIC: |
76 | return "dynamic"; | |
d62a17ae | 77 | default: |
78 | return ""; | |
d62a17ae | 79 | } |
718e3744 | 80 | } |
81 | ||
82 | /* If filter match to the prefix then return 1. */ | |
123214ef | 83 | static int filter_match_cisco(struct filter *mfilter, const struct prefix *p) |
718e3744 | 84 | { |
d62a17ae | 85 | struct filter_cisco *filter; |
86 | struct in_addr mask; | |
d7c0a89a QY |
87 | uint32_t check_addr; |
88 | uint32_t check_mask; | |
718e3744 | 89 | |
d62a17ae | 90 | filter = &mfilter->u.cfilter; |
91 | check_addr = p->u.prefix4.s_addr & ~filter->addr_mask.s_addr; | |
718e3744 | 92 | |
d62a17ae | 93 | if (filter->extended) { |
94 | masklen2ip(p->prefixlen, &mask); | |
95 | check_mask = mask.s_addr & ~filter->mask_mask.s_addr; | |
718e3744 | 96 | |
8643c2e5 DA |
97 | if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN) |
98 | == 0 | |
99 | && memcmp(&check_mask, &filter->mask.s_addr, | |
100 | IPV4_MAX_BYTELEN) | |
101 | == 0) | |
d62a17ae | 102 | return 1; |
8643c2e5 DA |
103 | } else if (memcmp(&check_addr, &filter->addr.s_addr, IPV4_MAX_BYTELEN) |
104 | == 0) | |
d62a17ae | 105 | return 1; |
718e3744 | 106 | |
d62a17ae | 107 | return 0; |
718e3744 | 108 | } |
109 | ||
110 | /* If filter match to the prefix then return 1. */ | |
123214ef | 111 | static int filter_match_zebra(struct filter *mfilter, const struct prefix *p) |
718e3744 | 112 | { |
d37ba549 | 113 | struct filter_zebra *filter = NULL; |
718e3744 | 114 | |
d62a17ae | 115 | filter = &mfilter->u.zfilter; |
718e3744 | 116 | |
3b0f6068 DL |
117 | if (filter->prefix.family == p->family) { |
118 | if (filter->exact) { | |
119 | if (filter->prefix.prefixlen == p->prefixlen) | |
d62a17ae | 120 | return prefix_match(&filter->prefix, p); |
3b0f6068 DL |
121 | else |
122 | return 0; | |
d62a17ae | 123 | } else |
3b0f6068 DL |
124 | return prefix_match(&filter->prefix, p); |
125 | } else | |
126 | return 0; | |
718e3744 | 127 | } |
6b0655a2 | 128 | |
718e3744 | 129 | /* Allocate new access list structure. */ |
d62a17ae | 130 | static struct access_list *access_list_new(void) |
718e3744 | 131 | { |
9f5dc319 | 132 | return XCALLOC(MTYPE_ACCESS_LIST, sizeof(struct access_list)); |
718e3744 | 133 | } |
134 | ||
135 | /* Free allocated access_list. */ | |
d62a17ae | 136 | static void access_list_free(struct access_list *access) |
718e3744 | 137 | { |
d62a17ae | 138 | XFREE(MTYPE_ACCESS_LIST, access); |
718e3744 | 139 | } |
140 | ||
141 | /* Delete access_list from access_master and free it. */ | |
4cf24501 | 142 | void access_list_delete(struct access_list *access) |
718e3744 | 143 | { |
d62a17ae | 144 | struct filter *filter; |
145 | struct filter *next; | |
146 | struct access_list_list *list; | |
147 | struct access_master *master; | |
718e3744 | 148 | |
d62a17ae | 149 | for (filter = access->head; filter; filter = next) { |
150 | next = filter->next; | |
151 | filter_free(filter); | |
152 | } | |
718e3744 | 153 | |
d62a17ae | 154 | master = access->master; |
718e3744 | 155 | |
3eff8e2f | 156 | list = &master->str; |
718e3744 | 157 | |
d62a17ae | 158 | if (access->next) |
159 | access->next->prev = access->prev; | |
160 | else | |
161 | list->tail = access->prev; | |
718e3744 | 162 | |
d62a17ae | 163 | if (access->prev) |
164 | access->prev->next = access->next; | |
165 | else | |
166 | list->head = access->next; | |
718e3744 | 167 | |
8c4796a2 IR |
168 | route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED); |
169 | ||
170 | if (master->delete_hook) | |
171 | master->delete_hook(access); | |
172 | ||
0a22ddfb | 173 | XFREE(MTYPE_ACCESS_LIST_STR, access->name); |
718e3744 | 174 | |
0a22ddfb | 175 | XFREE(MTYPE_TMP, access->remark); |
718e3744 | 176 | |
d62a17ae | 177 | access_list_free(access); |
718e3744 | 178 | } |
179 | ||
214d8a60 | 180 | /* Insert new access list to list of access_list. Each access_list |
718e3744 | 181 | is sorted by the name. */ |
d62a17ae | 182 | static struct access_list *access_list_insert(afi_t afi, const char *name) |
183 | { | |
d62a17ae | 184 | struct access_list *access; |
185 | struct access_list *point; | |
186 | struct access_list_list *alist; | |
187 | struct access_master *master; | |
188 | ||
189 | master = access_master_get(afi); | |
190 | if (master == NULL) | |
191 | return NULL; | |
192 | ||
193 | /* Allocate new access_list and copy given name. */ | |
194 | access = access_list_new(); | |
195 | access->name = XSTRDUP(MTYPE_ACCESS_LIST_STR, name); | |
196 | access->master = master; | |
197 | ||
3eff8e2f IR |
198 | /* Set access_list to string list. */ |
199 | alist = &master->str; | |
d62a17ae | 200 | |
3eff8e2f IR |
201 | /* Set point to insertion point. */ |
202 | for (point = alist->head; point; point = point->next) | |
203 | if (strcmp(point->name, name) >= 0) | |
204 | break; | |
d62a17ae | 205 | |
206 | /* In case of this is the first element of master. */ | |
207 | if (alist->head == NULL) { | |
208 | alist->head = alist->tail = access; | |
209 | return access; | |
210 | } | |
211 | ||
212 | /* In case of insertion is made at the tail of access_list. */ | |
213 | if (point == NULL) { | |
214 | access->prev = alist->tail; | |
215 | alist->tail->next = access; | |
216 | alist->tail = access; | |
217 | return access; | |
218 | } | |
219 | ||
220 | /* In case of insertion is made at the head of access_list. */ | |
221 | if (point == alist->head) { | |
222 | access->next = alist->head; | |
223 | alist->head->prev = access; | |
224 | alist->head = access; | |
225 | return access; | |
226 | } | |
227 | ||
228 | /* Insertion is made at middle of the access_list. */ | |
229 | access->next = point; | |
230 | access->prev = point->prev; | |
231 | ||
232 | if (point->prev) | |
233 | point->prev->next = access; | |
234 | point->prev = access; | |
235 | ||
236 | return access; | |
718e3744 | 237 | } |
238 | ||
239 | /* Lookup access_list from list of access_list by name. */ | |
d62a17ae | 240 | struct access_list *access_list_lookup(afi_t afi, const char *name) |
718e3744 | 241 | { |
d62a17ae | 242 | struct access_list *access; |
243 | struct access_master *master; | |
718e3744 | 244 | |
d62a17ae | 245 | if (name == NULL) |
246 | return NULL; | |
718e3744 | 247 | |
d62a17ae | 248 | master = access_master_get(afi); |
249 | if (master == NULL) | |
250 | return NULL; | |
718e3744 | 251 | |
d62a17ae | 252 | for (access = master->str.head; access; access = access->next) |
253 | if (strcmp(access->name, name) == 0) | |
254 | return access; | |
718e3744 | 255 | |
d62a17ae | 256 | return NULL; |
718e3744 | 257 | } |
258 | ||
259 | /* Get access list from list of access_list. If there isn't matched | |
260 | access_list create new one and return it. */ | |
4cf24501 | 261 | struct access_list *access_list_get(afi_t afi, const char *name) |
718e3744 | 262 | { |
d62a17ae | 263 | struct access_list *access; |
718e3744 | 264 | |
d62a17ae | 265 | access = access_list_lookup(afi, name); |
266 | if (access == NULL) | |
267 | access = access_list_insert(afi, name); | |
268 | return access; | |
718e3744 | 269 | } |
270 | ||
271 | /* Apply access list to object (which should be struct prefix *). */ | |
123214ef MS |
272 | enum filter_type access_list_apply(struct access_list *access, |
273 | const void *object) | |
718e3744 | 274 | { |
d62a17ae | 275 | struct filter *filter; |
123214ef | 276 | const struct prefix *p = (const struct prefix *)object; |
718e3744 | 277 | |
d62a17ae | 278 | if (access == NULL) |
279 | return FILTER_DENY; | |
718e3744 | 280 | |
d62a17ae | 281 | for (filter = access->head; filter; filter = filter->next) { |
282 | if (filter->cisco) { | |
283 | if (filter_match_cisco(filter, p)) | |
284 | return filter->type; | |
285 | } else { | |
0f6476cc | 286 | if (filter_match_zebra(filter, p)) |
d62a17ae | 287 | return filter->type; |
288 | } | |
718e3744 | 289 | } |
718e3744 | 290 | |
d62a17ae | 291 | return FILTER_DENY; |
718e3744 | 292 | } |
293 | ||
294 | /* Add hook function. */ | |
d62a17ae | 295 | void access_list_add_hook(void (*func)(struct access_list *access)) |
718e3744 | 296 | { |
d62a17ae | 297 | access_master_ipv4.add_hook = func; |
298 | access_master_ipv6.add_hook = func; | |
d37ba549 | 299 | access_master_mac.add_hook = func; |
718e3744 | 300 | } |
301 | ||
302 | /* Delete hook function. */ | |
d62a17ae | 303 | void access_list_delete_hook(void (*func)(struct access_list *access)) |
718e3744 | 304 | { |
d62a17ae | 305 | access_master_ipv4.delete_hook = func; |
306 | access_master_ipv6.delete_hook = func; | |
d37ba549 | 307 | access_master_mac.delete_hook = func; |
718e3744 | 308 | } |
309 | ||
358189ad | 310 | /* Calculate new sequential number. */ |
4cf24501 | 311 | int64_t filter_new_seq_get(struct access_list *access) |
718e3744 | 312 | { |
358189ad DA |
313 | int64_t maxseq; |
314 | int64_t newseq; | |
315 | struct filter *filter; | |
718e3744 | 316 | |
5037cc3e | 317 | maxseq = 0; |
718e3744 | 318 | |
358189ad DA |
319 | for (filter = access->head; filter; filter = filter->next) { |
320 | if (maxseq < filter->seq) | |
321 | maxseq = filter->seq; | |
322 | } | |
323 | ||
324 | newseq = ((maxseq / 5) * 5) + 5; | |
325 | ||
326 | return (newseq > UINT_MAX) ? UINT_MAX : newseq; | |
327 | } | |
328 | ||
329 | /* Return access list entry which has same seq number. */ | |
330 | static struct filter *filter_seq_check(struct access_list *access, | |
331 | int64_t seq) | |
332 | { | |
333 | struct filter *filter; | |
334 | ||
335 | for (filter = access->head; filter; filter = filter->next) | |
336 | if (filter->seq == seq) | |
337 | return filter; | |
338 | return NULL; | |
718e3744 | 339 | } |
340 | ||
718e3744 | 341 | /* Delete filter from specified access_list. If there is hook |
342 | function execute it. */ | |
4cf24501 RZ |
343 | void access_list_filter_delete(struct access_list *access, |
344 | struct filter *filter) | |
718e3744 | 345 | { |
d62a17ae | 346 | struct access_master *master; |
718e3744 | 347 | |
d62a17ae | 348 | master = access->master; |
718e3744 | 349 | |
d62a17ae | 350 | if (filter->next) |
351 | filter->next->prev = filter->prev; | |
352 | else | |
353 | access->tail = filter->prev; | |
718e3744 | 354 | |
d62a17ae | 355 | if (filter->prev) |
356 | filter->prev->next = filter->next; | |
357 | else | |
358 | access->head = filter->next; | |
718e3744 | 359 | |
d62a17ae | 360 | filter_free(filter); |
718e3744 | 361 | |
d62a17ae | 362 | route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED); |
363 | /* Run hook function. */ | |
364 | if (master->delete_hook) | |
365 | (*master->delete_hook)(access); | |
718e3744 | 366 | } |
6b0655a2 | 367 | |
358189ad | 368 | /* Add new filter to the end of specified access_list. */ |
4cf24501 RZ |
369 | void access_list_filter_add(struct access_list *access, |
370 | struct filter *filter) | |
358189ad DA |
371 | { |
372 | struct filter *replace; | |
373 | struct filter *point; | |
374 | ||
214d8a60 | 375 | /* Automatic assignment of seq no. */ |
358189ad DA |
376 | if (filter->seq == -1) |
377 | filter->seq = filter_new_seq_get(access); | |
378 | ||
379 | if (access->tail && filter->seq > access->tail->seq) | |
380 | point = NULL; | |
381 | else { | |
382 | /* Is there any same seq access list filter? */ | |
383 | replace = filter_seq_check(access, filter->seq); | |
384 | if (replace) | |
385 | access_list_filter_delete(access, replace); | |
386 | ||
387 | /* Check insert point. */ | |
388 | for (point = access->head; point; point = point->next) | |
389 | if (point->seq >= filter->seq) | |
390 | break; | |
391 | } | |
392 | ||
393 | /* In case of this is the first element of the list. */ | |
394 | filter->next = point; | |
395 | ||
396 | if (point) { | |
397 | if (point->prev) | |
398 | point->prev->next = filter; | |
399 | else | |
400 | access->head = filter; | |
401 | ||
402 | filter->prev = point->prev; | |
403 | point->prev = filter; | |
404 | } else { | |
405 | if (access->tail) | |
406 | access->tail->next = filter; | |
407 | else | |
408 | access->head = filter; | |
409 | ||
410 | filter->prev = access->tail; | |
411 | access->tail = filter; | |
412 | } | |
413 | ||
414 | /* Run hook function. */ | |
415 | if (access->master->add_hook) | |
416 | (*access->master->add_hook)(access); | |
417 | route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_ADDED); | |
418 | } | |
419 | ||
718e3744 | 420 | /* |
421 | deny Specify packets to reject | |
422 | permit Specify packets to forward | |
423 | dynamic ? | |
424 | */ | |
425 | ||
426 | /* | |
427 | Hostname or A.B.C.D Address to match | |
428 | any Any source host | |
429 | host A single host address | |
430 | */ | |
431 | ||
068ab901 RW |
432 | static void config_write_access_zebra(struct vty *, struct filter *, |
433 | json_object *); | |
434 | static void config_write_access_cisco(struct vty *, struct filter *, | |
435 | json_object *); | |
436 | ||
437 | static const char *filter_type2str(struct filter *filter) | |
438 | { | |
439 | if (filter->cisco) { | |
440 | if (filter->u.cfilter.extended) | |
441 | return "Extended"; | |
442 | else | |
443 | return "Standard"; | |
444 | } else | |
445 | return "Zebra"; | |
446 | } | |
718e3744 | 447 | |
448 | /* show access-list command. */ | |
068ab901 RW |
449 | static int filter_show(struct vty *vty, const char *name, afi_t afi, |
450 | bool use_json) | |
d62a17ae | 451 | { |
452 | struct access_list *access; | |
453 | struct access_master *master; | |
454 | struct filter *mfilter; | |
455 | struct filter_cisco *filter; | |
068ab901 RW |
456 | bool first; |
457 | json_object *json = NULL; | |
458 | json_object *json_proto = NULL; | |
d62a17ae | 459 | |
460 | master = access_master_get(afi); | |
068ab901 RW |
461 | if (master == NULL) { |
462 | if (use_json) | |
463 | vty_out(vty, "{}\n"); | |
d62a17ae | 464 | return 0; |
068ab901 RW |
465 | } |
466 | ||
467 | if (use_json) | |
468 | json = json_object_new_object(); | |
d62a17ae | 469 | |
470 | /* Print the name of the protocol */ | |
068ab901 RW |
471 | if (json) { |
472 | json_proto = json_object_new_object(); | |
473 | json_object_object_add(json, frr_protoname, json_proto); | |
474 | } else | |
475 | vty_out(vty, "%s:\n", frr_protoname); | |
d62a17ae | 476 | |
d62a17ae | 477 | for (access = master->str.head; access; access = access->next) { |
068ab901 RW |
478 | json_object *json_acl = NULL; |
479 | json_object *json_rules = NULL; | |
480 | ||
d62a17ae | 481 | if (name && strcmp(access->name, name) != 0) |
482 | continue; | |
483 | ||
068ab901 | 484 | first = true; |
d62a17ae | 485 | |
486 | for (mfilter = access->head; mfilter; mfilter = mfilter->next) { | |
068ab901 RW |
487 | json_object *json_rule = NULL; |
488 | ||
d62a17ae | 489 | filter = &mfilter->u.cfilter; |
490 | ||
068ab901 RW |
491 | if (first) { |
492 | const char *type = filter_type2str(mfilter); | |
493 | ||
494 | if (json) { | |
495 | json_acl = json_object_new_object(); | |
496 | json_object_object_add(json_proto, | |
497 | access->name, | |
498 | json_acl); | |
499 | ||
500 | json_object_string_add(json_acl, "type", | |
501 | type); | |
502 | json_object_string_add(json_acl, | |
503 | "addressFamily", | |
504 | afi2str(afi)); | |
505 | json_rules = json_object_new_array(); | |
506 | json_object_object_add( | |
507 | json_acl, "rules", json_rules); | |
508 | } else { | |
509 | vty_out(vty, "%s %s access list %s\n", | |
510 | type, | |
511 | (afi == AFI_IP) | |
512 | ? ("IP") | |
513 | : ((afi == AFI_IP6) | |
514 | ? ("IPv6 ") | |
515 | : ("MAC ")), | |
516 | access->name); | |
517 | } | |
518 | ||
519 | first = false; | |
d62a17ae | 520 | } |
521 | ||
068ab901 RW |
522 | if (json) { |
523 | json_rule = json_object_new_object(); | |
524 | json_object_array_add(json_rules, json_rule); | |
525 | ||
526 | json_object_int_add(json_rule, "sequenceNumber", | |
527 | mfilter->seq); | |
528 | json_object_string_add( | |
529 | json_rule, "filterType", | |
530 | filter_type_str(mfilter)); | |
531 | } else { | |
532 | vty_out(vty, " seq %" PRId64, mfilter->seq); | |
533 | vty_out(vty, " %s%s", filter_type_str(mfilter), | |
534 | mfilter->type == FILTER_DENY ? " " | |
535 | : ""); | |
536 | } | |
d62a17ae | 537 | |
538 | if (!mfilter->cisco) | |
068ab901 RW |
539 | config_write_access_zebra(vty, mfilter, |
540 | json_rule); | |
d62a17ae | 541 | else if (filter->extended) |
068ab901 RW |
542 | config_write_access_cisco(vty, mfilter, |
543 | json_rule); | |
d62a17ae | 544 | else { |
068ab901 | 545 | if (json) { |
b5bb6c67 DL |
546 | json_object_string_addf( |
547 | json_rule, "address", "%pI4", | |
548 | &filter->addr); | |
549 | json_object_string_addf( | |
550 | json_rule, "mask", "%pI4", | |
551 | &filter->addr_mask); | |
068ab901 | 552 | } else { |
975a328e | 553 | if (filter->addr_mask.s_addr |
068ab901 RW |
554 | == 0xffffffff) |
555 | vty_out(vty, " any\n"); | |
556 | else { | |
557 | vty_out(vty, " %pI4", | |
558 | &filter->addr); | |
559 | if (filter->addr_mask.s_addr | |
560 | != INADDR_ANY) | |
561 | vty_out(vty, | |
562 | ", wildcard bits %pI4", | |
563 | &filter->addr_mask); | |
564 | vty_out(vty, "\n"); | |
565 | } | |
d62a17ae | 566 | } |
567 | } | |
718e3744 | 568 | } |
718e3744 | 569 | } |
068ab901 | 570 | |
ad9df66c | 571 | return vty_json(vty, json); |
718e3744 | 572 | } |
573 | ||
d37ba549 MK |
574 | /* show MAC access list - this only has MAC filters for now*/ |
575 | DEFUN (show_mac_access_list, | |
576 | show_mac_access_list_cmd, | |
577 | "show mac access-list", | |
578 | SHOW_STR | |
579 | "mac access lists\n" | |
580 | "List mac access lists\n") | |
581 | { | |
068ab901 | 582 | return filter_show(vty, NULL, AFI_L2VPN, false); |
d37ba549 MK |
583 | } |
584 | ||
585 | DEFUN (show_mac_access_list_name, | |
586 | show_mac_access_list_name_cmd, | |
c60dec36 | 587 | "show mac access-list ACCESSLIST_MAC_NAME", |
d37ba549 | 588 | SHOW_STR |
1667fc40 | 589 | "mac access lists\n" |
d37ba549 | 590 | "List mac access lists\n" |
1667fc40 | 591 | "mac address\n") |
d37ba549 | 592 | { |
068ab901 | 593 | return filter_show(vty, argv[3]->arg, AFI_L2VPN, false); |
d37ba549 MK |
594 | } |
595 | ||
718e3744 | 596 | DEFUN (show_ip_access_list, |
597 | show_ip_access_list_cmd, | |
068ab901 | 598 | "show ip access-list [json]", |
718e3744 | 599 | SHOW_STR |
600 | IP_STR | |
068ab901 RW |
601 | "List IP access lists\n" |
602 | JSON_STR) | |
718e3744 | 603 | { |
068ab901 RW |
604 | bool uj = use_json(argc, argv); |
605 | return filter_show(vty, NULL, AFI_IP, uj); | |
718e3744 | 606 | } |
607 | ||
608 | DEFUN (show_ip_access_list_name, | |
609 | show_ip_access_list_name_cmd, | |
c60dec36 | 610 | "show ip access-list ACCESSLIST4_NAME [json]", |
718e3744 | 611 | SHOW_STR |
612 | IP_STR | |
613 | "List IP access lists\n" | |
7e869991 | 614 | "IP access-list name\n" |
068ab901 | 615 | JSON_STR) |
718e3744 | 616 | { |
068ab901 | 617 | bool uj = use_json(argc, argv); |
d62a17ae | 618 | int idx_acl = 3; |
068ab901 | 619 | return filter_show(vty, argv[idx_acl]->arg, AFI_IP, uj); |
718e3744 | 620 | } |
621 | ||
718e3744 | 622 | DEFUN (show_ipv6_access_list, |
623 | show_ipv6_access_list_cmd, | |
068ab901 | 624 | "show ipv6 access-list [json]", |
718e3744 | 625 | SHOW_STR |
626 | IPV6_STR | |
068ab901 RW |
627 | "List IPv6 access lists\n" |
628 | JSON_STR) | |
718e3744 | 629 | { |
068ab901 RW |
630 | bool uj = use_json(argc, argv); |
631 | return filter_show(vty, NULL, AFI_IP6, uj); | |
718e3744 | 632 | } |
633 | ||
634 | DEFUN (show_ipv6_access_list_name, | |
635 | show_ipv6_access_list_name_cmd, | |
c60dec36 | 636 | "show ipv6 access-list ACCESSLIST6_NAME [json]", |
718e3744 | 637 | SHOW_STR |
638 | IPV6_STR | |
639 | "List IPv6 access lists\n" | |
7e869991 | 640 | "IPv6 access-list name\n" |
068ab901 | 641 | JSON_STR) |
718e3744 | 642 | { |
068ab901 | 643 | bool uj = use_json(argc, argv); |
d62a17ae | 644 | int idx_word = 3; |
068ab901 | 645 | return filter_show(vty, argv[idx_word]->arg, AFI_IP6, uj); |
d62a17ae | 646 | } |
647 | ||
068ab901 RW |
648 | static void config_write_access_cisco(struct vty *vty, struct filter *mfilter, |
649 | json_object *json) | |
d62a17ae | 650 | { |
651 | struct filter_cisco *filter; | |
652 | ||
653 | filter = &mfilter->u.cfilter; | |
654 | ||
068ab901 | 655 | if (json) { |
068ab901 | 656 | json_object_boolean_add(json, "extended", !!filter->extended); |
b5bb6c67 DL |
657 | json_object_string_addf(json, "sourceAddress", "%pI4", |
658 | &filter->addr); | |
659 | json_object_string_addf(json, "sourceMask", "%pI4", | |
660 | &filter->addr_mask); | |
661 | json_object_string_addf(json, "destinationAddress", "%pI4", | |
662 | &filter->mask); | |
663 | json_object_string_addf(json, "destinationMask", "%pI4", | |
664 | &filter->mask_mask); | |
068ab901 | 665 | } else { |
d62a17ae | 666 | vty_out(vty, " ip"); |
667 | if (filter->addr_mask.s_addr == 0xffffffff) | |
668 | vty_out(vty, " any"); | |
975a328e | 669 | else if (filter->addr_mask.s_addr == INADDR_ANY) |
af3b34f6 | 670 | vty_out(vty, " host %pI4", &filter->addr); |
d62a17ae | 671 | else { |
af3b34f6 DA |
672 | vty_out(vty, " %pI4", &filter->addr); |
673 | vty_out(vty, " %pI4", &filter->addr_mask); | |
d62a17ae | 674 | } |
675 | ||
676 | if (filter->mask_mask.s_addr == 0xffffffff) | |
677 | vty_out(vty, " any"); | |
975a328e | 678 | else if (filter->mask_mask.s_addr == INADDR_ANY) |
af3b34f6 | 679 | vty_out(vty, " host %pI4", &filter->mask); |
d62a17ae | 680 | else { |
af3b34f6 DA |
681 | vty_out(vty, " %pI4", &filter->mask); |
682 | vty_out(vty, " %pI4", &filter->mask_mask); | |
d62a17ae | 683 | } |
684 | vty_out(vty, "\n"); | |
718e3744 | 685 | } |
718e3744 | 686 | } |
687 | ||
068ab901 RW |
688 | static void config_write_access_zebra(struct vty *vty, struct filter *mfilter, |
689 | json_object *json) | |
718e3744 | 690 | { |
d62a17ae | 691 | struct filter_zebra *filter; |
692 | struct prefix *p; | |
693 | char buf[BUFSIZ]; | |
718e3744 | 694 | |
d62a17ae | 695 | filter = &mfilter->u.zfilter; |
696 | p = &filter->prefix; | |
718e3744 | 697 | |
068ab901 | 698 | if (json) { |
b5bb6c67 | 699 | json_object_string_addf(json, "prefix", "%pFX", p); |
068ab901 RW |
700 | json_object_boolean_add(json, "exact-match", !!filter->exact); |
701 | } else { | |
702 | if (p->prefixlen == 0 && !filter->exact) | |
69b61704 | 703 | vty_out(vty, " any"); |
068ab901 | 704 | else if (p->family == AF_INET6 || p->family == AF_INET) |
b5bb6c67 | 705 | vty_out(vty, " %pFX%s", p, |
068ab901 RW |
706 | filter->exact ? " exact-match" : ""); |
707 | else if (p->family == AF_ETHERNET) { | |
708 | if (p->prefixlen == 0) | |
709 | vty_out(vty, " any"); | |
710 | else | |
711 | vty_out(vty, " %s", | |
712 | prefix_mac2str(&(p->u.prefix_eth), buf, | |
713 | sizeof(buf))); | |
714 | } | |
718e3744 | 715 | |
068ab901 RW |
716 | vty_out(vty, "\n"); |
717 | } | |
718e3744 | 718 | } |
719 | ||
d37ba549 | 720 | static struct cmd_node access_mac_node = { |
f4b8291f | 721 | .name = "MAC access list", |
62b346ee DL |
722 | .node = ACCESS_MAC_NODE, |
723 | .prompt = "", | |
62b346ee | 724 | }; |
d37ba549 | 725 | |
d37ba549 MK |
726 | static void access_list_reset_mac(void) |
727 | { | |
728 | struct access_list *access; | |
729 | struct access_list *next; | |
730 | struct access_master *master; | |
731 | ||
732 | master = access_master_get(AFI_L2VPN); | |
733 | if (master == NULL) | |
734 | return; | |
735 | ||
d37ba549 MK |
736 | for (access = master->str.head; access; access = next) { |
737 | next = access->next; | |
738 | access_list_delete(access); | |
739 | } | |
740 | ||
d37ba549 MK |
741 | assert(master->str.head == NULL); |
742 | assert(master->str.tail == NULL); | |
743 | } | |
744 | ||
745 | /* Install vty related command. */ | |
746 | static void access_list_init_mac(void) | |
747 | { | |
612c2c15 | 748 | install_node(&access_mac_node); |
d37ba549 MK |
749 | |
750 | install_element(ENABLE_NODE, &show_mac_access_list_cmd); | |
751 | install_element(ENABLE_NODE, &show_mac_access_list_name_cmd); | |
d37ba549 MK |
752 | } |
753 | ||
718e3744 | 754 | /* Access-list node. */ |
1d3c4b66 | 755 | static int config_write_access(struct vty *vty); |
62b346ee | 756 | static struct cmd_node access_node = { |
f4b8291f | 757 | .name = "ipv4 access list", |
62b346ee DL |
758 | .node = ACCESS_NODE, |
759 | .prompt = "", | |
1d3c4b66 | 760 | .config_write = config_write_access, |
62b346ee | 761 | }; |
718e3744 | 762 | |
1d3c4b66 | 763 | static int config_write_access(struct vty *vty) |
718e3744 | 764 | { |
1d3c4b66 RZ |
765 | struct lyd_node *dnode; |
766 | int written = 0; | |
767 | ||
768 | dnode = yang_dnode_get(running_config->dnode, "/frr-filter:lib"); | |
769 | if (dnode) { | |
770 | nb_cli_show_dnode_cmds(vty, dnode, false); | |
771 | written = 1; | |
772 | } | |
773 | ||
774 | return written; | |
718e3744 | 775 | } |
776 | ||
d62a17ae | 777 | static void access_list_reset_ipv4(void) |
718e3744 | 778 | { |
d62a17ae | 779 | struct access_list *access; |
780 | struct access_list *next; | |
781 | struct access_master *master; | |
718e3744 | 782 | |
d62a17ae | 783 | master = access_master_get(AFI_IP); |
784 | if (master == NULL) | |
785 | return; | |
718e3744 | 786 | |
d62a17ae | 787 | for (access = master->str.head; access; access = next) { |
788 | next = access->next; | |
789 | access_list_delete(access); | |
790 | } | |
718e3744 | 791 | |
d62a17ae | 792 | assert(master->str.head == NULL); |
793 | assert(master->str.tail == NULL); | |
718e3744 | 794 | } |
795 | ||
796 | /* Install vty related command. */ | |
d62a17ae | 797 | static void access_list_init_ipv4(void) |
798 | { | |
612c2c15 | 799 | install_node(&access_node); |
d62a17ae | 800 | |
801 | install_element(ENABLE_NODE, &show_ip_access_list_cmd); | |
802 | install_element(ENABLE_NODE, &show_ip_access_list_name_cmd); | |
d62a17ae | 803 | } |
804 | ||
c60dec36 DA |
805 | static void access_list_autocomplete_afi(afi_t afi, vector comps, |
806 | struct cmd_token *token) | |
807 | { | |
808 | struct access_list *access; | |
809 | struct access_list *next; | |
810 | struct access_master *master; | |
811 | ||
812 | master = access_master_get(afi); | |
813 | if (master == NULL) | |
814 | return; | |
815 | ||
816 | for (access = master->str.head; access; access = next) { | |
817 | next = access->next; | |
818 | vector_set(comps, XSTRDUP(MTYPE_COMPLETION, access->name)); | |
819 | } | |
820 | } | |
821 | ||
62b346ee | 822 | static struct cmd_node access_ipv6_node = { |
f4b8291f | 823 | .name = "ipv6 access list", |
62b346ee DL |
824 | .node = ACCESS_IPV6_NODE, |
825 | .prompt = "", | |
62b346ee | 826 | }; |
d62a17ae | 827 | |
c60dec36 DA |
828 | static void access_list_autocomplete(vector comps, struct cmd_token *token) |
829 | { | |
830 | access_list_autocomplete_afi(AFI_IP, comps, token); | |
831 | access_list_autocomplete_afi(AFI_IP6, comps, token); | |
832 | access_list_autocomplete_afi(AFI_L2VPN, comps, token); | |
833 | } | |
834 | ||
835 | static void access_list4_autocomplete(vector comps, struct cmd_token *token) | |
836 | { | |
837 | access_list_autocomplete_afi(AFI_IP, comps, token); | |
838 | } | |
839 | ||
840 | static void access_list6_autocomplete(vector comps, struct cmd_token *token) | |
841 | { | |
842 | access_list_autocomplete_afi(AFI_IP6, comps, token); | |
843 | } | |
844 | ||
845 | static void access_list_mac_autocomplete(vector comps, struct cmd_token *token) | |
846 | { | |
847 | access_list_autocomplete_afi(AFI_L2VPN, comps, token); | |
848 | } | |
849 | ||
850 | static const struct cmd_variable_handler access_list_handlers[] = { | |
851 | {.tokenname = "ACCESSLIST_NAME", | |
852 | .completions = access_list_autocomplete}, | |
853 | {.tokenname = "ACCESSLIST4_NAME", | |
854 | .completions = access_list4_autocomplete}, | |
855 | {.tokenname = "ACCESSLIST6_NAME", | |
856 | .completions = access_list6_autocomplete}, | |
857 | {.tokenname = "ACCESSLIST_MAC_NAME", | |
858 | .completions = access_list_mac_autocomplete}, | |
859 | {.completions = NULL}}; | |
860 | ||
d62a17ae | 861 | static void access_list_reset_ipv6(void) |
862 | { | |
863 | struct access_list *access; | |
864 | struct access_list *next; | |
865 | struct access_master *master; | |
866 | ||
867 | master = access_master_get(AFI_IP6); | |
868 | if (master == NULL) | |
869 | return; | |
870 | ||
d62a17ae | 871 | for (access = master->str.head; access; access = next) { |
872 | next = access->next; | |
873 | access_list_delete(access); | |
874 | } | |
718e3744 | 875 | |
d62a17ae | 876 | assert(master->str.head == NULL); |
877 | assert(master->str.tail == NULL); | |
718e3744 | 878 | } |
879 | ||
d62a17ae | 880 | static void access_list_init_ipv6(void) |
718e3744 | 881 | { |
612c2c15 | 882 | install_node(&access_ipv6_node); |
718e3744 | 883 | |
d62a17ae | 884 | install_element(ENABLE_NODE, &show_ipv6_access_list_cmd); |
885 | install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd); | |
718e3744 | 886 | } |
718e3744 | 887 | |
4d762f26 | 888 | void access_list_init(void) |
718e3744 | 889 | { |
c60dec36 DA |
890 | cmd_variable_handler_register(access_list_handlers); |
891 | ||
d62a17ae | 892 | access_list_init_ipv4(); |
893 | access_list_init_ipv6(); | |
d37ba549 | 894 | access_list_init_mac(); |
b62578bd RZ |
895 | |
896 | filter_cli_init(); | |
718e3744 | 897 | } |
898 | ||
4d762f26 | 899 | void access_list_reset(void) |
718e3744 | 900 | { |
d62a17ae | 901 | access_list_reset_ipv4(); |
902 | access_list_reset_ipv6(); | |
d37ba549 | 903 | access_list_reset_mac(); |
718e3744 | 904 | } |