bird-2.16.1/0000775000175000017500000000000014740230232011432 5ustar feelafeelabird-2.16.1/.dir-locals.el0000664000175000017500000000022414207634355014075 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.16.1/.gitignore0000664000175000017500000000026714207634355013443 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.16.1/client/0000775000175000017500000000000014730300120012701 5ustar feelafeelabird-2.16.1/client/Doc0000664000175000017500000000003714207634355013354 0ustar feelafeelaH Client S client.c commands.c bird-2.16.1/client/Makefile0000664000175000017500000000031314207634355014361 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.16.1/client/birdcl.c0000664000175000017500000000461514207634355014335 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.16.1/client/client.h0000664000175000017500000000136614207634355014361 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.16.1/client/cmds.Y0000664000175000017500000000045114207634355014004 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.16.1/client/client.c0000644000175000017500000002014614345463176014353 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.16.1/client/birdc.c0000644000175000017500000001052014730300120014124 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.16.1/client/commands.c0000644000175000017500000001410514730300120014645 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.16.1/client/util.c0000644000175000017500000000176014730300120014024 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_cli(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_cli(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_cli(msg, args); va_end(args); exit(1); } bird-2.16.1/conf/0000775000175000017500000000000014740230173012363 5ustar feelafeelabird-2.16.1/conf/Doc0000664000175000017500000000004414207634355013021 0ustar feelafeelaH Configuration S conf.c S cf-lex.l bird-2.16.1/conf/Makefile0000664000175000017500000000200014207634355014023 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.16.1/conf/flowspec.Y0000644000175000017500000001231314345463176014351 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.16.1/conf/gen_commands.m40000644000175000017500000000136114730300120015243 0ustar feelafeelam4_divert(-1)m4_dnl # # BIRD -- Generator of CLI Command List # # (c) 2000 Martin Mares # # Can be freely distributed and used under the terms of the GNU GPL. # m4_define(CF_CLI, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$3", "$4", 1 }, m4_divert(-1)') m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1 }, m4_divert(-1)') m4_define(CF_CLI_OPT, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1, .is_option = 1 }, m4_divert(-1)') m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 }, m4_divert(-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.16.1/conf/cf-lex.l0000644000175000017500000005177714740230173013735 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, v; 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; v = (2ULL << 48) | (((u64) l) << len2); } else { len1 = 16; len2 = 32; v = 0 | (((u64) l) << len2); } errno = 0; l = bstrtoul10(e+1, &e); if (!e || *e || (errno == ERANGE) || (l >> len2)) cf_error("Number out of range"); v |= l; cf_lval.rd = rd_from_u64(v); return VPN_RD; } [02]:{DIGIT}+:{DIGIT}+ { uint len1, len2; u64 l, v; char *e; if (yytext[0] == '0') { len1 = 16; len2 = 32; v = 0; } else { len1 = 32; len2 = 16; v = 2ULL << 48; } errno = 0; l = bstrtoul10(yytext+2, &e); if (!e || (*e != ':') || (errno == ERANGE) || (l >> len1)) cf_error("ASN out of range"); v |= ((u64) l) << len2; errno = 0; l = bstrtoul10(e+1, &e); if (!e || *e || (errno == ERANGE) || (l >> len2)) cf_error("Number out of range"); v |= l; cf_lval.rd = rd_from_u64(v); return VPN_RD; } {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ { unsigned long int l; ip4_addr ip4; u64 v; char *e; v = 1ULL << 48; e = strchr(yytext, ':'); *e++ = '\0'; if (!ip4_pton(yytext, &ip4)) cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext); v |= ((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"); v |= l; cf_lval.rd = rd_from_u64(v); 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_TOKEN; } 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.16.1/conf/conf.c0000644000175000017500000004015214740230173013454 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); c->cli = (struct cli_config_list) {}; 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.16.1/conf/conf.h0000644000175000017500000002337714740230173013473 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" #include "lib/tlists.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 */ TLIST_STRUCT_DEF(cli_config, struct cli_config) cli; /* Configured CLI sockets */ 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.16.1/conf/confbase.Y0000644000175000017500000002523314740230173014300 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; vpn_rd rd; 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; struct timeformat *tfp; 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 %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