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