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