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