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