BFD protocol, ready for release.

Supports OSPF and BGP and also statically configured sessions.
This commit is contained in:
Ondrej Zajicek 2013-11-19 22:33:48 +01:00
parent 0e175f9f0f
commit 1ec522538f
25 changed files with 1043 additions and 227 deletions

12
aclocal.m4 vendored
View file

@ -133,6 +133,18 @@ if test "$bird_cv_struct_ip_mreqn" = yes ; then
fi 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, AC_DEFUN(BIRD_CHECK_GCC_OPTION,
[ [
bird_tmp_cflags="$CFLAGS" bird_tmp_cflags="$CFLAGS"

View file

@ -10,6 +10,7 @@ AC_ARG_ENABLE(debug, [ --enable-debug enable internal debugging routin
AC_ARG_ENABLE(memcheck, [ --enable-memcheck check memory allocations when debugging (default: enabled)],,enable_memcheck=yes) 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(client, [ --enable-client enable building of BIRD client (default: enabled)],,enable_client=yes)
AC_ARG_ENABLE(ipv6, [ --enable-ipv6 enable building of IPv6 version (default: disabled)],,enable_ipv6=no) AC_ARG_ENABLE(ipv6, [ --enable-ipv6 enable building of IPv6 version (default: disabled)],,enable_ipv6=no)
AC_ARG_ENABLE(pthreads, [ --enable-pthreads enable POSIX threads support (default: detect)],,enable_pthreads=try)
AC_ARG_WITH(suffix, [ --with-suffix=STRING use specified suffix for BIRD files (default: 6 for IPv6 version)],[given_suffix="yes"]) AC_ARG_WITH(suffix, [ --with-suffix=STRING use specified suffix for BIRD files (default: 6 for IPv6 version)],[given_suffix="yes"])
AC_ARG_WITH(sysconfig, [ --with-sysconfig=FILE use specified BIRD system configuration file]) 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(protocols, [ --with-protocols=LIST include specified routing protocols (default: all)],,[with_protocols="all"])
@ -47,11 +48,10 @@ AC_SUBST(runtimedir)
if test "$enable_ipv6" = yes ; then if test "$enable_ipv6" = yes ; then
ip=ipv6 ip=ipv6
SUFFIX=6 SUFFIX=6
all_protocols=bfd,bgp,ospf,pipe,radv,rip,static proto_radv=radv
else else
ip=ipv4 ip=ipv4
SUFFIX="" SUFFIX=""
all_protocols=bfd,bgp,ospf,pipe,rip,static
fi fi
if test "$given_suffix" = yes ; then if test "$given_suffix" = yes ; then
@ -59,10 +59,6 @@ if test "$given_suffix" = yes ; then
fi fi
AC_SUBST(SUFFIX) AC_SUBST(SUFFIX)
if test "$with_protocols" = all ; then
with_protocols="$all_protocols"
fi
if test "$enable_debug" = yes ; then if test "$enable_debug" = yes ; then
CONFIG_FILE="bird$SUFFIX.conf" CONFIG_FILE="bird$SUFFIX.conf"
CONTROL_SOCKET="bird$SUFFIX.ctl" CONTROL_SOCKET="bird$SUFFIX.ctl"
@ -87,12 +83,29 @@ if test -z "$GCC" ; then
AC_MSG_ERROR([This program requires the GNU C Compiler.]) AC_MSG_ERROR([This program requires the GNU C Compiler.])
fi 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 if test "$bird_cflags_default" = yes ; then
BIRD_CHECK_GCC_OPTION(bird_cv_c_option_wno_pointer_sign, -Wno-pointer-sign, -Wall) BIRD_CHECK_GCC_OPTION(bird_cv_c_option_wno_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_aliasing, -fno-strict-aliasing)
BIRD_CHECK_GCC_OPTION(bird_cv_c_option_fno_strict_overflow, -fno-strict-overflow) 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_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_aliasing, -fno-strict-aliasing)
BIRD_ADD_GCC_OPTION(bird_cv_c_option_fno_strict_overflow, -fno-strict-overflow) BIRD_ADD_GCC_OPTION(bird_cv_c_option_fno_strict_overflow, -fno-strict-overflow)
@ -183,6 +196,13 @@ fi
AC_SUBST(iproutedir) AC_SUBST(iproutedir)
all_protocols="$proto_bfd bgp ospf pipe $proto_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]) AC_MSG_CHECKING([protocols])
protocols=`echo "$with_protocols" | sed 's/,/ /g'` protocols=`echo "$with_protocols" | sed 's/,/ /g'`
if test "$protocols" = no ; then protocols= ; fi if test "$protocols" = no ; then protocols= ; fi
@ -272,6 +292,7 @@ BIRD was configured with the following options:
Iproute2 directory: $iproutedir Iproute2 directory: $iproutedir
System configuration: $sysdesc System configuration: $sysdesc
Debugging: $enable_debug Debugging: $enable_debug
POSIX threads: $enable_pthreads
Routing protocols: $protocols Routing protocols: $protocols
Client: $enable_client Client: $enable_client
EOF EOF

View file

@ -1244,6 +1244,178 @@ undefined value is regarded as empty clist for most purposes.
<chapt>Protocols <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 <sect>BGP
<p>The Border Gateway Protocol is the routing protocol used for backbone <p>The Border Gateway Protocol is the routing protocol used for backbone
@ -1258,8 +1430,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 common routing policy. It is identified by a unique 16-bit number
(ASN). Routers within each AS usually exchange AS-internal routing (ASN). Routers within each AS usually exchange AS-internal routing
information with each other using an interior gateway protocol (IGP, information with each other using an interior gateway protocol (IGP,
such as OSPF or RIP). Boundary routers at the border of such as OSPF or RIP). Boundary routers at the border of the AS
the AS communicate global (inter-AS) network reachability information with communicate global (inter-AS) network reachability information with
their neighbors in the neighboring AS'es via exterior BGP (eBGP) and their neighbors in the neighboring AS'es via exterior BGP (eBGP) and
redistribute received information to other routers in the AS via redistribute received information to other routers in the AS via
interior BGP (iBGP). interior BGP (iBGP).
@ -1412,7 +1584,15 @@ for each neighbor using the following configuration parameters:
<tag>igp table <m/name/</tag> Specifies a table that is used <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 as an IGP routing table. Default: the same as the table BGP is
connected to. 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 <tag>ttl security <m/switch/</tag> Use GTSM (RFC 5082 - the
generalized TTL security mechanism). GTSM protects against generalized TTL security mechanism). GTSM protects against
spoofed packets by ignoring received packets with a smaller spoofed packets by ignoring received packets with a smaller
@ -1986,6 +2166,7 @@ protocol ospf &lt;name&gt; {
real broadcast &lt;switch&gt;; real broadcast &lt;switch&gt;;
ptp netmask &lt;switch&gt;; ptp netmask &lt;switch&gt;;
check link &lt;switch&gt;; check link &lt;switch&gt;;
bfd &lt;switch&gt;;
ecmp weight &lt;num&gt;; ecmp weight &lt;num&gt;;
ttl security [&lt;switch&gt;; | tx only] ttl security [&lt;switch&gt;; | tx only]
tx class|dscp &lt;num&gt;; tx class|dscp &lt;num&gt;;
@ -2260,6 +2441,14 @@ protocol ospf &lt;name&gt; {
prefix) is propagated. It is possible that some hardware prefix) is propagated. It is possible that some hardware
drivers or platforms do not implement this feature. Default value is no. 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> <tag>ttl security [<m/switch/ | tx only]</tag>
TTL security is a feature that protects routing protocols TTL security is a feature that protects routing protocols
from remote spoofed packets by using TTL 255 instead of TTL 1 from remote spoofed packets by using TTL 255 instead of TTL 1

View file

@ -24,6 +24,8 @@
#define _MAX(a,b) (((a)>(b))?(a):(b)) #define _MAX(a,b) (((a)>(b))?(a):(b))
#ifndef PARSER #ifndef PARSER
#undef MIN
#undef MAX
#define MIN(a,b) _MIN(a,b) #define MIN(a,b) _MIN(a,b)
#define MAX(a,b) _MAX(a,b) #define MAX(a,b) _MAX(a,b)
#endif #endif

View file

@ -276,7 +276,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
ip_ntox(va_arg(args, ip_addr), ipbuf); ip_ntox(va_arg(args, ip_addr), ipbuf);
else { else {
ip_ntop(va_arg(args, ip_addr), ipbuf); ip_ntop(va_arg(args, ip_addr), ipbuf);
if (field_width > 0) if (field_width == 1)
field_width = STD_ADDRESS_P_LENGTH; field_width = STD_ADDRESS_P_LENGTH;
} }
s = ipbuf; s = ipbuf;

View file

@ -681,6 +681,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 * protos_build - build a protocol list
* *
@ -718,8 +721,10 @@ protos_build(void)
#ifdef CONFIG_BGP #ifdef CONFIG_BGP
proto_build(&proto_bgp); proto_build(&proto_bgp);
#endif #endif
// XXX #ifdef CONFIG_BFD
proto_build(&proto_bfd); proto_build(&proto_bfd);
bfd_init_all();
#endif
proto_pool = rp_new(&root_pool, "Protocols"); proto_pool = rp_new(&root_pool, "Protocols");
proto_flush_event = ev_new(proto_pool); 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. * 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" #include "bfd.h"
@ -17,23 +114,34 @@
#define HASH_IP_EQ(a,b) ipa_equal(a,b) #define HASH_IP_EQ(a,b) ipa_equal(a,b)
#define HASH_IP_FN(k) ipa_hash(k) #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" }; 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); static inline void bfd_notify_kick(struct bfd_proto *p);
/*
* BFD sessions
*/
static void static void
bfd_session_update_state(struct bfd_session *s, uint state, uint diag) 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; int notify;
if (s->loc_state == state) if (state == old_state)
return; return;
TRACE(D_EVENTS, "Session changed %I %d %d", s->addr, state, diag); TRACE(D_EVENTS, "Session to %I changed state from %s to %s",
debug("STATE %I %d %d %d\n", s->addr, s->loc_state, state, diag); s->addr, bfd_state_names[old_state], bfd_state_names[state]);
bfd_lock_sessions(p); bfd_lock_sessions(p);
s->loc_state = state; s->loc_state = state;
s->loc_diag = diag; s->loc_diag = diag;
@ -43,23 +151,16 @@ bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
add_tail(&p->notify_list, &s->n); add_tail(&p->notify_list, &s->n);
bfd_unlock_sessions(p); 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) if (notify)
bfd_notify_kick(p); 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 static void
bfd_session_update_tx_interval(struct bfd_session *s) bfd_session_update_tx_interval(struct bfd_session *s)
{ {
@ -93,10 +194,9 @@ bfd_session_update_detection_time(struct bfd_session *s, int kick)
} }
static void 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) // if (!s->opened) goto stop;
goto stop;
if (s->passive && (s->rem_id == 0)) if (s->passive && (s->rem_id == 0))
goto stop; goto stop;
@ -111,10 +211,12 @@ bfd_session_control_tx_timer(struct bfd_session *s)
goto stop; goto stop;
/* So TX timer should run */ /* So TX timer should run */
if (tm2_active(s->tx_timer)) if (reset || !tm2_active(s->tx_timer))
return; {
s->last_tx = 0;
tm2_start(s->tx_timer, 0);
}
tm2_start(s->tx_timer, 0);
return; return;
stop: stop:
@ -125,6 +227,10 @@ bfd_session_control_tx_timer(struct bfd_session *s)
static void static void
bfd_session_request_poll(struct bfd_session *s, u8 request) 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; s->poll_scheduled |= request;
if (s->poll_active) if (s->poll_active)
@ -132,7 +238,8 @@ bfd_session_request_poll(struct bfd_session *s, u8 request)
s->poll_active = s->poll_scheduled; s->poll_active = s->poll_scheduled;
s->poll_scheduled = 0; s->poll_scheduled = 0;
bfd_send_ctl(s->bfd, s, 0);
bfd_session_control_tx_timer(s, 1);
} }
static void static void
@ -146,11 +253,10 @@ bfd_session_terminate_poll(struct bfd_session *s)
if (poll_done & BFD_POLL_RX) if (poll_done & BFD_POLL_RX)
s->req_min_rx_int = s->req_min_rx_new; 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() */ /* Timers are updated by caller - bfd_session_process_ctl() */
// xxx_restart_poll();
} }
void void
@ -191,10 +297,32 @@ bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old
if (next_state) if (next_state)
bfd_session_update_state(s, next_state, diag); 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) 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 static void
@ -255,8 +383,7 @@ bfd_tx_timer_hook(timer2 *t)
struct bfd_session *s = t->data; struct bfd_session *s = t->data;
s->last_tx = current_time(); s->last_tx = current_time();
// debug("TX %d\n", (s32) (s->last_tx TO_MS)); bfd_send_ctl(s->ifa->bfd, s, 0);
bfd_send_ctl(s->bfd, s, 0);
} }
static void static void
@ -277,48 +404,53 @@ bfd_get_free_id(struct bfd_proto *p)
} }
static struct bfd_session * 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); birdloop_enter(p->loop);
struct bfd_iface *ifa = bfd_get_iface(p, local, iface);
struct bfd_session *s = sl_alloc(p->session_slab); struct bfd_session *s = sl_alloc(p->session_slab);
bzero(s, sizeof(struct bfd_session)); bzero(s, sizeof(struct bfd_session));
s->addr = addr; s->addr = addr;
s->ifa = ifa;
s->loc_id = bfd_get_free_id(p); 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); 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); 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 */ /* Initialization of state variables - see RFC 5880 6.8.1 */
s->loc_state = BFD_STATE_DOWN; s->loc_state = BFD_STATE_DOWN;
s->rem_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->des_min_tx_int = s->des_min_tx_new = ifa->cf->idle_tx_int;
s->req_min_rx_int = s->req_min_rx_new = opts->min_rx_int; s->req_min_rx_int = s->req_min_rx_new = ifa->cf->min_rx_int;
s->rem_min_rx_int = 1; s->rem_min_rx_int = 1;
s->detect_mult = opts->multiplier; s->detect_mult = ifa->cf->multiplier;
s->passive = opts->passive; s->passive = ifa->cf->passive;
s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0); 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); s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
bfd_session_update_tx_interval(s); 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); birdloop_leave(p->loop);
return s; return s;
} }
/*
static void static void
bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa) bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
{ {
birdloop_enter(p->loop); birdloop_enter(p->loop);
s->bsock = bfd_get_socket(p, local, ifa);
// s->local = local;
// s->iface = ifa;
s->opened = 1; s->opened = 1;
bfd_session_control_tx_timer(s); bfd_session_control_tx_timer(s);
@ -331,10 +463,6 @@ bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
{ {
birdloop_enter(p->loop); birdloop_enter(p->loop);
bfd_free_socket(s->bsock);
s->bsock = NULL;
// s->local = IPA_NONE;
// s->iface = NULL;
s->opened = 0; s->opened = 0;
bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN); bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN);
@ -342,54 +470,295 @@ bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
birdloop_leave(p->loop); birdloop_leave(p->loop);
} }
*/
static void static void
bfd_remove_session(struct bfd_proto *p, struct bfd_session *s) bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
{ {
ip_addr ip = s->addr;
birdloop_enter(p->loop); birdloop_enter(p->loop);
bfd_free_socket(s->bsock); bfd_free_iface(s->ifa);
rfree(s->tx_timer); rfree(s->tx_timer);
rfree(s->hold_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); 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); 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); sl_free(p->session_slab, s);
TRACE(D_EVENTS, "Session to %I removed", ip);
birdloop_leave(p->loop); birdloop_leave(p->loop);
} }
static void static void
bfd_configure_session(struct bfd_proto *p, struct bfd_session *s, bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
struct bfd_session_config *opts)
{ {
birdloop_enter(p->loop); 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); u32 tx = (s->loc_state == BFD_STATE_UP) ? cf->min_tx_int : cf->idle_tx_int;
bfd_session_set_min_rx(s, opts->min_rx_int); bfd_session_set_min_tx(s, tx);
s->detect_mult = opts->multiplier; bfd_session_set_min_rx(s, cf->min_rx_int);
s->passive = opts->passive; 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); 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 static void
bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n) 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; return;
} }
@ -402,14 +771,15 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
if (nb->data) 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; return;
} }
nb->data = n->session; n->neigh = nb;
nb->data = n;
if (nb->scope > 0) if (nb->scope > 0)
bfd_open_session(p, n->session, nb->iface->addr->ip, nb->iface); bfd_neigh_notify(nb);
else else
TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface); TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface);
} }
@ -417,33 +787,54 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
static void static void
bfd_stop_neighbor(struct bfd_proto *p, struct bfd_neighbor *n) bfd_stop_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
{ {
if (!n->opts->multihop) if (n->neigh)
{ n->neigh->data = NULL;
struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, 0); n->neigh = NULL;
if (nb)
nb->data = 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 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_config *old = (struct bfd_config *) (p->p.cf);
struct bfd_session *s = nb->data; struct bfd_neighbor *on, *nn;
if (!s) WALK_LIST(on, old->neigh_list)
return; {
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) nn->req = on->req;
bfd_open_session(p, s, nb->iface->addr->ip, nb->iface); nn->active = 1;
return;
}
if ((nb->scope <= 0) && s->opened) bfd_stop_neighbor(p, on);
bfd_close_session(p, s); }
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 */ /* This core notify code should be replaced after main loop transition to birdloop */
int pipe(int pipefd[2]); int pipe(int pipefd[2]);
@ -456,6 +847,8 @@ bfd_notify_hook(sock *sk, int len)
struct bfd_proto *p = sk->data; struct bfd_proto *p = sk->data;
struct bfd_session *s; struct bfd_session *s;
list tmp_list; list tmp_list;
u8 state, diag;
node *n, *nn;
pipe_drain(sk->fd); pipe_drain(sk->fd);
@ -469,10 +862,21 @@ bfd_notify_hook(sock *sk, int len)
{ {
bfd_lock_sessions(p); bfd_lock_sessions(p);
rem2_node(&s->n); rem2_node(&s->n);
state = s->loc_state;
diag = s->loc_diag;
bfd_unlock_sessions(p); bfd_unlock_sessions(p);
// XXX do something /* FIXME: convert to btime and move to bfd_session_update_state() */
TRACE(D_EVENTS, "Notify: session changed %I %d %d", s->addr, s->loc_state, s->loc_diag); 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; return 0;
@ -523,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 * static struct proto *
bfd_init(struct proto_config *c) bfd_init(struct proto_config *c)
{ {
@ -539,25 +954,28 @@ bfd_start(struct proto *P)
struct bfd_proto *p = (struct bfd_proto *) P; struct bfd_proto *p = (struct bfd_proto *) P;
struct bfd_config *cf = (struct bfd_config *) (P->cf); 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"); p->tpool = rp_new(NULL, "BFD thread root");
pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE); pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
p->session_slab = sl_new(P->pool, sizeof(struct bfd_session)); 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_id, P->pool, 8);
HASH_INIT(p->session_hash_ip, P->pool, 4); HASH_INIT(p->session_hash_ip, P->pool, 8);
init_list(&p->sock_list);
init_list(&p->iface_list);
init_list(&p->notify_list); init_list(&p->notify_list);
bfd_notify_init(p); bfd_notify_init(p);
add_tail(&bfd_proto_list, &p->bfd_node);
birdloop_enter(p->loop); birdloop_enter(p->loop);
p->rx_1 = bfd_open_rx_sk(p, 0); p->rx_1 = bfd_open_rx_sk(p, 0);
p->rx_m = bfd_open_rx_sk(p, 1); p->rx_m = bfd_open_rx_sk(p, 1);
birdloop_leave(p->loop); birdloop_leave(p->loop);
bfd_take_requests(p);
struct bfd_neighbor *n; struct bfd_neighbor *n;
WALK_LIST(n, cf->neigh_list) WALK_LIST(n, cf->neigh_list)
bfd_start_neighbor(p, n); bfd_start_neighbor(p, n);
@ -572,76 +990,79 @@ static int
bfd_shutdown(struct proto *P) bfd_shutdown(struct proto *P)
{ {
struct bfd_proto *p = (struct bfd_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); 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 */ /* FIXME: This is hack */
birdloop_enter(p->loop); birdloop_enter(p->loop);
rfree(p->tpool); rfree(p->tpool);
birdloop_leave(p->loop); birdloop_leave(p->loop);
birdloop_free(p->loop);
return PS_DOWN; 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 static int
bfd_reconfigure(struct proto *P, struct proto_config *c) bfd_reconfigure(struct proto *P, struct proto_config *c)
{ {
struct bfd_proto *p = (struct bfd_proto *) P; 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_config *new = (struct bfd_config *) c;
struct bfd_neighbor *n; struct bfd_iface *ifa;
birdloop_mask_wakeups(p->loop); birdloop_mask_wakeups(p->loop);
WALK_LIST(n, old->neigh_list) WALK_LIST(ifa, p->iface_list)
bfd_match_neighbor(p, n, new); bfd_reconfigure_iface(p, ifa, new);
WALK_LIST(n, new->neigh_list) HASH_WALK(p->session_hash_id, next_id, s)
if (!n->session) {
bfd_start_neighbor(p, n); if (s->ifa->changed)
bfd_reconfigure_session(p, s);
}
HASH_WALK_END;
bfd_reconfigure_neighbors(p, new);
birdloop_unmask_wakeups(p->loop); birdloop_unmask_wakeups(p->loop);
return 1; 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 static void
bfd_copy_config(struct proto_config *dest, struct proto_config *src) bfd_copy_config(struct proto_config *dest, struct proto_config *src)
{ {
struct bfd_config *d = (struct bfd_config *) dest; struct bfd_config *d = (struct bfd_config *) dest;
// struct bfd_config *s = (struct bfd_config *) src; // 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); init_list(&d->neigh_list);
} }
void void
bfd_show_sessions(struct proto *P) bfd_show_sessions(struct proto *P)
{ {
byte tbuf[TM_DATETIME_BUFFER_SIZE];
struct bfd_proto *p = (struct bfd_proto *) P; struct bfd_proto *p = (struct bfd_proto *) P;
uint state, diag; uint state, diag;
u32 tx_int, timeout; u32 tx_int, timeout;
@ -655,22 +1076,25 @@ bfd_show_sessions(struct proto *P)
} }
cli_msg(-1013, "%s:", p->p.name); cli_msg(-1013, "%s:", p->p.name);
cli_msg(-1013, "%-12s\t%s\t%s\t%s\t%s", "Router IP", "Iface", cli_msg(-1013, "%-25s %-10s %-10s %-10s %8s %8s",
"State", "TX Int", "Timeout"); "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) 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; state = s->loc_state;
diag = s->loc_diag; diag = s->loc_diag;
ifname = (s->bsock && s->bsock->sk->iface) ? s->bsock->sk->iface->name : "---"; ifname = (s->ifa && s->ifa->sk->iface) ? s->ifa->sk->iface->name : "---";
tx_int = (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS); 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; 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%s %d\t%u\t%u", state = (state < 4) ? state : 0;
s->addr, ifname, bfd_state_names[state], diag, tx_int, timeout); 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; HASH_WALK_END;
@ -685,5 +1109,6 @@ struct protocol proto_bfd = {
.start = bfd_start, .start = bfd_start,
.shutdown = bfd_shutdown, .shutdown = bfd_shutdown,
.reconfigure = bfd_reconfigure, .reconfigure = bfd_reconfigure,
.preconfig = bfd_preconfig,
.copy_config = bfd_copy_config, .copy_config = bfd_copy_config,
}; };

View file

@ -20,6 +20,7 @@
#include "lib/socket.h" #include "lib/socket.h"
#include "lib/string.h" #include "lib/string.h"
#include "nest/bfd.h"
#include "io.h" #include "io.h"
@ -33,19 +34,23 @@
#define BFD_DEFAULT_MULTIPLIER 5 #define BFD_DEFAULT_MULTIPLIER 5
struct bfd_iface_config;
struct bfd_config struct bfd_config
{ {
struct proto_config c; 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_rx_int;
u32 min_tx_int; u32 min_tx_int;
u32 idle_tx_int; u32 idle_tx_int;
u8 multiplier; u8 multiplier;
u8 multihop;
u8 passive; u8 passive;
}; };
@ -55,9 +60,12 @@ struct bfd_neighbor
ip_addr addr; ip_addr addr;
ip_addr local; ip_addr local;
struct iface *iface; 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 struct bfd_proto
@ -66,6 +74,7 @@ struct bfd_proto
struct birdloop *loop; struct birdloop *loop;
pool *tpool; pool *tpool;
pthread_spinlock_t lock; pthread_spinlock_t lock;
node bfd_node;
slab *session_slab; slab *session_slab;
HASH(struct bfd_session) session_hash_id; HASH(struct bfd_session) session_hash_id;
@ -77,25 +86,31 @@ struct bfd_proto
sock *rx_1; sock *rx_1;
sock *rx_m; sock *rx_m;
list sock_list; list iface_list;
}; };
struct bfd_socket struct bfd_iface
{ {
node n; node n;
ip_addr local;
struct iface *iface;
struct bfd_iface_config *cf;
struct bfd_proto *bfd;
sock *sk; sock *sk;
u32 uc; u32 uc;
u8 changed;
}; };
struct bfd_session struct bfd_session
{ {
node n; node n;
ip_addr addr; /* Address of session */ 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_id; /* Next in bfd.session_hash_id */
struct bfd_session *next_ip; /* Next in bfd.session_hash_ip */ struct bfd_session *next_ip; /* Next in bfd.session_hash_ip */
struct bfd_proto *bfd;
u8 opened; u8 opened_unused;
u8 passive; u8 passive;
u8 poll_active; u8 poll_active;
u8 poll_scheduled; u8 poll_scheduled;
@ -123,7 +138,9 @@ struct bfd_session
timer2 *tx_timer; /* Periodic control packet timer */ timer2 *tx_timer; /* Periodic control packet timer */
timer2 *hold_timer; /* Timer for session down detection time */ 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 */
}; };
@ -168,10 +185,7 @@ void bfd_show_sessions(struct proto *P);
/* packets.c */ /* packets.c */
void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final); 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); 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); sock * bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa);
void bfd_free_socket(struct bfd_socket *sk);
#endif /* _BIRD_BFD_H_ */ #endif /* _BIRD_BFD_H_ */

View file

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

@ -52,6 +52,9 @@ struct birdloop
}; };
/*
* Current thread context
*/
static pthread_key_t current_loop_key; static pthread_key_t current_loop_key;
@ -74,6 +77,9 @@ birdloop_init_current(void)
} }
/*
* Time clock
*/
static void times_update_alt(struct birdloop *loop); static void times_update_alt(struct birdloop *loop);
@ -163,6 +169,9 @@ current_time(void)
} }
/*
* Wakeup code for birdloop
*/
static void static void
pipe_new(int *pfds) pipe_new(int *pfds)
@ -244,6 +253,9 @@ wakeup_kick(struct birdloop *loop)
} }
/*
* Events
*/
static inline uint static inline uint
events_waiting(struct birdloop *loop) events_waiting(struct birdloop *loop)
@ -279,12 +291,14 @@ ev2_schedule(event *e)
} }
/*
* Timers
*/
#define TIMER_LESS(a,b) ((a)->expires < (b)->expires) #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, \ #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)) heap[a]->index = (a), heap[b]->index = (b))
static inline uint timers_count(struct birdloop *loop) static inline uint timers_count(struct birdloop *loop)
{ return loop->timers.used - 1; } { return loop->timers.used - 1; }
@ -425,6 +439,9 @@ timers_fire(struct birdloop *loop)
} }
/*
* Sockets
*/
static void static void
sockets_init(struct birdloop *loop) sockets_init(struct birdloop *loop)
@ -494,12 +511,16 @@ sk_stop(sock *s)
static inline uint sk_want_events(sock *s) static inline uint sk_want_events(sock *s)
{ return (s->rx_hook ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); } { return (s->rx_hook ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); }
/*
FIXME: this should be called from sock code
static void static void
sockets_update(struct birdloop *loop, sock *s) sockets_update(struct birdloop *loop, sock *s)
{ {
if (s->index >= 0) if (s->index >= 0)
loop->poll_fd.data[s->index].events = sk_want_events(s); loop->poll_fd.data[s->index].events = sk_want_events(s);
} }
*/
static void static void
sockets_prepare(struct birdloop *loop) sockets_prepare(struct birdloop *loop)
@ -594,17 +615,21 @@ sockets_fire(struct birdloop *loop)
} }
/*
* Birdloop
*/
static void * birdloop_main(void *arg); static void * birdloop_main(void *arg);
struct birdloop * struct birdloop *
birdloop_new(pool *p) birdloop_new(void)
{ {
/* FIXME: this init should be elsewhere and thread-safe */ /* FIXME: this init should be elsewhere and thread-safe */
static int init = 0; static int init = 0;
if (!init) if (!init)
{ birdloop_init_current(); init = 1; } { birdloop_init_current(); init = 1; }
pool *p = rp_new(NULL, "Birdloop root");
struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop)); struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
loop->pool = p; loop->pool = p;
pthread_mutex_init(&loop->mutex, NULL); pthread_mutex_init(&loop->mutex, NULL);
@ -640,6 +665,12 @@ birdloop_stop(struct birdloop *loop)
die("pthread_join(): %M", rv); die("pthread_join(): %M", rv);
} }
void
birdloop_free(struct birdloop *loop)
{
rfree(loop->pool);
}
void void
birdloop_enter(struct birdloop *loop) birdloop_enter(struct birdloop *loop)
@ -735,4 +766,3 @@ birdloop_main(void *arg)
} }

View file

@ -63,6 +63,13 @@ tm2_new_init(pool *p, void (*hook)(struct timer2 *), void *data, uint rec, uint
return t; return t;
} }
static inline void
tm2_set_max(timer2 *t, btime when)
{
if (when > t->expires)
tm2_set(t, when);
}
/* /*
static inline void static inline void
tm2_start_max(timer2 *t, btime after) tm2_start_max(timer2 *t, btime after)
@ -78,9 +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_start(struct birdloop *loop);
void birdloop_stop(struct birdloop *loop); void birdloop_stop(struct birdloop *loop);
void birdloop_free(struct birdloop *loop);
void birdloop_enter(struct birdloop *loop); void birdloop_enter(struct birdloop *loop);
void birdloop_leave(struct birdloop *loop); void birdloop_leave(struct birdloop *loop);
void birdloop_mask_wakeups(struct birdloop *loop); void birdloop_mask_wakeups(struct birdloop *loop);

View file

@ -62,7 +62,7 @@ bfd_format_flags(u8 flags, char *buf)
void void
bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final) 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; struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->tbuf;
char fb[8]; char fb[8];
@ -143,7 +143,7 @@ bfd_rx_hook(sock *sk, int len)
s = bfd_find_session_by_addr(p, sk->faddr); s = bfd_find_session_by_addr(p, sk->faddr);
/* FIXME: better session matching and message */ /* FIXME: better session matching and message */
if (!s || !s->opened) if (!s)
return 1; return 1;
} }
@ -155,7 +155,7 @@ bfd_rx_hook(sock *sk, int len)
u32 old_tx_int = s->des_min_tx_int; u32 old_tx_int = s->des_min_tx_int;
u32 old_rx_int = s->rem_min_rx_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_state = bfd_pkt_get_state(pkt);
s->rem_diag = bfd_pkt_get_diag(pkt); s->rem_diag = bfd_pkt_get_diag(pkt);
s->rem_demand_mode = pkt->flags & BFD_FLAG_DEMAND; s->rem_demand_mode = pkt->flags & BFD_FLAG_DEMAND;
@ -213,7 +213,7 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop)
return NULL; return NULL;
} }
static inline sock * sock *
bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa) bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
{ {
sock *sk = sk_new(p->tpool); sock *sk = sk_new(p->tpool);
@ -246,32 +246,3 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
rfree(sk); rfree(sk);
return NULL; 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/iface.h"
#include "nest/protocol.h" #include "nest/protocol.h"
#include "nest/route.h" #include "nest/route.h"
#include "nest/locks.h" #include "nest/bfd.h"
#include "nest/cli.h" #include "nest/cli.h"
#include "nest/locks.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "lib/socket.h" #include "lib/socket.h"
#include "lib/resource.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_connect(struct bgp_proto *p);
static void bgp_active(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 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) if (rv < 0)
return; return;
if (p->cf->bfd)
bgp_update_bfd(p, p->cf->bfd);
if (p->startup_delay) if (p->startup_delay)
{ {
p->start_state = BSS_DELAY;
BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds", p->startup_delay); BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds", p->startup_delay);
bgp_start_timer(p->startup_timer, 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 static int
bgp_reload_routes(struct proto *P) bgp_reload_routes(struct proto *P)
{ {
@ -825,6 +862,7 @@ bgp_start(struct proto *P)
p->outgoing_conn.state = BS_IDLE; p->outgoing_conn.state = BS_IDLE;
p->incoming_conn.state = BS_IDLE; p->incoming_conn.state = BS_IDLE;
p->neigh = NULL; p->neigh = NULL;
p->bfd_req = NULL;
rt_lock_table(p->igp_table); rt_lock_table(p->igp_table);
@ -992,6 +1030,9 @@ bgp_check_config(struct bgp_config *c)
ipa_has_link_scope(c->source_addr))) ipa_has_link_scope(c->source_addr)))
cf_error("Multihop BGP cannot be used with link-local addresses"); 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 */ /* Different default based on rs_client */
if (!c->missing_lladdr) 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))) || (old->password && new->password && !strcmp(old->password, new->password)))
&& (get_igp_table(old) == get_igp_table(new)); && (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 */ /* We should update our copy of configuration ptr as old configuration will be freed */
if (same) if (same)
p->cf = new; 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_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" };
static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", ""}; 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 char *bgp_auto_errors[] = { "", "Route limit exceeded"};
static const char * static const char *

View file

@ -14,6 +14,7 @@
struct linpool; struct linpool;
struct eattr; struct eattr;
struct bfd_request;
struct bgp_config { struct bgp_config {
struct proto_config c; struct proto_config c;
@ -52,8 +53,10 @@ struct bgp_config {
unsigned error_delay_time_min; /* Time to wait after an error is detected */ unsigned error_delay_time_min; /* Time to wait after an error is detected */
unsigned error_delay_time_max; unsigned error_delay_time_max;
unsigned disable_after_error; /* Disable the protocol when error is detected */ unsigned disable_after_error; /* Disable the protocol when error is detected */
char *password; /* Password used for MD5 authentication */ char *password; /* Password used for MD5 authentication */
struct rtable_config *igp_table; /* Table used for recursive next hop lookups */ struct rtable_config *igp_table; /* Table used for recursive next hop lookups */
int bfd; /* Use BFD for liveness detection */
}; };
#define MLL_SELF 1 #define MLL_SELF 1
@ -99,6 +102,7 @@ struct bgp_proto {
struct bgp_conn incoming_conn; /* Incoming connection we have neither accepted nor rejected yet */ struct bgp_conn incoming_conn; /* Incoming connection we have neither accepted nor rejected yet */
struct object_lock *lock; /* Lock for neighbor connection */ struct object_lock *lock; /* Lock for neighbor connection */
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */ 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 */ ip_addr source_addr; /* Local address used as an advertised next hop */
rtable *igp_table; /* Table used for recursive next hop lookups */ rtable *igp_table; /* Table used for recursive next hop lookups */
struct event *event; /* Event for respawning and shutting process */ struct event *event; /* Event for respawning and shutting process */
@ -287,6 +291,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_NEXT_HOP 2
#define BEM_INVALID_MD5 3 /* MD5 authentication kernel request failed (possibly not supported) */ #define BEM_INVALID_MD5 3 /* MD5 authentication kernel request failed (possibly not supported) */
#define BEM_NO_SOCKET 4 #define BEM_NO_SOCKET 4
#define BEM_BFD_DOWN 5
/* Automatic shutdown error codes */ /* 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, PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP, INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
SECONDARY) SECONDARY, BFD)
CF_GRAMMAR CF_GRAMMAR
@ -110,6 +110,7 @@ bgp_proto:
| bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; } | bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; }
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; } | bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $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 CF_ADDTO(dynamic_attr, BGP_ORIGIN

View file

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

View file

@ -140,6 +140,9 @@ ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
#ifdef OSPFv3 #ifdef OSPFv3
n->iface_id = ntohl(ps->iface_id); n->iface_id = ntohl(ps->iface_id);
#endif #endif
if (n->ifa->cf->bfd)
ospf_neigh_update_bfd(n, n->ifa->bfd);
} }
ospf_neigh_sm(n, INM_HELLOREC); ospf_neigh_sm(n, INM_HELLOREC);

View file

@ -536,6 +536,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
ifa->check_link = ip->check_link; ifa->check_link = ip->check_link;
ifa->ecmp_weight = ip->ecmp_weight; ifa->ecmp_weight = ip->ecmp_weight;
ifa->check_ttl = (ip->ttl_security == 1); ifa->check_ttl = (ip->ttl_security == 1);
ifa->bfd = ip->bfd;
#ifdef OSPFv2 #ifdef OSPFv2
ifa->autype = ip->autype; ifa->autype = ip->autype;
@ -840,6 +841,19 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
ifa->ecmp_weight = new->ecmp_weight; 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 */ /* instance_id is not updated - it is part of key */
return 1; return 1;

View file

@ -582,6 +582,36 @@ ospf_neigh_remove(struct ospf_neighbor *n)
OSPF_TRACE(D_EVENTS, "Deleting neigbor."); OSPF_TRACE(D_EVENTS, "Deleting neigbor.");
} }
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 void
ospf_sh_neigh_info(struct ospf_neighbor *n) 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(struct ospf_iface *ifa, u32 rid);
struct ospf_neighbor *find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip); 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_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); void ospf_sh_neigh_info(struct ospf_neighbor *n);
#endif /* _BIRD_OSPF_NEIGHBOR_H_ */ #endif /* _BIRD_OSPF_NEIGHBOR_H_ */

View file

@ -46,6 +46,7 @@ do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \
#include "nest/route.h" #include "nest/route.h"
#include "nest/cli.h" #include "nest/cli.h"
#include "nest/locks.h" #include "nest/locks.h"
#include "nest/bfd.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "lib/string.h" #include "lib/string.h"
@ -276,6 +277,7 @@ struct ospf_iface
u8 ecmp_weight; /* Weight used for ECMP */ u8 ecmp_weight; /* Weight used for ECMP */
u8 ptp_netmask; /* Send real netmask for P2P */ u8 ptp_netmask; /* Send real netmask for P2P */
u8 check_ttl; /* Check incoming packets for TTL 255 */ u8 check_ttl; /* Check incoming packets for TTL 255 */
u8 bfd; /* Use BFD on iface */
}; };
struct ospf_md5 struct ospf_md5
@ -708,6 +710,7 @@ struct ospf_neighbor
#define ACKL_DIRECT 0 #define ACKL_DIRECT 0
#define ACKL_DELAY 1 #define ACKL_DELAY 1
timer *ackd_timer; /* Delayed ack timer */ timer *ackd_timer; /* Delayed ack timer */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
u32 csn; /* Last received crypt seq number (for MD5) */ u32 csn; /* Last received crypt seq number (for MD5) */
}; };
@ -818,6 +821,7 @@ struct ospf_iface_patt
u8 real_bcast; /* Not really used in OSPFv3 */ u8 real_bcast; /* Not really used in OSPFv3 */
u8 ptp_netmask; /* bool + 2 for unspecified */ u8 ptp_netmask; /* bool + 2 for unspecified */
u8 ttl_security; /* bool + 2 for TX only */ u8 ttl_security; /* bool + 2 for TX only */
u8 bfd;
#ifdef OSPFv2 #ifdef OSPFv2
list *passwords; list *passwords;

View file

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

View file

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

View file

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

View file

@ -36,13 +36,20 @@ static const bird_clock_t rate_limit_time = 5;
static const int rate_limit_count = 5; static const int rate_limit_count = 5;
// XXX add ifdef for threads #ifdef USE_PTHREADS
#include <pthread.h> #include <pthread.h>
static pthread_mutex_t log_mutex; static pthread_mutex_t log_mutex;
static inline void log_lock(void) { pthread_mutex_lock(&log_mutex); } static inline void log_lock(void) { pthread_mutex_lock(&log_mutex); }
static inline void log_unlock(void) { pthread_mutex_unlock(&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 #ifdef HAVE_SYSLOG
#include <sys/syslog.h> #include <sys/syslog.h>