0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-03 07:31:54 +00:00

Merge commit '0aeac9cb7f9887374ce0258c8653f9518529bf08' into integrated

Conflicts:

	configure.in
	lib/printf.c
	proto/ospf/config.Y
	proto/ospf/hello.c
	proto/ospf/iface.c
	proto/ospf/ospf.h
This commit is contained in:
Ondrej Zajicek 2014-04-29 16:17:09 +02:00
commit f47914c8a8
33 changed files with 1460 additions and 507 deletions

12
aclocal.m4 vendored
View File

@ -133,6 +133,18 @@ if test "$bird_cv_struct_ip_mreqn" = yes ; then
fi
])
AC_DEFUN(BIRD_CHECK_PTHREADS,
[
bird_tmp_cflags="$CFLAGS"
CFLAGS="$CFLAGS -pthread"
AC_CACHE_CHECK([whether POSIX threads are available], bird_cv_lib_pthreads,
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[pthread_t pt; pthread_create(&pt, NULL, NULL, NULL); pthread_spinlock_t lock; pthread_spin_lock(&lock); ]])],
[bird_cv_lib_pthreads=yes], [bird_cv_lib_pthreads=no])])
CFLAGS="$bird_tmp_cflags"
])
AC_DEFUN(BIRD_CHECK_GCC_OPTION,
[
bird_tmp_cflags="$CFLAGS"

View File

@ -90,7 +90,7 @@ CF_DECLS
%left '!'
%nonassoc '.'
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, XS, XMS, XUS)
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US)
CF_GRAMMAR
@ -129,11 +129,10 @@ expr:
;
/* XXX fix X* symbols, they collide with macros */
expr_us:
expr XS { $$ = (u32) $1 * 1000000; }
| expr XMS { $$ = (u32) $1 * 1000; }
| expr XUS { $$ = (u32) $1 * 1; }
expr S { $$ = (u32) $1 * 1000000; }
| expr MS { $$ = (u32) $1 * 1000; }
| expr US { $$ = (u32) $1 * 1; }
;
/* expr_u16: expr { check_u16($1); $$ = $1; }; */

View File

@ -9,6 +9,7 @@ AC_CONFIG_AUX_DIR(tools)
AC_ARG_ENABLE(debug, [ --enable-debug enable internal debugging routines (default: disabled)],,enable_debug=no)
AC_ARG_ENABLE(memcheck, [ --enable-memcheck check memory allocations when debugging (default: enabled)],,enable_memcheck=yes)
AC_ARG_ENABLE(client, [ --enable-client enable building of BIRD client (default: enabled)],,enable_client=yes)
AC_ARG_ENABLE(pthreads, [ --enable-pthreads enable POSIX threads support (default: detect)],,enable_pthreads=try)
AC_ARG_WITH(sysconfig, [ --with-sysconfig=FILE use specified BIRD system configuration file])
AC_ARG_WITH(protocols, [ --with-protocols=LIST include specified routing protocols (default: all)],,[with_protocols="all"])
AC_ARG_WITH(sysinclude, [ --with-sysinclude=PATH search for system includes on specified place])
@ -42,12 +43,6 @@ AC_SUBST(exedir)
AC_SUBST(srcdir_rel_mf)
AC_SUBST(runtimedir)
# all_protocols=bfd,bgp,ospf,pipe,radv,rip,static
all_protocols=pipe,radv,rip,static
if test "$with_protocols" = all ; then
with_protocols="$all_protocols"
fi
if test "$enable_debug" = yes ; then
CONFIG_FILE="bird.conf"
@ -73,12 +68,29 @@ if test -z "$GCC" ; then
AC_MSG_ERROR([This program requires the GNU C Compiler.])
fi
if test "$enable_pthreads" != no ; then
BIRD_CHECK_PTHREADS
if test "$bird_cv_lib_pthreads" = yes ; then
AC_DEFINE(USE_PTHREADS)
CFLAGS="$CFLAGS -pthread"
LDFLAGS="$LDFLAGS -pthread"
proto_bfd=bfd
elif test "$enable_pthreads" = yes ; then
AC_MSG_ERROR([POSIX threads not available.])
fi
if test "$enable_pthreads" = try ; then
enable_pthreads="$bird_cv_lib_pthreads"
fi
fi
if test "$bird_cflags_default" = yes ; then
BIRD_CHECK_GCC_OPTION(bird_cv_c_option_wno_pointer_sign, -Wno-pointer-sign, -Wall)
BIRD_CHECK_GCC_OPTION(bird_cv_c_option_fno_strict_aliasing, -fno-strict-aliasing)
BIRD_CHECK_GCC_OPTION(bird_cv_c_option_fno_strict_overflow, -fno-strict-overflow)
CFLAGS="$CFLAGS -pthread -Wall -Wstrict-prototypes -Wno-parentheses"
CFLAGS="$CFLAGS -Wall -Wstrict-prototypes -Wno-parentheses"
BIRD_ADD_GCC_OPTION(bird_cv_c_option_wno_pointer_sign, -Wno-pointer-sign)
BIRD_ADD_GCC_OPTION(bird_cv_c_option_fno_strict_aliasing, -fno-strict-aliasing)
BIRD_ADD_GCC_OPTION(bird_cv_c_option_fno_strict_overflow, -fno-strict-overflow)
@ -154,6 +166,14 @@ fi
AC_SUBST(iproutedir)
# all_protocols="$proto_bfd bgp ospf pipe radv rip static"
all_protocols="pipe radv rip static"
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
if test "$with_protocols" = all ; then
with_protocols="$all_protocols"
fi
AC_MSG_CHECKING([protocols])
protocols=`echo "$with_protocols" | sed 's/,/ /g'`
if test "$protocols" = no ; then protocols= ; fi
@ -243,6 +263,7 @@ BIRD was configured with the following options:
Iproute2 directory: $iproutedir
System configuration: $sysdesc
Debugging: $enable_debug
POSIX threads: $enable_pthreads
Routing protocols: $protocols
Client: $enable_client
EOF

View File

@ -144,7 +144,10 @@ options. The most important ones are:
nonzero if there are some errors.
<tag>-s <m/name of communication socket/</tag>
use given filename for a socket for communications with the client, default is <it/prefix/<file>/var/run/bird.ctl</file>.
use given filename for a socket for communications with the client, default is <it/prefix/<file>/var/run/bird.ctl</file>.
<tag>-P <m/name of PID file/</tag>
create a PID file with given filename</file>.
<tag>-u <m/user/</tag>
drop privileges and use that user ID, see the next section for details.
@ -915,62 +918,63 @@ bird>
incompatible with each other (that is to prevent you from shooting in the foot).
<descrip>
<tag/bool/ This is a boolean type, it can have only two values, <cf/true/ and
<cf/false/. Boolean is the only type you can use in <cf/if/
statements.
<tag/bool/ This is a boolean type, it can have only two values,
<cf/true/ and <cf/false/. Boolean is the only type you can use in
<cf/if/ statements.
<tag/int/ This is a general integer type, you can expect it to store signed values from -2000000000
to +2000000000. Overflows are not checked. You can use <cf/0x1234/ syntax to write hexadecimal values.
<tag/int/ This is a general integer type, you can expect it to store
signed values from -2000000000 to +2000000000. Overflows are not
checked. You can use <cf/0x1234/ syntax to write hexadecimal values.
<tag/pair/ This is a pair of two short integers. Each component can have values from 0 to
65535. Literals of this type are written as <cf/(1234,5678)/. The same syntax can also be
used to construct a pair from two arbitrary integer expressions (for example <cf/(1+2,a)/).
<tag/pair/ This is a pair of two short integers. Each component can have
values from 0 to 65535. Literals of this type are written as
<cf/(1234,5678)/. The same syntax can also be used to construct a pair
from two arbitrary integer expressions (for example <cf/(1+2,a)/).
<tag/quad/ This is a dotted quad of numbers used to represent
router IDs (and others). Each component can have a value
from 0 to 255. Literals of this type are written like IPv4
addresses.
<tag/quad/ This is a dotted quad of numbers used to represent router IDs
(and others). Each component can have a value from 0 to 255. Literals
of this type are written like IPv4 addresses.
<tag/string/ This is a string of characters. There are no ways
to modify strings in filters. You can pass them between
functions, assign them to variables of type <cf/string/,
print such variables, use standard string comparison
operations (e.g. <cf/=, !=, &lt;, &gt;, &lt;=, &gt;=/), but
you can't concatenate two strings. String literals are
written as <cf/"This is a string constant"/. Additionaly
matching <cf/&tilde;/ operator could be used to match a
string value against a shell pattern (represented also as a
string).
<tag/string/ This is a string of characters. There are no ways to modify
strings in filters. You can pass them between functions, assign them
to variables of type <cf/string/, print such variables, use standard
string comparison operations (e.g. <cf/=, !=, &lt;, &gt;, &lt;=,
&gt;=/), but you can't concatenate two strings. String literals are
written as <cf/"This is a string constant"/. Additionaly matching
<cf/&tilde;/ operator could be used to match a string value against a
shell pattern (represented also as a string).
<tag/ip/ This type can hold a single IP address. Depending on the compile-time configuration of BIRD you are using, it
is either an IPv4 or IPv6 address. IP addresses are written in the standard notation (<cf/10.20.30.40/ or <cf/fec0:3:4::1/). You can apply special operator <cf>.mask(<M>num</M>)</cf>
on values of type ip. It masks out all but first <cf><M>num</M></cf> bits from the IP
address. So <cf/1.2.3.4.mask(8) = 1.0.0.0/ is true.
<tag/ip/ This type can hold a single IP address. Depending on the
compile-time configuration of BIRD you are using, it is either an IPv4
or IPv6 address. IP addresses are written in the standard notation
(<cf/10.20.30.40/ or <cf/fec0:3:4::1/). You can apply special
operator <cf>.mask(<M>num</M>)</cf> on values of type ip. It masks out
all but first <cf><M>num</M></cf> bits from the IP address. So
<cf/1.2.3.4.mask(8) = 1.0.0.0/ is true.
<tag/prefix/ This type can hold a network prefix consisting of IP address and prefix length. Prefix literals are written as
<cf><M>ipaddress</M>/<M>pxlen</M></cf>, or
<tag/prefix/ This type can hold a network prefix consisting of IP
address and prefix length. Prefix literals are written
as <cf><M>ipaddress</M>/<M>pxlen</M></cf>, 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 pair, and <cf/.len/, which separates prefix
length from the pair. So <cf>1.2.0.0/16.pxlen = 16</cf> is true.
operators on prefixes: <cf/.ip/ which extracts the IP address from the
pair, and <cf/.len/, which separates prefix length from the
pair. So <cf>1.2.0.0/16.pxlen = 16</cf> is true.
<tag/ec/ This is a specialized type used to represent BGP
extended community values. It is essentially a 64bit value,
literals of this type are usually written as <cf>(<m/kind/,
<m/key/, <m/value/)</cf>, where <cf/kind/ is a kind of
extended community (e.g. <cf/rt/ / <cf/ro/ for a route
target / route origin communities), the format and possible
values of <cf/key/ and <cf/value/ are usually integers, but
<tag/ec/ This is a specialized type used to represent BGP extended
community values. It is essentially a 64bit value, literals of this
type are usually written as <cf>(<m/kind/, <m/key/, <m/value/)</cf>,
where <cf/kind/ is a kind of extended community (e.g. <cf/rt/ /
<cf/ro/ for a route target / route origin communities), the format and
possible values of <cf/key/ and <cf/value/ are usually integers, but
it depends on the used kind. Similarly to pairs, ECs can be
constructed using expressions for <cf/key/ and
<cf/value/ parts, (e.g. <cf/(ro, myas, 3*10)/, where
<cf/myas/ is an integer variable).
constructed using expressions for <cf/key/ and <cf/value/ parts,
(e.g. <cf/(ro, myas, 3*10)/, where <cf/myas/ is an integer variable).
<tag/int|pair|quad|ip|prefix|ec|enum set/
Filters recognize four types of sets. Sets are similar to strings: you can pass them around
but you can't modify them. Literals of type <cf>int set</cf> look like <cf>
[ 1, 2, 5..7 ]</cf>. As you can see, both simple values and ranges are permitted in
sets.
<tag/int|pair|quad|ip|prefix|ec|enum set/ Filters recognize four types
of sets. Sets are similar to strings: you can pass them around but you
can't modify them. Literals of type <cf>int set</cf> look like <cf> [
1, 2, 5..7 ]</cf>. As you can see, both simple values and ranges are
permitted in sets.
For pair sets, expressions like <cf/(123,*)/ can be used to denote ranges (in
that case <cf/(123,0)..(123,65535)/). You can also use <cf/(123,5..100)/ for range
@ -1267,6 +1271,178 @@ undefined value is regarded as empty clist for most purposes.
<chapt>Protocols
<sect><label id="sect-bfd">BFD
<sect1>Introduction
<p>Bidirectional Forwarding Detection (BFD) is not a routing protocol itself, it
is an independent tool providing liveness and failure detection. Routing
protocols like OSPF and BGP use integrated periodic "hello" messages to monitor
liveness of neighbors, but detection times of these mechanisms are high (e.g. 40
seconds by default in OSPF, could be set down to several seconds). BFD offers
universal, fast and low-overhead mechanism for failure detection, which could be
attached to any routing protocol in an advisory role.
<p>BFD consists of mostly independent BFD sessions. Each session monitors an
unicast bidirectional path between two BFD-enabled routers. This is done by
periodically sending control packets in both directions. BFD does not handle
neighbor discovery, BFD sessions are created on demand by request of other
protocols (like OSPF or BGP), which supply appropriate information like IP
addresses and associated interfaces. When a session changes its state, these
protocols are notified and act accordingly (e.g. break an OSPF adjacency when
the BFD session went down).
<p>BIRD implements basic BFD behavior as defined in
RFC 5880<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc5880.txt">
(some advanced features like the echo mode or authentication are not implemented),
IP transport for BFD as defined in
RFC 5881<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc5881.txt"> and
RFC 5883<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc5883.txt">
and interaction with client protocols as defined in
RFC 5882<htmlurl url="ftp://ftp.rfc-editor.org/in-notes/rfc5882.txt">.
<p>Note that BFD implementation in BIRD is currently a new feature in
development, expect some rough edges and possible UI and configuration changes
in the future. Also note that we currently support at most one protocol instance.
<sect1>Configuration
<p>BFD configuration consists mainly of multiple definitions of interfaces.
Most BFD config options are session specific. When a new session is requested
and dynamically created, it is configured from one of these definitions. For
sessions to directly connected neighbors, <cf/interface/ definitions are chosen
based on the interface associated with the session, while <cf/multihop/
definition is used for multihop sessions. If no definition is relevant, the
session is just created with the default configuration. Therefore, an empty BFD
configuration is often sufficient.
<p>Note that to use BFD for other protocols like OSPF or BGP, these protocols
also have to be configured to request BFD sessions, usually by <cf/bfd/ option.
<p>Some of BFD session options require <m/time/ value, which has to be specified
with the appropriate unit: <m/num/ <cf/s/|<cf/ms/|<cf/us/. Although microseconds
are allowed as units, practical minimum values are usually in order of tens of
milliseconds.
<code>
protocol bfd [&lt;name&gt;] {
interface &lt;interface pattern&gt; {
interval &lt;time&gt;;
min rx interval &lt;time&gt;;
min tx interval &lt;time&gt;;
idle tx interval &lt;time&gt;;
multiplier &lt;num&gt;;
passive &lt;switch&gt;;
};
multihop {
interval &lt;time&gt;;
min rx interval &lt;time&gt;;
min tx interval &lt;time&gt;;
idle tx interval &lt;time&gt;;
multiplier &lt;num&gt;;
passive &lt;switch&gt;;
};
neighbor &lt;ip&gt; [dev "&lt;interface&gt;"] [local &lt;ip&gt;] [multihop &lt;switch&gt;];
}
</code>
<descrip>
<tag>interface <m/pattern [, ...]/ { <m/options/ }</tag>
Interface definitions allow to specify options for sessions associated
with such interfaces and also may contain interface specific options.
See <ref id="dsc-iface" name="interface"> common option for a detailed
description of interface patterns. Note that contrary to the behavior of
<cf/interface/ definitions of other protocols, BFD protocol would accept
sessions (in default configuration) even on interfaces not covered by
such definitions.
<tag>multihop { <m/options/ }</tag>
Multihop definitions allow to specify options for multihop BFD sessions,
in the same manner as <cf/interface/ definitions are used for directly
connected sessions. Currently only one such definition (for all multihop
sessions) could be used.
<tag>neighbor <m/ip/ [dev "<m/interface/"] [local <m/ip/] [multihop <m/switch/]</tag>
BFD sessions are usually created on demand as requested by other
protocols (like OSPF or BGP). This option allows to explicitly add
a BFD session to the specified neighbor regardless of such requests.
The session is identified by the IP address of the neighbor, with
optional specification of used interface and local IP. By default
the neighbor must be directly connected, unless the the session is
configured as multihop. Note that local IP must be specified for
multihop sessions.
</descrip>
<p>Session specific options (part of <cf/interface/ and <cf/multihop/ definitions):
<descrip>
<tag>interval <m/time/</tag>
BFD ensures availability of the forwarding path associated with the
session by periodically sending BFD control packets in both
directions. The rate of such packets is controlled by two options,
<cf/min rx interval/ and <cf/min tx interval/ (see below). This option
is just a shorthand to set both of these options together.
<tag>min rx interval <m/time/</tag>
This option specifies the minimum RX interval, which is announced to the
neighbor and used there to limit the neighbor's rate of generated BFD
control packets. Default: 10 ms.
<tag>min tx interval <m/time/</tag>
This option specifies the desired TX interval, which controls the rate
of generated BFD control packets (together with <cf/min rx interval/
announced by the neighbor). Note that this value is used only if the BFD
session is up, otherwise the value of <cf/idle tx interval/ is used
instead. Default: 100 ms.
<tag>idle tx interval <m/time/</tag>
In order to limit unnecessary traffic in cases where a neighbor is not
available or not running BFD, the rate of generated BFD control packets
is lower when the BFD session is not up. This option specifies the
desired TX interval in such cases instead of <cf/min tx interval/.
Default: 1 s.
<tag>multiplier <m/num/</tag>
Failure detection time for BFD sessions is based on established rate of
BFD control packets (<cf>min rx/tx interval</cf>) multiplied by this
multiplier, which is essentially (ignoring jitter) a number of missed
packets after which the session is declared down. Note that rates and
multipliers could be different in each direction of a BFD session.
Default: 5.
<tag>passive <m/switch/</tag>
Generally, both BFD session endpoinds try to establish the session by
sending control packets to the other side. This option allows to enable
passive mode, which means that the router does not send BFD packets
until it has received one from the other side. Default: disabled.
</descrip>
<sect1>Example
<p><code>
protocol bfd {
interface "eth*" {
min rx interval 20 ms;
min tx interval 50 ms;
idle tx interval 300 ms;
};
interface "gre*" {
interval 200 ms;
multiplier 10;
passive;
};
multihop {
interval 200 ms;
multiplier 10;
};
neighbor 192.168.1.10;
neighbor 192.168.2.2 dev "eth2";
neighbor 192.168.10.1 local 192.168.1.1 multihop;
}
</code>
<sect>BGP
<p>The Border Gateway Protocol is the routing protocol used for backbone
@ -1281,8 +1457,8 @@ AS). Each AS is a part of the network with common management and
common routing policy. It is identified by a unique 16-bit number
(ASN). Routers within each AS usually exchange AS-internal routing
information with each other using an interior gateway protocol (IGP,
such as OSPF or RIP). Boundary routers at the border of
the AS communicate global (inter-AS) network reachability information with
such as OSPF or RIP). Boundary routers at the border of the AS
communicate global (inter-AS) network reachability information with
their neighbors in the neighboring AS'es via exterior BGP (eBGP) and
redistribute received information to other routers in the AS via
interior BGP (iBGP).
@ -1435,7 +1611,15 @@ for each neighbor using the following configuration parameters:
<tag>igp table <m/name/</tag> Specifies a table that is used
as an IGP routing table. Default: the same as the table BGP is
connected to.
<tag>bfd <M>switch</M></tag>
BGP could use BFD protocol as an advisory mechanism for neighbor
liveness and failure detection. If enabled, BIRD setups a BFD session
for the BGP neighbor and tracks its liveness by it. This has an
advantage of an order of magnitude lower detection times in case of
failure. Note that BFD protocol also has to be configured, see
<ref id="sect-bfd" name="BFD"> section for details. Default: disabled.
<tag>ttl security <m/switch/</tag> Use GTSM (RFC 5082 - the
generalized TTL security mechanism). GTSM protects against
spoofed packets by ignoring received packets with a smaller
@ -2020,6 +2204,7 @@ protocol ospf &lt;name&gt; {
real broadcast &lt;switch&gt;;
ptp netmask &lt;switch&gt;;
check link &lt;switch&gt;;
bfd &lt;switch&gt;;
ecmp weight &lt;num&gt;;
ttl security [&lt;switch&gt;; | tx only]
tx class|dscp &lt;num&gt;;
@ -2294,6 +2479,14 @@ protocol ospf &lt;name&gt; {
prefix) is propagated. It is possible that some hardware
drivers or platforms do not implement this feature. Default value is no.
<tag>bfd <M>switch</M></tag>
OSPF could use BFD protocol as an advisory mechanism for neighbor
liveness and failure detection. If enabled, BIRD setups a BFD session
for each OSPF neighbor and tracks its liveness by it. This has an
advantage of an order of magnitude lower detection times in case of
failure. Note that BFD protocol also has to be configured, see
<ref id="sect-bfd" name="BFD"> section for details. Default value is no.
<tag>ttl security [<m/switch/ | tx only]</tag>
TTL security is a feature that protects routing protocols
from remote spoofed packets by using TTL 255 instead of TTL 1

View File

@ -59,41 +59,35 @@ adata_empty(struct linpool *pool, int l)
}
static void
pm_format(struct f_path_mask *p, byte *buf, unsigned int size)
pm_format(struct f_path_mask *p, buffer *buf)
{
byte *end = buf + size - 16;
buffer_puts(buf, "[= ");
while (p)
{
switch(p->kind)
{
if (buf > end)
{
strcpy(buf, " ...");
return;
}
case PM_ASN:
buffer_print(buf, "%u ", p->val);
break;
switch(p->kind)
{
case PM_ASN:
buf += bsprintf(buf, " %u", p->val);
break;
case PM_QUESTION:
buffer_puts(buf, "? ");
break;
case PM_QUESTION:
buf += bsprintf(buf, " ?");
break;
case PM_ASTERISK:
buffer_puts(buf, "* ");
break;
case PM_ASTERISK:
buf += bsprintf(buf, " *");
break;
case PM_ASN_EXPR:
buf += bsprintf(buf, " %u", f_eval_asn((struct f_inst *) p->val));
break;
}
p = p->next;
case PM_ASN_EXPR:
buffer_print(buf, "%u ", f_eval_asn((struct f_inst *) p->val));
break;
}
*buf = 0;
p = p->next;
}
buffer_puts(buf, "=]");
}
static inline int
@ -103,7 +97,7 @@ int_cmp(int i1, int i2)
}
static inline int
uint_cmp(unsigned int i1, unsigned int i2)
uint_cmp(uint i1, uint i2)
{
return (int)(i1 > i2) - (int)(i1 < i2);
}
@ -437,60 +431,32 @@ val_in_range(struct f_val v1, struct f_val v2)
return CMP_ERROR;
}
static void
tree_node_print(struct f_tree *t, char **sep)
{
if (t == NULL)
return;
tree_node_print(t->left, sep);
logn(*sep);
val_print(t->from);
if (val_compare(t->from, t->to) != 0)
{
logn( ".." );
val_print(t->to);
}
*sep = ", ";
tree_node_print(t->right, sep);
}
static void
tree_print(struct f_tree *t)
{
char *sep = "";
logn( "[" );
tree_node_print(t, &sep);
logn( "] " );
}
/*
* val_print - format filter value
* val_format - format filter value
*/
void
val_print(struct f_val v)
val_format(struct f_val v, buffer *buf)
{
char buf2[1024];
switch (v.type) {
case T_VOID: logn("(void)"); return;
case T_BOOL: logn(v.val.i ? "TRUE" : "FALSE"); return;
case T_INT: logn("%d", v.val.i); return;
case T_STRING: logn("%s", v.val.s); return;
case T_IP: logn("%I", v.val.px.ip); return;
case T_PREFIX: logn("%I/%d", v.val.px.ip, v.val.px.len); return;
case T_PAIR: logn("(%d,%d)", v.val.i >> 16, v.val.i & 0xffff); return;
case T_QUAD: logn("%R", v.val.i); return;
case T_EC: ec_format(buf2, v.val.ec); logn("%s", buf2); return;
case T_PREFIX_SET: trie_print(v.val.ti); return;
case T_SET: tree_print(v.val.t); return;
case T_ENUM: logn("(enum %x)%d", v.type, v.val.i); return;
case T_PATH: as_path_format(v.val.ad, buf2, 1000); logn("(path %s)", buf2); return;
case T_CLIST: int_set_format(v.val.ad, 1, -1, buf2, 1000); logn("(clist %s)", buf2); return;
case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); logn("(eclist %s)", buf2); return;
case T_PATH_MASK: pm_format(v.val.path_mask, buf2, 1000); logn("(pathmask%s)", buf2); return;
default: logn( "[unknown type %x]", v.type ); return;
switch (v.type)
{
case T_VOID: buffer_puts(buf, "(void)"); return;
case T_BOOL: buffer_puts(buf, v.val.i ? "TRUE" : "FALSE"); return;
case T_INT: buffer_print(buf, "%d", v.val.i); return;
case T_STRING: buffer_print(buf, "%s", v.val.s); return;
case T_IP: buffer_print(buf, "%I", v.val.px.ip); return;
case T_PREFIX: buffer_print(buf, "%I/%d", v.val.px.ip, v.val.px.len); return;
case T_PAIR: buffer_print(buf, "(%d,%d)", v.val.i >> 16, v.val.i & 0xffff); return;
case T_QUAD: buffer_print(buf, "%R", v.val.i); return;
case T_EC: ec_format(buf2, v.val.ec); buffer_print(buf, "%s", buf2); return;
case T_PREFIX_SET: trie_format(v.val.ti, buf); return;
case T_SET: tree_format(v.val.t, buf); return;
case T_ENUM: buffer_print(buf, "(enum %x)%d", v.type, v.val.i); return;
case T_PATH: as_path_format(v.val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return;
case T_CLIST: int_set_format(v.val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return;
case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
case T_PATH_MASK: pm_format(v.val.path_mask, buf); return;
default: buffer_print(buf, "[unknown type %x]", v.type); return;
}
}
@ -498,6 +464,7 @@ static struct rte **f_rte;
static struct rta *f_old_rta;
static struct ea_list **f_tmp_attrs;
static struct linpool *f_pool;
static struct buffer f_buf;
static int f_flags;
static inline void f_rte_cow(void)
@ -786,7 +753,7 @@ interpret(struct f_inst *what)
break;
case 'p':
ONEARG;
val_print(v1);
val_format(v1, &f_buf);
break;
case '?': /* ? has really strange error value, so we can implement if ... else nicely :-) */
ONEARG;
@ -804,7 +771,7 @@ interpret(struct f_inst *what)
case P('p',','):
ONEARG;
if (what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p))
log_commit(*L_INFO);
log_commit(*L_INFO, &f_buf);
switch (what->a2.i) {
case F_QUITBIRD:
@ -1507,7 +1474,8 @@ f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struc
f_pool = tmp_pool;
f_flags = flags;
log_reset();
LOG_BUFFER_INIT(f_buf);
struct f_val res = interpret(filter->root);
if (f_old_rta) {
@ -1546,7 +1514,8 @@ f_eval(struct f_inst *expr, struct linpool *tmp_pool)
f_rte = NULL;
f_pool = tmp_pool;
log_reset();
LOG_BUFFER_INIT(f_buf);
return interpret(expr);
}

View File

@ -81,12 +81,13 @@ struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, s
struct f_tree *build_tree(struct f_tree *);
struct f_tree *find_tree(struct f_tree *t, struct f_val val);
int same_tree(struct f_tree *t1, struct f_tree *t2);
void tree_format(struct f_tree *t, buffer *buf);
struct f_trie *f_new_trie(linpool *lp);
void trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h);
int trie_match_prefix(struct f_trie *t, ip_addr px, int plen);
int trie_same(struct f_trie *t1, struct f_trie *t2);
void trie_print(struct f_trie *t);
void trie_format(struct f_trie *t, buffer *buf);
void fprefix_get_bounds(struct f_prefix *px, int *l, int *h);
@ -121,7 +122,8 @@ int i_same(struct f_inst *f1, struct f_inst *f2);
int val_compare(struct f_val v1, struct f_val v2);
int val_same(struct f_val v1, struct f_val v2);
void val_print(struct f_val v);
void val_format(struct f_val v, buffer *buf);
#define F_NOP 0
#define F_NONL 1

View File

@ -106,7 +106,7 @@ eclist el2;
print "5 = ", p2.len;
print "Delete 3: ", delete(p2, 3);
print "Filter 1-3: ", filter(p2, [1..3]);
pm1 = [= 1 2 * 3 4 5 =];
p2 = prepend( + empty +, 5 );
p2 = prepend( p2, 4 );

View File

@ -137,3 +137,37 @@ same_tree(struct f_tree *t1, struct f_tree *t2)
return 0;
return 1;
}
static void
tree_node_format(struct f_tree *t, buffer *buf)
{
if (t == NULL)
return;
tree_node_format(t->left, buf);
val_format(t->from, buf);
if (val_compare(t->from, t->to) != 0)
{
buffer_puts(buf, "..");
val_format(t->to, buf);
}
buffer_puts(buf, ", ");
tree_node_format(t->right, buf);
}
void
tree_format(struct f_tree *t, buffer *buf)
{
buffer_puts(buf, "[");
tree_node_format(t, buf);
/* Undo last separator */
if (buf->pos[-1] != '[')
buf->pos -= 2;
buffer_puts(buf, "]");
}

View File

@ -265,37 +265,37 @@ trie_same(struct f_trie *t1, struct f_trie *t2)
}
static void
trie_node_print(struct f_trie_node *t, char **sep)
trie_node_format(struct f_trie_node *t, buffer *buf)
{
if (t == NULL)
return;
if (ipa_nonzero(t->accept))
{
logn("%s%I/%d{%I}", *sep, t->addr, t->plen, t->accept);
*sep = ", ";
}
buffer_print(buf, "%I/%d{%I}, ", t->addr, t->plen, t->accept);
trie_node_print(t->c[0], sep);
trie_node_print(t->c[1], sep);
trie_node_format(t->c[0], buf);
trie_node_format(t->c[1], buf);
}
/**
* trie_print
* @t: trie to be printed
* trie_format
* @t: trie to be formatted
* @buf: destination buffer
*
* Prints the trie to the log buffer.
* Prints the trie to the supplied buffer.
*/
void
trie_print(struct f_trie *t)
trie_format(struct f_trie *t, buffer *buf)
{
char *sep = "";
logn("[");
buffer_puts(buf, "[");
if (t->zero)
{
logn("%I/%d", IPA_NONE, 0);
sep = ", ";
}
trie_node_print(&t->root, &sep);
logn("]");
buffer_print(buf, "%I/%d", IPA_NONE, 0);
trie_node_format(&t->root, buf);
/* Undo last separator */
if (buf->pos[-1] != '[')
buf->pos -= 2;
buffer_puts(buf, "]");
}

View File

@ -10,6 +10,7 @@
#define _BIRD_BIRDLIB_H_
#include "timer.h"
#include "alloca.h"
/* Ugly structure offset handling macros */
@ -19,12 +20,14 @@
/* Utility macros */
#ifdef PARSER
#define _MIN(a,b) (((a)<(b))?(a):(b))
#define _MAX(a,b) (((a)>(b))?(a):(b))
#else
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#ifndef PARSER
#undef MIN
#undef MAX
#define MIN(a,b) _MIN(a,b)
#define MAX(a,b) _MAX(a,b)
#endif
#define ABS(a) ((a)>=0 ? (a) : -(a))
@ -40,24 +43,61 @@
#define IP_VERSION 6
#endif
/* Macros for gcc attributes */
#define NORET __attribute__((noreturn))
#define UNUSED __attribute__((unused))
/* Microsecond time */
typedef s64 btime;
#define _S *1000000
#define _MS *1000
#define _US *1
#define TO_S /1000000
#define TO_MS /1000
#define TO_US /1
#ifndef PARSER
#define S _S
#define MS _MS
#define US _US
#endif
/* Logging and dying */
typedef struct buffer {
byte *start;
byte *pos;
byte *end;
} buffer;
#define STACK_BUFFER_INIT(buf,size) \
do { \
buf.start = alloca(size); \
buf.pos = buf.start; \
buf.end = buf.start + size; \
} while(0)
#define LOG_BUFFER_INIT(buf) \
STACK_BUFFER_INIT(buf, LOG_BUFFER_SIZE)
#define LOG_BUFFER_SIZE 1024
struct rate_limit {
bird_clock_t timestamp;
int count;
};
#define log log_msg
void log_reset(void);
void log_commit(int class);
void log_commit(int class, buffer *buf);
void log_msg(char *msg, ...);
void log_rl(struct rate_limit *rl, char *msg, ...);
void logn(char *msg, ...);
void die(char *msg, ...) NORET;
void bug(char *msg, ...) NORET;

View File

@ -282,7 +282,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
ip4_ntop(ipa_to_ip4(ip), ipbuf);
else
ip6_ntop(ipa_to_ip6(ip), ipbuf);
if (field_width > 0)
if (field_width == 1)
field_width = STD_ADDRESS_P_LENGTH;
}
s = ipbuf;
@ -418,3 +418,40 @@ int bsnprintf(char * buf, int size, const char *fmt, ...)
va_end(args);
return i;
}
int
buffer_vprint(buffer *buf, const char *fmt, va_list args)
{
int i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
return i;
}
int
buffer_print(buffer *buf, const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i=bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
va_end(args);
buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
return i;
}
void
buffer_puts(buffer *buf, const char *str)
{
byte *bp = buf->pos;
byte *be = buf->end;
while (bp < be && *str)
*bp++ = *str++;
if (bp < be)
*bp = 0;
buf->pos = bp;
}

View File

@ -17,6 +17,10 @@ int bvsprintf(char *str, const char *fmt, va_list args);
int bsnprintf(char *str, int size, const char *fmt, ...);
int bvsnprintf(char *str, int size, const char *fmt, va_list args);
int buffer_vprint(buffer *buf, const char *fmt, va_list args);
int buffer_print(buffer *buf, const char *fmt, ...);
void buffer_puts(buffer *buf, const char *str);
int patmatch(byte *pat, byte *str);
#endif

51
nest/bfd.h Normal file
View File

@ -0,0 +1,51 @@
/*
* BIRD -- Bidirectional Forwarding Detection (BFD)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_NBFD_H_
#define _BIRD_NBFD_H_
#include "lib/lists.h"
#include "lib/resource.h"
struct bfd_session;
struct bfd_request {
resource r;
node n;
ip_addr addr;
ip_addr local;
struct iface *iface;
void (*hook)(struct bfd_request *);
void *data;
struct bfd_session *session;
u8 state;
u8 diag;
u8 old_state;
u8 down;
};
#ifdef CONFIG_BFD
struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, void (*hook)(struct bfd_request *), void *data);
static inline void cf_check_bfd(int use) { }
#else
static inline struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, void (*hook)(struct bfd_request *), void *data) { return NULL; }
static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }
#endif /* CONFIG_BFD */
#endif /* _BIRD_NBFD_H_ */

View File

@ -92,13 +92,10 @@ cmd_show_memory(void)
cli_msg(0, "");
}
extern const char *log_buffer_ptr;
void
cmd_eval(struct f_inst *expr)
{
struct f_val v = f_eval(expr, this_cli->parser_pool);
log_reset();
if (v.type == T_RETURN)
{
@ -106,7 +103,8 @@ cmd_eval(struct f_inst *expr)
return;
}
val_print(v);
cli_msg(23, "%s", log_buffer_ptr);
log_reset();
buffer buf;
LOG_BUFFER_INIT(buf);
val_format(v, &buf);
cli_msg(23, "%s", buf.start);
}

View File

@ -699,6 +699,9 @@ proto_build(struct protocol *p)
}
}
/* FIXME: convert this call to some protocol hook */
extern void bfd_init_all(void);
/**
* protos_build - build a protocol list
*
@ -737,8 +740,10 @@ protos_build(void)
#ifdef CONFIG_BGP
proto_build(&proto_bgp);
#endif
// XXX
#ifdef CONFIG_BFD
proto_build(&proto_bfd);
bfd_init_all();
#endif
proto_pool = rp_new(&root_pool, "Protocols");
proto_flush_event = ev_new(proto_pool);

View File

@ -4,6 +4,103 @@
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/**
* DOC: Bidirectional Forwarding Detection
*
* The BFD protocol is implemented in three files: |bfd.c| containing the
* protocol logic and the protocol glue with BIRD core, |packets.c| handling BFD
* packet processing, RX, TX and protocol sockets. |io.c| then contains generic
* code for the event loop, threads and event sources (sockets, microsecond
* timers). This generic code will be merged to the main BIRD I/O code in the
* future.
*
* The BFD implementation uses a separate thread with an internal event loop for
* handling the protocol logic, which requires high-res and low-latency timing,
* so it is not affected by the rest of BIRD, which has several low-granularity
* hooks in the main loop, uses second-based timers and cannot offer good
* latency. The core of BFD protocol (the code related to BFD sessions,
* interfaces and packets) runs in the BFD thread, while the rest (the code
* related to BFD requests, BFD neighbors and the protocol glue) runs in the
* main thread.
*
* BFD sessions are represented by structure &bfd_session that contains a state
* related to the session and two timers (TX timer for periodic packets and hold
* timer for session timeout). These sessions are allocated from @session_slab
* and are accessible by two hash tables, @session_hash_id (by session ID) and
* @session_hash_ip (by IP addresses of neighbors). Slab and both hashes are in
* the main protocol structure &bfd_proto. The protocol logic related to BFD
* sessions is implemented in internal functions bfd_session_*(), which are
* expected to be called from the context of BFD thread, and external functions
* bfd_add_session(), bfd_remove_session() and bfd_reconfigure_session(), which
* form an interface to the BFD core for the rest and are expected to be called
* from the context of main thread.
*
* Each BFD session has an associated BFD interface, represented by structure
* &bfd_iface. A BFD interface contains a socket used for TX (the one for RX is
* shared in &bfd_proto), an interface configuration and reference counter.
* Compared to interface structures of other protocols, these structures are not
* created and removed based on interface notification events, but according to
* the needs of BFD sessions. When a new session is created, it requests a
* proper BFD interface by function bfd_get_iface(), which either finds an
* existing one in &iface_list (from &bfd_proto) or allocates a new one. When a
* session is removed, an associated iface is dicharged by bfd_free_iface().
*
* BFD requests are the external API for the other protocols. When a protocol
* wants a BFD session, it calls bfd_request_session(), which creates a
* structure &bfd_request containing approprite information and an notify hook.
* This structure is a resource associated with the caller's resource pool. When
* a BFD protocol is available, a BFD request is submitted to the protocol, an
* appropriate BFD session is found or created and the request is attached to
* the session. When a session changes state, all attached requests (and related
* protocols) are notified. Note that BFD requests do not depend on BFD protocol
* running. When the BFD protocol is stopped or removed (or not available from
* beginning), related BFD requests are stored in @bfd_wait_list, where waits
* for a new protocol.
*
* BFD neighbors are just a way to statically configure BFD sessions without
* requests from other protocol. Structures &bfd_neighbor are part of BFD
* configuration (like static routes in the static protocol). BFD neighbors are
* handled by BFD protocol like it is a BFD client -- when a BFD neighbor is
* ready, the protocol just creates a BFD request like any other protocol.
*
* The protocol uses a new generic event loop (structure &birdloop) from |io.c|,
* which supports sockets, timers and events like the main loop. Timers
* (structure &timer2) are new microsecond based timers, while sockets and
* events are the same. A birdloop is associated with a thread (field @thread)
* in which event hooks are executed. Most functions for setting event sources
* (like sk_start() or tm2_start()) must be called from the context of that
* thread. Birdloop allows to temporarily acquire the context of that thread for
* the main thread by calling birdloop_enter() and then birdloop_leave(), which
* also ensures mutual exclusion with all event hooks. Note that resources
* associated with a birdloop (like timers) should be attached to the
* independent resource pool, detached from the main resource tree.
*
* There are two kinds of interaction between the BFD core (running in the BFD
* thread) and the rest of BFD (running in the main thread). The first kind are
* configuration calls from main thread to the BFD thread (like bfd_add_session()).
* These calls are synchronous and use birdloop_enter() mechanism for mutual
* exclusion. The second kind is a notification about session changes from the
* BFD thread to the main thread. This is done in an asynchronous way, sesions
* with pending notifications are linked (in the BFD thread) to @notify_list in
* &bfd_proto, and then bfd_notify_hook() in the main thread is activated using
* bfd_notify_kick() and a pipe. The hook then processes scheduled sessions and
* calls hooks from associated BFD requests. This @notify_list (and state fields
* in structure &bfd_session) is protected by a spinlock in &bfd_proto and
* functions bfd_lock_sessions() / bfd_unlock_sessions().
*
* There are few data races (accessing @p->p.debug from TRACE() from the BFD
* thread and accessing some some private fields of %bfd_session from
* bfd_show_sessions() from the main thread, but these are harmless (i hope).
*
* TODO: document functions and access restrictions for fields in BFD structures.
*
* Supported standards:
* - RFC 5880 - main BFD standard
* - RFC 5881 - BFD for IP links
* - RFC 5882 - generic application of BFD
* - RFC 5883 - BFD for multihop paths
*/
#include "bfd.h"
@ -17,20 +114,34 @@
#define HASH_IP_EQ(a,b) ipa_equal(a,b)
#define HASH_IP_FN(k) ipa_hash(k)
static list bfd_proto_list;
static list bfd_wait_list;
const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" };
static void bfd_session_set_min_tx(struct bfd_session *s, u32 val);
static struct bfd_iface *bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface);
static void bfd_free_iface(struct bfd_iface *ifa);
static inline void bfd_notify_kick(struct bfd_proto *p);
/*
* BFD sessions
*/
static void
bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
{
struct bfd_proto *p = s->bfd;
struct bfd_proto *p = s->ifa->bfd;
uint old_state = s->loc_state;
int notify;
if (s->loc_state == state)
if (state == old_state)
return;
//TRACE(D_EVENTS, "Session changed %I %d %d", s->addr, state, diag);
debug("STATE %I %d %d %d\n", s->addr, s->loc_state, state, diag);
TRACE(D_EVENTS, "Session to %I changed state from %s to %s",
s->addr, bfd_state_names[old_state], bfd_state_names[state]);
bfd_lock_sessions(p);
s->loc_state = state;
s->loc_diag = diag;
@ -40,23 +151,16 @@ bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
add_tail(&p->notify_list, &s->n);
bfd_unlock_sessions(p);
if (state == BFD_STATE_UP)
bfd_session_set_min_tx(s, s->ifa->cf->min_tx_int);
if (old_state == BFD_STATE_UP)
bfd_session_set_min_tx(s, s->ifa->cf->idle_tx_int);
if (notify)
bfd_notify_kick(p);
}
static void
bfd_session_timeout(struct bfd_session *s)
{
s->rem_state = BFD_STATE_DOWN;
s->rem_id = 0;
s->rem_min_tx_int = 0;
s->rem_min_rx_int = 1;
s->rem_demand_mode = 0;
s->rem_detect_mult = 0;
bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
}
static void
bfd_session_update_tx_interval(struct bfd_session *s)
{
@ -90,10 +194,9 @@ bfd_session_update_detection_time(struct bfd_session *s, int kick)
}
static void
bfd_session_control_tx_timer(struct bfd_session *s)
bfd_session_control_tx_timer(struct bfd_session *s, int reset)
{
if (!s->opened)
goto stop;
// if (!s->opened) goto stop;
if (s->passive && (s->rem_id == 0))
goto stop;
@ -108,10 +211,12 @@ bfd_session_control_tx_timer(struct bfd_session *s)
goto stop;
/* So TX timer should run */
if (tm2_active(s->tx_timer))
return;
if (reset || !tm2_active(s->tx_timer))
{
s->last_tx = 0;
tm2_start(s->tx_timer, 0);
}
tm2_start(s->tx_timer, 0);
return;
stop:
@ -122,6 +227,10 @@ bfd_session_control_tx_timer(struct bfd_session *s)
static void
bfd_session_request_poll(struct bfd_session *s, u8 request)
{
/* Not sure about this, but doing poll in this case does not make sense */
if (s->rem_id == 0)
return;
s->poll_scheduled |= request;
if (s->poll_active)
@ -129,7 +238,8 @@ bfd_session_request_poll(struct bfd_session *s, u8 request)
s->poll_active = s->poll_scheduled;
s->poll_scheduled = 0;
bfd_send_ctl(s->bfd, s, 0);
bfd_session_control_tx_timer(s, 1);
}
static void
@ -143,11 +253,10 @@ bfd_session_terminate_poll(struct bfd_session *s)
if (poll_done & BFD_POLL_RX)
s->req_min_rx_int = s->req_min_rx_new;
s->poll_active = 0;
s->poll_active = s->poll_scheduled;
s->poll_scheduled = 0;
/* Timers are updated by caller - bfd_session_process_ctl() */
// xxx_restart_poll();
}
void
@ -188,10 +297,32 @@ bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old
if (next_state)
bfd_session_update_state(s, next_state, diag);
bfd_session_control_tx_timer(s);
bfd_session_control_tx_timer(s, 0);
if (flags & BFD_FLAG_POLL)
bfd_send_ctl(s->bfd, s, 1);
bfd_send_ctl(s->ifa->bfd, s, 1);
}
static void
bfd_session_timeout(struct bfd_session *s)
{
struct bfd_proto *p = s->ifa->bfd;
TRACE(D_EVENTS, "Session to %I expired", s->addr);
s->rem_state = BFD_STATE_DOWN;
s->rem_id = 0;
s->rem_min_tx_int = 0;
s->rem_min_rx_int = 1;
s->rem_demand_mode = 0;
s->rem_detect_mult = 0;
s->poll_active = 0;
s->poll_scheduled = 0;
bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
bfd_session_control_tx_timer(s, 1);
}
static void
@ -252,8 +383,7 @@ bfd_tx_timer_hook(timer2 *t)
struct bfd_session *s = t->data;
s->last_tx = current_time();
// debug("TX %d\n", (s32) (s->last_tx TO_MS));
bfd_send_ctl(s->bfd, s, 0);
bfd_send_ctl(s->ifa->bfd, s, 0);
}
static void
@ -274,48 +404,53 @@ bfd_get_free_id(struct bfd_proto *p)
}
static struct bfd_session *
bfd_add_session(struct bfd_proto *p, ip_addr addr, struct bfd_session_config *opts)
bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface)
{
birdloop_enter(p->loop);
struct bfd_iface *ifa = bfd_get_iface(p, local, iface);
struct bfd_session *s = sl_alloc(p->session_slab);
bzero(s, sizeof(struct bfd_session));
s->addr = addr;
s->ifa = ifa;
s->loc_id = bfd_get_free_id(p);
debug("XXX INS1 %d %d %u %I\n", p->session_hash_id.count, p->session_hash_ip.count, s->loc_id, s->addr);
HASH_INSERT(p->session_hash_id, HASH_ID, s);
debug("XXX INS2 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
HASH_INSERT(p->session_hash_ip, HASH_IP, s);
debug("XXX INS3 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
s->bfd = p;
/* Initialization of state variables - see RFC 5880 6.8.1 */
s->loc_state = BFD_STATE_DOWN;
s->rem_state = BFD_STATE_DOWN;
s->des_min_tx_int = s->des_min_tx_new = opts->min_tx_int; // XXX opts->idle_tx_int;
s->req_min_rx_int = s->req_min_rx_new = opts->min_rx_int;
s->des_min_tx_int = s->des_min_tx_new = ifa->cf->idle_tx_int;
s->req_min_rx_int = s->req_min_rx_new = ifa->cf->min_rx_int;
s->rem_min_rx_int = 1;
s->detect_mult = opts->multiplier;
s->passive = opts->passive;
s->detect_mult = ifa->cf->multiplier;
s->passive = ifa->cf->passive;
s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
bfd_session_update_tx_interval(s);
bfd_session_control_tx_timer(s, 1);
init_list(&s->request_list);
s->last_state_change = now;
TRACE(D_EVENTS, "Session to %I added", s->addr);
birdloop_leave(p->loop);
return s;
}
/*
static void
bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
{
birdloop_enter(p->loop);
s->bsock = bfd_get_socket(p, local, ifa);
// s->local = local;
// s->iface = ifa;
s->opened = 1;
bfd_session_control_tx_timer(s);
@ -328,10 +463,6 @@ bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
{
birdloop_enter(p->loop);
bfd_free_socket(s->bsock);
s->bsock = NULL;
// s->local = IPA_NONE;
// s->iface = NULL;
s->opened = 0;
bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN);
@ -339,54 +470,295 @@ bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
birdloop_leave(p->loop);
}
*/
static void
bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
{
ip_addr ip = s->addr;
birdloop_enter(p->loop);
bfd_free_socket(s->bsock);
bfd_free_iface(s->ifa);
rfree(s->tx_timer);
rfree(s->hold_timer);
debug("XXX REM1 %d %d %u %I\n", p->session_hash_id.count, p->session_hash_ip.count, s->loc_id, s->addr);
HASH_REMOVE(p->session_hash_id, HASH_ID, s);
debug("XXX REM2 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
debug("XXX REM3 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
sl_free(p->session_slab, s);
TRACE(D_EVENTS, "Session to %I removed", ip);
birdloop_leave(p->loop);
}
static void
bfd_configure_session(struct bfd_proto *p, struct bfd_session *s,
struct bfd_session_config *opts)
bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
{
birdloop_enter(p->loop);
// XXX opts->idle_tx_int;
struct bfd_iface_config *cf = s->ifa->cf;
bfd_session_set_min_tx(s, opts->min_tx_int);
bfd_session_set_min_rx(s, opts->min_rx_int);
s->detect_mult = opts->multiplier;
s->passive = opts->passive;
u32 tx = (s->loc_state == BFD_STATE_UP) ? cf->min_tx_int : cf->idle_tx_int;
bfd_session_set_min_tx(s, tx);
bfd_session_set_min_rx(s, cf->min_rx_int);
s->detect_mult = cf->multiplier;
s->passive = cf->passive;
bfd_session_control_tx_timer(s);
bfd_session_control_tx_timer(s, 0);
birdloop_leave(p->loop);
TRACE(D_EVENTS, "Session to %I reconfigured", s->addr);
}
/*
* BFD interfaces
*/
static struct bfd_iface_config bfd_default_iface = {
.min_rx_int = BFD_DEFAULT_MIN_RX_INT,
.min_tx_int = BFD_DEFAULT_MIN_TX_INT,
.idle_tx_int = BFD_DEFAULT_IDLE_TX_INT,
.multiplier = BFD_DEFAULT_MULTIPLIER
};
static inline struct bfd_iface_config *
bfd_find_iface_config(struct bfd_config *cf, struct iface *iface)
{
struct bfd_iface_config *ic;
ic = iface ? (void *) iface_patt_find(&cf->patt_list, iface, NULL) : cf->multihop;
return ic ? ic : &bfd_default_iface;
}
static struct bfd_iface *
bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface)
{
struct bfd_iface *ifa;
WALK_LIST(ifa, p->iface_list)
if (ipa_equal(ifa->local, local) && (ifa->iface == iface))
return ifa->uc++, ifa;
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
struct bfd_iface_config *ic = bfd_find_iface_config(cf, iface);
ifa = mb_allocz(p->tpool, sizeof(struct bfd_iface));
ifa->local = local;
ifa->iface = iface;
ifa->cf = ic;
ifa->bfd = p;
ifa->sk = bfd_open_tx_sk(p, local, iface);
ifa->uc = 1;
add_tail(&p->iface_list, &ifa->n);
return ifa;
}
static void
bfd_free_iface(struct bfd_iface *ifa)
{
if (!ifa || --ifa->uc)
return;
rem_node(&ifa->n);
sk_stop(ifa->sk);
rfree(ifa->sk);
mb_free(ifa);
}
static void
bfd_reconfigure_iface(struct bfd_proto *p, struct bfd_iface *ifa, struct bfd_config *nc)
{
struct bfd_iface_config *nic = bfd_find_iface_config(nc, ifa->iface);
ifa->changed = !!memcmp(nic, ifa->cf, sizeof(struct bfd_iface_config));
/* This should be probably changed to not access ifa->cf from the BFD thread */
birdloop_enter(p->loop);
ifa->cf = nic;
birdloop_leave(p->loop);
}
/*
* BFD requests
*/
static void
bfd_request_notify(struct bfd_request *req, u8 state, u8 diag)
{
u8 old_state = req->state;
if (state == old_state)
return;
req->state = state;
req->diag = diag;
req->old_state = old_state;
req->down = (old_state == BFD_STATE_UP) && (state == BFD_STATE_DOWN);
if (req->hook)
req->hook(req);
}
static int
bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
{
struct bfd_session *s = bfd_find_session_by_addr(p, req->addr);
u8 state, diag;
if (!s)
s = bfd_add_session(p, req->addr, req->local, req->iface);
rem_node(&req->n);
add_tail(&s->request_list, &req->n);
req->session = s;
bfd_lock_sessions(p);
state = s->loc_state;
diag = s->loc_diag;
bfd_unlock_sessions(p);
bfd_request_notify(req, state, diag);
return 1;
}
static void
bfd_submit_request(struct bfd_request *req)
{
node *n;
WALK_LIST(n, bfd_proto_list)
if (bfd_add_request(SKIP_BACK(struct bfd_proto, bfd_node, n), req))
return;
rem_node(&req->n);
add_tail(&bfd_wait_list, &req->n);
req->session = NULL;
bfd_request_notify(req, BFD_STATE_ADMIN_DOWN, 0);
}
static void
bfd_take_requests(struct bfd_proto *p)
{
node *n, *nn;
WALK_LIST_DELSAFE(n, nn, bfd_wait_list)
bfd_add_request(p, SKIP_BACK(struct bfd_request, n, n));
}
static void
bfd_drop_requests(struct bfd_proto *p)
{
node *n;
HASH_WALK(p->session_hash_id, next_id, s)
{
/* We assume that p is not in bfd_proto_list */
WALK_LIST_FIRST(n, s->request_list)
bfd_submit_request(SKIP_BACK(struct bfd_request, n, n));
}
HASH_WALK_END;
}
static struct resclass bfd_request_class;
struct bfd_request *
bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface,
void (*hook)(struct bfd_request *), void *data)
{
struct bfd_request *req = ralloc(p, &bfd_request_class);
/* Hack: self-link req->n, we will call rem_node() on it */
req->n.prev = req->n.next = &req->n;
req->addr = addr;
req->local = local;
req->iface = iface;
bfd_submit_request(req);
req->hook = hook;
req->data = data;
return req;
}
static void
bfd_request_free(resource *r)
{
struct bfd_request *req = (struct bfd_request *) r;
struct bfd_session *s = req->session;
rem_node(&req->n);
/* Remove the session if there is no request for it. Skip that if
inside notify hooks, will be handled by bfd_notify_hook() itself */
if (s && EMPTY_LIST(s->request_list) && !s->notify_running)
bfd_remove_session(s->ifa->bfd, s);
}
static void
bfd_request_dump(resource *r)
{
struct bfd_request *req = (struct bfd_request *) r;
debug("(code %p, data %p)\n", req->hook, req->data);
}
static struct resclass bfd_request_class = {
"BFD request",
sizeof(struct bfd_request),
bfd_request_free,
bfd_request_dump,
NULL,
NULL
};
/*
* BFD neighbors
*/
static void
bfd_neigh_notify(struct neighbor *nb)
{
struct bfd_proto *p = (struct bfd_proto *) nb->proto;
struct bfd_neighbor *n = nb->data;
if (!n)
return;
if ((nb->scope > 0) && !n->req)
{
ip_addr local = ipa_nonzero(n->local) ? n->local : nb->iface->addr->ip;
n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, NULL, NULL);
}
if ((nb->scope <= 0) && n->req)
{
rfree(n->req);
n->req = NULL;
}
}
static void
bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
{
n->session = bfd_add_session(p, n->addr, n->opts);
n->active = 1;
if (n->opts->multihop)
if (n->multihop)
{
bfd_open_session(p, n->session, n->local, NULL);
n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, NULL, NULL);
return;
}
@ -399,14 +771,15 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
if (nb->data)
{
log(L_ERR "%s: Duplicate remote address %I", p->p.name, n->addr);
log(L_ERR "%s: Duplicate neighbor %I", p->p.name, n->addr);
return;
}
nb->data = n->session;
n->neigh = nb;
nb->data = n;
if (nb->scope > 0)
bfd_open_session(p, n->session, nb->iface->addr->ip, nb->iface);
bfd_neigh_notify(nb);
else
TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface);
}
@ -414,33 +787,54 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
static void
bfd_stop_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
{
if (!n->opts->multihop)
{
struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, 0);
if (nb)
nb->data = NULL;
}
if (n->neigh)
n->neigh->data = NULL;
n->neigh = NULL;
bfd_remove_session(p, n->session);
rfree(n->req);
n->req = NULL;
}
static inline int
bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
{
return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) &&
(x->iface == y->iface) && (x->multihop == y->multihop);
}
static void
bfd_neigh_notify(struct neighbor *nb)
bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new)
{
struct bfd_proto *p = (struct bfd_proto *) nb->proto;
struct bfd_session *s = nb->data;
struct bfd_config *old = (struct bfd_config *) (p->p.cf);
struct bfd_neighbor *on, *nn;
if (!s)
return;
WALK_LIST(on, old->neigh_list)
{
WALK_LIST(nn, new->neigh_list)
if (bfd_same_neighbor(nn, on))
{
nn->neigh = on->neigh;
if (nn->neigh)
nn->neigh->data = nn;
if ((nb->scope > 0) && !s->opened)
bfd_open_session(p, s, nb->iface->addr->ip, nb->iface);
nn->req = on->req;
nn->active = 1;
return;
}
if ((nb->scope <= 0) && s->opened)
bfd_close_session(p, s);
bfd_stop_neighbor(p, on);
}
WALK_LIST(nn, new->neigh_list)
if (!nn->active)
bfd_start_neighbor(p, nn);
}
/*
* BFD notify socket
*/
/* This core notify code should be replaced after main loop transition to birdloop */
int pipe(int pipefd[2]);
@ -453,6 +847,8 @@ bfd_notify_hook(sock *sk, int len)
struct bfd_proto *p = sk->data;
struct bfd_session *s;
list tmp_list;
u8 state, diag;
node *n, *nn;
pipe_drain(sk->fd);
@ -466,10 +862,21 @@ bfd_notify_hook(sock *sk, int len)
{
bfd_lock_sessions(p);
rem2_node(&s->n);
state = s->loc_state;
diag = s->loc_diag;
bfd_unlock_sessions(p);
// XXX do something
TRACE(D_EVENTS, "Notify: session changed %I %d %d", s->addr, s->loc_state, s->loc_diag);
/* FIXME: convert to btime and move to bfd_session_update_state() */
s->last_state_change = now;
s->notify_running = 1;
WALK_LIST_DELSAFE(n, nn, s->request_list)
bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag);
s->notify_running = 0;
/* Remove the session if all requests were removed in notify hooks */
if (EMPTY_LIST(s->request_list))
bfd_remove_session(p, s);
}
return 0;
@ -520,6 +927,17 @@ bfd_notify_init(struct bfd_proto *p)
}
/*
* BFD protocol glue
*/
void
bfd_init_all(void)
{
init_list(&bfd_proto_list);
init_list(&bfd_wait_list);
}
static struct proto *
bfd_init(struct proto_config *c)
{
@ -536,32 +954,33 @@ bfd_start(struct proto *P)
struct bfd_proto *p = (struct bfd_proto *) P;
struct bfd_config *cf = (struct bfd_config *) (P->cf);
p->loop = birdloop_new(P->pool);
p->loop = birdloop_new();
p->tpool = rp_new(NULL, "BFD thread root");
pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
HASH_INIT(p->session_hash_id, P->pool, 4);
HASH_INIT(p->session_hash_ip, P->pool, 4);
HASH_INIT(p->session_hash_id, P->pool, 8);
HASH_INIT(p->session_hash_ip, P->pool, 8);
init_list(&p->sock_list);
birdloop_mask_wakeups(p->loop);
init_list(&p->iface_list);
init_list(&p->notify_list);
bfd_notify_init(p);
add_tail(&bfd_proto_list, &p->bfd_node);
birdloop_enter(p->loop);
p->rx_1 = bfd_open_rx_sk(p, 0);
p->rx_m = bfd_open_rx_sk(p, 1);
birdloop_leave(p->loop);
bfd_take_requests(p);
struct bfd_neighbor *n;
WALK_LIST(n, cf->neigh_list)
bfd_start_neighbor(p, n);
birdloop_unmask_wakeups(p->loop);
birdloop_start(p->loop);
return PS_UP;
}
@ -571,69 +990,79 @@ static int
bfd_shutdown(struct proto *P)
{
struct bfd_proto *p = (struct bfd_proto *) P;
struct bfd_config *cf = (struct bfd_config *) (P->cf);
rem_node(&p->bfd_node);
birdloop_stop(p->loop);
struct bfd_neighbor *n;
WALK_LIST(n, cf->neigh_list)
bfd_stop_neighbor(p, n);
bfd_drop_requests(p);
/* FIXME: This is hack */
birdloop_enter(p->loop);
rfree(p->tpool);
birdloop_leave(p->loop);
birdloop_free(p->loop);
return PS_DOWN;
}
static inline int
bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
{
return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) &&
(x->iface == y->iface) && (x->opts->multihop == y->opts->multihop);
}
static void
bfd_match_neighbor(struct bfd_proto *p, struct bfd_neighbor *on, struct bfd_config *new)
{
struct bfd_neighbor *nn;
WALK_LIST(nn, new->neigh_list)
if (bfd_same_neighbor(nn, on))
{
nn->session = on->session;
bfd_configure_session(p, nn->session, nn->opts);
return;
}
bfd_stop_neighbor(p, on);
}
static int
bfd_reconfigure(struct proto *P, struct proto_config *c)
{
struct bfd_proto *p = (struct bfd_proto *) P;
struct bfd_config *old = (struct bfd_config *) (P->cf);
// struct bfd_config *old = (struct bfd_config *) (P->cf);
struct bfd_config *new = (struct bfd_config *) c;
struct bfd_neighbor *n;
struct bfd_iface *ifa;
birdloop_mask_wakeups(p->loop);
WALK_LIST(n, old->neigh_list)
bfd_match_neighbor(p, n, new);
WALK_LIST(ifa, p->iface_list)
bfd_reconfigure_iface(p, ifa, new);
WALK_LIST(n, new->neigh_list)
if (!n->session)
bfd_start_neighbor(p, n);
HASH_WALK(p->session_hash_id, next_id, s)
{
if (s->ifa->changed)
bfd_reconfigure_session(p, s);
}
HASH_WALK_END;
bfd_reconfigure_neighbors(p, new);
birdloop_unmask_wakeups(p->loop);
return 1;
}
/* Ensure one instance */
struct bfd_config *bfd_cf;
static void
bfd_preconfig(struct protocol *P UNUSED, struct config *c UNUSED)
{
bfd_cf = NULL;
}
static void
bfd_copy_config(struct proto_config *dest, struct proto_config *src)
{
struct bfd_config *d = (struct bfd_config *) dest;
// struct bfd_config *s = (struct bfd_config *) src;
/* We clean up neigh_list, ifaces are non-sharable */
/* We clean up patt_list and neigh_list, neighbors and ifaces are non-sharable */
init_list(&d->patt_list);
init_list(&d->neigh_list);
}
void
bfd_show_sessions(struct proto *P)
{
byte tbuf[TM_DATETIME_BUFFER_SIZE];
struct bfd_proto *p = (struct bfd_proto *) P;
uint state, diag;
u32 tx_int, timeout;
@ -647,22 +1076,25 @@ bfd_show_sessions(struct proto *P)
}
cli_msg(-1013, "%s:", p->p.name);
cli_msg(-1013, "%-12s\t%s\t%s\t%s\t%s", "Router IP", "Iface",
"State", "TX Int", "Timeout");
cli_msg(-1013, "%-25s %-10s %-10s %-10s %8s %8s",
"IP address", "Interface", "State", "Since", "Interval", "Timeout");
debug("XXX WALK %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
HASH_WALK(p->session_hash_id, next_id, s)
{
// FIXME this is unsafe
/* FIXME: this is thread-unsafe, but perhaps harmless */
state = s->loc_state;
diag = s->loc_diag;
ifname = (s->bsock && s->bsock->sk->iface) ? s->bsock->sk->iface->name : "---";
tx_int = (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS);
ifname = (s->ifa && s->ifa->sk->iface) ? s->ifa->sk->iface->name : "---";
tx_int = s->last_tx ? (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS) : 0;
timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult;
cli_msg(-1013, "%I\t%s\t%d %d\t%u\t%u",
s->addr, ifname, state, diag, tx_int, timeout);
state = (state < 4) ? state : 0;
tm_format_datetime(tbuf, &config->tf_proto, s->last_state_change);
cli_msg(-1013, "%-25I %-10s %-10s %-10s %3u.%03u %3u.%03u",
s->addr, ifname, bfd_state_names[state], tbuf,
tx_int / 1000, tx_int % 1000, timeout / 1000, timeout % 1000);
}
HASH_WALK_END;
@ -677,5 +1109,6 @@ struct protocol proto_bfd = {
.start = bfd_start,
.shutdown = bfd_shutdown,
.reconfigure = bfd_reconfigure,
.preconfig = bfd_preconfig,
.copy_config = bfd_copy_config,
};

View File

@ -20,6 +20,7 @@
#include "lib/socket.h"
#include "lib/string.h"
#include "nest/bfd.h"
#include "io.h"
@ -27,25 +28,29 @@
#define BFD_ECHO_PORT 3785
#define BFD_MULTI_CTL_PORT 4784
#define BFD_DEFAULT_MIN_RX_INT (10 MS)
#define BFD_DEFAULT_MIN_TX_INT (100 MS)
#define BFD_DEFAULT_IDLE_TX_INT (1 S)
#define BFD_DEFAULT_MIN_RX_INT (10 _MS)
#define BFD_DEFAULT_MIN_TX_INT (100 _MS)
#define BFD_DEFAULT_IDLE_TX_INT (1 _S)
#define BFD_DEFAULT_MULTIPLIER 5
struct bfd_iface_config;
struct bfd_config
{
struct proto_config c;
list neigh_list; /* List of struct bfd_neighbor */
list patt_list; /* List of iface configs (struct bfd_iface_config) */
list neigh_list; /* List of configured neighbors (struct bfd_neighbor) */
struct bfd_iface_config *multihop; /* Multihop pseudoiface config */
};
struct bfd_session_config
struct bfd_iface_config
{
struct iface_patt i;
u32 min_rx_int;
u32 min_tx_int;
u32 idle_tx_int;
u8 multiplier;
u8 multihop;
u8 passive;
};
@ -55,9 +60,12 @@ struct bfd_neighbor
ip_addr addr;
ip_addr local;
struct iface *iface;
struct bfd_session_config *opts;
struct bfd_session *session;
struct neighbor *neigh;
struct bfd_request *req;
u8 multihop;
u8 active;
};
struct bfd_proto
@ -66,6 +74,7 @@ struct bfd_proto
struct birdloop *loop;
pool *tpool;
pthread_spinlock_t lock;
node bfd_node;
slab *session_slab;
HASH(struct bfd_session) session_hash_id;
@ -77,25 +86,31 @@ struct bfd_proto
sock *rx_1;
sock *rx_m;
list sock_list;
list iface_list;
};
struct bfd_socket
struct bfd_iface
{
node n;
ip_addr local;
struct iface *iface;
struct bfd_iface_config *cf;
struct bfd_proto *bfd;
sock *sk;
u32 uc;
u8 changed;
};
struct bfd_session
{
node n;
ip_addr addr; /* Address of session */
struct bfd_iface *ifa; /* Iface associated with session */
struct bfd_session *next_id; /* Next in bfd.session_hash_id */
struct bfd_session *next_ip; /* Next in bfd.session_hash_ip */
struct bfd_proto *bfd;
u8 opened;
u8 opened_unused;
u8 passive;
u8 poll_active;
u8 poll_scheduled;
@ -123,10 +138,13 @@ struct bfd_session
timer2 *tx_timer; /* Periodic control packet timer */
timer2 *hold_timer; /* Timer for session down detection time */
struct bfd_socket *bsock; /* Socket associated with session */
list request_list; /* List of client requests (struct bfd_request) */
bird_clock_t last_state_change; /* Time of last state change */
u8 notify_running; /* 1 if notify hooks are running */
};
extern const char *bfd_state_names[];
#define BFD_STATE_ADMIN_DOWN 0
#define BFD_STATE_DOWN 1
@ -146,6 +164,7 @@ struct bfd_session
#define BFD_POLL_TX 1
#define BFD_POLL_RX 2
#define BFD_FLAGS 0x3f
#define BFD_FLAG_POLL (1 << 5)
#define BFD_FLAG_FINAL (1 << 4)
#define BFD_FLAG_CPI (1 << 3)
@ -166,10 +185,7 @@ void bfd_show_sessions(struct proto *P);
/* packets.c */
void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final);
sock * bfd_open_rx_sk(struct bfd_proto *p, int multihop);
struct bfd_socket * bfd_get_socket(struct bfd_proto *p, ip_addr local, struct iface *ifa);
void bfd_free_socket(struct bfd_socket *sk);
sock * bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa);
#endif /* _BIRD_BFD_H_ */

View File

@ -12,20 +12,21 @@ CF_HDR
CF_DEFINES
#define BFD_CFG ((struct bfd_config *) this_proto)
#define BFD_SESSION this_bfd_session
#define BFD_IFACE ((struct bfd_iface_config *) this_ipatt)
#define BFD_NEIGHBOR this_bfd_neighbor
static struct bfd_session_config *this_bfd_session;
static struct bfd_neighbor *this_bfd_neighbor;
extern struct bfd_config *bfd_cf;
CF_DECLS
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, MULTIHOP, PASSIVE,
NEIGHBOR, DEV)
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL)
%type <iface> bfd_neigh_iface
%type <a> bfd_neigh_local
%type <i> bfd_neigh_multihop
CF_GRAMMAR
@ -34,12 +35,19 @@ CF_ADDTO(proto, bfd_proto)
bfd_proto_start: proto_start BFD
{
this_proto = proto_config_new(&proto_bfd, sizeof(struct bfd_config), $1);
init_list(&BFD_CFG->patt_list);
init_list(&BFD_CFG->neigh_list);
if (bfd_cf)
cf_error("Only one BFD instance allowed");
bfd_cf = BFD_CFG;
};
bfd_proto_item:
proto_item
| bfd_neighbor
| INTERFACE bfd_iface
| MULTIHOP bfd_multihop
| NEIGHBOR bfd_neighbor
;
bfd_proto_opts:
@ -51,38 +59,41 @@ bfd_proto:
bfd_proto_start proto_name '{' bfd_proto_opts '}';
bfd_session_start:
bfd_iface_start:
{
this_bfd_session = cfg_allocz(sizeof(struct bfd_session_config));
this_ipatt = cfg_allocz(sizeof(struct bfd_iface_config));
init_list(&this_ipatt->ipn_list);
BFD_SESSION->min_rx_int = BFD_DEFAULT_MIN_RX_INT;
BFD_SESSION->min_tx_int = BFD_DEFAULT_MIN_TX_INT;
BFD_SESSION->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT;
BFD_SESSION->multiplier = BFD_DEFAULT_MULTIPLIER;
BFD_IFACE->min_rx_int = BFD_DEFAULT_MIN_RX_INT;
BFD_IFACE->min_tx_int = BFD_DEFAULT_MIN_TX_INT;
BFD_IFACE->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT;
BFD_IFACE->multiplier = BFD_DEFAULT_MULTIPLIER;
};
bfd_session_item:
INTERVAL expr_us { BFD_SESSION->min_rx_int = BFD_SESSION->min_tx_int = $2; }
| MIN RX INTERVAL expr_us { BFD_SESSION->min_rx_int = $4; }
| MIN TX INTERVAL expr_us { BFD_SESSION->min_tx_int = $4; }
| IDLE TX INTERVAL expr_us { BFD_SESSION->idle_tx_int = $4; }
| MULTIPLIER expr { BFD_SESSION->multiplier = $2; }
| MULTIHOP bool { BFD_SESSION->multihop = $2; }
| PASSIVE bool { BFD_SESSION->passive = $2; }
bfd_iface_item:
INTERVAL expr_us { BFD_IFACE->min_rx_int = BFD_IFACE->min_tx_int = $2; }
| MIN RX INTERVAL expr_us { BFD_IFACE->min_rx_int = $4; }
| MIN TX INTERVAL expr_us { BFD_IFACE->min_tx_int = $4; }
| IDLE TX INTERVAL expr_us { BFD_IFACE->idle_tx_int = $4; }
| MULTIPLIER expr { BFD_IFACE->multiplier = $2; }
| PASSIVE bool { BFD_IFACE->passive = $2; }
;
bfd_session_opts:
bfd_iface_opts:
/* empty */
| bfd_session_opts bfd_session_item ';'
| bfd_iface_opts bfd_iface_item ';'
;
bfd_session_opt_list:
bfd_iface_opt_list:
/* empty */
| '{' bfd_session_opts '}'
| '{' bfd_iface_opts '}'
;
bfd_session:
bfd_session_start bfd_session_opt_list;
bfd_iface: bfd_iface_start iface_patt_list bfd_iface_opt_list
{ add_tail(&BFD_CFG->patt_list, NODE this_ipatt); };
bfd_multihop: bfd_iface_start bfd_iface_opt_list
{ BFD_CFG->multihop = BFD_IFACE; };
bfd_neigh_iface:
@ -96,15 +107,26 @@ bfd_neigh_local:
| LOCAL ipa { $$ = $2; }
;
bfd_neighbor: NEIGHBOR ipa bfd_neigh_iface bfd_neigh_local bfd_session
bfd_neigh_multihop:
/* empty */ { $$ = 0; }
| MULTIHOP bool { $$ = $2; }
;
bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
{
this_bfd_neighbor = cfg_allocz(sizeof(struct bfd_neighbor));
add_tail(&BFD_CFG->neigh_list, NODE this_bfd_neighbor);
BFD_NEIGHBOR->addr = $2;
BFD_NEIGHBOR->local = $4;
BFD_NEIGHBOR->iface = $3;
BFD_NEIGHBOR->opts = BFD_SESSION;
BFD_NEIGHBOR->addr = $1;
BFD_NEIGHBOR->local = $3;
BFD_NEIGHBOR->iface = $2;
BFD_NEIGHBOR->multihop = $4;
if ($4 && $2)
cf_error("Neighbor cannot set both interface and multihop");
if ($4 && ipa_zero($3))
cf_error("Multihop neighbor requires specified local address");
};

View File

@ -35,6 +35,7 @@ struct birdloop
btime real_time;
u8 use_monotonic_clock;
u8 stop_called;
u8 poll_active;
u8 wakeup_masked;
int wakeup_fds[2];
@ -51,6 +52,9 @@ struct birdloop
};
/*
* Current thread context
*/
static pthread_key_t current_loop_key;
@ -73,6 +77,9 @@ birdloop_init_current(void)
}
/*
* Time clock
*/
static void times_update_alt(struct birdloop *loop);
@ -85,7 +92,7 @@ times_init(struct birdloop *loop)
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
if (rv < 0)
{
// log(L_WARN "Monotonic clock is missing");
log(L_WARN "Monotonic clock is missing");
loop->use_monotonic_clock = 0;
loop->last_time = 0;
@ -94,13 +101,11 @@ times_init(struct birdloop *loop)
return;
}
/*
if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40)))
log(L_WARN "Monotonic clock is crazy");
*/
loop->use_monotonic_clock = 1;
loop->last_time = (ts.tv_sec S) + (ts.tv_nsec / 1000);
loop->last_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
loop->real_time = 0;
}
@ -114,12 +119,10 @@ times_update_pri(struct birdloop *loop)
if (rv < 0)
die("clock_gettime: %m");
btime new_time = (ts.tv_sec S) + (ts.tv_nsec / 1000);
btime new_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000);
/*
if (new_time < loop->last_time)
log(L_ERR "Monotonic clock is broken");
*/
loop->last_time = new_time;
loop->real_time = 0;
@ -135,15 +138,13 @@ times_update_alt(struct birdloop *loop)
if (rv < 0)
die("gettimeofday: %m");
btime new_time = (tv.tv_sec S) + tv.tv_usec;
btime new_time = ((s64) tv.tv_sec S) + tv.tv_usec;
btime delta = new_time - loop->real_time;
if ((delta < 0) || (delta > (60 S)))
{
/*
if (loop->real_time)
log(L_WARN "Time jump, delta %d us", (int) delta);
*/
delta = 100 MS;
}
@ -168,6 +169,9 @@ current_time(void)
}
/*
* Wakeup code for birdloop
*/
static void
pipe_new(int *pfds)
@ -249,6 +253,9 @@ wakeup_kick(struct birdloop *loop)
}
/*
* Events
*/
static inline uint
events_waiting(struct birdloop *loop)
@ -284,12 +291,14 @@ ev2_schedule(event *e)
}
/*
* Timers
*/
#define TIMER_LESS(a,b) ((a)->expires < (b)->expires)
#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
heap[a]->index = (a), heap[b]->index = (b))
static inline uint timers_count(struct birdloop *loop)
{ return loop->timers.used - 1; }
@ -430,6 +439,9 @@ timers_fire(struct birdloop *loop)
}
/*
* Sockets
*/
static void
sockets_init(struct birdloop *loop)
@ -499,12 +511,16 @@ sk_stop(sock *s)
static inline uint sk_want_events(sock *s)
{ return (s->rx_hook ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); }
/*
FIXME: this should be called from sock code
static void
sockets_update(struct birdloop *loop, sock *s)
{
if (s->index >= 0)
loop->poll_fd.data[s->index].events = sk_want_events(s);
}
*/
static void
sockets_prepare(struct birdloop *loop)
@ -599,17 +615,21 @@ sockets_fire(struct birdloop *loop)
}
/*
* Birdloop
*/
static void * birdloop_main(void *arg);
struct birdloop *
birdloop_new(pool *p)
birdloop_new(void)
{
/* FIXME: this init should be elsewhere and thread-safe */
static int init = 0;
if (!init)
{ birdloop_init_current(); init = 1; }
pool *p = rp_new(NULL, "Birdloop root");
struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
loop->pool = p;
pthread_mutex_init(&loop->mutex, NULL);
@ -621,14 +641,37 @@ birdloop_new(pool *p)
timers_init(loop);
sockets_init(loop);
int rv = pthread_create(&loop->thread, NULL, birdloop_main, loop);
if (rv < 0)
die("pthread_create(): %m");
return loop;
}
void
birdloop_start(struct birdloop *loop)
{
int rv = pthread_create(&loop->thread, NULL, birdloop_main, loop);
if (rv)
die("pthread_create(): %M", rv);
}
void
birdloop_stop(struct birdloop *loop)
{
pthread_mutex_lock(&loop->mutex);
loop->stop_called = 1;
wakeup_do_kick(loop);
pthread_mutex_unlock(&loop->mutex);
int rv = pthread_join(loop->thread, NULL);
if (rv)
die("pthread_join(): %M", rv);
}
void
birdloop_free(struct birdloop *loop)
{
rfree(loop->pool);
}
void
birdloop_enter(struct birdloop *loop)
{
@ -707,14 +750,19 @@ birdloop_main(void *arg)
if (loop->close_scheduled)
sockets_close_fds(loop);
if (loop->stop_called)
break;
if (rv)
sockets_fire(loop);
timers_fire(loop);
}
loop->stop_called = 0;
pthread_mutex_unlock(&loop->mutex);
return NULL;
}

View File

@ -15,16 +15,6 @@
// #include "lib/timer.h"
#define S *1000000
#define MS *1000
#define US *1
#define TO_S /1000000
#define TO_MS /1000
#define TO_US /1
typedef s64 btime;
typedef struct timer2
{
resource r;
@ -73,6 +63,13 @@ tm2_new_init(pool *p, void (*hook)(struct timer2 *), void *data, uint rec, uint
return t;
}
static inline void
tm2_set_max(timer2 *t, btime when)
{
if (when > t->expires)
tm2_set(t, when);
}
/*
static inline void
tm2_start_max(timer2 *t, btime after)
@ -88,7 +85,11 @@ void sk_stop(sock *s);
struct birdloop *birdloop_new(pool *p);
struct birdloop *birdloop_new(void);
void birdloop_start(struct birdloop *loop);
void birdloop_stop(struct birdloop *loop);
void birdloop_free(struct birdloop *loop);
void birdloop_enter(struct birdloop *loop);
void birdloop_leave(struct birdloop *loop);
void birdloop_mask_wakeups(struct birdloop *loop);

View File

@ -20,8 +20,8 @@ struct bfd_ctl_packet
u32 req_min_echo_rx_int;
};
#define BFD_BASE_LEN sizeof(struct bfd_ctl_packet)
#define BFD_BASE_LEN sizeof(struct bfd_ctl_packet)
#define BFD_MAX_LEN 64
static inline u8 bfd_pack_vdiag(u8 version, u8 diag)
{ return (version << 5) | diag; }
@ -43,11 +43,28 @@ static inline void bfd_pkt_set_state(struct bfd_ctl_packet *pkt, u8 val)
{ pkt->flags = val << 6; }
char *
bfd_format_flags(u8 flags, char *buf)
{
char *bp = buf;
if (flags & BFD_FLAGS) *bp++ = ' ';
if (flags & BFD_FLAG_POLL) *bp++ = 'P';
if (flags & BFD_FLAG_FINAL) *bp++ = 'F';
if (flags & BFD_FLAG_CPI) *bp++ = 'C';
if (flags & BFD_FLAG_AP) *bp++ = 'A';
if (flags & BFD_FLAG_DEMAND) *bp++ = 'D';
if (flags & BFD_FLAG_MULTIPOINT) *bp++ = 'M';
*bp = 0;
return buf;
}
void
bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
{
sock *sk = s->bsock->sk;
sock *sk = s->ifa->sk;
struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->tbuf;
char fb[8];
pkt->vdiag = bfd_pack_vdiag(1, s->loc_diag);
pkt->flags = bfd_pack_flags(s->loc_state, 0);
@ -65,7 +82,10 @@ bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final)
pkt->flags |= BFD_FLAG_POLL;
if (sk->tbuf != sk->tpos)
log(L_ERR "%s: old packet was overwritten in TX buffer", p->p.name);
log(L_WARN "%s: Old packet overwritten in TX buffer", p->p.name);
TRACE(D_PACKETS, "Sending CTL to %I [%s%s]", s->addr,
bfd_state_names[s->loc_state], bfd_format_flags(pkt->flags, fb));
sk_send_to(sk, pkt->length, s->addr, sk->dport);
}
@ -79,6 +99,10 @@ bfd_rx_hook(sock *sk, int len)
struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->rbuf;
const char *err_dsc = NULL;
uint err_val = 0;
char fb[8];
if ((sk->sport == BFD_CONTROL_PORT) && (sk->ttl < 255))
DROP("wrong TTL", sk->ttl);
if (len < BFD_BASE_LEN)
DROP("too short", len);
@ -93,7 +117,8 @@ bfd_rx_hook(sock *sk, int len)
if (pkt->detect_mult == 0)
DROP("invalid detect mult", 0);
if (pkt->flags & BFD_FLAG_MULTIPOINT)
if ((pkt->flags & BFD_FLAG_MULTIPOINT) ||
((pkt->flags & BFD_FLAG_POLL) && (pkt->flags & BFD_FLAG_FINAL)))
DROP("invalid flags", pkt->flags);
if (pkt->snd_id == 0)
@ -107,7 +132,7 @@ bfd_rx_hook(sock *sk, int len)
s = bfd_find_session_by_id(p, id);
if (!s)
DROP("unknown session", id);
DROP("unknown session id", id);
}
else
{
@ -118,7 +143,7 @@ bfd_rx_hook(sock *sk, int len)
s = bfd_find_session_by_addr(p, sk->faddr);
/* FIXME: better session matching and message */
if (!s || !s->opened)
if (!s)
return 1;
}
@ -130,7 +155,7 @@ bfd_rx_hook(sock *sk, int len)
u32 old_tx_int = s->des_min_tx_int;
u32 old_rx_int = s->rem_min_rx_int;
s->rem_id = ntohl(pkt->snd_id);
s->rem_id= ntohl(pkt->snd_id);
s->rem_state = bfd_pkt_get_state(pkt);
s->rem_diag = bfd_pkt_get_diag(pkt);
s->rem_demand_mode = pkt->flags & BFD_FLAG_DEMAND;
@ -138,11 +163,14 @@ bfd_rx_hook(sock *sk, int len)
s->rem_min_rx_int = ntohl(pkt->req_min_rx_int);
s->rem_detect_mult = pkt->detect_mult;
TRACE(D_PACKETS, "CTL received from %I [%s%s]", sk->faddr,
bfd_state_names[s->rem_state], bfd_format_flags(pkt->flags, fb));
bfd_session_process_ctl(s, pkt->flags, old_tx_int, old_rx_int);
return 1;
drop:
// log(L_WARN "%s: Bad packet from %I - %s (%u)", p->p.name, sk->faddr, err_dsc, err_val);
log(L_REMOTE "%s: Bad packet from %I - %s (%u)", p->p.name, sk->faddr, err_dsc, err_val);
return 1;
}
@ -161,7 +189,7 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop)
sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
sk->data = p;
sk->rbsize = 64; // XXX
sk->rbsize = BFD_MAX_LEN;
sk->rx_hook = bfd_rx_hook;
sk->err_hook = bfd_err_hook;
@ -185,7 +213,7 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop)
return NULL;
}
static inline sock *
sock *
bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
{
sock *sk = sk_new(p->tpool);
@ -195,7 +223,7 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
sk->iface = ifa;
sk->data = p;
sk->tbsize = 64; // XXX
sk->tbsize = BFD_MAX_LEN;
sk->err_hook = bfd_err_hook;
/* TODO: configurable ToS, priority and TTL security */
@ -218,32 +246,3 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
rfree(sk);
return NULL;
}
struct bfd_socket *
bfd_get_socket(struct bfd_proto *p, ip_addr local, struct iface *ifa)
{
struct bfd_socket *sk;
WALK_LIST(sk, p->sock_list)
if (ipa_equal(sk->sk->saddr, local) && (sk->sk->iface == ifa))
return sk->uc++, sk;
sk = mb_allocz(p->tpool, sizeof(struct bfd_socket));
sk->sk = bfd_open_tx_sk(p, local, ifa);
sk->uc = 1;
add_tail(&p->sock_list, &sk->n);
return sk;
}
void
bfd_free_socket(struct bfd_socket *sk)
{
if (!sk || --sk->uc)
return;
rem_node(&sk->n);
sk_stop(sk->sk);
rfree(sk->sk);
mb_free(sk);
}

View File

@ -59,8 +59,9 @@
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/locks.h"
#include "nest/bfd.h"
#include "nest/cli.h"
#include "nest/locks.h"
#include "conf/conf.h"
#include "lib/socket.h"
#include "lib/resource.h"
@ -76,6 +77,7 @@ static void bgp_close(struct bgp_proto *p, int apply_md5);
static void bgp_connect(struct bgp_proto *p);
static void bgp_active(struct bgp_proto *p);
static sock *bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags);
static void bgp_update_bfd(struct bgp_proto *p, int use_bfd);
/**
@ -153,8 +155,12 @@ bgp_initiate(struct bgp_proto *p)
if (rv < 0)
return;
if (p->cf->bfd)
bgp_update_bfd(p, p->cf->bfd);
if (p->startup_delay)
{
p->start_state = BSS_DELAY;
BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds", p->startup_delay);
bgp_start_timer(p->startup_timer, p->startup_delay);
}
@ -765,6 +771,37 @@ bgp_neigh_notify(neighbor *n)
}
}
static void
bgp_bfd_notify(struct bfd_request *req)
{
struct bgp_proto *p = req->data;
int ps = p->p.proto_state;
if (req->down && ((ps == PS_START) || (ps == PS_UP)))
{
BGP_TRACE(D_EVENTS, "BFD session down");
bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN);
if (ps == PS_UP)
bgp_update_startup_delay(p);
bgp_stop(p, 0);
}
}
static void
bgp_update_bfd(struct bgp_proto *p, int use_bfd)
{
if (use_bfd && !p->bfd_req)
p->bfd_req = bfd_request_session(p->p.pool, p->cf->remote_ip, p->source_addr,
p->cf->multihop ? NULL : p->neigh->iface,
bgp_bfd_notify, p);
if (!use_bfd && p->bfd_req)
{
rfree(p->bfd_req);
p->bfd_req = NULL;
}
}
static int
bgp_reload_routes(struct proto *P)
{
@ -825,6 +862,7 @@ bgp_start(struct proto *P)
p->outgoing_conn.state = BS_IDLE;
p->incoming_conn.state = BS_IDLE;
p->neigh = NULL;
p->bfd_req = NULL;
rt_lock_table(p->igp_table);
@ -992,6 +1030,9 @@ bgp_check_config(struct bgp_config *c)
ipa_has_link_scope(c->source_addr)))
cf_error("Multihop BGP cannot be used with link-local addresses");
if (c->multihop && c->bfd && ipa_zero(c->source_addr))
cf_error("Multihop BGP with BFD requires specified source address");
/* Different default based on rs_client */
if (!c->missing_lladdr)
@ -1034,6 +1075,9 @@ bgp_reconfigure(struct proto *P, struct proto_config *C)
|| (old->password && new->password && !strcmp(old->password, new->password)))
&& (get_igp_table(old) == get_igp_table(new));
if (same && (p->start_state > BSS_PREPARE))
bgp_update_bfd(p, new->bfd);
/* We should update our copy of configuration ptr as old configuration will be freed */
if (same)
p->cf = new;
@ -1115,7 +1159,7 @@ bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code)
static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" };
static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", ""};
static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket" };
static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket", "BFD session down" };
static char *bgp_auto_errors[] = { "", "Route limit exceeded"};
static const char *

View File

@ -14,6 +14,7 @@
struct linpool;
struct eattr;
struct bfd_request;
struct bgp_config {
struct proto_config c;
@ -53,8 +54,10 @@ struct bgp_config {
unsigned error_delay_time_min; /* Time to wait after an error is detected */
unsigned error_delay_time_max;
unsigned disable_after_error; /* Disable the protocol when error is detected */
char *password; /* Password used for MD5 authentication */
struct rtable_config *igp_table; /* Table used for recursive next hop lookups */
int bfd; /* Use BFD for liveness detection */
};
#define MLL_SELF 1
@ -100,6 +103,7 @@ struct bgp_proto {
struct bgp_conn incoming_conn; /* Incoming connection we have neither accepted nor rejected yet */
struct object_lock *lock; /* Lock for neighbor connection */
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
ip_addr source_addr; /* Local address used as an advertised next hop */
rtable *igp_table; /* Table used for recursive next hop lookups */
struct event *event; /* Event for respawning and shutting process */
@ -288,6 +292,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
#define BEM_INVALID_NEXT_HOP 2
#define BEM_INVALID_MD5 3 /* MD5 authentication kernel request failed (possibly not supported) */
#define BEM_NO_SOCKET 4
#define BEM_BFD_DOWN 5
/* Automatic shutdown error codes */

View File

@ -26,7 +26,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
SECONDARY, ALLOW)
SECONDARY, ALLOW, BFD)
CF_GRAMMAR
@ -112,6 +112,7 @@ bgp_proto:
| bgp_proto ALLOW LOCAL AS expr ';' { BGP_CFG->allow_local_as = $5; }
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
| bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); }
;
CF_ADDTO(dynamic_attr, BGP_ORIGIN

View File

@ -299,6 +299,7 @@ ospf_iface_item:
| TX PRIORITY expr { OSPF_PATT->tx_priority = $3; }
| TTL SECURITY bool { OSPF_PATT->ttl_security = $3; }
| TTL SECURITY TX ONLY { OSPF_PATT->ttl_security = 2; }
| BFD bool { OSPF_PATT->bfd = $2; cf_check_bfd($2); }
| password_list { ospf_check_auth(); }
;

View File

@ -537,6 +537,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
ifa->check_link = ip->check_link;
ifa->ecmp_weight = ip->ecmp_weight;
ifa->check_ttl = (ip->ttl_security == 1);
ifa->bfd = ip->bfd;
ifa->autype = ip->autype;
ifa->passwords = ip->passwords;
ifa->instance_id = ip->instance_id;
@ -824,6 +825,19 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
ifa->ecmp_weight = new->ecmp_weight;
}
/* BFD */
if (ifa->bfd != new->bfd)
{
OSPF_TRACE(D_EVENTS, "%s BFD on interface %s",
new->bfd ? "Enabling" : "Disabling", ifname);
ifa->bfd = new->bfd;
struct ospf_neighbor *n;
WALK_LIST(n, ifa->neigh_list)
ospf_neigh_update_bfd(n, ifa->bfd);
}
/* instance_id is not updated - it is part of key */
return 1;

View File

@ -560,6 +560,36 @@ ospf_neigh_remove(struct ospf_neighbor *n)
OSPF_TRACE(D_EVENTS, "Deleting neigbor %R", rid);
}
static void
ospf_neigh_bfd_hook(struct bfd_request *req)
{
struct ospf_neighbor *n = req->data;
struct proto *p = &n->ifa->oa->po->proto;
if (req->down)
{
OSPF_TRACE(D_EVENTS, "BFD session down for %I on %s",
n->ip, n->ifa->iface->name);
ospf_neigh_remove(n);
}
}
void
ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd)
{
if (use_bfd && !n->bfd_req)
n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip, n->ifa->iface,
ospf_neigh_bfd_hook, n);
if (!use_bfd && n->bfd_req)
{
rfree(n->bfd_req);
n->bfd_req = NULL;
}
}
void
ospf_sh_neigh_info(struct ospf_neighbor *n)
{

View File

@ -16,6 +16,7 @@ void bdr_election(struct ospf_iface *ifa);
struct ospf_neighbor *find_neigh(struct ospf_iface *ifa, u32 rid);
struct ospf_neighbor *find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip);
void ospf_neigh_remove(struct ospf_neighbor *n);
void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd);
void ospf_sh_neigh_info(struct ospf_neighbor *n);
#endif /* _BIRD_OSPF_NEIGHBOR_H_ */

View File

@ -15,7 +15,7 @@
* The RAdv protocol is implemented in two files: |radv.c| containing
* the interface with BIRD core and the protocol logic and |packets.c|
* handling low level protocol stuff (RX, TX and packet formats).
* The protocol does not import or export any routes.
* The protocol does not export any routes.
*
* The RAdv is structured in the usual way - for each handled interface
* there is a structure &radv_iface that contains a state related to

View File

@ -39,10 +39,14 @@
#undef CONFIG_STATIC
#undef CONFIG_RIP
#undef CONFIG_RADV
#undef CONFIG_BFD
#undef CONFIG_BGP
#undef CONFIG_OSPF
#undef CONFIG_PIPE
/* We use multithreading */
#undef USE_PTHREADS
/* We have <syslog.h> and syslog() */
#undef HAVE_SYSLOG

View File

@ -538,6 +538,11 @@ sk_free(resource *r)
if (s->fd >= 0)
{
close(s->fd);
/* FIXME: we should call sk_stop() for SKF_THREAD sockets */
if (s->flags & SKF_THREAD)
return;
if (s == current_sock)
current_sock = sk_next(s);
if (s == stored_sock)

View File

@ -32,8 +32,24 @@ static FILE *dbgf;
static list *current_log_list;
static char *current_syslog_name; /* NULL -> syslog closed */
bird_clock_t rate_limit_time = 5;
int rate_limit_count = 5;
static const bird_clock_t rate_limit_time = 5;
static const int rate_limit_count = 5;
#ifdef USE_PTHREADS
#include <pthread.h>
static pthread_mutex_t log_mutex;
static inline void log_lock(void) { pthread_mutex_lock(&log_mutex); }
static inline void log_unlock(void) { pthread_mutex_unlock(&log_mutex); }
#else
static inline void log_lock(void) { }
static inline void log_unlock(void) { }
#endif
#ifdef HAVE_SYSLOG
#include <sys/syslog.h>
@ -65,28 +81,6 @@ static char *class_names[] = {
"BUG"
};
#define LOG_BUFFER_SIZE 1024
static char log_buffer[LOG_BUFFER_SIZE];
static char *log_buffer_pos;
static int log_buffer_remains;
const char *log_buffer_ptr = log_buffer;
/**
* log_reset - reset the log buffer
*
* This function resets a log buffer and discards buffered
* messages. Should be used before a log message is prepared
* using logn().
*/
void
log_reset(void)
{
log_buffer_pos = log_buffer;
log_buffer_remains = LOG_BUFFER_SIZE;
log_buffer[0] = 0;
}
/**
* log_commit - commit a log message
@ -101,10 +95,14 @@ log_reset(void)
* in log(), so it should be written like *L_INFO.
*/
void
log_commit(int class)
log_commit(int class, buffer *buf)
{
struct log_config *l;
if (buf->pos == buf->end)
strcpy(buf->end - 100, " ... <too long>");
log_lock();
WALK_LIST(l, *current_log_list)
{
if (!(l->mask & (1 << class)))
@ -119,47 +117,30 @@ log_commit(int class)
tm_format_datetime(tbuf, &config->tf_log, now);
fprintf(l->fh, "%s <%s> ", tbuf, class_names[class]);
}
fputs(log_buffer, l->fh);
fputs(buf->start, l->fh);
fputc('\n', l->fh);
fflush(l->fh);
}
#ifdef HAVE_SYSLOG
else
syslog(syslog_priorities[class], "%s", log_buffer);
syslog(syslog_priorities[class], "%s", buf->start);
#endif
}
cli_echo(class, log_buffer);
log_unlock();
log_reset();
}
static void
log_print(const char *msg, va_list args)
{
int i;
if (log_buffer_remains == 0)
return;
i=bvsnprintf(log_buffer_pos, log_buffer_remains, msg, args);
if (i < 0)
{
bsprintf(log_buffer + LOG_BUFFER_SIZE - 100, " ... <too long>");
log_buffer_remains = 0;
return;
}
log_buffer_pos += i;
log_buffer_remains -= i;
/* FIXME: cli_echo is not thread-safe */
cli_echo(class, buf->start);
}
int buffer_vprint(buffer *buf, const char *fmt, va_list args);
static void
vlog(int class, const char *msg, va_list args)
{
log_reset();
log_print(msg, args);
log_commit(class);
buffer buf;
LOG_BUFFER_INIT(buf);
buffer_vprint(&buf, msg, args);
log_commit(class, &buf);
}
@ -188,26 +169,6 @@ log_msg(char *msg, ...)
va_end(args);
}
/**
* logn - prepare a partial message in the log buffer
* @msg: printf-like formatting string (without message class information)
*
* This function formats a message according to the format string @msg
* and adds it to the log buffer. Messages in the log buffer are
* logged when the buffer is flushed using log_commit() function. The
* message should not contain |\n|, log_commit() also terminates a
* line.
*/
void
logn(char *msg, ...)
{
va_list args;
va_start(args, msg);
log_print(msg, args);
va_end(args);
}
void
log_rl(struct rate_limit *rl, char *msg, ...)
{

View File

@ -199,7 +199,7 @@ unix_read_config(struct config **cp, char *name)
return ret;
}
static void
static struct config *
read_config(void)
{
struct config *conf;
@ -211,7 +211,8 @@ read_config(void)
else
die("Unable to open configuration file %s: %m", config_name);
}
config_commit(conf, RECONFIG_HARD, 0);
return conf;
}
void
@ -776,7 +777,7 @@ main(int argc, char **argv)
proto_build(&proto_unix_kernel);
proto_build(&proto_unix_iface);
read_config();
struct config *conf = read_config();
if (parse_and_exit)
exit(0);
@ -800,6 +801,8 @@ main(int argc, char **argv)
signal_init();
config_commit(conf, RECONFIG_HARD, 0);
#ifdef LOCAL_DEBUG
async_dump_flag = 1;
#endif