]> git.proxmox.com Git - mirror_frr.git/blob - lib/filter_cli.c
Merge pull request #5895 from patrasar/2404618
[mirror_frr.git] / lib / filter_cli.c
1 /*
2 * FRR filter CLI implementation.
3 *
4 * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
5 * Rafael Zalamena
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA.
21 */
22
23 #include "zebra.h"
24 #include "northbound.h"
25 #include "prefix.h"
26
27 #include "lib/command.h"
28 #include "lib/filter.h"
29 #include "lib/northbound_cli.h"
30 #include "lib/plist.h"
31 #include "lib/plist_int.h"
32 #include "lib/printfrr.h"
33
34 #ifndef VTYSH_EXTRACT_PL
35 #include "lib/filter_cli_clippy.c"
36 #endif /* VTYSH_EXTRACT_PL */
37
38 #define ACCESS_LIST_STR "Access list entry\n"
39 #define ACCESS_LIST_LEG_STR "IP standard access list\n"
40 #define ACCESS_LIST_ELEG_STR "IP extended access list\n"
41 #define ACCESS_LIST_ELEG_EXT_STR "IP extended access list (expanded range)\n"
42 #define ACCESS_LIST_ZEBRA_STR "Access list entry\n"
43 #define ACCESS_LIST_SEQ_STR \
44 "Sequence number of an entry\n" \
45 "Sequence number\n"
46 #define ACCESS_LIST_ACTION_STR \
47 "Specify packets to reject\n" \
48 "Specify packets to forward\n"
49 #define ACCESS_LIST_REMARK_STR "Access list entry comment\n"
50 #define ACCESS_LIST_REMARK_LINE_STR "Comment up to 100 characters\n"
51
52 #define PREFIX_LIST_NAME_STR "Prefix list entry name\n"
53
54 /*
55 * Helper function to locate filter data structures for Cisco-style ACLs.
56 */
57 static int64_t acl_cisco_get_seq(struct access_list *acl, const char *action,
58 const char *src, const char *src_mask,
59 const char *dst, const char *dst_mask)
60 {
61 struct filter_cisco *fc;
62 struct filter f, *fn;
63
64 memset(&f, 0, sizeof(f));
65 f.cisco = 1;
66 if (strcmp(action, "permit") == 0)
67 f.type = FILTER_PERMIT;
68 else
69 f.type = FILTER_DENY;
70
71 fc = &f.u.cfilter;
72 inet_pton(AF_INET, src, &fc->addr);
73 inet_pton(AF_INET, src_mask, &fc->addr_mask);
74 fc->addr.s_addr &= ~fc->addr_mask.s_addr;
75 if (dst != NULL) {
76 fc->extended = 1;
77 inet_pton(AF_INET, dst, &fc->mask);
78 inet_pton(AF_INET, dst_mask, &fc->mask_mask);
79 fc->mask.s_addr &= ~fc->mask_mask.s_addr;
80 }
81
82 fn = filter_lookup_cisco(acl, &f);
83 if (fn == NULL)
84 return -1;
85
86 return fn->seq;
87 }
88
89 /*
90 * Helper function to locate filter data structures for zebra-style ACLs.
91 */
92 static int64_t acl_zebra_get_seq(struct access_list *acl, const char *action,
93 const struct prefix *p, bool exact)
94 {
95 struct filter_zebra *fz;
96 struct filter f, *fn;
97
98 memset(&f, 0, sizeof(f));
99 memset(&fz, 0, sizeof(fz));
100 if (strcmp(action, "permit") == 0)
101 f.type = FILTER_PERMIT;
102 else
103 f.type = FILTER_DENY;
104
105 fz = &f.u.zfilter;
106 if (p->family)
107 prefix_copy(&fz->prefix, p);
108 fz->exact = exact;
109
110 fn = filter_lookup_zebra(acl, &f);
111 if (fn == NULL)
112 return -1;
113
114 return fn->seq;
115 }
116
117 /*
118 * Helper function to concatenate address with mask in Cisco style.
119 */
120 static void concat_addr_mask_v4(const char *addr, const char *mask, char *dst,
121 size_t dstlen)
122 {
123 struct in_addr ia;
124 int plen;
125
126 assert(inet_pton(AF_INET, mask, &ia) == 1);
127 ia.s_addr = ~ia.s_addr;
128 plen = ip_masklen(ia);
129 snprintf(dst, dstlen, "%s/%d", addr, plen);
130 }
131
132 /*
133 * Helper function to generate a sequence number for legacy commands.
134 */
135 static int acl_get_seq_cb(const struct lyd_node *dnode, void *arg)
136 {
137 int64_t *seq = arg;
138 int64_t cur_seq = yang_dnode_get_uint32(dnode, "sequence");
139
140 if (cur_seq > *seq)
141 *seq = cur_seq;
142
143 return YANG_ITER_CONTINUE;
144 }
145
146 /**
147 * Helper function that iterates over the XPath `xpath` on the candidate
148 * configuration in `vty->candidate_config`.
149 *
150 * \param[in] vty shell context with the candidate configuration.
151 * \param[in] xpath the XPath to look for the sequence leaf.
152 * \returns next unused sequence number.
153 */
154 static long acl_get_seq(struct vty *vty, const char *xpath)
155 {
156 int64_t seq = 0;
157
158 yang_dnode_iterate(acl_get_seq_cb, &seq, vty->candidate_config->dnode,
159 "%s/entry", xpath);
160
161 return seq + 5;
162 }
163
164 /*
165 * Cisco (legacy) access lists.
166 */
167 DEFPY_YANG(
168 access_list_std, access_list_std_cmd,
169 "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>",
170 ACCESS_LIST_STR
171 ACCESS_LIST_LEG_STR
172 ACCESS_LIST_SEQ_STR
173 ACCESS_LIST_ACTION_STR
174 "A single host address\n"
175 "Address to match\n"
176 "Address to match\n"
177 "Wildcard bits\n")
178 {
179 int64_t sseq;
180 char ipmask[64];
181 char xpath[XPATH_MAXLEN];
182 char xpath_entry[XPATH_MAXLEN + 128];
183
184 /*
185 * Create the access-list first, so we can generate sequence if
186 * none given (backward compatibility).
187 */
188 snprintf(xpath, sizeof(xpath),
189 "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
190 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
191 if (seq_str == NULL) {
192 /* Use XPath to find the next sequence number. */
193 sseq = acl_get_seq(vty, xpath);
194 snprintfrr(xpath_entry, sizeof(xpath_entry),
195 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
196 } else
197 snprintfrr(xpath_entry, sizeof(xpath_entry),
198 "%s/entry[sequence='%s']", xpath, seq_str);
199
200 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
201
202 nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
203 if (host_str != NULL && mask_str == NULL) {
204 nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, host_str);
205 } else if (host_str != NULL && mask_str != NULL) {
206 concat_addr_mask_v4(host_str, mask_str, ipmask, sizeof(ipmask));
207 nb_cli_enqueue_change(vty, "./network", NB_OP_MODIFY, ipmask);
208 } else {
209 nb_cli_enqueue_change(vty, "./source-any", NB_OP_CREATE, NULL);
210 }
211
212 return nb_cli_apply_changes(vty, xpath_entry);
213 }
214
215 DEFPY_YANG(
216 no_access_list_std, no_access_list_std_cmd,
217 "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>",
218 NO_STR
219 ACCESS_LIST_STR
220 ACCESS_LIST_LEG_STR
221 ACCESS_LIST_SEQ_STR
222 ACCESS_LIST_ACTION_STR
223 "A single host address\n"
224 "Address to match\n"
225 "Address to match\n"
226 "Wildcard bits\n")
227 {
228 struct access_list *acl;
229 struct lyd_node *dnode;
230 int64_t sseq;
231 char xpath[XPATH_MAXLEN];
232 char xpath_entry[XPATH_MAXLEN + 32];
233
234 /* If the user provided sequence number, then just go for it. */
235 if (seq_str != NULL) {
236 snprintf(
237 xpath, sizeof(xpath),
238 "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']",
239 name, seq_str);
240 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
241 return nb_cli_apply_changes(vty, NULL);
242 }
243
244 /* Otherwise, to keep compatibility, we need to figure it out. */
245 snprintf(xpath, sizeof(xpath),
246 "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
247
248 /* Access-list must exist before entries. */
249 if (yang_dnode_exists(running_config->dnode, xpath) == false)
250 return CMD_WARNING;
251
252 /* Use access-list data structure to fetch sequence. */
253 dnode = yang_dnode_get(running_config->dnode, xpath);
254 acl = nb_running_get_entry(dnode, NULL, true);
255 sseq = acl_cisco_get_seq(acl, action, host_str,
256 mask_str ? mask_str : CISCO_HOST_WILDCARD_MASK,
257 NULL, NULL);
258 if (sseq == -1)
259 return CMD_WARNING;
260
261 snprintfrr(xpath_entry, sizeof(xpath_entry),
262 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
263 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
264
265 return nb_cli_apply_changes(vty, NULL);
266 }
267
268 DEFPY_YANG(
269 access_list_ext, access_list_ext_cmd,
270 "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
271 ACCESS_LIST_STR
272 ACCESS_LIST_ELEG_STR
273 ACCESS_LIST_SEQ_STR
274 ACCESS_LIST_ACTION_STR
275 "IPv4 address\n"
276 "Source address to match\n"
277 "Source address mask to apply\n"
278 "Single source host\n"
279 "Source address to match\n"
280 "Any source host\n"
281 "Destination address to match\n"
282 "Destination address mask to apply\n"
283 "Single destination host\n"
284 "Destination address to match\n"
285 "Any destination host\n")
286 {
287 int64_t sseq;
288 char ipmask[64], ipmask_dst[64];
289 char xpath[XPATH_MAXLEN];
290 char xpath_entry[XPATH_MAXLEN + 128];
291
292 /*
293 * Create the access-list first, so we can generate sequence if
294 * none given (backward compatibility).
295 */
296 snprintf(xpath, sizeof(xpath),
297 "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
298 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
299 if (seq_str == NULL) {
300 /* Use XPath to find the next sequence number. */
301 sseq = acl_get_seq(vty, xpath);
302 snprintfrr(xpath_entry, sizeof(xpath_entry),
303 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
304 } else
305 snprintfrr(xpath_entry, sizeof(xpath_entry),
306 "%s/entry[sequence='%s']", xpath, seq_str);
307
308 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
309
310 nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
311 if (src_str != NULL && src_mask_str == NULL) {
312 nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, src_str);
313 } else if (src_str != NULL && src_mask_str != NULL) {
314 concat_addr_mask_v4(src_str, src_mask_str, ipmask,
315 sizeof(ipmask));
316 nb_cli_enqueue_change(vty, "./network", NB_OP_MODIFY, ipmask);
317 } else {
318 nb_cli_enqueue_change(vty, "./source-any", NB_OP_CREATE, NULL);
319 }
320
321 if (dst_str != NULL && dst_mask_str == NULL) {
322 nb_cli_enqueue_change(vty, "./destination-host", NB_OP_MODIFY,
323 dst_str);
324 } else if (dst_str != NULL && dst_mask_str != NULL) {
325 concat_addr_mask_v4(dst_str, dst_mask_str, ipmask_dst,
326 sizeof(ipmask_dst));
327 nb_cli_enqueue_change(vty, "./destination-network",
328 NB_OP_MODIFY, ipmask_dst);
329 } else {
330 nb_cli_enqueue_change(vty, "./destination-any", NB_OP_CREATE,
331 NULL);
332 }
333
334 return nb_cli_apply_changes(vty, xpath_entry);
335 }
336
337 DEFPY_YANG(
338 no_access_list_ext, no_access_list_ext_cmd,
339 "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
340 NO_STR
341 ACCESS_LIST_STR
342 ACCESS_LIST_ELEG_STR
343 ACCESS_LIST_SEQ_STR
344 ACCESS_LIST_ACTION_STR
345 "Any Internet Protocol\n"
346 "Source address to match\n"
347 "Source address mask to apply\n"
348 "Single source host\n"
349 "Source address to match\n"
350 "Any source host\n"
351 "Destination address to match\n"
352 "Destination address mask to apply\n"
353 "Single destination host\n"
354 "Destination address to match\n"
355 "Any destination host\n")
356 {
357 struct access_list *acl;
358 struct lyd_node *dnode;
359 int64_t sseq;
360 char xpath[XPATH_MAXLEN];
361 char xpath_entry[XPATH_MAXLEN + 32];
362
363 /* If the user provided sequence number, then just go for it. */
364 if (seq_str != NULL) {
365 snprintfrr(
366 xpath, sizeof(xpath),
367 "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']",
368 name, seq_str);
369 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
370 return nb_cli_apply_changes(vty, NULL);
371 }
372
373 /* Otherwise, to keep compatibility, we need to figure it out. */
374 snprintf(xpath, sizeof(xpath),
375 "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
376
377 /* Access-list must exist before entries. */
378 if (yang_dnode_exists(running_config->dnode, xpath) == false)
379 return CMD_WARNING;
380
381 /* Use access-list data structure to fetch sequence. */
382 dnode = yang_dnode_get(running_config->dnode, xpath);
383 acl = nb_running_get_entry(dnode, NULL, true);
384 if (src_str != NULL) {
385 if (dst_str != NULL)
386 sseq = acl_cisco_get_seq(
387 acl, action, src_str,
388 src_mask_str ? src_mask_str
389 : CISCO_HOST_WILDCARD_MASK,
390 dst_str,
391 dst_mask_str ? dst_mask_str
392 : CISCO_HOST_WILDCARD_MASK);
393 else
394 sseq = acl_cisco_get_seq(
395 acl, action, src_str,
396 src_mask_str ? src_mask_str
397 : CISCO_HOST_WILDCARD_MASK,
398 "0.0.0.0", CISCO_ANY_WILDCARD_MASK);
399 } else {
400 if (dst_str != NULL)
401 sseq = acl_cisco_get_seq(
402 acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK,
403 dst_str,
404 dst_mask_str ? dst_mask_str
405 : CISCO_HOST_WILDCARD_MASK);
406 else
407 sseq = acl_cisco_get_seq(
408 acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK,
409 "0.0.0.0", CISCO_ANY_WILDCARD_MASK);
410 }
411 if (sseq == -1)
412 return CMD_WARNING;
413
414 snprintfrr(xpath_entry, sizeof(xpath_entry),
415 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
416 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
417
418 return nb_cli_apply_changes(vty, NULL);
419 }
420
421 /*
422 * Zebra access lists.
423 */
424 DEFPY_YANG(
425 access_list, access_list_cmd,
426 "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
427 ACCESS_LIST_STR
428 ACCESS_LIST_ZEBRA_STR
429 ACCESS_LIST_SEQ_STR
430 ACCESS_LIST_ACTION_STR
431 "Prefix to match. e.g. 10.0.0.0/8\n"
432 "Exact match of the prefixes\n"
433 "Match any IPv4\n")
434 {
435 int64_t sseq;
436 char xpath[XPATH_MAXLEN];
437 char xpath_entry[XPATH_MAXLEN + 128];
438
439 /*
440 * Create the access-list first, so we can generate sequence if
441 * none given (backward compatibility).
442 */
443 snprintf(xpath, sizeof(xpath),
444 "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
445 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
446 if (seq_str == NULL) {
447 /* Use XPath to find the next sequence number. */
448 sseq = acl_get_seq(vty, xpath);
449 snprintfrr(xpath_entry, sizeof(xpath_entry),
450 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
451 } else
452 snprintfrr(xpath_entry, sizeof(xpath_entry),
453 "%s/entry[sequence='%s']", xpath, seq_str);
454
455 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
456
457 nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
458 if (prefix_str != NULL) {
459 nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY,
460 prefix_str);
461 nb_cli_enqueue_change(vty, "./ipv4-exact-match", NB_OP_MODIFY,
462 exact ? "true" : "false");
463 } else {
464 nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
465 }
466
467 return nb_cli_apply_changes(vty, xpath_entry);
468 }
469
470 DEFPY_YANG(
471 no_access_list, no_access_list_cmd,
472 "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
473 NO_STR
474 ACCESS_LIST_STR
475 ACCESS_LIST_ZEBRA_STR
476 ACCESS_LIST_SEQ_STR
477 ACCESS_LIST_ACTION_STR
478 "Prefix to match. e.g. 10.0.0.0/8\n"
479 "Exact match of the prefixes\n"
480 "Match any IPv4\n")
481 {
482 struct access_list *acl;
483 struct lyd_node *dnode;
484 int64_t sseq;
485 struct prefix pany;
486 char xpath[XPATH_MAXLEN];
487 char xpath_entry[XPATH_MAXLEN + 32];
488
489 /* If the user provided sequence number, then just go for it. */
490 if (seq_str != NULL) {
491 snprintf(
492 xpath, sizeof(xpath),
493 "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']",
494 name, seq_str);
495 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
496 return nb_cli_apply_changes(vty, NULL);
497 }
498
499 /* Otherwise, to keep compatibility, we need to figure it out. */
500 snprintf(xpath, sizeof(xpath),
501 "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
502
503 /* Access-list must exist before entries. */
504 if (yang_dnode_exists(running_config->dnode, xpath) == false)
505 return CMD_WARNING;
506
507 /* Use access-list data structure to fetch sequence. */
508 dnode = yang_dnode_get(running_config->dnode, xpath);
509 acl = nb_running_get_entry(dnode, NULL, true);
510 if (prefix_str == NULL) {
511 memset(&pany, 0, sizeof(pany));
512 pany.family = AF_INET;
513 sseq = acl_zebra_get_seq(acl, action, &pany, exact);
514 } else
515 sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
516 exact);
517 if (sseq == -1)
518 return CMD_WARNING;
519
520 snprintfrr(xpath_entry, sizeof(xpath_entry),
521 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
522 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
523
524 return nb_cli_apply_changes(vty, NULL);
525 }
526
527 DEFPY_YANG(
528 no_access_list_all, no_access_list_all_cmd,
529 "no access-list WORD$name",
530 NO_STR
531 ACCESS_LIST_STR
532 ACCESS_LIST_ZEBRA_STR)
533 {
534 char xpath[XPATH_MAXLEN];
535
536 snprintf(xpath, sizeof(xpath),
537 "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
538 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
539
540 return nb_cli_apply_changes(vty, NULL);
541 }
542
543 DEFPY_YANG(
544 access_list_remark, access_list_remark_cmd,
545 "access-list WORD$name remark LINE...",
546 ACCESS_LIST_STR
547 ACCESS_LIST_ZEBRA_STR
548 ACCESS_LIST_REMARK_STR
549 ACCESS_LIST_REMARK_LINE_STR)
550 {
551 int rv;
552 char *remark;
553 char xpath[XPATH_MAXLEN];
554
555 snprintf(xpath, sizeof(xpath),
556 "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
557 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
558
559 remark = argv_concat(argv, argc, 3);
560 nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
561 rv = nb_cli_apply_changes(vty, xpath);
562 XFREE(MTYPE_TMP, remark);
563
564 return rv;
565 }
566
567 DEFPY_YANG(
568 no_access_list_remark, no_access_list_remark_cmd,
569 "no access-list WORD$name remark",
570 NO_STR
571 ACCESS_LIST_STR
572 ACCESS_LIST_ZEBRA_STR
573 ACCESS_LIST_REMARK_STR)
574 {
575 char xpath[XPATH_MAXLEN];
576
577 snprintf(xpath, sizeof(xpath),
578 "/frr-filter:lib/access-list[type='ipv4'][name='%s']/remark",
579 name);
580 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
581
582 return nb_cli_apply_changes(vty, NULL);
583 }
584
585 ALIAS(
586 no_access_list_remark, no_access_list_remark_line_cmd,
587 "no access-list WORD$name remark LINE...",
588 NO_STR
589 ACCESS_LIST_STR
590 ACCESS_LIST_ZEBRA_STR
591 ACCESS_LIST_REMARK_STR
592 ACCESS_LIST_REMARK_LINE_STR)
593
594 DEFPY_YANG(
595 ipv6_access_list, ipv6_access_list_cmd,
596 "ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
597 IPV6_STR
598 ACCESS_LIST_STR
599 ACCESS_LIST_ZEBRA_STR
600 ACCESS_LIST_SEQ_STR
601 ACCESS_LIST_ACTION_STR
602 "IPv6 prefix\n"
603 "Exact match of the prefixes\n"
604 "Match any IPv6\n")
605 {
606 int64_t sseq;
607 char xpath[XPATH_MAXLEN];
608 char xpath_entry[XPATH_MAXLEN + 128];
609
610 /*
611 * Create the access-list first, so we can generate sequence if
612 * none given (backward compatibility).
613 */
614 snprintf(xpath, sizeof(xpath),
615 "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name);
616 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
617 if (seq_str == NULL) {
618 /* Use XPath to find the next sequence number. */
619 sseq = acl_get_seq(vty, xpath);
620 snprintfrr(xpath_entry, sizeof(xpath_entry),
621 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
622 } else
623 snprintfrr(xpath_entry, sizeof(xpath_entry),
624 "%s/entry[sequence='%s']", xpath, seq_str);
625
626 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
627
628 nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
629 if (prefix_str != NULL) {
630 nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY,
631 prefix_str);
632 nb_cli_enqueue_change(vty, "./ipv6-exact-match", NB_OP_MODIFY,
633 exact ? "true" : "false");
634 } else {
635 nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
636 }
637
638 return nb_cli_apply_changes(vty, xpath_entry);
639 }
640
641 DEFPY_YANG(
642 no_ipv6_access_list, no_ipv6_access_list_cmd,
643 "no ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
644 NO_STR
645 IPV6_STR
646 ACCESS_LIST_STR
647 ACCESS_LIST_ZEBRA_STR
648 ACCESS_LIST_SEQ_STR
649 ACCESS_LIST_ACTION_STR
650 "IPv6 prefix\n"
651 "Exact match of the prefixes\n"
652 "Match any IPv6\n")
653 {
654 struct access_list *acl;
655 struct lyd_node *dnode;
656 int64_t sseq;
657 struct prefix pany;
658 char xpath[XPATH_MAXLEN];
659 char xpath_entry[XPATH_MAXLEN + 32];
660
661 /* If the user provided sequence number, then just go for it. */
662 if (seq_str != NULL) {
663 snprintf(
664 xpath, sizeof(xpath),
665 "/frr-filter:lib/access-list[type='ipv6'][name='%s']/entry[sequence='%s']",
666 name, seq_str);
667 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
668 return nb_cli_apply_changes(vty, NULL);
669 }
670
671 /* Otherwise, to keep compatibility, we need to figure it out. */
672 snprintf(xpath, sizeof(xpath),
673 "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name);
674
675 /* Access-list must exist before entries. */
676 if (yang_dnode_exists(running_config->dnode, xpath) == false)
677 return CMD_WARNING;
678
679 /* Use access-list data structure to fetch sequence. */
680 dnode = yang_dnode_get(running_config->dnode, xpath);
681 acl = nb_running_get_entry(dnode, NULL, true);
682 if (prefix == NULL) {
683 memset(&pany, 0, sizeof(pany));
684 pany.family = AF_INET6;
685 sseq = acl_zebra_get_seq(acl, action, &pany, exact);
686 } else
687 sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
688 exact);
689 if (sseq == -1)
690 return CMD_WARNING;
691
692 snprintfrr(xpath_entry, sizeof(xpath_entry),
693 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
694 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
695
696 return nb_cli_apply_changes(vty, NULL);
697 }
698
699 DEFPY_YANG(
700 no_ipv6_access_list_all, no_ipv6_access_list_all_cmd,
701 "no ipv6 access-list WORD$name",
702 NO_STR
703 IPV6_STR
704 ACCESS_LIST_STR
705 ACCESS_LIST_ZEBRA_STR)
706 {
707 char xpath[XPATH_MAXLEN];
708
709 snprintf(xpath, sizeof(xpath),
710 "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name);
711 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
712
713 return nb_cli_apply_changes(vty, NULL);
714 }
715
716 DEFPY_YANG(
717 ipv6_access_list_remark, ipv6_access_list_remark_cmd,
718 "ipv6 access-list WORD$name remark LINE...",
719 IPV6_STR
720 ACCESS_LIST_STR
721 ACCESS_LIST_ZEBRA_STR
722 ACCESS_LIST_REMARK_STR
723 ACCESS_LIST_REMARK_LINE_STR)
724 {
725 int rv;
726 char *remark;
727 char xpath[XPATH_MAXLEN];
728
729 snprintf(xpath, sizeof(xpath),
730 "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name);
731 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
732
733 remark = argv_concat(argv, argc, 4);
734 nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
735 rv = nb_cli_apply_changes(vty, xpath);
736 XFREE(MTYPE_TMP, remark);
737
738 return rv;
739 }
740
741 DEFPY_YANG(
742 no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd,
743 "no ipv6 access-list WORD$name remark",
744 NO_STR
745 IPV6_STR
746 ACCESS_LIST_STR
747 ACCESS_LIST_ZEBRA_STR
748 ACCESS_LIST_REMARK_STR)
749 {
750 char xpath[XPATH_MAXLEN];
751
752 snprintf(xpath, sizeof(xpath),
753 "/frr-filter:lib/access-list[type='ipv6'][name='%s']/remark",
754 name);
755 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
756
757 return nb_cli_apply_changes(vty, NULL);
758 }
759
760 ALIAS(
761 no_ipv6_access_list_remark, no_ipv6_access_list_remark_line_cmd,
762 "no ipv6 access-list WORD$name remark LINE...",
763 NO_STR
764 IPV6_STR
765 ACCESS_LIST_STR
766 ACCESS_LIST_ZEBRA_STR
767 ACCESS_LIST_REMARK_STR
768 ACCESS_LIST_REMARK_LINE_STR)
769
770 DEFPY_YANG(
771 mac_access_list, mac_access_list_cmd,
772 "mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>",
773 MAC_STR
774 ACCESS_LIST_STR
775 ACCESS_LIST_ZEBRA_STR
776 ACCESS_LIST_SEQ_STR
777 ACCESS_LIST_ACTION_STR
778 "MAC address\n"
779 "Match any MAC address\n")
780 {
781 int64_t sseq;
782 char xpath[XPATH_MAXLEN];
783 char xpath_entry[XPATH_MAXLEN + 128];
784
785 /*
786 * Create the access-list first, so we can generate sequence if
787 * none given (backward compatibility).
788 */
789 snprintf(xpath, sizeof(xpath),
790 "/frr-filter:lib/access-list[type='mac'][name='%s']", name);
791 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
792 if (seq_str == NULL) {
793 /* Use XPath to find the next sequence number. */
794 sseq = acl_get_seq(vty, xpath);
795 snprintfrr(xpath_entry, sizeof(xpath_entry),
796 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
797 } else
798 snprintfrr(xpath_entry, sizeof(xpath_entry),
799 "%s/entry[sequence='%s']", xpath, seq_str);
800
801 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
802
803 nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
804 if (mac_str != NULL) {
805 nb_cli_enqueue_change(vty, "./mac", NB_OP_MODIFY, mac_str);
806 } else {
807 nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
808 }
809
810 return nb_cli_apply_changes(vty, xpath_entry);
811 }
812
813 DEFPY_YANG(
814 no_mac_access_list, no_mac_access_list_cmd,
815 "no mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$prefix|any>",
816 NO_STR
817 MAC_STR
818 ACCESS_LIST_STR
819 ACCESS_LIST_ZEBRA_STR
820 ACCESS_LIST_SEQ_STR
821 ACCESS_LIST_ACTION_STR
822 "MAC address\n"
823 "Match any MAC address\n")
824 {
825 struct access_list *acl;
826 struct lyd_node *dnode;
827 int64_t sseq;
828 struct prefix pany;
829 char xpath[XPATH_MAXLEN];
830 char xpath_entry[XPATH_MAXLEN + 32];
831
832 /* If the user provided sequence number, then just go for it. */
833 if (seq_str != NULL) {
834 snprintf(
835 xpath, sizeof(xpath),
836 "/frr-filter:lib/access-list[type='mac'][name='%s']/entry[sequence='%s']",
837 name, seq_str);
838 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
839 return nb_cli_apply_changes(vty, NULL);
840 }
841
842 /* Otherwise, to keep compatibility, we need to figure it out. */
843 snprintf(xpath, sizeof(xpath),
844 "/frr-filter:lib/access-list[type='mac'][name='%s']", name);
845
846 /* Access-list must exist before entries. */
847 if (yang_dnode_exists(running_config->dnode, xpath) == false)
848 return CMD_WARNING;
849
850 /* Use access-list data structure to fetch sequence. */
851 dnode = yang_dnode_get(running_config->dnode, xpath);
852 acl = nb_running_get_entry(dnode, NULL, true);
853 if (prefix == NULL) {
854 memset(&pany, 0, sizeof(pany));
855 pany.family = AF_ETHERNET;
856 sseq = acl_zebra_get_seq(acl, action, &pany, false);
857 } else
858 sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
859 false);
860 if (sseq == -1)
861 return CMD_WARNING;
862
863 snprintfrr(xpath_entry, sizeof(xpath_entry),
864 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
865 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
866
867 return nb_cli_apply_changes(vty, NULL);
868 }
869
870 DEFPY_YANG(
871 no_mac_access_list_all, no_mac_access_list_all_cmd,
872 "no mac access-list WORD$name",
873 NO_STR
874 MAC_STR
875 ACCESS_LIST_STR
876 ACCESS_LIST_ZEBRA_STR)
877 {
878 char xpath[XPATH_MAXLEN];
879
880 snprintf(xpath, sizeof(xpath),
881 "/frr-filter:lib/access-list[type='mac'][name='%s']", name);
882 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
883
884 return nb_cli_apply_changes(vty, NULL);
885 }
886
887 DEFPY_YANG(
888 mac_access_list_remark, mac_access_list_remark_cmd,
889 "mac access-list WORD$name remark LINE...",
890 MAC_STR
891 ACCESS_LIST_STR
892 ACCESS_LIST_ZEBRA_STR
893 ACCESS_LIST_REMARK_STR
894 ACCESS_LIST_REMARK_LINE_STR)
895 {
896 int rv;
897 char *remark;
898 char xpath[XPATH_MAXLEN];
899
900 snprintf(xpath, sizeof(xpath),
901 "/frr-filter:lib/access-list[type='mac'][name='%s']", name);
902 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
903
904 remark = argv_concat(argv, argc, 4);
905 nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
906 rv = nb_cli_apply_changes(vty, xpath);
907 XFREE(MTYPE_TMP, remark);
908
909 return rv;
910 }
911
912 DEFPY_YANG(
913 no_mac_access_list_remark, no_mac_access_list_remark_cmd,
914 "no mac access-list WORD$name remark",
915 NO_STR
916 MAC_STR
917 ACCESS_LIST_STR
918 ACCESS_LIST_ZEBRA_STR
919 ACCESS_LIST_REMARK_STR)
920 {
921 char xpath[XPATH_MAXLEN];
922
923 snprintf(xpath, sizeof(xpath),
924 "/frr-filter:lib/access-list[type='mac'][name='%s']/remark",
925 name);
926 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
927
928 return nb_cli_apply_changes(vty, NULL);
929 }
930
931 ALIAS(
932 no_mac_access_list_remark, no_mac_access_list_remark_line_cmd,
933 "no mac access-list WORD$name remark LINE...",
934 NO_STR
935 MAC_STR
936 ACCESS_LIST_STR
937 ACCESS_LIST_ZEBRA_STR
938 ACCESS_LIST_REMARK_STR
939 ACCESS_LIST_REMARK_LINE_STR)
940
941 void access_list_show(struct vty *vty, struct lyd_node *dnode,
942 bool show_defaults)
943 {
944 int type = yang_dnode_get_enum(dnode, "../type");
945 struct prefix p;
946 bool is_any;
947 bool is_exact = false;
948 bool cisco_style = false;
949 bool cisco_extended = false;
950 struct in_addr mask;
951 char macstr[PREFIX2STR_BUFFER];
952
953 is_any = yang_dnode_exists(dnode, "./any");
954 switch (type) {
955 case YALT_IPV4:
956 if (is_any)
957 break;
958
959 if (yang_dnode_exists(dnode, "./host")
960 || yang_dnode_exists(dnode, "./network")
961 || yang_dnode_exists(dnode, "./source-any")) {
962 cisco_style = true;
963 if (yang_dnode_exists(dnode, "./destination-host")
964 || yang_dnode_exists(dnode, "./destination-network")
965 || yang_dnode_exists(dnode, "./destination-any"))
966 cisco_extended = true;
967 } else {
968 yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix");
969 is_exact = yang_dnode_get_bool(dnode,
970 "./ipv4-exact-match");
971 }
972 break;
973 case YALT_IPV6: /* ipv6 */
974 vty_out(vty, "ipv6 ");
975 if (is_any)
976 break;
977
978 yang_dnode_get_prefix(&p, dnode, "./ipv6-prefix");
979 is_exact = yang_dnode_get_bool(dnode, "./ipv6-exact-match");
980 break;
981 case YALT_MAC: /* mac */
982 vty_out(vty, "mac ");
983 if (is_any)
984 break;
985
986 yang_dnode_get_prefix(&p, dnode, "./mac");
987 break;
988 }
989
990 vty_out(vty, "access-list %s seq %s %s",
991 yang_dnode_get_string(dnode, "../name"),
992 yang_dnode_get_string(dnode, "./sequence"),
993 yang_dnode_get_string(dnode, "./action"));
994
995 /* Handle Cisco style access lists. */
996 if (cisco_style) {
997 if (cisco_extended)
998 vty_out(vty, " ip");
999
1000 if (yang_dnode_exists(dnode, "./network")) {
1001 yang_dnode_get_prefix(&p, dnode, "./network");
1002 masklen2ip(p.prefixlen, &mask);
1003 vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask);
1004 } else if (yang_dnode_exists(dnode, "./host")) {
1005 if (cisco_extended)
1006 vty_out(vty, " host");
1007
1008 vty_out(vty, " %s",
1009 yang_dnode_get_string(dnode, "./host"));
1010 } else if (yang_dnode_exists(dnode, "./source-any"))
1011 vty_out(vty, " any");
1012
1013 /* Not extended, exit earlier. */
1014 if (!cisco_extended) {
1015 vty_out(vty, "\n");
1016 return;
1017 }
1018
1019 /* Handle destination address. */
1020 if (yang_dnode_exists(dnode, "./destination-network")) {
1021 yang_dnode_get_prefix(&p, dnode,
1022 "./destination-network");
1023 masklen2ip(p.prefixlen, &mask);
1024 vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask);
1025 } else if (yang_dnode_exists(dnode, "./destination-host"))
1026 vty_out(vty, " host %s",
1027 yang_dnode_get_string(dnode,
1028 "./destination-host"));
1029 else if (yang_dnode_exists(dnode, "./destination-any"))
1030 vty_out(vty, " any");
1031
1032 vty_out(vty, "\n");
1033 return;
1034 }
1035
1036 /* Zebra style access list. */
1037 if (!is_any) {
1038 /* If type is MAC don't show '/mask'. */
1039 if (type == 2 /* mac */) {
1040 prefix_mac2str(&p.u.prefix_eth, macstr, sizeof(macstr));
1041 vty_out(vty, " %s", macstr);
1042 } else
1043 vty_out(vty, " %pFX", &p);
1044 } else
1045 vty_out(vty, " any");
1046
1047 if (is_exact)
1048 vty_out(vty, " exact-match");
1049
1050 vty_out(vty, "\n");
1051 }
1052
1053 void access_list_remark_show(struct vty *vty, struct lyd_node *dnode,
1054 bool show_defaults)
1055 {
1056 int type = yang_dnode_get_enum(dnode, "../type");
1057
1058 switch (type) {
1059 case YALT_IPV4:
1060 break;
1061 case YALT_IPV6:
1062 vty_out(vty, "ipv6 ");
1063 break;
1064 case YALT_MAC:
1065 vty_out(vty, "mac ");
1066 break;
1067 }
1068
1069 vty_out(vty, "access-list %s remark %s\n",
1070 yang_dnode_get_string(dnode, "../name"),
1071 yang_dnode_get_string(dnode, NULL));
1072 }
1073
1074 /*
1075 * Prefix lists.
1076 */
1077
1078 /**
1079 * Remove main data structure prefix list if there are no more entries or
1080 * remark. This fixes compatibility with old CLI and tests.
1081 */
1082 static int plist_remove_if_empty(struct vty *vty, const char *iptype,
1083 const char *name)
1084 {
1085 char xpath[XPATH_MAXLEN];
1086
1087 snprintf(xpath, sizeof(xpath),
1088 "/frr-filter:lib/prefix-list[type='%s'][name='%s']/remark",
1089 iptype, name);
1090 /* List is not empty if there is a remark, check that: */
1091 if (yang_dnode_exists(vty->candidate_config->dnode, xpath))
1092 return CMD_SUCCESS;
1093
1094 /* Check if we have any entries: */
1095 snprintf(xpath, sizeof(xpath),
1096 "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype,
1097 name);
1098 /*
1099 * NOTE: if the list is empty it will return the first sequence
1100 * number: 5.
1101 */
1102 if (acl_get_seq(vty, xpath) != 5)
1103 return CMD_SUCCESS;
1104
1105 /* Nobody is using this list, lets remove it. */
1106 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
1107 return nb_cli_apply_changes(vty, NULL);
1108 }
1109
1110 static int plist_remove(struct vty *vty, const char *iptype, const char *name,
1111 const char *seq, const char *action, struct prefix *p,
1112 long ge, long le)
1113 {
1114 struct prefix_list_entry *pentry;
1115 enum prefix_list_type plt;
1116 struct prefix_list *pl;
1117 struct lyd_node *dnode;
1118 char xpath[XPATH_MAXLEN];
1119 char xpath_entry[XPATH_MAXLEN + 32];
1120 int rv;
1121
1122 /* If the user provided sequence number, then just go for it. */
1123 if (seq != NULL) {
1124 snprintf(
1125 xpath, sizeof(xpath),
1126 "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%s']",
1127 iptype, name, seq);
1128 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
1129
1130 rv = nb_cli_apply_changes(vty, NULL);
1131 if (rv == CMD_SUCCESS)
1132 return plist_remove_if_empty(vty, iptype, name);
1133
1134 return rv;
1135 }
1136
1137 /* Otherwise, to keep compatibility, we need to figure it out. */
1138 snprintf(xpath, sizeof(xpath),
1139 "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype,
1140 name);
1141
1142 /* Access-list must exist before entries. */
1143 if (yang_dnode_exists(running_config->dnode, xpath) == false)
1144 return CMD_WARNING;
1145
1146 /* Use access-list data structure to fetch sequence. */
1147 assert(action != NULL);
1148 if (strcmp(action, "permit") == 0)
1149 plt = PREFIX_PERMIT;
1150 else
1151 plt = PREFIX_DENY;
1152
1153 dnode = yang_dnode_get(running_config->dnode, xpath);
1154 pl = nb_running_get_entry(dnode, NULL, true);
1155 pentry = prefix_list_entry_lookup(pl, p, plt, -1, le, ge);
1156 if (pentry == NULL)
1157 return CMD_WARNING;
1158
1159 snprintfrr(xpath_entry, sizeof(xpath_entry),
1160 "%s/entry[sequence='%" PRId64 "']", xpath, pentry->seq);
1161 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
1162
1163 rv = nb_cli_apply_changes(vty, NULL);
1164 if (rv == CMD_SUCCESS)
1165 return plist_remove_if_empty(vty, iptype, name);
1166
1167 return rv;
1168 }
1169
1170 DEFPY_YANG(
1171 ip_prefix_list, ip_prefix_list_cmd,
1172 "ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)$ge|le (0-32)$le}]>",
1173 IP_STR
1174 PREFIX_LIST_STR
1175 PREFIX_LIST_NAME_STR
1176 ACCESS_LIST_SEQ_STR
1177 ACCESS_LIST_ACTION_STR
1178 "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n"
1179 "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
1180 "Minimum prefix length to be matched\n"
1181 "Minimum prefix length\n"
1182 "Maximum prefix length to be matched\n"
1183 "Maximum prefix length\n")
1184 {
1185 int64_t sseq;
1186 char xpath[XPATH_MAXLEN];
1187 char xpath_entry[XPATH_MAXLEN + 128];
1188
1189 /*
1190 * Create the prefix-list first, so we can generate sequence if
1191 * none given (backward compatibility).
1192 */
1193 snprintf(xpath, sizeof(xpath),
1194 "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name);
1195 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
1196 if (seq_str == NULL) {
1197 /* Use XPath to find the next sequence number. */
1198 sseq = acl_get_seq(vty, xpath);
1199 snprintfrr(xpath_entry, sizeof(xpath_entry),
1200 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
1201 } else
1202 snprintfrr(xpath_entry, sizeof(xpath_entry),
1203 "%s/entry[sequence='%s']", xpath, seq_str);
1204
1205 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
1206
1207 nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
1208 if (prefix_str != NULL) {
1209 nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY,
1210 prefix_str);
1211
1212 if (ge_str)
1213 nb_cli_enqueue_change(
1214 vty, "./ipv4-prefix-length-greater-or-equal",
1215 NB_OP_MODIFY, ge_str);
1216 if (le_str)
1217 nb_cli_enqueue_change(
1218 vty, "./ipv4-prefix-length-lesser-or-equal",
1219 NB_OP_MODIFY, le_str);
1220 } else {
1221 nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
1222 }
1223
1224 return nb_cli_apply_changes(vty, xpath_entry);
1225 }
1226
1227 DEFPY_YANG(
1228 no_ip_prefix_list, no_ip_prefix_list_cmd,
1229 "no ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)|le (0-32)}]>",
1230 NO_STR
1231 IP_STR
1232 PREFIX_LIST_STR
1233 PREFIX_LIST_NAME_STR
1234 ACCESS_LIST_SEQ_STR
1235 ACCESS_LIST_ACTION_STR
1236 "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n"
1237 "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
1238 "Minimum prefix length to be matched\n"
1239 "Minimum prefix length\n"
1240 "Maximum prefix length to be matched\n"
1241 "Maximum prefix length\n")
1242 {
1243 return plist_remove(vty, "ipv4", name, seq_str, action,
1244 (struct prefix *)prefix, ge, le);
1245 }
1246
1247 DEFPY_YANG(
1248 no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd,
1249 "no ip prefix-list WORD$name seq (1-4294967295)$seq",
1250 NO_STR
1251 IP_STR
1252 PREFIX_LIST_STR
1253 PREFIX_LIST_NAME_STR
1254 ACCESS_LIST_SEQ_STR)
1255 {
1256 return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0);
1257 }
1258
1259 DEFPY_YANG(
1260 no_ip_prefix_list_all, no_ip_prefix_list_all_cmd,
1261 "no ip prefix-list WORD$name",
1262 NO_STR
1263 IP_STR
1264 PREFIX_LIST_STR
1265 PREFIX_LIST_NAME_STR)
1266 {
1267 char xpath[XPATH_MAXLEN];
1268
1269 snprintf(xpath, sizeof(xpath),
1270 "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name);
1271 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
1272
1273 return nb_cli_apply_changes(vty, NULL);
1274 }
1275
1276 DEFPY_YANG(
1277 ip_prefix_list_remark, ip_prefix_list_remark_cmd,
1278 "ip prefix-list WORD$name description LINE...",
1279 IP_STR
1280 PREFIX_LIST_STR
1281 PREFIX_LIST_NAME_STR
1282 ACCESS_LIST_REMARK_STR
1283 ACCESS_LIST_REMARK_LINE_STR)
1284 {
1285 int rv;
1286 char *remark;
1287 char xpath[XPATH_MAXLEN];
1288
1289 snprintf(xpath, sizeof(xpath),
1290 "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name);
1291 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
1292
1293 remark = argv_concat(argv, argc, 4);
1294 nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
1295 rv = nb_cli_apply_changes(vty, xpath);
1296 XFREE(MTYPE_TMP, remark);
1297
1298 return rv;
1299 }
1300
1301 DEFPY_YANG(
1302 no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd,
1303 "no ip prefix-list WORD$name description",
1304 NO_STR
1305 IP_STR
1306 PREFIX_LIST_STR
1307 PREFIX_LIST_NAME_STR
1308 ACCESS_LIST_REMARK_STR)
1309 {
1310 char xpath[XPATH_MAXLEN];
1311
1312 snprintf(xpath, sizeof(xpath),
1313 "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']/remark",
1314 name);
1315 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
1316
1317 return nb_cli_apply_changes(vty, NULL);
1318 }
1319
1320 ALIAS(
1321 no_ip_prefix_list_remark, no_ip_prefix_list_remark_line_cmd,
1322 "no ip prefix-list WORD$name description LINE...",
1323 NO_STR
1324 IP_STR
1325 PREFIX_LIST_STR
1326 PREFIX_LIST_NAME_STR
1327 ACCESS_LIST_REMARK_STR
1328 ACCESS_LIST_REMARK_LINE_STR)
1329
1330 DEFPY_YANG(
1331 ipv6_prefix_list, ipv6_prefix_list_cmd,
1332 "ipv6 prefix-list WORD$name [seq (1-4294967295)] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
1333 IPV6_STR
1334 PREFIX_LIST_STR
1335 PREFIX_LIST_NAME_STR
1336 ACCESS_LIST_SEQ_STR
1337 ACCESS_LIST_ACTION_STR
1338 "Any prefix match. Same as \"::0/0 le 128\"\n"
1339 "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
1340 "Maximum prefix length to be matched\n"
1341 "Maximum prefix length\n"
1342 "Minimum prefix length to be matched\n"
1343 "Minimum prefix length\n")
1344 {
1345 int64_t sseq;
1346 char xpath[XPATH_MAXLEN];
1347 char xpath_entry[XPATH_MAXLEN + 128];
1348
1349 /*
1350 * Create the prefix-list first, so we can generate sequence if
1351 * none given (backward compatibility).
1352 */
1353 snprintf(xpath, sizeof(xpath),
1354 "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name);
1355 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
1356 if (seq_str == NULL) {
1357 /* Use XPath to find the next sequence number. */
1358 sseq = acl_get_seq(vty, xpath);
1359 snprintfrr(xpath_entry, sizeof(xpath_entry),
1360 "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
1361 } else
1362 snprintfrr(xpath_entry, sizeof(xpath_entry),
1363 "%s/entry[sequence='%s']", xpath, seq_str);
1364
1365 nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
1366
1367 nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
1368 if (prefix_str != NULL) {
1369 nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY,
1370 prefix_str);
1371
1372 if (ge_str)
1373 nb_cli_enqueue_change(
1374 vty, "./ipv6-prefix-length-greater-or-equal",
1375 NB_OP_MODIFY, ge_str);
1376 if (le_str)
1377 nb_cli_enqueue_change(
1378 vty, "./ipv6-prefix-length-lesser-or-equal",
1379 NB_OP_MODIFY, le_str);
1380 } else {
1381 nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
1382 }
1383
1384 return nb_cli_apply_changes(vty, xpath_entry);
1385 }
1386
1387 DEFPY_YANG(
1388 no_ipv6_prefix_list, no_ipv6_prefix_list_cmd,
1389 "no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
1390 NO_STR
1391 IPV6_STR
1392 PREFIX_LIST_STR
1393 PREFIX_LIST_NAME_STR
1394 ACCESS_LIST_SEQ_STR
1395 ACCESS_LIST_ACTION_STR
1396 "Any prefix match. Same as \"::0/0 le 128\"\n"
1397 "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
1398 "Maximum prefix length to be matched\n"
1399 "Maximum prefix length\n"
1400 "Minimum prefix length to be matched\n"
1401 "Minimum prefix length\n")
1402 {
1403 return plist_remove(vty, "ipv6", name, seq_str, action,
1404 (struct prefix *)prefix, ge, le);
1405 }
1406
1407 DEFPY_YANG(
1408 no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd,
1409 "no ipv6 prefix-list WORD$name seq (1-4294967295)$seq",
1410 NO_STR
1411 IPV6_STR
1412 PREFIX_LIST_STR
1413 PREFIX_LIST_NAME_STR
1414 ACCESS_LIST_SEQ_STR)
1415 {
1416 return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0);
1417 }
1418
1419 DEFPY_YANG(
1420 no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd,
1421 "no ipv6 prefix-list WORD$name",
1422 NO_STR
1423 IPV6_STR
1424 PREFIX_LIST_STR
1425 PREFIX_LIST_NAME_STR)
1426 {
1427 char xpath[XPATH_MAXLEN];
1428
1429 snprintf(xpath, sizeof(xpath),
1430 "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name);
1431 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
1432
1433 return nb_cli_apply_changes(vty, NULL);
1434 }
1435
1436 DEFPY_YANG(
1437 ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd,
1438 "ipv6 prefix-list WORD$name description LINE...",
1439 IPV6_STR
1440 PREFIX_LIST_STR
1441 PREFIX_LIST_NAME_STR
1442 ACCESS_LIST_REMARK_STR
1443 ACCESS_LIST_REMARK_LINE_STR)
1444 {
1445 int rv;
1446 char *remark;
1447 char xpath[XPATH_MAXLEN];
1448
1449 snprintf(xpath, sizeof(xpath),
1450 "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name);
1451 nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
1452
1453 remark = argv_concat(argv, argc, 4);
1454 nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
1455 rv = nb_cli_apply_changes(vty, xpath);
1456 XFREE(MTYPE_TMP, remark);
1457
1458 return rv;
1459 }
1460
1461 DEFPY_YANG(
1462 no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd,
1463 "no ipv6 prefix-list WORD$name description",
1464 NO_STR
1465 IPV6_STR
1466 PREFIX_LIST_STR
1467 PREFIX_LIST_NAME_STR
1468 ACCESS_LIST_REMARK_STR)
1469 {
1470 char xpath[XPATH_MAXLEN];
1471
1472 snprintf(xpath, sizeof(xpath),
1473 "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']/remark",
1474 name);
1475 nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
1476
1477 return nb_cli_apply_changes(vty, NULL);
1478 }
1479
1480 ALIAS(
1481 no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_line_cmd,
1482 "no ipv6 prefix-list WORD$name description LINE...",
1483 NO_STR
1484 IPV6_STR
1485 PREFIX_LIST_STR
1486 PREFIX_LIST_NAME_STR
1487 ACCESS_LIST_REMARK_STR
1488 ACCESS_LIST_REMARK_LINE_STR)
1489
1490 void prefix_list_show(struct vty *vty, struct lyd_node *dnode,
1491 bool show_defaults)
1492 {
1493 int type = yang_dnode_get_enum(dnode, "../type");
1494 const char *ge_str = NULL, *le_str = NULL;
1495 bool is_any;
1496 struct prefix p;
1497
1498 is_any = yang_dnode_exists(dnode, "./any");
1499 switch (type) {
1500 case YPLT_IPV4:
1501 if (!is_any)
1502 yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix");
1503 if (yang_dnode_exists(dnode,
1504 "./ipv4-prefix-length-greater-or-equal"))
1505 ge_str = yang_dnode_get_string(
1506 dnode, "./ipv4-prefix-length-greater-or-equal");
1507 if (yang_dnode_exists(dnode,
1508 "./ipv4-prefix-length-lesser-or-equal"))
1509 le_str = yang_dnode_get_string(
1510 dnode, "./ipv4-prefix-length-lesser-or-equal");
1511
1512 vty_out(vty, "ip ");
1513 break;
1514 case YPLT_IPV6:
1515 if (!is_any)
1516 yang_dnode_get_prefix(&p, dnode, "ipv6-prefix");
1517 if (yang_dnode_exists(dnode,
1518 "./ipv6-prefix-length-greater-or-equal"))
1519 ge_str = yang_dnode_get_string(
1520 dnode, "./ipv6-prefix-length-greater-or-equal");
1521 if (yang_dnode_exists(dnode,
1522 "./ipv6-prefix-length-lesser-or-equal"))
1523 le_str = yang_dnode_get_string(
1524 dnode, "./ipv6-prefix-length-lesser-or-equal");
1525
1526 vty_out(vty, "ipv6 ");
1527 break;
1528 }
1529
1530 vty_out(vty, "prefix-list %s seq %s %s",
1531 yang_dnode_get_string(dnode, "../name"),
1532 yang_dnode_get_string(dnode, "./sequence"),
1533 yang_dnode_get_string(dnode, "./action"));
1534
1535 if (is_any) {
1536 vty_out(vty, " any\n");
1537 return;
1538 }
1539
1540 vty_out(vty, " %pFX", &p);
1541 if (ge_str)
1542 vty_out(vty, " ge %s", ge_str);
1543 if (le_str)
1544 vty_out(vty, " le %s", le_str);
1545
1546 vty_out(vty, "\n");
1547 }
1548
1549 void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode,
1550 bool show_defaults)
1551 {
1552 int type = yang_dnode_get_enum(dnode, "../type");
1553
1554 switch (type) {
1555 case YPLT_IPV4:
1556 vty_out(vty, "ip ");
1557 break;
1558 case YPLT_IPV6:
1559 vty_out(vty, "ipv6 ");
1560 break;
1561 }
1562
1563 vty_out(vty, "prefix-list %s description %s\n",
1564 yang_dnode_get_string(dnode, "../name"),
1565 yang_dnode_get_string(dnode, NULL));
1566 }
1567
1568 void filter_cli_init(void)
1569 {
1570 /* access-list cisco-style (legacy). */
1571 install_element(CONFIG_NODE, &access_list_std_cmd);
1572 install_element(CONFIG_NODE, &no_access_list_std_cmd);
1573 install_element(CONFIG_NODE, &access_list_ext_cmd);
1574 install_element(CONFIG_NODE, &no_access_list_ext_cmd);
1575
1576 /* access-list zebra-style. */
1577 install_element(CONFIG_NODE, &access_list_cmd);
1578 install_element(CONFIG_NODE, &no_access_list_cmd);
1579 install_element(CONFIG_NODE, &no_access_list_all_cmd);
1580 install_element(CONFIG_NODE, &access_list_remark_cmd);
1581 install_element(CONFIG_NODE, &no_access_list_remark_cmd);
1582 install_element(CONFIG_NODE, &no_access_list_remark_line_cmd);
1583
1584 install_element(CONFIG_NODE, &ipv6_access_list_cmd);
1585 install_element(CONFIG_NODE, &no_ipv6_access_list_cmd);
1586 install_element(CONFIG_NODE, &no_ipv6_access_list_all_cmd);
1587 install_element(CONFIG_NODE, &ipv6_access_list_remark_cmd);
1588 install_element(CONFIG_NODE, &no_ipv6_access_list_remark_cmd);
1589 install_element(CONFIG_NODE, &no_ipv6_access_list_remark_line_cmd);
1590
1591 install_element(CONFIG_NODE, &mac_access_list_cmd);
1592 install_element(CONFIG_NODE, &no_mac_access_list_cmd);
1593 install_element(CONFIG_NODE, &no_mac_access_list_all_cmd);
1594 install_element(CONFIG_NODE, &mac_access_list_remark_cmd);
1595 install_element(CONFIG_NODE, &no_mac_access_list_remark_cmd);
1596 install_element(CONFIG_NODE, &no_mac_access_list_remark_line_cmd);
1597
1598 /* prefix lists. */
1599 install_element(CONFIG_NODE, &ip_prefix_list_cmd);
1600 install_element(CONFIG_NODE, &no_ip_prefix_list_cmd);
1601 install_element(CONFIG_NODE, &no_ip_prefix_list_seq_cmd);
1602 install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd);
1603 install_element(CONFIG_NODE, &ip_prefix_list_remark_cmd);
1604 install_element(CONFIG_NODE, &no_ip_prefix_list_remark_cmd);
1605 install_element(CONFIG_NODE, &no_ip_prefix_list_remark_line_cmd);
1606
1607 install_element(CONFIG_NODE, &ipv6_prefix_list_cmd);
1608 install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd);
1609 install_element(CONFIG_NODE, &no_ipv6_prefix_list_seq_cmd);
1610 install_element(CONFIG_NODE, &no_ipv6_prefix_list_all_cmd);
1611 install_element(CONFIG_NODE, &ipv6_prefix_list_remark_cmd);
1612 install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_cmd);
1613 install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_line_cmd);
1614 }