1 /*
2  * Route map northbound 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 
25 #include "lib/command.h"
26 #include "lib/northbound_cli.h"
27 #include "lib/routemap.h"
28 
29 #ifndef VTYSH_EXTRACT_PL
30 #include "lib/routemap_cli_clippy.c"
31 #endif /* VTYSH_EXTRACT_PL */
32 
33 #define ROUTE_MAP_CMD_STR \
34 	"Create route-map or enter route-map command mode\n" \
35 	"Route map tag\n"
36 #define ROUTE_MAP_OP_CMD_STR \
37 	"Route map denies set operations\n" \
38 	"Route map permits set operations\n"
39 #define ROUTE_MAP_SEQUENCE_CMD_STR \
40 	"Sequence to insert to/delete from existing route-map entry\n"
41 
42 DEFPY_YANG_NOSH(
43 	route_map, route_map_cmd,
44 	"route-map WORD$name <deny|permit>$action (1-65535)$sequence",
45 	ROUTE_MAP_CMD_STR
46 	ROUTE_MAP_OP_CMD_STR
47 	ROUTE_MAP_SEQUENCE_CMD_STR)
48 {
49 	struct route_map_index *rmi;
50 	struct route_map *rm;
51 	int action_type;
52 	char xpath_action[XPATH_MAXLEN + 64];
53 	char xpath_index[XPATH_MAXLEN + 32];
54 	char xpath[XPATH_MAXLEN];
55 	int rv;
56 
57 	snprintf(xpath, sizeof(xpath),
58 		 "/frr-route-map:lib/route-map[name='%s']", name);
59 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
60 
61 	snprintf(xpath_index, sizeof(xpath_index), "%s/entry[sequence='%lu']",
62 		 xpath, sequence);
63 	nb_cli_enqueue_change(vty, xpath_index, NB_OP_CREATE, NULL);
64 
65 	snprintf(xpath_action, sizeof(xpath_action), "%s/action", xpath_index);
66 	nb_cli_enqueue_change(vty, xpath_action, NB_OP_MODIFY, action);
67 
68 	rv = nb_cli_apply_changes(vty, NULL);
69 	if (rv == CMD_SUCCESS) {
70 		VTY_PUSH_XPATH(RMAP_NODE, xpath_index);
71 
72 		/* Add support for non-migrated route map users. */
73 		nb_cli_pending_commit_check(vty);
74 		rm = route_map_get(name);
75 		action_type = (action[0] == 'p') ? RMAP_PERMIT : RMAP_DENY;
76 		rmi = route_map_index_get(rm, action_type, sequence);
77 		VTY_PUSH_CONTEXT(RMAP_NODE, rmi);
78 	}
79 
80 	return rv;
81 }
82 
83 DEFPY_YANG(
84 	no_route_map_all, no_route_map_all_cmd,
85 	"no route-map WORD$name",
86 	NO_STR
87 	ROUTE_MAP_CMD_STR)
88 {
89 	char xpath[XPATH_MAXLEN];
90 
91 	snprintf(xpath, sizeof(xpath),
92 		 "/frr-route-map:lib/route-map[name='%s']", name);
93 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
94 
95 	return nb_cli_apply_changes(vty, NULL);
96 }
97 
98 DEFPY_YANG(
99 	no_route_map, no_route_map_cmd,
100 	"no route-map WORD$name <deny|permit>$action (1-65535)$sequence",
101 	NO_STR
102 	ROUTE_MAP_CMD_STR
103 	ROUTE_MAP_OP_CMD_STR
104 	ROUTE_MAP_SEQUENCE_CMD_STR)
105 {
106 	char xpath[XPATH_MAXLEN];
107 
108 	snprintf(xpath, sizeof(xpath),
109 		 "/frr-route-map:lib/route-map[name='%s']/entry[sequence='%lu']",
110 		 name, sequence);
111 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
112 
113 	return nb_cli_apply_changes(vty, NULL);
114 }
115 
route_map_instance_show(struct vty * vty,struct lyd_node * dnode,bool show_defaults)116 void route_map_instance_show(struct vty *vty, struct lyd_node *dnode,
117 			     bool show_defaults)
118 {
119 	const struct route_map_rule *rmr;
120 	const struct route_map_index *rmi;
121 	const char *name = yang_dnode_get_string(dnode, "../name");
122 	const char *action = yang_dnode_get_string(dnode, "./action");
123 	const char *sequence = yang_dnode_get_string(dnode, "./sequence");
124 
125 	vty_out(vty, "route-map %s %s %s\n", name, action, sequence);
126 
127 	rmi = nb_running_get_entry(dnode, NULL, false);
128 	if (rmi == NULL) {
129 		/*
130 		 * We can't have outdated rules if route map hasn't
131 		 * been created yet.
132 		 */
133 		return;
134 	}
135 
136 #define SKIP_RULE(name) if (strcmp((name), rmr->cmd->str) == 0) continue
137 
138 	/* Print route map `match` for old CLI users. */
139 	for (rmr = rmi->match_list.head; rmr; rmr = rmr->next) {
140 		/* Skip all matches implemented by northbound. */
141 		SKIP_RULE("interface");
142 		SKIP_RULE("ip address");
143 		SKIP_RULE("ip address prefix-list");
144 		SKIP_RULE("ip next-hop");
145 		SKIP_RULE("ip next-hop prefix-list");
146 		SKIP_RULE("ip next-hop type");
147 		SKIP_RULE("ipv6 address");
148 		SKIP_RULE("ipv6 address prefix-list");
149 		SKIP_RULE("ipv6 next-hop type");
150 		SKIP_RULE("metric");
151 		SKIP_RULE("tag");
152 		/* Zebra specific match conditions. */
153 		SKIP_RULE("ip address prefix-len");
154 		SKIP_RULE("ipv6 address prefix-len");
155 		SKIP_RULE("ip next-hop prefix-len");
156 		SKIP_RULE("source-protocol");
157 		SKIP_RULE("source-instance");
158 
159 		vty_out(vty, " match %s %s\n", rmr->cmd->str,
160 			rmr->rule_str ? rmr->rule_str : "");
161 	}
162 
163 	/* Print route map `set` for old CLI users. */
164 	for (rmr = rmi->set_list.head; rmr; rmr = rmr->next) {
165 		/* Skip all sets implemented by northbound. */
166 		SKIP_RULE("metric");
167 		SKIP_RULE("tag");
168 		/* Zebra specific set actions. */
169 		SKIP_RULE("src");
170 
171 		vty_out(vty, " set %s %s\n", rmr->cmd->str,
172 			rmr->rule_str ? rmr->rule_str : "");
173 	}
174 
175 #undef SKIP_RULE
176 }
177 
route_map_instance_show_end(struct vty * vty,struct lyd_node * dnode)178 void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode)
179 {
180 	vty_out(vty, "!\n");
181 }
182 
183 DEFPY_YANG(
184 	match_interface, match_interface_cmd,
185 	"match interface IFNAME",
186 	MATCH_STR
187 	"Match first hop interface of route\n"
188 	INTERFACE_STR)
189 {
190 	const char *xpath = "./match-condition[condition='interface']";
191 	char xpath_value[XPATH_MAXLEN];
192 
193 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
194 	snprintf(xpath_value, sizeof(xpath_value), "%s/interface", xpath);
195 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ifname);
196 
197 	return nb_cli_apply_changes(vty, NULL);
198 }
199 
200 DEFPY_YANG(
201 	no_match_interface, no_match_interface_cmd,
202 	"no match interface [IFNAME]",
203 	NO_STR
204 	MATCH_STR
205 	"Match first hop interface of route\n"
206 	INTERFACE_STR)
207 {
208 	const char *xpath = "./match-condition[condition='interface']";
209 
210 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
211 
212 	return nb_cli_apply_changes(vty, NULL);
213 }
214 
215 DEFPY_YANG(
216 	match_ip_address, match_ip_address_cmd,
217 	"match ip address <(1-199)|(1300-2699)|WORD>$name",
218 	MATCH_STR
219 	IP_STR
220 	"Match address of route\n"
221 	"IP access-list number\n"
222 	"IP access-list number (expanded range)\n"
223 	"IP Access-list name\n")
224 {
225 	const char *xpath = "./match-condition[condition='ipv4-address-list']";
226 	char xpath_value[XPATH_MAXLEN + 32];
227 
228 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
229 	snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
230 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
231 
232 	return nb_cli_apply_changes(vty, NULL);
233 }
234 
235 DEFPY_YANG(
236 	no_match_ip_address, no_match_ip_address_cmd,
237 	"no match ip address [<(1-199)|(1300-2699)|WORD>]",
238 	NO_STR
239 	MATCH_STR
240 	IP_STR
241 	"Match address of route\n"
242 	"IP access-list number\n"
243 	"IP access-list number (expanded range)\n"
244 	"IP Access-list name\n")
245 {
246 	const char *xpath = "./match-condition[condition='ipv4-address-list']";
247 
248 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
249 
250 	return nb_cli_apply_changes(vty, NULL);
251 }
252 
253 DEFPY_YANG(
254 	match_ip_address_prefix_list,
255 	match_ip_address_prefix_list_cmd,
256 	"match ip address prefix-list WORD$name",
257 	MATCH_STR
258 	IP_STR
259 	"Match address of route\n"
260 	"Match entries of prefix-lists\n"
261 	"IP prefix-list name\n")
262 {
263 	const char *xpath = "./match-condition[condition='ipv4-prefix-list']";
264 	char xpath_value[XPATH_MAXLEN];
265 
266 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
267 	snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
268 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
269 
270 	return nb_cli_apply_changes(vty, NULL);
271 }
272 
273 DEFPY_YANG(
274 	no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd,
275 	"no match ip address prefix-list [WORD]",
276 	NO_STR
277 	MATCH_STR
278 	IP_STR
279 	"Match address of route\n"
280 	"Match entries of prefix-lists\n"
281 	"IP prefix-list name\n")
282 {
283 	const char *xpath = "./match-condition[condition='ipv4-prefix-list']";
284 
285 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
286 
287 	return nb_cli_apply_changes(vty, NULL);
288 }
289 
290 DEFPY_YANG(
291 	match_ip_next_hop, match_ip_next_hop_cmd,
292 	"match ip next-hop <(1-199)|(1300-2699)|WORD>$name",
293 	MATCH_STR
294 	IP_STR
295 	"Match next-hop address of route\n"
296 	"IP access-list number\n"
297 	"IP access-list number (expanded range)\n"
298 	"IP Access-list name\n")
299 {
300 	const char *xpath = "./match-condition[condition='ipv4-next-hop-list']";
301 	char xpath_value[XPATH_MAXLEN + 32];
302 
303 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
304 	snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
305 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
306 
307 	return nb_cli_apply_changes(vty, NULL);
308 }
309 
310 DEFPY_YANG(
311 	no_match_ip_next_hop, no_match_ip_next_hop_cmd,
312 	"no match ip next-hop [<(1-199)|(1300-2699)|WORD>]",
313 	NO_STR
314 	MATCH_STR
315 	IP_STR
316 	"Match address of route\n"
317 	"IP access-list number\n"
318 	"IP access-list number (expanded range)\n"
319 	"IP Access-list name\n")
320 {
321 	const char *xpath = "./match-condition[condition='ipv4-next-hop-list']";
322 
323 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
324 
325 	return nb_cli_apply_changes(vty, NULL);
326 }
327 
328 DEFPY_YANG(
329 	match_ip_next_hop_prefix_list,
330 	match_ip_next_hop_prefix_list_cmd,
331 	"match ip next-hop prefix-list WORD$name",
332 	MATCH_STR
333 	IP_STR
334 	"Match next-hop address of route\n"
335 	"Match entries of prefix-lists\n"
336 	"IP prefix-list name\n")
337 {
338 	const char *xpath =
339 		"./match-condition[condition='ipv4-next-hop-prefix-list']";
340 	char xpath_value[XPATH_MAXLEN];
341 
342 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
343 	snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
344 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
345 
346 	return nb_cli_apply_changes(vty, NULL);
347 }
348 
349 DEFPY_YANG(
350 	no_match_ip_next_hop_prefix_list,
351 	no_match_ip_next_hop_prefix_list_cmd,
352 	"no match ip next-hop prefix-list [WORD]",
353 	NO_STR
354 	MATCH_STR
355 	IP_STR
356 	"Match next-hop address of route\n"
357 	"Match entries of prefix-lists\n"
358 	"IP prefix-list name\n")
359 {
360 	const char *xpath =
361 		"./match-condition[condition='ipv4-next-hop-prefix-list']";
362 
363 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
364 
365 	return nb_cli_apply_changes(vty, NULL);
366 }
367 
368 DEFPY_YANG(
369 	match_ip_next_hop_type, match_ip_next_hop_type_cmd,
370 	"match ip next-hop type <blackhole>$type",
371 	MATCH_STR
372 	IP_STR
373 	"Match next-hop address of route\n"
374 	"Match entries by type\n"
375 	"Blackhole\n")
376 {
377 	const char *xpath = "./match-condition[condition='ipv4-next-hop-type']";
378 	char xpath_value[XPATH_MAXLEN];
379 
380 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
381 	snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-next-hop-type",
382 		 xpath);
383 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type);
384 
385 	return nb_cli_apply_changes(vty, NULL);
386 }
387 
388 DEFPY_YANG(
389 	no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd,
390 	"no match ip next-hop type [<blackhole>]",
391 	NO_STR MATCH_STR IP_STR
392 	"Match next-hop address of route\n"
393 	"Match entries by type\n"
394 	"Blackhole\n")
395 {
396 	const char *xpath = "./match-condition[condition='ipv4-next-hop-type']";
397 
398 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
399 
400 	return nb_cli_apply_changes(vty, NULL);
401 }
402 
403 DEFPY_YANG(
404 	match_ipv6_address, match_ipv6_address_cmd,
405 	"match ipv6 address WORD$name",
406 	MATCH_STR
407 	IPV6_STR
408 	"Match IPv6 address of route\n"
409 	"IPv6 access-list name\n")
410 {
411 	const char *xpath = "./match-condition[condition='ipv6-address-list']";
412 	char xpath_value[XPATH_MAXLEN];
413 
414 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
415 	snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
416 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
417 
418 	return nb_cli_apply_changes(vty, NULL);
419 }
420 
421 DEFPY_YANG(
422 	no_match_ipv6_address, no_match_ipv6_address_cmd,
423 	"no match ipv6 address [WORD]",
424 	NO_STR
425 	MATCH_STR
426 	IPV6_STR
427 	"Match IPv6 address of route\n"
428 	"IPv6 access-list name\n")
429 {
430 	const char *xpath = "./match-condition[condition='ipv6-address-list']";
431 
432 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
433 
434 	return nb_cli_apply_changes(vty, NULL);
435 }
436 
437 DEFPY_YANG(
438 	match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd,
439 	"match ipv6 address prefix-list WORD$name",
440 	MATCH_STR
441 	IPV6_STR
442 	"Match address of route\n"
443 	"Match entries of prefix-lists\n"
444 	"IP prefix-list name\n")
445 {
446 	const char *xpath = "./match-condition[condition='ipv6-prefix-list']";
447 	char xpath_value[XPATH_MAXLEN];
448 
449 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
450 	snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath);
451 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name);
452 
453 	return nb_cli_apply_changes(vty, NULL);
454 }
455 
456 DEFPY_YANG(
457 	no_match_ipv6_address_prefix_list,
458 	no_match_ipv6_address_prefix_list_cmd,
459 	"no match ipv6 address prefix-list [WORD]",
460 	NO_STR
461 	MATCH_STR
462 	IPV6_STR
463 	"Match address of route\n"
464 	"Match entries of prefix-lists\n"
465 	"IP prefix-list name\n")
466 {
467 	const char *xpath = "./match-condition[condition='ipv6-prefix-list']";
468 
469 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
470 
471 	return nb_cli_apply_changes(vty, NULL);
472 }
473 
474 DEFPY_YANG(
475 	match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd,
476 	"match ipv6 next-hop type <blackhole>$type",
477 	MATCH_STR IPV6_STR
478 	"Match next-hop address of route\n"
479 	"Match entries by type\n"
480 	"Blackhole\n")
481 {
482 	const char *xpath = "./match-condition[condition='ipv6-next-hop-type']";
483 	char xpath_value[XPATH_MAXLEN];
484 
485 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
486 	snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-next-hop-type",
487 		 xpath);
488 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type);
489 
490 	return nb_cli_apply_changes(vty, NULL);
491 }
492 
493 DEFPY_YANG(
494 	no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd,
495 	"no match ipv6 next-hop type [<blackhole>]",
496 	NO_STR MATCH_STR IPV6_STR
497 	"Match address of route\n"
498 	"Match entries by type\n"
499 	"Blackhole\n")
500 {
501 	const char *xpath = "./match-condition[condition='ipv6-next-hop-type']";
502 
503 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
504 
505 	return nb_cli_apply_changes(vty, NULL);
506 }
507 
508 DEFPY_YANG(
509 	match_metric, match_metric_cmd,
510 	"match metric (0-4294967295)$metric",
511 	MATCH_STR
512 	"Match metric of route\n"
513 	"Metric value\n")
514 {
515 	const char *xpath = "./match-condition[condition='metric']";
516 	char xpath_value[XPATH_MAXLEN];
517 
518 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
519 	snprintf(xpath_value, sizeof(xpath_value), "%s/metric", xpath);
520 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, metric_str);
521 
522 	return nb_cli_apply_changes(vty, NULL);
523 }
524 
525 DEFPY_YANG(
526 	no_match_metric, no_match_metric_cmd,
527 	"no match metric [(0-4294967295)]",
528 	NO_STR
529 	MATCH_STR
530 	"Match metric of route\n"
531 	"Metric value\n")
532 {
533 	const char *xpath = "./match-condition[condition='metric']";
534 
535 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
536 
537 	return nb_cli_apply_changes(vty, NULL);
538 }
539 
540 DEFPY_YANG(
541 	match_tag, match_tag_cmd,
542 	"match tag (1-4294967295)$tag",
543 	MATCH_STR
544 	"Match tag of route\n"
545 	"Tag value\n")
546 {
547 	const char *xpath = "./match-condition[condition='tag']";
548 	char xpath_value[XPATH_MAXLEN];
549 
550 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
551 	snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath);
552 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str);
553 
554 	return nb_cli_apply_changes(vty, NULL);
555 }
556 
557 DEFPY_YANG(
558 	no_match_tag, no_match_tag_cmd,
559 	"no match tag [(1-4294967295)]",
560 	NO_STR
561 	MATCH_STR
562 	"Match tag of route\n"
563 	"Tag value\n")
564 {
565 	const char *xpath = "./match-condition[condition='tag']";
566 
567 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
568 
569 	return nb_cli_apply_changes(vty, NULL);
570 }
571 
route_map_condition_show(struct vty * vty,struct lyd_node * dnode,bool show_defaults)572 void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
573 			      bool show_defaults)
574 {
575 	int condition = yang_dnode_get_enum(dnode, "./condition");
576 
577 	switch (condition) {
578 	case 0: /* interface */
579 		vty_out(vty, " match interface %s\n",
580 			yang_dnode_get_string(dnode, "./interface"));
581 		break;
582 	case 1: /* ipv4-address-list */
583 	case 3: /* ipv4-next-hop-list */
584 		switch (condition) {
585 		case 1:
586 			vty_out(vty, " match ip address %s\n",
587 				yang_dnode_get_string(dnode, "./list-name"));
588 			break;
589 		case 3:
590 			vty_out(vty, " match ip next-hop %s\n",
591 				yang_dnode_get_string(dnode, "./list-name"));
592 			break;
593 		}
594 		break;
595 	case 2: /* ipv4-prefix-list */
596 		vty_out(vty, " match ip address prefix-list %s\n",
597 			yang_dnode_get_string(dnode, "./list-name"));
598 		break;
599 	case 4: /* ipv4-next-hop-prefix-list */
600 		vty_out(vty, " match ip next-hop prefix-list %s\n",
601 			yang_dnode_get_string(dnode, "./list-name"));
602 		break;
603 	case 5: /* ipv4-next-hop-type */
604 		vty_out(vty, " match ip next-hop type %s\n",
605 			yang_dnode_get_string(dnode, "./ipv4-next-hop-type"));
606 		break;
607 	case 6: /* ipv6-address-list */
608 		vty_out(vty, " match ipv6 address %s\n",
609 			yang_dnode_get_string(dnode, "./list-name"));
610 		break;
611 	case 7: /* ipv6-prefix-list */
612 		vty_out(vty, " match ipv6 address prefix-list %s\n",
613 			yang_dnode_get_string(dnode, "./list-name"));
614 		break;
615 	case 8: /* ipv6-next-hop-type */
616 		vty_out(vty, " match ipv6 next-hop type %s\n",
617 			yang_dnode_get_string(dnode, "./ipv6-next-hop-type"));
618 		break;
619 	case 9: /* metric */
620 		vty_out(vty, " match metric %s\n",
621 			yang_dnode_get_string(dnode, "./metric"));
622 		break;
623 	case 10: /* tag */
624 		vty_out(vty, " match tag %s\n",
625 			yang_dnode_get_string(dnode, "./tag"));
626 		break;
627 	case 100: /* ipv4-prefix-length */
628 		vty_out(vty, " match ip address prefix-len %s\n",
629 			yang_dnode_get_string(dnode,"./frr-zebra:ipv4-prefix-length"));
630 		break;
631 	case 101: /* ipv6-prefix-length */
632 		vty_out(vty, " match ipv6 address prefix-len %s\n",
633 			yang_dnode_get_string(dnode, "./frr-zebra:ipv6-prefix-length"));
634 		break;
635 	case 102: /* ipv4-next-hop-prefix-length */
636 		vty_out(vty, " match ip next-hop prefix-len %s\n",
637 			yang_dnode_get_string(dnode, "./frr-zebra:ipv4-prefix-length"));
638 		break;
639 	case 103: /* source-protocol */
640 		vty_out(vty, " match source-protocol %s\n",
641 			yang_dnode_get_string(dnode, "./frr-zebra:source-protocol"));
642 		break;
643 	case 104: /* source-instance */
644 		vty_out(vty, " match source-instance %s\n",
645 			yang_dnode_get_string(dnode, "./frr-zebra:source-instance"));
646 		break;
647 	}
648 }
649 
650 DEFPY_YANG(
651 	set_ip_nexthop, set_ip_nexthop_cmd,
652 	"set ip next-hop A.B.C.D$addr",
653 	SET_STR
654 	IP_STR
655 	"Next hop address\n"
656 	"IP address of next hop\n")
657 {
658 	const char *xpath = "./set-action[action='ipv4-next-hop']";
659 	char xpath_value[XPATH_MAXLEN];
660 
661 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
662 	snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-address", xpath);
663 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str);
664 
665 	return nb_cli_apply_changes(vty, NULL);
666 }
667 
668 DEFPY_YANG(
669 	no_set_ip_nexthop, no_set_ip_nexthop_cmd,
670 	"no set ip next-hop [A.B.C.D]",
671 	NO_STR
672 	SET_STR
673 	IP_STR
674 	"Next hop address\n"
675 	"IP address of next hop\n")
676 {
677 	const char *xpath = "./set-action[action='ipv4-next-hop']";
678 
679 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
680 
681 	return nb_cli_apply_changes(vty, NULL);
682 }
683 
684 DEFPY_YANG(
685 	set_ipv6_nexthop_local, set_ipv6_nexthop_local_cmd,
686 	"set ipv6 next-hop local X:X::X:X$addr",
687 	SET_STR
688 	IPV6_STR
689 	"IPv6 next-hop address\n"
690 	"IPv6 local address\n"
691 	"IPv6 address of next hop\n")
692 {
693 	const char *xpath = "./set-action[action='ipv6-next-hop']";
694 	char xpath_value[XPATH_MAXLEN];
695 
696 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
697 	snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-address", xpath);
698 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str);
699 
700 	return nb_cli_apply_changes(vty, NULL);
701 }
702 
703 DEFPY_YANG(
704 	no_set_ipv6_nexthop_local, no_set_ipv6_nexthop_local_cmd,
705 	"no set ipv6 next-hop local [X:X::X:X]",
706 	NO_STR
707 	SET_STR
708 	IPV6_STR
709 	"IPv6 next-hop address\n"
710 	"IPv6 local address\n"
711 	"IPv6 address of next hop\n")
712 {
713 	const char *xpath = "./set-action[action='ipv6-next-hop']";
714 
715 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
716 
717 	return nb_cli_apply_changes(vty, NULL);
718 }
719 
720 DEFPY_YANG(
721 	set_metric, set_metric_cmd,
722 	"set metric <(-4294967295-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt>",
723 	SET_STR
724 	"Metric value for destination routing protocol\n"
725 	"Metric value (use +/- for additions or subtractions)\n"
726 	"Assign round trip time\n"
727 	"Add round trip time\n"
728 	"Subtract round trip time\n")
729 {
730 	const char *xpath = "./set-action[action='metric']";
731 	char xpath_value[XPATH_MAXLEN];
732 	char value[64];
733 
734 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
735 	if (rtt) {
736 		snprintf(xpath_value, sizeof(xpath_value),
737 			 "%s/use-round-trip-time", xpath);
738 		snprintf(value, sizeof(value), "true");
739 	} else if (artt) {
740 		snprintf(xpath_value, sizeof(xpath_value),
741 			 "%s/add-round-trip-time", xpath);
742 		snprintf(value, sizeof(value), "true");
743 	} else if (srtt) {
744 		snprintf(xpath_value, sizeof(xpath_value),
745 			 "%s/subtract-round-trip-time", xpath);
746 		snprintf(value, sizeof(value), "true");
747 	} else if (metric_str && metric_str[0] == '+') {
748 		snprintf(xpath_value, sizeof(xpath_value), "%s/add-metric",
749 			 xpath);
750 		snprintf(value, sizeof(value), "%s", ++metric_str);
751 	} else if (metric_str && metric_str[0] == '-') {
752 		snprintf(xpath_value, sizeof(xpath_value), "%s/subtract-metric",
753 			 xpath);
754 		snprintf(value, sizeof(value), "%s", ++metric_str);
755 	} else {
756 		snprintf(xpath_value, sizeof(xpath_value), "%s/value", xpath);
757 		snprintf(value, sizeof(value), "%s", metric_str);
758 	}
759 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value);
760 
761 	return nb_cli_apply_changes(vty, NULL);
762 }
763 
764 DEFPY_YANG(
765 	no_set_metric, no_set_metric_cmd,
766 	"no set metric [OPTVAL]",
767 	NO_STR
768 	SET_STR
769 	"Metric value for destination routing protocol\n"
770 	"Metric value\n")
771 {
772 	const char *xpath = "./set-action[action='metric']";
773 
774 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
775 	return nb_cli_apply_changes(vty, NULL);
776 }
777 
778 DEFPY_YANG(
779 	set_tag, set_tag_cmd,
780 	"set tag (1-4294967295)$tag",
781 	SET_STR
782 	"Tag value for routing protocol\n"
783 	"Tag value\n")
784 {
785 	const char *xpath = "./set-action[action='tag']";
786 	char xpath_value[XPATH_MAXLEN];
787 
788 	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
789 	snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath);
790 	nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str);
791 
792 	return nb_cli_apply_changes(vty, NULL);
793 }
794 
795 DEFPY_YANG(
796 	no_set_tag, no_set_tag_cmd,
797 	"no set tag [(1-4294967295)]",
798 	NO_STR
799 	SET_STR
800 	"Tag value for routing protocol\n"
801 	"Tag value\n")
802 {
803 	const char *xpath = "./set-action[action='tag']";
804 
805 	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
806 
807 	return nb_cli_apply_changes(vty, NULL);
808 }
809 
route_map_action_show(struct vty * vty,struct lyd_node * dnode,bool show_defaults)810 void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
811 			   bool show_defaults)
812 {
813 	int action = yang_dnode_get_enum(dnode, "./action");
814 
815 	switch (action) {
816 	case 0: /* ipv4-next-hop */
817 		vty_out(vty, " set ip next-hop %s\n",
818 			yang_dnode_get_string(dnode, "./ipv4-address"));
819 		break;
820 	case 1: /* ipv6-next-hop */
821 		vty_out(vty, " set ipv6 next-hop local %s\n",
822 			yang_dnode_get_string(dnode, "./ipv6-address"));
823 		break;
824 	case 2: /* metric */
825 		if (yang_dnode_get(dnode, "./use-round-trip-time")) {
826 			vty_out(vty, " set metric rtt\n");
827 		} else if (yang_dnode_get(dnode, "./add-round-trip-time")) {
828 			vty_out(vty, " set metric +rtt\n");
829 		} else if (yang_dnode_get(dnode, "./subtract-round-trip-time")) {
830 			vty_out(vty, " set metric -rtt\n");
831 		} else if (yang_dnode_get(dnode, "./add-metric")) {
832 			vty_out(vty, " set metric +%s\n",
833 				yang_dnode_get_string(dnode, "./add-metric"));
834 		} else if (yang_dnode_get(dnode, "./subtract-metric")) {
835 			vty_out(vty, " set metric -%s\n",
836 				yang_dnode_get_string(dnode,
837 						      "./subtract-metric"));
838 		} else {
839 			vty_out(vty, " set metric %s\n",
840 				yang_dnode_get_string(dnode, "./value"));
841 		}
842 		break;
843 	case 3: /* tag */
844 		vty_out(vty, " set tag %s\n",
845 			yang_dnode_get_string(dnode, "./tag"));
846 		break;
847 	case 100: /* source */
848 		if (yang_dnode_exists(dnode, "./frr-zebra:source-v4"))
849 			vty_out(vty, " set src %s\n",
850 				yang_dnode_get_string(dnode, "./frr-zebra:source-v4"));
851 		else
852 			vty_out(vty, " set src %s\n",
853 				yang_dnode_get_string(dnode, "./frr-zebra:source-v6"));
854 		break;
855 	}
856 }
857 
858 DEFPY_YANG(
859 	rmap_onmatch_next, rmap_onmatch_next_cmd,
860 	"on-match next",
861 	"Exit policy on matches\n"
862 	"Next clause\n")
863 {
864 	nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "next");
865 
866 	return nb_cli_apply_changes(vty, NULL);
867 }
868 
869 DEFPY_YANG(
870 	no_rmap_onmatch_next,
871 	no_rmap_onmatch_next_cmd,
872 	"no on-match next",
873 	NO_STR
874 	"Exit policy on matches\n"
875 	"Next clause\n")
876 {
877 	nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL);
878 
879 	return nb_cli_apply_changes(vty, NULL);
880 }
881 
882 DEFPY_YANG(
883 	rmap_onmatch_goto, rmap_onmatch_goto_cmd,
884 	"on-match goto (1-65535)$rm_num",
885 	"Exit policy on matches\n"
886 	"Goto Clause number\n"
887 	"Number\n")
888 {
889 	nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "goto");
890 	nb_cli_enqueue_change(vty, "./goto-value", NB_OP_MODIFY, rm_num_str);
891 
892 	return nb_cli_apply_changes(vty, NULL);
893 }
894 
895 DEFPY_YANG(
896 	no_rmap_onmatch_goto, no_rmap_onmatch_goto_cmd,
897 	"no on-match goto",
898 	NO_STR
899 	"Exit policy on matches\n"
900 	"Goto Clause number\n")
901 {
902 	nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL);
903 
904 	return nb_cli_apply_changes(vty, NULL);
905 }
906 
907 /* Cisco/GNU Zebra compatibility aliases */
908 ALIAS_YANG(
909 	rmap_onmatch_goto, rmap_continue_cmd,
910 	"continue (1-65535)$rm_num",
911 	"Continue on a different entry within the route-map\n"
912 	"Route-map entry sequence number\n")
913 
914 ALIAS_YANG(
915 	no_rmap_onmatch_goto, no_rmap_continue_cmd,
916 	"no continue [(1-65535)]",
917 	NO_STR
918 	"Continue on a different entry within the route-map\n"
919 	"Route-map entry sequence number\n")
920 
route_map_exit_policy_show(struct vty * vty,struct lyd_node * dnode,bool show_defaults)921 void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode,
922 				bool show_defaults)
923 {
924 	int exit_policy = yang_dnode_get_enum(dnode, NULL);
925 
926 	switch (exit_policy) {
927 	case 0: /* permit-or-deny */
928 		/* NOTHING: default option. */
929 		break;
930 	case 1: /* next */
931 		vty_out(vty, " on-match next\n");
932 		break;
933 	case 2: /* goto */
934 		vty_out(vty, " on-match goto %s\n",
935 			yang_dnode_get_string(dnode, "../goto-value"));
936 		break;
937 	}
938 }
939 
940 DEFPY_YANG(
941 	rmap_call, rmap_call_cmd,
942 	"call WORD$name",
943 	"Jump to another Route-Map after match+set\n"
944 	"Target route-map name\n")
945 {
946 	nb_cli_enqueue_change(vty, "./call", NB_OP_MODIFY, name);
947 
948 	return nb_cli_apply_changes(vty, NULL);
949 }
950 
951 DEFPY_YANG(
952 	no_rmap_call, no_rmap_call_cmd,
953 	"no call [NAME]",
954 	NO_STR
955 	"Jump to another Route-Map after match+set\n"
956 	"Target route-map name\n")
957 {
958 	nb_cli_enqueue_change(vty, "./call", NB_OP_DESTROY, NULL);
959 
960 	return nb_cli_apply_changes(vty, NULL);
961 }
962 
route_map_call_show(struct vty * vty,struct lyd_node * dnode,bool show_defaults)963 void route_map_call_show(struct vty *vty, struct lyd_node *dnode,
964 			 bool show_defaults)
965 {
966 	vty_out(vty, " call %s\n", yang_dnode_get_string(dnode, NULL));
967 }
968 
969 DEFPY_YANG(
970 	rmap_description, rmap_description_cmd,
971 	"description LINE...",
972 	"Route-map comment\n"
973 	"Comment describing this route-map rule\n")
974 {
975 	char *desc;
976 	int rv;
977 
978 	desc = argv_concat(argv, argc, 1);
979 	nb_cli_enqueue_change(vty, "./description", NB_OP_MODIFY, desc);
980 	rv = nb_cli_apply_changes(vty, NULL);
981 	XFREE(MTYPE_TMP, desc);
982 
983 	return rv;
984 }
985 
986 DEFUN_YANG (no_rmap_description,
987        no_rmap_description_cmd,
988        "no description",
989        NO_STR
990        "Route-map comment\n")
991 {
992 	nb_cli_enqueue_change(vty, "./description", NB_OP_DESTROY, NULL);
993 
994 	return nb_cli_apply_changes(vty, NULL);
995 }
996 
route_map_description_show(struct vty * vty,struct lyd_node * dnode,bool show_defaults)997 void route_map_description_show(struct vty *vty, struct lyd_node *dnode,
998 				bool show_defaults)
999 {
1000 	vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL));
1001 }
1002 
route_map_config_write(struct vty * vty)1003 static int route_map_config_write(struct vty *vty)
1004 {
1005 	struct lyd_node *dnode;
1006 	int written = 0;
1007 
1008 	dnode = yang_dnode_get(running_config->dnode,
1009 			       "/frr-route-map:lib");
1010 	if (dnode) {
1011 		nb_cli_show_dnode_cmds(vty, dnode, false);
1012 		written = 1;
1013 	}
1014 
1015 	return written;
1016 }
1017 
1018 /* Route map node structure. */
1019 static int route_map_config_write(struct vty *vty);
1020 static struct cmd_node rmap_node = {
1021 	.name = "routemap",
1022 	.node = RMAP_NODE,
1023 	.parent_node = CONFIG_NODE,
1024 	.prompt = "%s(config-route-map)# ",
1025 	.config_write = route_map_config_write,
1026 };
1027 
rmap_autocomplete(vector comps,struct cmd_token * token)1028 static void rmap_autocomplete(vector comps, struct cmd_token *token)
1029 {
1030 	struct route_map *map;
1031 
1032 	for (map = route_map_master.head; map; map = map->next)
1033 		vector_set(comps, XSTRDUP(MTYPE_COMPLETION, map->name));
1034 }
1035 
1036 static const struct cmd_variable_handler rmap_var_handlers[] = {
1037 	{.varname = "route_map", .completions = rmap_autocomplete},
1038 	{.tokenname = "ROUTEMAP_NAME", .completions = rmap_autocomplete},
1039 	{.tokenname = "RMAP_NAME", .completions = rmap_autocomplete},
1040 	{.completions = NULL}
1041 };
1042 
route_map_cli_init(void)1043 void route_map_cli_init(void)
1044 {
1045 	/* Auto complete handler. */
1046 	cmd_variable_handler_register(rmap_var_handlers);
1047 
1048 	/* CLI commands. */
1049 	install_node(&rmap_node);
1050 	install_default(RMAP_NODE);
1051 	install_element(CONFIG_NODE, &route_map_cmd);
1052 	install_element(CONFIG_NODE, &no_route_map_cmd);
1053 	install_element(CONFIG_NODE, &no_route_map_all_cmd);
1054 
1055 	/* Install the on-match stuff */
1056 	install_element(RMAP_NODE, &rmap_onmatch_next_cmd);
1057 	install_element(RMAP_NODE, &no_rmap_onmatch_next_cmd);
1058 	install_element(RMAP_NODE, &rmap_onmatch_goto_cmd);
1059 	install_element(RMAP_NODE, &no_rmap_onmatch_goto_cmd);
1060 	install_element(RMAP_NODE, &rmap_continue_cmd);
1061 	install_element(RMAP_NODE, &no_rmap_continue_cmd);
1062 
1063 	/* Install the call stuff. */
1064 	install_element(RMAP_NODE, &rmap_call_cmd);
1065 	install_element(RMAP_NODE, &no_rmap_call_cmd);
1066 
1067 	/* Install description commands. */
1068 	install_element(RMAP_NODE, &rmap_description_cmd);
1069 	install_element(RMAP_NODE, &no_rmap_description_cmd);
1070 
1071 	/* Install 'match' commands. */
1072 	install_element(RMAP_NODE, &match_interface_cmd);
1073 	install_element(RMAP_NODE, &no_match_interface_cmd);
1074 
1075 	install_element(RMAP_NODE, &match_ip_address_cmd);
1076 	install_element(RMAP_NODE, &no_match_ip_address_cmd);
1077 
1078 	install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd);
1079 	install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd);
1080 
1081 	install_element(RMAP_NODE, &match_ip_next_hop_cmd);
1082 	install_element(RMAP_NODE, &no_match_ip_next_hop_cmd);
1083 
1084 	install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd);
1085 	install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd);
1086 
1087 	install_element(RMAP_NODE, &match_ip_next_hop_type_cmd);
1088 	install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd);
1089 
1090 	install_element(RMAP_NODE, &match_ipv6_address_cmd);
1091 	install_element(RMAP_NODE, &no_match_ipv6_address_cmd);
1092 
1093 	install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd);
1094 	install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd);
1095 
1096 	install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd);
1097 	install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd);
1098 
1099 	install_element(RMAP_NODE, &match_metric_cmd);
1100 	install_element(RMAP_NODE, &no_match_metric_cmd);
1101 
1102 	install_element(RMAP_NODE, &match_tag_cmd);
1103 	install_element(RMAP_NODE, &no_match_tag_cmd);
1104 
1105 	/* Install 'set' commands. */
1106 	install_element(RMAP_NODE, &set_ip_nexthop_cmd);
1107 	install_element(RMAP_NODE, &no_set_ip_nexthop_cmd);
1108 
1109 	install_element(RMAP_NODE, &set_ipv6_nexthop_local_cmd);
1110 	install_element(RMAP_NODE, &no_set_ipv6_nexthop_local_cmd);
1111 
1112 	install_element(RMAP_NODE, &set_metric_cmd);
1113 	install_element(RMAP_NODE, &no_set_metric_cmd);
1114 
1115 	install_element(RMAP_NODE, &set_tag_cmd);
1116 	install_element(RMAP_NODE, &no_set_tag_cmd);
1117 }
1118