diff --git a/NEWS b/NEWS index 3aaa4dd6..1341fcd4 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,44 @@ +Version 1.3.12 (2013-11-23) + o BFD protocol (RFC 5880). + o BFD support for OSPF and BGP. + o New 'allow local as' option for BGP. + o Filters allows setting gw, ifname and ifindex. + o Filter operator 'delete/filter' extended to bgp_paths. + o Filter operator 'len' extended to [e]clists. + o PID file support. + o Several bugfixes and minor improvements. + +Version 1.3.11 (2013-07-27) + o OSPF stub router option (RFC 3137). + o TTL security for OSPF and RIP. + o Protocol packet priority and traffic class handling. + o Multiple routing tables support for FreeBSD and OpenBSD. + o Extends constants to all filter data types. + o Implements eval command. + o 'bgppath ~ int set' filter operation. + o Several bugfixes. + +Version 1.3.10 (2013-04-30) + o Lightweight BIRD client for embedded environments. + o Dynamic IPv6 router advertisements. + o New 'next hop keep' option for BGP. + o Smart default routing table for 'show route export/preexport/protocol'. + o Automatic router ID selection could be configured to use address of loopback. + o Allows configured global addresses of NBMA neighbors in OSPFv3. + o Allows BIRD commands from UNIX shell even in restricted mode. + o Route limits inherited from templates can be disabled. + o Symbol names enclosed by apostrophes can contain dots. + o Several bugfixes. + +Version 1.3.9 (2013-01-11) + o BIRD can be configured to keep and show filtered routes. + o Separate receive and import limits. + o Several new reconfiguration cmd options (undo, timeout, check). + o Configurable automatic router ID selection. + o Dragonfly BSD support. + o Fixed OSPFv3 vlinks. + o Several minor bugfixes. + Version 1.3.8 (2012-08-07) o Generalized import and export route limits. o RDNSS and DNSSL support for RAdv. @@ -11,7 +52,7 @@ Version 1.3.8 (2012-08-07) Version 1.3.7 (2012-03-22) o Route Origin Authorization basics. o RIPng working again. - o Extended clist operations in filters. + o Extended clist operations in filters. o Fixes several bugs in BSD iface handling. o Several minor bugfixes and enhancements. diff --git a/README b/README index 5c2ef076..daeb18bd 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ (c) 1998--2008 Martin Mares (c) 1998--2000 Pavel Machek (c) 1998--2008 Ondrej Filip - (c) 2009--2011 CZ.NIC z.s.p.o. + (c) 2009--2013 CZ.NIC z.s.p.o. ================================================================================ diff --git a/aclocal.m4 b/aclocal.m4 index 75b3f92a..02c0f76b 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -133,19 +133,35 @@ if test "$bird_cv_struct_ip_mreqn" = yes ; then fi ]) -AC_DEFUN(BIRD_CHECK_GCC_OPTIONS, -[AC_CACHE_VAL(bird_cv_c_option_no_pointer_sign, [ -cat >conftest.c <&AS_MESSAGE_LOG_FD 2>&1 ; then - bird_cv_c_option_no_pointer_sign=yes -else - bird_cv_c_option_no_pointer_sign=no -fi -rm -rf conftest* a.out -])]) +AC_DEFUN(BIRD_CHECK_PTHREADS, +[ + bird_tmp_cflags="$CFLAGS" + + CFLAGS="$CFLAGS -pthread" + AC_CACHE_CHECK([whether POSIX threads are available], bird_cv_lib_pthreads, + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[pthread_t pt; pthread_create(&pt, NULL, NULL, NULL); pthread_spinlock_t lock; pthread_spin_lock(&lock); ]])], + [bird_cv_lib_pthreads=yes], [bird_cv_lib_pthreads=no])]) + + CFLAGS="$bird_tmp_cflags" +]) + +AC_DEFUN(BIRD_CHECK_GCC_OPTION, +[ + bird_tmp_cflags="$CFLAGS" + + CFLAGS="$3 $2" + AC_CACHE_CHECK([whether CC supports $2], $1, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [$1=yes], [$1=no])]) + + CFLAGS="$bird_tmp_cflags" +]) + +AC_DEFUN(BIRD_ADD_GCC_OPTION, +[ + if test "$$1" = yes ; then + CFLAGS="$CFLAGS $2" + fi +]) # BIRD_CHECK_PROG_FLAVOR_GNU(PROGRAM-PATH, IF-SUCCESS, [IF-FAILURE]) # copied autoconf internal _AC_PATH_PROG_FLAVOR_GNU diff --git a/bird.conf b/bird.conf index 2d10ef4b..bafd6ea1 100644 --- a/bird.conf +++ b/bird.conf @@ -25,14 +25,14 @@ protocol kernel { protocol static { # disabled; - route fec0:2::/64 reject; - route fec0:3::/64 reject; - route fec0:4::/64 reject; + route fec0:2::/64 blackhole; + route fec0:3::/64 unreachable; + route fec0:4::/64 prohibit; # route 0.0.0.0/0 via 195.113.31.113; -# route 62.168.0.0/25 reject; +# route 62.168.0.0/25 unreachable; # route 1.2.3.4/32 via 195.113.31.124; -# route 10.0.0.0/8 reject; +# route 10.0.0.0/8 unreachable; # route 10.1.1.0:255.255.255.0 via 62.168.0.3; # route 10.1.2.0:255.255.255.0 via 62.168.0.3; # route 10.1.3.0:255.255.255.0 via 62.168.0.4; diff --git a/client/Makefile b/client/Makefile index 867476cc..a1578766 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,5 +1,11 @@ -source=client.c commands.c util.c +source=commands.c util.c client.c root-rel=../ dir-name=client +clients := $(client) birdcl + +source-dep := $(source) $(addsuffix .c,$(clients)) + +subdir: $(addsuffix .o,$(clients)) + include ../Rules diff --git a/client/birdc.c b/client/birdc.c new file mode 100644 index 00000000..bbe18331 --- /dev/null +++ b/client/birdc.c @@ -0,0 +1,223 @@ +/* + * 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" +#include "sysdep/unix/unix.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); +extern Function *rl_last_func; + +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); + input_stop_list(); + return 0; +} + +void +input_init(void) +{ + 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: %m"); +} + +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(); + rl_callback_handler_remove(); +} diff --git a/client/birdcl.c b/client/birdcl.c new file mode 100644 index 00000000..2d5e1067 --- /dev/null +++ b/client/birdcl.c @@ -0,0 +1,160 @@ +/* + * 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 "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: %m"); + + more_active = 1; +} + +void +more_end(void) +{ + more_active = 0; + + if (tcsetattr (0, TCSANOW, &stored_tty) < 0) + die("tcsetattr: %m"); +} + +static void +sig_handler(int signal) +{ + cleanup(); + exit(0); +} + +void +input_init(void) +{ + if (!interactive) + return; + + if (tcgetattr(0, &stored_tty) < 0) + die("tcgetattr: %m"); + + 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(); +} diff --git a/client/client.c b/client/client.c index 8711cf0a..a9d0096d 100644 --- a/client/client.c +++ b/client/client.c @@ -2,22 +2,32 @@ * 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 -#include -#include #include "nest/bird.h" #include "lib/resource.h" @@ -25,33 +35,31 @@ #include "client/client.h" #include "sysdep/unix/unix.h" +#define SERVER_READ_BUF_LEN 4096 + static char *opt_list = "s:vr"; -static int verbose; +static int verbose, restricted, once; static char *init_cmd; -static int once; static char *server_path = PATH_CONTROL_SOCKET; static int server_fd; -static byte server_read_buf[4096]; +static byte server_read_buf[SERVER_READ_BUF_LEN]; static byte *server_read_pos = server_read_buf; -#define STATE_PROMPT 0 -#define STATE_CMD_SERVER 1 -#define STATE_CMD_USER 2 +int init = 1; /* During intial sequence */ +int busy = 1; /* Executing BIRD command */ +int interactive; /* Whether stdin is terminal */ -static int input_initialized; -static int input_hidden_end; -static int cstate = STATE_CMD_SERVER; -static int nstate = STATE_CMD_SERVER; +static int num_lines, skip_input; +int term_lns, term_cls; -static int num_lines, skip_input, interactive; /*** Parsing of arguments ***/ static void -usage(void) +usage(char *name) { - fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); + fprintf(stderr, "Usage: %s [-s ] [-v] [-r]\n", name); exit(1); } @@ -70,10 +78,10 @@ parse_args(int argc, char **argv) verbose++; break; case 'r': - init_cmd = "restrict"; + restricted = 1; break; default: - usage(); + usage(argv[0]); } /* If some arguments are not options, we take it as commands */ @@ -83,9 +91,6 @@ parse_args(int argc, char **argv) int i; int len = 0; - if (init_cmd) - usage(); - for (i = optind; i < argc; i++) len += strlen(argv[i]) + 1; @@ -99,17 +104,14 @@ parse_args(int argc, char **argv) tmp[-1] = 0; once = 1; + interactive = 0; } } + /*** Input ***/ -static void server_send(char *); - -/* HACK: libreadline internals we need to access */ -extern int _rl_vis_botlin; -extern void _rl_move_vert(int); -extern Function *rl_last_func; +static void server_send(char *cmd); static int handle_internal_command(char *cmd) @@ -127,182 +129,38 @@ handle_internal_command(char *cmd) return 0; } -void +static void submit_server_command(char *cmd) { - server_send(cmd); - nstate = STATE_CMD_SERVER; + busy = 1; num_lines = 2; -} - -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 -got_line(char *cmd_buffer) -{ - char *cmd; - - if (!cmd_buffer) - { - cleanup(); - exit(0); - } - if (cmd_buffer[0]) - { - cmd = cmd_expand(cmd_buffer); - if (cmd) - { - add_history_dedup(cmd); - - if (!handle_internal_command(cmd)) - submit_server_command(cmd); - - free(cmd); - } - else - add_history_dedup(cmd_buffer); - } - free(cmd_buffer); + server_send(cmd); } void -input_start_list(void) /* Leave the currently edited line and make space for listing */ +submit_command(char *cmd_raw) { - _rl_move_vert(_rl_vis_botlin); -#ifdef HAVE_RL_CRLF - rl_crlf(); -#endif -} + char *cmd = cmd_expand(cmd_raw); -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); - input_stop_list(); - return 0; -} - -static void -input_init(void) -{ - rl_readline_name = "birdc"; - rl_add_defun("bird-complete", input_complete, '\t'); - rl_add_defun("bird-help", input_help, '?'); - rl_callback_handler_install("bird> ", got_line); - input_initialized = 1; -// readline library does strange things when stdin is nonblocking. -// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) -// die("fcntl: %m"); -} - -static void -input_hide(void) -{ - input_hidden_end = rl_end; - rl_end = 0; - rl_expand_prompt(""); - rl_redisplay(); -} - -static void -input_reveal(void) -{ - /* need this, otherwise some lib seems to eat pending output when - the prompt is displayed */ - fflush(stdout); - tcdrain(fileno(stdout)); - - rl_end = input_hidden_end; - rl_expand_prompt("bird> "); - rl_forced_update_display(); -} - -void -cleanup(void) -{ - if (input_initialized) - { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); - } -} - -void -update_state(void) -{ - if (nstate == cstate) + 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 @@ -312,40 +170,39 @@ update_state(void) return; } - if (!init_cmd && once) + if (once) { /* Initial command is finished and we want to exit */ cleanup(); exit(0); } - if (nstate == STATE_PROMPT) - { - if (input_initialized) - input_reveal(); - else - input_init(); - } + input_init(); - if (nstate != STATE_PROMPT) - input_hide(); + term_lns = (term_lns > 0) ? term_lns : 25; + term_cls = (term_cls > 0) ? term_cls : 80; - cstate = nstate; + init = 0; } + +/*** Output ***/ + void more(void) { + more_begin(); printf("--More--\015"); fflush(stdout); redo: switch (getchar()) { - case 32: + case ' ': num_lines = 2; break; - case 13: + case '\n': + case '\r': num_lines--; break; case 'q': @@ -357,6 +214,7 @@ more(void) printf(" \015"); fflush(stdout); + more_end(); } @@ -383,6 +241,7 @@ server_connect(void) die("fcntl: %m"); } + #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) static void @@ -391,36 +250,32 @@ server_got_reply(char *x) int code; int len = 0; - if (*x == '+') /* Async reply */ + if (*x == '+') /* Async reply */ PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ + 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] == '-')) + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) { if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); + PRINTF(len, "%s\n", verbose ? x : x+5); + if (x[4] == ' ') { - nstate = STATE_PROMPT; - skip_input = 0; - return; + busy = 0; + skip_input = 0; + return; } } else PRINTF(len, "??? <%s>\n", x); - if (skip_input) - return; - - if (interactive && input_initialized && (len > 0)) + if (interactive && busy && !skip_input && !init && (len > 0)) { - int lns = LINES ? LINES : 25; - int cls = COLS ? COLS : 80; - num_lines += (len + cls - 1) / cls; /* Divide and round up */ - if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) - more(); + num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ + if (num_lines >= term_lns) + more(); } } @@ -465,19 +320,23 @@ server_read(void) } } -static fd_set select_fds; - 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); - if (cstate != STATE_CMD_USER) - FD_SET(server_fd, &select_fds); - if (cstate != STATE_CMD_SERVER) + FD_SET(server_fd, &select_fds); + if (!busy) FD_SET(0, &select_fds); rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); @@ -489,16 +348,16 @@ select_loop(void) die("select: %m"); } + if (FD_ISSET(0, &select_fds)) + { + input_read(); + continue; + } + if (FD_ISSET(server_fd, &select_fds)) { server_read(); - update_state(); - } - - if (FD_ISSET(0, &select_fds)) - { - rl_callback_read_char(); - update_state(); + continue; } } } @@ -556,14 +415,22 @@ server_send(char *cmd) } } + +/* XXXX + + get_term_size(); + + if (tcgetattr(0, &tty_save) != 0) + { + perror("tcgetattr error"); + return(EXIT_FAILURE); + } + } + + */ int main(int argc, char **argv) { -#ifdef HAVE_LIBDMALLOC - if (!getenv("DMALLOC_OPTIONS")) - dmalloc_debug(0x2f03d00); -#endif - interactive = isatty(0); parse_args(argc, argv); cmd_build_tree(); diff --git a/client/client.h b/client/client.h index 64de97ec..b194a772 100644 --- a/client/client.h +++ b/client/client.h @@ -6,15 +6,31 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -/* client.c */ -void cleanup(void); +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); diff --git a/conf/cf-lex.l b/conf/cf-lex.l index c8eae0e8..b1bbeae2 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -15,10 +15,10 @@ * 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_NUMBER for a numeric constant etc.) and class dependent data. - * When an unknown symbol is encountered, it's automatically added to the - * symbol table with class %SYM_VOID. + * 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. @@ -172,7 +172,7 @@ else: { return ELSECOL; } -({ALPHA}{ALNUM}*|[']({ALNUM}|[-])*[']) { +({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.]|[:])*[']) { if(*yytext == '\'') { yytext[yyleng-1] = 0; yytext++; @@ -623,24 +623,23 @@ cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos) char * cf_symbol_class_name(struct symbol *sym) { + if ((sym->class & 0xff00) == SYM_CONSTANT) + return "constant"; + switch (sym->class) { case SYM_VOID: return "undefined"; case SYM_PROTO: return "protocol"; - case SYM_NUMBER: - return "numeric constant"; + case SYM_TEMPLATE: + return "protocol template"; case SYM_FUNCTION: return "function"; case SYM_FILTER: return "filter"; case SYM_TABLE: return "routing table"; - case SYM_IPA: - return "network address"; - case SYM_TEMPLATE: - return "protocol template"; case SYM_ROA: return "ROA table"; default: diff --git a/conf/conf.c b/conf/conf.c index 9375861f..14225d3b 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -21,9 +21,12 @@ * 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 it's non-%NULL and the user wants to reconfigure once again, we just + * 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). + * 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. * * Loading of new configuration is very simple: just call config_alloc() * to get a new &config structure, then use config_parse() to parse a @@ -55,10 +58,23 @@ static jmp_buf conf_jmpbuf; -struct config *config, *new_config, *old_config, *future_config; -static event *config_event; -int shutting_down, future_type; -bird_clock_t boot_time; +struct config *config, *new_config; + +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 @@ -82,8 +98,6 @@ config_alloc(byte *name) c->load_time = now; c->tf_base.fmt1 = c->tf_log.fmt1 = "%d-%m-%Y %T"; - if (!boot_time) - boot_time = now; return c; } @@ -154,7 +168,8 @@ cli_parse(struct config *c) void config_free(struct config *c) { - rfree(c->pool); + if (c) + rfree(c->pool); } void @@ -170,10 +185,7 @@ config_del_obstacle(struct config *c) DBG("+++ deleting obstacle %d\n", c->obstacle_count); c->obstacle_count--; if (!c->obstacle_count) - { - ASSERT(config_event); - ev_schedule(config_event); - } + ev_schedule(config_event); } static int @@ -188,25 +200,50 @@ global_commit(struct config *new, struct config *old) log(L_WARN "Reconfiguration of BGP listening socket not implemented, please restart BIRD."); if (!new->router_id) - new->router_id = old->router_id; - if (new->router_id != old->router_id) - return 1; + { + 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) { - int force_restart, nobs; + if (type == RECONFIG_UNDO) + { + c = old_config; + type = old_cftype; + } + else + config_free(old_config); - DBG("do_commit\n"); old_config = config; - config = new_config = c; + old_cftype = type; + config = c; + + configuring = 1; + if (old_config && !config->shutdown) + log(L_INFO "Reconfiguring"); + + /* This should not be necessary, but it seems there are some + functions that access new_config instead of config */ + new_config = config; + if (old_config) old_config->obstacle_count++; DBG("sysdep_commit\n"); - force_restart = sysdep_commit(c, old_config); + int force_restart = sysdep_commit(c, old_config); DBG("global_commit\n"); force_restart |= global_commit(c, old_config); DBG("rt_commit\n"); @@ -214,38 +251,38 @@ config_do_commit(struct config *c, int type) roa_commit(c, old_config); DBG("protos_commit\n"); protos_commit(c, old_config, force_restart, type); - new_config = NULL; /* Just to be sure nobody uses that now */ + + /* Just to be sure nobody uses that now */ + new_config = NULL; + + int obs = 0; if (old_config) - nobs = --old_config->obstacle_count; - else - nobs = 0; - DBG("do_commit finished with %d obstacles remaining\n", nobs); - return !nobs; + obs = --old_config->obstacle_count; + + DBG("do_commit finished with %d obstacles remaining\n", obs); + return !obs; } static void config_done(void *unused UNUSED) { - struct config *c; + if (config->shutdown) + sysdep_shutdown_done(); - DBG("config_done\n"); - for(;;) + configuring = 0; + if (old_config) + log(L_INFO "Reconfigured"); + + if (future_cftype) { - if (config->shutdown) - sysdep_shutdown_done(); - log(L_INFO "Reconfigured"); - if (old_config) - { - config_free(old_config); - old_config = NULL; - } - if (!future_config) - break; - c = future_config; + 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(c, future_type)) - break; + if (config_do_commit(conf, type)) + config_done(NULL); } } @@ -253,6 +290,7 @@ config_done(void *unused UNUSED) * config_commit - commit a configuration * @c: new configuration * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD) + * @timeout: timeout for undo (or 0 for no timeout) * * When a configuration is parsed and prepared for use, the * config_commit() function starts the process of reconfiguration. @@ -265,6 +303,10 @@ config_done(void *unused UNUSED) * 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 @@ -272,49 +314,147 @@ config_done(void *unused UNUSED) * are accepted. */ int -config_commit(struct config *c, int type) +config_commit(struct config *c, int type, int timeout) { - if (!config) /* First-time configuration */ + if (shutting_down) { - config_do_commit(c, RECONFIG_HARD); - return CONF_DONE; + config_free(c); + return CONF_SHUTDOWN; } - if (old_config) /* Reconfiguration already in progress */ + + undo_available = 1; + if (timeout > 0) + tm_start(config_timer, timeout); + else + tm_stop(config_timer); + + if (configuring) { - if (shutting_down == 2) - { - log(L_INFO "New configuration discarded due to shutdown"); - config_free(c); - return CONF_SHUTDOWN; - } - if (future_config) + if (future_cftype) { log(L_INFO "Queueing new configuration, ignoring the one already queued"); config_free(future_config); } else - log(L_INFO "Queued new configuration"); + log(L_INFO "Queueing new configuration"); + + future_cftype = type; future_config = c; - future_type = type; return CONF_QUEUED; } - if (!shutting_down) - log(L_INFO "Reconfiguring"); - if (config_do_commit(c, type)) { config_done(NULL); return CONF_DONE; } - if (!config_event) + 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) { - config_event = ev_new(&root_pool); - config_event->hook = config_done; + 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; } +extern void cmd_reconfig_undo_notify(void); + +static void +config_timeout(struct timer *t) +{ + 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_event = ev_new(&root_pool); + config_event->hook = config_done; + + config_timer = tm_new(&root_pool); + config_timer->hook = config_timeout; +} + /** * order_shutdown - order BIRD shutdown * @@ -328,15 +468,16 @@ order_shutdown(void) if (shutting_down) return; + log(L_INFO "Shutting down"); c = lp_alloc(config->mem, sizeof(struct config)); memcpy(c, config, sizeof(struct config)); init_list(&c->protos); init_list(&c->tables); c->shutdown = 1; + + config_commit(c, RECONFIG_HARD, 0); shutting_down = 1; - config_commit(c, RECONFIG_HARD); - shutting_down = 2; } /** diff --git a/conf/conf.h b/conf/conf.h index c76832b6..28624294 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -26,6 +26,7 @@ struct config { int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */ char *syslog_name; /* Name used for syslog (NULL -> no syslog) */ struct rtable_config *master_rtc; /* Configuration of master routing table */ + struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */ u32 router_id; /* Our Router ID */ ip_addr listen_bgp_addr; /* Listening BGP socket should use this address */ @@ -54,28 +55,33 @@ struct config { /* 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 */ -extern struct config *old_config; /* Old configuration when reconfiguration is in progress */ -extern struct config *future_config; /* New config held here if recon requested during recon */ - -extern int shutting_down; -extern bird_clock_t boot_time; struct config *config_alloc(byte *name); int config_parse(struct config *); int cli_parse(struct config *); void config_free(struct config *); -int config_commit(struct config *, int type); -#define RECONFIG_HARD 0 -#define RECONFIG_SOFT 1 +int config_commit(struct config *, int type, int timeout); +int config_confirm(void); +int config_undo(void); +void config_init(void); void cf_error(char *msg, ...) NORET; void config_add_obstacle(struct config *); void config_del_obstacle(struct config *); void order_shutdown(void); -#define CONF_DONE 0 -#define CONF_PROGRESS 1 -#define CONF_QUEUED 2 -#define CONF_SHUTDOWN 3 +#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 */ @@ -104,15 +110,17 @@ struct symbol { /* Remember to update cf_symbol_class_name() */ #define SYM_VOID 0 #define SYM_PROTO 1 -#define SYM_NUMBER 2 +#define SYM_TEMPLATE 2 #define SYM_FUNCTION 3 #define SYM_FILTER 4 #define SYM_TABLE 5 -#define SYM_IPA 6 -#define SYM_TEMPLATE 7 -#define SYM_ROA 8 +#define SYM_ROA 6 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ +#define SYM_CONSTANT 0x200 /* 0x200-0x2ff are variable types */ + +#define SYM_TYPE(s) (((struct f_val *) (s)->def)->type) +#define SYM_VAL(s) (((struct f_val *) (s)->def)->val) struct include_file_stack { void *buffer; /* Internal lexer state */ diff --git a/conf/confbase.Y b/conf/confbase.Y index dcb0719f..8b9f206a 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -73,6 +73,7 @@ CF_DECLS %type ipa_scope %type expr bool pxlen +%type expr_us %type