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