1 /* Configuration generator.
2 * Copyright (C) 2000 Kunihiro Ishiguro
4 * This file is part of GNU Zebra.
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include "vtysh/vtysh.h"
29 #include "vtysh/vtysh_user.h"
31 DEFINE_MGROUP(MVTYSH
, "vtysh");
32 DEFINE_MTYPE_STATIC(MVTYSH
, VTYSH_CONFIG
, "Vtysh configuration");
33 DEFINE_MTYPE_STATIC(MVTYSH
, VTYSH_CONFIG_LINE
, "Vtysh configuration line");
37 PREDECL_LIST(config_master
);
38 PREDECL_HASH(config_master_hash
);
41 /* Configuration node name. */
44 /* Configuration string line. */
47 /* Configuration can be nested. */
48 struct config
*parent
;
54 /* Index of this config. */
57 /* Node entry for the typed Red-black tree */
58 struct config_master_item rbt_item
;
59 struct config_master_hash_item hash_item
;
62 struct list
*config_top
;
64 static int line_cmp(char *c1
, char *c2
)
66 return strcmp(c1
, c2
);
69 static void line_del(char *line
)
71 XFREE(MTYPE_VTYSH_CONFIG_LINE
, line
);
74 static struct config
*config_new(void)
76 struct config
*config
;
77 config
= XCALLOC(MTYPE_VTYSH_CONFIG
, sizeof(struct config
));
81 static void config_del(struct config
*config
)
83 vector_free(config
->nested
);
84 list_delete(&config
->line
);
86 XFREE(MTYPE_VTYSH_CONFIG_LINE
, config
->exit
);
87 XFREE(MTYPE_VTYSH_CONFIG_LINE
, config
->name
);
88 XFREE(MTYPE_VTYSH_CONFIG
, config
);
91 static int config_cmp(const struct config
*c1
, const struct config
*c2
)
93 return strcmp(c1
->name
, c2
->name
);
96 static uint32_t config_hash(const struct config
*c
)
98 return string_hash_make(c
->name
);
101 DECLARE_LIST(config_master
, struct config
, rbt_item
);
102 DECLARE_HASH(config_master_hash
, struct config
, hash_item
, config_cmp
,
106 * The config_master_head is a list for order of receipt
107 * The hash is for quick lookup under this NODE
109 struct configuration
{
110 struct config_master_head master
;
111 struct config_master_hash_head hash_master
;
114 static struct config
*config_get_vec(vector vec
, int index
, const char *line
)
116 struct config
*config
, *config_loop
;
117 struct configuration
*configuration
;
118 struct config lookup
;
120 config
= config_loop
= NULL
;
122 configuration
= vector_lookup_ensure(vec
, index
);
124 if (!configuration
) {
125 configuration
= XMALLOC(MTYPE_VTYSH_CONFIG
,
126 sizeof(struct configuration
));
127 config_master_init(&configuration
->master
);
128 config_master_hash_init(&configuration
->hash_master
);
129 vector_set_index(vec
, index
, configuration
);
132 lookup
.name
= (char *)line
;
133 config
= config_master_hash_find(&configuration
->hash_master
, &lookup
);
136 config
= config_new();
137 config
->line
= list_new();
138 config
->line
->del
= (void (*)(void *))line_del
;
139 config
->line
->cmp
= (int (*)(void *, void *))line_cmp
;
140 config
->name
= XSTRDUP(MTYPE_VTYSH_CONFIG_LINE
, line
);
142 config
->index
= index
;
143 config
->nested
= vector_init(1);
144 config_master_add_tail(&configuration
->master
, config
);
145 config_master_hash_add(&configuration
->hash_master
, config
);
150 static struct config
*config_get(int index
, const char *line
)
152 return config_get_vec(configvec
, index
, line
);
155 static struct config
*config_get_nested(struct config
*parent
, int index
,
158 struct config
*config
;
160 config
= config_get_vec(parent
->nested
, index
, line
);
161 config
->parent
= parent
;
166 void config_add_line(struct list
*config
, const char *line
)
168 listnode_add(config
, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE
, line
));
171 static void config_add_line_uniq(struct list
*config
, const char *line
)
173 struct listnode
*node
, *nnode
;
176 for (ALL_LIST_ELEMENTS(config
, node
, nnode
, pnt
)) {
177 if (strcmp(pnt
, line
) == 0)
180 listnode_add_sort(config
, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE
, line
));
184 * Add a line that should only be shown once, and always show at the end of the
187 * If the line already exists, it will be moved to the end of the block. If it
188 * does not exist, it will be added at the end of the block.
190 * Note that this only makes sense when there is just one such line that should
191 * show up at the very end of a config block. Furthermore, if the same block
192 * can show up from multiple daemons, all of them must make sure to print the
193 * line at the end of their config, otherwise the line will show at the end of
194 * the config for the last daemon that printed it.
196 * Here is a motivating example with the 'exit-vrf' command. Suppose we receive
197 * a config from Zebra like so:
204 * Then suppose we later receive this config from PIM:
207 * ip msdp mesh-group MyGroup member 1.2.3.4
210 * Then we will combine them into one config block like so:
215 * ip msdp mesh-group MyGroup member 1.2.3.4
218 * Because PIM also sent us an 'exit-vrf', we noticed that we already had one
219 * under the 'vrf BLUE' config block and so we moved it to the end of the
220 * config block again. If PIM had neglected to send us 'exit-vrf', the result
227 * ip msdp mesh-group MyGroup member 1.2.3.4
229 * Therefore, daemons that share config blocks must take care to consistently
230 * print the same block terminators.
232 * Ideally this would be solved by adding a string to struct config that is
233 * always printed at the end when dumping a config. However, this would only
234 * work when the user is using integrated config. In the non-integrated config
235 * case, daemons are responsible for writing their own config files, and so the
236 * must be able to print these blocks correctly independently of vtysh, which
237 * means they are the ones that need to handle printing the block terminators
238 * and VTYSH needs to be smart enough to combine them properly.
243 * The config to add the line to
246 * The line to add to the end of the config
248 static void config_add_line_uniq_end(struct list
*config
, const char *line
)
250 struct listnode
*node
;
253 for (ALL_LIST_ELEMENTS_RO(config
, node
, pnt
)) {
254 if (strcmp(pnt
, line
) == 0)
259 config_add_line(config
, line
);
261 listnode_move_to_tail(config
, node
);
264 static void config_add_line_head(struct list
*config
, const char *line
)
266 listnode_add_head(config
, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE
, line
));
269 void vtysh_config_parse_line(void *arg
, const char *line
)
272 static struct config
*config
= NULL
;
283 /* Suppress exclamation points ! and commented lines. The !s are
285 * dynamically in vtysh_config_dump() */
290 /* Store line to current configuration. */
292 if (config
->index
== KEYCHAIN_NODE
293 && strncmp(line
, " key", strlen(" key")) == 0) {
294 config
= config_get_nested(
295 config
, KEYCHAIN_KEY_NODE
, line
);
296 } else if (config
->index
== KEYCHAIN_KEY_NODE
) {
297 if (strncmp(line
, " exit", strlen(" exit"))
299 config_add_line_uniq_end(config
->line
,
301 config
= config
->parent
;
303 config_add_line_uniq(config
->line
,
306 } else if (strncmp(line
, " link-params",
307 strlen(" link-params"))
309 config_add_line(config
->line
, line
);
310 config
->index
= LINK_PARAMS_NODE
;
311 } else if (strncmp(line
, " ip multicast boundary",
312 strlen(" ip multicast boundary"))
314 config_add_line_uniq_end(config
->line
, line
);
315 } else if (strncmp(line
, " ip igmp query-interval",
316 strlen(" ip igmp query-interval"))
318 config_add_line_uniq_end(config
->line
, line
);
319 } else if (config
->index
== LINK_PARAMS_NODE
320 && strncmp(line
, " exit-link-params",
323 config_add_line(config
->line
, line
);
324 config
->index
= INTERFACE_NODE
;
325 } else if (!strncmp(line
, " vrrp", strlen(" vrrp"))
326 || !strncmp(line
, " no vrrp",
327 strlen(" no vrrp"))) {
328 config_add_line(config
->line
, line
);
329 } else if (!strncmp(line
, " ip mroute",
330 strlen(" ip mroute"))) {
331 config_add_line_uniq_end(config
->line
, line
);
332 } else if (config
->index
== RMAP_NODE
||
333 config
->index
== INTERFACE_NODE
||
334 config
->index
== VTY_NODE
)
335 config_add_line_uniq(config
->line
, line
);
336 else if (config
->index
== NH_GROUP_NODE
) {
337 if (strncmp(line
, " resilient",
338 strlen(" resilient")) == 0)
339 config_add_line_head(config
->line
,
342 config_add_line_uniq_end(config
->line
,
345 config_add_line(config
->line
, line
);
347 config_add_line(config_top
, line
);
350 if (strncmp(line
, "exit", strlen("exit")) == 0) {
353 XFREE(MTYPE_VTYSH_CONFIG_LINE
,
356 XSTRDUP(MTYPE_VTYSH_CONFIG_LINE
, line
);
358 } else if (strncmp(line
, "interface", strlen("interface")) == 0)
359 config
= config_get(INTERFACE_NODE
, line
);
360 else if (strncmp(line
, "pseudowire", strlen("pseudowire")) == 0)
361 config
= config_get(PW_NODE
, line
);
362 else if (strncmp(line
, "vrf", strlen("vrf")) == 0)
363 config
= config_get(VRF_NODE
, line
);
364 else if (strncmp(line
, "nexthop-group", strlen("nexthop-group"))
366 config
= config_get(NH_GROUP_NODE
, line
);
367 else if (strncmp(line
, "router-id", strlen("router-id")) == 0)
368 config
= config_get(ZEBRA_NODE
, line
);
369 else if (strncmp(line
, "router rip", strlen("router rip")) == 0)
370 config
= config_get(RIP_NODE
, line
);
371 else if (strncmp(line
, "router ripng", strlen("router ripng"))
373 config
= config_get(RIPNG_NODE
, line
);
374 else if (strncmp(line
, "router eigrp", strlen("router eigrp"))
376 config
= config_get(EIGRP_NODE
, line
);
377 else if (strncmp(line
, "router babel", strlen("router babel"))
379 config
= config_get(BABEL_NODE
, line
);
380 else if (strncmp(line
, "router ospf", strlen("router ospf"))
382 config
= config_get(OSPF_NODE
, line
);
383 else if (strncmp(line
, "router ospf6", strlen("router ospf6"))
385 config
= config_get(OSPF6_NODE
, line
);
386 else if (strncmp(line
, "mpls ldp", strlen("mpls ldp")) == 0)
387 config
= config_get(LDP_NODE
, line
);
388 else if (strncmp(line
, "l2vpn", strlen("l2vpn")) == 0)
389 config
= config_get(LDP_L2VPN_NODE
, line
);
390 else if (strncmp(line
, "router bgp", strlen("router bgp")) == 0)
391 config
= config_get(BGP_NODE
, line
);
392 else if (strncmp(line
, "router isis", strlen("router isis"))
394 config
= config_get(ISIS_NODE
, line
);
395 else if (strncmp(line
, "router openfabric", strlen("router openfabric"))
397 config
= config_get(OPENFABRIC_NODE
, line
);
398 else if (strncmp(line
, "route-map", strlen("route-map")) == 0)
399 config
= config_get(RMAP_NODE
, line
);
400 else if (strncmp(line
, "no route-map", strlen("no route-map"))
402 config
= config_get(RMAP_NODE
, line
);
403 else if (strncmp(line
, "pbr-map", strlen("pbr-map")) == 0)
404 config
= config_get(PBRMAP_NODE
, line
);
405 else if (strncmp(line
, "access-list", strlen("access-list"))
407 config
= config_get(ACCESS_NODE
, line
);
408 else if (strncmp(line
, "ipv6 access-list",
409 strlen("ipv6 access-list"))
411 config
= config_get(ACCESS_IPV6_NODE
, line
);
412 else if (strncmp(line
, "mac access-list",
413 strlen("mac access-list"))
415 config
= config_get(ACCESS_MAC_NODE
, line
);
416 else if (strncmp(line
, "ip prefix-list",
417 strlen("ip prefix-list"))
419 config
= config_get(PREFIX_NODE
, line
);
420 else if (strncmp(line
, "ipv6 prefix-list",
421 strlen("ipv6 prefix-list"))
423 config
= config_get(PREFIX_IPV6_NODE
, line
);
424 else if (strncmp(line
, "bgp as-path access-list",
425 strlen("bgp as-path access-list"))
427 config
= config_get(AS_LIST_NODE
, line
);
428 else if (strncmp(line
, "bgp community-list",
429 strlen("bgp community-list"))
431 || strncmp(line
, "bgp extcommunity-list",
432 strlen("bgp extcommunity-list"))
434 || strncmp(line
, "bgp large-community-list",
435 strlen("bgp large-community-list"))
437 config
= config_get(COMMUNITY_LIST_NODE
, line
);
438 else if (strncmp(line
, "bgp community alias",
439 strlen("bgp community alias")) == 0)
440 config
= config_get(COMMUNITY_ALIAS_NODE
, line
);
441 else if (strncmp(line
, "ip route", strlen("ip route")) == 0)
442 config
= config_get(IP_NODE
, line
);
443 else if (strncmp(line
, "ipv6 route", strlen("ipv6 route")) == 0)
444 config
= config_get(IP_NODE
, line
);
445 else if (strncmp(line
, "key", strlen("key")) == 0)
446 config
= config_get(KEYCHAIN_NODE
, line
);
447 else if (strncmp(line
, "line", strlen("line")) == 0)
448 config
= config_get(VTY_NODE
, line
);
449 else if ((strncmp(line
, "ipv6 forwarding",
450 strlen("ipv6 forwarding"))
452 || (strncmp(line
, "ip forwarding",
453 strlen("ip forwarding"))
455 config
= config_get(FORWARDING_NODE
, line
);
456 else if (strncmp(line
, "debug vrf", strlen("debug vrf")) == 0)
457 config
= config_get(VRF_DEBUG_NODE
, line
);
458 else if (strncmp(line
, "debug northbound",
459 strlen("debug northbound"))
461 config
= config_get(NORTHBOUND_DEBUG_NODE
, line
);
462 else if (strncmp(line
, "debug route-map",
463 strlen("debug route-map"))
465 config
= config_get(RMAP_DEBUG_NODE
, line
);
466 else if (strncmp(line
, "debug resolver",
467 strlen("debug resolver")) == 0)
468 config
= config_get(RESOLVER_DEBUG_NODE
, line
);
469 else if (strncmp(line
, "debug", strlen("debug")) == 0)
470 config
= config_get(DEBUG_NODE
, line
);
471 else if (strncmp(line
, "password", strlen("password")) == 0
472 || strncmp(line
, "enable password",
473 strlen("enable password"))
475 config
= config_get(AAA_NODE
, line
);
476 else if (strncmp(line
, "ip protocol", strlen("ip protocol"))
478 config
= config_get(PROTOCOL_NODE
, line
);
479 else if (strncmp(line
, "ipv6 protocol", strlen("ipv6 protocol"))
481 config
= config_get(PROTOCOL_NODE
, line
);
482 else if (strncmp(line
, "ip nht", strlen("ip nht")) == 0)
483 config
= config_get(PROTOCOL_NODE
, line
);
484 else if (strncmp(line
, "ipv6 nht", strlen("ipv6 nht")) == 0)
485 config
= config_get(PROTOCOL_NODE
, line
);
486 else if (strncmp(line
, "mpls", strlen("mpls")) == 0)
487 config
= config_get(MPLS_NODE
, line
);
488 else if (strncmp(line
, "segment-routing",
489 strlen("segment-routing"))
491 config
= config_get(SEGMENT_ROUTING_NODE
, line
);
492 else if (strncmp(line
, "bfd", strlen("bfd")) == 0)
493 config
= config_get(BFD_NODE
, line
);
494 else if (strncmp(line
, "rpki", strlen("rpki")) == 0)
495 config
= config_get(RPKI_NODE
, line
);
497 if (strncmp(line
, "log", strlen("log")) == 0 ||
498 strncmp(line
, "hostname", strlen("hostname")) ==
500 strncmp(line
, "domainname", strlen("domainname")) ==
502 strncmp(line
, "allow-reserved-ranges",
503 strlen("allow-reserved-ranges")) == 0 ||
504 strncmp(line
, "frr", strlen("frr")) == 0 ||
505 strncmp(line
, "agentx", strlen("agentx")) == 0 ||
506 strncmp(line
, "no log", strlen("no log")) == 0 ||
507 strncmp(line
, "no ip prefix-list",
508 strlen("no ip prefix-list")) == 0 ||
509 strncmp(line
, "no ipv6 prefix-list",
510 strlen("no ipv6 prefix-list")) == 0 ||
511 strncmp(line
, "service ", strlen("service ")) ==
513 strncmp(line
, "no service cputime-stats",
514 strlen("no service cputime-stats")) == 0 ||
515 strncmp(line
, "service cputime-warning",
516 strlen("service cputime-warning")) == 0)
517 config_add_line_uniq(config_top
, line
);
519 config_add_line(config_top
, line
);
526 /* Macro to check delimiter is needed between each configuration line
528 #define NO_DELIMITER(I) \
529 ((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
530 || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE \
531 || (I) == COMMUNITY_ALIAS_NODE || (I) == ACCESS_IPV6_NODE \
532 || (I) == ACCESS_MAC_NODE || (I) == PREFIX_IPV6_NODE \
533 || (I) == FORWARDING_NODE || (I) == DEBUG_NODE || (I) == AAA_NODE \
534 || (I) == VRF_DEBUG_NODE || (I) == NORTHBOUND_DEBUG_NODE \
535 || (I) == RMAP_DEBUG_NODE || (I) == RESOLVER_DEBUG_NODE \
536 || (I) == MPLS_NODE || (I) == KEYCHAIN_KEY_NODE)
538 static void configvec_dump(vector vec
, bool nested
)
540 struct listnode
*mnode
, *mnnode
;
541 struct config
*config
;
542 struct configuration
*configuration
;
546 for (i
= 0; i
< vector_active(vec
); i
++)
547 if ((configuration
= vector_slot(vec
, i
)) != NULL
) {
548 while ((config
= config_master_pop(
549 &configuration
->master
))) {
550 config_master_hash_del(
551 &configuration
->hash_master
, config
);
552 /* Don't print empty sections for interface.
554 * other hand could have a legitimate empty
555 * section at the end.
556 * VRF is handled in the backend, we could have
557 * "configured" VRFs with static routes which
558 * are not under the VRF node.
560 if (config
->index
== INTERFACE_NODE
561 && (listcount(config
->line
) == 1)
562 && (line
= listnode_head(config
->line
))
563 && strmatch(line
, "exit")) {
568 vty_out(vty
, "%s\n", config
->name
);
570 for (ALL_LIST_ELEMENTS(config
->line
, mnode
,
572 vty_out(vty
, "%s\n", line
);
574 configvec_dump(config
->nested
, true);
577 vty_out(vty
, "%s\n", config
->exit
);
579 if (!NO_DELIMITER(i
))
584 config_master_fini(&configuration
->master
);
585 config_master_hash_fini(&configuration
->hash_master
);
586 XFREE(MTYPE_VTYSH_CONFIG
, configuration
);
587 vector_slot(vec
, i
) = NULL
;
588 if (!nested
&& NO_DELIMITER(i
))
593 void vtysh_config_dump(void)
595 struct listnode
*node
, *nnode
;
598 for (ALL_LIST_ELEMENTS(config_top
, node
, nnode
, line
))
599 vty_out(vty
, "%s\n", line
);
601 list_delete_all_node(config_top
);
605 configvec_dump(configvec
, false);
608 /* Read up configuration file from file_name. */
609 static int vtysh_read_file(FILE *confp
, bool dry_run
)
615 vty
->wfd
= STDERR_FILENO
;
616 vty
->type
= VTY_TERM
;
617 vty
->node
= CONFIG_NODE
;
619 vtysh_execute_no_pager("enable");
620 vtysh_execute_no_pager("configure terminal");
623 vtysh_execute_no_pager("XFRR_start_configuration");
625 /* Execute configuration file. */
626 ret
= vtysh_config_from_file(vty
, confp
);
629 vtysh_execute_no_pager("XFRR_end_configuration");
631 vtysh_execute_no_pager("end");
632 vtysh_execute_no_pager("disable");
639 /* Read up configuration file from config_default_dir. */
640 int vtysh_read_config(const char *config_default_dir
, bool dry_run
)
646 confp
= fopen(config_default_dir
, "r");
649 "%% Can't open configuration file %s due to '%s'.\n",
650 config_default_dir
, safe_strerror(errno
));
651 return CMD_ERR_NO_FILE
;
654 save
= vtysh_add_timestamp
;
655 vtysh_add_timestamp
= false;
657 ret
= vtysh_read_file(confp
, dry_run
);
660 vtysh_add_timestamp
= save
;
665 /* We don't write vtysh specific into file from vtysh. vtysh.conf should
666 * be edited by hand. So, we handle only "write terminal" case here and
667 * integrate vtysh specific conf with conf from daemons.
669 void vtysh_config_write(void)
674 name
= cmd_hostname_get();
675 if (name
&& name
[0] != '\0') {
676 snprintf(line
, sizeof(line
), "hostname %s", name
);
677 vtysh_config_parse_line(NULL
, line
);
680 name
= cmd_domainname_get();
681 if (name
&& name
[0] != '\0') {
682 snprintf(line
, sizeof(line
), "domainname %s", name
);
683 vtysh_config_parse_line(NULL
, line
);
686 if (vtysh_write_integrated
== WRITE_INTEGRATED_NO
)
687 vtysh_config_parse_line(NULL
,
688 "no service integrated-vtysh-config");
689 if (vtysh_write_integrated
== WRITE_INTEGRATED_YES
)
690 vtysh_config_parse_line(NULL
,
691 "service integrated-vtysh-config");
696 void vtysh_config_init(void)
698 config_top
= list_new();
699 config_top
->del
= (void (*)(void *))line_del
;
700 configvec
= vector_init(1);