diff options
author | Ondřej Surý <ondrej@debian.org> | 2019-09-16 16:01:02 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@debian.org> | 2019-09-16 16:01:02 +0200 |
commit | c76f25056e140470b62a6494eb29a2e19d06c739 (patch) | |
tree | 69847344945c07dbde5cfe4378b90fc5697cdd5d | |
parent | 35d302e9115c26cc136fcc1b89fa77f73d87978e (diff) | |
download | bird-c76f25056e140470b62a6494eb29a2e19d06c739.tar.gz |
New upstream version 2.0.6
-rw-r--r-- | ChangeLog | 165 | ||||
-rw-r--r-- | NEWS | 7 | ||||
-rwxr-xr-x | configure | 179 | ||||
-rw-r--r-- | configure.ac | 27 | ||||
-rw-r--r-- | doc/bird.pdf | bin | 0 -> 356743 bytes | |||
-rw-r--r-- | doc/bird.sgml | 19 | ||||
-rw-r--r-- | doc/prog.pdf | bin | 0 -> 456876 bytes | |||
-rw-r--r-- | filter/config.Y | 32 | ||||
-rw-r--r-- | filter/f-inst.c | 12 | ||||
-rw-r--r-- | filter/f-util.c | 29 | ||||
-rw-r--r-- | filter/test.conf | 14 | ||||
-rw-r--r-- | lib/birdlib.h | 2 | ||||
-rw-r--r-- | lib/bitops.h | 2 | ||||
-rw-r--r-- | lib/socket.h | 2 | ||||
-rw-r--r-- | nest/a-path.c | 31 | ||||
-rw-r--r-- | nest/attrs.h | 6 | ||||
-rw-r--r-- | nest/config.Y | 11 | ||||
-rw-r--r-- | nest/proto.c | 36 | ||||
-rw-r--r-- | nest/protocol.h | 8 | ||||
-rw-r--r-- | nest/route.h | 1 | ||||
-rw-r--r-- | nest/rt-table.c | 124 | ||||
-rw-r--r-- | proto/bgp/attrs.c | 40 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 33 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 2 | ||||
-rw-r--r-- | proto/bgp/config.Y | 3 | ||||
-rw-r--r-- | proto/bgp/packets.c | 135 | ||||
-rw-r--r-- | proto/ospf/config.Y | 7 | ||||
-rw-r--r-- | proto/radv/config.Y | 15 | ||||
-rw-r--r-- | proto/radv/packets.c | 47 | ||||
-rw-r--r-- | proto/radv/radv.c | 19 | ||||
-rw-r--r-- | proto/radv/radv.h | 10 | ||||
-rw-r--r-- | sysdep/config.h | 2 | ||||
-rw-r--r-- | sysdep/unix/main.c | 3 |
33 files changed, 710 insertions, 313 deletions
@@ -1,3 +1,168 @@ +commit 5235c3f78da15826b0654ba68dc7a897faa42c98 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Sep 10 17:34:41 2019 +0200 + + NEWS and version update + +commit 532471967e6d92ae7a480367cc6d935cca75c7b2 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Sep 10 17:28:06 2019 +0200 + + Doc: Update BGP mask documentation + +commit 452e90ba72f57c44b44f9940ac951d2fde417583 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Sep 10 13:45:18 2019 +0200 + + Filter: Fix crash with 'where' filters and function calls + + The old 'where' code computed size value incorrectly, which leads + to invalid instruction lines and filter errors or crashes. + +commit 1127887a8b111dab18c592f1f3f575920f38bfe3 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Mon Sep 9 13:17:30 2019 +0200 + + BGP: Fix handling of bgp_aggregator atttribute + + The attribute should not be modifiable by filters as we do not + support its type. + +commit 8388f5a7e14108a1458fea35bfbb5a453e2c563c +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Mon Sep 9 03:13:35 2019 +0200 + + BGP: Fix bugs in handling of shutdown messages + + There is an improper check for valid message size, which may lead to + stack overflow and buffer leaks to log when a large message is received. + + Thanks to Daniel McCarney for bugreport and analysis. + +commit 56d8b1e7f6252158caf0ecd3147376b858b16d97 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Thu Aug 29 20:58:16 2019 +0200 + + OSPF: Fix 'show ospf lsadb' cmd without proto arg + + It crashed when used without protocol argument. + + Thanks to Alexander for the bugreport. + +commit 32a254050d3da57ca6d7627aa606a8dce11907ee +Author: Maria Matejka <mq@ucw.cz> +Date: Mon Aug 26 21:53:56 2019 +0200 + + Channel refeed with import table splitting between routes for one prefix + +commit 5ab3447de18235de566eb5116c0aec24050f5f85 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Wed Aug 21 17:30:00 2019 +0200 + + Sysdep: Drop supplementary groups when dropping GID + + We forgot to do that. Oops. + +commit 4fa0e472cf3e8c61a3f67e91d201dbc12ea94221 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Wed Aug 21 17:16:08 2019 +0200 + + BGP: Use reallocation for capability structure + + Instead of having large stack buffer for max amount of AFI/SAFI pairs. + The old code is not correct w.r.t. extendeded option length, as more + AFI/SAFI pairs may fit into the capability option. + +commit 524d2538537b2530bf031daa1d5c8e4653f92c5c +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Aug 20 19:12:59 2019 +0200 + + BGP: Implement extended optional parameters length + + Extends BGP options/capabilities data length to 16bit, to avoid issues + with too many capabilities. See draft-ietf-idr-ext-opt-param-07 + +commit a297a4f044bcc7c38549710a720bc1f97df9ba65 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Aug 13 18:57:40 2019 +0200 + + Nest: Fix crash in route reload when some channels are not up. + + Only channels that are up can be reloaded. + +commit b7d7599ce3576f28310af18b403fed49a0840b67 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Aug 13 18:22:07 2019 +0200 + + BGP: implement Adj-RIB-Out + + The patch implements optional internal export table to a channel and + hooks it to BGP so it can be used as Adj-RIB-Out. When enabled, all + exported (post-filtered) routes are stored there. An export table can be + examined using e.g. 'show route export table bgp1.ipv4'. + +commit dfe63ed84d42178a53b01071c64f23250e74d6d9 +Author: Maria Matejka <mq@ucw.cz> +Date: Tue Aug 13 16:45:27 2019 +0200 + + Filter: Fixing empty block and never-executed-statement bug + +commit 70a4320bdd44122d7a93bc71c77a9d684b3c9adc +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Mon Aug 12 00:41:36 2019 +0200 + + RAdv: Allow solicited RAs to be sent as unicast + + Add option to send solicited router advertisements as unicast directly + to soliciting nodes instead of as multicast to all-nodes group. + +commit 9f3e09832081bc029dc98ae6dda49ee86d138fde +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Aug 6 18:54:19 2019 +0200 + + Filter: Allow to use set constants / expressions in path masks + + Allow to not only use set literals in path masks, but also existing + set constants or set expressions. + +commit ef113c6f725349a2ab52f3cbef18403f82c84134 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Aug 6 16:58:13 2019 +0200 + + Filter: Allow to use sets in path masks + +commit e2b530aa729f9c5973e498b45dd6f55ab669d1ac +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Aug 6 15:29:06 2019 +0200 + + BGP: Improve reconfiguration + + Several BGP channel options (including 'next hop self') could be + reconfigured without session reset, with just route refeed/refresh. + The patch improves reconfiguration code to do it that way. + +commit f6a6a77640a9749c79a91300d130ba6b5941d408 +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Aug 6 15:09:42 2019 +0200 + + BGP: Fix 'deterministic med' to work with 'merge paths' + + The 'deterministic med' option is implemented by suppressing other than + best-in-group routes (grouped by ASN) from best route selection. This + interferes with 'merge paths' as supressed routes are no longer mergable + with best route. This is fixed by suppressing only those routes that are + not mergable with best-in-group route. + +commit 8c42205e35e24537122a4c821bd3128d698abf1b +Author: Ondrej Zajicek (work) <santiago@crfreenet.org> +Date: Tue Aug 6 14:53:02 2019 +0200 + + Configure: CFLAGS update + + - add -flto only to default CFLAGS + - add -fno-strict-aliasing, -fno-strict-overflow always + - remove -Wno-implicit-fallthrough + commit cc02da816fb062c93b4f0d61b0cfa02b673a5e00 Author: Ondrej Zajicek <santiago@crfreenet.org> Date: Thu Aug 1 14:49:03 2019 +0200 @@ -1,3 +1,10 @@ +Version 2.0.6 (2019-09-10) + o RAdv: Solicited unicast RAs + o BGP: Optional Adj-RIB-Out + o BGP: Extended optional parameters length + o Filter: Sets and set expressions in path masks + o Several important bugfixes + Version 2.0.5 (2019-08-01) o OSPF Graceful restart (RFC 3623, RFC 5187) o BGP: Dynamic BGP @@ -654,7 +654,6 @@ CFLAGS CC CONTROL_SOCKET CONFIG_FILE -runstatedir srcdir exedir objdir @@ -680,6 +679,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -764,6 +764,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' @@ -1016,6 +1017,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1153,7 +1163,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1306,6 +1316,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -4047,6 +4058,9 @@ $as_echo "#define USE_PTHREADS 1" >>confdefs.h fi fi +# This is assumed to be necessary for proper BIRD build +CFLAGS="$CFLAGS -fno-strict-aliasing -fno-strict-overflow" + if test "$bird_cflags_default" = yes ; then bird_tmp_cflags="$CFLAGS" @@ -4121,102 +4135,7 @@ $as_echo "$bird_cv_c_option_wno_missing_init" >&6; } CFLAGS="$bird_tmp_cflags" - bird_tmp_cflags="$CFLAGS" - CFLAGS=" -fno-strict-aliasing" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CC supports -fno-strict-aliasing" >&5 -$as_echo_n "checking whether CC supports -fno-strict-aliasing... " >&6; } -if ${bird_cv_c_option_fno_strict_aliasing+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - bird_cv_c_option_fno_strict_aliasing=yes -else - bird_cv_c_option_fno_strict_aliasing=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $bird_cv_c_option_fno_strict_aliasing" >&5 -$as_echo "$bird_cv_c_option_fno_strict_aliasing" >&6; } - - CFLAGS="$bird_tmp_cflags" - - - bird_tmp_cflags="$CFLAGS" - CFLAGS=" -fno-strict-overflow" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CC supports -fno-strict-overflow" >&5 -$as_echo_n "checking whether CC supports -fno-strict-overflow... " >&6; } -if ${bird_cv_c_option_fno_strict_overflow+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - bird_cv_c_option_fno_strict_overflow=yes -else - bird_cv_c_option_fno_strict_overflow=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $bird_cv_c_option_fno_strict_overflow" >&5 -$as_echo "$bird_cv_c_option_fno_strict_overflow" >&6; } - - CFLAGS="$bird_tmp_cflags" - - - CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes -Wno-parentheses" - - if test "$bird_cv_c_option_wno_pointer_sign" = yes ; then - CFLAGS="$CFLAGS -Wno-pointer-sign" - fi - - - if test "$bird_cv_c_option_wno_missing_init" = yes ; then - CFLAGS="$CFLAGS -Wno-missing-field-initializers" - fi - - - if test "$bird_cv_c_option_fno_strict_aliasing" = yes ; then - CFLAGS="$CFLAGS -fno-strict-aliasing" - fi - - - if test "$bird_cv_c_option_fno_strict_overflow" = yes ; then - CFLAGS="$CFLAGS -fno-strict-overflow" - fi - -fi - -if test "$enable_debug" = no; then + if test "$enable_debug" = no; then bird_tmp_cflags="$CFLAGS" bird_tmp_ldflags="$LDFLAGS" @@ -4257,13 +4176,27 @@ $as_echo "$bird_cv_c_lto" >&6; } CFLAGS="$bird_tmp_cflags" LDFLAGS="$bird_tmp_ldflags" -fi + fi + + if test "$bird_cv_c_lto" = yes; then + CFLAGS="$CFLAGS -flto" + LDFLAGS="$LDFLAGS -flto=4" + fi + + CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes -Wno-parentheses" + + if test "$bird_cv_c_option_wno_pointer_sign" = yes ; then + CFLAGS="$CFLAGS -Wno-pointer-sign" + fi + + + if test "$bird_cv_c_option_wno_missing_init" = yes ; then + CFLAGS="$CFLAGS -Wno-missing-field-initializers" + fi -if test "$bird_cv_c_lto" = yes; then - CFLAGS="$CFLAGS -flto" - LDFLAGS="$LDFLAGS -flto=4" fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking CFLAGS" >&5 $as_echo_n "checking CFLAGS... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CFLAGS" >&5 @@ -5901,48 +5834,6 @@ fi fi fi -else - - bird_tmp_cflags="$CFLAGS" - CFLAGS=" -Wno-implicit-fallthrough" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CC supports -Wno-implicit-fallthrough" >&5 -$as_echo_n "checking whether CC supports -Wno-implicit-fallthrough... " >&6; } -if ${bird_cv_c_option_wno_implicit_fallthrough+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - bird_cv_c_option_wno_implicit_fallthrough=yes -else - bird_cv_c_option_wno_implicit_fallthrough=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $bird_cv_c_option_wno_implicit_fallthrough" >&5 -$as_echo "$bird_cv_c_option_wno_implicit_fallthrough" >&6; } - - CFLAGS="$bird_tmp_cflags" - - - if test "$bird_cv_c_option_wno_implicit_fallthrough" = yes ; then - CFLAGS="$CFLAGS -Wno-implicit-fallthrough" - fi - fi CLIENT=birdcl diff --git a/configure.ac b/configure.ac index 14dbcd52..7e8578ab 100644 --- a/configure.ac +++ b/configure.ac @@ -137,27 +137,27 @@ if test "$enable_pthreads" != no ; then fi fi +# This is assumed to be necessary for proper BIRD build +CFLAGS="$CFLAGS -fno-strict-aliasing -fno-strict-overflow" + if test "$bird_cflags_default" = yes ; then BIRD_CHECK_GCC_OPTION([bird_cv_c_option_wno_pointer_sign], [-Wno-pointer-sign], [-Wall]) BIRD_CHECK_GCC_OPTION([bird_cv_c_option_wno_missing_init], [-Wno-missing-field-initializers], [-Wall -Wextra]) - BIRD_CHECK_GCC_OPTION([bird_cv_c_option_fno_strict_aliasing], [-fno-strict-aliasing]) - BIRD_CHECK_GCC_OPTION([bird_cv_c_option_fno_strict_overflow], [-fno-strict-overflow]) + + if test "$enable_debug" = no; then + BIRD_CHECK_LTO + fi + + if test "$bird_cv_c_lto" = yes; then + CFLAGS="$CFLAGS -flto" + LDFLAGS="$LDFLAGS -flto=4" + fi CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes -Wno-parentheses" BIRD_ADD_GCC_OPTION([bird_cv_c_option_wno_pointer_sign], [-Wno-pointer-sign]) BIRD_ADD_GCC_OPTION([bird_cv_c_option_wno_missing_init], [-Wno-missing-field-initializers]) - BIRD_ADD_GCC_OPTION([bird_cv_c_option_fno_strict_aliasing], [-fno-strict-aliasing]) - BIRD_ADD_GCC_OPTION([bird_cv_c_option_fno_strict_overflow], [-fno-strict-overflow]) fi -if test "$enable_debug" = no; then - BIRD_CHECK_LTO -fi - -if test "$bird_cv_c_lto" = yes; then - CFLAGS="$CFLAGS -flto" - LDFLAGS="$LDFLAGS -flto=4" -fi AC_MSG_CHECKING([CFLAGS]) AC_MSG_RESULT([$CFLAGS]) @@ -387,9 +387,6 @@ if test "$enable_debug" = yes ; then AC_CHECK_LIB([efence], [malloc]) fi fi -else - BIRD_CHECK_GCC_OPTION([bird_cv_c_option_wno_implicit_fallthrough], [-Wno-implicit-fallthrough]) - BIRD_ADD_GCC_OPTION([bird_cv_c_option_wno_implicit_fallthrough], [-Wno-implicit-fallthrough]) fi CLIENT=birdcl diff --git a/doc/bird.pdf b/doc/bird.pdf Binary files differnew file mode 100644 index 00000000..113c0ec2 --- /dev/null +++ b/doc/bird.pdf diff --git a/doc/bird.sgml b/doc/bird.sgml index 83dec4f8..cb90e615 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1473,7 +1473,8 @@ foot). but <tt>bgp_path ˜ [= * 4 5 * =]</tt> is false. BGP mask expressions can also contain integer expressions enclosed in parenthesis and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>. You can - also use ranges, for example <tt>[= * 3..5 2 100..200 * =]</tt>. + also use ranges (e.g. <tt>[= * 3..5 2 100..200 * =]</tt>) and sets + (e.g. <tt>[= 1 2 [3, 5, 7] * =]</tt>). <tag><label id="type-clist">clist</tag> Clist is similar to a set, except that unlike other sets, it can be @@ -2705,6 +2706,16 @@ be used in explicit configuration. be examined later by <cf/show route/, and can be used to reconfigure import filters without full route refresh. Default: off. + <tag><label id="bgp-export-table">export table <m/switch/</tag> + A BGP export table contains all routes sent to given BGP neighbor, after + application of export filters. It is also called <em/Adj-RIB-Out/ in BGP + terminology. BIRD BGP by default operates without export tables, in + which case routes from master table are just processed by export filters + and then announced by BGP. Enabling <cf/export table/ allows to store + routes after export filter processing, so they can be examined later by + <cf/show route/, and can be used to eliminate unnecessary updates or + withdraws. Default: off. + <tag><label id="bgp-secondary">secondary <m/switch/</tag> Usually, if an export filter rejects a selected route, no other route is propagated for that network. This option allows to try the next route in @@ -4121,6 +4132,12 @@ definitions, prefix definitions and DNS definitions: The minimum delay between two consecutive router advertisements, in seconds. Default: 3 + <tag><label id="radv-solicited-ra-unicast">solicited ra unicast <m/switch/</tag> + Solicited router advertisements are usually sent to all-nodes multicast + group like unsolicited ones, but the router can be configured to send + them as unicast directly to soliciting nodes instead. This is especially + useful on wireless networks (see <rfc id="7772">). Default: no + <tag><label id="radv-iface-managed">managed <m/switch/</tag> This option specifies whether hosts should use DHCPv6 for IP address configuration. Default: no diff --git a/doc/prog.pdf b/doc/prog.pdf Binary files differnew file mode 100644 index 00000000..3d2ba378 --- /dev/null +++ b/doc/prog.pdf diff --git a/filter/config.Y b/filter/config.Y index 8171a7c2..340053ba 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -446,7 +446,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %nonassoc THEN %nonassoc ELSE -%type <xp> cmds_int +%type <xp> cmds_int cmd_prep %type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail %type <fda> dynamic_attr %type <fsa> static_attr @@ -624,17 +624,25 @@ cmds: /* EMPTY */ { $$ = NULL; } | cmds_int { $$ = $1.begin; } ; -cmds_int: cmd { +cmd_prep: cmd { $$.begin = $$.end = $1; - while ($$.end->next) - $$.end = $$.end->next; + if ($1) + while ($$.end->next) + $$.end = $$.end->next; +} + ; + +cmds_int: cmd_prep + | cmds_int cmd_prep { + if (!$1.begin) + $$ = $2; + else if (!$2.begin) + $$ = $1; + else { + $$.begin = $1.begin; + $$.end = $2.end; + $1.end->next = $2.begin; } - | cmds_int cmd { - $$.begin = $1.begin; - $1.end->next = $2; - $$.end = $2; - while ($$.end->next) - $$.end = $$.end->next; } ; @@ -801,6 +809,10 @@ bgp_path: bgp_path_tail: NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; } | NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; } + | '[' set_items ']' bgp_path_tail { + if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask"); + $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = build_tree($2), .kind = PM_ASN_SET }, }); $$->next = $4; + } | '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); $$->next = $2; } | '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); $$->next = $2; } | bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; } diff --git a/filter/f-inst.c b/filter/f-inst.c index 0867ac4a..49ae993a 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -308,12 +308,24 @@ case T_PATH_MASK_ITEM: pm->item[i] = vv(i).val.pmi; break; + case T_INT: pm->item[i] = (struct f_path_mask_item) { .asn = vv(i).val.i, .kind = PM_ASN, }; break; + + case T_SET: + if (vv(i).val.t->from.type != T_INT) + runtime("Only integer sets allowed in path mask"); + + pm->item[i] = (struct f_path_mask_item) { + .set = vv(i).val.t, + .kind = PM_ASN_SET, + }; + break; + default: runtime( "Error resolving path mask template: value not an integer" ); } diff --git a/filter/f-util.c b/filter/f-util.c index e61949f2..410999a6 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -32,33 +32,12 @@ filter_name(const struct filter *filter) struct filter *f_new_where(struct f_inst *where) { - struct f_inst acc = { - .fi_code = FI_DIE, - .lineno = ifs->lino, - .size = 1, - .i_FI_DIE = { .fret = F_ACCEPT, }, - }; - - struct f_inst rej = { - .fi_code = FI_DIE, - .lineno = ifs->lino, - .size = 1, - .i_FI_DIE = { .fret = F_REJECT, }, - }; - - struct f_inst i = { - .fi_code = FI_CONDITION, - .lineno = ifs->lino, - .size = 3 + where->size, - .i_FI_CONDITION = { - .f1 = where, - .f2 = &acc, - .f3 = &rej, - }, - }; + struct f_inst *cond = f_new_inst(FI_CONDITION, where, + f_new_inst(FI_DIE, F_ACCEPT), + f_new_inst(FI_DIE, F_REJECT)); struct filter *f = cfg_allocz(sizeof(struct filter)); - f->root = f_linearize(&i); + f->root = f_linearize(cond); return f; } diff --git a/filter/test.conf b/filter/test.conf index 9abd76f3..b1342819 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -597,11 +597,15 @@ function mkpath(int a; int b) return [= a b 3 2 1 =]; } +define set35 = [3 .. 5]; + function t_path() bgpmask pm1; bgppath p2; +int set set12; { pm1 = [= 4 3 2 1 =]; + set12 = [1, 2]; bt_assert(format(pm1) = "[= 4 3 2 1 =]"); @@ -626,6 +630,8 @@ bgppath p2; bt_assert(p2 !~ [8, ten..(2*ten)]); bt_assert(p2 ~ [= * 4 3 * 1 =]); bt_assert(p2 ~ [= (3+2) (2*2) 3 2 1 =]); + bt_assert(p2 ~ [= 5 [2, 4, 6] 3 [1..2] 1 =]); + bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]); bt_assert(p2 ~ mkpath(5, 4)); bt_assert(p2.len = 5); @@ -1172,6 +1178,10 @@ bt_test_suite(t_include, "Testing including another config file"); function t_if_else() int i; { + /* Empty blocks regression test */ + if true then {} + else {} + if true then bt_assert(true); @@ -1181,6 +1191,10 @@ int i; bt_assert(true); else bt_assert(false); + + /* Empty blocks regression test */ + if true then {} + else {} } bt_test_suite(t_if_else, "Testing if-else statement"); diff --git a/lib/birdlib.h b/lib/birdlib.h index 30ea433c..5202b0c8 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -38,7 +38,7 @@ struct align_probe { char x; long int y; }; #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) #define BYTES(n) ((((uint) (n)) + 7) / 8) #define CALL(fn, args...) ({ if (fn) fn(args); }) -#define ADVANCE(w, r, l) ({ r -= l; w += l; }) +#define ADVANCE(w, r, l) ({ r -= (l); w += (l); }) static inline int uint_cmp(uint i1, uint i2) { return (int)(i1 > i2) - (int)(i1 < i2); } diff --git a/lib/bitops.h b/lib/bitops.h index af648c26..39ade388 100644 --- a/lib/bitops.h +++ b/lib/bitops.h @@ -29,4 +29,6 @@ static inline u32 u32_hash(u32 v) { return v * 2902958171u; } static inline u8 u32_popcount(u32 v) { return __builtin_popcount(v); } +static inline int uint_is_pow2(uint n) { return n && !(n & (n-1)); } + #endif diff --git a/lib/socket.h b/lib/socket.h index e53ec5ba..03c15dcd 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -97,7 +97,7 @@ void sk_dump_all(void); int sk_is_ipv4(sock *s); /* True if socket is IPv4 */ int sk_is_ipv6(sock *s); /* True if socket is IPv6 */ -static inline int sk_send_buffer_empty(sock *sk) +static inline int sk_tx_buffer_empty(sock *sk) { return sk->tbuf == sk->tpos; } int sk_setup_multicast(sock *s); /* Prepare UDP or IP socket for multicasting */ diff --git a/nest/a-path.c b/nest/a-path.c index a1b7c42f..b6a30f54 100644 --- a/nest/a-path.c +++ b/nest/a-path.c @@ -740,6 +740,31 @@ pm_match(struct pm_pos *pos, u32 asn, u32 asn2) return 0; } +static int +pm_match_set(struct pm_pos *pos, const struct f_tree *set) +{ + struct f_val asn = { .type = T_INT }; + + if (! pos->set) + { + asn.val.i = pos->val.asn; + return !!find_tree(set, &asn); + } + + const u8 *p = pos->val.sp; + int len = *p++; + int i; + + for (i = 0; i < len; i++) + { + asn.val.i = get_as(p + i * BS); + if (find_tree(set, &asn)) + return 1; + } + + return 0; +} + static void pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh) { @@ -824,13 +849,17 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask) val2 = mask->item[m].to; goto step; case PM_QUESTION: + case PM_ASN_SET: step: nh = nl = -1; for (i = h; i >= l; i--) if (pos[i].mark) { pos[i].mark = 0; - if ((mask->item[m].kind == PM_QUESTION) || pm_match(pos + i, val, val2)) + if ((mask->item[m].kind == PM_QUESTION) || + ((mask->item[m].kind != PM_ASN_SET) ? + pm_match(pos + i, val, val2) : + pm_match_set(pos + i, mask->item[m].set))) pm_mark(pos, i, plen, &nl, &nh); } diff --git a/nest/attrs.h b/nest/attrs.h index 4efcff79..6fb0a8fa 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -60,16 +60,18 @@ static inline struct adata *as_path_prepend(struct linpool *pool, const struct a #define PM_ASTERISK 2 #define PM_ASN_EXPR 3 #define PM_ASN_RANGE 4 +#define PM_ASN_SET 5 struct f_path_mask_item { union { u32 asn; /* PM_ASN */ - struct f_line *expr; /* PM_ASN_EXPR */ + const struct f_line *expr; /* PM_ASN_EXPR */ + const struct f_tree *set; /* PM_ASN_SET */ struct { /* PM_ASN_RANGE */ u32 from; u32 to; }; - }; + }; int kind; }; diff --git a/nest/config.Y b/nest/config.Y index e1a932d4..c62501a3 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -567,6 +567,17 @@ r_args: rt_show_add_table($$, c->in_table); $$->tables_defined_by = RSD_TDB_DIRECT; } + | r_args EXPORT TABLE CF_SYM_KNOWN '.' r_args_channel { + cf_assert_symbol($4, SYM_PROTO); + $$ = $1; + struct proto_config *cf = $4->proto; + if (!cf->proto) cf_error("%s is not a protocol", $4->name); + struct channel *c = proto_find_channel_by_name(cf->proto, $6); + if (!c) cf_error("Channel %s.%s not found", $4->name, $6); + if (!c->out_table) cf_error("No export table in channel %s.%s", $4->name, $6); + rt_show_add_table($$, c->out_table); + $$->tables_defined_by = RSD_TDB_DIRECT; + } | r_args FILTER filter { $$ = $1; if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice"); diff --git a/nest/proto.c b/nest/proto.c index 2fb4e96f..6beca56d 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -317,6 +317,13 @@ channel_reset_import(struct channel *c) rt_prune_sync(c->in_table, 1); } +static void +channel_reset_export(struct channel *c) +{ + /* Just free the routes */ + rt_prune_sync(c->out_table, 1); +} + /* Called by protocol to activate in_table */ void channel_setup_in_table(struct channel *c) @@ -331,6 +338,18 @@ channel_setup_in_table(struct channel *c) c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c); } +/* Called by protocol to activate out_table */ +void +channel_setup_out_table(struct channel *c) +{ + struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config)); + cf->name = "export"; + cf->addr_type = c->net_type; + + c->out_table = mb_allocz(c->proto->pool, sizeof(struct rtable)); + rt_setup(c->proto->pool, c->out_table, cf); +} + static void channel_do_start(struct channel *c) @@ -376,6 +395,7 @@ channel_do_down(struct channel *c) c->in_table = NULL; c->reload_event = NULL; + c->out_table = NULL; CALL(c->channel->cleanup, c); @@ -411,6 +431,9 @@ channel_set_state(struct channel *c, uint state) if (c->in_table && (cs == CS_UP)) channel_reset_import(c); + if (c->out_table && (cs == CS_UP)) + channel_reset_export(c); + break; case CS_UP: @@ -433,6 +456,9 @@ channel_set_state(struct channel *c, uint state) if (c->in_table && (cs == CS_UP)) channel_reset_import(c); + if (c->out_table && (cs == CS_UP)) + channel_reset_export(c); + channel_do_flush(c); break; @@ -619,7 +645,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) c->last_tx_filter_change = current_time(); /* Execute channel-specific reconfigure hook */ - if (c->channel->reconfigure && !c->channel->reconfigure(c, cf)) + if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed)) return 0; /* If the channel is not open, it has no routes and we cannot reload it anyways */ @@ -1926,7 +1952,7 @@ proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) /* All channels must support reload */ if (dir != CMD_RELOAD_OUT) WALK_LIST(c, p->channels) - if (!channel_reloadable(c)) + if ((c->channel_state == CS_UP) && !channel_reloadable(c)) { cli_msg(-8006, "%s: reload failed", p->name); return; @@ -1937,12 +1963,14 @@ proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) /* re-importing routes */ if (dir != CMD_RELOAD_OUT) WALK_LIST(c, p->channels) - channel_request_reload(c); + if (c->channel_state == CS_UP) + channel_request_reload(c); /* re-exporting routes */ if (dir != CMD_RELOAD_IN) WALK_LIST(c, p->channels) - channel_request_feeding(c); + if (c->channel_state == CS_UP) + channel_request_feeding(c); cli_msg(-15, "%s: reloading", p->name); } diff --git a/nest/protocol.h b/nest/protocol.h index da12c771..c664c1e6 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -451,7 +451,7 @@ struct channel_class { uint config_size; /* Size of channel config data structure */ void (*init)(struct channel *, struct channel_config *); /* Create new instance */ - int (*reconfigure)(struct channel *, struct channel_config *); /* Try to reconfigure instance, returns success */ + int (*reconfigure)(struct channel *, struct channel_config *, int *import_changed, int *export_changed); /* Try to reconfigure instance, returns success */ int (*start)(struct channel *); /* Start the instance */ void (*shutdown)(struct channel *); /* Stop the instance */ void (*cleanup)(struct channel *); /* Channel finished flush */ @@ -537,8 +537,11 @@ struct channel { struct rtable *in_table; /* Internal table for received routes */ struct event *reload_event; /* Event responsible for reloading from in_table */ - struct fib_iterator reload_fit; /* Iterator in in_table used during reloading */ + struct fib_iterator reload_fit; /* FIB iterator in in_table used during reloading */ + struct rte *reload_next_rte; /* Route iterator in in_table used during reloading */ u8 reload_active; /* Iterator reload_fit is linked */ + + struct rtable *out_table; /* Internal table for exported routes */ }; @@ -607,6 +610,7 @@ int proto_configure_channel(struct proto *p, struct channel **c, struct channel_ void channel_set_state(struct channel *c, uint state); void channel_setup_in_table(struct channel *c); +void channel_setup_out_table(struct channel *c); void channel_schedule_reload(struct channel *c); static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); } diff --git a/nest/route.h b/nest/route.h index 8d799454..f4f32ce0 100644 --- a/nest/route.h +++ b/nest/route.h @@ -320,6 +320,7 @@ int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src int rt_reload_channel(struct channel *c); void rt_reload_channel_abort(struct channel *c); void rt_prune_sync(rtable *t, int all); +int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed); struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); diff --git a/nest/rt-table.c b/nest/rt-table.c index 53f5d979..318ec2ee 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -633,7 +633,6 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed) struct proto *p = c->proto; struct proto_stats *stats = &c->stats; - /* * First, apply export limit. * @@ -679,6 +678,8 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed) } } + if (c->out_table && !rte_update_out(c, net->n.addr, new, old, refeed)) + return; if (new) stats->exp_updates_accepted++; @@ -2507,6 +2508,10 @@ rt_feed_channel_abort(struct channel *c) } +/* + * Import table + */ + int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) { @@ -2548,6 +2553,10 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr goto drop_update; } + /* Move iterator if needed */ + if (old == c->reload_next_rte) + c->reload_next_rte = old->next; + /* Remove the old rte */ *pos = old->next; rte_free_quick(old); @@ -2615,21 +2624,32 @@ rt_reload_channel(struct channel *c) c->reload_active = 1; } - FIB_ITERATE_START(&tab->fib, fit, net, n) - { - if (max_feed <= 0) + do { + for (rte *e = c->reload_next_rte; e; e = e->next) { - FIB_ITERATE_PUT(fit); - return 0; + if (max_feed-- <= 0) + { + c->reload_next_rte = e; + debug("%s channel reload burst split (max_feed=%d)", c->proto->name, max_feed); + return 0; + } + + rte_update2(c, e->net->n.addr, rte_do_cow(e), e->attrs->src); } - for (rte *e = n->routes; e; e = e->next) + c->reload_next_rte = NULL; + + FIB_ITERATE_START(&tab->fib, fit, net, n) { - rte_update2(c, n->n.addr, rte_do_cow(e), e->attrs->src); - max_feed--; + if (c->reload_next_rte = n->routes) + { + FIB_ITERATE_PUT_NEXT(fit, &tab->fib); + break; + } } + FIB_ITERATE_END; } - FIB_ITERATE_END; + while (c->reload_next_rte); c->reload_active = 0; return 1; @@ -2642,6 +2662,7 @@ rt_reload_channel_abort(struct channel *c) { /* Unlink the iterator */ fit_get(&c->in_table->fib, &c->reload_fit); + c->reload_next_rte = NULL; c->reload_active = 0; } } @@ -2668,6 +2689,89 @@ rt_prune_sync(rtable *t, int all) } +/* + * Export table + */ + +int +rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed) +{ + struct rtable *tab = c->out_table; + struct rte_src *src; + rte *old, **pos; + net *net; + + if (new) + { + net = net_get(tab, n); + src = new->attrs->src; + } + else + { + net = net_find(tab, n); + src = old0->attrs->src; + + if (!net) + goto drop_withdraw; + } + + /* Find the old rte */ + for (pos = &net->routes; old = *pos; pos = &old->next) + if (old->attrs->src == src) + { + if (new && rte_same(old, new)) + { + /* REF_STALE / REF_DISCARD not used in export table */ + /* + if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY)) + { + old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY); + return 1; + } + */ + + goto drop_update; + } + + /* Remove the old rte */ + *pos = old->next; + rte_free_quick(old); + tab->rt_count--; + + break; + } + + if (!new) + { + if (!old) + goto drop_withdraw; + + return 1; + } + + /* Insert the new rte */ + rte *e = rte_do_cow(new); + e->flags |= REF_COW; + e->net = net; + e->sender = c; + e->lastmod = current_time(); + e->next = *pos; + *pos = e; + tab->rt_count++; + return 1; + +drop_update: + return refeed; + +drop_withdraw: + return 0; +} + + +/* + * Hostcache + */ + static inline u32 hc_hash(ip_addr a, rtable *dep) { diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 69c4b172..886ee6ba 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1747,7 +1747,7 @@ bgp_rte_mergable(rte *pri, rte *sec) return 0; /* RFC 4271 9.1.2.1. Route resolvability test */ - if (!rte_resolvable(sec)) + if (rte_resolvable(pri) != rte_resolvable(sec)) return 0; /* LLGR draft - depreference stale routes */ @@ -1833,7 +1833,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best) rte *key = new ? new : old; u32 lpref = key->pref; u32 lasn = bgp_get_neighbor(key); - int old_is_group_best = 0; + int old_suppressed = old ? old->u.bgp.suppressed : 0; /* * Proper RFC 4271 path selection is a bit complicated, it cannot be @@ -1880,7 +1880,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best) * We could find the best-in-group and then make some shortcuts like * in rte_recalculate, but as we would have to walk through all * net->routes just to find it, it is probably not worth. So we - * just have two simpler fast cases that use just the old route. + * just have one simple fast case that use just the old route. * We also set suppressed flag to avoid using it in bgp_rte_better(). */ @@ -1889,23 +1889,11 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best) if (old) { - old_is_group_best = !old->u.bgp.suppressed; old->u.bgp.suppressed = 1; - int new_is_better = new && bgp_rte_better(new, old); - /* The first case - replace not best with worse (or remove not best) */ - if (!old_is_group_best && !new_is_better) + /* The fast case - replace not best with worse (or remove not best) */ + if (old_suppressed && !(new && bgp_rte_better(new, old))) return 0; - - /* The second case - replace the best with better */ - if (old_is_group_best && new_is_better) - { - /* new is best-in-group, the see discussion below - this is - a special variant of NBG && OBG. From OBG we can deduce - that same_group(old_best) iff (old == old_best) */ - new->u.bgp.suppressed = 0; - return (old == old_best); - } } /* The default case - find a new best-in-group route */ @@ -1922,6 +1910,16 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best) if (!r) return 0; + /* Found if new is mergable with best-in-group */ + if (new && (new != r) && bgp_rte_mergable(r, new)) + new->u.bgp.suppressed = 0; + + /* Found all existing routes mergable with best-in-group */ + for (s=net->routes; rte_is_valid(s); s=s->next) + if (use_deterministic_med(s) && same_group(s, lpref, lasn)) + if ((s != r) && bgp_rte_mergable(r, s)) + s->u.bgp.suppressed = 0; + /* Found best-in-group */ r->u.bgp.suppressed = 0; @@ -1935,9 +1933,9 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best) * rte_recalculate() without ignore that possibility). * * There are three possible cases according to whether the old route - * was the best in group (OBG, stored in old_is_group_best) and - * whether the new route is the best in group (NBG, tested by r == new). - * These cases work even if old or new is NULL. + * was the best in group (OBG, i.e. !old_suppressed) and whether the + * new route is the best in group (NBG, tested by r == new). These + * cases work even if old or new is NULL. * * NBG -> new is a possible candidate for the best route, so we just * check for the first reason using same_group(). @@ -1952,7 +1950,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best) if (r == new) return old_best && same_group(old_best, lpref, lasn); else - return old_is_group_best; + return !old_suppressed; } struct rte * diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 5a403b40..b26e5e87 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -100,6 +100,7 @@ * RFC 8203 - BGP Administrative Shutdown Communication * RFC 8212 - Default EBGP Route Propagation Behavior without Policies * draft-ietf-idr-bgp-extended-messages-27 + * draft-ietf-idr-ext-opt-param-07 * draft-uttaro-idr-bgp-persistence-04 */ @@ -1721,6 +1722,9 @@ bgp_channel_start(struct channel *C) if (c->cf->import_table) channel_setup_in_table(C); + if (c->cf->export_table) + channel_setup_out_table(C); + c->stale_timer = tm_new_init(c->pool, bgp_long_lived_stale_timeout, c, 0, 0); c->next_hop_addr = c->cf->next_hop_addr; @@ -2054,23 +2058,36 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF) #define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL ) static int -bgp_channel_reconfigure(struct channel *C, struct channel_config *CC) +bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *import_changed, int *export_changed) { struct bgp_channel *c = (void *) C; struct bgp_channel_config *new = (void *) CC; struct bgp_channel_config *old = c->cf; - if (memcmp(((byte *) old) + sizeof(struct channel_config), - ((byte *) new) + sizeof(struct channel_config), - /* Remaining items must be checked separately */ - OFFSETOF(struct bgp_channel_config, rest) - sizeof(struct channel_config))) + if ((new->secondary != old->secondary) || + (new->gr_able != old->gr_able) || + (new->llgr_able != old->llgr_able) || + (new->llgr_time != old->llgr_time) || + (new->ext_next_hop != old->ext_next_hop) || + (new->add_path != old->add_path) || + (new->import_table != old->import_table) || + (new->export_table != old->export_table) || + (IGP_TABLE(new, ip4) != IGP_TABLE(old, ip4)) || + (IGP_TABLE(new, ip6) != IGP_TABLE(old, ip6))) return 0; - /* Check change in IGP tables */ - if ((IGP_TABLE(old, ip4) != IGP_TABLE(new, ip4)) || - (IGP_TABLE(old, ip6) != IGP_TABLE(new, ip6))) + if (new->mandatory && !old->mandatory && (C->channel_state != CS_UP)) return 0; + if (new->gw_mode != old->gw_mode) + *import_changed = 1; + + if (!ipa_equal(new->next_hop_addr, old->next_hop_addr) || + (new->next_hop_self != old->next_hop_self) || + (new->next_hop_keep != old->next_hop_keep) || + (new->missing_lladdr != old->missing_lladdr)) + *export_changed = 1; + c->cf = new; return 1; } diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 075e1bb9..f9a19d53 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -150,8 +150,8 @@ struct bgp_channel_config { u8 ext_next_hop; /* Allow both IPv4 and IPv6 next hops */ u8 add_path; /* Use ADD-PATH extension [RFC 7911] */ u8 import_table; /* Use c.in_table as Adj-RIB-In */ + u8 export_table; /* Use c.out_table as Adj-RIB-Out */ - uint rest[0]; /* Remaining items are reconfigured separately */ struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */ struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */ }; diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index bbc7d9a4..8222024d 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -255,6 +255,7 @@ bgp_channel_item: | ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; } | ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; } | IMPORT TABLE bool { BGP_CC->import_table = $3; } + | EXPORT TABLE bool { BGP_CC->export_table = $3; } | IGP TABLE rtable { if (BGP_CC->desc->no_igp) cf_error("IGP table not allowed here"); @@ -302,7 +303,7 @@ dynamic_attr: BGP_LOCAL_PREF dynamic_attr: BGP_ATOMIC_AGGR { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); } ; dynamic_attr: BGP_AGGREGATOR - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); } ; dynamic_attr: BGP_COMMUNITY { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); } ; dynamic_attr: BGP_ORIGINATOR_ID diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index daa88630..4632e4ad 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -185,14 +185,20 @@ bgp_find_af_caps(struct bgp_caps *caps, u32 afi) } static struct bgp_af_caps * -bgp_get_af_caps(struct bgp_caps *caps, u32 afi) +bgp_get_af_caps(struct bgp_caps **pcaps, u32 afi) { + struct bgp_caps *caps = *pcaps; struct bgp_af_caps *ac; WALK_AF_CAPS(caps, ac) if (ac->afi == afi) return ac; + uint n = caps->af_count; + if (uint_is_pow2(n)) + *pcaps = caps = mb_realloc(caps, sizeof(struct bgp_caps) + + (2 * n) * sizeof(struct bgp_af_caps)); + ac = &caps->af_data[caps->af_count++]; memset(ac, 0, sizeof(struct bgp_af_caps)); ac->afi = afi; @@ -294,8 +300,8 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf) /* * Note that max length is ~ 22+21*af_count. With max 12 channels that is - * 274. Option limit is 253 and buffer size is 4096, so we cannot overflow - * unless we add new capabilities or more AFs. XXXXX + * 274. We are limited just by buffer size (4096, minus header), as we support + * extended optional parameres. Therefore, we have enough space for expansion. */ WALK_AF_CAPS(caps, ac) @@ -411,14 +417,23 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf) return buf; } -static void -bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, int len) +static int +bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len) { struct bgp_proto *p = conn->bgp; + struct bgp_caps *caps; struct bgp_af_caps *ac; int i, cl; u32 af; + if (!conn->remote_caps) + caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps)); + else + { + caps = conn->remote_caps; + conn->remote_caps = NULL; + } + caps->length += len; while (len > 0) @@ -437,7 +452,7 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i goto err; af = get_af4(pos+2); - ac = bgp_get_af_caps(caps, af); + ac = bgp_get_af_caps(&caps, af); ac->ready = 1; break; @@ -460,7 +475,7 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i continue; af = get_af4(pos+2+i); - ac = bgp_get_af_caps(caps, af); + ac = bgp_get_af_caps(&caps, af); ac->ext_next_hop = 1; } break; @@ -490,7 +505,7 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i for (i = 2; i < cl; i += 4) { af = get_af3(pos+2+i); - ac = bgp_get_af_caps(caps, af); + ac = bgp_get_af_caps(&caps, af); ac->gr_able = 1; ac->gr_af_flags = pos[2+i+3]; } @@ -522,7 +537,7 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i for (i = 0; i < cl; i += 4) { af = get_af3(pos+2+i); - ac = bgp_get_af_caps(caps, af); + ac = bgp_get_af_caps(&caps, af); ac->add_path = pos[2+i+3]; } break; @@ -551,7 +566,7 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i for (i = 0; i < cl; i += 7) { af = get_af3(pos+2+i); - ac = bgp_get_af_caps(caps, af); + ac = bgp_get_af_caps(&caps, af); ac->llgr_able = 1; ac->llgr_flags = pos[2+i+3]; ac->llgr_time = get_u24(pos + 2+i+4); @@ -577,11 +592,13 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i } } - return; + conn->remote_caps = caps; + return 0; err: + mb_free(caps); bgp_error(conn, 2, 0, NULL, 0); - return; + return -1; } static int @@ -621,43 +638,68 @@ bgp_check_capabilities(struct bgp_conn *conn) } static int -bgp_read_options(struct bgp_conn *conn, byte *pos, int len) +bgp_read_options(struct bgp_conn *conn, byte *pos, uint len, uint rest) { struct bgp_proto *p = conn->bgp; - struct bgp_caps *caps; - int ol; + int ext = 0; - /* Max number of announced AFIs is limited by max option length (255) */ - caps = alloca(sizeof(struct bgp_caps) + 64 * sizeof(struct bgp_af_caps)); - memset(caps, 0, sizeof(struct bgp_caps)); + /* Handle extended length (draft-ietf-idr-ext-opt-param-07) */ + if ((len > 0) && (rest > 0) && (pos[0] == 255)) + { + if (rest < 3) + goto err; + + /* Update pos/len to describe optional data */ + len = get_u16(pos+1); + ext = 1; + pos += 3; + rest -= 3; + } + + /* Verify that optional data fits into OPEN packet */ + if (len > rest) + goto err; + + /* Length of option parameter header */ + uint hlen = ext ? 3 : 2; while (len > 0) { - if ((len < 2) || (len < (2 + pos[1]))) - { bgp_error(conn, 2, 0, NULL, 0); return -1; } + if (len < hlen) + goto err; + + uint otype = get_u8(pos); + uint olen = ext ? get_u16(pos+1) : get_u8(pos+1); + + if (len < (hlen + olen)) + goto err; - ol = pos[1]; - if (pos[0] == 2) + if (otype == 2) { /* BGP capabilities, RFC 5492 */ if (p->cf->capabilities) - bgp_read_capabilities(conn, caps, pos + 2, ol); + if (bgp_read_capabilities(conn, pos + hlen, olen) < 0) + return -1; } else { /* Unknown option */ - bgp_error(conn, 2, 4, pos, ol); /* FIXME: ol or ol+2 ? */ + bgp_error(conn, 2, 4, pos, hlen + olen); return -1; } - ADVANCE(pos, len, 2 + ol); + ADVANCE(pos, len, hlen + olen); } - uint n = sizeof(struct bgp_caps) + caps->af_count * sizeof(struct bgp_af_caps); - conn->remote_caps = mb_allocz(p->p.pool, n); - memcpy(conn->remote_caps, caps, n); + /* Prepare empty caps if no capability option was announced */ + if (!conn->remote_caps) + conn->remote_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps)); return 0; + +err: + bgp_error(conn, 2, 0, NULL, 0); + return -1; } static byte * @@ -676,12 +718,29 @@ bgp_create_open(struct bgp_conn *conn, byte *buf) if (p->cf->capabilities) { /* Prepare local_caps and write capabilities to buffer */ - byte *end = bgp_write_capabilities(conn, buf+12); - uint len = end - (buf+12); + byte *pos = buf+12; + byte *end = bgp_write_capabilities(conn, pos); + uint len = end - pos; - buf[9] = len + 2; /* Optional parameters length */ - buf[10] = 2; /* Option 2: Capability list */ - buf[11] = len; /* Option data length */ + if (len < 254) + { + buf[9] = len + 2; /* Optional parameters length */ + buf[10] = 2; /* Option 2: Capability list */ + buf[11] = len; /* Option data length */ + } + else /* draft-ietf-idr-ext-opt-param-07 */ + { + /* Move capabilities 4 B forward */ + memmove(buf + 16, pos, len); + pos = buf + 16; + end = pos + len; + + buf[9] = 255; /* Non-ext OP length, fake */ + buf[10] = 255; /* Non-ext OP type, signals extended length */ + put_u16(buf+11, len + 3); /* Extended optional parameters length */ + buf[13] = 2; /* Option 2: Capability list */ + put_u16(buf+14, len); /* Option extended data length */ + } return end; } @@ -705,8 +764,8 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len) if (conn->state != BS_OPENSENT) { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; } - /* Check message contents */ - if (len < 29 || len != 29 + (uint) pkt[28]) + /* Check message length */ + if (len < 29) { bgp_error(conn, 1, 2, pkt+16, 2); return; } if (pkt[19] != BGP_VERSION) @@ -717,7 +776,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len) id = get_u32(pkt+24); BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%R)", asn, hold, id); - if (bgp_read_options(conn, pkt+29, pkt[28]) < 0) + if (bgp_read_options(conn, pkt+29, pkt[28], len-29) < 0) return; if (hold > 0 && hold < 3) @@ -2900,7 +2959,7 @@ bgp_handle_message(struct bgp_proto *p, byte *data, uint len, byte **bp) return 1; /* Handle proper message */ - if ((msg_len > 255) && (msg_len + 1 > len)) + if (msg_len + 1 > len) return 0; /* Some elementary cleanup */ @@ -2916,7 +2975,7 @@ bgp_handle_message(struct bgp_proto *p, byte *data, uint len, byte **bp) void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, uint code, uint subcode, byte *data, uint len) { - byte argbuf[256], *t = argbuf; + byte argbuf[256+16], *t = argbuf; uint i; /* Don't report Cease messages generated by myself */ diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 2ec8c0b6..f631a649 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -537,7 +537,12 @@ CF_CLI(SHOW OSPF STATE ALL, optproto opttext, [<name>], [[Show information about CF_CLI_HELP(SHOW OSPF LSADB, ..., [[Show content of OSPF LSA database]]); CF_CLI(SHOW OSPF LSADB, lsadb_args, [global | area <id> | link] [type <num>] [lsid <id>] [self | router <id>] [<proto>], [[Show content of OSPF LSA database]]) -{ ospf_sh_lsadb($4); }; +{ + if (!$4->proto) + $4->proto = (struct ospf_proto *) proto_get_named(NULL, &proto_ospf); + + ospf_sh_lsadb($4); +}; lsadb_args: /* empty */ { diff --git a/proto/radv/config.Y b/proto/radv/config.Y index 53715f77..dda9cfcd 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -1,6 +1,8 @@ /* * BIRD -- Router Advertisement Configuration * + * (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2011--2019 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -26,12 +28,12 @@ static u8 radv_mult_val; /* Used by radv_mult for second return value */ CF_DECLS -CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL, - MANAGED, OTHER, CONFIG, LINGER, LINK, MTU, REACHABLE, TIME, RETRANS, - TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT, - LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, - LOCAL, TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH, PROPAGATE, - ROUTE, ROUTES, RA_PREFERENCE, RA_LIFETIME) +CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL, SOLICITED, + UNICAST, MANAGED, OTHER, CONFIG, LINGER, LINK, MTU, REACHABLE, TIME, + RETRANS, TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT, + LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, LOCAL, + TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH, PROPAGATE, ROUTE, + ROUTES, RA_PREFERENCE, RA_LIFETIME) CF_ENUM(T_ENUM_RA_PREFERENCE, RA_PREF_, LOW, MEDIUM, HIGH) @@ -98,6 +100,7 @@ radv_iface_item: MIN RA INTERVAL expr { RADV_IFACE->min_ra_int = $4; if ($4 < 3) cf_error("Min RA interval must be at least 3"); } | MAX RA INTERVAL expr { RADV_IFACE->max_ra_int = $4; if (($4 < 4) || ($4 > 1800)) cf_error("Max RA interval must be in range 4-1800"); } | MIN DELAY expr { RADV_IFACE->min_delay = $3; if ($3 <= 0) cf_error("Min delay must be positive"); } + | SOLICITED RA UNICAST bool { RADV_IFACE->solicited_ra_unicast = $4; } | MANAGED bool { RADV_IFACE->managed = $2; } | OTHER CONFIG bool { RADV_IFACE->other_config = $3; } | LINK MTU expr { RADV_IFACE->link_mtu = $3; } diff --git a/proto/radv/packets.c b/proto/radv/packets.c index b12d3a12..3139d321 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -1,6 +1,8 @@ /* * BIRD -- RAdv Packet Processing * + * (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2011--2019 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -370,19 +372,49 @@ radv_prepare_ra(struct radv_iface *ifa) void -radv_send_ra(struct radv_iface *ifa) +radv_send_ra(struct radv_iface *ifa, ip_addr to) { struct radv_proto *p = ifa->ra; + /* TX queue is already full */ + if (!sk_tx_buffer_empty(ifa->sk)) + return; + + if (ifa->valid_time <= current_time()) + radv_invalidate(ifa); + /* We store prepared RA in tbuf */ if (!ifa->plen) radv_prepare_ra(ifa); - RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name); - sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0); + if (ipa_zero(to)) + { + to = IP6_ALL_NODES; + RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name); + } + else + { + RADV_TRACE(D_PACKETS, "Sending RA to %I via %s", to, ifa->iface->name); + } + + int done = sk_send_to(ifa->sk, ifa->plen, to, 0); + if (!done) + log(L_WARN "%s: TX queue full on %s", p->p.name, ifa->iface->name); } +static void +radv_receive_rs(struct radv_proto *p, struct radv_iface *ifa, ip_addr from) +{ + RADV_TRACE(D_PACKETS, "Received RS from %I via %s", + from, ifa->iface->name); + + if (ifa->cf->solicited_ra_unicast && ipa_nonzero(from)) + radv_send_ra(ifa, from); + else + radv_iface_notify(ifa, RA_EV_RS); +} + static int radv_rx_hook(sock *sk, uint size) { @@ -410,9 +442,7 @@ radv_rx_hook(sock *sk, uint size) switch (buf[0]) { case ICMPV6_RS: - RADV_TRACE(D_PACKETS, "Received RS from %I via %s", - sk->faddr, ifa->iface->name); - radv_iface_notify(ifa, RA_EV_RS); + radv_receive_rs(p, ifa, sk->faddr); return 1; case ICMPV6_RA: @@ -430,7 +460,10 @@ static void radv_tx_hook(sock *sk) { struct radv_iface *ifa = sk->data; - log(L_WARN "%s: TX hook called", ifa->ra->p.name); + log(L_INFO "%s: TX queue ready on %s", ifa->ra->p.name, ifa->iface->name); + + /* Some RAs may be missed due to full TX queue */ + radv_iface_notify(ifa, RA_EV_RS); } static void diff --git a/proto/radv/radv.c b/proto/radv/radv.c index 990b6024..622b3c3c 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -1,6 +1,8 @@ /* * BIRD -- Router Advertisement * + * (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2011--2019 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -35,18 +37,14 @@ * case of the specified trigger prefix was changed. * * Supported standards: - * - RFC 4861 - main RA standard - * - RFC 4191 - Default Router Preferences and More-Specific Routes - * - RFC 6106 - DNS extensions (RDDNS, DNSSL) + * RFC 4861 - main RA standard + * RFC 4191 - Default Router Preferences and More-Specific Routes + * RFC 6106 - DNS extensions (RDDNS, DNSSL) */ static void radv_prune_prefixes(struct radv_iface *ifa); static void radv_prune_routes(struct radv_proto *p); -/* Invalidate cached RA packet */ -static inline void radv_invalidate(struct radv_iface *ifa) -{ ifa->plen = 0; } - static void radv_timer(timer *tm) { @@ -56,16 +54,13 @@ radv_timer(timer *tm) RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name); - if (ifa->valid_time <= now) - radv_invalidate(ifa); - if (ifa->prune_time <= now) radv_prune_prefixes(ifa); if (p->prune_time <= now) radv_prune_routes(p); - radv_send_ra(ifa); + radv_send_ra(ifa, IPA_NONE); /* Update timer */ ifa->last = now; @@ -627,7 +622,7 @@ radv_iface_shutdown(struct radv_iface *ifa) if (ifa->sk) { radv_invalidate(ifa); - radv_send_ra(ifa); + radv_send_ra(ifa, IPA_NONE); } } diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 719948d5..2c8ad7d4 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -1,6 +1,8 @@ /* * BIRD -- Router Advertisement * + * (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2011--2019 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -66,6 +68,8 @@ struct radv_iface_config u32 max_ra_int; u32 min_delay; + u8 solicited_ra_unicast; /* Send solicited RAs as unicast */ + u32 prefix_linger_time; /* How long we advertise dead prefixes with lifetime 0 */ u32 route_linger_time; /* How long we advertise dead routes with lifetime 0 */ @@ -204,12 +208,16 @@ struct radv_iface log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0) +/* Invalidate cached RA packet */ +static inline void radv_invalidate(struct radv_iface *ifa) +{ ifa->plen = 0; } + /* radv.c */ void radv_iface_notify(struct radv_iface *ifa, int event); /* packets.c */ int radv_process_domain(struct radv_dnssl_config *cf); -void radv_send_ra(struct radv_iface *ifa); +void radv_send_ra(struct radv_iface *ifa, ip_addr to); int radv_sk_open(struct radv_iface *ifa); diff --git a/sysdep/config.h b/sysdep/config.h index 7165fb43..4ca01297 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -13,7 +13,7 @@ #ifdef GIT_LABEL #define BIRD_VERSION XSTR1(GIT_LABEL) #else -#define BIRD_VERSION "2.0.5" +#define BIRD_VERSION "2.0.6" #endif /* Include parameters determined by configure script */ diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 05becbe7..db848033 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -83,6 +83,9 @@ drop_gid(gid_t gid) { if (setgid(gid) < 0) die("setgid: %m"); + + if (setgroups(0, NULL) < 0) + die("setgroups: %m"); } /* |