Merge tag 'v1.6.2' into int-new

This commit is contained in:
Ondrej Zajicek (work) 2016-11-08 17:03:31 +01:00
commit cc5b93f72d
40 changed files with 988 additions and 338 deletions

View file

@ -1 +0,0 @@
bird.conf

12
.gitignore vendored Normal file
View file

@ -0,0 +1,12 @@
/autom4te.cache/
/doc/*.html
/obj/
/Makefile
/bird
/birdc
/birdcl
/config.log
/config.status
/configure
/bird.conf
/bird.log

16
NEWS
View file

@ -1,3 +1,19 @@
Version 1.6.2 (2016-09-29)
o Fixes serious bug introduced in the previous version
Version 1.6.1 (2016-09-22)
o Support for IPv6 ECMP
o Better handling of IPv6 tentative addresses
o Several updates and fixes in Babel protocol
o Filter: New !~ operator
o Filter: ASN ranges in bgpmask
o KRT: New kernel protocol option 'metric'
o KRT: New route attribute 'krt_scope'
o Improved BIRD help messages
o Fixes memory leak in BGP multipath
o Fixes handling of empty path segments in BGP AS_PATH
o Several bug fixes
Version 1.6.0 (2016-04-29) Version 1.6.0 (2016-04-29)
o Major RIP protocol redesign o Major RIP protocol redesign
o New Babel routing protocol o New Babel routing protocol

156
README
View file

@ -1,82 +1,114 @@
BIRD Internet Routing Daemon BIRD Internet Routing Daemon
Home page http://bird.network.cz/
Mailing list bird-users@network.cz
(c) 1998--2008 Martin Mares <mj@ucw.cz> (c) 1998--2008 Martin Mares <mj@ucw.cz>
(c) 1998--2000 Pavel Machek <pavel@ucw.cz> (c) 1998--2000 Pavel Machek <pavel@ucw.cz>
(c) 1998--2008 Ondrej Filip <feela@network.cz> (c) 1998--2008 Ondrej Filip <feela@network.cz>
(c) 2009--2013 CZ.NIC z.s.p.o. (c) 2009--2016 CZ.NIC z.s.p.o.
================================================================================ ================================================================================
The BIRD project is an attempt to create a routing daemon running on UNIX-like The BIRD project aims to develop a dynamic IP routing daemon with full support
systems (but not necessarily limited to them) with full support of all modern of all modern routing protocols, easy to use configuration interface and
routing protocols, easy to use configuration interface and powerful route powerful route filtering language, primarily targeted on (but not limited to)
filtering language. Linux and other UNIX-like systems and distributed under the GNU General
Public License.
What do we support
==================
o Both IPv4 and IPv6 (use --enable-ipv6 when configuring)
o Multiple routing tables
o Border Gateway Protocol (BGPv4)
o Routing Information Protocol (RIPv2, RIPng)
o Open Shortest Path First protocol (OSPFv2, OSPFv3)
o Babel Routing Protocol (Babel)
o Bidirectional Forwarding Detection (BFD)
o IPv6 router advertisements
o Static routes
o Inter-table protocol
o Command-line interface allowing on-line control and inspection of
status of the daemon
o Soft reconfiguration, no need to use complex online commands to
change the configuration, just edit the configuration file and notify
BIRD to re-read it and it will smoothly switch itself to the new
configuration, not disturbing routing protocols unless they are
affected by the configuration changes
o Powerful language for route filtering, see doc/bird.conf.example
o Linux, FreeBSD, NetBSD and OpenBSD ports
How to install BIRD
===================
o From standard distribution package of your OS (recommended)
o From official binary packages for Debian and Red Hat Linux
ftp://bird.network.cz/pub/bird/debian/
ftp://bird.network.cz/pub/bird/redhat/
o From source code of the latest stable release version
ftp://bird.network.cz/pub/bird/
o From source code of the actual development version
git://git.nic.cz/bird.git
https://gitlab.labs.nic.cz/labs/bird/
How to install BIRD from source code
------------------------------------
$ ./configure
$ make
$ su
# make install
# vi /usr/local/etc/bird.conf
See the file INSTALL for more information about installation from source code.
Documentation
=============
Online documentation is available at http://bird.network.cz/?get_doc or as HTML
files in the doc directory, you can install it by `make install-docs' and
rebuild it by `make docs', but you'll need SGMLtools and LaTeX to be installed
on your machine. You can also download a neatly formatted PostScript version as
a separate archive (bird-doc-*.tar.gz) from ftp://bird.network.cz/pub/bird/
User support
============
If you want to help us debugging, enhancing and porting BIRD or just lurk If you want to help us debugging, enhancing and porting BIRD or just lurk
around to see what's going to develop from this strange creature, feel free around to see what's going to develop, feel free to subscribe to the BIRD
to subscribe to the BIRD users mailing list (bird-users@bird.network.cz), users mailing list bird-users@network.cz, just send `subscribe' to
send subscribes to majordomo at the same machine). Bug reports, suggestions, bird-request@network.cz. Bug reports, suggestions, feature requests and
feature requests (: and code :) are welcome. code are welcome! We don't use gitlab issues for reporting, sorry.
You can download the latest version from ftp://bird.network.cz/pub/bird/ Subscribe: http://bird.network.cz/mailman/listinfo/bird-users/
and look at the BIRD home page at http://bird.network.cz/. Archive: http://bird.network.cz/pipermail/bird-users/
Licence
=======
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
History
=======
BIRD development started as a student project at the Faculty of Math BIRD development started as a student project at the Faculty of Math
and Physics, Charles University, Prague, Czech Republic under supervision and Physics, Charles University, Prague, Czech Republic under supervision
of RNDr. Libor Forst <forst@cuni.cz>. BIRD has been developed and supported of RNDr. Libor Forst <forst@cuni.cz>. BIRD has been developed and supported
by CZ.NIC z.s.p.o. http://www.nic.cz/ since 2009. by CZ.NIC z.s.p.o. http://www.nic.cz/ since 2009.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
How to install BIRD:
./configure
make
make install
vi /usr/local/etc/bird.conf
Online documentation is available as HTML files in the doc directory,
you can install it by `make install-docs' and rebuild it by `make docs',
but for the latter you need SGMLtools and LaTeX to be installed on your
machine. You can also download a neatly formatted PostScript version
as a separate archive (bird-doc-*.tar.gz).
What do we support:
o Both IPv4 and IPv6 (use --enable-ipv6 when configuring)
o Multiple routing tables
o BGP
o RIP
o OSPF
o Static routes
o Inter-table protocol
o IPv6 router advertisements
o Bidirectional Forwarding Detection (BFD)
o Command-line interface (using the `birdc' client; to get
some help, just press `?')
o Soft reconfiguration -- no online commands for changing the
configuration in very limited ways, just edit the configuration
file and issue a `configure' command or send SIGHUP and BIRD
will start using the new configuration, possibly restarting
protocols affected by the configuration changes.
o Powerful language for route filtering (see doc/bird.conf.example).
What is missing:
o See the TODO list
Good Luck and enjoy the BIRD :) Good Luck and enjoy the BIRD :)
The BIRD Team The BIRD Team

View file

@ -40,6 +40,7 @@ bug(const char *msg, ...)
fputs("Internal error: ", stderr); fputs("Internal error: ", stderr);
vlog(msg, args); vlog(msg, args);
vfprintf(stderr, msg, args); vfprintf(stderr, msg, args);
va_end(args);
exit(1); exit(1);
} }
@ -51,5 +52,6 @@ die(const char *msg, ...)
va_start(args, msg); va_start(args, msg);
cleanup(); cleanup();
vlog(msg, args); vlog(msg, args);
va_end(args);
exit(1); exit(1);
} }

View file

@ -30,6 +30,7 @@
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <libgen.h> #include <libgen.h>
#include <glob.h> #include <glob.h>
@ -233,6 +234,7 @@ else: {
<CCOMM>. <CCOMM>.
\!\= return NEQ; \!\= return NEQ;
\!\~ return NMA;
\<\= return LEQ; \<\= return LEQ;
\>\= return GEQ; \>\= return GEQ;
\&\& return AND; \&\& return AND;

View file

@ -512,6 +512,7 @@ cf_error(const char *msg, ...)
va_start(args, msg); va_start(args, msg);
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0) if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
strcpy(buf, "<bug: error message too long>"); strcpy(buf, "<bug: error message too long>");
va_end(args);
new_config->err_msg = cfg_strdup(buf); new_config->err_msg = cfg_strdup(buf);
new_config->err_lino = ifs->lino; new_config->err_lino = ifs->lino;
new_config->err_file_name = ifs->file_name; new_config->err_file_name = ifs->file_name;

View file

@ -87,7 +87,7 @@ CF_DECLS
%nonassoc PREFIX_DUMMY %nonassoc PREFIX_DUMMY
%left AND OR %left AND OR
%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ PO PC %nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA PO PC
%left '+' '-' %left '+' '-'
%left '*' '/' '%' %left '*' '/' '%'
%left '!' %left '!'

View file

@ -610,8 +610,8 @@ agreement").
options, in that case for given interface the first matching interface options, in that case for given interface the first matching interface
option is used. option is used.
This option is allowed in BFD, Direct, OSPF, RAdv and RIP protocols, but This option is allowed in Babel, BFD, Direct, OSPF, RAdv and RIP
in OSPF protocol it is used in the <cf/area/ subsection. protocols, but in OSPF protocol it is used in the <cf/area/ subsection.
Default: none. Default: none.
@ -667,7 +667,7 @@ agreement").
<descrip> <descrip>
<tag>id <M>num</M></tag> <tag>id <M>num</M></tag>
ID of the password, (0-255). If it's not used, BIRD will choose ID based ID of the password, (1-255). If it is not used, BIRD will choose ID based
on an order of the password item in the interface. For example, second on an order of the password item in the interface. For example, second
password item in one interface will have default ID 2. ID is used by password item in one interface will have default ID 2. ID is used by
some routing protocols to identify which password was used to some routing protocols to identify which password was used to
@ -1016,9 +1016,9 @@ foot).
of type <cf/string/, print such variables, use standard string of type <cf/string/, print such variables, use standard string
comparison operations (e.g. <cf/=, !=, &lt;, &gt;, &lt;=, &gt;=/), but comparison operations (e.g. <cf/=, !=, &lt;, &gt;, &lt;=, &gt;=/), but
you can't concatenate two strings. String literals are written as you can't concatenate two strings. String literals are written as
<cf/"This is a string constant"/. Additionally matching <cf/&tilde;/ <cf/"This is a string constant"/. Additionally matching (<cf/&tilde;,
operator could be used to match a string value against a shell pattern !&tilde;/) operators could be used to match a string value against
(represented also as a string). a shell pattern (represented also as a string).
<tag/ip/ <tag/ip/
This type can hold a single IP address. Depending on the compile-time This type can hold a single IP address. Depending on the compile-time
@ -1035,7 +1035,7 @@ foot).
or <cf><m>ipaddress</m>/<m>netmask</m></cf>. There are two special or <cf><m>ipaddress</m>/<m>netmask</m></cf>. There are two special
operators on prefixes: <cf/.ip/ which extracts the IP address from the operators on prefixes: <cf/.ip/ which extracts the IP address from the
pair, and <cf/.len/, which separates prefix length from the pair. pair, and <cf/.len/, which separates prefix length from the pair.
So <cf>1.2.0.0/16.pxlen = 16</cf> is true. So <cf>1.2.0.0/16.len = 16</cf> is true.
<tag/ec/ <tag/ec/
This is a specialized type used to represent BGP extended community This is a specialized type used to represent BGP extended community
@ -1165,8 +1165,10 @@ foot).
is 4 3 2 1, then: <tt>bgp_path &tilde; [= * 4 3 * =]</tt> is true, is 4 3 2 1, then: <tt>bgp_path &tilde; [= * 4 3 * =]</tt> is true,
but <tt>bgp_path &tilde; [= * 4 5 * =]</tt> is false. BGP mask but <tt>bgp_path &tilde; [= * 4 5 * =]</tt> is false. BGP mask
expressions can also contain integer expressions enclosed in parenthesis expressions can also contain integer expressions enclosed in parenthesis
and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>. There is and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>. You can
also old syntax that uses / .. / instead of [= .. =] and ? instead of *. also use ranges, for example <tt>[= * 3..5 2 100..200 * =]</tt>.
There is also old (deprecated) syntax that uses / .. / instead of [= .. =]
and ? instead of *.
<tag/clist/ <tag/clist/
Clist is similar to a set, except that unlike other sets, it can be Clist is similar to a set, except that unlike other sets, it can be
@ -1200,9 +1202,9 @@ foot).
<tag/eclist/ <tag/eclist/
Eclist is a data type used for BGP extended community lists. Eclists Eclist is a data type used for BGP extended community lists. Eclists
are very similar to clists, but they are sets of ECs instead of pairs. are very similar to clists, but they are sets of ECs instead of pairs.
The same operations (like <cf/add/, <cf/delete/, or <cf/&tilde;/ The same operations (like <cf/add/, <cf/delete/ or <cf/&tilde;/ and
membership operator) can be used to modify or test eclists, with ECs <cf/!&tilde;/ membership operators) can be used to modify or test
instead of pairs as arguments. eclists, with ECs instead of pairs as arguments.
</descrip> </descrip>
@ -1211,19 +1213,19 @@ foot).
<p>The filter language supports common integer operators <cf>(+,-,*,/)</cf>, <p>The filter language supports common integer operators <cf>(+,-,*,/)</cf>,
parentheses <cf/(a*(b+c))/, comparison <cf/(a=b, a!=b, a&lt;b, a&gt;=b)/. parentheses <cf/(a*(b+c))/, comparison <cf/(a=b, a!=b, a&lt;b, a&gt;=b)/.
Logical operations include unary not (<cf/!/), and (<cf/&amp;&amp;/) and or Logical operations include unary not (<cf/!/), and (<cf/&amp;&amp;/) and or
(<cf/&verbar;&verbar;/). Special operators include <cf/&tilde;/ for "is element (<cf/&verbar;&verbar;/). Special operators include (<cf/&tilde;/,
of a set" operation - it can be used on element and set of elements of the same <cf/!&tilde;/) for "is (not) element of a set" operation - it can be used on
type (returning true if element is contained in the given set), or on two element and set of elements of the same type (returning true if element is
strings (returning true if first string matches a shell-like pattern stored in contained in the given set), or on two strings (returning true if first string
second string) or on IP and prefix (returning true if IP is within the range matches a shell-like pattern stored in second string) or on IP and prefix
defined by that prefix), or on prefix and prefix (returning true if first prefix (returning true if IP is within the range defined by that prefix), or on prefix
is more specific than second one) or on bgppath and bgpmask (returning true if and prefix (returning true if first prefix is more specific than second one) or
the path matches the mask) or on number and bgppath (returning true if the on bgppath and bgpmask (returning true if the path matches the mask) or on
number is in the path) or on bgppath and int (number) set (returning true if any number and bgppath (returning true if the number is in the path) or on bgppath
ASN from the path is in the set) or on pair/quad and clist (returning true if and int (number) set (returning true if any ASN from the path is in the set) or
the pair/quad is element of the clist) or on clist and pair/quad set (returning on pair/quad and clist (returning true if the pair/quad is element of the
true if there is an element of the clist that is also a member of the pair/quad clist) or on clist and pair/quad set (returning true if there is an element of
set). the clist that is also a member of the pair/quad set).
<p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It <p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It
examines a ROA table and does RFC 6483 route origin validation for a given examines a ROA table and does RFC 6483 route origin validation for a given
@ -1312,7 +1314,7 @@ clist for most purposes.
<cf/RTS_DUMMY/, <cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/, <cf/RTS_DUMMY/, <cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
<cf/RTS_STATIC_DEVICE/, <cf/RTS_REDIRECT/, <cf/RTS_RIP/, <cf/RTS_OSPF/, <cf/RTS_STATIC_DEVICE/, <cf/RTS_REDIRECT/, <cf/RTS_RIP/, <cf/RTS_OSPF/,
<cf/RTS_OSPF_IA/, <cf/RTS_OSPF_EXT1/, <cf/RTS_OSPF_EXT2/, <cf/RTS_BGP/, <cf/RTS_OSPF_IA/, <cf/RTS_OSPF_EXT1/, <cf/RTS_OSPF_EXT2/, <cf/RTS_BGP/,
<cf/RTS_PIPE/. <cf/RTS_PIPE/, <cf/RTS_BABEL/.
<tag><m/enum/ cast</tag> <tag><m/enum/ cast</tag>
Route type (Currently <cf/RTC_UNICAST/ for normal routes, Route type (Currently <cf/RTC_UNICAST/ for normal routes,
@ -1475,8 +1477,37 @@ protocol babel [<name>] {
yes. yes.
</descrip> </descrip>
<sect1>Attributes
<sect><label id="sect-bfd">BFD <p>Babel defines just one attribute: the internal babel metric of the route. It
is exposed as the <cf/babel_metric/ attribute and has range from 1 to infinity
(65535).
<sect1>Example
<p><code>
protocol babel {
interface "eth*" {
type wired;
};
interface "wlan0", "wlan1" {
type wireless;
hello interval 1;
rxcost 512;
};
interface "tap0";
# This matches the default of babeld: redistribute all addresses
# configured on local interfaces, plus re-distribute all routes received
# from other babel peers.
export where (source = RTS_DEVICE) || (source = RTS_BABEL);
}
</code>
<sect>BFD
<label id="sect-bfd">
<sect1>Introduction <sect1>Introduction
@ -2358,6 +2389,17 @@ limitations can be overcome using another routing table and the pipe protocol.
protocol work with. Available only on systems supporting multiple protocol work with. Available only on systems supporting multiple
routing tables. routing tables.
<tag>metric <m/number/</tag> (Linux)
Use specified value as a kernel metric (priority) for all routes sent to
the kernel. When multiple routes for the same network are in the kernel
routing table, the Linux kernel chooses one with lower metric. Also,
routes with different metrics do not clash with each other, therefore
using dedicated metric value is a reliable way to avoid overwriting
routes from other sources (e.g. kernel device routes). Metric 0 has a
special meaning of undefined metric, in which either OS default is used,
or per-route metric can be set using <cf/krt_metric/ attribute. Default:
0 (undefined).
<tag>graceful restart <m/switch/</tag> <tag>graceful restart <m/switch/</tag>
Participate in graceful restart recovery. If this option is enabled and Participate in graceful restart recovery. If this option is enabled and
a graceful restart recovery is active, the Kernel protocol will defer a graceful restart recovery is active, the Kernel protocol will defer
@ -2390,9 +2432,11 @@ these attributes:
route. See /etc/iproute2/rt_protos for common values. On BSD, it is route. See /etc/iproute2/rt_protos for common values. On BSD, it is
based on STATIC and PROTOx flags. The attribute is read-only. based on STATIC and PROTOx flags. The attribute is read-only.
<tag>int <cf/krt_metric/</tag> <tag>int <cf/krt_metric/</tag> (Linux)
The kernel metric of the route. When multiple same routes are in a The kernel metric of the route. When multiple same routes are in a
kernel routing table, the Linux kernel chooses one with lower metric. kernel routing table, the Linux kernel chooses one with lower metric.
Note that preferred way to set kernel metric is to use protocol option
<cf/metric/, unless per-route metric values are needed.
<tag>ip <cf/krt_prefsrc/</tag> (Linux) <tag>ip <cf/krt_prefsrc/</tag> (Linux)
The preferred source address. Used in source address selection for The preferred source address. Used in source address selection for
@ -2400,6 +2444,15 @@ these attributes:
<tag>int <cf/krt_realm/</tag> (Linux) <tag>int <cf/krt_realm/</tag> (Linux)
The realm of the route. Can be used for traffic classification. The realm of the route. Can be used for traffic classification.
<tag>int <cf/krt_scope/</tag> (Linux IPv4)
The scope of the route. Valid values are 0-254, although Linux kernel
may reject some values depending on route type and nexthop. It is
supposed to represent `indirectness' of the route, where nexthops of
routes are resolved through routes with a higher scope, but in current
kernels anything below <it/link/ (253) is treated as <it/global/ (0).
When not present, global scope is implied for all routes except device
routes, where link scope is used by default.
</descrip> </descrip>
<p>In Linux, there is also a plenty of obscure route attributes mostly focused <p>In Linux, there is also a plenty of obscure route attributes mostly focused

View file

@ -618,16 +618,17 @@ bgp_path:
; ;
bgp_path_tail1: bgp_path_tail1:
NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } NUM bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
| '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } | NUM DDOT NUM bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; }
| '?' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; $$->val = 0; } | '*' bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
| bgp_path_expr bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; } | '?' bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; }
| bgp_path_expr bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; }
| { $$ = NULL; } | { $$ = NULL; }
; ;
bgp_path_tail2: bgp_path_tail2:
NUM bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } NUM bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
| '?' bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } | '?' bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
| { $$ = NULL; } | { $$ = NULL; }
; ;
@ -725,6 +726,7 @@ term:
| term '>' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; } | term '>' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; }
| term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; } | term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; }
| term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; } | term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; }
| term NMA term { $$ = f_new_inst(); $$->code = P('!','~'); $$->a1.p = $1; $$->a2.p = $3; }
| '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; } | '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; }
| DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; } | DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; }

View file

@ -81,6 +81,10 @@ pm_format(struct f_path_mask *p, buffer *buf)
buffer_puts(buf, "* "); buffer_puts(buf, "* ");
break; break;
case PM_ASN_RANGE:
buffer_print(buf, "%u..%u ", p->val, p->val2);
break;
case PM_ASN_EXPR: case PM_ASN_EXPR:
buffer_print(buf, "%u ", f_eval_asn((struct f_inst *) p->val)); buffer_print(buf, "%u ", f_eval_asn((struct f_inst *) p->val));
break; break;
@ -146,13 +150,24 @@ val_compare(struct f_val v1, struct f_val v2)
} }
static int static int
pm_path_same(struct f_path_mask *m1, struct f_path_mask *m2) pm_same(struct f_path_mask *m1, struct f_path_mask *m2)
{ {
while (m1 && m2) while (m1 && m2)
{ {
if ((m1->kind != m2->kind) || (m1->val != m2->val)) if (m1->kind != m2->kind)
return 0; return 0;
if (m1->kind == PM_ASN_EXPR)
{
if (!i_same((struct f_inst *) m1->val, (struct f_inst *) m2->val))
return 0;
}
else
{
if ((m1->val != m2->val) || (m1->val2 != m2->val2))
return 0;
}
m1 = m1->next; m1 = m1->next;
m2 = m2->next; m2 = m2->next;
} }
@ -182,7 +197,7 @@ val_same(struct f_val v1, struct f_val v2)
switch (v1.type) { switch (v1.type) {
case T_PATH_MASK: case T_PATH_MASK:
return pm_path_same(v1.val.path_mask, v2.val.path_mask); return pm_same(v1.val.path_mask, v2.val.path_mask);
case T_PATH: case T_PATH:
case T_CLIST: case T_CLIST:
case T_ECLIST: case T_ECLIST:
@ -673,6 +688,16 @@ interpret(struct f_inst *what)
runtime( "~ applied on unknown type pair" ); runtime( "~ applied on unknown type pair" );
res.val.i = !!res.val.i; res.val.i = !!res.val.i;
break; break;
case P('!','~'):
TWOARGS;
res.type = T_BOOL;
res.val.i = val_in_range(v1, v2);
if (res.val.i == CMP_ERROR)
runtime( "!~ applied on unknown type pair" );
res.val.i = !res.val.i;
break;
case P('d','e'): case P('d','e'):
ONEARG; ONEARG;
res.type = T_BOOL; res.type = T_BOOL;
@ -1415,7 +1440,8 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case P('A','p'): TWOARGS; break; case P('A','p'): TWOARGS; break;
case P('C','a'): TWOARGS; break; case P('C','a'): TWOARGS; break;
case P('a','f'): case P('a','f'):
case P('a','l'): ONEARG; break; case P('a','l'):
case P('a','L'): ONEARG; break;
#if 0 #if 0
case P('R','C'): case P('R','C'):
TWOARGS; TWOARGS;

View file

@ -96,22 +96,28 @@ clist l2;
eclist el; eclist el;
eclist el2; eclist el2;
{ {
print "Entering path test...";
pm1 = / 4 3 2 1 /; pm1 = / 4 3 2 1 /;
pm2 = [= 4 3 2 1 =]; pm2 = [= 3..6 3 2 1..2 =];
print "Testing path masks: ", pm1, " ", pm2; print "Testing path masks: ", pm1, " ", pm2;
p2 = prepend( + empty +, 1 ); p2 = prepend( + empty +, 1 );
p2 = prepend( p2, 2 ); p2 = prepend( p2, 2 );
p2 = prepend( p2, 3 ); p2 = prepend( p2, 3 );
p2 = prepend( p2, 4 ); p2 = prepend( p2, 4 );
print "Testing paths: ", p2; print "Testing path: (4 3 2 1) = ", p2;
print "Should be true: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 3 ~ p2, " ", p2 ~ [2, 10..20], " ", p2 ~ [4, 10..20]; print "Should be true: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 3 ~ p2, " ", p2 ~ [2, 10..20], " ", p2 ~ [4, 10..20];
print "4 = ", p2.len; print "4 = ", p2.len;
p2 = prepend( p2, 5 ); p2 = prepend( p2, 5 );
print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 10 ~ p2, " ", p2 ~ [8, ten..(2*ten)]; print "Testing path: (5 4 3 2 1) = ", p2;
print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 10 ~ p2, " ", p2 ~ [8, ten..(2*ten)], " ", p2 ~ [= 1..4 4 3 2 1 =], " ", p2 ~ [= 5 4 4..100 2 1 =];
print "Should be true: ", p2 ~ / ? 4 3 2 1 /, " ", p2, " ", / ? 4 3 2 1 /; print "Should be true: ", p2 ~ / ? 4 3 2 1 /, " ", p2, " ", / ? 4 3 2 1 /;
print "Should be true: ", p2 ~ [= * 4 3 * 1 =], " ", p2, " ", [= * 4 3 * 1 =]; print "Should be true: ", p2 ~ [= * 4 3 * 1 =], " ", p2, " ", [= * 4 3 * 1 =];
print "Should be true: ", p2 ~ [= 5..6 4..10 1..3 1..3 1..65536 =];
print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4); print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4);
print "Should be true: ", p2.len = 5, " ", p2.first = 5, " ", p2.last = 1; print "Should be true: ", p2.len = 5, " ", p2.first = 5, " ", p2.last = 1;
print "Should be true: ", pm1 = [= 4 3 2 1 =], " ", pm1 != [= 4 3 1 2 =], " ",
pm2 = [= 3..6 3 2 1..2 =], " ", pm2 != [= 3..6 3 2 1..3 =], " ",
[= 1 2 (1+2) =] = [= 1 2 (1+2) =], " ", [= 1 2 (1+2) =] != [= 1 2 (2+1) =];
print "5 = ", p2.len; print "5 = ", p2.len;
print "Delete 3: ", delete(p2, 3); print "Delete 3: ", delete(p2, 3);
print "Filter 1-3: ", filter(p2, [1..3]); print "Filter 1-3: ", filter(p2, [1..3]);

View file

@ -82,7 +82,7 @@ build_tree(struct f_tree *from)
if (len <= 1024) if (len <= 1024)
buf = alloca(len * sizeof(struct f_tree *)); buf = alloca(len * sizeof(struct f_tree *));
else else
buf = malloc(len * sizeof(struct f_tree *)); buf = xmalloc(len * sizeof(struct f_tree *));
/* Convert a degenerated tree into an sorted array */ /* Convert a degenerated tree into an sorted array */
i = 0; i = 0;
@ -94,7 +94,7 @@ build_tree(struct f_tree *from)
root = build_tree_rec(buf, 0, len); root = build_tree_rec(buf, 0, len);
if (len > 1024) if (len > 1024)
free(buf); xfree(buf);
return root; return root;
} }

View file

@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon Summary: BIRD Internet Routing Daemon
Name: bird Name: bird
Version: 1.6.0 Version: 1.6.2
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
Group: Networking/Daemons Group: Networking/Daemons

View file

@ -435,18 +435,23 @@ parse_path(struct adata *path, struct pm_pos *pos)
static int static int
pm_match(struct pm_pos *pos, u32 asn) pm_match(struct pm_pos *pos, u32 asn, u32 asn2)
{ {
u32 gas;
if (! pos->set) if (! pos->set)
return pos->val.asn == asn; return ((pos->val.asn >= asn) && (pos->val.asn <= asn2));
u8 *p = pos->val.sp; u8 *p = pos->val.sp;
int len = *p++; int len = *p++;
int i; int i;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
if (get_as(p + i * BS) == asn) {
gas = get_as(p + i * BS);
if ((gas >= asn) && (gas <= asn2))
return 1; return 1;
}
return 0; return 0;
} }
@ -490,7 +495,7 @@ pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
* next part of mask, we advance each marked state. * next part of mask, we advance each marked state.
* We start with marked first position, when we * We start with marked first position, when we
* run out of marked positions, we reject. When * run out of marked positions, we reject. When
* we process the whole mask, we accept iff final position * we process the whole mask, we accept if final position
* (auxiliary position after last real position in AS path) * (auxiliary position after last real position in AS path)
* is marked. * is marked.
*/ */
@ -502,6 +507,7 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
int plen = parse_path(path, pos); int plen = parse_path(path, pos);
int l, h, i, nh, nl; int l, h, i, nh, nl;
u32 val = 0; u32 val = 0;
u32 val2 = 0;
/* l and h are bound of interval of positions where /* l and h are bound of interval of positions where
are marked states */ are marked states */
@ -525,11 +531,15 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
h = plen; h = plen;
break; break;
case PM_ASN: case PM_ASN: /* Define single ASN as ASN..ASN - very narrow interval */
val = mask->val; val2 = val = mask->val;
goto step; goto step;
case PM_ASN_EXPR: case PM_ASN_EXPR:
val = f_eval_asn((struct f_inst *) mask->val); val2 = val = f_eval_asn((struct f_inst *) mask->val);
goto step;
case PM_ASN_RANGE:
val = mask->val;
val2 = mask->val2;
goto step; goto step;
case PM_QUESTION: case PM_QUESTION:
step: step:
@ -538,7 +548,7 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
if (pos[i].mark) if (pos[i].mark)
{ {
pos[i].mark = 0; pos[i].mark = 0;
if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val)) if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val, val2))
pm_mark(pos, i, plen, &nl, &nh); pm_mark(pos, i, plen, &nl, &nh);
} }

View file

@ -45,11 +45,13 @@ struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_
#define PM_QUESTION 1 #define PM_QUESTION 1
#define PM_ASTERISK 2 #define PM_ASTERISK 2
#define PM_ASN_EXPR 3 #define PM_ASN_EXPR 3
#define PM_ASN_RANGE 4
struct f_path_mask { struct f_path_mask {
struct f_path_mask *next; struct f_path_mask *next;
int kind; int kind;
uintptr_t val; uintptr_t val;
uintptr_t val2;
}; };
int as_path_match(struct adata *path, struct f_path_mask *mask); int as_path_match(struct adata *path, struct f_path_mask *mask);

View file

@ -74,7 +74,7 @@ CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CL
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST) CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST)
CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULTIPATH) CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULTIPATH)

View file

@ -287,7 +287,7 @@ void rte_update2(struct channel *c, net_addr *n, rte *new, struct rte_src *src);
/* rte_update() moved to protocol.h to avoid dependency conflicts */ /* rte_update() moved to protocol.h to avoid dependency conflicts */
void rte_discard(rtable *tab, rte *old); void rte_discard(rtable *tab, rte *old);
int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter); int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, int silent); rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, linpool *pool, int silent);
void rt_refresh_begin(rtable *t, struct channel *c); void rt_refresh_begin(rtable *t, struct channel *c);
void rt_refresh_end(rtable *t, struct channel *c); void rt_refresh_end(rtable *t, struct channel *c);
void rt_schedule_prune(rtable *t); void rt_schedule_prune(rtable *t);
@ -509,6 +509,8 @@ int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */
static inline int mpnh_same(struct mpnh *x, struct mpnh *y) static inline int mpnh_same(struct mpnh *x, struct mpnh *y)
{ return (x == y) || mpnh__same(x, y); } { return (x == y) || mpnh__same(x, y); }
struct mpnh *mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp); struct mpnh *mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp);
void mpnh_insert(struct mpnh **n, struct mpnh *y);
int mpnh_is_sorted(struct mpnh *x);
void rta_init(void); void rta_init(void);
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */ rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */

View file

@ -250,6 +250,34 @@ mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp)
return root; return root;
} }
void
mpnh_insert(struct mpnh **n, struct mpnh *x)
{
for (; *n; n = &((*n)->next))
{
int cmp = mpnh_compare_node(*n, x);
if (cmp < 0)
continue;
else if (cmp > 0)
break;
else
return;
}
x->next = *n;
*n = x;
}
int
mpnh_is_sorted(struct mpnh *x)
{
for (; x && x->next; x = x->next)
if (mpnh_compare_node(x, x->next) >= 0)
return 0;
return 1;
}
static struct mpnh * static struct mpnh *
mpnh_copy(struct mpnh *o) mpnh_copy(struct mpnh *o)
@ -1114,7 +1142,7 @@ rta_dump(rta *a)
static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE", static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP", "RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1", "RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
"RTS_OSPF_EXT2", "RTS_BGP" }; "RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
static char *rtc[] = { "", " BC", " MC", " AC" }; static char *rtc[] = { "", " BC", " MC", " AC" };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" }; static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };

View file

@ -50,7 +50,7 @@ static linpool *rte_update_pool;
static list routing_tables; static list routing_tables;
static void rt_format_via(rte *e, byte *via); static byte *rt_format_via(rte *e);
static void rt_free_hostcache(rtable *tab); static void rt_free_hostcache(rtable *tab);
static void rt_notify_hostcache(rtable *tab, net *net); static void rt_notify_hostcache(rtable *tab, net *net);
static void rt_update_hostcache(rtable *tab); static void rt_update_hostcache(rtable *tab);
@ -346,10 +346,7 @@ rte_mergable(rte *pri, rte *sec)
static void static void
rte_trace(struct proto *p, rte *e, int dir, char *msg) rte_trace(struct proto *p, rte *e, int dir, char *msg)
{ {
byte via[IPA_MAX_TEXT_LENGTH+32]; log(L_TRACE "%s %c %s %N %s", p->name, dir, msg, e->net->n.addr, rt_format_via(e));
rt_format_via(e, via);
log(L_TRACE "%s %c %s %N %s", p->name, dir, msg, e->net->n.addr, via);
} }
static inline void static inline void
@ -367,7 +364,7 @@ rte_trace_out(uint flag, struct proto *p, rte *e, char *msg)
} }
static rte * static rte *
export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int silent) export_filter_(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
{ {
struct proto *p = c->proto; struct proto *p = c->proto;
struct filter *filter = c->out_filter; struct filter *filter = c->out_filter;
@ -382,9 +379,9 @@ export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int si
if (!tmpa) if (!tmpa)
tmpa = &tmpb; tmpa = &tmpb;
*tmpa = make_tmp_attrs(rt, rte_update_pool); *tmpa = make_tmp_attrs(rt, pool);
v = p->import_control ? p->import_control(p, &rt, tmpa, rte_update_pool) : 0; v = p->import_control ? p->import_control(p, &rt, tmpa, pool) : 0;
if (v < 0) if (v < 0)
{ {
if (silent) if (silent)
@ -403,7 +400,7 @@ export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int si
} }
v = filter && ((filter == FILTER_REJECT) || v = filter && ((filter == FILTER_REJECT) ||
(f_run(filter, &rt, tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)); (f_run(filter, &rt, tmpa, pool, FF_FORCE_TMPATTR) > F_ACCEPT));
if (v) if (v)
{ {
if (silent) if (silent)
@ -426,6 +423,12 @@ export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int si
return NULL; return NULL;
} }
static inline rte *
export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int silent)
{
return export_filter_(c, rt0, rt_free, tmpa, rte_update_pool, silent);
}
static void static void
do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int refeed) do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
{ {
@ -706,7 +709,7 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
static struct mpnh * static struct mpnh *
mpnh_merge_rta(struct mpnh *nhs, rta *a, int max) mpnh_merge_rta(struct mpnh *nhs, rta *a, linpool *pool, int max)
{ {
struct mpnh nh = { .gw = a->gw, .iface = a->iface }; struct mpnh nh = { .gw = a->gw, .iface = a->iface };
struct mpnh *nh2 = (a->dest == RTD_MULTIPATH) ? a->nexthops : &nh; struct mpnh *nh2 = (a->dest == RTD_MULTIPATH) ? a->nexthops : &nh;
@ -714,7 +717,7 @@ mpnh_merge_rta(struct mpnh *nhs, rta *a, int max)
} }
rte * rte *
rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, int silent) rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
{ {
// struct proto *p = c->proto; // struct proto *p = c->proto;
struct mpnh *nhs = NULL; struct mpnh *nhs = NULL;
@ -726,7 +729,7 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, int
if (!rte_is_valid(best0)) if (!rte_is_valid(best0))
return NULL; return NULL;
best = export_filter(c, best0, rt_free, tmpa, silent); best = export_filter_(c, best0, rt_free, tmpa, pool, silent);
if (!best || !rte_is_reachable(best)) if (!best || !rte_is_reachable(best))
return best; return best;
@ -736,13 +739,13 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, int
if (!rte_mergable(best0, rt0)) if (!rte_mergable(best0, rt0))
continue; continue;
rt = export_filter(c, rt0, &tmp, NULL, 1); rt = export_filter_(c, rt0, &tmp, NULL, pool, 1);
if (!rt) if (!rt)
continue; continue;
if (rte_is_reachable(rt)) if (rte_is_reachable(rt))
nhs = mpnh_merge_rta(nhs, rt->attrs, c->merge_limit); nhs = mpnh_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
if (tmp) if (tmp)
rte_free(tmp); rte_free(tmp);
@ -750,11 +753,11 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, int
if (nhs) if (nhs)
{ {
nhs = mpnh_merge_rta(nhs, best->attrs, c->merge_limit); nhs = mpnh_merge_rta(nhs, best->attrs, pool, c->merge_limit);
if (nhs->next) if (nhs->next)
{ {
best = rte_cow_rta(best, rte_update_pool); best = rte_cow_rta(best, pool);
best->attrs->dest = RTD_MULTIPATH; best->attrs->dest = RTD_MULTIPATH;
best->attrs->nexthops = nhs; best->attrs->nexthops = nhs;
} }
@ -805,7 +808,7 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
/* Prepare new merged route */ /* Prepare new merged route */
if (new_best) if (new_best)
new_best = rt_export_merged(c, net, &new_best_free, &tmpa, 0); new_best = rt_export_merged(c, net, &new_best_free, &tmpa, rte_update_pool, 0);
/* Prepare old merged route (without proper merged next hops) */ /* Prepare old merged route (without proper merged next hops) */
/* There are some issues with running filter on old route - see rt_notify_basic() */ /* There are some issues with running filter on old route - see rt_notify_basic() */
@ -919,6 +922,13 @@ rte_validate(rte *e)
return 0; return 0;
} }
if ((e->attrs->dest == RTD_MULTIPATH) && !mpnh_is_sorted(e->attrs->nexthops))
{
log(L_WARN "Ignoring unsorted multipath route %N received via %s",
n->n.addr, e->sender->proto->name);
return 0;
}
return 1; return 1;
} }
@ -2426,11 +2436,14 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_add
* CLI commands * CLI commands
*/ */
static void static byte *
rt_format_via(rte *e, byte *via) rt_format_via(rte *e)
{ {
rta *a = e->attrs; rta *a = e->attrs;
/* Max text length w/o IP addr and interface name is 16 */
static byte via[IPA_MAX_TEXT_LENGTH+sizeof(a->iface->name)+16];
switch (a->dest) switch (a->dest)
{ {
case RTD_ROUTER: bsprintf(via, "via %I on %s", a->gw, a->iface->name); break; case RTD_ROUTER: bsprintf(via, "via %I on %s", a->gw, a->iface->name); break;
@ -2441,12 +2454,12 @@ rt_format_via(rte *e, byte *via)
case RTD_MULTIPATH: bsprintf(via, "multipath"); break; case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
default: bsprintf(via, "???"); default: bsprintf(via, "???");
} }
return via;
} }
static void static void
rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa) rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa)
{ {
byte via[IPA_MAX_TEXT_LENGTH+32];
byte from[IPA_MAX_TEXT_LENGTH+8]; byte from[IPA_MAX_TEXT_LENGTH+8];
byte tm[TM_DATETIME_BUFFER_SIZE], info[256]; byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
rta *a = e->attrs; rta *a = e->attrs;
@ -2455,7 +2468,6 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs);
struct mpnh *nh; struct mpnh *nh;
rt_format_via(e, via);
tm_format_datetime(tm, &config->tf_route, e->lastmod); tm_format_datetime(tm, &config->tf_route, e->lastmod);
if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->gw)) if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->gw))
bsprintf(from, " from %I", a->from); bsprintf(from, " from %I", a->from);
@ -2476,7 +2488,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
get_route_info(e, info, tmpa); get_route_info(e, info, tmpa);
else else
bsprintf(info, " (%d)", e->pref); bsprintf(info, " (%d)", e->pref);
cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, via, a->src->proto->name, cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rt_format_via(e), a->src->proto->name,
tm, from, primary ? (sync_error ? " !" : " *") : "", info); tm, from, primary ? (sync_error ? " !" : " *") : "", info);
for (nh = a->nexthops; nh; nh = nh->next) for (nh = a->nexthops; nh; nh = nh->next)
cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1); cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1);
@ -2517,7 +2529,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED)) if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
{ {
rte *rt_free; rte *rt_free;
e = rt_export_merged(ec, n, &rt_free, &tmpa, 1); e = rt_export_merged(ec, n, &rt_free, &tmpa, rte_update_pool, 1);
pass = 1; pass = 1;
if (!e) if (!e)

View file

@ -565,6 +565,11 @@ babel_select_route(struct babel_entry *e)
babel_send_seqno_request(e); babel_send_seqno_request(e);
babel_announce_rte(p, e); babel_announce_rte(p, e);
/* Section 3.6 of the RFC forbids an infeasible from being selected. This
is cleared after announcing the route to the core to make sure an
unreachable route is propagated first. */
e->selected_in = NULL;
} }
else else
{ {
@ -783,16 +788,21 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed)
msg.update.prefix = e->n.prefix; msg.update.prefix = e->n.prefix;
msg.update.router_id = r->router_id; msg.update.router_id = r->router_id;
/* Update feasibility distance */ babel_enqueue(&msg, ifa);
/* Update feasibility distance for redistributed routes */
if (!OUR_ROUTE(r))
{
struct babel_source *s = babel_get_source(e, r->router_id); struct babel_source *s = babel_get_source(e, r->router_id);
s->expires = now + BABEL_GARBAGE_INTERVAL; s->expires = now + BABEL_GARBAGE_INTERVAL;
if ((msg.update.seqno > s->seqno) || if ((msg.update.seqno > s->seqno) ||
((msg.update.seqno == s->seqno) && (msg.update.metric < s->metric))) ((msg.update.seqno == s->seqno) && (msg.update.metric < s->metric)))
{ {
s->seqno = msg.update.seqno; s->seqno = msg.update.seqno;
s->metric = msg.update.metric; s->metric = msg.update.metric;
} }
babel_enqueue(&msg, ifa); }
} }
FIB_WALK_END; FIB_WALK_END;
} }
@ -834,8 +844,8 @@ babel_send_retraction(struct babel_iface *ifa, ip_addr prefix, int plen)
struct babel_proto *p = ifa->proto; struct babel_proto *p = ifa->proto;
union babel_msg msg = {}; union babel_msg msg = {};
TRACE(D_PACKETS, "Sending retraction for %I/%d router-id %lR seqno %d", TRACE(D_PACKETS, "Sending retraction for %I/%d seqno %d",
prefix, plen, p->router_id, p->update_seqno); prefix, plen, p->update_seqno);
msg.type = BABEL_TLV_UPDATE; msg.type = BABEL_TLV_UPDATE;
msg.update.plen = plen; msg.update.plen = plen;
@ -843,7 +853,23 @@ babel_send_retraction(struct babel_iface *ifa, ip_addr prefix, int plen)
msg.update.seqno = p->update_seqno; msg.update.seqno = p->update_seqno;
msg.update.metric = BABEL_INFINITY; msg.update.metric = BABEL_INFINITY;
msg.update.prefix = prefix; msg.update.prefix = prefix;
msg.update.router_id = p->router_id;
babel_enqueue(&msg, ifa);
}
static void
babel_send_wildcard_retraction(struct babel_iface *ifa)
{
struct babel_proto *p = ifa->proto;
union babel_msg msg = {};
TRACE(D_PACKETS, "Sending wildcard retraction on %s", ifa->ifname);
msg.type = BABEL_TLV_UPDATE;
msg.update.wildcard = 1;
msg.update.interval = ifa->cf->update_interval;
msg.update.seqno = p->update_seqno;
msg.update.metric = BABEL_INFINITY;
babel_enqueue(&msg, ifa); babel_enqueue(&msg, ifa);
} }
@ -1040,17 +1066,18 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
struct babel_proto *p = ifa->proto; struct babel_proto *p = ifa->proto;
struct babel_msg_update *msg = &m->update; struct babel_msg_update *msg = &m->update;
struct babel_neighbor *n; struct babel_neighbor *nbr;
struct babel_entry *e; struct babel_entry *e;
struct babel_source *s; struct babel_source *s;
struct babel_route *r; struct babel_route *r;
node *n;
int feasible; int feasible;
TRACE(D_PACKETS, "Handling update for %I/%d with seqno %d metric %d", TRACE(D_PACKETS, "Handling update for %I/%d with seqno %d metric %d",
msg->prefix, msg->plen, msg->seqno, msg->metric); msg->prefix, msg->plen, msg->seqno, msg->metric);
n = babel_find_neighbor(ifa, msg->sender); nbr = babel_find_neighbor(ifa, msg->sender);
if (!n) if (!nbr)
{ {
DBG("Babel: Haven't heard from neighbor %I; ignoring update.\n", msg->sender); DBG("Babel: Haven't heard from neighbor %I; ignoring update.\n", msg->sender);
return; return;
@ -1095,55 +1122,88 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
* of the Interval value included in the update. * of the Interval value included in the update.
*/ */
/* Retraction */
if (msg->metric == BABEL_INFINITY) if (msg->metric == BABEL_INFINITY)
e = babel_find_entry(p, msg->prefix, msg->plen); {
if (msg->wildcard)
{
/*
* Special case: This is a retraction of all prefixes announced by this
* neighbour (see second-to-last paragraph of section 4.4.9 in the RFC).
*/
WALK_LIST(n, nbr->routes)
{
r = SKIP_BACK(struct babel_route, neigh_route, n);
r->metric = BABEL_INFINITY;
babel_select_route(r->e);
}
}
else else
e = babel_get_entry(p, msg->prefix, msg->plen); {
e = babel_find_entry(p, msg->prefix, msg->plen);
if (!e) if (!e)
return; return;
/* The route entry indexed by neighbour */
r = babel_find_route(e, nbr);
if (!r)
return;
r->metric = BABEL_INFINITY;
babel_select_route(e);
}
/* Done with retractions */
return;
}
e = babel_get_entry(p, msg->prefix, msg->plen);
r = babel_find_route(e, nbr); /* the route entry indexed by neighbour */
s = babel_find_source(e, msg->router_id); /* for feasibility */ s = babel_find_source(e, msg->router_id); /* for feasibility */
r = babel_find_route(e, n); /* the route entry indexed by neighbour */
feasible = babel_is_feasible(s, msg->seqno, msg->metric); feasible = babel_is_feasible(s, msg->seqno, msg->metric);
if (!r) if (!r)
{ {
if (!feasible || (msg->metric == BABEL_INFINITY)) if (!feasible)
return; return;
r = babel_get_route(e, n); r = babel_get_route(e, nbr);
r->advert_metric = msg->metric; r->advert_metric = msg->metric;
r->router_id = msg->router_id; r->router_id = msg->router_id;
r->metric = babel_compute_metric(n, msg->metric); r->metric = babel_compute_metric(nbr, msg->metric);
r->next_hop = msg->next_hop; r->next_hop = msg->next_hop;
r->seqno = msg->seqno; r->seqno = msg->seqno;
} }
else if (r == r->e->selected_in && !feasible) else if (r == r->e->selected_in && !feasible)
{ {
/* Route is installed and update is infeasible - we may lose the route, so /*
send a unicast seqno request (section 3.8.2.2 second paragraph). */ * Route is installed and update is infeasible - we may lose the route,
* so send a unicast seqno request (section 3.8.2.2 second paragraph).
*/
babel_unicast_seqno_request(r); babel_unicast_seqno_request(r);
if (msg->router_id == r->router_id) return; if (msg->router_id == r->router_id)
r->metric = BABEL_INFINITY; /* retraction */ return;
/* Treat as retraction */
r->metric = BABEL_INFINITY;
} }
else else
{ {
/* Last paragraph above - update the entry */ /* Last paragraph above - update the entry */
r->advert_metric = msg->metric; r->advert_metric = msg->metric;
r->metric = babel_compute_metric(n, msg->metric); r->metric = babel_compute_metric(nbr, msg->metric);
r->router_id = msg->router_id;
r->next_hop = msg->next_hop; r->next_hop = msg->next_hop;
r->router_id = msg->router_id;
r->seqno = msg->seqno; r->seqno = msg->seqno;
if (msg->metric != BABEL_INFINITY)
{
r->expiry_interval = BABEL_ROUTE_EXPIRY_FACTOR(msg->interval); r->expiry_interval = BABEL_ROUTE_EXPIRY_FACTOR(msg->interval);
r->expires = now + r->expiry_interval; r->expires = now + r->expiry_interval;
if (r->expiry_interval > BABEL_ROUTE_REFRESH_INTERVAL) if (r->expiry_interval > BABEL_ROUTE_REFRESH_INTERVAL)
r->refresh_time = now + r->expiry_interval - BABEL_ROUTE_REFRESH_INTERVAL; r->refresh_time = now + r->expiry_interval - BABEL_ROUTE_REFRESH_INTERVAL;
}
/* If the route is not feasible at this point, it means it is from another /* If the route is not feasible at this point, it means it is from another
neighbour than the one currently selected; so send a unicast seqno neighbour than the one currently selected; so send a unicast seqno
@ -1313,6 +1373,7 @@ babel_iface_start(struct babel_iface *ifa)
ifa->up = 1; ifa->up = 1;
babel_send_hello(ifa, 0); babel_send_hello(ifa, 0);
babel_send_wildcard_retraction(ifa);
babel_send_wildcard_request(ifa); babel_send_wildcard_request(ifa);
babel_send_update(ifa, 0); /* Full update */ babel_send_update(ifa, 0); /* Full update */
} }
@ -1529,6 +1590,9 @@ babel_reconfigure_iface(struct babel_proto *p, struct babel_iface *ifa, struct b
ifa->cf = new; ifa->cf = new;
if (ifa->next_hello > (now + new->hello_interval))
ifa->next_hello = now + (random() % new->hello_interval) + 1;
if (ifa->next_regular > (now + new->update_interval)) if (ifa->next_regular > (now + new->update_interval))
ifa->next_regular = now + (random() % new->update_interval) + 1; ifa->next_regular = now + (random() % new->update_interval) + 1;
@ -2022,6 +2086,30 @@ babel_start(struct proto *P)
return PS_UP; return PS_UP;
} }
static inline void
babel_iface_shutdown(struct babel_iface *ifa)
{
if (ifa->sk)
{
babel_send_wildcard_retraction(ifa);
babel_send_queue(ifa);
}
}
static int
babel_shutdown(struct proto *P)
{
struct babel_proto *p = (void *) P;
struct babel_iface *ifa;
TRACE(D_EVENTS, "Shutdown requested");
WALK_LIST(ifa, p->interfaces)
babel_iface_shutdown(ifa);
return PS_DOWN;
}
static int static int
babel_reconfigure(struct proto *P, struct proto_config *c) babel_reconfigure(struct proto *P, struct proto_config *c)
{ {
@ -2049,6 +2137,7 @@ struct protocol proto_babel = {
.init = babel_init, .init = babel_init,
.dump = babel_dump, .dump = babel_dump,
.start = babel_start, .start = babel_start,
.shutdown = babel_shutdown,
.reconfigure = babel_reconfigure, .reconfigure = babel_reconfigure,
.get_route_info = babel_get_route_info, .get_route_info = babel_get_route_info,
.get_attr = babel_get_attr .get_attr = babel_get_attr

View file

@ -50,10 +50,12 @@
#define BABEL_INITIAL_HOP_COUNT 255 #define BABEL_INITIAL_HOP_COUNT 255
#define BABEL_MAX_SEND_INTERVAL 5 #define BABEL_MAX_SEND_INTERVAL 5
#define BABEL_TIME_UNITS 100 /* On-wire times are counted in centiseconds */ #define BABEL_TIME_UNITS 100 /* On-wire times are counted in centiseconds */
#define BABEL_SEQNO_REQUEST_EXPIRY 60 #define BABEL_SEQNO_REQUEST_EXPIRY 60
#define BABEL_GARBAGE_INTERVAL 300 #define BABEL_GARBAGE_INTERVAL 300
/* Max interval that will not overflow when carried as 16-bit centiseconds */
#define BABEL_MAX_INTERVAL (0xFFFF/BABEL_TIME_UNITS)
#define BABEL_OVERHEAD (SIZE_OF_IP_HEADER+UDP_HEADER_LENGTH) #define BABEL_OVERHEAD (SIZE_OF_IP_HEADER+UDP_HEADER_LENGTH)
#define BABEL_MIN_MTU (512 + BABEL_OVERHEAD) #define BABEL_MIN_MTU (512 + BABEL_OVERHEAD)
@ -266,7 +268,7 @@ struct babel_msg_ihu {
struct babel_msg_update { struct babel_msg_update {
u8 type; u8 type;
u8 ae; u8 wildcard;
u8 plen; u8 plen;
u16 interval; u16 interval;
u16 seqno; u16 seqno;

View file

@ -77,17 +77,18 @@ babel_iface_finish:
BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED; BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED;
} }
/* Make sure we do not overflow the 16-bit centisec fields */
if (!BABEL_IFACE->update_interval) if (!BABEL_IFACE->update_interval)
BABEL_IFACE->update_interval = BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR; BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
BABEL_IFACE->ihu_interval = BABEL_IFACE->hello_interval*BABEL_IHU_INTERVAL_FACTOR; BABEL_IFACE->ihu_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_IHU_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
}; };
babel_iface_item: babel_iface_item:
| PORT expr { BABEL_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); } | PORT expr { BABEL_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); }
| RXCOST expr { BABEL_IFACE->rxcost = $2; if (($2<1) || ($2>65535)) cf_error("Invalid rxcost"); } | RXCOST expr { BABEL_IFACE->rxcost = $2; if (($2<1) || ($2>65535)) cf_error("Invalid rxcost"); }
| HELLO INTERVAL expr { BABEL_IFACE->hello_interval = $3; if (($3<1) || ($3>65535)) cf_error("Invalid hello interval"); } | HELLO INTERVAL expr { BABEL_IFACE->hello_interval = $3; if (($3<1) || ($3>BABEL_MAX_INTERVAL)) cf_error("Invalid hello interval"); }
| UPDATE INTERVAL expr { BABEL_IFACE->update_interval = $3; if (($3<1) || ($3>65535)) cf_error("Invalid hello interval"); } | UPDATE INTERVAL expr { BABEL_IFACE->update_interval = $3; if (($3<1) || ($3>BABEL_MAX_INTERVAL)) cf_error("Invalid update interval"); }
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; } | TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; } | TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
| RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); } | RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); }

View file

@ -462,7 +462,6 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
struct babel_msg_update *msg = &m->update; struct babel_msg_update *msg = &m->update;
msg->type = BABEL_TLV_UPDATE; msg->type = BABEL_TLV_UPDATE;
msg->ae = tlv->ae;
msg->interval = get_time16(&tlv->interval); msg->interval = get_time16(&tlv->interval);
msg->seqno = get_u16(&tlv->seqno); msg->seqno = get_u16(&tlv->seqno);
msg->metric = get_u16(&tlv->metric); msg->metric = get_u16(&tlv->metric);
@ -480,7 +479,7 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
if (tlv->plen > 0) if (tlv->plen > 0)
return PARSE_ERROR; return PARSE_ERROR;
msg->prefix = IPA_NONE; msg->wildcard = 1;
break; break;
case BABEL_AE_IP4: case BABEL_AE_IP4:
@ -523,7 +522,8 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE; return PARSE_IGNORE;
} }
if (!state->router_id_seen) /* Update must have Router ID, unless it is retraction */
if (!state->router_id_seen && (msg->metric != BABEL_INFINITY))
{ {
DBG("Babel: No router ID seen before update\n"); DBG("Babel: No router ID seen before update\n");
return PARSE_ERROR; return PARSE_ERROR;
@ -548,8 +548,11 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
* When needed, we write Router-ID TLV before Update TLV and return size of * When needed, we write Router-ID TLV before Update TLV and return size of
* both of them. There is enough space for the Router-ID TLV, because * both of them. There is enough space for the Router-ID TLV, because
* sizeof(struct babel_tlv_router_id) == sizeof(struct babel_tlv_update). * sizeof(struct babel_tlv_router_id) == sizeof(struct babel_tlv_update).
*
* Router ID is not used for retractions, so do not us it in such case.
*/ */
if (!state->router_id_seen || (msg->router_id != state->router_id)) if ((msg->metric < BABEL_INFINITY) &&
(!state->router_id_seen || (msg->router_id != state->router_id)))
{ {
len0 = babel_write_router_id(hdr, msg->router_id, state, max_len); len0 = babel_write_router_id(hdr, msg->router_id, state, max_len);
tlv = (struct babel_tlv_update *) NEXT_TLV(tlv); tlv = (struct babel_tlv_update *) NEXT_TLV(tlv);
@ -562,12 +565,22 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
memset(tlv, 0, sizeof(struct babel_tlv_update)); memset(tlv, 0, sizeof(struct babel_tlv_update));
TLV_HDR(tlv, BABEL_TLV_UPDATE, len); TLV_HDR(tlv, BABEL_TLV_UPDATE, len);
if (msg->wildcard)
{
tlv->ae = BABEL_AE_WILDCARD;
tlv->plen = 0;
}
else
{
tlv->ae = BABEL_AE_IP6; tlv->ae = BABEL_AE_IP6;
tlv->plen = msg->plen; tlv->plen = msg->plen;
put_ip6_px(tlv->addr, msg->prefix, msg->plen);
}
put_time16(&tlv->interval, msg->interval); put_time16(&tlv->interval, msg->interval);
put_u16(&tlv->seqno, msg->seqno); put_u16(&tlv->seqno, msg->seqno);
put_u16(&tlv->metric, msg->metric); put_u16(&tlv->metric, msg->metric);
put_ip6_px(tlv->addr, msg->prefix, msg->plen);
return len0 + len; return len0 + len;
} }

View file

@ -589,7 +589,7 @@ sockets_fire(struct birdloop *loop)
times_update(loop); times_update(loop);
/* Last fd is internal wakeup fd */ /* Last fd is internal wakeup fd */
if (pfd[loop->sock_num].revents & POLLIN) if (pfd[poll_num].revents & POLLIN)
wakeup_drain(loop); wakeup_drain(loop);
int i; int i;

View file

@ -118,7 +118,7 @@ validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ileng
{ {
int res = 0; int res = 0;
u8 *a, *dst; u8 *a, *dst;
int len, plen, copy; int len, plen;
dst = a = idata; dst = a = idata;
len = *ilength; len = *ilength;
@ -132,15 +132,20 @@ validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ileng
if (len < plen) if (len < plen)
return -1; return -1;
if (a[1] == 0)
{
log(L_WARN "%s: %s_PATH attribute contains empty segment, skipping it",
p->p.name, as_path ? "AS" : "AS4");
goto skip;
}
switch (a[0]) switch (a[0])
{ {
case AS_PATH_SET: case AS_PATH_SET:
copy = 1;
res++; res++;
break; break;
case AS_PATH_SEQUENCE: case AS_PATH_SEQUENCE:
copy = 1;
res += a[1]; res += a[1];
break; break;
@ -154,20 +159,17 @@ validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, uint *ileng
log(L_WARN "%s: %s_PATH attribute contains AS_CONFED_* segment, skipping segment", log(L_WARN "%s: %s_PATH attribute contains AS_CONFED_* segment, skipping segment",
p->p.name, as_path ? "AS" : "AS4"); p->p.name, as_path ? "AS" : "AS4");
copy = 0; goto skip;
break;
default: default:
return -1; return -1;
} }
if (copy)
{
if (dst != a) if (dst != a)
memmove(dst, a, plen); memmove(dst, a, plen);
dst += plen; dst += plen;
}
skip:
len -= plen; len -= plen;
a += plen; a += plen;
} }

View file

@ -369,7 +369,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
} }
put_u16(buf, wd_size); put_u16(buf, wd_size);
if (remains >= 3072) if (!wd_size)
{ {
while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next) while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next)
{ {
@ -382,7 +382,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
} }
DBG("Processing bucket %p\n", buck); DBG("Processing bucket %p\n", buck);
a_size = bgp_encode_attrs(p, w+2, buck->eattrs, 2048); a_size = bgp_encode_attrs(p, w+2, buck->eattrs, remains - 1024);
if (a_size < 0) if (a_size < 0)
{ {
@ -461,8 +461,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
w += size; w += size;
remains -= size; remains -= size;
} }
else
if (remains >= 3072)
{ {
while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next) while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next)
{ {
@ -478,7 +477,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
rem_stored = remains; rem_stored = remains;
w_stored = w; w_stored = w;
size = bgp_encode_attrs(p, w, buck->eattrs, 2048); size = bgp_encode_attrs(p, w, buck->eattrs, remains - 1024);
if (size < 0) if (size < 0)
{ {
log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name); log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name);

View file

@ -162,7 +162,6 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
{ {
/* ECMP route */ /* ECMP route */
struct mpnh *nhs = NULL; struct mpnh *nhs = NULL;
struct mpnh **nhp = &nhs;
int num = 0; int num = 0;
for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next) for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next)
@ -174,9 +173,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
nh->gw = rt->next_hop; nh->gw = rt->next_hop;
nh->iface = rt->from->nbr->iface; nh->iface = rt->from->nbr->iface;
nh->weight = rt->from->ifa->cf->ecmp_weight; nh->weight = rt->from->ifa->cf->ecmp_weight;
nh->next = NULL; mpnh_insert(&nhs, nh);
*nhp = nh;
nhp = &(nh->next);
num++; num++;
if (rt->tag != rt_tag) if (rt->tag != rt_tag)

View file

@ -80,7 +80,6 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
{ {
struct static_route *r2; struct static_route *r2;
struct mpnh *nhs = NULL; struct mpnh *nhs = NULL;
struct mpnh **nhp = &nhs;
for (r2 = r->mp_next; r2; r2 = r2->mp_next) for (r2 = r->mp_next; r2; r2 = r2->mp_next)
if (r2->installed) if (r2->installed)
@ -89,9 +88,7 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
nh->gw = r2->via; nh->gw = r2->via;
nh->iface = r2->neigh->iface; nh->iface = r2->neigh->iface;
nh->weight = r2->weight; nh->weight = r2->weight;
nh->next = NULL; mpnh_insert(&nhs, nh);
*nhp = nh;
nhp = &(nh->next);
} }
/* There is at least one nexthop */ /* There is at least one nexthop */

View file

@ -946,6 +946,12 @@ krt_sock_hook(sock *sk, int size UNUSED)
return 0; return 0;
} }
static void
krt_sock_err_hook(sock *sk, int e UNUSED)
{
krt_sock_hook(sk, 0);
}
static sock * static sock *
krt_sock_open(pool *pool, void *data, int table_id) krt_sock_open(pool *pool, void *data, int table_id)
{ {
@ -967,6 +973,7 @@ krt_sock_open(pool *pool, void *data, int table_id)
sk = sk_new(pool); sk = sk_new(pool);
sk->type = SK_MAGIC; sk->type = SK_MAGIC;
sk->rx_hook = krt_sock_hook; sk->rx_hook = krt_sock_hook;
sk->err_hook = krt_sock_err_hook;
sk->fd = fd; sk->fd = fd;
sk->data = data; sk->data = data;

View file

@ -7,7 +7,7 @@
#define _BIRD_CONFIG_H_ #define _BIRD_CONFIG_H_
/* BIRD version */ /* BIRD version */
#define BIRD_VERSION "1.6.0" #define BIRD_VERSION "1.6.2"
/* Include parameters determined by configure script */ /* Include parameters determined by configure script */
#include "sysdep/autoconf.h" #include "sysdep/autoconf.h"

View file

@ -32,8 +32,11 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; }
/* Kernel routes */ /* Kernel routes */
#define KRT_ALLOW_MERGE_PATHS 1
#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0x10) #define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0x10)
#define EA_KRT_REALM EA_CODE(EAP_KRT, 0x11) #define EA_KRT_REALM EA_CODE(EAP_KRT, 0x11)
#define EA_KRT_SCOPE EA_CODE(EAP_KRT, 0x12)
#define KRT_METRICS_MAX 0x10 /* RTAX_QUICKACK+1 */ #define KRT_METRICS_MAX 0x10 /* RTAX_QUICKACK+1 */
@ -86,6 +89,7 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; }
struct krt_params { struct krt_params {
u32 table_id; /* Kernel table ID we sync with */ u32 table_id; /* Kernel table ID we sync with */
u32 metric; /* Kernel metric used for all routes */
}; };
struct krt_state { struct krt_state {

View file

@ -10,8 +10,8 @@ CF_HDR
CF_DECLS CF_DECLS
CF_KEYWORDS(KERNEL, TABLE, KRT_PREFSRC, KRT_REALM, KRT_MTU, KRT_WINDOW, KRT_RTT, CF_KEYWORDS(KERNEL, TABLE, METRIC, KRT_PREFSRC, KRT_REALM, KRT_SCOPE, KRT_MTU, KRT_WINDOW,
KRT_RTTVAR, KRT_SSTRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING, KRT_RTT, KRT_RTTVAR, KRT_SSTRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING,
KRT_HOPLIMIT, KRT_INITCWND, KRT_RTO_MIN, KRT_INITRWND, KRT_QUICKACK, KRT_HOPLIMIT, KRT_INITCWND, KRT_RTO_MIN, KRT_INITRWND, KRT_QUICKACK,
KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR, KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR,
KRT_LOCK_SSTRESH, KRT_LOCK_CWND, KRT_LOCK_ADVMSS, KRT_LOCK_REORDERING, KRT_LOCK_SSTRESH, KRT_LOCK_CWND, KRT_LOCK_ADVMSS, KRT_LOCK_REORDERING,
@ -22,13 +22,13 @@ CF_GRAMMAR
CF_ADDTO(kern_proto, kern_proto kern_sys_item ';') CF_ADDTO(kern_proto, kern_proto kern_sys_item ';')
kern_sys_item: kern_sys_item:
KERNEL TABLE expr { KERNEL TABLE expr { THIS_KRT->sys.table_id = $3; }
THIS_KRT->sys.table_id = $3; | METRIC expr { THIS_KRT->sys.metric = $2; }
}
; ;
CF_ADDTO(dynamic_attr, KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_KRT_PREFSRC); }) CF_ADDTO(dynamic_attr, KRT_PREFSRC { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_KRT_PREFSRC); })
CF_ADDTO(dynamic_attr, KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); }) CF_ADDTO(dynamic_attr, KRT_REALM { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REALM); })
CF_ADDTO(dynamic_attr, KRT_SCOPE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SCOPE); })
CF_ADDTO(dynamic_attr, KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_MTU); }) CF_ADDTO(dynamic_attr, KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_MTU); })
CF_ADDTO(dynamic_attr, KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_WINDOW); }) CF_ADDTO(dynamic_attr, KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_WINDOW); })

View file

@ -20,7 +20,6 @@
#include "nest/route.h" #include "nest/route.h"
#include "nest/protocol.h" #include "nest/protocol.h"
#include "nest/iface.h" #include "nest/iface.h"
#include "lib/alloca.h"
#include "sysdep/unix/timer.h" #include "sysdep/unix/timer.h"
#include "sysdep/unix/unix.h" #include "sysdep/unix/unix.h"
#include "sysdep/unix/krt.h" #include "sysdep/unix/krt.h"
@ -39,6 +38,10 @@
#define MSG_TRUNC 0x20 #define MSG_TRUNC 0x20
#endif #endif
#ifndef IFA_FLAGS
#define IFA_FLAGS 8
#endif
#ifndef IFF_LOWER_UP #ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000 #define IFF_LOWER_UP 0x10000
#endif #endif
@ -48,6 +51,45 @@
#endif #endif
#define krt_ecmp6(p) ((p)->af == AF_INET6)
/*
* Structure nl_parse_state keeps state of received route processing. Ideally,
* we could just independently parse received Netlink messages and immediately
* propagate received routes to the rest of BIRD, but Linux kernel represents
* and announces IPv6 ECMP routes not as one route with multiple next hops (like
* RTA_MULTIPATH in IPv4 ECMP), but as a set of routes with the same prefix.
*
* Therefore, BIRD keeps currently processed route in nl_parse_state structure
* and postpones its propagation until we expect it to be final; i.e., when
* non-matching route is received or when the scan ends. When another matching
* route is received, it is merged with the already processed route to form an
* ECMP route. Note that merging is done only for IPv6 (merge == 1), but the
* postponing is done in both cases (for simplicity). All IPv4 routes are just
* considered non-matching.
*
* This is ignored for asynchronous notifications (every notification is handled
* as a separate route). It is not an issue for our routes, as we ignore such
* notifications anyways. But importing alien IPv6 ECMP routes does not work
* properly.
*/
struct nl_parse_state
{
struct linpool *pool;
int scan;
int merge;
net *net;
rta *attrs;
struct krt_proto *proto;
s8 new;
s8 krt_src;
u8 krt_type;
u8 krt_proto;
u32 krt_metric;
};
/* /*
* Synchronous Netlink interface * Synchronous Netlink interface
*/ */
@ -63,6 +105,13 @@ struct nl_sock
#define NL_RX_SIZE 8192 #define NL_RX_SIZE 8192
#define NL_OP_DELETE 0
#define NL_OP_ADD (NLM_F_CREATE|NLM_F_EXCL)
#define NL_OP_REPLACE (NLM_F_CREATE|NLM_F_REPLACE)
#define NL_OP_APPEND (NLM_F_CREATE|NLM_F_APPEND)
static linpool *nl_linpool;
static struct nl_sock nl_scan = {.fd = -1}; /* Netlink socket for synchronous scan */ static struct nl_sock nl_scan = {.fd = -1}; /* Netlink socket for synchronous scan */
static struct nl_sock nl_req = {.fd = -1}; /* Netlink socket for requests */ static struct nl_sock nl_req = {.fd = -1}; /* Netlink socket for requests */
@ -166,7 +215,7 @@ nl_get_reply(struct nl_sock *nl)
static struct tbf rl_netlink_err = TBF_DEFAULT_LOG_LIMITS; static struct tbf rl_netlink_err = TBF_DEFAULT_LOG_LIMITS;
static int static int
nl_error(struct nlmsghdr *h) nl_error(struct nlmsghdr *h, int ignore_esrch)
{ {
struct nlmsgerr *e; struct nlmsgerr *e;
int ec; int ec;
@ -178,7 +227,7 @@ nl_error(struct nlmsghdr *h)
} }
e = (struct nlmsgerr *) NLMSG_DATA(h); e = (struct nlmsgerr *) NLMSG_DATA(h);
ec = -e->error; ec = -e->error;
if (ec) if (ec && !(ignore_esrch && (ec == ESRCH)))
log_rl(&rl_netlink_err, L_WARN "Netlink: %s", strerror(ec)); log_rl(&rl_netlink_err, L_WARN "Netlink: %s", strerror(ec));
return ec; return ec;
} }
@ -192,14 +241,14 @@ nl_get_scan(void)
return NULL; return NULL;
if (h->nlmsg_type == NLMSG_ERROR) if (h->nlmsg_type == NLMSG_ERROR)
{ {
nl_error(h); nl_error(h, 0);
return NULL; return NULL;
} }
return h; return h;
} }
static int static int
nl_exchange(struct nlmsghdr *pkt) nl_exchange(struct nlmsghdr *pkt, int ignore_esrch)
{ {
struct nlmsghdr *h; struct nlmsghdr *h;
@ -211,7 +260,7 @@ nl_exchange(struct nlmsghdr *pkt)
break; break;
log(L_WARN "nl_exchange: Unexpected reply received"); log(L_WARN "nl_exchange: Unexpected reply received");
} }
return nl_error(h) ? -1 : 0; return nl_error(h, ignore_esrch) ? -1 : 0;
} }
/* /*
@ -248,17 +297,19 @@ static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = {
}; };
#define BIRD_IFA_MAX (IFA_ANYCAST+1) #define BIRD_IFA_MAX (IFA_FLAGS+1)
static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = { static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = {
[IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) }, [IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) },
[IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) }, [IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) },
[IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) }, [IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) },
[IFA_FLAGS] = { 1, 1, sizeof(u32) },
}; };
static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = { static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
[IFA_ADDRESS] = { 1, 1, sizeof(ip6_addr) }, [IFA_ADDRESS] = { 1, 1, sizeof(ip6_addr) },
[IFA_LOCAL] = { 1, 1, sizeof(ip6_addr) }, [IFA_LOCAL] = { 1, 1, sizeof(ip6_addr) },
[IFA_FLAGS] = { 1, 1, sizeof(u32) },
}; };
@ -627,6 +678,7 @@ nl_parse_addr4(struct ifaddrmsg *i, int scan, int new)
{ {
struct rtattr *a[BIRD_IFA_MAX]; struct rtattr *a[BIRD_IFA_MAX];
struct iface *ifi; struct iface *ifi;
u32 ifa_flags;
int scope; int scope;
if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want4, a, sizeof(a))) if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want4, a, sizeof(a)))
@ -650,10 +702,15 @@ nl_parse_addr4(struct ifaddrmsg *i, int scan, int new)
return; return;
} }
if (a[IFA_FLAGS])
ifa_flags = rta_get_u32(a[IFA_FLAGS]);
else
ifa_flags = i->ifa_flags;
struct ifa ifa; struct ifa ifa;
bzero(&ifa, sizeof(ifa)); bzero(&ifa, sizeof(ifa));
ifa.iface = ifi; ifa.iface = ifi;
if (i->ifa_flags & IFA_F_SECONDARY) if (ifa_flags & IFA_F_SECONDARY)
ifa.flags |= IA_SECONDARY; ifa.flags |= IA_SECONDARY;
ifa.ip = rta_get_ipa(a[IFA_LOCAL]); ifa.ip = rta_get_ipa(a[IFA_LOCAL]);
@ -730,6 +787,7 @@ nl_parse_addr6(struct ifaddrmsg *i, int scan, int new)
{ {
struct rtattr *a[BIRD_IFA_MAX]; struct rtattr *a[BIRD_IFA_MAX];
struct iface *ifi; struct iface *ifi;
u32 ifa_flags;
int scope; int scope;
if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want6, a, sizeof(a))) if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want6, a, sizeof(a)))
@ -748,14 +806,22 @@ nl_parse_addr6(struct ifaddrmsg *i, int scan, int new)
return; return;
} }
if (a[IFA_FLAGS])
ifa_flags = rta_get_u32(a[IFA_FLAGS]);
else
ifa_flags = i->ifa_flags;
struct ifa ifa; struct ifa ifa;
bzero(&ifa, sizeof(ifa)); bzero(&ifa, sizeof(ifa));
ifa.iface = ifi; ifa.iface = ifi;
if (i->ifa_flags & IFA_F_SECONDARY) if (ifa_flags & IFA_F_SECONDARY)
ifa.flags |= IA_SECONDARY; ifa.flags |= IA_SECONDARY;
/* IFA_LOCAL can be unset for IPv6 interfaces */ /* Ignore tentative addresses silently */
if (ifa_flags & IFA_F_TENTATIVE)
return;
/* IFA_LOCAL can be unset for IPv6 interfaces */
ifa.ip = rta_get_ipa(a[IFA_LOCAL] ? : a[IFA_ADDRESS]); ifa.ip = rta_get_ipa(a[IFA_LOCAL] ? : a[IFA_ADDRESS]);
if (i->ifa_prefixlen > IP6_MAX_PREFIX_LENGTH) if (i->ifa_prefixlen > IP6_MAX_PREFIX_LENGTH)
@ -916,12 +982,13 @@ nh_bufsize(struct mpnh *nh)
} }
static int static int
nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int dest, ip_addr gw, struct iface *iface)
{ {
eattr *ea; eattr *ea;
net *net = e->net; net *net = e->net;
rta *a = e->attrs; rta *a = e->attrs;
int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops); int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops);
u32 priority = 0;
struct { struct {
struct nlmsghdr h; struct nlmsghdr h;
@ -932,13 +999,13 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
int rsize = sizeof(*r) + bufsize; int rsize = sizeof(*r) + bufsize;
r = alloca(rsize); r = alloca(rsize);
DBG("nl_send_route(%N,new=%d)\n", net->n.addr, new); DBG("nl_send_route(%N,op=%x)\n", net->n.addr, op);
bzero(&r->h, sizeof(r->h)); bzero(&r->h, sizeof(r->h));
bzero(&r->r, sizeof(r->r)); bzero(&r->r, sizeof(r->r));
r->h.nlmsg_type = new ? RTM_NEWROUTE : RTM_DELROUTE; r->h.nlmsg_type = op ? RTM_NEWROUTE : RTM_DELROUTE;
r->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); r->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
r->h.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (new ? NLM_F_CREATE|NLM_F_EXCL : 0); r->h.nlmsg_flags = op | NLM_F_REQUEST | NLM_F_ACK;
r->r.rtm_family = p->af; r->r.rtm_family = p->af;
r->r.rtm_dst_len = net_pxlen(net->n.addr); r->r.rtm_dst_len = net_pxlen(net->n.addr);
@ -946,18 +1013,37 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
r->r.rtm_scope = RT_SCOPE_UNIVERSE; r->r.rtm_scope = RT_SCOPE_UNIVERSE;
nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr)); nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr));
/*
* Strange behavior for RTM_DELROUTE:
* 1) rtm_family is ignored in IPv6, works for IPv4
* 2) not setting RTA_PRIORITY is different from setting default value (on IPv6)
* 3) not setting RTA_PRIORITY is equivalent to setting 0, which is wildcard
*/
if (krt_table_id(p) < 256) if (krt_table_id(p) < 256)
r->r.rtm_table = krt_table_id(p); r->r.rtm_table = krt_table_id(p);
else else
nl_add_attr_u32(&r->h, rsize, RTA_TABLE, krt_table_id(p)); nl_add_attr_u32(&r->h, rsize, RTA_TABLE, krt_table_id(p));
/* For route delete, we do not specify route attributes */ if (a->source == RTS_DUMMY)
if (!new) priority = e->u.krt.metric;
return nl_exchange(&r->h); else if (KRT_CF->sys.metric)
priority = KRT_CF->sys.metric;
else if ((op != NL_OP_DELETE) && (ea = ea_find(eattrs, EA_KRT_METRIC)))
priority = ea->u.data;
if (priority)
nl_add_attr_u32(&r->h, sizeof(r), RTA_PRIORITY, priority);
if (ea = ea_find(eattrs, EA_KRT_METRIC)) /* For route delete, we do not specify remaining route attributes */
nl_add_attr_u32(&r->h, rsize, RTA_PRIORITY, ea->u.data); if (op == NL_OP_DELETE)
goto dest;
/* Default scope is LINK for device routes, UNIVERSE otherwise */
if (ea = ea_find(eattrs, EA_KRT_SCOPE))
r->r.rtm_scope = ea->u.data;
else
r->r.rtm_scope = (dest == RTD_DEVICE) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
@ -981,18 +1067,18 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX); nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX);
dest:
/* a->iface != NULL checked in krt_capable() for router and device routes */ /* a->iface != NULL checked in krt_capable() for router and device routes */
switch (dest)
switch (a->dest)
{ {
case RTD_ROUTER: case RTD_ROUTER:
r->r.rtm_type = RTN_UNICAST; r->r.rtm_type = RTN_UNICAST;
nl_add_attr_u32(&r->h, rsize, RTA_OIF, a->iface->index); nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index);
nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, a->gw); nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, gw);
break; break;
case RTD_DEVICE: case RTD_DEVICE:
r->r.rtm_type = RTN_UNICAST; r->r.rtm_type = RTN_UNICAST;
nl_add_attr_u32(&r->h, rsize, RTA_OIF, a->iface->index); nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index);
break; break;
case RTD_BLACKHOLE: case RTD_BLACKHOLE:
r->r.rtm_type = RTN_BLACKHOLE; r->r.rtm_type = RTN_BLACKHOLE;
@ -1007,11 +1093,50 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
r->r.rtm_type = RTN_UNICAST; r->r.rtm_type = RTN_UNICAST;
nl_add_multipath(&r->h, rsize, a->nexthops); nl_add_multipath(&r->h, rsize, a->nexthops);
break; break;
case RTD_NONE:
break;
default: default:
bug("krt_capable inconsistent with nl_send_route"); bug("krt_capable inconsistent with nl_send_route");
} }
return nl_exchange(&r->h); /* Ignore missing for DELETE */
return nl_exchange(&r->h, (op == NL_OP_DELETE));
}
static inline int
nl_add_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
{
rta *a = e->attrs;
int err = 0;
if (krt_ecmp6(p) && (a->dest == RTD_MULTIPATH))
{
struct mpnh *nh = a->nexthops;
err = nl_send_route(p, e, eattrs, NL_OP_ADD, RTD_ROUTER, nh->gw, nh->iface);
if (err < 0)
return err;
for (nh = nh->next; nh; nh = nh->next)
err += nl_send_route(p, e, eattrs, NL_OP_APPEND, RTD_ROUTER, nh->gw, nh->iface);
return err;
}
return nl_send_route(p, e, eattrs, NL_OP_ADD, a->dest, a->gw, a->iface);
}
static inline int
nl_delete_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
{
int err = 0;
/* For IPv6, we just repeatedly request DELETE until we get error */
do
err = nl_send_route(p, e, eattrs, NL_OP_DELETE, RTD_NONE, IPA_NONE, NULL);
while (krt_ecmp6(p) && !err);
return err;
} }
void void
@ -1020,17 +1145,21 @@ krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list
int err = 0; int err = 0;
/* /*
* NULL for eattr of the old route is a little hack, but we don't * We could use NL_OP_REPLACE, but route replace on Linux has some problems:
* get proper eattrs for old in rt_notify() anyway. NULL means no *
* extended route attributes and therefore matches if the kernel * 1) Does not check for matching rtm_protocol
* route has any of them. * 2) Has broken semantics for IPv6 ECMP
* 3) Crashes some kernel version when used for IPv6 ECMP
*
* So we use NL_OP_DELETE and then NL_OP_ADD. We also do not trust the old
* route value, so we do not try to optimize IPv6 ECMP reconfigurations.
*/ */
if (old) if (old)
nl_send_route(p, old, NULL, 0); nl_delete_rte(p, old, eattrs);
if (new) if (new)
err = nl_send_route(p, new, eattrs, 1); err = nl_add_rte(p, new, eattrs);
if (err < 0) if (err < 0)
n->n.flags |= KRF_SYNC_ERROR; n->n.flags |= KRF_SYNC_ERROR;
@ -1039,10 +1168,80 @@ krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list
} }
static inline struct mpnh *
nl_alloc_mpnh(struct nl_parse_state *s, ip_addr gw, struct iface *iface, byte weight)
{
struct mpnh *nh = lp_alloc(s->pool, sizeof(struct mpnh));
nh->gw = gw;
nh->iface = iface;
nh->next = NULL;
nh->weight = weight;
return nh;
}
static int
nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint priority, uint krt_type)
{
/* Route merging must be active */
if (!s->merge)
return 0;
/* Saved and new route must have same network, proto/table, and priority */
if ((s->net != net) || (s->proto != p) || (s->krt_metric != priority))
return 0;
/* Both must be regular unicast routes */
if ((s->krt_type != RTN_UNICAST) || (krt_type != RTN_UNICAST))
return 0;
return 1;
}
static void
nl_announce_route(struct nl_parse_state *s)
{
rte *e = rte_get_temp(s->attrs);
e->net = s->net;
e->u.krt.src = s->krt_src;
e->u.krt.proto = s->krt_proto;
e->u.krt.seen = 0;
e->u.krt.best = 0;
e->u.krt.metric = s->krt_metric;
if (s->scan)
krt_got_route(s->proto, e);
else
krt_got_route_async(s->proto, e, s->new);
s->net = NULL;
s->attrs = NULL;
s->proto = NULL;
lp_flush(s->pool);
}
static inline void
nl_parse_begin(struct nl_parse_state *s, int scan, int merge)
{
memset(s, 0, sizeof (struct nl_parse_state));
s->pool = nl_linpool;
s->scan = scan;
s->merge = merge;
}
static inline void
nl_parse_end(struct nl_parse_state *s)
{
if (s->net)
nl_announce_route(s);
}
#define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0) #define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0)
static void static void
nl_parse_route(struct nlmsghdr *h, int scan) nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
{ {
struct krt_proto *p; struct krt_proto *p;
struct rtmsg *i; struct rtmsg *i;
@ -1052,6 +1251,8 @@ nl_parse_route(struct nlmsghdr *h, int scan)
net_addr dst; net_addr dst;
u32 oif = ~0; u32 oif = ~0;
u32 table_id; u32 table_id;
u32 priority = 0;
u32 def_scope = RT_SCOPE_UNIVERSE;
int src; int src;
if (!(i = nl_checkin(h, sizeof(*i)))) if (!(i = nl_checkin(h, sizeof(*i))))
@ -1096,24 +1297,22 @@ nl_parse_route(struct nlmsghdr *h, int scan)
if (!p) if (!p)
SKIP("unknown table %d\n", table); SKIP("unknown table %d\n", table);
if (a[RTA_IIF]) if (a[RTA_IIF])
SKIP("IIF set\n"); SKIP("IIF set\n");
if (i->rtm_tos != 0) /* We don't support TOS */ if (i->rtm_tos != 0) /* We don't support TOS */
SKIP("TOS %02x\n", i->rtm_tos); SKIP("TOS %02x\n", i->rtm_tos);
if (scan && !new) if (s->scan && !new)
SKIP("RTM_DELROUTE in scan\n"); SKIP("RTM_DELROUTE in scan\n");
if (a[RTA_PRIORITY])
priority = rta_get_u32(a[RTA_PRIORITY]);
int c = net_classify(&dst); int c = net_classify(&dst);
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
SKIP("strange class/scope\n"); SKIP("strange class/scope\n");
// ignore rtm_scope, it is not a real scope
// if (i->rtm_scope != RT_SCOPE_UNIVERSE)
// SKIP("scope %u\n", i->rtm_scope);
switch (i->rtm_protocol) switch (i->rtm_protocol)
{ {
case RTPROT_UNSPEC: case RTPROT_UNSPEC:
@ -1128,7 +1327,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
return; return;
case RTPROT_BIRD: case RTPROT_BIRD:
if (!scan) if (!s->scan)
SKIP("echo\n"); SKIP("echo\n");
src = KRT_SRC_BIRD; src = KRT_SRC_BIRD;
break; break;
@ -1140,12 +1339,14 @@ nl_parse_route(struct nlmsghdr *h, int scan)
net *net = net_get(p->p.main_channel->table, &dst); net *net = net_get(p->p.main_channel->table, &dst);
rta ra = { if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type))
.src= p->p.main_source, nl_announce_route(s);
.source = RTS_INHERIT,
.scope = SCOPE_UNIVERSE, rta *ra = lp_allocz(s->pool, sizeof(rta));
.cast = RTC_UNICAST ra->src = p->p.main_source;
}; ra->source = RTS_INHERIT;
ra->scope = SCOPE_UNIVERSE;
ra->cast = RTC_UNICAST;
switch (i->rtm_type) switch (i->rtm_type)
{ {
@ -1153,9 +1354,9 @@ nl_parse_route(struct nlmsghdr *h, int scan)
if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET)) if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET))
{ {
ra.dest = RTD_MULTIPATH; ra->dest = RTD_MULTIPATH;
ra.nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]); ra->nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]);
if (!ra.nexthops) if (!ra->nexthops)
{ {
log(L_ERR "KRT: Received strange multipath route %N", net->n.addr); log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
return; return;
@ -1164,8 +1365,8 @@ nl_parse_route(struct nlmsghdr *h, int scan)
break; break;
} }
ra.iface = if_find_by_index(oif); ra->iface = if_find_by_index(oif);
if (!ra.iface) if (!ra->iface)
{ {
log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif); log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif);
return; return;
@ -1173,37 +1374,38 @@ nl_parse_route(struct nlmsghdr *h, int scan)
if (a[RTA_GATEWAY]) if (a[RTA_GATEWAY])
{ {
ra.dest = RTD_ROUTER; ra->dest = RTD_ROUTER;
ra.gw = rta_get_ipa(a[RTA_GATEWAY]); ra->gw = rta_get_ipa(a[RTA_GATEWAY]);
/* Silently skip strange 6to4 routes */ /* Silently skip strange 6to4 routes */
const net_addr_ip6 sit = NET_ADDR_IP6(IP6_NONE, 96); const net_addr_ip6 sit = NET_ADDR_IP6(IP6_NONE, 96);
if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra.gw, (net_addr *) &sit)) if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->gw, (net_addr *) &sit))
return; return;
neighbor *nbr; neighbor *nbr;
nbr = neigh_find2(&p->p, &ra.gw, ra.iface, nbr = neigh_find2(&p->p, &ra->gw, ra->iface,
(i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
if (!nbr || (nbr->scope == SCOPE_HOST)) if (!nbr || (nbr->scope == SCOPE_HOST))
{ {
log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr, ra.gw); log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr, ra->gw);
return; return;
} }
} }
else else
{ {
ra.dest = RTD_DEVICE; ra->dest = RTD_DEVICE;
def_scope = RT_SCOPE_LINK;
} }
break; break;
case RTN_BLACKHOLE: case RTN_BLACKHOLE:
ra.dest = RTD_BLACKHOLE; ra->dest = RTD_BLACKHOLE;
break; break;
case RTN_UNREACHABLE: case RTN_UNREACHABLE:
ra.dest = RTD_UNREACHABLE; ra->dest = RTD_UNREACHABLE;
break; break;
case RTN_PROHIBIT: case RTN_PROHIBIT:
ra.dest = RTD_PROHIBIT; ra->dest = RTD_PROHIBIT;
break; break;
/* FIXME: What about RTN_THROW? */ /* FIXME: What about RTN_THROW? */
default: default:
@ -1211,39 +1413,41 @@ nl_parse_route(struct nlmsghdr *h, int scan)
return; return;
} }
rte *e = rte_get_temp(&ra); if (i->rtm_scope != def_scope)
e->net = net; {
e->u.krt.src = src; ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
e->u.krt.proto = i->rtm_protocol; ea->next = ra->eattrs;
e->u.krt.seen = 0; ra->eattrs = ea;
e->u.krt.best = 0; ea->flags = EALF_SORTED;
e->u.krt.metric = 0; ea->count = 1;
ea->attrs[0].id = EA_KRT_SCOPE;
if (a[RTA_PRIORITY]) ea->attrs[0].flags = 0;
e->u.krt.metric = rta_get_u32(a[RTA_PRIORITY]); ea->attrs[0].type = EAF_TYPE_INT;
ea->attrs[0].u.data = i->rtm_scope;
}
if (a[RTA_PREFSRC]) if (a[RTA_PREFSRC])
{ {
ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]); ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]);
ea_list *ea = alloca(sizeof(ea_list) + sizeof(eattr)); ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
ea->next = ra.eattrs; ea->next = ra->eattrs;
ra.eattrs = ea; ra->eattrs = ea;
ea->flags = EALF_SORTED; ea->flags = EALF_SORTED;
ea->count = 1; ea->count = 1;
ea->attrs[0].id = EA_KRT_PREFSRC; ea->attrs[0].id = EA_KRT_PREFSRC;
ea->attrs[0].flags = 0; ea->attrs[0].flags = 0;
ea->attrs[0].type = EAF_TYPE_IP_ADDRESS; ea->attrs[0].type = EAF_TYPE_IP_ADDRESS;
ea->attrs[0].u.ptr = alloca(sizeof(struct adata) + sizeof(ps)); ea->attrs[0].u.ptr = lp_alloc(s->pool, sizeof(struct adata) + sizeof(ps));
ea->attrs[0].u.ptr->length = sizeof(ps); ea->attrs[0].u.ptr->length = sizeof(ps);
memcpy(ea->attrs[0].u.ptr->data, &ps, sizeof(ps)); memcpy(ea->attrs[0].u.ptr->data, &ps, sizeof(ps));
} }
if (a[RTA_FLOW]) if (a[RTA_FLOW])
{ {
ea_list *ea = alloca(sizeof(ea_list) + sizeof(eattr)); ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
ea->next = ra.eattrs; ea->next = ra->eattrs;
ra.eattrs = ea; ra->eattrs = ea;
ea->flags = EALF_SORTED; ea->flags = EALF_SORTED;
ea->count = 1; ea->count = 1;
ea->attrs[0].id = EA_KRT_REALM; ea->attrs[0].id = EA_KRT_REALM;
@ -1255,7 +1459,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
if (a[RTA_METRICS]) if (a[RTA_METRICS])
{ {
u32 metrics[KRT_METRICS_MAX]; u32 metrics[KRT_METRICS_MAX];
ea_list *ea = alloca(sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr)); ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr));
int t, n = 0; int t, n = 0;
if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0) if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0)
@ -1276,37 +1480,69 @@ nl_parse_route(struct nlmsghdr *h, int scan)
if (n > 0) if (n > 0)
{ {
ea->next = ra.eattrs; ea->next = ra->eattrs;
ea->flags = EALF_SORTED; ea->flags = EALF_SORTED;
ea->count = n; ea->count = n;
ra.eattrs = ea; ra->eattrs = ea;
} }
} }
if (scan) /*
krt_got_route(p, e); * Ideally, now we would send the received route to the rest of kernel code.
* But IPv6 ECMP routes are sent as a sequence of routes, so we postpone it
* and merge next hops until the end of the sequence.
*/
if (!s->net)
{
/* Store the new route */
s->net = net;
s->attrs = ra;
s->proto = p;
s->new = new;
s->krt_src = src;
s->krt_type = i->rtm_type;
s->krt_proto = i->rtm_protocol;
s->krt_metric = priority;
}
else else
krt_got_route_async(p, e, new); {
/* Merge next hops with the stored route */
rta *a = s->attrs;
if (a->dest != RTD_MULTIPATH)
{
a->dest = RTD_MULTIPATH;
a->nexthops = nl_alloc_mpnh(s, a->gw, a->iface, 0);
}
mpnh_insert(&a->nexthops, nl_alloc_mpnh(s, ra->gw, ra->iface, 0));
}
} }
void void
krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NULL */ krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NULL */
{ {
struct nlmsghdr *h; struct nlmsghdr *h;
struct nl_parse_state s;
nl_parse_begin(&s, 1, 0);
nl_request_dump(AF_INET, RTM_GETROUTE); nl_request_dump(AF_INET, RTM_GETROUTE);
while (h = nl_get_scan()) while (h = nl_get_scan())
if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE) if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
nl_parse_route(h, 1); nl_parse_route(&s, h);
else else
log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type); log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
nl_parse_end(&s);
nl_parse_begin(&s, 1, 1);
nl_request_dump(AF_INET6, RTM_GETROUTE); nl_request_dump(AF_INET6, RTM_GETROUTE);
while (h = nl_get_scan()) while (h = nl_get_scan())
if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE) if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
nl_parse_route(h, 1); nl_parse_route(&s, h);
else else
log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type); log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
nl_parse_end(&s);
} }
/* /*
@ -1319,12 +1555,16 @@ static byte *nl_async_rx_buffer; /* Receive buffer */
static void static void
nl_async_msg(struct nlmsghdr *h) nl_async_msg(struct nlmsghdr *h)
{ {
struct nl_parse_state s;
switch (h->nlmsg_type) switch (h->nlmsg_type)
{ {
case RTM_NEWROUTE: case RTM_NEWROUTE:
case RTM_DELROUTE: case RTM_DELROUTE:
DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type); DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type);
nl_parse_route(h, 0); nl_parse_begin(&s, 0, 0);
nl_parse_route(&s, h);
nl_parse_end(&s);
break; break;
case RTM_NEWLINK: case RTM_NEWLINK:
case RTM_DELLINK: case RTM_DELLINK:
@ -1396,6 +1636,12 @@ nl_async_hook(sock *sk, int size UNUSED)
return 1; return 1;
} }
static void
nl_async_err_hook(sock *sk, int e UNUSED)
{
nl_async_hook(sk, 0);
}
static void static void
nl_open_async(void) nl_open_async(void)
{ {
@ -1433,6 +1679,7 @@ nl_open_async(void)
sk = nl_async_sk = sk_new(krt_pool); sk = nl_async_sk = sk_new(krt_pool);
sk->type = SK_MAGIC; sk->type = SK_MAGIC;
sk->rx_hook = nl_async_hook; sk->rx_hook = nl_async_hook;
sk->err_hook = nl_async_err_hook;
sk->fd = fd; sk->fd = fd;
if (sk_open(sk) < 0) if (sk_open(sk) < 0)
bug("Netlink: sk_open failed"); bug("Netlink: sk_open failed");
@ -1446,6 +1693,7 @@ nl_open_async(void)
void void
krt_sys_io_init(void) krt_sys_io_init(void)
{ {
nl_linpool = lp_new(krt_pool, 4080);
HASH_INIT(nl_table_map, krt_pool, 6); HASH_INIT(nl_table_map, krt_pool, 6);
} }
@ -1478,19 +1726,21 @@ krt_sys_shutdown(struct krt_proto *p)
int int
krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o) krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o)
{ {
return n->sys.table_id == o->sys.table_id; return (n->sys.table_id == o->sys.table_id) && (n->sys.metric == o->sys.metric);
} }
void void
krt_sys_init_config(struct krt_config *cf) krt_sys_init_config(struct krt_config *cf)
{ {
cf->sys.table_id = RT_TABLE_MAIN; cf->sys.table_id = RT_TABLE_MAIN;
cf->sys.metric = 0;
} }
void void
krt_sys_copy_config(struct krt_config *d, struct krt_config *s) krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
{ {
d->sys.table_id = s->sys.table_id; d->sys.table_id = s->sys.table_id;
d->sys.metric = s->sys.metric;
} }
static const char *krt_metrics_names[KRT_METRICS_MAX] = { static const char *krt_metrics_names[KRT_METRICS_MAX] = {
@ -1515,6 +1765,10 @@ krt_sys_get_attr(eattr *a, byte *buf, int buflen UNUSED)
bsprintf(buf, "realm"); bsprintf(buf, "realm");
return GA_NAME; return GA_NAME;
case EA_KRT_SCOPE:
bsprintf(buf, "scope");
return GA_NAME;
case EA_KRT_LOCK: case EA_KRT_LOCK:
buf += bsprintf(buf, "lock:"); buf += bsprintf(buf, "lock:");
ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX); ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX);

View file

@ -1892,6 +1892,20 @@ int sk_is_ipv4(sock *s)
int sk_is_ipv6(sock *s) int sk_is_ipv6(sock *s)
{ return s->af == AF_INET6; } { return s->af == AF_INET6; }
void
sk_err(sock *s, int revents)
{
int se = 0, sse = sizeof(se);
if ((s->type != SK_MAGIC) && (revents & POLLERR))
if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &se, &sse) < 0)
{
log(L_ERR "IO: Socket error: SO_ERROR: %m");
se = 0;
}
s->err_hook(s, se);
}
void void
sk_dump_all(void) sk_dump_all(void)
{ {
@ -2202,7 +2216,7 @@ io_loop(void)
int steps; int steps;
steps = MAX_STEPS; steps = MAX_STEPS;
if (s->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) if (s->fast_rx && (pfd[s->index].revents & POLLIN) && s->rx_hook)
do do
{ {
steps--; steps--;
@ -2224,6 +2238,7 @@ io_loop(void)
goto next; goto next;
} }
while (e && steps); while (e && steps);
current_sock = sk_next(s); current_sock = sk_next(s);
next: ; next: ;
} }
@ -2247,7 +2262,7 @@ io_loop(void)
goto next2; goto next2;
} }
if (!s->fast_rx && (pfd[s->index].revents & (POLLIN | POLLHUP | POLLERR)) && s->rx_hook) if (!s->fast_rx && (pfd[s->index].revents & POLLIN) && s->rx_hook)
{ {
count++; count++;
io_log_event(s->rx_hook, s->data); io_log_event(s->rx_hook, s->data);
@ -2255,10 +2270,18 @@ io_loop(void)
if (s != current_sock) if (s != current_sock)
goto next2; goto next2;
} }
if (pfd[s->index].revents & (POLLHUP | POLLERR))
{
sk_err(s, pfd[s->index].revents);
goto next2;
}
current_sock = sk_next(s); current_sock = sk_next(s);
next2: ; next2: ;
} }
stored_sock = current_sock; stored_sock = current_sock;
} }
} }

View file

@ -29,6 +29,8 @@ CF_DECLS
CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC, MERGE, PATHS) CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC, MERGE, PATHS)
%type <i> kern_mp_limit
CF_GRAMMAR CF_GRAMMAR
/* Kernel syncer protocol */ /* Kernel syncer protocol */
@ -43,6 +45,11 @@ kern_proto_start: proto_start KERNEL {
CF_ADDTO(kern_proto, kern_proto_start proto_name '{') CF_ADDTO(kern_proto, kern_proto_start proto_name '{')
CF_ADDTO(kern_proto, kern_proto kern_item ';') CF_ADDTO(kern_proto, kern_proto kern_item ';')
kern_mp_limit:
/* empty */ { $$ = KRT_DEFAULT_ECMP_LIMIT; }
| LIMIT expr { $$ = $2; if (($2 <= 0) || ($2 > 255)) cf_error("Merge paths limit must be in range 1-255"); }
;
kern_item: kern_item:
proto_item proto_item
| proto_channel { this_proto->net_type = $1->net_type; } | proto_channel { this_proto->net_type = $1->net_type; }
@ -55,13 +62,18 @@ kern_item:
THIS_KRT->learn = $2; THIS_KRT->learn = $2;
#ifndef KRT_ALLOW_LEARN #ifndef KRT_ALLOW_LEARN
if ($2) if ($2)
cf_error("Learning of kernel routes not supported in this configuration"); cf_error("Learning of kernel routes not supported on this platform");
#endif #endif
} }
| DEVICE ROUTES bool { THIS_KRT->devroutes = $3; } | DEVICE ROUTES bool { THIS_KRT->devroutes = $3; }
| GRACEFUL RESTART bool { THIS_KRT->graceful_restart = $3; } | GRACEFUL RESTART bool { THIS_KRT->graceful_restart = $3; }
| MERGE PATHS bool { krt_set_merge_paths(this_channel, $3, KRT_DEFAULT_ECMP_LIMIT); } | MERGE PATHS bool kern_mp_limit {
| MERGE PATHS bool LIMIT expr { krt_set_merge_paths(this_channel, $3, $5); } krt_set_merge_paths(this_channel, $3, $4);
#ifndef KRT_ALLOW_MERGE_PATHS
if ($3)
cf_error("Path merging not supported on this platform");
#endif
}
; ;
/* Kernel interface protocol */ /* Kernel interface protocol */

View file

@ -604,7 +604,7 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa)
rte *rt; rte *rt;
if (c->ra_mode == RA_MERGED) if (c->ra_mode == RA_MERGED)
return rt_export_merged(c, net, rt_free, tmpa, 1); return rt_export_merged(c, net, rt_free, tmpa, krt_filter_lp, 1);
rt = net->routes; rt = net->routes;
*rt_free = NULL; *rt_free = NULL;

View file

@ -20,6 +20,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include "nest/bird.h" #include "nest/bird.h"
#include "nest/cli.h" #include "nest/cli.h"
@ -209,6 +210,7 @@ bug(const char *msg, ...)
va_start(args, msg); va_start(args, msg);
vlog(L_BUG[0], msg, args); vlog(L_BUG[0], msg, args);
va_end(args);
abort(); abort();
} }
@ -226,6 +228,7 @@ die(const char *msg, ...)
va_start(args, msg); va_start(args, msg);
vlog(L_FATAL[0], msg, args); vlog(L_FATAL[0], msg, args);
va_end(args);
exit(1); exit(1);
} }
@ -312,7 +315,11 @@ log_init_debug(char *f)
else if (!*f) else if (!*f)
dbgf = stderr; dbgf = stderr;
else if (!(dbgf = fopen(f, "a"))) else if (!(dbgf = fopen(f, "a")))
log(L_ERR "Error opening debug file `%s': %m", f); {
/* Cannot use die() nor log() here, logging is not yet initialized */
fprintf(stderr, "bird: Unable to open debug file %s: %s\n", f, strerror(errno));
exit(1);
}
if (dbgf) if (dbgf)
setvbuf(dbgf, NULL, _IONBF, 0); setvbuf(dbgf, NULL, _IONBF, 0);
} }

View file

@ -621,7 +621,7 @@ signal_init(void)
* Parsing of command-line arguments * Parsing of command-line arguments
*/ */
static char *opt_list = "c:dD:ps:P:u:g:flR"; static char *opt_list = "c:dD:ps:P:u:g:flRh";
static int parse_and_exit; static int parse_and_exit;
char *bird_name; char *bird_name;
static char *use_user; static char *use_user;
@ -629,10 +629,43 @@ static char *use_group;
static int run_in_foreground = 0; static int run_in_foreground = 0;
static void static void
usage(void) display_usage(void)
{ {
fprintf(stderr, "Usage: %s [-c <config-file>] [-d] [-D <debug-file>] [-p] [-s <control-socket>] [-P <pid-file>] [-u <user>] [-g <group>] [-f] [-l] [-R]\n", bird_name); fprintf(stderr, "Usage: %s [--version] [--help] [-c <config-file>] [OPTIONS]\n", bird_name);
exit(1); }
static void
display_help(void)
{
display_usage();
fprintf(stderr,
"\n"
"Options: \n"
" -c <config-file> Use given configuration file instead\n"
" of prefix/etc/bird.conf\n"
" -d Enable debug messages and run bird in foreground\n"
" -D <debug-file> Log debug messages to given file instead of stderr\n"
" -f Run bird in foreground\n"
" -g <group> Use given group ID\n"
" -h, --help Display this information\n"
" -l Look for a configuration file and a communication socket\n"
" file in the current working directory\n"
" -p Test configuration file and exit without start\n"
" -P <pid-file> Create a PID file with given filename\n"
" -R Apply graceful restart recovery after start\n"
" -s <control-socket> Use given filename for a control socket\n"
" -u <user> Drop privileges and use given user ID\n"
" --version Display version of BIRD\n");
exit(0);
}
static void
display_version(void)
{
fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
exit(0);
} }
static inline char * static inline char *
@ -706,12 +739,9 @@ parse_args(int argc, char **argv)
if (argc == 2) if (argc == 2)
{ {
if (!strcmp(argv[1], "--version")) if (!strcmp(argv[1], "--version"))
{ display_version();
fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
exit(0);
}
if (!strcmp(argv[1], "--help")) if (!strcmp(argv[1], "--help"))
usage(); display_help();
} }
while ((c = getopt(argc, argv, opt_list)) >= 0) while ((c = getopt(argc, argv, opt_list)) >= 0)
switch (c) switch (c)
@ -755,11 +785,19 @@ parse_args(int argc, char **argv)
case 'R': case 'R':
graceful_restart_recovery(); graceful_restart_recovery();
break; break;
case 'h':
display_help();
break;
default: default:
usage(); fputc('\n', stderr);
display_usage();
exit(1);
} }
if (optind < argc) if (optind < argc)
usage(); {
display_usage();
exit(1);
}
} }
/* /*