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