bird-2.15.1/0000775000175000017500000000000014577240306011444 5ustar feelafeelabird-2.15.1/.dir-locals.el0000664000175000017500000000022414207634355014074 0ustar feelafeela; BIRD project coding conventions ((c-mode (c-file-style . "bsd") (c-basic-offset . 2) (fill-column . 80) (show-trailing-whitespace . t))) bird-2.15.1/.gitignore0000664000175000017500000000026714207634355013442 0ustar feelafeela/autom4te.cache/ /obj/ /pkg/ /Makefile /bird /birdc /birdcl /bird.conf /bird.log /config.log /config.status /configure /sysdep/autoconf.h.in /sysdep/autoconf.h.in~ /cscope.* *.tar.gz bird-2.15.1/README0000664000175000017500000001011514207634355012323 0ustar feelafeela BIRD Internet Routing Daemon Home page http://bird.network.cz/ Mailing list bird-users@network.cz (c) 1998--2008 Martin Mares (c) 1998--2000 Pavel Machek (c) 1998--2008 Ondrej Filip (c) 2009--2019 CZ.NIC z.s.p.o. ================================================================================ The BIRD project aims to develop a dynamic IP routing daemon with full support of all modern routing protocols, easy to use configuration interface and powerful route filtering language, primarily targeted on (but not limited to) Linux and other UNIX-like systems and distributed under the GNU General Public License. What do we support ================== o Both IPv4 and IPv6 o Multiple routing tables o Border Gateway Protocol (BGPv4) o Routing Information Protocol (RIPv2, RIPng) o Open Shortest Path First protocol (OSPFv2, OSPFv3) o Babel Routing Protocol (Babel) o Bidirectional Forwarding Detection (BFD) o IPv6 router advertisements o Static routes o Inter-table protocol o Command-line interface allowing on-line control and inspection of status of the daemon o Soft reconfiguration, no need to use complex online commands to change the configuration, just edit the configuration file and notify BIRD to re-read it and it will smoothly switch itself to the new configuration, not disturbing routing protocols unless they are affected by the configuration changes o Powerful language for route filtering, see doc/bird.conf.example o Linux, FreeBSD, NetBSD and OpenBSD ports How to install BIRD =================== o From standard distribution package of your OS (recommended) o From official binary packages for Debian and Red Hat Linux ftp://bird.network.cz/pub/bird/debian/ ftp://bird.network.cz/pub/bird/redhat/ o From source code of the latest stable release version ftp://bird.network.cz/pub/bird/ o From current development code in Git repository https://gitlab.labs.nic.cz/labs/bird/ See the file INSTALL for information about installation from source code. Documentation ============= Online documentation is available at http://bird.network.cz/?get_doc or as HTML files in the doc directory, you can install it by `make install-docs' and rebuild it by `make docs', but you'll need SGMLtools and LaTeX to be installed on your machine. You can also download a neatly formatted PDF version as a separate archive (bird-doc-*.tar.gz) from ftp://bird.network.cz/pub/bird/ User support ============ If you want to help us debugging, enhancing and porting BIRD or just lurk around to see what's going to develop, feel free to subscribe to the BIRD users mailing list bird-users@network.cz, just send `subscribe' to bird-request@network.cz. Bug reports, suggestions, feature requests and code are welcome! We don't use gitlab issues for reporting, sorry. Subscribe: http://bird.network.cz/mailman/listinfo/bird-users/ Archive: http://bird.network.cz/pipermail/bird-users/ Licence ======= This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA History ======= BIRD development started as a student project at the Faculty of Math and Physics, Charles University, Prague, Czech Republic under supervision of RNDr. Libor Forst . BIRD has been developed and supported by CZ.NIC z.s.p.o. http://www.nic.cz/ since 2009. Good Luck and enjoy the BIRD :) The BIRD Team bird-2.15.1/client/0000775000175000017500000000000014573415512012721 5ustar feelafeelabird-2.15.1/client/Doc0000664000175000017500000000003714207634355013353 0ustar feelafeelaH Client S client.c commands.c bird-2.15.1/client/Makefile0000664000175000017500000000031314207634355014360 0ustar feelafeelasrc := commands.c util.c client.c obj := $(src-o-files) $(all-client) $(conf-y-targets): $(s)cmds.Y $(exedir)/birdc: $(o)birdc.o $(exedir)/birdc: LIBS += $(CLIENT_LIBS) $(exedir)/birdcl: $(o)birdcl.o bird-2.15.1/client/birdcl.c0000664000175000017500000000461514207634355014334 0ustar feelafeela/* * BIRD Client - Light variant I/O * * (c) 1999--2004 Martin Mares * (c) 2013 Tomas Hlavacek * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include #include #include #include #include #include #include "nest/bird.h" #include "lib/resource.h" #include "lib/string.h" #include "client/client.h" #include "sysdep/unix/unix.h" #define INPUT_BUF_LEN 2048 struct termios tty_save; void input_start_list(void) { /* Empty in non-ncurses version. */ } void input_stop_list(void) { /* Empty in non-ncurses version. */ } void input_notify(int prompt) { /* No ncurses -> no status to reveal/hide, print prompt manually. */ if (!prompt) return; printf("bird> "); fflush(stdout); } static int lastnb(char *str, int i) { while (i--) if ((str[i] != ' ') && (str[i] != '\t')) return str[i]; return 0; } void input_read(void) { char buf[INPUT_BUF_LEN]; if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0)) { putchar('\n'); cleanup(); exit(0); } int l = strlen(buf); if ((l+1) == INPUT_BUF_LEN) { printf("Input too long.\n"); return; } if (buf[l-1] == '\n') buf[--l] = '\0'; if (!interactive) printf("%s\n", buf); if (l == 0) return; if (lastnb(buf, l) == '?') { cmd_help(buf, strlen(buf)); return; } submit_command(buf); } static struct termios stored_tty; static int more_active = 0; void more_begin(void) { static struct termios tty; tty = stored_tty; tty.c_lflag &= (~ECHO); tty.c_lflag &= (~ICANON); if (tcsetattr (0, TCSANOW, &tty) < 0) DIE("tcsetattr"); more_active = 1; } void more_end(void) { more_active = 0; if (tcsetattr (0, TCSANOW, &stored_tty) < 0) DIE("tcsetattr"); } static void sig_handler(int signal UNUSED) { cleanup(); exit(0); } void input_init(void) { if (!interactive) return; if (tcgetattr(0, &stored_tty) < 0) DIE("tcgetattr"); if (signal(SIGINT, sig_handler) == SIG_IGN) signal(SIGINT, SIG_IGN); if (signal(SIGTERM, sig_handler) == SIG_IGN) signal(SIGTERM, SIG_IGN); struct winsize tws; if (ioctl(0, TIOCGWINSZ, &tws) == 0) { term_lns = tws.ws_row; term_cls = tws.ws_col; } } void cleanup(void) { if (more_active) more_end(); } bird-2.15.1/client/client.h0000664000175000017500000000136614207634355014360 0ustar feelafeela/* * BIRD Client * * (c) 1999--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ extern int init, busy, interactive; extern int term_lns, term_cls; /* birdc.c / birdcl.c */ void input_start_list(void); void input_stop_list(void); void input_init(void); void input_notify(int prompt); void input_read(void); void more_begin(void); void more_end(void); void cleanup(void); /* commands.c */ void cmd_build_tree(void); void cmd_help(char *cmd, int len); int cmd_complete(char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); /* client.c */ void submit_command(char *cmd_raw); /* die() with system error messages */ #define DIE(x, y...) die(x ": %s", ##y, strerror(errno)) bird-2.15.1/client/cmds.Y0000664000175000017500000000045114207634355014003 0ustar feelafeela# # BIRD -- Internal Commands Of The Client # # (c) 2000 Martin Mares # # Can be freely distributed and used under the terms of the GNU GPL. # CF_CLI(QUIT,,, [[Quit the client]]) CF_CLI(EXIT,,, [[Exit the client]]) CF_CLI(HELP,,, [[Description of the help system]]) bird-2.15.1/client/util.c0000664000175000017500000000174414207634355014052 0ustar feelafeela/* * BIRD Client -- Utility Functions * * (c) 1999--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include #include #include "nest/bird.h" #include "lib/string.h" #include "client/client.h" /* Client versions of logging functions */ static void vlog(const char *msg, va_list args) { char buf[1024]; int n = vsnprintf(buf, sizeof(buf), msg, args); if (n < 0) snprintf(buf, sizeof(buf), "???"); else if (n >= (int) sizeof(buf)) snprintf(buf + sizeof(buf) - 100, 100, " ... "); fputs(buf, stderr); fputc('\n', stderr); } void bug(const char *msg, ...) { va_list args; va_start(args, msg); cleanup(); fputs("Internal error: ", stderr); vlog(msg, args); vfprintf(stderr, msg, args); va_end(args); exit(1); } void die(const char *msg, ...) { va_list args; va_start(args, msg); cleanup(); vlog(msg, args); va_end(args); exit(1); } bird-2.15.1/client/client.c0000644000175000017500000002014614345463176014352 0ustar feelafeela/* * BIRD Client * * (c) 1999--2004 Martin Mares * (c) 2013 Tomas Hlavacek * * Can be freely distributed and used under the terms of the GNU GPL. */ /** * DOC: BIRD client * * There are two variants of BIRD client: regular and light. regular * variant depends on readline and ncurses libraries, while light * variant uses just libc. Most of the code and the main() is common * for both variants (in client.c file) and just a few functions are * different (in birdc.c for regular and birdcl.c for light). Two * binaries are generated by linking common object files like client.o * (which is compiled from client.c just once) with either birdc.o or * birdcl.o for each variant. */ #include #include #include #include #include #include #include #include #include #include "nest/bird.h" #include "lib/resource.h" #include "lib/string.h" #include "client/client.h" #include "sysdep/unix/unix.h" #define SERVER_READ_BUF_LEN 4096 static char *opt_list = "s:vrl"; static int verbose, restricted, once; static char *init_cmd; static char *server_path = PATH_CONTROL_SOCKET; static int server_fd; static byte server_read_buf[SERVER_READ_BUF_LEN]; static byte *server_read_pos = server_read_buf; int init = 1; /* During intial sequence */ int busy = 1; /* Executing BIRD command */ int interactive; /* Whether stdin is terminal */ int last_code; /* Last return code */ static int num_lines, skip_input; int term_lns, term_cls; /*** Parsing of arguments ***/ static void usage(char *name) { fprintf(stderr, "Usage: %s [-s ] [-v] [-r] [-l]\n", name); exit(1); } static void parse_args(int argc, char **argv) { int server_changed = 0; int c; while ((c = getopt(argc, argv, opt_list)) >= 0) switch (c) { case 's': server_path = optarg; server_changed = 1; break; case 'v': verbose++; break; case 'r': restricted = 1; break; case 'l': if (!server_changed) server_path = xbasename(server_path); break; default: usage(argv[0]); } /* If some arguments are not options, we take it as commands */ if (optind < argc) { char *tmp; int i; int len = 0; for (i = optind; i < argc; i++) len += strlen(argv[i]) + 1; tmp = init_cmd = malloc(len); for (i = optind; i < argc; i++) { strcpy(tmp, argv[i]); tmp += strlen(tmp); *tmp++ = ' '; } tmp[-1] = 0; once = 1; interactive = 0; } } /*** Input ***/ static void server_send(char *cmd); static int handle_internal_command(char *cmd) { if (!strncmp(cmd, "exit", 4) || !strncmp(cmd, "quit", 4)) { cleanup(); exit(0); } if (!strncmp(cmd, "help", 4)) { puts("Press `?' for context sensitive help."); return 1; } return 0; } static void submit_server_command(char *cmd) { busy = 1; num_lines = 2; server_send(cmd); } static inline void submit_init_command(char *cmd_raw) { char *cmd = cmd_expand(cmd_raw); if (!cmd) { cleanup(); exit(1); } submit_server_command(cmd); free(cmd); } void submit_command(char *cmd_raw) { char *cmd = cmd_expand(cmd_raw); if (!cmd) return; if (!handle_internal_command(cmd)) submit_server_command(cmd); free(cmd); } static void init_commands(void) { if (restricted) { submit_server_command("restrict"); restricted = 0; return; } if (init_cmd) { /* First transition - client received hello from BIRD and there is waiting initial command */ submit_init_command(init_cmd); init_cmd = NULL; return; } if (once) { /* Initial command is finished and we want to exit */ cleanup(); exit((last_code < 8000) ? 0 : 1); } input_init(); term_lns = (term_lns > 0) ? term_lns : 25; term_cls = (term_cls > 0) ? term_cls : 80; init = 0; } /*** Output ***/ void more(void) { more_begin(); printf("--More--\015"); fflush(stdout); redo: switch (getchar()) { case ' ': num_lines = 2; break; case '\n': case '\r': num_lines--; break; case 'q': skip_input = 1; break; default: goto redo; } printf(" \015"); fflush(stdout); more_end(); } /*** Communication with server ***/ static void server_connect(void) { struct sockaddr_un sa; server_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (server_fd < 0) DIE("Cannot create socket"); if (strlen(server_path) >= sizeof(sa.sun_path)) die("server_connect: path too long"); bzero(&sa, sizeof(sa)); sa.sun_family = AF_UNIX; strcpy(sa.sun_path, server_path); if (connect(server_fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) DIE("Unable to connect to server control socket (%s)", server_path); if (fcntl(server_fd, F_SETFL, O_NONBLOCK) < 0) DIE("fcntl"); } #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) static void server_got_reply(char *x) { int code; int len = 0; if (*x == '+') /* Async reply */ PRINTF(len, ">>> %s\n", x+1); else if (x[0] == ' ') /* Continuation */ PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); else if (strlen(x) > 4 && sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && (x[4] == ' ' || x[4] == '-')) { if (code) PRINTF(len, "%s\n", verbose ? x : x+5); last_code = code; if (x[4] == ' ') { busy = 0; skip_input = 0; return; } } else PRINTF(len, "??? <%s>\n", x); if (interactive && busy && !skip_input && !init && (len > 0)) { num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ if (num_lines >= term_lns) more(); } } static void server_read(void) { int c; byte *start, *p; redo: c = read(server_fd, server_read_pos, server_read_buf + sizeof(server_read_buf) - server_read_pos); if (!c) die("Connection closed by server"); if (c < 0) { if (errno == EINTR) goto redo; else DIE("Server read error"); } start = server_read_buf; p = server_read_pos; server_read_pos += c; while (p < server_read_pos) if (*p++ == '\n') { p[-1] = 0; server_got_reply(start); start = p; } if (start != server_read_buf) { int l = server_read_pos - start; memmove(server_read_buf, start, l); server_read_pos = server_read_buf + l; } else if (server_read_pos == server_read_buf + sizeof(server_read_buf)) { strcpy(server_read_buf, "?"); server_read_pos = server_read_buf + 11; } } static void select_loop(void) { int rv; while (1) { if (init && !busy) init_commands(); if (!init) input_notify(!busy); fd_set select_fds; FD_ZERO(&select_fds); FD_SET(server_fd, &select_fds); if (!busy) FD_SET(0, &select_fds); rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); if (rv < 0) { if (errno == EINTR) continue; else DIE("select"); } if (FD_ISSET(0, &select_fds)) { input_read(); continue; } if (FD_ISSET(server_fd, &select_fds)) { server_read(); continue; } } } static void wait_for_write(int fd) { while (1) { int rv; fd_set set; FD_ZERO(&set); FD_SET(fd, &set); rv = select(fd+1, NULL, &set, NULL, NULL); if (rv < 0) { if (errno == EINTR) continue; else DIE("select"); } if (FD_ISSET(server_fd, &set)) return; } } static void server_send(char *cmd) { int l = strlen(cmd); byte *z = alloca(l + 1); memcpy(z, cmd, l); z[l++] = '\n'; while (l) { int cnt = write(server_fd, z, l); if (cnt < 0) { if (errno == EAGAIN) wait_for_write(server_fd); else if (errno == EINTR) continue; else DIE("Server write error"); } else { l -= cnt; z += cnt; } } } int main(int argc, char **argv) { interactive = isatty(0); parse_args(argc, argv); cmd_build_tree(); server_connect(); select_loop(); return 0; } bird-2.15.1/client/birdc.c0000644000175000017500000001052014573415512014144 0ustar feelafeela/* * BIRD Client - Readline variant I/O * * (c) 1999--2004 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include #include #include #include #include #include #include "nest/bird.h" #include "lib/resource.h" #include "lib/string.h" #include "client/client.h" static int input_hidden_end; static int prompt_active; /*** Input ***/ /* HACK: libreadline internals we need to access */ extern int _rl_vis_botlin; extern void _rl_move_vert(int); #define HISTORY "/.birdc_history" static char *history_file; static void add_history_dedup(char *cmd) { /* Add history line if it differs from the last one */ HIST_ENTRY *he = history_get(history_length); if (!he || strcmp(he->line, cmd)) add_history(cmd); } static void input_got_line(char *cmd_buffer) { if (!cmd_buffer) { cleanup(); exit(0); } if (cmd_buffer[0]) { add_history_dedup(cmd_buffer); submit_command(cmd_buffer); } free(cmd_buffer); } void input_start_list(void) { /* Leave the currently edited line and make space for listing */ _rl_move_vert(_rl_vis_botlin); #ifdef HAVE_RL_CRLF rl_crlf(); #endif } void input_stop_list(void) { /* Reprint the currently edited line after listing */ rl_on_new_line(); rl_redisplay(); } static int input_complete(int arg UNUSED, int key UNUSED) { static int complete_flag; char buf[256]; if (rl_last_func != input_complete) complete_flag = 0; switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) { case 0: complete_flag = 1; break; case 1: rl_insert_text(buf); break; default: complete_flag = 1; #ifdef HAVE_RL_DING rl_ding(); #endif } return 0; } static int input_help(int arg, int key UNUSED) { int i, in_string, in_bracket; if (arg != 1) return rl_insert(arg, '?'); in_string = in_bracket = 0; for (i = 0; i < rl_point; i++) { if (rl_line_buffer[i] == '"') in_string = ! in_string; else if (! in_string) { if (rl_line_buffer[i] == '[') in_bracket++; else if (rl_line_buffer[i] == ']') in_bracket--; } } /* `?' inside string or path -> insert */ if (in_string || in_bracket) return rl_insert(1, '?'); rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ rl_insert_text("?"); rl_redisplay(); rl_end_undo_group(); input_start_list(); cmd_help(rl_line_buffer, rl_point); rl_undo_command(1, 0); /* ? is "internal". Do not submit command in non interactive session */ if (!interactive) rl_replace_line("", 0); input_stop_list(); return 0; } void history_init(void) { const char *homedir = getenv("HOME"); if (!homedir) homedir = "."; history_file = malloc(strlen(homedir) + sizeof(HISTORY)); if (!history_file) die("couldn't alloc enough memory for history file name"); sprintf(history_file, "%s%s", homedir, HISTORY); read_history(history_file); } void input_init(void) { if (interactive) history_init(); rl_readline_name = "birdc"; rl_add_defun("bird-complete", input_complete, '\t'); rl_add_defun("bird-help", input_help, '?'); rl_callback_handler_install("bird> ", input_got_line); // rl_get_screen_size(); term_lns = LINES; term_cls = COLS; prompt_active = 1; // readline library does strange things when stdin is nonblocking. // if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) // DIE("fcntl"); } static void input_reveal(void) { /* need this, otherwise some lib seems to eat pending output when the prompt is displayed */ fflush(stdout); tcdrain(STDOUT_FILENO); rl_end = input_hidden_end; rl_expand_prompt("bird> "); rl_forced_update_display(); prompt_active = 1; } static void input_hide(void) { input_hidden_end = rl_end; rl_end = 0; rl_expand_prompt(""); rl_redisplay(); prompt_active = 0; } void input_notify(int prompt) { if (prompt == prompt_active) return; if (prompt) input_reveal(); else input_hide(); } void input_read(void) { rl_callback_read_char(); } void more_begin(void) { } void more_end(void) { } void cleanup(void) { if (init) return; input_hide(); if (interactive) write_history(history_file); rl_callback_handler_remove(); } bird-2.15.1/client/commands.c0000644000175000017500000001410514573415512014665 0ustar feelafeela/* * BIRD Client -- Command Handling * * (c) 1999--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include #include #include "nest/bird.h" #include "lib/resource.h" #include "lib/string.h" #include "client/client.h" struct cmd_info { char *command; char *args; char *help; int is_real_cmd; int is_option; }; static struct cmd_info command_table[] = { #include "conf/commands.h" }; struct cmd_node { struct cmd_node *sibling, *son, **plastson; struct cmd_info *cmd, *help; int len; u8 final; s8 prio; char token[1]; }; static struct cmd_node cmd_root; #define isspace_(X) isspace((unsigned char) (X)) void cmd_build_tree(void) { uint i; cmd_root.plastson = &cmd_root.son; for(i=0; icommand; new = &cmd_root; while (*c) { char *d = c; while (*c && !isspace_(*c)) c++; old = new; for(new=old->son; new; new=new->sibling) if (new->len == c-d && !memcmp(new->token, d, c-d)) break; if (!new) { int size = sizeof(struct cmd_node) + c-d; new = malloc(size); bzero(new, size); *old->plastson = new; old->plastson = &new->sibling; new->plastson = &new->son; new->len = c-d; memcpy(new->token, d, c-d); new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */ } while (isspace_(*c)) c++; } if (cmd->is_real_cmd) new->cmd = cmd; else new->help = cmd; if (cmd->is_option) old->final = 1; } } static void cmd_do_display_help(struct cmd_info *c) { char buf[strlen(c->command) + strlen(c->args) + 4]; sprintf(buf, "%s %s", c->command, c->args); printf("%-45s %s\n", buf, c->help); } static void cmd_display_help(struct cmd_info *c1, struct cmd_info *c2) { if (c1) cmd_do_display_help(c1); else if (c2) cmd_do_display_help(c2); } static struct cmd_node * cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous) { struct cmd_node *m, *best = NULL, *best2 = NULL; *pambiguous = 0; for(m=root->son; m; m=m->sibling) { if (m->len == len && !memcmp(m->token, cmd, len)) return m; if (m->len > len && !memcmp(m->token, cmd, len)) { if (best && best->prio > m->prio) continue; if (best && best->prio == m->prio) best2 = best; best = m; } } if (best2) { *pambiguous = 1; return NULL; } return best; } static void cmd_list_ambiguous(struct cmd_node *root, char *cmd, int len) { struct cmd_node *m; for(m=root->son; m; m=m->sibling) if (m->len > len && !memcmp(m->token, cmd, len)) cmd_display_help(m->help, m->cmd); } void cmd_help(char *cmd, int len) { char *end = cmd + len; struct cmd_node *n, *m; char *z; int ambig; n = &cmd_root; while (cmd < end && !n->final) { if (isspace_(*cmd)) { cmd++; continue; } z = cmd; while (cmd < end && !isspace_(*cmd)) cmd++; m = cmd_find_abbrev(n, z, cmd-z, &ambig); if (ambig) { cmd_list_ambiguous(n, z, cmd-z); return; } if (!m) break; n = m; } cmd_display_help(n->cmd, NULL); /* Currently no help for options */ if (n->final) return; for (m=n->son; m; m=m->sibling) cmd_display_help(m->help, m->cmd); } static int cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf) { struct cmd_node *m; int best, best_prio, i; *pcount = 0; best = -1; best_prio = -1; for(m=root->son; m; m=m->sibling) { if (m->len < len || memcmp(m->token, cmd, len)) continue; if (best_prio > m->prio) continue; if (best_prio < m->prio) { *pcount = 0; best = -1; } (*pcount)++; if (best < 0) { strcpy(buf, m->token + len); best = m->len - len; best_prio = m->prio; } else { i = 0; while (i < best && i < m->len - len && buf[i] == m->token[len+i]) i++; best = i; } } return best; } int cmd_complete(char *cmd, int len, char *buf, int again) { char *start = cmd; char *end = cmd + len; char *fin; struct cmd_node *n, *m; char *z; int ambig, cnt = 0, common; /* Find the last word we want to complete */ for(fin=end; fin > start && !isspace_(fin[-1]); fin--) ; /* Find the context */ n = &cmd_root; while (cmd < fin && n->son && !n->final) { if (isspace_(*cmd)) { cmd++; continue; } z = cmd; while (cmd < fin && !isspace_(*cmd)) cmd++; m = cmd_find_abbrev(n, z, cmd-z, &ambig); if (ambig) { if (!again) return -1; input_start_list(); cmd_list_ambiguous(n, z, cmd-z); input_stop_list(); return 0; } if (!m) return -1; n = m; } /* Completion of parameters is not yet supported */ if (!n->son) return -1; /* We know the context, let's try to complete */ common = cmd_find_common_match(n, fin, end-fin, &cnt, buf); if (!cnt) return -1; if (cnt == 1) { buf[common++] = ' '; buf[common] = 0; return 1; } if (common > 0) { buf[common] = 0; return 1; } if (!again) return -1; input_start_list(); cmd_list_ambiguous(n, fin, end-fin); input_stop_list(); return 0; } char * cmd_expand(char *cmd) { struct cmd_node *n, *m; char *c, *b, *args; int ambig; args = c = cmd; n = &cmd_root; while (*c && !n->final) { if (isspace_(*c)) { c++; continue; } b = c; while (*c && !isspace_(*c)) c++; m = cmd_find_abbrev(n, b, c-b, &ambig); if (!m) { if (!ambig) break; puts("Ambiguous command, possible expansions are:"); cmd_list_ambiguous(n, b, c-b); return NULL; } args = c; n = m; } if (!n->cmd) { puts("No such command. Press `?' for help."); return NULL; } b = malloc(strlen(n->cmd->command) + strlen(args) + 1); sprintf(b, "%s%s", n->cmd->command, args); return b; } bird-2.15.1/conf/0000775000175000017500000000000014573415512012370 5ustar feelafeelabird-2.15.1/conf/Doc0000664000175000017500000000004414207634355013020 0ustar feelafeelaH Configuration S conf.c S cf-lex.l bird-2.15.1/conf/Makefile0000664000175000017500000000200014207634355014022 0ustar feelafeelasrc := cf-parse.tab.c cf-lex.c conf.c obj := $(src-o-files) $(all-daemon) tests_objs := $(tests_objs) $(src-o-files) ifdef DEBUG BISON_DEBUG=-t #FLEX_DEBUG=-d endif $(o)cf-parse.y: $(s)gen_parser.m4 $(o)keywords.h: $(s)gen_keywords.m4 $(o)commands.h: $(s)gen_commands.m4 $(conf-y-targets): $(s)confbase.Y $(s)flowspec.Y $(M4) $(M4FLAGS) -P $(if $(word 2,$(filter %.m4,$^)),$(error "Too many M4 scripts for one target"),$(filter %.m4,$^)) $(filter %.Y,$^) >$@ $(o)cf-parse.tab.h: $(o)cf-parse.tab.c $(o)cf-parse.tab.c: $(o)cf-parse.y $(BISON) $(BISON_DEBUG) $(BISONFLAGS) -dv -pcf_ -b $(@:.tab.c=) $< $(o)cf-lex.c: $(s)cf-lex.l $(FLEX) $(FLEX_DEBUG) -f -s -B -8 -Pcf_ -o$@ $< $(o)cf-lex.o: CFLAGS+=-Wno-sign-compare -Wno-unused-function prepare: $(o)keywords.h $(o)commands.h $(o)cf-parse.tab.h $(addprefix $(o), cf-parse.y keywords.h commands.h cf-parse.tab.h cf-parse.tab.c cf-lex.c): $(objdir)/.dir-stamp $(call clean,cf-parse.tab.h cf-parse.tab.c cf-parse.y keywords.h commands.h cf-lex.c cf-parse.output) bird-2.15.1/conf/flowspec.Y0000644000175000017500000001231314345463176014350 0ustar feelafeela/* * BIRD -- Flow specification (RFC 5575) grammar * * (c) 2016 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ CF_HDR #define PARSER 1 #include "nest/bird.h" #include "lib/flowspec.h" CF_DEFINES struct flow_builder *this_flow; CF_DECLS %type flow_num_op flow_srcdst flow_logic_op flow_num_type_ flow_frag_val flow_neg %type net_flow4_ net_flow6_ net_flow_ CF_KEYWORDS(FLOW4, FLOW6, DST, SRC, PROTO, NEXT, HEADER, DPORT, SPORT, ICMP, TYPE, CODE, TCP, FLAGS, LENGTH, DSCP, DONT_FRAGMENT, IS_FRAGMENT, FIRST_FRAGMENT, LAST_FRAGMENT, FRAGMENT, LABEL, OFFSET) CF_GRAMMAR /* Network Flow Specification */ flow_num_op: TRUE { $$ = FLOW_OP_TRUE; } | '=' { $$ = FLOW_OP_EQ; } | NEQ { $$ = FLOW_OP_NEQ; } | '<' { $$ = FLOW_OP_LT; } | LEQ { $$ = FLOW_OP_LEQ; } | '>' { $$ = FLOW_OP_GT; } | GEQ { $$ = FLOW_OP_GEQ; } | FALSE { $$ = FLOW_OP_FALSE; } ; flow_logic_op: OR { $$ = FLOW_OP_OR; } | AND { $$ = FLOW_OP_AND; } ; flow_num_type_: PROTO { $$ = FLOW_TYPE_IP_PROTOCOL; } | NEXT HEADER { $$ = FLOW_TYPE_NEXT_HEADER; } | PORT { $$ = FLOW_TYPE_PORT; } | DPORT { $$ = FLOW_TYPE_DST_PORT; } | SPORT { $$ = FLOW_TYPE_SRC_PORT; } | ICMP TYPE { $$ = FLOW_TYPE_ICMP_TYPE; } | ICMP CODE { $$ = FLOW_TYPE_ICMP_CODE; } | LENGTH { $$ = FLOW_TYPE_PACKET_LENGTH; } | DSCP { $$ = FLOW_TYPE_DSCP; } | LABEL { $$ = FLOW_TYPE_LABEL; } ; flow_num_type: flow_num_type_{ flow_builder_set_type(this_flow, $1); }; flow_flag_type: TCP FLAGS { flow_builder_set_type(this_flow, FLOW_TYPE_TCP_FLAGS); }; flow_frag_type: FRAGMENT { flow_builder_set_type(this_flow, FLOW_TYPE_FRAGMENT); }; flow_srcdst: DST { $$ = FLOW_TYPE_DST_PREFIX; } | SRC { $$ = FLOW_TYPE_SRC_PREFIX; } ; flow_num_opts: flow_num_op expr { flow_check_cf_value_length(this_flow, $2); flow_builder_add_op_val(this_flow, $1, $2); } | flow_num_opts flow_logic_op flow_num_op expr { flow_check_cf_value_length(this_flow, $4); flow_builder_add_op_val(this_flow, $2 | $3, $4); } | flow_num_opt_ext | flow_num_opts OR flow_num_opt_ext ; flow_num_opt_ext_expr: expr { flow_check_cf_value_length(this_flow, $1); flow_builder_add_op_val(this_flow, FLOW_OP_EQ, $1); } | expr DDOT expr { flow_check_cf_value_length(this_flow, $1); flow_check_cf_value_length(this_flow, $3); flow_builder_add_op_val(this_flow, FLOW_OP_GEQ, $1); flow_builder_add_op_val(this_flow, FLOW_OP_AND | FLOW_OP_LEQ, $3); } ; flow_num_opt_ext: flow_num_opt_ext_expr | flow_num_opt_ext ',' flow_num_opt_ext_expr ; flow_bmk_opts: flow_neg expr '/' expr { flow_check_cf_bmk_values(this_flow, $1, $2, $4); flow_builder_add_val_mask(this_flow, $1, $2, $4); } | flow_bmk_opts flow_logic_op flow_neg expr '/' expr { flow_check_cf_bmk_values(this_flow, $3, $4, $6); flow_builder_add_val_mask(this_flow, $2 | $3, $4, $6); } | flow_bmk_opts ',' flow_neg expr '/' expr { flow_check_cf_bmk_values(this_flow, $3, $4, $6); flow_builder_add_val_mask(this_flow, 0x40 | $3, $4, $6); /* AND */ } ; flow_neg: /* empty */ { $$ = 0x00; } | '!' { $$ = 0x02; } ; flow_frag_val: DONT_FRAGMENT { $$ = 1; } | IS_FRAGMENT { $$ = 2; } | FIRST_FRAGMENT { $$ = 4; } | LAST_FRAGMENT { $$ = 8; } ; flow_frag_opts: flow_neg flow_frag_val { flow_builder_add_val_mask(this_flow, 0, ($1 ? 0 : $2), $2); } | flow_frag_opts flow_logic_op flow_neg flow_frag_val { flow_builder_add_val_mask(this_flow, $2, ($3 ? 0 : $4), $4); } | flow_frag_opts ',' flow_neg flow_frag_val { flow_builder_add_val_mask(this_flow, 0x40, ($3 ? 0 : $4), $4); /* AND */ } ; flow4_item: flow_srcdst net_ip4 { flow_builder_set_type(this_flow, $1); flow_builder4_add_pfx(this_flow, (net_addr_ip4 *) &($2)); } | flow_num_type flow_num_opts | flow_flag_type flow_bmk_opts | flow_frag_type flow_frag_opts ; flow6_item: flow_srcdst net_ip6 { flow_builder_set_type(this_flow, $1); flow_builder6_add_pfx(this_flow, (net_addr_ip6 *) &($2), 0); } | flow_srcdst net_ip6 OFFSET NUM { if ($4 > $2.pxlen) cf_error("Prefix offset is higher than prefix length"); flow_builder_set_type(this_flow, $1); flow_builder6_add_pfx(this_flow, (net_addr_ip6 *) &($2), $4); } | flow_num_type flow_num_opts | flow_flag_type flow_bmk_opts | flow_frag_type flow_frag_opts ; flow4_opts: /* empty */ | flow4_opts flow4_item ';' ; flow6_opts: /* empty */ | flow6_opts flow6_item ';' ; flow_builder_init: { if (this_flow == NULL) this_flow = flow_builder_init(config_pool); /* FIXME: This should be allocated from tmp in future */ else flow_builder_clear(this_flow); }; flow_builder_set_ipv4: { this_flow->ipv6 = 0; }; flow_builder_set_ipv6: { this_flow->ipv6 = 1; }; net_flow4_: FLOW4 '{' flow_builder_init flow_builder_set_ipv4 flow4_opts '}' { $$ = (net_addr *) flow_builder4_finalize(this_flow, cfg_mem); flow4_validate_cf((net_addr_flow4 *) $$); }; net_flow6_: FLOW6 '{' flow_builder_init flow_builder_set_ipv6 flow6_opts '}' { $$ = (net_addr *) flow_builder6_finalize(this_flow, cfg_mem); flow6_validate_cf((net_addr_flow6 *) $$); }; net_flow_: net_flow4_ | net_flow6_ ; CF_CODE CF_END bird-2.15.1/conf/cf-lex.l0000644000175000017500000005176114510244071013722 0ustar feelafeela/* * BIRD -- Configuration Lexer * * (c) 1998--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ /** * DOC: Lexical analyzer * * The lexical analyzer used for configuration files and CLI commands * is generated using the |flex| tool accompanied by a couple of * functions maintaining the hash tables containing information about * symbols and keywords. * * Each symbol is represented by a &symbol structure containing name * of the symbol, its lexical scope, symbol class (%SYM_PROTO for a * name of a protocol, %SYM_CONSTANT for a constant etc.) and class * dependent data. When an unknown symbol is encountered, it's * automatically added to the symbol table with class %SYM_VOID. * * The keyword tables are generated from the grammar templates * using the |gen_keywords.m4| script. */ %{ #undef REJECT /* Avoid name clashes */ #include #include #include #include #include #include #include #include #include #include #include #define PARSER 1 #include "nest/bird.h" #include "nest/route.h" #include "nest/protocol.h" #include "filter/filter.h" #include "filter/f-inst.h" #include "conf/conf.h" #include "conf/cf-parse.tab.h" #include "lib/string.h" #include "lib/hash.h" #include "conf/keywords.h" /* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */ #ifdef SYM #undef SYM #endif static uint cf_hash(const byte *c); #define SYM_KEY(n) n->name #define SYM_NEXT(n) n->next #define SYM_EQ(a,b) !strcmp(a,b) #define SYM_FN(k) cf_hash(k) #define SYM_ORDER 4 /* Initial */ #define SYM_REHASH sym_rehash #define SYM_PARAMS /8, *1, 2, 2, 4, 20 HASH_DEFINE_REHASH_FN(SYM, struct symbol) struct sym_scope *global_root_scope; pool *global_root_scope_pool; linpool *global_root_scope_linpool; linpool *cfg_mem; int (*cf_read_hook)(byte *buf, unsigned int max, int fd); struct include_file_stack *ifs; static struct include_file_stack *ifs_head; #define QUOTED_BUFFER_SIZE 4096 static BUFFER_(char) quoted_buffer; static char quoted_buffer_data[QUOTED_BUFFER_SIZE]; static inline void quoted_buffer_init(void) { quoted_buffer.used = 0; quoted_buffer.size = QUOTED_BUFFER_SIZE; quoted_buffer.data = quoted_buffer_data; } #define MAX_INCLUDE_DEPTH 8 #define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd); #define YY_NO_UNPUT #define YY_FATAL_ERROR(msg) cf_error(msg) #define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng; static void cf_include(char *arg, int alen); static int check_eof(void); static enum yytokentype cf_lex_symbol(const char *data); %} %option noyywrap %option noinput %option nounput %option noreject %x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE ALPHA [a-zA-Z_] DIGIT [0-9] XIGIT [0-9a-fA-F] ALNUM [a-zA-Z_0-9] WHITE [ \t] %% ^{WHITE}*include{WHITE}*\" { if (!ifs->depth) cf_error("Include not allowed in CLI"); BEGIN(INCLUDE); } [^"\n]+["]{WHITE}*; { char *start, *end; start = yytext; end = strchr(start, '"'); *end = 0; if (start == end) cf_error("Include with empty argument"); cf_include(start, end-start); BEGIN(INITIAL); } ["] cf_error("Include with empty argument"); . cf_error("Unterminated include"); \n cf_error("Unterminated include"); <> cf_error("Unterminated include"); {DIGIT}+:{DIGIT}+ { uint len1 UNUSED, len2; u64 l; char *e; errno = 0; l = bstrtoul10(yytext, &e); if (!e || (*e != ':') || (errno == ERANGE) || (l >> 32)) cf_error("ASN out of range"); if (l >> 16) { len1 = 32; len2 = 16; cf_lval.i64 = (2ULL << 48) | (((u64) l) << len2); } else { len1 = 16; len2 = 32; cf_lval.i64 = 0 | (((u64) l) << len2); } errno = 0; l = bstrtoul10(e+1, &e); if (!e || *e || (errno == ERANGE) || (l >> len2)) cf_error("Number out of range"); cf_lval.i64 |= l; return VPN_RD; } [02]:{DIGIT}+:{DIGIT}+ { uint len1, len2; u64 l; char *e; if (yytext[0] == '0') { cf_lval.i64 = 0; len1 = 16; len2 = 32; } else { cf_lval.i64 = 2ULL << 48; len1 = 32; len2 = 16; } errno = 0; l = bstrtoul10(yytext+2, &e); if (!e || (*e != ':') || (errno == ERANGE) || (l >> len1)) cf_error("ASN out of range"); cf_lval.i64 |= ((u64) l) << len2; errno = 0; l = bstrtoul10(e+1, &e); if (!e || *e || (errno == ERANGE) || (l >> len2)) cf_error("Number out of range"); cf_lval.i64 |= l; return VPN_RD; } {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ { unsigned long int l; ip4_addr ip4; char *e; cf_lval.i64 = 1ULL << 48; e = strchr(yytext, ':'); *e++ = '\0'; if (!ip4_pton(yytext, &ip4)) cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext); cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16; errno = 0; l = bstrtoul10(e, &e); if (!e || *e || (errno == ERANGE) || (l >> 16)) cf_error("Number out of range"); cf_lval.i64 |= l; return VPN_RD; } {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { if (!ip4_pton(yytext, &cf_lval.ip4)) cf_error("Invalid IPv4 address %s", yytext); return IP4; } ({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}*|{XIGIT}{2}(:{XIGIT}{2})*) { char *s = yytext; struct adata *bs; /* Skip 'hex:' prefix */ if (s[0] == 'h' && s[1] == 'e' && s[2] == 'x' && s[3] == ':') s += 4; int len = bstrhextobin(s, NULL); if (len < 0) cf_error("Invalid hex string"); bs = cfg_allocz(sizeof(struct adata) + len); bs->length = bstrhextobin(s, bs->data); ASSERT(bs->length == len); cf_lval.bs = bs; return BYTETEXT; } ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) { if (!ip6_pton(yytext, &cf_lval.ip6)) cf_error("Invalid IPv6 address %s", yytext); return IP6; } 0x{XIGIT}+ { char *e; unsigned long int l; errno = 0; l = bstrtoul16(yytext+2, &e); if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l) cf_error("Number out of range"); cf_lval.i = l; return NUM; } {DIGIT}+ { char *e; unsigned long int l; errno = 0; l = bstrtoul10(yytext, &e); if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l) cf_error("Number out of range"); cf_lval.i = l; return NUM; } else: { /* Hack to distinguish if..else from else: in case */ return ELSECOL; } ['] { BEGIN(APOSTROPHED); quoted_buffer_init(); } {ALNUM}|[-]|[.:] BUFFER_PUSH(quoted_buffer) = yytext[0]; \n cf_error("Unterminated symbol"); <> cf_error("Unterminated symbol"); ['] { BEGIN(INITIAL); BUFFER_PUSH(quoted_buffer) = 0; return cf_lex_symbol(quoted_buffer_data); } . cf_error("Invalid character in apostrophed symbol"); ({ALPHA}{ALNUM}*) { return cf_lex_symbol(yytext); } (.|\n) { BEGIN(INITIAL); return CLI_MARKER; } \.\. { return DDOT; } [={}:;,.()+*/%<>~\[\]?!\|-] { return yytext[0]; } ["] { BEGIN(QUOTED); quoted_buffer_init(); } \n cf_error("Unterminated string"); <> cf_error("Unterminated string"); ["] { BEGIN(INITIAL); BUFFER_PUSH(quoted_buffer) = 0; cf_lval.t = cfg_strdup(quoted_buffer_data); return TEXT; } . BUFFER_PUSH(quoted_buffer) = yytext[0]; <> { if (check_eof()) return END; } {WHITE}+ \n ifs->lino++; ifs->chno = 0; # BEGIN(COMMENT); \/\* BEGIN(CCOMM); . cf_error("Unknown character"); \n { ifs->lino++; ifs->chno = 0; BEGIN(INITIAL); } . \*\/ BEGIN(INITIAL); \n ifs->lino++; ifs->chno = 0; \/\* cf_error("Comment nesting not supported"); <> cf_error("Unterminated comment"); . \!\= return NEQ; \!\~ return NMA; \<\= return LEQ; \>\= return GEQ; \&\& return AND; \|\| return OR; \-\> return IMP; \[\= return PO; \=\] return PC; %% static uint cf_hash(const byte *c) { uint h = 13 << 24; while (*c) h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24); return h; } /* * IFS stack - it contains structures needed for recursive processing * of include in config files. On the top of the stack is a structure * for currently processed file. Other structures are either for * active files interrupted because of include directive (these have * fd and flex buffer) or for inactive files scheduled to be processed * later (when parent requested including of several files by wildcard * match - these do not have fd and flex buffer yet). * * FIXME: Most of these ifs and include functions are really sysdep/unix. */ static struct include_file_stack * push_ifs(struct include_file_stack *old) { struct include_file_stack *ret; ret = cfg_allocz(sizeof(struct include_file_stack)); ret->lino = 1; ret->prev = old; return ret; } static struct include_file_stack * pop_ifs(struct include_file_stack *old) { yy_delete_buffer(old->buffer); close(old->fd); return old->prev; } static void enter_ifs(struct include_file_stack *new) { if (!new->buffer) { new->fd = open(new->file_name, O_RDONLY); if (new->fd < 0) { ifs = ifs->up; cf_error("Unable to open included file %s: %m", new->file_name); } new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE); } yy_switch_to_buffer(new->buffer); } /** * cf_lex_unwind - unwind lexer state during error * * cf_lex_unwind() frees the internal state on IFS stack when the lexical * analyzer is terminated by cf_error(). */ void cf_lex_unwind(void) { struct include_file_stack *n; for (n = ifs; n != ifs_head; n = n->prev) { /* Memory is freed automatically */ if (n->buffer) yy_delete_buffer(n->buffer); if (n->fd) close(n->fd); } ifs = ifs_head; } static void cf_include(char *arg, int alen) { struct include_file_stack *base_ifs = ifs; int new_depth, rv, i; char *patt; glob_t g = {}; new_depth = ifs->depth + 1; if (new_depth > MAX_INCLUDE_DEPTH) cf_error("Max include depth reached"); /* expand arg to properly handle relative filenames */ if (*arg != '/') { int dlen = strlen(ifs->file_name); char *dir = alloca(dlen + 1); patt = alloca(dlen + alen + 2); memcpy(dir, ifs->file_name, dlen + 1); sprintf(patt, "%s/%s", dirname(dir), arg); } else patt = arg; /* Skip globbing if there are no wildcards, mainly to get proper response when the included config file is missing */ if (!strpbrk(arg, "?*[")) { ifs = push_ifs(ifs); ifs->file_name = cfg_strdup(patt); ifs->depth = new_depth; ifs->up = base_ifs; enter_ifs(ifs); return; } /* Expand the pattern */ rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g); if (rv == GLOB_ABORTED) cf_error("Unable to match pattern %s: %m", patt); if ((rv != 0) || (g.gl_pathc <= 0)) return; /* * Now we put all found files to ifs stack in reverse order, they * will be activated and processed in order as ifs stack is popped * by pop_ifs() and enter_ifs() in check_eof(). */ for(i = g.gl_pathc - 1; i >= 0; i--) { char *fname = g.gl_pathv[i]; struct stat fs; if (stat(fname, &fs) < 0) { globfree(&g); cf_error("Unable to stat included file %s: %m", fname); } if (fs.st_mode & S_IFDIR) continue; /* Prepare new stack item */ ifs = push_ifs(ifs); ifs->file_name = cfg_strdup(fname); ifs->depth = new_depth; ifs->up = base_ifs; } globfree(&g); enter_ifs(ifs); } static int check_eof(void) { if (ifs == ifs_head) { /* EOF in main config file */ ifs->lino = 1; /* Why this? */ return 1; } ifs = pop_ifs(ifs); enter_ifs(ifs); return 0; } static inline void cf_swap_soft_scope(struct config *conf); struct symbol * cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c) { if (scope->readonly) cf_error("Unknown symbol %s", c); struct symbol *s; uint l = strlen(c); if (l > SYM_MAX_LEN) cf_error("Symbol too long"); s = lp_alloc(lp, sizeof(struct symbol) + l + 1); *s = (struct symbol) { .scope = scope, .class = SYM_VOID, }; strcpy(s->name, c); if (!scope->hash.data) HASH_INIT(scope->hash, p, SYM_ORDER); HASH_INSERT2(scope->hash, SYM, p, s); if (new_config && (scope == new_config->root_scope)) add_tail(&(new_config->symbols), &(s->n)); return s; } /** * cf_find_symbol_scope - find a symbol by name * @scope: config scope * @c: symbol name * * This functions searches the symbol table in the scope @scope for a symbol of * given name. First it examines the current scope, then the underlying one * and so on until it either finds the symbol and returns a pointer to its * &symbol structure or reaches the end of the scope chain and returns %NULL to * signify no match. */ struct symbol * cf_find_symbol_scope(const struct sym_scope *scope, const byte *c) { struct symbol *s; /* Find the symbol here or anywhere below */ while (scope) if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c))) return s; else scope = scope->next; return NULL; } /** * cf_get_symbol - get a symbol by name * @c: symbol name * * This functions searches the symbol table of the currently parsed config * (@new_config) for a symbol of given name. It returns either the already * existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no * existing symbol is found. */ struct symbol * cf_get_symbol(struct config *conf, const byte *c) { return cf_find_symbol_scope(conf->current_scope, c) ?: ( cf_swap_soft_scope(conf), cf_new_symbol(conf->current_scope, conf->pool, conf->mem, c) ); } /** * cf_localize_symbol - get the local instance of given symbol * @sym: the symbol to localize * * This functions finds the symbol that is local to current scope * for purposes of cf_define_symbol(). */ struct symbol * cf_localize_symbol(struct config *conf, struct symbol *sym) { /* If the symbol type is void, it has been recently allocated just in this scope. */ if (!sym->class) return sym; /* If the scope is the current, it is already defined in this scope. */ if (cf_symbol_is_local(conf, sym)) cf_error("Symbol '%s' already defined", sym->name); /* Not allocated here yet, doing it now. */ cf_swap_soft_scope(conf); return cf_new_symbol(conf->current_scope, conf->pool, conf->mem, sym->name); } struct symbol * cf_default_name(struct config *conf, char *template, int *counter) { char buf[SYM_MAX_LEN]; struct symbol *s; char *perc = strchr(template, '%'); for(;;) { bsprintf(buf, template, ++(*counter)); s = cf_get_symbol(conf, buf); if (s->class == SYM_VOID) return s; if (!perc) break; } cf_error("Unable to generate default name"); } static enum yytokentype cf_lex_symbol(const char *data) { /* Have we defined such a symbol? */ struct symbol *sym = cf_get_symbol(new_config, data); cf_lval.s = sym; switch (sym->class) { case SYM_KEYWORD: { int val = sym->keyword->value; if (val > 0) return val; cf_lval.i = -val; return ENUM; } case SYM_METHOD: return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE; case SYM_VOID: return CF_SYM_UNDEFINED; default: return CF_SYM_KNOWN; } } void f_type_methods_register(void); /** * cf_lex_init - initialize the lexer * @is_cli: true if we're going to parse CLI command, false for configuration * @c: configuration structure * * cf_lex_init() initializes the lexical analyzer and prepares it for * parsing of a new input. */ void cf_lex_init(int is_cli, struct config *c) { if (!global_root_scope_pool) { global_root_scope_pool = rp_new(&root_pool, "Keywords pool"); global_root_scope_linpool = lp_new(global_root_scope_pool); global_root_scope = lp_allocz(global_root_scope_linpool, sizeof(*global_root_scope)); for (const struct keyword *k = keyword_list; k->name; k++) { struct symbol *sym = cf_new_symbol(global_root_scope, global_root_scope_pool, global_root_scope_linpool, k->name); sym->class = SYM_KEYWORD; sym->keyword = k; } global_root_scope->readonly = 1; f_type_methods_register(); } ifs_head = ifs = push_ifs(NULL); if (!is_cli) { ifs->file_name = c->file_name; ifs->fd = c->file_fd; ifs->depth = 1; } yyrestart(NULL); ifs->buffer = YY_CURRENT_BUFFER; if (is_cli) BEGIN(CLI); else BEGIN(INITIAL); c->root_scope = c->current_scope = cfg_allocz(sizeof(struct sym_scope)); c->root_scope->active = 1; if (is_cli) c->current_scope->next = config->root_scope; else c->current_scope->next = global_root_scope; } /** * cf_push_scope - enter new scope * @sym: symbol representing scope name * * If we want to enter a new scope to process declarations inside * a nested block, we can just call cf_push_scope() to push a new * scope onto the scope stack which will cause all new symbols to be * defined in this scope and all existing symbols to be sought for * in all scopes stored on the stack. */ void cf_push_scope(struct config *conf, struct symbol *sym) { struct sym_scope *s = cfg_allocz(sizeof(struct sym_scope)); s->next = conf->current_scope; conf->current_scope = s; s->active = 1; s->name = sym; s->slots = 0; } /** * cf_pop_scope - leave a scope * * cf_pop_scope() pops the topmost scope from the scope stack, * leaving all its symbols in the symbol table, but making them * invisible to the rest of the config. */ void cf_pop_scope(struct config *conf) { ASSERT(!conf->current_scope->soft_scopes); conf->current_scope->active = 0; conf->current_scope = conf->current_scope->next; ASSERT(conf->current_scope); } /** * cf_push_soft_scope - enter new soft scope * * If we want to enter a new anonymous scope that most likely will not contain * any symbols, we can use cf_push_soft_scope() insteas of cf_push_scope(). * Such scope will be converted to a regular scope on first use. */ void cf_push_soft_scope(struct config *conf) { if (conf->current_scope->soft_scopes < 0xfe) conf->current_scope->soft_scopes++; else cf_push_block_scope(conf); } /** * cf_pop_soft_scope - leave a soft scope * * Leave a soft scope entered by cf_push_soft_scope(). */ void cf_pop_soft_scope(struct config *conf) { if (conf->current_scope->soft_scopes) conf->current_scope->soft_scopes--; else cf_pop_block_scope(conf); } /** * cf_swap_soft_scope - convert soft scope to regular scope * * Soft scopes cannot hold symbols, so they must be converted to regular scopes * on first use. It is done automatically by cf_new_symbol(). */ static inline void cf_swap_soft_scope(struct config *conf) { if (conf->current_scope->soft_scopes) { conf->current_scope->soft_scopes--; cf_push_block_scope(conf); } } /** * cf_symbol_class_name - get name of a symbol class * @sym: symbol * * This function returns a string representing the class * of the given symbol. */ char * cf_symbol_class_name(struct symbol *sym) { switch (sym->class) { case SYM_VOID: return "undefined"; case SYM_PROTO: return "protocol"; case SYM_TEMPLATE: return "protocol template"; case SYM_FUNCTION: return "function"; case SYM_FILTER: return "filter"; case SYM_TABLE: return "routing table"; case SYM_ATTRIBUTE: return "custom attribute"; case SYM_MPLS_DOMAIN: return "MPLS domain"; case SYM_MPLS_RANGE: return "MPLS label range"; case SYM_CONSTANT_RANGE: return "constant"; case SYM_VARIABLE_RANGE: return "variable"; default: return "unknown type"; } } /** * DOC: Parser * * Both the configuration and CLI commands are analyzed using a syntax * driven parser generated by the |bison| tool from a grammar which * is constructed from information gathered from grammar snippets by * the |gen_parser.m4| script. * * Grammar snippets are files (usually with extension |.Y|) contributed * by various BIRD modules in order to provide information about syntax of their * configuration and their CLI commands. Each snipped consists of several * sections, each of them starting with a special keyword: |CF_HDR| for * a list of |#include| directives needed by the C code, |CF_DEFINES| * for a list of C declarations, |CF_DECLS| for |bison| declarations * including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR| * for the grammar rules, |CF_CODE| for auxiliary C code and finally * |CF_END| at the end of the snippet. * * To create references between the snippets, it's possible to define * multi-part rules by utilizing the |CF_ADDTO| macro which adds a new * alternative to a multi-part rule. * * CLI commands are defined using a |CF_CLI| macro. Its parameters are: * the list of keywords determining the command, the list of parameters, * help text for the parameters and help text for the command. * * Values of |enum| filter types can be defined using |CF_ENUM| with * the following parameters: name of filter type, prefix common for all * literals of this type and names of all the possible values. */ bird-2.15.1/conf/conf.c0000644000175000017500000004010214510244071013443 0ustar feelafeela/* * BIRD Internet Routing Daemon -- Configuration File Handling * * (c) 1998--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ /** * DOC: Configuration manager * * Configuration of BIRD is complex, yet straightforward. There are three * modules taking care of the configuration: config manager (which takes care * of storage of the config information and controls switching between configs), * lexical analyzer and parser. * * The configuration manager stores each config as a &config structure * accompanied by a linear pool from which all information associated * with the config and pointed to by the &config structure is allocated. * * There can exist up to four different configurations at one time: an active * one (pointed to by @config), configuration we are just switching from * (@old_config), one queued for the next reconfiguration (@future_config; if * there is one and the user wants to reconfigure once again, we just free the * previous queued config and replace it with the new one) and finally a config * being parsed (@new_config). The stored @old_config is also used for undo * reconfiguration, which works in a similar way. Reconfiguration could also * have timeout (using @config_timer) and undo is automatically called if the * new configuration is not confirmed later. The new config (@new_config) and * associated linear pool (@cfg_mem) is non-NULL only during parsing. * * Loading of new configuration is very simple: just call config_alloc() to get * a new &config structure, then use config_parse() to parse a configuration * file and fill all fields of the structure and finally ask the config manager * to switch to the new config by calling config_commit(). * * CLI commands are parsed in a very similar way -- there is also a stripped-down * &config structure associated with them and they are lex-ed and parsed by the * same functions, only a special fake token is prepended before the command * text to make the parser recognize only the rules corresponding to CLI commands. */ #include #include #undef LOCAL_DEBUG #include "nest/bird.h" #include "nest/route.h" #include "nest/protocol.h" #include "nest/iface.h" #include "nest/mpls.h" #include "lib/resource.h" #include "lib/string.h" #include "lib/event.h" #include "lib/timer.h" #include "conf/conf.h" #include "filter/filter.h" #include "sysdep/unix/unix.h" static jmp_buf conf_jmpbuf; struct config *config, *new_config; pool *config_pool; static struct config *old_config; /* Old configuration */ static struct config *future_config; /* New config held here if recon requested during recon */ static int old_cftype; /* Type of transition old_config -> config (RECONFIG_SOFT/HARD) */ static int future_cftype; /* Type of scheduled transition, may also be RECONFIG_UNDO */ /* Note that when future_cftype is RECONFIG_UNDO, then future_config is NULL, therefore proper check for future scheduled config checks future_cftype */ static event *config_event; /* Event for finalizing reconfiguration */ static timer *config_timer; /* Timer for scheduled configuration rollback */ /* These are public just for cmd_show_status(), should not be accessed elsewhere */ int shutting_down; /* Shutdown requested, do not accept new config changes */ int configuring; /* Reconfiguration is running */ int undo_available; /* Undo was not requested from last reconfiguration */ /* Note that both shutting_down and undo_available are related to requests, not processing */ /** * config_alloc - allocate a new configuration * @name: name of the config * * This function creates new &config structure, attaches a resource * pool and a linear memory pool to it and makes it available for * further use. Returns a pointer to the structure. */ struct config * config_alloc(const char *name) { pool *p = rp_new(config_pool, "Config"); linpool *l = lp_new_default(p); struct config *c = lp_allocz(l, sizeof(struct config)); /* Duplication of name string in local linear pool */ uint nlen = strlen(name) + 1; char *ndup = lp_allocu(l, nlen); memcpy(ndup, name, nlen); init_list(&c->tests); init_list(&c->symbols); c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */ c->pool = p; c->mem = l; c->file_name = ndup; c->load_time = current_time(); c->tf_route = c->tf_proto = TM_ISO_SHORT_MS; c->tf_base = c->tf_log = TM_ISO_LONG_MS; c->gr_wait = DEFAULT_GR_WAIT; return c; } /** * config_parse - parse a configuration * @c: configuration * * config_parse() reads input by calling a hook function pointed to * by @cf_read_hook and parses it according to the configuration * grammar. It also calls all the preconfig and postconfig hooks * before, resp. after parsing. * * Result: 1 if the config has been parsed successfully, 0 if any * error has occurred (such as anybody calling cf_error()) and * the @err_msg field has been set to the error message. */ int config_parse(struct config *c) { int done = 0; DBG("Parsing configuration file `%s'\n", c->file_name); new_config = c; cfg_mem = c->mem; if (setjmp(conf_jmpbuf)) goto cleanup; cf_lex_init(0, c); sysdep_preconfig(c); protos_preconfig(c); mpls_preconfig(c); rt_preconfig(c); cf_parse(); rt_postconfig(c); if (EMPTY_LIST(c->protos)) cf_error("No protocol is specified in the config file"); /* if (!c->router_id) cf_error("Router ID must be configured manually"); */ done = 1; cleanup: new_config = NULL; cfg_mem = NULL; return done; } /** * cli_parse - parse a CLI command * @c: temporary config structure * * cli_parse() is similar to config_parse(), but instead of a configuration, * it parses a CLI command. See the CLI module for more information. */ int cli_parse(struct config *c) { int done = 0; new_config = c; cfg_mem = c->mem; if (setjmp(conf_jmpbuf)) goto cleanup; cf_lex_init(1, c); cf_parse(); done = 1; cleanup: new_config = NULL; cfg_mem = NULL; return done; } /** * config_free - free a configuration * @c: configuration to be freed * * This function takes a &config structure and frees all resources * associated with it. */ void config_free(struct config *c) { if (!c) return; ASSERT(!c->obstacle_count); rfree(c->pool); } /** * config_free_old - free stored old configuration * * This function frees the old configuration (%old_config) that is saved for the * purpose of undo. It is useful before parsing a new config when reconfig is * requested, to avoid keeping three (perhaps memory-heavy) configs together. * Configuration is not freed when it is still active during reconfiguration. */ void config_free_old(void) { if (!old_config || old_config->obstacle_count) return; tm_stop(config_timer); undo_available = 0; config_free(old_config); old_config = NULL; } void config_add_obstacle(struct config *c) { DBG("+++ adding obstacle %d\n", c->obstacle_count); c->obstacle_count++; } void config_del_obstacle(struct config *c) { DBG("+++ deleting obstacle %d\n", c->obstacle_count); c->obstacle_count--; if (!c->obstacle_count && (c != config)) ev_schedule(config_event); } static int global_commit(struct config *new, struct config *old) { if (!new->hostname) { new->hostname = get_hostname(new->mem); if (!new->hostname) log(L_WARN "Cannot determine hostname"); } if (!old) return 0; if (!new->router_id) { new->router_id = old->router_id; if (new->router_id_from) { u32 id = if_choose_router_id(new->router_id_from, old->router_id); if (!id) log(L_WARN "Cannot determine router ID, using old one"); else new->router_id = id; } } return 0; } static int config_do_commit(struct config *c, int type) { if (type == RECONFIG_UNDO) { c = old_config; type = old_cftype; } else config_free(old_config); old_config = config; old_cftype = type; config = c; configuring = 1; if (old_config && !config->shutdown) log(L_INFO "Reconfiguring"); if (old_config) old_config->obstacle_count++; DBG("filter_commit\n"); filter_commit(c, old_config); DBG("sysdep_commit\n"); int force_restart = sysdep_commit(c, old_config); DBG("global_commit\n"); force_restart |= global_commit(c, old_config); mpls_commit(c, old_config); DBG("rt_commit\n"); rt_commit(c, old_config); DBG("protos_commit\n"); protos_commit(c, old_config, force_restart, type); int obs = 0; if (old_config) obs = --old_config->obstacle_count; DBG("do_commit finished with %d obstacles remaining\n", obs); return !obs; } static void config_done(void *unused UNUSED) { if (config->shutdown) sysdep_shutdown_done(); configuring = 0; if (old_config) log(L_INFO "Reconfigured"); if (future_cftype) { int type = future_cftype; struct config *conf = future_config; future_cftype = RECONFIG_NONE; future_config = NULL; log(L_INFO "Reconfiguring to queued configuration"); if (config_do_commit(conf, type)) config_done(NULL); } } /** * config_commit - commit a configuration * @c: new configuration * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) * @timeout: timeout for undo (in seconds; or 0 for no timeout) * * When a configuration is parsed and prepared for use, the * config_commit() function starts the process of reconfiguration. * It checks whether there is already a reconfiguration in progress * in which case it just queues the new config for later processing. * Else it notifies all modules about the new configuration by calling * their commit() functions which can either accept it immediately * or call config_add_obstacle() to report that they need some time * to complete the reconfiguration. After all such obstacles are removed * using config_del_obstacle(), the old configuration is freed and * everything runs according to the new one. * * When @timeout is nonzero, the undo timer is activated with given * timeout. The timer is deactivated when config_commit(), * config_confirm() or config_undo() is called. * * Result: %CONF_DONE if the configuration has been accepted immediately, * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED * if it's been queued due to another reconfiguration being in progress now * or %CONF_SHUTDOWN if BIRD is in shutdown mode and no new configurations * are accepted. */ int config_commit(struct config *c, int type, uint timeout) { if (shutting_down) { config_free(c); return CONF_SHUTDOWN; } undo_available = 1; if (timeout) tm_start(config_timer, timeout S); else tm_stop(config_timer); if (configuring) { if (future_cftype) { log(L_INFO "Queueing new configuration, ignoring the one already queued"); config_free(future_config); } else log(L_INFO "Queueing new configuration"); future_cftype = type; future_config = c; return CONF_QUEUED; } if (config_do_commit(c, type)) { config_done(NULL); return CONF_DONE; } return CONF_PROGRESS; } /** * config_confirm - confirm a commited configuration * * When the undo timer is activated by config_commit() with nonzero timeout, * this function can be used to deactivate it and therefore confirm * the current configuration. * * Result: %CONF_CONFIRM when the current configuration is confirmed, * %CONF_NONE when there is nothing to confirm (i.e. undo timer is not active). */ int config_confirm(void) { if (config_timer->expires == 0) return CONF_NOTHING; tm_stop(config_timer); return CONF_CONFIRM; } /** * config_undo - undo a configuration * * Function config_undo() can be used to change the current * configuration back to stored %old_config. If no reconfiguration is * running, this stored configuration is commited in the same way as a * new configuration in config_commit(). If there is already a * reconfiguration in progress and no next reconfiguration is * scheduled, then the undo is scheduled for later processing as * usual, but if another reconfiguration is already scheduled, then * such reconfiguration is removed instead (i.e. undo is applied on * the last commit that scheduled it). * * Result: %CONF_DONE if the configuration has been accepted immediately, * %CONF_PROGRESS if it will take some time to switch to it, %CONF_QUEUED * if it's been queued due to another reconfiguration being in progress now, * %CONF_UNQUEUED if a scheduled reconfiguration is removed, %CONF_NOTHING * if there is no relevant configuration to undo (the previous config request * was config_undo() too) or %CONF_SHUTDOWN if BIRD is in shutdown mode and * no new configuration changes are accepted. */ int config_undo(void) { if (shutting_down) return CONF_SHUTDOWN; if (!undo_available || !old_config) return CONF_NOTHING; undo_available = 0; tm_stop(config_timer); if (configuring) { if (future_cftype) { config_free(future_config); future_config = NULL; log(L_INFO "Removing queued configuration"); future_cftype = RECONFIG_NONE; return CONF_UNQUEUED; } else { log(L_INFO "Queueing undo configuration"); future_cftype = RECONFIG_UNDO; return CONF_QUEUED; } } if (config_do_commit(NULL, RECONFIG_UNDO)) { config_done(NULL); return CONF_DONE; } return CONF_PROGRESS; } int config_status(void) { if (shutting_down) return CONF_SHUTDOWN; if (configuring) return future_cftype ? CONF_QUEUED : CONF_PROGRESS; return CONF_DONE; } btime config_timer_status(void) { return tm_active(config_timer) ? tm_remains(config_timer) : -1; } extern void cmd_reconfig_undo_notify(void); static void config_timeout(timer *t UNUSED) { log(L_INFO "Config timeout expired, starting undo"); cmd_reconfig_undo_notify(); int r = config_undo(); if (r < 0) log(L_ERR "Undo request failed"); } void config_init(void) { config_pool = rp_new(&root_pool, "Configurations"); config_event = ev_new(config_pool); config_event->hook = config_done; config_timer = tm_new(config_pool); config_timer->hook = config_timeout; } /** * order_shutdown - order BIRD shutdown * * This function initiates shutdown of BIRD. It's accomplished by asking * for switching to an empty configuration. */ void order_shutdown(int gr) { struct config *c; if (shutting_down) return; if (!gr) log(L_INFO "Shutting down"); else log(L_INFO "Shutting down for graceful restart"); c = lp_alloc(config->mem, sizeof(struct config)); memcpy(c, config, sizeof(struct config)); init_list(&c->protos); init_list(&c->tables); init_list(&c->mpls_domains); init_list(&c->symbols); memset(c->def_tables, 0, sizeof(c->def_tables)); c->shutdown = 1; c->gr_down = gr; config_commit(c, RECONFIG_HARD, 0); shutting_down = 1; } /** * cf_error - report a configuration error * @msg: printf-like format string * * cf_error() can be called during execution of config_parse(), that is * from the parser, a preconfig hook or a postconfig hook, to report an * error in the configuration. */ void cf_error(const char *msg, ...) { char buf[1024]; va_list args; va_start(args, msg); if (bvsnprintf(buf, sizeof(buf), msg, args) < 0) strcpy(buf, ""); va_end(args); new_config->err_msg = cfg_strdup(buf); new_config->err_lino = ifs->lino; new_config->err_chno = ifs->chno - ifs->toklen + 1; new_config->err_file_name = ifs->file_name; cf_lex_unwind(); longjmp(conf_jmpbuf, 1); } /** * cfg_strdup - copy a string to config memory * @c: string to copy * * cfg_strdup() creates a new copy of the string in the memory * pool associated with the configuration being currently parsed. * It's often used when a string literal occurs in the configuration * and we want to preserve it for further use. */ char * cfg_strdup(const char *c) { int l = strlen(c) + 1; char *z = cfg_allocu(l); memcpy(z, c, l); return z; } void cfg_copy_list(list *dest, list *src, unsigned node_size) { node *dn, *sn; init_list(dest); WALK_LIST(sn, *src) { dn = cfg_alloc(node_size); memcpy(dn, sn, node_size); memset(dn, 0, sizeof(node)); add_tail(dest, dn); } } bird-2.15.1/conf/gen_keywords.m40000644000175000017500000000401314510244071015315 0ustar feelafeelam4_divert(-1)m4_dnl # # BIRD -- Generator of Configuration Keyword List # # (c) 1998--2000 Martin Mares # # Can be freely distributed and used under the terms of the GNU GPL. # # Common aliases m4_define(DNL, `m4_dnl') # Diversions used: # 1 keywords # Simple iterator m4_define(CF_itera, `m4_ifelse($#, 1, [[CF_iter($1)]], [[CF_iter($1)[[]]CF_itera(m4_shift($@))]])') m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)') # We include all the headers m4_define(CF_HDR, `m4_divert(0)') m4_define(CF_DECLS, `m4_divert(-1)') m4_define(CF_DEFINES, `m4_divert(-1)') # Keywords are translated to C initializers m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1 }, m4_divert(-1)') m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)CF_handle_kw($1)]])') m4_define(CF_KEYWORDS, `CF_iterate([[CF_keywd]], [[$@]])DNL') m4_define(CF_KEYWORDS_EXCLUSIVE, `CF_KEYWORDS($@)') # CLI commands generate keywords as well m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]])) ') # Enums are translated to C initializers: use CF_ENUM(typename, prefix, values) # For different prefix: CF_ENUM_PX(typename, external prefix, C prefix, values) m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1) }, m4_divert(-1)') m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL') m4_define(CF_ENUM_PX, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL') # After all configuration templates end, we generate the m4_m4wrap(` m4_divert(0) static const struct keyword keyword_list[] = { m4_undivert(1){ NULL, -1 } }; ') # As we are processing C source, we must access all M4 primitives via # m4_* and also set different quoting convention: `[[' and ']]' m4_changequote([[,]]) bird-2.15.1/conf/conf.h0000644000175000017500000002322314573415512013466 0ustar feelafeela/* * BIRD Internet Routing Daemon -- Configuration File Handling * * (c) 1998--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ #ifndef _BIRD_CONF_H_ #define _BIRD_CONF_H_ #include "sysdep/config.h" #include "lib/ip.h" #include "lib/hash.h" #include "lib/resource.h" #include "lib/timer.h" /* Configuration structure */ struct config { pool *pool; /* Pool the configuration is stored in */ linpool *mem; /* Linear pool containing configuration data */ list protos; /* Configured protocol instances (struct proto_config) */ list tables; /* Configured routing tables (struct rtable_config) */ list mpls_domains; /* Configured MPLS domains (struct mpls_domain_config) */ list logfiles; /* Configured log files (sysdep) */ list tests; /* Configured unit tests (f_bt_test_suite) */ list symbols; /* Configured symbols in config order */ int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */ const char *syslog_name; /* Name used for syslog (NULL -> no syslog) */ struct rtable_config *def_tables[NET_MAX]; /* Default routing tables for each network */ struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */ u32 router_id; /* Our Router ID */ u32 proto_default_debug; /* Default protocol debug mask */ u32 proto_default_mrtdump; /* Default protocol mrtdump mask */ u32 channel_default_debug; /* Default channel debug mask */ u32 table_default_debug; /* Default table debug mask */ struct timeformat tf_route; /* Time format for 'show route' */ struct timeformat tf_proto; /* Time format for 'show protocol' */ struct timeformat tf_log; /* Time format for the logfile */ struct timeformat tf_base; /* Time format for other purposes */ u32 gr_wait; /* Graceful restart wait timeout (sec) */ const char *hostname; /* Hostname */ int cli_debug; /* Tracing of CLI connections and commands */ int latency_debug; /* I/O loop tracks duration of each event */ u32 latency_limit; /* Events with longer duration are logged (us) */ u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */ u32 watchdog_timeout; /* Watchdog timeout (in seconds, 0 = disabled) */ char *err_msg; /* Parser error message */ int err_lino; /* Line containing error */ int err_chno; /* Character where the parser stopped */ char *err_file_name; /* File name containing error */ char *file_name; /* Name of main configuration file */ int file_fd; /* File descriptor of main configuration file */ struct sym_scope *root_scope; /* Scope for root symbols */ struct sym_scope *current_scope; /* Current scope where we are actually in while parsing */ int obstacle_count; /* Number of items blocking freeing of this config */ int shutdown; /* This is a pseudo-config for daemon shutdown */ int gr_down; /* This is a pseudo-config for graceful restart */ btime load_time; /* When we've got this configuration */ }; /* Please don't use these variables in protocols. Use proto_config->global instead. */ extern struct config *config; /* Currently active configuration */ extern struct config *new_config; /* Configuration being parsed */ struct config *config_alloc(const char *name); int config_parse(struct config *); int cli_parse(struct config *); void config_free(struct config *); void config_free_old(void); int config_commit(struct config *, int type, uint timeout); int config_confirm(void); int config_undo(void); int config_status(void); btime config_timer_status(void); void config_init(void); void cf_error(const char *msg, ...) NORET; #define cf_warn(msg, args...) log(L_WARN "%s:%d:%d: " msg, ifs->file_name, ifs->lino, ifs->chno - ifs->toklen + 1, ##args) void config_add_obstacle(struct config *); void config_del_obstacle(struct config *); void order_shutdown(int gr); #define RECONFIG_NONE 0 #define RECONFIG_HARD 1 #define RECONFIG_SOFT 2 #define RECONFIG_UNDO 3 #define CONF_DONE 0 #define CONF_PROGRESS 1 #define CONF_QUEUED 2 #define CONF_UNQUEUED 3 #define CONF_CONFIRM 4 #define CONF_SHUTDOWN -1 #define CONF_NOTHING -2 /* Pools */ extern pool *config_pool; extern linpool *cfg_mem; #define cfg_alloc(size) lp_alloc(cfg_mem, size) #define cfg_allocu(size) lp_allocu(cfg_mem, size) #define cfg_allocz(size) lp_allocz(cfg_mem, size) char *cfg_strdup(const char *c); void cfg_copy_list(list *dest, list *src, unsigned node_size); /* Lexer */ extern int (*cf_read_hook)(byte *buf, uint max, int fd); struct keyword { byte *name; int value; }; struct symbol { node n; /* In list of symbols in config */ struct symbol *next; struct sym_scope *scope; int class; /* SYM_* */ uint flags; /* SYM_FLAG_* */ union { struct proto_config *proto; /* For SYM_PROTO and SYM_TEMPLATE */ const struct f_line *function; /* For SYM_FUNCTION */ const struct filter *filter; /* For SYM_FILTER */ struct rtable_config *table; /* For SYM_TABLE */ struct f_dynamic_attr *attribute; /* For SYM_ATTRIBUTE */ struct mpls_domain_config *mpls_domain; /* For SYM_MPLS_DOMAIN */ struct mpls_range_config *mpls_range; /* For SYM_MPLS_RANGE */ struct f_val *val; /* For SYM_CONSTANT */ uint offset; /* For SYM_VARIABLE */ const struct keyword *keyword; /* For SYM_KEYWORD */ const struct f_method *method; /* For SYM_METHOD */ }; char name[0]; }; struct sym_scope { struct sym_scope *next; /* Next on scope stack */ struct symbol *name; /* Name of this scope */ HASH(struct symbol) hash; /* Local symbol hash */ uint slots; /* Variable slots */ byte soft_scopes; /* Number of soft scopes above */ byte active:1; /* Currently entered */ byte block:1; /* No independent stack frame */ byte readonly:1; /* Do not add new symbols */ }; extern struct sym_scope *global_root_scope; extern pool *global_root_scope_pool; extern linpool *global_root_scope_linpool; #define SYM_MAX_LEN 64 /* Remember to update cf_symbol_class_name() */ #define SYM_VOID 0 #define SYM_PROTO 1 #define SYM_TEMPLATE 2 #define SYM_FUNCTION 3 #define SYM_FILTER 4 #define SYM_TABLE 5 #define SYM_ATTRIBUTE 6 #define SYM_KEYWORD 7 #define SYM_METHOD 8 #define SYM_MPLS_DOMAIN 9 #define SYM_MPLS_RANGE 10 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ #define SYM_VARIABLE_RANGE SYM_VARIABLE ... (SYM_VARIABLE | 0xff) #define SYM_CONSTANT 0x200 /* 0x200-0x2ff are variable types */ #define SYM_CONSTANT_RANGE SYM_CONSTANT ... (SYM_CONSTANT | 0xff) #define SYM_TYPE(s) ((s)->val->type) #define SYM_VAL(s) ((s)->val->val) /* Symbol flags */ #define SYM_FLAG_SAME 0x1 /* For SYM_FUNCTION and SYM_FILTER */ struct include_file_stack { void *buffer; /* Internal lexer state */ char *file_name; /* File name */ int fd; /* File descriptor */ int lino; /* Current line num */ int chno; /* Current char num (on current line) */ int toklen; /* Current token length */ int depth; /* Include depth, 0 = cannot include */ struct include_file_stack *prev; /* Previous record in stack */ struct include_file_stack *up; /* Parent (who included this file) */ }; extern struct include_file_stack *ifs; int cf_lex(void); void cf_lex_init(int is_cli, struct config *c); void cf_lex_unwind(void); struct symbol *cf_find_symbol_scope(const struct sym_scope *scope, const byte *c); static inline struct symbol *cf_find_symbol_cfg(const struct config *cfg, const byte *c) { return cf_find_symbol_scope(cfg->root_scope, c); } #define cf_find_symbol(where, what) _Generic(*(where), \ struct config: cf_find_symbol_cfg, \ struct sym_scope: cf_find_symbol_scope \ )((where), (what)) struct symbol *cf_get_symbol(struct config *conf, const byte *c); struct symbol *cf_default_name(struct config *conf, char *template, int *counter); struct symbol *cf_localize_symbol(struct config *conf, struct symbol *sym); static inline int cf_symbol_is_local(struct config *conf, struct symbol *sym) { return (sym->scope == conf->current_scope) && !conf->current_scope->soft_scopes; } /* internal */ struct symbol *cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c); /** * cf_define_symbol - define meaning of a symbol * @sym: symbol to be defined * @type: symbol class to assign * @def: class dependent data * * Defines new meaning of a symbol. If the symbol is an undefined * one (%SYM_VOID), it's just re-defined to the new type. If it's defined * in different scope, a new symbol in current scope is created and the * meaning is assigned to it. If it's already defined in the current scope, * an error is reported via cf_error(). * * Result: Pointer to the newly defined symbol. If we are in the top-level * scope, it's the same @sym as passed to the function. */ #define cf_define_symbol(conf_, osym_, type_, var_, def_) ({ \ struct symbol *sym_ = cf_localize_symbol(conf_, osym_); \ sym_->class = type_; \ sym_->var_ = def_; \ sym_; }) #define cf_create_symbol(conf_, name_, type_, var_, def_) \ cf_define_symbol(conf_, cf_get_symbol(conf_, name_), type_, var_, def_) void cf_push_scope(struct config *, struct symbol *); void cf_pop_scope(struct config *); void cf_push_soft_scope(struct config *); void cf_pop_soft_scope(struct config *); static inline void cf_push_block_scope(struct config *conf) { cf_push_scope(conf, NULL); conf->current_scope->block = 1; } static inline void cf_pop_block_scope(struct config *conf) { ASSERT(conf->current_scope->block); cf_pop_scope(conf); } char *cf_symbol_class_name(struct symbol *sym); /* Parser */ extern char *cf_text; int cf_parse(void); /* Sysdep hooks */ void sysdep_preconfig(struct config *); int sysdep_commit(struct config *, struct config *); void sysdep_shutdown_done(void); #endif bird-2.15.1/conf/confbase.Y0000644000175000017500000002477614573415512014320 0ustar feelafeela/* * BIRD -- Configuration Parser Top * * (c) 1998--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ CF_HDR #define PARSER 1 #include "nest/bird.h" #include "conf/conf.h" #include "lib/resource.h" #include "lib/socket.h" #include "lib/timer.h" #include "lib/string.h" #include "nest/protocol.h" #include "nest/iface.h" #include "nest/route.h" #include "nest/bfd.h" #include "nest/cli.h" #include "filter/filter.h" /* FIXME: Turn on YYERROR_VERBOSE and work around lots of bison bugs? */ CF_DEFINES static void check_u16(uint val) { if (val > 0xFFFF) cf_error("Value %u out of range (0-65535)", val); } #define cf_assert(cond, ...) do { if (!(cond)) cf_error(__VA_ARGS__); } while (0) static inline void cf_assert_symbol(const struct symbol *sym, uint class) { switch (class) { case SYM_PROTO: cf_assert(sym->class == SYM_PROTO, "Protocol name required"); break; case SYM_TEMPLATE: cf_assert(sym->class == SYM_TEMPLATE, "Protocol template name required"); break; case SYM_FUNCTION: cf_assert(sym->class == SYM_FUNCTION, "Function name required"); break; case SYM_FILTER: cf_assert(sym->class == SYM_FILTER, "Filter name required"); break; case SYM_TABLE: cf_assert(sym->class == SYM_TABLE, "Table name required"); break; case SYM_ATTRIBUTE: cf_assert(sym->class == SYM_ATTRIBUTE, "Custom attribute name required"); break; case SYM_MPLS_DOMAIN: cf_assert(sym->class == SYM_MPLS_DOMAIN, "MPLS domain name required"); break; case SYM_MPLS_RANGE: cf_assert(sym->class == SYM_MPLS_RANGE, "MPLS range name required"); break; case SYM_VARIABLE: cf_assert((sym->class & ~0xff) == SYM_VARIABLE, "Variable name required"); break; case SYM_CONSTANT: cf_assert((sym->class & ~0xff) == SYM_CONSTANT, "Constant name required"); break; default: bug("This shall not happen"); } } CF_DECLS %union { uint i; u32 i32; u64 i64; ip_addr a; ip4_addr ip4; ip6_addr ip6; net_addr net; net_addr *net_ptr; struct symbol *s; struct keyword *kw; const char *t; struct rtable_config *r; struct channel_config *cc; struct channel *c; struct f_inst *x; struct { struct f_inst *begin, *end; } xp; enum filter_return fret; enum ec_subtype ecs; struct f_dynamic_attr fda; struct f_static_attr fsa; struct f_lval flv; struct f_line *fl; struct f_arg *fa; const struct filter *f; struct f_tree *e; struct f_trie *trie; struct f_val v; struct password_item *p; struct rt_show_data *ra; struct sym_show_data *sd; struct lsadb_show_data *ld; struct mrt_dump_data *md; struct mpls_show_ranges_cmd *msrc; struct bfd_show_sessions_cmd *bssc; struct iface *iface; void *g; btime time; struct f_prefix px; struct proto_spec ps; struct channel_limit cl; struct timeformat *tf; mpls_label_stack *mls; const struct adata *bs; struct aggr_item_node *ai; } %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT %token GEQ LEQ NEQ AND OR IMP %token PO PC %token NUM ENUM %token IP4 %token IP6 %token VPN_RD %token CF_SYM_KNOWN CF_SYM_UNDEFINED CF_SYM_METHOD_BARE CF_SYM_METHOD_ARGS %token TEXT %token BYTETEXT %type ipa_scope %type expr bool pxlen4 %type