]> git.proxmox.com Git - mirror_frr.git/blob - vtysh/vtysh_config.c
Merge pull request #8972 from ton31337/fix/bgp_extended_msg_support
[mirror_frr.git] / vtysh / vtysh_config.c
1 /* Configuration generator.
2 * Copyright (C) 2000 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
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
9 * later version.
10 *
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.
15 *
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
19 */
20
21 #include <zebra.h>
22
23 #include "command.h"
24 #include "linklist.h"
25 #include "memory.h"
26 #include "typesafe.h"
27
28 #include "vtysh/vtysh.h"
29 #include "vtysh/vtysh_user.h"
30
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");
34
35 vector configvec;
36
37 PREDECL_LIST(config_master);
38 PREDECL_HASH(config_master_hash);
39
40 struct config {
41 /* Configuration node name. */
42 char *name;
43
44 /* Configuration string line. */
45 struct list *line;
46
47 /* Configuration can be nest. */
48 struct config *config;
49
50 /* Index of this config. */
51 uint32_t index;
52
53 /* Node entry for the typed Red-black tree */
54 struct config_master_item rbt_item;
55 struct config_master_hash_item hash_item;
56 };
57
58 struct list *config_top;
59
60 static int line_cmp(char *c1, char *c2)
61 {
62 return strcmp(c1, c2);
63 }
64
65 static void line_del(char *line)
66 {
67 XFREE(MTYPE_VTYSH_CONFIG_LINE, line);
68 }
69
70 static struct config *config_new(void)
71 {
72 struct config *config;
73 config = XCALLOC(MTYPE_VTYSH_CONFIG, sizeof(struct config));
74 return config;
75 }
76
77 static void config_del(struct config *config)
78 {
79 list_delete(&config->line);
80 XFREE(MTYPE_VTYSH_CONFIG_LINE, config->name);
81 XFREE(MTYPE_VTYSH_CONFIG, config);
82 }
83
84 static int config_cmp(const struct config *c1, const struct config *c2)
85 {
86 return strcmp(c1->name, c2->name);
87 }
88
89 static uint32_t config_hash(const struct config *c)
90 {
91 return string_hash_make(c->name);
92 }
93
94 DECLARE_LIST(config_master, struct config, rbt_item);
95 DECLARE_HASH(config_master_hash, struct config, hash_item, config_cmp,
96 config_hash);
97
98 /*
99 * The config_master_head is a list for order of receipt
100 * The hash is for quick lookup under this NODE
101 */
102 struct configuration {
103 struct config_master_head master;
104 struct config_master_hash_head hash_master;
105 };
106
107 static struct config *config_get(int index, const char *line)
108 {
109 struct config *config, *config_loop;
110 struct configuration *configuration;
111 struct config lookup;
112
113 config = config_loop = NULL;
114
115 configuration = vector_lookup_ensure(configvec, index);
116
117 if (!configuration) {
118 configuration = XMALLOC(MTYPE_VTYSH_CONFIG,
119 sizeof(struct configuration));
120 config_master_init(&configuration->master);
121 config_master_hash_init(&configuration->hash_master);
122 vector_set_index(configvec, index, configuration);
123 }
124
125 lookup.name = (char *)line;
126 config = config_master_hash_find(&configuration->hash_master, &lookup);
127
128 if (!config) {
129 config = config_new();
130 config->line = list_new();
131 config->line->del = (void (*)(void *))line_del;
132 config->line->cmp = (int (*)(void *, void *))line_cmp;
133 config->name = XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line);
134 config->index = index;
135 config_master_add_tail(&configuration->master, config);
136 config_master_hash_add(&configuration->hash_master, config);
137 }
138 return config;
139 }
140
141 void config_add_line(struct list *config, const char *line)
142 {
143 listnode_add(config, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line));
144 }
145
146 static void config_add_line_uniq(struct list *config, const char *line)
147 {
148 struct listnode *node, *nnode;
149 char *pnt;
150
151 for (ALL_LIST_ELEMENTS(config, node, nnode, pnt)) {
152 if (strcmp(pnt, line) == 0)
153 return;
154 }
155 listnode_add_sort(config, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line));
156 }
157
158 /*
159 * Add a line that should only be shown once, and always show at the end of the
160 * config block.
161 *
162 * If the line already exists, it will be moved to the end of the block. If it
163 * does not exist, it will be added at the end of the block.
164 *
165 * Note that this only makes sense when there is just one such line that should
166 * show up at the very end of a config block. Furthermore, if the same block
167 * can show up from multiple daemons, all of them must make sure to print the
168 * line at the end of their config, otherwise the line will show at the end of
169 * the config for the last daemon that printed it.
170 *
171 * Here is a motivating example with the 'exit-vrf' command. Suppose we receive
172 * a config from Zebra like so:
173 *
174 * vrf BLUE
175 * ip route A
176 * ip route B
177 * exit-vrf
178 *
179 * Then suppose we later receive this config from PIM:
180 *
181 * vrf BLUE
182 * ip msdp mesh-group MyGroup member 1.2.3.4
183 * exit-vrf
184 *
185 * Then we will combine them into one config block like so:
186 *
187 * vrf BLUE
188 * ip route A
189 * ip route B
190 * ip msdp mesh-group MyGroup member 1.2.3.4
191 * exit-vrf
192 *
193 * Because PIM also sent us an 'exit-vrf', we noticed that we already had one
194 * under the 'vrf BLUE' config block and so we moved it to the end of the
195 * config block again. If PIM had neglected to send us 'exit-vrf', the result
196 * would be this:
197 *
198 * vrf BLUE
199 * ip route A
200 * ip route B
201 * exit-vrf
202 * ip msdp mesh-group MyGroup member 1.2.3.4
203 *
204 * Therefore, daemons that share config blocks must take care to consistently
205 * print the same block terminators.
206 *
207 * Ideally this would be solved by adding a string to struct config that is
208 * always printed at the end when dumping a config. However, this would only
209 * work when the user is using integrated config. In the non-integrated config
210 * case, daemons are responsible for writing their own config files, and so the
211 * must be able to print these blocks correctly independently of vtysh, which
212 * means they are the ones that need to handle printing the block terminators
213 * and VTYSH needs to be smart enough to combine them properly.
214 *
215 * ---
216 *
217 * config
218 * The config to add the line to
219 *
220 * line
221 * The line to add to the end of the config
222 */
223 static void config_add_line_uniq_end(struct list *config, const char *line)
224 {
225 struct listnode *node;
226 char *pnt;
227
228 for (ALL_LIST_ELEMENTS_RO(config, node, pnt)) {
229 if (strcmp(pnt, line) == 0)
230 break;
231 }
232
233 if (!node)
234 config_add_line(config, line);
235 else
236 listnode_move_to_tail(config, node);
237 }
238
239 void vtysh_config_parse_line(void *arg, const char *line)
240 {
241 char c;
242 static struct config *config = NULL;
243
244 if (!line)
245 return;
246
247 c = line[0];
248
249 if (c == '\0')
250 return;
251
252 switch (c) {
253 /* Suppress exclamation points ! and commented lines. The !s are
254 * generated
255 * dynamically in vtysh_config_dump() */
256 case '!':
257 case '#':
258 break;
259 case ' ':
260 /* Store line to current configuration. */
261 if (config) {
262 if (strncmp(line, " link-params",
263 strlen(" link-params"))
264 == 0) {
265 config_add_line(config->line, line);
266 config->index = LINK_PARAMS_NODE;
267 } else if (strncmp(line, " ip multicast boundary",
268 strlen(" ip multicast boundary"))
269 == 0) {
270 config_add_line_uniq_end(config->line, line);
271 } else if (strncmp(line, " ip igmp query-interval",
272 strlen(" ip igmp query-interval")) == 0) {
273 config_add_line_uniq_end(config->line, line);
274 } else if (config->index == LINK_PARAMS_NODE
275 && strncmp(line, " exit-link-params",
276 strlen(" exit"))
277 == 0) {
278 config_add_line(config->line, line);
279 config->index = INTERFACE_NODE;
280 } else if (config->index == VRF_NODE
281 && strncmp(line, " exit-vrf",
282 strlen(" exit-vrf"))
283 == 0) {
284 config_add_line_uniq_end(config->line, line);
285 } else if (!strncmp(line, " vrrp", strlen(" vrrp"))
286 || !strncmp(line, " no vrrp",
287 strlen(" no vrrp"))) {
288 config_add_line(config->line, line);
289 } else if (!strncmp(line, " ip mroute", strlen(" ip mroute"))) {
290 config_add_line_uniq_end(config->line, line);
291 } else if (config->index == RMAP_NODE
292 || config->index == INTERFACE_NODE
293 || config->index == VTY_NODE
294 || config->index == VRF_NODE
295 || config->index == NH_GROUP_NODE)
296 config_add_line_uniq(config->line, line);
297 else
298 config_add_line(config->line, line);
299 } else
300 config_add_line(config_top, line);
301 break;
302 default:
303 if (strncmp(line, "interface", strlen("interface")) == 0)
304 config = config_get(INTERFACE_NODE, line);
305 else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0)
306 config = config_get(PW_NODE, line);
307 else if (strncmp(line, "vrf", strlen("vrf")) == 0)
308 config = config_get(VRF_NODE, line);
309 else if (strncmp(line, "nexthop-group", strlen("nexthop-group"))
310 == 0)
311 config = config_get(NH_GROUP_NODE, line);
312 else if (strncmp(line, "router-id", strlen("router-id")) == 0)
313 config = config_get(ZEBRA_NODE, line);
314 else if (strncmp(line, "router rip", strlen("router rip")) == 0)
315 config = config_get(RIP_NODE, line);
316 else if (strncmp(line, "router ripng", strlen("router ripng"))
317 == 0)
318 config = config_get(RIPNG_NODE, line);
319 else if (strncmp(line, "router eigrp", strlen("router eigrp"))
320 == 0)
321 config = config_get(EIGRP_NODE, line);
322 else if (strncmp(line, "router babel", strlen("router babel"))
323 == 0)
324 config = config_get(BABEL_NODE, line);
325 else if (strncmp(line, "router ospf", strlen("router ospf"))
326 == 0)
327 config = config_get(OSPF_NODE, line);
328 else if (strncmp(line, "router ospf6", strlen("router ospf6"))
329 == 0)
330 config = config_get(OSPF6_NODE, line);
331 else if (strncmp(line, "mpls ldp", strlen("mpls ldp")) == 0)
332 config = config_get(LDP_NODE, line);
333 else if (strncmp(line, "l2vpn", strlen("l2vpn")) == 0)
334 config = config_get(LDP_L2VPN_NODE, line);
335 else if (strncmp(line, "router bgp", strlen("router bgp")) == 0)
336 config = config_get(BGP_NODE, line);
337 else if (strncmp(line, "router isis", strlen("router isis"))
338 == 0)
339 config = config_get(ISIS_NODE, line);
340 else if (strncmp(line, "router openfabric", strlen("router openfabric"))
341 == 0)
342 config = config_get(OPENFABRIC_NODE, line);
343 else if (strncmp(line, "route-map", strlen("route-map")) == 0)
344 config = config_get(RMAP_NODE, line);
345 else if (strncmp(line, "no route-map", strlen("no route-map"))
346 == 0)
347 config = config_get(RMAP_NODE, line);
348 else if (strncmp(line, "pbr-map", strlen("pbr-map")) == 0)
349 config = config_get(PBRMAP_NODE, line);
350 else if (strncmp(line, "access-list", strlen("access-list"))
351 == 0)
352 config = config_get(ACCESS_NODE, line);
353 else if (strncmp(line, "ipv6 access-list",
354 strlen("ipv6 access-list"))
355 == 0)
356 config = config_get(ACCESS_IPV6_NODE, line);
357 else if (strncmp(line, "mac access-list",
358 strlen("mac access-list"))
359 == 0)
360 config = config_get(ACCESS_MAC_NODE, line);
361 else if (strncmp(line, "ip prefix-list",
362 strlen("ip prefix-list"))
363 == 0)
364 config = config_get(PREFIX_NODE, line);
365 else if (strncmp(line, "ipv6 prefix-list",
366 strlen("ipv6 prefix-list"))
367 == 0)
368 config = config_get(PREFIX_IPV6_NODE, line);
369 else if (strncmp(line, "bgp as-path access-list",
370 strlen("bgp as-path access-list"))
371 == 0)
372 config = config_get(AS_LIST_NODE, line);
373 else if (strncmp(line, "bgp community-list",
374 strlen("bgp community-list"))
375 == 0
376 || strncmp(line, "bgp extcommunity-list",
377 strlen("bgp extcommunity-list"))
378 == 0
379 || strncmp(line, "bgp large-community-list",
380 strlen("bgp large-community-list"))
381 == 0)
382 config = config_get(COMMUNITY_LIST_NODE, line);
383 else if (strncmp(line, "bgp community alias",
384 strlen("bgp community alias")) == 0)
385 config = config_get(COMMUNITY_ALIAS_NODE, line);
386 else if (strncmp(line, "ip route", strlen("ip route")) == 0)
387 config = config_get(IP_NODE, line);
388 else if (strncmp(line, "ipv6 route", strlen("ipv6 route")) == 0)
389 config = config_get(IP_NODE, line);
390 else if (strncmp(line, "key", strlen("key")) == 0)
391 config = config_get(KEYCHAIN_NODE, line);
392 else if (strncmp(line, "line", strlen("line")) == 0)
393 config = config_get(VTY_NODE, line);
394 else if ((strncmp(line, "ipv6 forwarding",
395 strlen("ipv6 forwarding"))
396 == 0)
397 || (strncmp(line, "ip forwarding",
398 strlen("ip forwarding"))
399 == 0))
400 config = config_get(FORWARDING_NODE, line);
401 else if (strncmp(line, "debug vrf", strlen("debug vrf")) == 0)
402 config = config_get(VRF_DEBUG_NODE, line);
403 else if (strncmp(line, "debug northbound",
404 strlen("debug northbound"))
405 == 0)
406 config = config_get(NORTHBOUND_DEBUG_NODE, line);
407 else if (strncmp(line, "debug route-map",
408 strlen("debug route-map"))
409 == 0)
410 config = config_get(RMAP_DEBUG_NODE, line);
411 else if (strncmp(line, "debug resolver",
412 strlen("debug resolver")) == 0)
413 config = config_get(RESOLVER_DEBUG_NODE, line);
414 else if (strncmp(line, "debug", strlen("debug")) == 0)
415 config = config_get(DEBUG_NODE, line);
416 else if (strncmp(line, "password", strlen("password")) == 0
417 || strncmp(line, "enable password",
418 strlen("enable password"))
419 == 0)
420 config = config_get(AAA_NODE, line);
421 else if (strncmp(line, "ip protocol", strlen("ip protocol"))
422 == 0)
423 config = config_get(PROTOCOL_NODE, line);
424 else if (strncmp(line, "ipv6 protocol", strlen("ipv6 protocol"))
425 == 0)
426 config = config_get(PROTOCOL_NODE, line);
427 else if (strncmp(line, "ip nht", strlen("ip nht")) == 0)
428 config = config_get(PROTOCOL_NODE, line);
429 else if (strncmp(line, "ipv6 nht", strlen("ipv6 nht")) == 0)
430 config = config_get(PROTOCOL_NODE, line);
431 else if (strncmp(line, "mpls", strlen("mpls")) == 0)
432 config = config_get(MPLS_NODE, line);
433 else if (strncmp(line, "segment-routing",
434 strlen("segment-routing"))
435 == 0)
436 config = config_get(SEGMENT_ROUTING_NODE, line);
437 else if (strncmp(line, "bfd", strlen("bfd")) == 0)
438 config = config_get(BFD_NODE, line);
439 else {
440 if (strncmp(line, "log", strlen("log")) == 0
441 || strncmp(line, "hostname", strlen("hostname")) == 0
442 || strncmp(line, "domainname", strlen("domainname")) == 0
443 || strncmp(line, "frr", strlen("frr")) == 0
444 || strncmp(line, "agentx", strlen("agentx")) == 0
445 || strncmp(line, "no log", strlen("no log")) == 0
446 || strncmp(line, "no ip prefix-list", strlen("no ip prefix-list")) == 0
447 || strncmp(line, "no ipv6 prefix-list", strlen("no ipv6 prefix-list")) == 0)
448 config_add_line_uniq(config_top, line);
449 else
450 config_add_line(config_top, line);
451 config = NULL;
452 }
453 break;
454 }
455 }
456
457 /* Macro to check delimiter is needed between each configuration line
458 * or not. */
459 #define NO_DELIMITER(I) \
460 ((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
461 || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE \
462 || (I) == COMMUNITY_ALIAS_NODE || (I) == ACCESS_IPV6_NODE \
463 || (I) == ACCESS_MAC_NODE || (I) == PREFIX_IPV6_NODE \
464 || (I) == FORWARDING_NODE || (I) == DEBUG_NODE || (I) == AAA_NODE \
465 || (I) == VRF_DEBUG_NODE || (I) == NORTHBOUND_DEBUG_NODE \
466 || (I) == RMAP_DEBUG_NODE || (I) == RESOLVER_DEBUG_NODE \
467 || (I) == MPLS_NODE)
468
469 /* Display configuration to file pointer. */
470 void vtysh_config_dump(void)
471 {
472 struct listnode *node, *nnode;
473 struct listnode *mnode, *mnnode;
474 struct config *config;
475 struct configuration *configuration;
476 char *line;
477 unsigned int i;
478
479 for (ALL_LIST_ELEMENTS(config_top, node, nnode, line))
480 vty_out(vty, "%s\n", line);
481
482 vty_out(vty, "!\n");
483
484 for (i = 0; i < vector_active(configvec); i++)
485 if ((configuration = vector_slot(configvec, i)) != NULL) {
486 while ((config = config_master_pop(
487 &configuration->master))) {
488 config_master_hash_del(
489 &configuration->hash_master, config);
490 /* Don't print empty sections for interface.
491 * Route maps on the
492 * other hand could have a legitimate empty
493 * section at the end.
494 * VRF is handled in the backend, we could have
495 * "configured" VRFs with static routes which
496 * are not under the VRF node.
497 */
498 if (config->index == INTERFACE_NODE
499 && list_isempty(config->line)) {
500 config_del(config);
501 continue;
502 }
503
504 vty_out(vty, "%s\n", config->name);
505
506 for (ALL_LIST_ELEMENTS(config->line, mnode,
507 mnnode, line))
508 vty_out(vty, "%s\n", line);
509 if (!NO_DELIMITER(i))
510 vty_out(vty, "!\n");
511
512 config_del(config);
513 }
514 if (NO_DELIMITER(i))
515 vty_out(vty, "!\n");
516 }
517
518 for (i = 0; i < vector_active(configvec); i++)
519 if ((configuration = vector_slot(configvec, i)) != NULL) {
520 config_master_fini(&configuration->master);
521 config_master_hash_fini(&configuration->hash_master);
522 XFREE(MTYPE_VTYSH_CONFIG, configuration);
523 vector_slot(configvec, i) = NULL;
524 }
525 list_delete_all_node(config_top);
526 }
527
528 /* Read up configuration file from file_name. */
529 static int vtysh_read_file(FILE *confp, bool dry_run)
530 {
531 struct vty *vty;
532 int ret;
533
534 vty = vty_new();
535 vty->wfd = STDERR_FILENO;
536 vty->type = VTY_TERM;
537 vty->node = CONFIG_NODE;
538
539 vtysh_execute_no_pager("enable");
540 vtysh_execute_no_pager("configure terminal");
541
542 if (!dry_run)
543 vtysh_execute_no_pager("XFRR_start_configuration");
544
545 /* Execute configuration file. */
546 ret = vtysh_config_from_file(vty, confp);
547
548 if (!dry_run)
549 vtysh_execute_no_pager("XFRR_end_configuration");
550
551 vtysh_execute_no_pager("end");
552 vtysh_execute_no_pager("disable");
553
554 vty_close(vty);
555
556 return (ret);
557 }
558
559 /* Read up configuration file from config_default_dir. */
560 int vtysh_read_config(const char *config_default_dir, bool dry_run)
561 {
562 FILE *confp = NULL;
563 bool save;
564 int ret;
565
566 confp = fopen(config_default_dir, "r");
567 if (confp == NULL) {
568 fprintf(stderr,
569 "%% Can't open configuration file %s due to '%s'.\n",
570 config_default_dir, safe_strerror(errno));
571 return CMD_ERR_NO_FILE;
572 }
573
574 save = vtysh_add_timestamp;
575 vtysh_add_timestamp = false;
576
577 ret = vtysh_read_file(confp, dry_run);
578 fclose(confp);
579
580 vtysh_add_timestamp = save;
581
582 return (ret);
583 }
584
585 /* We don't write vtysh specific into file from vtysh. vtysh.conf should
586 * be edited by hand. So, we handle only "write terminal" case here and
587 * integrate vtysh specific conf with conf from daemons.
588 */
589 void vtysh_config_write(void)
590 {
591 char line[512];
592
593 if (cmd_hostname_get()) {
594 snprintf(line, sizeof(line), "hostname %s", cmd_hostname_get());
595 vtysh_config_parse_line(NULL, line);
596 }
597
598 if (cmd_domainname_get()) {
599 snprintf(line, sizeof(line), "domainname %s",
600 cmd_domainname_get());
601 vtysh_config_parse_line(NULL, line);
602 }
603 if (vtysh_write_integrated == WRITE_INTEGRATED_NO)
604 vtysh_config_parse_line(NULL,
605 "no service integrated-vtysh-config");
606 if (vtysh_write_integrated == WRITE_INTEGRATED_YES)
607 vtysh_config_parse_line(NULL,
608 "service integrated-vtysh-config");
609
610 user_config_write();
611 }
612
613 void vtysh_config_init(void)
614 {
615 config_top = list_new();
616 config_top->del = (void (*)(void *))line_del;
617 configvec = vector_init(1);
618 }