mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Merge commit '0072d11f' into tmp-learn
This commit is contained in:
commit
becca314e2
@ -134,21 +134,11 @@ docker_fedora-34-amd64:
|
||||
IMG_NAME: "fedora-34-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_centos-7-amd64:
|
||||
variables:
|
||||
IMG_NAME: "centos-7-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_centos-8-amd64:
|
||||
variables:
|
||||
IMG_NAME: "centos-8-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-14_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-14.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-16_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-16.04-amd64"
|
||||
@ -312,18 +302,10 @@ build-fedora-34-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.nic.cz/labs/bird:fedora-33-amd64
|
||||
|
||||
build-centos-7-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.nic.cz/labs/bird:centos-7-amd64
|
||||
|
||||
build-centos-8-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.nic.cz/labs/bird:centos-8-amd64
|
||||
|
||||
build-ubuntu-14_04-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.nic.cz/labs/bird:ubuntu-14.04-amd64
|
||||
|
||||
build-ubuntu-16_04-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.nic.cz/labs/bird:ubuntu-16.04-amd64
|
||||
@ -466,14 +448,7 @@ pkg-fedora-33-amd64:
|
||||
pkg-fedora-34-amd64:
|
||||
<<: *pkg-rpm
|
||||
needs: [build-fedora-34-amd64]
|
||||
image: registry.nic.cz/labs/bird:fedora-34-amd64
|
||||
|
||||
pkg-centos-7-amd64:
|
||||
<<: *pkg-rpm-wa
|
||||
variables:
|
||||
LC_ALL: en_US.UTF-8
|
||||
needs: [build-centos-7-amd64]
|
||||
image: registry.nic.cz/labs/bird:centos-7-amd64
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-34-amd64
|
||||
|
||||
pkg-centos-8-amd64:
|
||||
<<: *pkg-rpm-wa
|
||||
|
4
INSTALL
4
INSTALL
@ -27,9 +27,9 @@ Requirements
|
||||
|
||||
For compiling BIRD you need these programs and libraries:
|
||||
|
||||
- GNU C Compiler (or LLVM Clang)
|
||||
- GNU C Compiler (or LLVM Clang) capable of compiling C11 code
|
||||
- GNU Make
|
||||
- GNU Bison
|
||||
- GNU Bison (at least 3.0)
|
||||
- GNU M4
|
||||
- Flex
|
||||
|
||||
|
8
NEWS
8
NEWS
@ -1,3 +1,11 @@
|
||||
Version 2.0.10 (2022-06-16)
|
||||
o BGP performance improvements
|
||||
o BFD: New 'strict bind' option
|
||||
o RPKI: VRF support
|
||||
o Allow use of 240.0.0.0/4 as a private range
|
||||
o BIRD client uses exit status to report errors
|
||||
o Important bugfixes
|
||||
|
||||
Version 2.0.9 (2022-02-09)
|
||||
o BGP: Flowspec validation procedure
|
||||
o Babel: MAC authentication support
|
||||
|
14
aclocal.m4
vendored
14
aclocal.m4
vendored
@ -1,5 +1,6 @@
|
||||
dnl ** Additional Autoconf tests for BIRD configure script
|
||||
dnl ** (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
dnl ** (c) 2021 Maria Matejka <mq@jmq.cz>
|
||||
|
||||
AC_DEFUN([BIRD_CHECK_POINTER_ALIGNMENT],
|
||||
[
|
||||
@ -35,14 +36,23 @@ AC_DEFUN([BIRD_CHECK_THREAD_LOCAL],
|
||||
AC_COMPILE_IFELSE([
|
||||
AC_LANG_PROGRAM(
|
||||
[
|
||||
_Thread_local static int x = 42;
|
||||
static _Thread_local int x = 42;
|
||||
],
|
||||
[]
|
||||
)
|
||||
],
|
||||
[bird_cv_thread_local=yes],
|
||||
[AC_COMPILE_IFELSE([
|
||||
AC_LANG_PROGRAM(
|
||||
[
|
||||
static __thread int x = 42;
|
||||
],
|
||||
[]
|
||||
)
|
||||
],
|
||||
[bird_cv_thread_local=__thread],
|
||||
[bird_cv_thread_local=no]
|
||||
)
|
||||
)])
|
||||
)
|
||||
])
|
||||
|
||||
|
114
bird-gdb.py
114
bird-gdb.py
@ -122,7 +122,7 @@ class BIRDFLinePrinter(BIRDPrinter):
|
||||
"n": n,
|
||||
"code": str(self.val['items'][n]['fi_code']),
|
||||
} if n % 8 == 0 else str(self.val['items'][n]['fi_code']) for n in range(cnt)]))
|
||||
|
||||
|
||||
|
||||
class BIRDFExecStackPrinter(BIRDPrinter):
|
||||
"Print BIRD's struct f_exec_stack"
|
||||
@ -140,6 +140,118 @@ class BIRDFExecStackPrinter(BIRDPrinter):
|
||||
"n": n
|
||||
} for n in range(cnt-1, -1, -1) ])
|
||||
|
||||
|
||||
class BIRD:
|
||||
def skip_back(t, i, v):
|
||||
if isinstance(t, str):
|
||||
t = gdb.lookup_type(t)
|
||||
elif isinstance(t, gdb.Value):
|
||||
t = gdb.lookup_type(t.string())
|
||||
elif not isinstance(t, gdb.Type):
|
||||
raise Exception(f"First argument of skip_back(t, i, v) must be a type, got {type(t)}")
|
||||
|
||||
t = t.strip_typedefs()
|
||||
nullptr = gdb.Value(0).cast(t.pointer())
|
||||
|
||||
if isinstance(i, gdb.Value):
|
||||
i = i.string()
|
||||
elif not isinstance(i, str):
|
||||
raise Exception(f"Second argument of skip_back(t, i, v) must be a item name, got {type(i)}")
|
||||
|
||||
if not isinstance(v, gdb.Value):
|
||||
raise Exception(f"Third argument of skip_back(t, i, v) must be a value, got {type(v)}")
|
||||
if v.type.code != gdb.TYPE_CODE_PTR and v.type.code != gdb.TYPE_CODE_REF:
|
||||
raise Exception(f"Third argument of skip_back(t, i, v) must be a pointer, is {v.type} ({v.type.code})")
|
||||
if v.type.target().strip_typedefs() != nullptr[i].type:
|
||||
raise Exception(f"Third argument of skip_back(t, i, v) points to type {v.type.target().strip_typedefs()}, should be {nullptr[i].type}")
|
||||
|
||||
uintptr_t = gdb.lookup_type("uintptr_t")
|
||||
taddr = v.dereference().address.cast(uintptr_t) - nullptr[i].address.cast(uintptr_t)
|
||||
return gdb.Value(taddr).cast(t.pointer())
|
||||
|
||||
class skip_back_gdb(gdb.Function):
|
||||
"Given address of a structure item, returns address of the structure, as the SKIP_BACK macro does"
|
||||
def __init__(self):
|
||||
gdb.Function.__init__(self, "SKIP_BACK")
|
||||
|
||||
def invoke(self, t, i, v):
|
||||
return BIRD.skip_back(t, i, v)
|
||||
|
||||
|
||||
BIRD.skip_back_gdb()
|
||||
|
||||
|
||||
class BIRDList:
|
||||
def __init__(self, val):
|
||||
ltype = val.type.strip_typedefs()
|
||||
if ltype.code != gdb.TYPE_CODE_UNION or ltype.tag != "list":
|
||||
raise Exception(f"Not a list, is type {ltype}")
|
||||
|
||||
self.head = val["head"]
|
||||
self.tail_node = val["tail_node"]
|
||||
|
||||
if str(self.head.address) == '0x0':
|
||||
raise Exception("List head is NULL")
|
||||
|
||||
if str(self.tail_node["prev"].address) == '0x0':
|
||||
raise Exception("List tail is NULL")
|
||||
|
||||
def walk(self, do):
|
||||
cur = self.head
|
||||
while cur.dereference() != self.tail_node:
|
||||
do(cur)
|
||||
cur = cur.dereference()["next"]
|
||||
|
||||
|
||||
class BIRDListLength(gdb.Function):
|
||||
"""Returns length of the list, as in
|
||||
print $list_length(routing_tables)"""
|
||||
def __init__(self):
|
||||
super(BIRDListLength, self).__init__("list_length")
|
||||
|
||||
def count(self, _):
|
||||
self.cnt += 1
|
||||
|
||||
def invoke(self, l):
|
||||
self.cnt = 0
|
||||
BIRDList(l).walk(self.count)
|
||||
return self.cnt
|
||||
|
||||
BIRDListLength()
|
||||
|
||||
class BIRDListItem(gdb.Function):
|
||||
"""Returns n-th item of the list."""
|
||||
def __init__(self):
|
||||
super(BIRDListItem, self).__init__("list_item")
|
||||
|
||||
class BLException(Exception):
|
||||
def __init__(self, node, msg):
|
||||
Exception.__init__(self, msg)
|
||||
self.node = node
|
||||
|
||||
def count(self, node):
|
||||
if self.cnt == self.pos:
|
||||
raise self.BLException(node, "Node found")
|
||||
|
||||
self.cnt += 1
|
||||
|
||||
def invoke(self, l, n, t=None, item="n"):
|
||||
self.cnt = 0
|
||||
self.pos = n
|
||||
bl = BIRDList(l)
|
||||
try:
|
||||
bl.walk(self.count)
|
||||
except self.BLException as e:
|
||||
if t is None:
|
||||
return e.node
|
||||
else:
|
||||
return BIRD.skip_back(t, item, e.node)
|
||||
|
||||
raise Exception("List too short")
|
||||
|
||||
BIRDListItem()
|
||||
|
||||
|
||||
def register_printers(objfile):
|
||||
objfile.pretty_printers.append(BIRDFInstPrinter.lookup)
|
||||
objfile.pretty_printers.append(BIRDFValPrinter.lookup)
|
||||
|
@ -577,6 +577,8 @@ check_eof(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void cf_swap_soft_scope(void);
|
||||
|
||||
static struct symbol *
|
||||
cf_new_symbol(const byte *c)
|
||||
{
|
||||
@ -586,6 +588,8 @@ cf_new_symbol(const byte *c)
|
||||
if (l > SYM_MAX_LEN)
|
||||
cf_error("Symbol too long");
|
||||
|
||||
cf_swap_soft_scope();
|
||||
|
||||
s = cfg_allocz(sizeof(struct symbol) + l + 1);
|
||||
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
|
||||
strcpy(s->name, c);
|
||||
@ -676,8 +680,8 @@ cf_localize_symbol(struct symbol *sym)
|
||||
return sym;
|
||||
|
||||
/* If the scope is the current, it is already defined in this scope. */
|
||||
if (sym->scope == conf_this_scope)
|
||||
cf_error("Symbol already defined");
|
||||
if (cf_symbol_is_local(sym))
|
||||
cf_error("Symbol '%s' already defined", sym->name);
|
||||
|
||||
/* Not allocated here yet, doing it now. */
|
||||
return cf_new_symbol(sym->name);
|
||||
@ -839,12 +843,60 @@ cf_push_scope(struct symbol *sym)
|
||||
void
|
||||
cf_pop_scope(void)
|
||||
{
|
||||
ASSERT(!conf_this_scope->soft_scopes);
|
||||
|
||||
conf_this_scope->active = 0;
|
||||
conf_this_scope = conf_this_scope->next;
|
||||
|
||||
ASSERT(conf_this_scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_push_soft_scope - enter new soft scope
|
||||
*
|
||||
* If we want to enter a new anonymous scope that most likely will not contain
|
||||
* any symbols, we can use cf_push_soft_scope() insteas of cf_push_scope().
|
||||
* Such scope will be converted to a regular scope on first use.
|
||||
*/
|
||||
void
|
||||
cf_push_soft_scope(void)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes < 0xfe)
|
||||
conf_this_scope->soft_scopes++;
|
||||
else
|
||||
cf_push_scope(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_pop_soft_scope - leave a soft scope
|
||||
*
|
||||
* Leave a soft scope entered by cf_push_soft_scope().
|
||||
*/
|
||||
void
|
||||
cf_pop_soft_scope(void)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes)
|
||||
conf_this_scope->soft_scopes--;
|
||||
else
|
||||
cf_pop_scope();
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_swap_soft_scope - convert soft scope to regular scope
|
||||
*
|
||||
* Soft scopes cannot hold symbols, so they must be converted to regular scopes
|
||||
* on first use. It is done automatically by cf_new_symbol().
|
||||
*/
|
||||
static inline void
|
||||
cf_swap_soft_scope(void)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes)
|
||||
{
|
||||
conf_this_scope->soft_scopes--;
|
||||
cf_push_scope(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_symbol_class_name - get name of a symbol class
|
||||
* @sym: symbol
|
||||
|
@ -140,6 +140,7 @@ config_parse(struct config *c)
|
||||
protos_preconfig(c);
|
||||
rt_preconfig(c);
|
||||
cf_parse();
|
||||
rt_postconfig(c);
|
||||
|
||||
if (EMPTY_LIST(c->protos))
|
||||
cf_error("No protocol is specified in the config file");
|
||||
|
11
conf/conf.h
11
conf/conf.h
@ -44,7 +44,7 @@ struct config {
|
||||
|
||||
int cli_debug; /* Tracing of CLI connections and commands */
|
||||
int latency_debug; /* I/O loop tracks duration of each event */
|
||||
int pipe_debug; /* Track route propagation through pipes */
|
||||
int table_debug; /* Track route propagation through tables */
|
||||
u32 latency_limit; /* Events with longer duration are logged (us) */
|
||||
u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */
|
||||
u32 watchdog_timeout; /* Watchdog timeout (in seconds, 0 = disabled) */
|
||||
@ -136,7 +136,8 @@ struct sym_scope {
|
||||
HASH(struct symbol) hash; /* Local symbol hash */
|
||||
|
||||
uint slots; /* Variable slots */
|
||||
int active; /* Currently entered */
|
||||
byte active; /* Currently entered */
|
||||
byte soft_scopes; /* Number of soft scopes above */
|
||||
};
|
||||
|
||||
extern struct sym_scope *global_root_scope;
|
||||
@ -202,6 +203,9 @@ struct symbol *cf_get_symbol(const byte *c);
|
||||
struct symbol *cf_default_name(char *template, int *counter);
|
||||
struct symbol *cf_localize_symbol(struct symbol *sym);
|
||||
|
||||
static inline int cf_symbol_is_local(struct symbol *sym)
|
||||
{ return (sym->scope == conf_this_scope) && !conf_this_scope->soft_scopes; }
|
||||
|
||||
/**
|
||||
* cf_define_symbol - define meaning of a symbol
|
||||
* @sym: symbol to be defined
|
||||
@ -225,6 +229,9 @@ struct symbol *cf_localize_symbol(struct symbol *sym);
|
||||
|
||||
void cf_push_scope(struct symbol *);
|
||||
void cf_pop_scope(void);
|
||||
void cf_push_soft_scope(void);
|
||||
void cf_pop_soft_scope(void);
|
||||
|
||||
char *cf_symbol_class_name(struct symbol *sym);
|
||||
|
||||
/* Parser */
|
||||
|
@ -76,6 +76,7 @@ CF_DECLS
|
||||
struct f_attr_bit fab;
|
||||
struct f_lval flv;
|
||||
struct f_line *fl;
|
||||
struct f_arg *fa;
|
||||
const struct filter *f;
|
||||
struct f_tree *e;
|
||||
struct f_trie *trie;
|
||||
@ -111,7 +112,7 @@ CF_DECLS
|
||||
%type <i> expr bool pxlen4
|
||||
%type <time> expr_us time
|
||||
%type <a> ipa
|
||||
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
|
||||
%type <net> net_ip4_ net_ip4 net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
|
||||
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
|
||||
%type <ad> label_stack_start label_stack
|
||||
|
||||
@ -154,14 +155,14 @@ conf: definition ;
|
||||
definition:
|
||||
DEFINE symbol '=' term ';' {
|
||||
struct f_val val;
|
||||
if (f_eval(f_linearize($4), &val) > F_RETURN) cf_error("Runtime error");
|
||||
if (f_eval(f_linearize($4, 1), &val) > F_RETURN) cf_error("Runtime error");
|
||||
cf_define_symbol($2, SYM_CONSTANT | val.type, val, lp_val_copy(cfg_mem, &val));
|
||||
}
|
||||
;
|
||||
|
||||
expr:
|
||||
NUM
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
|
||||
| symbol_known {
|
||||
if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
|
||||
$$ = SYM_VAL($1).i; }
|
||||
@ -307,6 +308,15 @@ net_:
|
||||
|
||||
/* Networks - regular */
|
||||
|
||||
net_ip4:
|
||||
net_ip4_
|
||||
| CF_SYM_KNOWN {
|
||||
if (($1->class != (SYM_CONSTANT | T_NET)) || (SYM_VAL($1).net->type != NET_IP4))
|
||||
cf_error("IPv4 network constant expected");
|
||||
$$ = * SYM_VAL($1).net;
|
||||
}
|
||||
;
|
||||
|
||||
net_ip6:
|
||||
net_ip6_
|
||||
| CF_SYM_KNOWN {
|
||||
|
@ -142,7 +142,7 @@ flow_frag_opts:
|
||||
;
|
||||
|
||||
flow4_item:
|
||||
flow_srcdst net_ip {
|
||||
flow_srcdst net_ip4 {
|
||||
flow_builder_set_type(this_flow, $1);
|
||||
flow_builder4_add_pfx(this_flow, (net_addr_ip4 *) &($2));
|
||||
}
|
||||
|
48
configure.ac
48
configure.ac
@ -36,12 +36,6 @@ AC_ARG_ENABLE([memcheck],
|
||||
[enable_memcheck=yes]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([pthreads],
|
||||
[AS_HELP_STRING([--enable-pthreads], [enable POSIX threads support @<:@try@:>@])],
|
||||
[],
|
||||
[enable_pthreads=try]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([libssh],
|
||||
[AS_HELP_STRING([--enable-libssh], [enable LibSSH support in RPKI @<:@try@:>@])],
|
||||
[],
|
||||
@ -125,25 +119,19 @@ if test -z "$GCC" ; then
|
||||
fi
|
||||
|
||||
BIRD_CHECK_THREAD_LOCAL
|
||||
if test "$bird_cv_thread_local" = yes ; then
|
||||
AC_DEFINE([HAVE_THREAD_LOCAL], [1], [Define to 1 if _Thread_local is available])
|
||||
if test "$bird_cv_thread_local" = no ; then
|
||||
AC_MSG_ERROR([This program requires thread local storage.])
|
||||
elif test "$bird_cv_thread_local" != yes ; then
|
||||
AC_DEFINE_UNQUOTED([_Thread_local], [$bird_cv_thread_local], [Legacy _Thread_local])
|
||||
fi
|
||||
|
||||
if test "$enable_pthreads" != no ; then
|
||||
BIRD_CHECK_PTHREADS
|
||||
BIRD_CHECK_PTHREADS
|
||||
|
||||
if test "$bird_cv_lib_pthreads" = yes ; then
|
||||
AC_DEFINE([USE_PTHREADS], [1], [Define to 1 if pthreads are enabled])
|
||||
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
|
||||
if test "$bird_cv_lib_pthreads" = yes ; then
|
||||
CFLAGS="$CFLAGS -pthread"
|
||||
LDFLAGS="$LDFLAGS -pthread"
|
||||
else
|
||||
AC_MSG_ERROR([POSIX threads not available.])
|
||||
fi
|
||||
|
||||
# This is assumed to be necessary for proper BIRD build
|
||||
@ -304,8 +292,7 @@ if test "$enable_mpls_kernel" != no ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip rpki static"
|
||||
|
||||
all_protocols="bfd babel bgp mrt ospf perf pipe radv rip rpki static"
|
||||
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
||||
|
||||
if test "$with_protocols" = all ; then
|
||||
@ -350,10 +337,16 @@ case $sysdesc in
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_CHECK_HEADERS_ONCE([alloca.h syslog.h])
|
||||
AC_CHECK_HEADER([sys/mman.h], [AC_DEFINE([HAVE_MMAP], [1], [Define to 1 if mmap() is available.])])
|
||||
AC_CHECK_HEADERS_ONCE([alloca.h syslog.h stdatomic.h])
|
||||
AC_CHECK_HEADER([sys/mman.h], [AC_DEFINE([HAVE_MMAP], [1], [Define to 1 if mmap() is available.])], have_mman=no)
|
||||
AC_CHECK_FUNC([aligned_alloc], [AC_DEFINE([HAVE_ALIGNED_ALLOC], [1], [Define to 1 if aligned_alloc() is available.])], have_aligned_alloc=no)
|
||||
AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [], [#include <sys/socket.h>])
|
||||
|
||||
if test "$have_aligned_alloc" = "no" && test "$have_mman" = "no" ; then
|
||||
AC_MSG_ERROR([No means of aligned alloc found. Need mmap() or aligned_alloc().])
|
||||
fi
|
||||
|
||||
|
||||
AC_C_BIGENDIAN(
|
||||
[AC_DEFINE([CPU_BIG_ENDIAN], [1], [Define to 1 if cpu is big endian])],
|
||||
[AC_DEFINE([CPU_LITTLE_ENDIAN], [1], [Define to 1 if cpu is little endian])],
|
||||
@ -409,7 +402,7 @@ if test "$enable_debug" = yes ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "enable_debug_expensive" = yes ; then
|
||||
if test "$enable_debug_expensive" = yes ; then
|
||||
AC_DEFINE([ENABLE_EXPENSIVE_CHECKS], [1], [Define to 1 if you want to run expensive consistency checks.])
|
||||
fi
|
||||
fi
|
||||
@ -474,7 +467,6 @@ AC_MSG_RESULT([ Object directory: $objdir])
|
||||
AC_MSG_RESULT([ Iproute2 directory: $iproutedir])
|
||||
AC_MSG_RESULT([ System configuration: $sysdesc])
|
||||
AC_MSG_RESULT([ Debugging: $enable_debug])
|
||||
AC_MSG_RESULT([ POSIX threads: $enable_pthreads])
|
||||
AC_MSG_RESULT([ Routing protocols: $protocols])
|
||||
AC_MSG_RESULT([ LibSSH support in RPKI: $enable_libssh])
|
||||
AC_MSG_RESULT([ Kernel MPLS support: $enable_mpls_kernel])
|
||||
|
@ -684,6 +684,30 @@ to set options.
|
||||
limit to the settle time from the initial ROA table change even if
|
||||
there are consecutive updates gradually renewing the settle time.
|
||||
Default: 20 s.
|
||||
|
||||
<tag><label id="rtable-gc-threshold">gc threshold <m/number/</tag>
|
||||
Specify a minimum amount of removed networks that triggers a garbage
|
||||
collection (GC) cycle. Default: 1000.
|
||||
|
||||
<tag><label id="rtable-gc-period">gc period <m/time/</tag>
|
||||
Specify a period of time between consecutive GC cycles. When there is a
|
||||
significant amount of route withdraws, GC cycles are executed repeatedly
|
||||
with given period time (with some random factor). When there is just
|
||||
small amount of changes, GC cycles are not executed. In extensive route
|
||||
server setups, running GC on hundreds of full BGP routing tables can
|
||||
take significant amount of time, therefore they should use higher GC
|
||||
periods. Default: adaptive, based on number of routing tables in the
|
||||
configuration. From 10 s (with <= 25 routing tables) up to 600 s (with
|
||||
>= 1500 routing tables).
|
||||
|
||||
<tag><label id="rtable-cork-threshold">cork threshold <m/number/ <m/number/</tag>
|
||||
Too many pending exports may lead to memory bloating. In such cases,
|
||||
BIRD tries to relieve the memory pressure by pausing some routines until
|
||||
the queue sizes get low enough. This option allows the user to set the
|
||||
thresholds; first value is the low threshold (when to resume), the
|
||||
second one is the high threshold (when to pause). The higher is the
|
||||
threshold, the more memory can get used. In most cases, the defaults
|
||||
should work for you. Default: 128, 512.
|
||||
</descrip>
|
||||
|
||||
|
||||
@ -1247,8 +1271,8 @@ this:
|
||||
|
||||
<code>
|
||||
filter not_too_far
|
||||
int var;
|
||||
{
|
||||
int var;
|
||||
if defined( rip_metric ) then
|
||||
var = rip_metric;
|
||||
else {
|
||||
@ -1277,9 +1301,9 @@ local variables. Recursion is not allowed. Function definitions look like this:
|
||||
|
||||
<code>
|
||||
function name ()
|
||||
int local_variable;
|
||||
{
|
||||
local_variable = 5;
|
||||
int local_variable;
|
||||
int another_variable = 5;
|
||||
}
|
||||
|
||||
function with_parameters (int parameter)
|
||||
@ -1288,16 +1312,19 @@ function with_parameters (int parameter)
|
||||
}
|
||||
</code>
|
||||
|
||||
<p>Unlike in C, variables are declared after the <cf/function/ line, but before
|
||||
the first <cf/{/. You can't declare variables in nested blocks. Functions are
|
||||
called like in C: <cf>name(); with_parameters(5);</cf>. Function may return
|
||||
values using the <cf>return <m/[expr]/</cf> command. Returning a value exits
|
||||
from current function (this is similar to C).
|
||||
<p>Like in C programming language, variables are declared inside function body,
|
||||
either at the beginning, or mixed with other statements. Declarations may
|
||||
contain initialization. You can also declare variables in nested blocks, such
|
||||
variables have scope restricted to such block. There is a deprecated syntax to
|
||||
declare variables after the <cf/function/ line, but before the first <cf/{/.
|
||||
Functions are called like in C: <cf>name(); with_parameters(5);</cf>. Function
|
||||
may return values using the <cf>return <m/[expr]/</cf> command. Returning a
|
||||
value exits from current function (this is similar to C).
|
||||
|
||||
<p>Filters are defined in a way similar to functions except they can't have
|
||||
<p>Filters are defined in a way similar to functions except they cannot have
|
||||
explicit parameters. They get a route table entry as an implicit parameter, it
|
||||
is also passed automatically to any functions called. The filter must terminate
|
||||
with either <cf/accept/ or <cf/reject/ statement. If there's a runtime error in
|
||||
with either <cf/accept/ or <cf/reject/ statement. If there is a runtime error in
|
||||
filter, the route is rejected.
|
||||
|
||||
<p>A nice trick to debug filters is to use <cf>show route filter <m/name/</cf>
|
||||
@ -1667,7 +1694,8 @@ prefix and an ASN as arguments.
|
||||
<sect>Control structures
|
||||
<label id="control-structures">
|
||||
|
||||
<p>Filters support two control structures: conditions and case switches.
|
||||
<p>Filters support several control structures: conditions, for loops and case
|
||||
switches.
|
||||
|
||||
<p>Syntax of a condition is: <cf>if <M>boolean expression</M> then <m/commandT/;
|
||||
else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
|
||||
@ -1675,6 +1703,14 @@ else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
|
||||
omitted. If the <cf><m>boolean expression</m></cf> is true, <m/commandT/ is
|
||||
executed, otherwise <m/commandF/ is executed.
|
||||
|
||||
<p>For loops allow to iterate over elements in compound data like BGP paths or
|
||||
community lists. The syntax is: <cf>for [ <m/type/ ] <m/variable/ in <m/expr/
|
||||
do <m/command/;</cf> and you can also use compound command like in conditions.
|
||||
The expression is evaluated to a compound data, then for each element from such
|
||||
data the command is executed with the item assigned to the variable. A variable
|
||||
may be an existing one (when just name is used) or a locally defined (when type
|
||||
and name is used). In both cases, it must have the same type as elements.
|
||||
|
||||
<p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case
|
||||
<m/expr/ { else: | <m/num_or_prefix [ .. num_or_prefix]/: <m/statement/ ; [
|
||||
... ] }</cf>. The expression after <cf>case</cf> can be of any type which can be
|
||||
@ -1687,16 +1723,21 @@ neither of the <cf/:/ clauses, the statements after <cf/else:/ are executed.
|
||||
<p>Here is example that uses <cf/if/ and <cf/case/ structures:
|
||||
|
||||
<code>
|
||||
if 1234 = i then printn "."; else {
|
||||
print "not 1234";
|
||||
print "You need {} around multiple commands";
|
||||
}
|
||||
|
||||
for int asn in bgp_path do {
|
||||
printn "ASN: ", asn;
|
||||
if asn < 65536 then print " (2B)"; else print " (4B)";
|
||||
}
|
||||
|
||||
case arg1 {
|
||||
2: print "two"; print "I can do more commands without {}";
|
||||
3 .. 5: print "three to five";
|
||||
else: print "something else";
|
||||
}
|
||||
|
||||
if 1234 = i then printn "."; else {
|
||||
print "not 1234";
|
||||
print "You need {} around multiple commands";
|
||||
}
|
||||
</code>
|
||||
|
||||
|
||||
|
176
filter/config.Y
176
filter/config.Y
@ -32,6 +32,36 @@ static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
||||
f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_DEFAULT, f_new_inst(FI_EA_GET, da), f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = def })), arg), da)
|
||||
|
||||
|
||||
static int
|
||||
f_new_var(struct sym_scope *s)
|
||||
{
|
||||
/*
|
||||
* - A variable is an offset on vstack from vbase.
|
||||
* - Vbase is set on filter start / function call.
|
||||
* - Scopes contain anonymous scopes (blocks) inside filter/function scope
|
||||
* - Each scope knows number of vars in that scope
|
||||
* - Offset is therefore a sum of 'slots' up to named scope
|
||||
* - New variables are added on top of vstk, so intermediate values cannot
|
||||
* be there during FI_VAR_INIT. I.e. no 'var' inside 'term'.
|
||||
* - Also, each f_line must always have its scope, otherwise a variable may
|
||||
* be defined but not initialized if relevant f_line is not executed.
|
||||
*/
|
||||
|
||||
int offset = s->slots++;
|
||||
|
||||
while (!s->name)
|
||||
{
|
||||
s = s->next;
|
||||
ASSERT(s);
|
||||
offset += s->slots;
|
||||
}
|
||||
|
||||
if (offset >= 0xff)
|
||||
cf_error("Too many variables, at most 255 allowed");
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets and their items are during parsing handled as lists, linked
|
||||
* through left ptr. The first item in a list also contains a pointer
|
||||
@ -276,7 +306,7 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
|
||||
|
||||
checker = f_new_inst(FI_EQ, expr, getter);
|
||||
setter->next = checker;
|
||||
|
||||
|
||||
return assert_done(setter, start, end);
|
||||
}
|
||||
|
||||
@ -287,6 +317,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
|
||||
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
||||
IF, THEN, ELSE, CASE,
|
||||
FOR, IN, DO,
|
||||
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||
FROM, GW, NET, MASK, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
|
||||
ROA_CHECK, ASN, SRC, DST,
|
||||
@ -305,21 +336,23 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
%nonassoc ELSE
|
||||
|
||||
%type <xp> cmds_int cmd_prep
|
||||
%type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
|
||||
%type <x> term cmd cmd_var cmds cmds_scoped constant constructor print_list var var_init var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
|
||||
%type <fsa> static_attr
|
||||
%type <fab> attr_bit
|
||||
%type <f> filter where_filter
|
||||
%type <fl> filter_body function_body
|
||||
%type <flv> lvalue
|
||||
%type <i> type function_args function_vars
|
||||
%type <i> type function_vars
|
||||
%type <fa> function_argsn function_args
|
||||
%type <ecs> ec_kind
|
||||
%type <fret> break_command
|
||||
%type <fret> break_command
|
||||
%type <i32> cnum
|
||||
%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
|
||||
%type <trie> fprefix_set
|
||||
%type <v> set_atom switch_atom fipa
|
||||
%type <px> fprefix
|
||||
%type <t> get_cf_position
|
||||
%type <s> for_var
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
@ -343,7 +376,7 @@ filter_def:
|
||||
|
||||
conf: filter_eval ;
|
||||
filter_eval:
|
||||
EVAL term { f_eval_int(f_linearize($2)); }
|
||||
EVAL term { f_eval_int(f_linearize($2, 1)); }
|
||||
;
|
||||
|
||||
conf: custom_attr ;
|
||||
@ -425,25 +458,28 @@ type:
|
||||
;
|
||||
|
||||
function_argsn:
|
||||
/* EMPTY */
|
||||
/* EMPTY */ { $$ = NULL; }
|
||||
| function_argsn type symbol ';' {
|
||||
if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
|
||||
cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++);
|
||||
$$ = cfg_alloc(sizeof(struct f_arg));
|
||||
$$->arg = cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
|
||||
$$->next = $1;
|
||||
}
|
||||
;
|
||||
|
||||
function_args:
|
||||
'(' ')' { $$ = 0; }
|
||||
'(' ')' { $$ = NULL; }
|
||||
| '(' function_argsn type symbol ')' {
|
||||
cf_define_symbol($4, SYM_VARIABLE | $3, offset, $4->scope->slots++);
|
||||
$$ = $4->scope->slots;
|
||||
$$ = cfg_alloc(sizeof(struct f_arg));
|
||||
$$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
|
||||
$$->next = $2;
|
||||
}
|
||||
;
|
||||
|
||||
function_vars:
|
||||
/* EMPTY */ { $$ = 0; }
|
||||
| function_vars type symbol ';' {
|
||||
cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++);
|
||||
cf_define_symbol($3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
|
||||
$$ = $1 + 1;
|
||||
}
|
||||
;
|
||||
@ -471,20 +507,35 @@ where_filter:
|
||||
|
||||
function_body:
|
||||
function_vars '{' cmds '}' {
|
||||
$$ = f_linearize($3);
|
||||
$$ = f_linearize($3, 0);
|
||||
$$->vars = $1;
|
||||
}
|
||||
;
|
||||
|
||||
conf: function_def ;
|
||||
function_def:
|
||||
FUNCTION symbol { DBG( "Beginning of function %s\n", $2->name );
|
||||
FUNCTION symbol {
|
||||
DBG( "Beginning of function %s\n", $2->name );
|
||||
$2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
|
||||
cf_push_scope($2);
|
||||
} function_args function_body {
|
||||
DBG("Definition of function %s with %u args and %u local vars.\n", $2->name, $4, $5->vars);
|
||||
$5->args = $4;
|
||||
$2->function = $5;
|
||||
} function_args {
|
||||
/* Make dummy f_line for storing function prototype */
|
||||
struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
|
||||
$2->function = dummy;
|
||||
|
||||
/* Revert the args */
|
||||
while ($4) {
|
||||
struct f_arg *tmp = $4;
|
||||
$4 = $4->next;
|
||||
|
||||
tmp->next = dummy->arg_list;
|
||||
dummy->arg_list = tmp;
|
||||
dummy->args++;
|
||||
}
|
||||
} function_body {
|
||||
$6->args = $2->function->args;
|
||||
$6->arg_list = $2->function->arg_list;
|
||||
$2->function = $6;
|
||||
cf_pop_scope();
|
||||
}
|
||||
;
|
||||
@ -495,7 +546,11 @@ cmds: /* EMPTY */ { $$ = NULL; }
|
||||
| cmds_int { $$ = $1.begin; }
|
||||
;
|
||||
|
||||
cmd_prep: cmd {
|
||||
cmds_scoped: { cf_push_soft_scope(); } cmds { cf_pop_soft_scope(); $$ = $2; } ;
|
||||
|
||||
cmd_var: var | cmd ;
|
||||
|
||||
cmd_prep: cmd_var {
|
||||
$$.begin = $$.end = $1;
|
||||
if ($1)
|
||||
while ($$.end->next)
|
||||
@ -517,15 +572,6 @@ cmds_int: cmd_prep
|
||||
}
|
||||
;
|
||||
|
||||
block:
|
||||
cmd {
|
||||
$$=$1;
|
||||
}
|
||||
| '{' cmds '}' {
|
||||
$$=$2;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
* Complex types, their bison value is struct f_val
|
||||
*/
|
||||
@ -549,7 +595,7 @@ set_atom:
|
||||
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
|
||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||
| '(' term ')' {
|
||||
if (f_eval(f_linearize($2), &($$)) > F_RETURN) cf_error("Runtime error");
|
||||
if (f_eval(f_linearize($2, 1), &($$)) > F_RETURN) cf_error("Runtime error");
|
||||
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
|
||||
}
|
||||
| symbol_known {
|
||||
@ -561,13 +607,13 @@ set_atom:
|
||||
|
||||
switch_atom:
|
||||
NUM { $$.type = T_INT; $$.val.i = $1; }
|
||||
| '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2)); }
|
||||
| '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2, 1)); }
|
||||
| fipa { $$ = $1; }
|
||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||
;
|
||||
|
||||
cnum:
|
||||
term { $$ = f_eval_int(f_linearize($1)); }
|
||||
term { $$ = f_eval_int(f_linearize($1, 1)); }
|
||||
|
||||
pair_item:
|
||||
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
|
||||
@ -651,19 +697,19 @@ fprefix_set:
|
||||
;
|
||||
|
||||
switch_body: /* EMPTY */ { $$ = NULL; }
|
||||
| switch_body switch_items ':' cmds {
|
||||
| switch_body switch_items ':' cmds_scoped {
|
||||
/* Fill data fields */
|
||||
struct f_tree *t;
|
||||
struct f_line *line = f_linearize($4);
|
||||
struct f_line *line = f_linearize($4, 0);
|
||||
for (t = $2; t; t = t->left)
|
||||
t->data = line;
|
||||
$$ = f_merge_items($1, $2);
|
||||
}
|
||||
| switch_body ELSECOL cmds {
|
||||
| switch_body ELSECOL cmds_scoped {
|
||||
struct f_tree *t = f_new_tree();
|
||||
t->from.type = t->to.type = T_VOID;
|
||||
t->right = t;
|
||||
t->data = f_linearize($3);
|
||||
t->data = f_linearize($3, 0);
|
||||
$$ = f_merge_items($1, t);
|
||||
}
|
||||
;
|
||||
@ -680,6 +726,7 @@ bgp_path:
|
||||
bgp_path_tail:
|
||||
NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; }
|
||||
| NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; }
|
||||
| '[' ']' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = NULL, .kind = PM_ASN_SET }, }); $$->next = $3; }
|
||||
| '[' set_items ']' bgp_path_tail {
|
||||
if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
|
||||
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = build_tree($2), .kind = PM_ASN_SET }, }); $$->next = $4;
|
||||
@ -699,6 +746,7 @@ constant:
|
||||
| fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
|
||||
| VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
|
||||
| net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
|
||||
| '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
|
||||
| '[' set_items ']' {
|
||||
DBG( "We've got a set here..." );
|
||||
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
|
||||
@ -722,27 +770,22 @@ var_list: /* EMPTY */ { $$ = NULL; }
|
||||
| var_list ',' term { $$ = $3; $$->next = $1; }
|
||||
|
||||
function_call:
|
||||
symbol_known '(' var_list ')' {
|
||||
symbol_known '(' var_list ')'
|
||||
{
|
||||
if ($1->class != SYM_FUNCTION)
|
||||
cf_error("You can't call something which is not a function. Really.");
|
||||
|
||||
struct f_inst *fc = f_new_inst(FI_CALL, $1);
|
||||
uint args = 0;
|
||||
/* Revert the var_list */
|
||||
struct f_inst *args = NULL;
|
||||
while ($3) {
|
||||
args++;
|
||||
struct f_inst *tmp = $3->next;
|
||||
$3->next = fc;
|
||||
struct f_inst *tmp = $3;
|
||||
$3 = $3->next;
|
||||
|
||||
fc = $3;
|
||||
$3 = tmp;
|
||||
tmp->next = args;
|
||||
args = tmp;
|
||||
}
|
||||
|
||||
if (args != $1->function->args)
|
||||
cf_error("Function call '%s' got %u arguments, need %u arguments.",
|
||||
$1->name, args, $1->function->args);
|
||||
|
||||
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
|
||||
$$->next = fc;
|
||||
$$ = f_new_inst(FI_CALL, args, $1);
|
||||
}
|
||||
;
|
||||
|
||||
@ -866,13 +909,44 @@ print_list: /* EMPTY */ { $$ = NULL; }
|
||||
}
|
||||
;
|
||||
|
||||
var_init:
|
||||
/* empty */ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { }); }
|
||||
| '=' term { $$ = $2; }
|
||||
;
|
||||
|
||||
var:
|
||||
type symbol var_init ';' {
|
||||
struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
|
||||
$$ = f_new_inst(FI_VAR_INIT, $3, sym);
|
||||
}
|
||||
|
||||
for_var:
|
||||
type symbol { $$ = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
|
||||
| CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
|
||||
;
|
||||
|
||||
cmd:
|
||||
IF term THEN block {
|
||||
'{' cmds_scoped '}' {
|
||||
$$ = $2;
|
||||
}
|
||||
| IF term THEN cmd {
|
||||
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
|
||||
}
|
||||
| IF term THEN block ELSE block {
|
||||
| IF term THEN cmd ELSE cmd {
|
||||
$$ = f_new_inst(FI_CONDITION, $2, $4, $6);
|
||||
}
|
||||
| FOR {
|
||||
/* Reserve space for walk data on stack */
|
||||
cf_push_scope(NULL);
|
||||
conf_this_scope->slots += 2;
|
||||
} for_var IN
|
||||
/* Parse term in the parent scope */
|
||||
{ conf_this_scope->active = 0; } term { conf_this_scope->active = 1; }
|
||||
DO cmd {
|
||||
cf_pop_scope();
|
||||
$$ = f_new_inst(FI_FOR_INIT, $6, $3);
|
||||
$$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
|
||||
}
|
||||
| symbol_known '=' term ';' {
|
||||
switch ($1->class) {
|
||||
case SYM_VARIABLE_RANGE:
|
||||
@ -929,7 +1003,7 @@ cmd:
|
||||
| PRINTN print_list ';' {
|
||||
$$ = f_new_inst(FI_PRINT, $2);
|
||||
}
|
||||
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
|
||||
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
|
||||
| CASE term '{' switch_body '}' {
|
||||
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ btype
|
||||
f_type_element_type(btype t)
|
||||
{
|
||||
switch(t) {
|
||||
case T_PATH: return T_INT;
|
||||
case T_CLIST: return T_PAIR;
|
||||
case T_ECLIST: return T_EC;
|
||||
case T_LCLIST: return T_LC;
|
||||
@ -78,6 +79,8 @@ f_type_element_type(btype t)
|
||||
};
|
||||
}
|
||||
|
||||
const struct f_trie f_const_empty_trie = { .ipv4 = -1, };
|
||||
|
||||
const struct f_val f_const_empty_path = {
|
||||
.type = T_PATH,
|
||||
.val.ad = &null_adata,
|
||||
@ -90,6 +93,9 @@ const struct f_val f_const_empty_path = {
|
||||
}, f_const_empty_lclist = {
|
||||
.type = T_LCLIST,
|
||||
.val.ad = &null_adata,
|
||||
}, f_const_empty_prefix_set = {
|
||||
.type = T_PREFIX_SET,
|
||||
.val.ti = &f_const_empty_trie,
|
||||
};
|
||||
|
||||
static void
|
||||
@ -178,7 +184,7 @@ val_compare(const struct f_val *v1, const struct f_val *v2)
|
||||
if (val_is_ip4(v1) && (v2->type == T_QUAD))
|
||||
return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i);
|
||||
|
||||
debug( "Types do not match in val_compare\n" );
|
||||
DBG( "Types do not match in val_compare\n" );
|
||||
return F_CMP_ERROR;
|
||||
}
|
||||
|
||||
@ -292,6 +298,12 @@ val_same(const struct f_val *v1, const struct f_val *v2)
|
||||
int
|
||||
clist_set_type(const struct f_tree *set, struct f_val *v)
|
||||
{
|
||||
if (!set)
|
||||
{
|
||||
v->type = T_VOID;
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (set->from.type)
|
||||
{
|
||||
case T_PAIR:
|
||||
@ -528,6 +540,9 @@ val_in_range(const struct f_val *v1, const struct f_val *v2)
|
||||
if (v2->type != T_SET)
|
||||
return F_CMP_ERROR;
|
||||
|
||||
if (!v2->val.t)
|
||||
return 0;
|
||||
|
||||
/* With integrated Quad<->IP implicit conversion */
|
||||
if ((v1->type == v2->val.t->from.type) ||
|
||||
((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to))))
|
||||
|
@ -209,9 +209,11 @@ int val_in_range(const struct f_val *v1, const struct f_val *v2);
|
||||
|
||||
int clist_set_type(const struct f_tree *set, struct f_val *v);
|
||||
static inline int eclist_set_type(const struct f_tree *set)
|
||||
{ return set->from.type == T_EC; }
|
||||
{ return !set || set->from.type == T_EC; }
|
||||
static inline int lclist_set_type(const struct f_tree *set)
|
||||
{ return set->from.type == T_LC; }
|
||||
{ return !set || set->from.type == T_LC; }
|
||||
static inline int path_set_type(const struct f_tree *set)
|
||||
{ return !set || set->from.type == T_INT; }
|
||||
|
||||
const struct adata *clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
|
||||
const struct adata *eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
|
||||
@ -227,7 +229,7 @@ undef_value(struct f_val v)
|
||||
(v.val.ad == &null_adata);
|
||||
}
|
||||
|
||||
extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist;
|
||||
extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist, f_const_empty_prefix_set;
|
||||
static inline const struct f_val *f_get_empty(btype t)
|
||||
{
|
||||
switch (t) {
|
||||
|
@ -191,6 +191,12 @@ if (f$1->type && f$2->type && (f$1->type != f$2->type) &&
|
||||
cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code));
|
||||
FID_INTERPRET_BODY()')
|
||||
|
||||
m4_define(ARG_PREFER_SAME_TYPE, `
|
||||
FID_NEW_BODY()m4_dnl
|
||||
if (f$1->type && f$2->type && (f$1->type != f$2->type))
|
||||
(void) (f_const_promotion(f$2, f$1->type) || f_const_promotion(f$1, f$2->type));
|
||||
FID_INTERPRET_BODY()')
|
||||
|
||||
# Executing another filter line. This replaces the recursion
|
||||
# that was needed in the former implementation.
|
||||
m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
|
||||
@ -216,7 +222,7 @@ whati->f$1 = f$1;
|
||||
FID_DUMP_BODY()m4_dnl
|
||||
f_dump_line(item->fl$1, indent + 1);
|
||||
FID_LINEARIZE_BODY()m4_dnl
|
||||
item->fl$1 = f_linearize(whati->f$1);
|
||||
item->fl$1 = f_linearize(whati->f$1, $2);
|
||||
FID_SAME_BODY()m4_dnl
|
||||
if (!f_same(f1->fl$1, f2->fl$1)) return 0;
|
||||
FID_ITERATE_BODY()m4_dnl
|
||||
@ -244,9 +250,13 @@ m4_define(ERROR,
|
||||
# This macro specifies result type and makes there are no conflicting definitions
|
||||
m4_define(RESULT_TYPE,
|
||||
`m4_ifdef([[INST_RESULT_TYPE]],
|
||||
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitons]])]])]],
|
||||
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitions in]] INST_NAME)]])]],
|
||||
[[m4_define(INST_RESULT_TYPE,$1) RESULT_TYPE_($1)]])')
|
||||
|
||||
m4_define(RESULT_TYPE_CHECK,
|
||||
`m4_ifelse(INST_OUTVAL,0,,
|
||||
[[m4_ifdef([[INST_RESULT_TYPE]],,[[ERROR([[Missing type definition in]] INST_NAME)]])]])')
|
||||
|
||||
m4_define(RESULT_TYPE_, `
|
||||
FID_NEW_BODY()m4_dnl
|
||||
what->type = $1;
|
||||
@ -300,6 +310,7 @@ m4_define(FID_ITERATE, `FID_ZONE(10, Iteration)')
|
||||
|
||||
# This macro does all the code wrapping. See inline comments.
|
||||
m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
|
||||
RESULT_TYPE_CHECK()m4_dnl Check for defined RESULT_TYPE()
|
||||
FID_ENUM()m4_dnl Contents of enum fi_code { ... }
|
||||
INST_NAME(),
|
||||
FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code
|
||||
@ -375,6 +386,7 @@ case INST_NAME(): {
|
||||
#undef whati
|
||||
#undef item
|
||||
dest->items[pos].fi_code = what->fi_code;
|
||||
dest->items[pos].flags = what->flags;
|
||||
dest->items[pos].lineno = what->lineno;
|
||||
break;
|
||||
}
|
||||
@ -402,6 +414,7 @@ m4_define(INST, `m4_dnl This macro is called on beginning of each instruction
|
||||
INST_FLUSH()m4_dnl First, old data is flushed
|
||||
m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name,
|
||||
m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count,
|
||||
m4_define([[INST_OUTVAL]], [[$3]])m4_dnl instruction output value count,
|
||||
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
|
||||
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
|
||||
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
|
||||
@ -505,6 +518,11 @@ f_const_promotion(struct f_inst *arg, btype want)
|
||||
return 1;
|
||||
}
|
||||
|
||||
else if ((c->type == T_SET) && (!c->val.t) && (want == T_PREFIX_SET)) {
|
||||
*c = f_const_empty_prefix_set;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -560,7 +578,7 @@ FID_WR_PUT(8)
|
||||
}
|
||||
|
||||
struct f_line *
|
||||
f_linearize_concat(const struct f_inst * const inst[], uint count)
|
||||
f_linearize_concat(const struct f_inst * const inst[], uint count, uint results)
|
||||
{
|
||||
uint len = 0;
|
||||
for (uint i=0; i<count; i++)
|
||||
@ -572,6 +590,8 @@ f_linearize_concat(const struct f_inst * const inst[], uint count)
|
||||
for (uint i=0; i<count; i++)
|
||||
out->len = linearize(out, inst[i], out->len);
|
||||
|
||||
out->results = results;
|
||||
|
||||
#ifdef LOCAL_DEBUG
|
||||
f_dump_line(out, 0);
|
||||
#endif
|
||||
@ -640,6 +660,7 @@ FID_WR_PUT(4)m4_dnl
|
||||
struct f_inst {
|
||||
struct f_inst *next; /* Next instruction */
|
||||
enum f_instruction_code fi_code; /* Instruction code */
|
||||
enum f_instruction_flags flags; /* Flags, instruction-specific */
|
||||
btype type; /* Type of returned value, if known */
|
||||
int size; /* How many instructions are underneath */
|
||||
int lineno; /* Line number */
|
||||
|
244
filter/f-inst.c
244
filter/f-inst.c
@ -62,8 +62,9 @@
|
||||
* m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args
|
||||
* m4_dnl ARG(num, type); argument, its id (in data fields) and type accessible by v1, v2, v3
|
||||
* m4_dnl ARG_ANY(num); argument with no type check accessible by v1, v2, v3
|
||||
* m4_dnl ARG_TYPE(num, type); just declare the type of argument
|
||||
* m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount
|
||||
* m4_dnl LINE(num, unused); this argument has to be converted to its own f_line
|
||||
* m4_dnl LINE(num, out); this argument has to be converted to its own f_line
|
||||
* m4_dnl SYMBOL; symbol handed from config
|
||||
* m4_dnl STATIC_ATTR; static attribute definition
|
||||
* m4_dnl DYNAMIC_ATTR; dynamic attribute definition
|
||||
@ -80,10 +81,17 @@
|
||||
* m4_dnl )
|
||||
*
|
||||
* m4_dnl RESULT(type, union-field, value); putting this on value stack
|
||||
* m4_dnl RESULT_(type, union-field, value); like RESULT(), but do not declare the type
|
||||
* m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly
|
||||
* m4_dnl RESULT_TYPE(type); just declare the type of result value
|
||||
* m4_dnl RESULT_VOID; return undef
|
||||
* m4_dnl }
|
||||
*
|
||||
* Note that runtime arguments m4_dnl (ARG*, VARARG) must be defined before
|
||||
* parse-time arguments m4_dnl (LINE, SYMBOL, ...). During linearization,
|
||||
* first ones move position in f_line by linearizing arguments first, while
|
||||
* second ones store data to the current position.
|
||||
*
|
||||
* Also note that the { ... } blocks are not respected by M4 at all.
|
||||
* If you get weird unmatched-brace-pair errors, check what it generated and why.
|
||||
* What is really considered as one instruction is not the { ... } block
|
||||
@ -91,6 +99,24 @@
|
||||
*
|
||||
* Other code is just copied into the interpreter part.
|
||||
*
|
||||
* The filter language uses a simple type system, where values have types
|
||||
* (constants T_*) and also terms (instructions) are statically typed. Our
|
||||
* static typing is partial (some terms do not declare types of arguments
|
||||
* or results), therefore it can detect most but not all type errors and
|
||||
* therefore we still have runtime type checks.
|
||||
*
|
||||
* m4_dnl Types of arguments are declared by macros ARG() and ARG_TYPE(),
|
||||
* m4_dnl types of results are declared by RESULT() and RESULT_TYPE().
|
||||
* m4_dnl Macros ARG_ANY(), RESULT_() and RESULT_VAL() do not declare types
|
||||
* m4_dnl themselves, but can be combined with ARG_TYPE() / RESULT_TYPE().
|
||||
*
|
||||
* m4_dnl Note that types should be declared only once. If there are
|
||||
* m4_dnl multiple RESULT() macros in an instruction definition, they must
|
||||
* m4_dnl use the exact same expression for type, or they should be replaced
|
||||
* m4_dnl by multiple RESULT_() macros and a common RESULT_TYPE() macro.
|
||||
* m4_dnl See e.g. FI_EA_GET or FI_MIN instructions.
|
||||
*
|
||||
*
|
||||
* If you are satisfied with this, you don't need to read the following
|
||||
* detailed description of what is really done with the instruction definitions.
|
||||
*
|
||||
@ -215,6 +241,37 @@
|
||||
*
|
||||
* m4_dnl If you are stymied, see FI_CALL or FI_CONSTANT or just search for
|
||||
* m4_dnl the mentioned macros in this file to see what is happening there in wild.
|
||||
*
|
||||
*
|
||||
* A note about soundness of the type system:
|
||||
*
|
||||
* A type system is sound when types of expressions are consistent with
|
||||
* types of values resulting from evaluation of such expressions. Untyped
|
||||
* expressions are ok, but badly typed expressions are not sound. So is
|
||||
* the type system of BIRD filtering code sound? There are some points:
|
||||
*
|
||||
* All cases of (one) m4_dnl RESULT() macro are obviously ok, as the macro
|
||||
* both declares a type and returns a value. One have to check instructions
|
||||
* that use m4_dnl RESULT_TYPE() macro. There are two issues:
|
||||
*
|
||||
* FI_AND, FI_OR - second argument is statically checked to be T_BOOL and
|
||||
* passed as result without dynamic typecheck, declared to be T_BOOL. If
|
||||
* an untyped non-bool expression is used as a second argument, then
|
||||
* the mismatched type is returned.
|
||||
*
|
||||
* FI_VAR_GET - soundness depends on consistency of declared symbol types
|
||||
* and stored values. This is maintained when values are stored by
|
||||
* FI_VAR_SET, but when they are stored by FI_CALL, only static checking is
|
||||
* used, so when an untyped expression returning mismatched value is used
|
||||
* as a function argument, then inconsistent value is stored and subsequent
|
||||
* FI_VAR_GET would be unsound.
|
||||
*
|
||||
* Both of these issues are inconsequential, as mismatched values from
|
||||
* unsound expressions will be caught by dynamic typechecks like mismatched
|
||||
* values from untyped expressions.
|
||||
*
|
||||
* Also note that FI_CALL is the only expression without properly declared
|
||||
* result type.
|
||||
*/
|
||||
|
||||
/* Binary operators */
|
||||
@ -255,7 +312,7 @@
|
||||
RESULT_TYPE(T_BOOL);
|
||||
|
||||
if (v1.val.i)
|
||||
LINE(2,0);
|
||||
LINE(2,1);
|
||||
else
|
||||
RESULT_VAL(v1);
|
||||
}
|
||||
@ -265,7 +322,7 @@
|
||||
RESULT_TYPE(T_BOOL);
|
||||
|
||||
if (!v1.val.i)
|
||||
LINE(2,0);
|
||||
LINE(2,1);
|
||||
else
|
||||
RESULT_VAL(v1);
|
||||
}
|
||||
@ -358,7 +415,7 @@
|
||||
break;
|
||||
|
||||
case T_SET:
|
||||
if (vv(i).val.t->from.type != T_INT)
|
||||
if (!path_set_type(vv(i).val.t))
|
||||
runtime("Only integer sets allowed in path mask");
|
||||
|
||||
pm->item[i] = (struct f_path_mask_item) {
|
||||
@ -380,12 +437,14 @@
|
||||
INST(FI_NEQ, 2, 1) {
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
ARG_PREFER_SAME_TYPE(1, 2);
|
||||
RESULT(T_BOOL, i, !val_same(&v1, &v2));
|
||||
}
|
||||
|
||||
INST(FI_EQ, 2, 1) {
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
ARG_PREFER_SAME_TYPE(1, 2);
|
||||
RESULT(T_BOOL, i, val_same(&v1, &v2));
|
||||
}
|
||||
|
||||
@ -456,6 +515,18 @@
|
||||
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
|
||||
}
|
||||
|
||||
INST(FI_VAR_INIT, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG_ANY(1);
|
||||
SYMBOL;
|
||||
ARG_TYPE(1, sym->class & 0xff);
|
||||
|
||||
/* New variable is always the last on stack */
|
||||
uint pos = curline.vbase + sym->offset;
|
||||
fstk->vstk[pos] = v1;
|
||||
fstk->vcnt = pos + 1;
|
||||
}
|
||||
|
||||
/* Set to indirect value prepared in v1 */
|
||||
INST(FI_VAR_SET, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
@ -486,12 +557,100 @@
|
||||
RESULT_VAL(val);
|
||||
}
|
||||
|
||||
INST(FI_FOR_INIT, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG_ANY(1);
|
||||
SYMBOL;
|
||||
|
||||
FID_NEW_BODY()
|
||||
ASSERT((sym->class & ~0xff) == SYM_VARIABLE);
|
||||
|
||||
/* Static type check */
|
||||
if (f1->type)
|
||||
{
|
||||
enum btype t_var = (sym->class & 0xff);
|
||||
enum btype t_arg = f_type_element_type(f1->type);
|
||||
if (!t_arg)
|
||||
cf_error("Value of expression in FOR must be iterable, got %s",
|
||||
f_type_name(f1->type));
|
||||
if (t_var != t_arg)
|
||||
cf_error("Loop variable '%s' in FOR must be %s, is %s",
|
||||
sym->name, f_type_name(t_arg), f_type_name(t_var));
|
||||
}
|
||||
|
||||
FID_INTERPRET_BODY()
|
||||
|
||||
/* Dynamic type check */
|
||||
if ((sym->class & 0xff) != f_type_element_type(v1.type))
|
||||
runtime("Mismatched argument and variable type");
|
||||
|
||||
/* Setup the index */
|
||||
v2 = (struct f_val) { .type = T_INT, .val.i = 0 };
|
||||
|
||||
/* Keep v1 and v2 on the stack */
|
||||
fstk->vcnt += 2;
|
||||
}
|
||||
|
||||
INST(FI_FOR_NEXT, 2, 0) {
|
||||
NEVER_CONSTANT;
|
||||
SYMBOL;
|
||||
|
||||
/* Type checks are done in FI_FOR_INIT */
|
||||
|
||||
/* Loop variable */
|
||||
struct f_val *var = &fstk->vstk[curline.vbase + sym->offset];
|
||||
int step = 0;
|
||||
|
||||
switch(v1.type)
|
||||
{
|
||||
case T_PATH:
|
||||
var->type = T_INT;
|
||||
step = as_path_walk(v1.val.ad, &v2.val.i, &var->val.i);
|
||||
break;
|
||||
|
||||
case T_CLIST:
|
||||
var->type = T_PAIR;
|
||||
step = int_set_walk(v1.val.ad, &v2.val.i, &var->val.i);
|
||||
break;
|
||||
|
||||
case T_ECLIST:
|
||||
var->type = T_EC;
|
||||
step = ec_set_walk(v1.val.ad, &v2.val.i, &var->val.ec);
|
||||
break;
|
||||
|
||||
case T_LCLIST:
|
||||
var->type = T_LC;
|
||||
step = lc_set_walk(v1.val.ad, &v2.val.i, &var->val.lc);
|
||||
break;
|
||||
|
||||
default:
|
||||
runtime( "Clist or lclist expected" );
|
||||
}
|
||||
|
||||
if (step)
|
||||
{
|
||||
/* Keep v1 and v2 on the stack */
|
||||
fstk->vcnt += 2;
|
||||
|
||||
/* Repeat this instruction */
|
||||
curline.pos--;
|
||||
|
||||
/* Execute the loop body */
|
||||
LINE(1, 0);
|
||||
|
||||
/* Space for loop variable, may be unused */
|
||||
fstk->vcnt += 1;
|
||||
}
|
||||
else
|
||||
var->type = T_VOID;
|
||||
}
|
||||
|
||||
INST(FI_CONDITION, 1, 0) {
|
||||
ARG(1, T_BOOL);
|
||||
if (v1.val.i)
|
||||
LINE(2,0);
|
||||
else
|
||||
LINE(3,1);
|
||||
LINE(3,0);
|
||||
}
|
||||
|
||||
INST(FI_PRINT, 0, 0) {
|
||||
@ -771,6 +930,7 @@
|
||||
INST(FI_DEFAULT, 2, 1) {
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
RESULT_TYPE(f_type_element_type(v2.type));
|
||||
|
||||
log(L_INFO "Type of arg 1 is: %d", v1.type);
|
||||
|
||||
@ -945,7 +1105,7 @@
|
||||
RESULT(T_INT, i, v1.val.lc.ldp2);
|
||||
}
|
||||
|
||||
INST(FI_MIN, 1, 1) { /* Get minimum element from set */
|
||||
INST(FI_MIN, 1, 1) { /* Get minimum element from list */
|
||||
ARG_ANY(1);
|
||||
RESULT_TYPE(f_type_element_type(v1.type));
|
||||
switch(v1.type)
|
||||
@ -979,7 +1139,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
INST(FI_MAX, 1, 1) { /* Get maximum element from set */
|
||||
INST(FI_MAX, 1, 1) { /* Get maximum element from list */
|
||||
ARG_ANY(1);
|
||||
RESULT_TYPE(f_type_element_type(v1.type));
|
||||
switch(v1.type)
|
||||
@ -1013,7 +1173,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
INST(FI_RETURN, 1, 1) {
|
||||
INST(FI_RETURN, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
/* Acquire the return value */
|
||||
ARG_ANY(1);
|
||||
@ -1041,28 +1201,59 @@
|
||||
|
||||
INST(FI_CALL, 0, 1) {
|
||||
NEVER_CONSTANT;
|
||||
VARARG;
|
||||
SYMBOL;
|
||||
|
||||
/* Fake result type declaration */
|
||||
RESULT_TYPE(T_VOID);
|
||||
|
||||
FID_NEW_BODY()
|
||||
ASSERT(sym->class == SYM_FUNCTION);
|
||||
|
||||
if (whati->varcount != sym->function->args)
|
||||
cf_error("Function '%s' expects %u arguments, got %u arguments",
|
||||
sym->name, sym->function->args, whati->varcount);
|
||||
|
||||
/* Typecheck individual arguments */
|
||||
struct f_inst *a = fvar;
|
||||
struct f_arg *b = sym->function->arg_list;
|
||||
for (uint i = 1; a && b; a = a->next, b = b->next, i++)
|
||||
{
|
||||
enum btype b_type = b->arg->class & 0xff;
|
||||
|
||||
if (a->type && (a->type != b_type) && !f_const_promotion(a, b_type))
|
||||
cf_error("Argument %u of '%s' must be %s, got %s",
|
||||
i, sym->name, f_type_name(b_type), f_type_name(a->type));
|
||||
}
|
||||
ASSERT(!a && !b);
|
||||
|
||||
/* Add implicit void slot for the return value */
|
||||
struct f_inst *tmp = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
|
||||
tmp->next = whati->fvar;
|
||||
whati->fvar = tmp;
|
||||
what->size += tmp->size;
|
||||
|
||||
/* Mark recursive calls, they have dummy f_line */
|
||||
if (!sym->function->len)
|
||||
what->flags |= FIF_RECURSIVE;
|
||||
|
||||
FID_SAME_BODY()
|
||||
if (!(f1->sym->flags & SYM_FLAG_SAME))
|
||||
return 0;
|
||||
if (!(f1->sym->flags & SYM_FLAG_SAME) && !(f1_->flags & FIF_RECURSIVE))
|
||||
return 0;
|
||||
|
||||
FID_ITERATE_BODY()
|
||||
if (!(what->flags & FIF_RECURSIVE))
|
||||
BUFFER_PUSH(fit->lines) = whati->sym->function;
|
||||
|
||||
FID_INTERPRET_BODY()
|
||||
|
||||
/* Push the body on stack */
|
||||
LINEX(sym->function);
|
||||
curline.vbase = curline.ventry;
|
||||
curline.emask |= FE_RETURN;
|
||||
|
||||
/* Before this instruction was called, there was the T_VOID
|
||||
* automatic return value pushed on value stack and also
|
||||
* sym->function->args function arguments. Setting the
|
||||
* vbase to point to first argument. */
|
||||
ASSERT(curline.ventry >= sym->function->args);
|
||||
curline.ventry -= sym->function->args;
|
||||
curline.vbase = curline.ventry;
|
||||
/* Arguments on stack */
|
||||
fstk->vcnt += sym->function->args;
|
||||
|
||||
/* Storage for local variables */
|
||||
f_vcnt_check_overflow(sym->function->vars);
|
||||
@ -1176,17 +1367,10 @@
|
||||
|
||||
if (v1.type == T_PATH)
|
||||
{
|
||||
const struct f_tree *set = NULL;
|
||||
u32 key = 0;
|
||||
|
||||
if (v2.type == T_INT)
|
||||
key = v2.val.i;
|
||||
else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
|
||||
set = v2.val.t;
|
||||
if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT))
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
else
|
||||
runtime("Can't delete non-integer (set)");
|
||||
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_CLIST)
|
||||
@ -1238,10 +1422,8 @@
|
||||
|
||||
if (v1.type == T_PATH)
|
||||
{
|
||||
u32 key = 0;
|
||||
|
||||
if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]);
|
||||
if ((v2.type == T_SET) && path_set_type(v2.val.t))
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
else
|
||||
runtime("Can't filter integer");
|
||||
}
|
||||
@ -1301,7 +1483,7 @@
|
||||
|
||||
}
|
||||
|
||||
INST(FI_FORMAT, 1, 0) { /* Format */
|
||||
INST(FI_FORMAT, 1, 1) { /* Format */
|
||||
ARG_ANY(1);
|
||||
RESULT(T_STRING, s, val_format_str(fpool, &v1));
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
/* Flags for instructions */
|
||||
enum f_instruction_flags {
|
||||
FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */
|
||||
FIF_RECURSIVE = 1, /* FI_CALL: function is directly recursive */
|
||||
} PACKED;
|
||||
|
||||
/* Include generated filter instruction declarations */
|
||||
@ -35,19 +35,26 @@ const char *f_instruction_name_(enum f_instruction_code fi);
|
||||
static inline const char *f_instruction_name(enum f_instruction_code fi)
|
||||
{ return f_instruction_name_(fi) + 3; }
|
||||
|
||||
struct f_arg {
|
||||
struct symbol *arg;
|
||||
struct f_arg *next;
|
||||
};
|
||||
|
||||
/* Filter structures for execution */
|
||||
/* Line of instructions to be unconditionally executed one after another */
|
||||
struct f_line {
|
||||
uint len; /* Line length */
|
||||
u8 args; /* Function: Args required */
|
||||
u8 vars;
|
||||
u8 results; /* Results left on stack: cmd -> 0, term -> 1 */
|
||||
struct f_arg *arg_list;
|
||||
struct f_line_item items[0]; /* The items themselves */
|
||||
};
|
||||
|
||||
/* Convert the f_inst infix tree to the f_line structures */
|
||||
struct f_line *f_linearize_concat(const struct f_inst * const inst[], uint count);
|
||||
static inline struct f_line *f_linearize(const struct f_inst *root)
|
||||
{ return f_linearize_concat(&root, 1); }
|
||||
struct f_line *f_linearize_concat(const struct f_inst * const inst[], uint count, uint results);
|
||||
static inline struct f_line *f_linearize(const struct f_inst *root, uint results)
|
||||
{ return f_linearize_concat(&root, 1, results); }
|
||||
|
||||
void f_dump_line(const struct f_line *, uint indent);
|
||||
|
||||
|
@ -37,6 +37,6 @@ struct filter *f_new_where(struct f_inst *where)
|
||||
f_new_inst(FI_DIE, F_REJECT));
|
||||
|
||||
struct filter *f = cfg_allocz(sizeof(struct filter));
|
||||
f->root = f_linearize(cond);
|
||||
f->root = f_linearize(cond, 0);
|
||||
return f;
|
||||
}
|
||||
|
@ -179,8 +179,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
|
||||
}
|
||||
|
||||
/* End of current line. Drop local variables before exiting. */
|
||||
fstk->vcnt -= curline.line->vars;
|
||||
fstk->vcnt -= curline.line->args;
|
||||
fstk->vcnt = curline.ventry + curline.line->results;
|
||||
fstk->ecnt--;
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,7 @@ int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
bt_init(argc, argv);
|
||||
|
||||
bt_bird_init();
|
||||
|
||||
bt_assert_hook = bt_assert_filter;
|
||||
|
210
filter/test.conf
210
filter/test.conf
@ -146,9 +146,8 @@ bt_test_same(onef, twof, 0);
|
||||
*/
|
||||
|
||||
function t_bool()
|
||||
bool b;
|
||||
{
|
||||
b = true;
|
||||
bool b = true;
|
||||
bt_assert(b);
|
||||
bt_assert(!!b);
|
||||
|
||||
@ -184,12 +183,11 @@ define xyzzy = (120+10);
|
||||
define '1a-a1' = (xyzzy-100);
|
||||
|
||||
function t_int()
|
||||
int i;
|
||||
{
|
||||
bt_assert(xyzzy = 130);
|
||||
bt_assert('1a-a1' = 30);
|
||||
|
||||
i = four;
|
||||
int i = four;
|
||||
i = 12*100 + 60/2 + i;
|
||||
i = (i + 0);
|
||||
bt_assert(i = 1234);
|
||||
@ -238,14 +236,19 @@ define is2 = [(17+2), 17, 15, 11, 8, 5, 3, 2];
|
||||
define is3 = [5, 17, 2, 11, 8, 15, 3, 19];
|
||||
|
||||
function t_int_set()
|
||||
int set is;
|
||||
{
|
||||
int set is = [];
|
||||
bt_assert(is = []);
|
||||
bt_assert(0 !~ is);
|
||||
|
||||
bt_assert(1 ~ [1,2,3]);
|
||||
bt_assert(5 ~ [1..20]);
|
||||
bt_assert(2 ~ [ 1, 2, 3 ]);
|
||||
bt_assert(5 ~ [ 4 .. 7 ]);
|
||||
bt_assert(1 !~ [ 2, 3, 4 ]);
|
||||
bt_assert(999 !~ [ 666, 333 ]);
|
||||
bt_assert(1 !~ []);
|
||||
bt_assert(1 !~ is);
|
||||
|
||||
is = [ 2, 3, 4, 7..11 ];
|
||||
bt_assert(10 ~ is);
|
||||
@ -280,6 +283,7 @@ int set is;
|
||||
bt_assert([1,4..10,20] = [1,4..10,20]);
|
||||
|
||||
bt_assert(format([ 1, 2, 1, 1, 1, 3, 4, 1, 1, 1, 5 ]) = "[1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5]");
|
||||
bt_assert(format([]) = "[]");
|
||||
}
|
||||
|
||||
bt_test_suite(t_int_set, "Testing sets of integers");
|
||||
@ -293,9 +297,8 @@ bt_test_suite(t_int_set, "Testing sets of integers");
|
||||
*/
|
||||
|
||||
function t_string()
|
||||
string st;
|
||||
{
|
||||
st = "Hello";
|
||||
string st = "Hello";
|
||||
bt_assert(format(st) = "Hello");
|
||||
bt_assert(st ~ "Hell*");
|
||||
bt_assert(st ~ "?ello");
|
||||
@ -320,9 +323,8 @@ function 'mkpair-a'(int a)
|
||||
}
|
||||
|
||||
function t_pair()
|
||||
pair pp;
|
||||
{
|
||||
pp = (1, 2);
|
||||
pair pp = (1, 2);
|
||||
bt_assert(format(pp) = "(1,2)");
|
||||
bt_assert((1,2) = pp);
|
||||
bt_assert((1,1+1) = pp);
|
||||
@ -343,10 +345,11 @@ bt_test_suite(t_pair, "Testing pairs");
|
||||
*/
|
||||
|
||||
function t_pair_set()
|
||||
pair pp;
|
||||
pair set ps;
|
||||
{
|
||||
pp = (1, 2);
|
||||
pair pp = (1, 2);
|
||||
pair set ps = [];
|
||||
bt_assert(pp !~ ps);
|
||||
|
||||
ps = [(1,(one+one)), (3,4)..(4,8), (5,*), (6,3..6)];
|
||||
bt_assert(format(ps) = "[(1,2), (3,4)..(4,8), (5,0)..(5,65535), (6,3)..(6,6)]");
|
||||
bt_assert(pp ~ ps);
|
||||
@ -363,6 +366,7 @@ pair set ps;
|
||||
bt_assert((6,6+one) !~ ps);
|
||||
bt_assert(((one+6),2) !~ ps);
|
||||
bt_assert((1,1) !~ ps);
|
||||
bt_assert(pp !~ []);
|
||||
|
||||
ps = [(20..150, 200..300), (50100..50200, 1000..50000), (*, 5+5)];
|
||||
bt_assert((100,200) ~ ps);
|
||||
@ -414,6 +418,7 @@ quad qq;
|
||||
qq = 1.2.3.4;
|
||||
bt_assert(qq ~ [1.2.3.4, 5.6.7.8]);
|
||||
bt_assert(qq !~ [1.2.1.1, 1.2.3.5]);
|
||||
bt_assert(qq !~ []);
|
||||
}
|
||||
|
||||
bt_test_suite(t_quad_set, "Testing sets of quads");
|
||||
@ -494,6 +499,7 @@ ip set ips;
|
||||
|
||||
bt_assert(1.2.3.4 !~ [ 1.2.3.3, 1.2.3.5 ]);
|
||||
bt_assert(1.2.3.4 ~ [ 1.2.3.3..1.2.3.5 ]);
|
||||
bt_assert(1.2.3.4 !~ []);
|
||||
}
|
||||
|
||||
bt_test_suite(t_ip_set, "Testing sets of ip address");
|
||||
@ -582,13 +588,34 @@ function test_pxset(prefix set pxs)
|
||||
bt_assert(1.0.0.0/8 ~ [ 1.0.0.0/8+ ]);
|
||||
bt_assert(1.0.0.0/9 !~ [ 1.0.0.0/8- ]);
|
||||
bt_assert(1.2.0.0/17 !~ [ 1.0.0.0/8{ 15 , 16 } ]);
|
||||
bt_assert(net10 !~ []);
|
||||
|
||||
bt_assert([ 10.0.0.0/8{ 15 , 17 } ] = [ 10.0.0.0/8{ 15 , 17 } ]);
|
||||
}
|
||||
|
||||
function test_empty_pxset(prefix set pxs)
|
||||
int set s0;
|
||||
prefix set s1;
|
||||
{
|
||||
s0 = [];
|
||||
s1 = [];
|
||||
bt_assert(pxs != s0);
|
||||
bt_assert(pxs = s1);
|
||||
bt_assert(pxs = []);
|
||||
}
|
||||
|
||||
function t_prefix_set()
|
||||
prefix set pxs;
|
||||
{
|
||||
pxs = [];
|
||||
bt_assert(format(pxs) = "[]");
|
||||
bt_assert(pxs = []);
|
||||
bt_assert(1.2.0.0/16 !~ []);
|
||||
bt_assert(1.2.0.0/16 !~ pxs);
|
||||
|
||||
test_empty_pxset([]);
|
||||
test_empty_pxset(pxs);
|
||||
|
||||
pxs = [ 1.2.0.0/16, 1.4.0.0/16+, 44.66.88.64/30{24,28}, 12.34.56.0/24{8,16} ];
|
||||
bt_assert(format(pxs) = "[1.2.0.0/16{0.1.0.0}, 1.4.0.0/16{0.1.255.255}, 12.34.0.0/16{1.255.0.0}, 44.66.88.64/28{0.0.1.240}]");
|
||||
|
||||
@ -673,6 +700,12 @@ bt_test_suite(t_prefix6, "Testing prefix IPv6");
|
||||
function t_prefix6_set()
|
||||
prefix set pxs;
|
||||
{
|
||||
pxs = [];
|
||||
bt_assert(format(pxs) = "[]");
|
||||
bt_assert(pxs = []);
|
||||
bt_assert(12::34/128 !~ []);
|
||||
bt_assert(12::34/128 !~ pxs);
|
||||
|
||||
bt_assert(1180::/16 ~ [ 1100::/8{15, 17} ]);
|
||||
bt_assert(12::34 = 12::34);
|
||||
bt_assert(12::34 ~ [ 12::33..12::35 ]);
|
||||
@ -790,6 +823,7 @@ int set set12;
|
||||
bt_assert(3 ~ p2);
|
||||
bt_assert(p2 ~ [2, 10..20]);
|
||||
bt_assert(p2 ~ [4, 10..20]);
|
||||
bt_assert(p2 !~ []);
|
||||
|
||||
p2 = prepend(p2, 5);
|
||||
bt_assert(p2 !~ pm1);
|
||||
@ -800,6 +834,8 @@ int set set12;
|
||||
bt_assert(p2 ~ [= 5 [2, 4, 6] 3 [1..2] 1 =]);
|
||||
bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]);
|
||||
bt_assert(p2 ~ mkpath(5, 4));
|
||||
bt_assert(p2 ~ [= * [3] * =]);
|
||||
bt_assert(p2 !~ [= * [] * =]);
|
||||
|
||||
bt_assert(p2.len = 5);
|
||||
bt_assert(p2.first = 5);
|
||||
@ -808,6 +844,10 @@ int set set12;
|
||||
bt_assert(p2.len = 5);
|
||||
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 1), 2), 4), 5));
|
||||
bt_assert(filter(p2, [1..3]) = prepend(prepend(prepend(+empty+, 1), 2), 3));
|
||||
bt_assert(delete(p2, []) = p2);
|
||||
bt_assert(filter(p2, []) = +empty+);
|
||||
bt_assert(delete(prepend(prepend(+empty+, 0), 1), []) = prepend(prepend(+empty+, 0), 1));
|
||||
bt_assert(filter(prepend(prepend(+empty+, 0), 1), []) = +empty+);
|
||||
|
||||
p2 = prepend( + empty +, 5 );
|
||||
p2 = prepend( p2, 4 );
|
||||
@ -827,6 +867,15 @@ int set set12;
|
||||
bt_assert(delete(p2, [4..5]) = prepend(prepend(prepend(prepend(+empty+, 3), 3), 2), 1));
|
||||
|
||||
bt_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
|
||||
|
||||
# iteration over path
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
for int i in p2 do {
|
||||
x = x + i;
|
||||
y = y + x;
|
||||
}
|
||||
bt_assert(x = 18 && y = 50);
|
||||
}
|
||||
|
||||
bt_test_suite(t_path, "Testing paths");
|
||||
@ -868,6 +917,7 @@ clist r;
|
||||
bt_assert(l ~ [(2,2..3)]);
|
||||
bt_assert(l ~ [(1,1..2)]);
|
||||
bt_assert(l ~ [(1,1)..(1,2)]);
|
||||
bt_assert(l !~ []);
|
||||
|
||||
l = add(l, (2,5));
|
||||
l = add(l, (5,one));
|
||||
@ -905,6 +955,9 @@ clist r;
|
||||
bt_assert(l !~ [(*,(one+6))]);
|
||||
bt_assert(l !~ [(*, (one+one+one))]);
|
||||
|
||||
bt_assert(delete(l, []) = l);
|
||||
bt_assert(filter(l, []) = -empty-);
|
||||
|
||||
l = delete(l, [(*,(one+onef(3)))]);
|
||||
l = delete(l, [(*,(4+one))]);
|
||||
bt_assert(l = add(-empty-, (3,1)));
|
||||
@ -949,6 +1002,12 @@ clist r;
|
||||
bt_assert(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))");
|
||||
bt_assert(r.min = (1,3));
|
||||
bt_assert(r.max = (3,1));
|
||||
|
||||
# iteration over clist
|
||||
int x = 0;
|
||||
for pair c in r do
|
||||
x = x + c.asn * c.asn * c.data;
|
||||
bt_assert(x = 36);
|
||||
}
|
||||
|
||||
bt_test_suite(t_clist, "Testing lists of communities");
|
||||
@ -1022,11 +1081,15 @@ eclist r;
|
||||
bt_assert((ro, 10.20.30.40, 100) !~ el);
|
||||
bt_assert(el !~ [(rt, 10, 35..40)]);
|
||||
bt_assert(el !~ [(ro, 10, *)]);
|
||||
bt_assert(el !~ []);
|
||||
|
||||
el = add(el, (rt, 10, 40));
|
||||
el2 = filter(el, [(rt, 10, 20..40)] );
|
||||
el2 = add(el2, (rt, 10, 50));
|
||||
|
||||
bt_assert(delete(el, []) = el);
|
||||
bt_assert(filter(el, []) = --empty--);
|
||||
|
||||
# eclist A (1,30,40)
|
||||
bt_assert(el = add(add(add(--empty--, (rt, 10, 1)), (rt, 10, 30)), (rt, 10, 40)));
|
||||
bt_assert(format(el) = "(eclist (rt, 10, 1) (rt, 10, 30) (rt, 10, 40))");
|
||||
@ -1060,6 +1123,13 @@ eclist r;
|
||||
bt_assert(format(r) = "(eclist (rt, 2, 1) (rt, 1, 3) (rt, 2, 2) (rt, 3, 1) (rt, 2, 3))");
|
||||
bt_assert(r.min = (rt, 1, 3));
|
||||
bt_assert(r.max = (rt, 3, 1));
|
||||
|
||||
# iteration over eclist
|
||||
int x = 0;
|
||||
for ec c in r do
|
||||
if c > (rt, 2, 0) && c < (rt, 3, 0) then
|
||||
x = x + 1;
|
||||
bt_assert(x = 3);
|
||||
}
|
||||
|
||||
bt_test_suite(t_eclist, "Testing lists of extended communities");
|
||||
@ -1144,6 +1214,9 @@ lclist r;
|
||||
ll2 = add(ll2, (30, 30, 30));
|
||||
ll2 = add(ll2, (40, 40, 40));
|
||||
|
||||
bt_assert(delete(ll, []) = ll);
|
||||
bt_assert(filter(ll, []) = ---empty---);
|
||||
|
||||
# lclist A (10, 20, 30)
|
||||
bt_assert(format(ll) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 30))");
|
||||
|
||||
@ -1175,6 +1248,19 @@ lclist r;
|
||||
bt_assert(format(r) = "(lclist (2, 3, 3) (1, 2, 3) (2, 3, 1) (3, 1, 2) (2, 1, 3))");
|
||||
bt_assert(r.min = (1, 2, 3));
|
||||
bt_assert(r.max = (3, 1, 2));
|
||||
|
||||
# iteration over lclist
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
lc mx = (0, 0, 0);
|
||||
for lc c in r do {
|
||||
int asn2 = c.asn * c.asn;
|
||||
x = x + asn2 * c.data1;
|
||||
y = y + asn2 * c.data2;
|
||||
if c > mx then mx = c;
|
||||
}
|
||||
bt_assert(x = 39 && y = 49);
|
||||
bt_assert(mx = r.max);
|
||||
}
|
||||
|
||||
bt_test_suite(t_lclist, "Testing lists of large communities");
|
||||
@ -1203,6 +1289,7 @@ lc set lls;
|
||||
bt_assert(ll !~ [(5,10,15), (10,21,30)]);
|
||||
bt_assert(ll !~ [(10,21..25,*)]);
|
||||
bt_assert(ll !~ [(11, *, *)]);
|
||||
bt_assert(ll !~ []);
|
||||
|
||||
lls = [(10, 10, 10), (20, 20, 15..25), (30, 30, *), (40, 35..45, *), (50, *, *), (55..65, *, *)];
|
||||
bt_assert(format(lls) = "[(10, 10, 10), (20, 20, 15)..(20, 20, 25), (30, 30, 0)..(30, 30, 4294967295), (40, 35, 0)..(40, 45, 4294967295), (50, 0, 0)..(50, 4294967295, 4294967295), (55, 0, 0)..(65, 4294967295, 4294967295)]");
|
||||
@ -1259,6 +1346,10 @@ bt_test_suite(t_rd, "Testing route distinguishers");
|
||||
function t_rd_set()
|
||||
rd set rds;
|
||||
{
|
||||
rds = [];
|
||||
bt_assert(rds = []);
|
||||
bt_assert(10:20 !~ rds);
|
||||
|
||||
rds = [10:20, 100000:100..100000:200];
|
||||
bt_assert(format(rds) = "[10:20, 100000:100..100000:200]");
|
||||
|
||||
@ -1269,6 +1360,7 @@ rd set rds;
|
||||
bt_assert(100000:128 ~ rds);
|
||||
bt_assert(100000:200 ~ rds);
|
||||
bt_assert(100010:150 !~ rds);
|
||||
bt_assert(100010:150 !~ []);
|
||||
}
|
||||
|
||||
bt_test_suite(t_rd_set, "Testing sets of route distinguishers");
|
||||
@ -1335,7 +1427,85 @@ function fifteen()
|
||||
return 15;
|
||||
}
|
||||
|
||||
function local_vars(int j)
|
||||
{
|
||||
int k = 10;
|
||||
bt_assert(j = 5 && k = 10);
|
||||
{
|
||||
int j = 15;
|
||||
k = 20;
|
||||
bt_assert(j = 15 && k = 20);
|
||||
}
|
||||
bt_assert(j = 5 && k = 20);
|
||||
|
||||
if j < 10 then
|
||||
{
|
||||
int j = 25;
|
||||
string k = "hello";
|
||||
bt_assert(j = 25 && k = "hello");
|
||||
}
|
||||
bt_assert(j = 5 && k = 20);
|
||||
|
||||
int m = 100;
|
||||
{
|
||||
j = 35;
|
||||
int k = 40;
|
||||
bt_assert(j = 35 && k = 40 && m = 100);
|
||||
}
|
||||
bt_assert(j = 35 && k = 20 && m = 100);
|
||||
}
|
||||
|
||||
function factorial(int x)
|
||||
{
|
||||
if x = 0 then return 0;
|
||||
if x = 1 then return 1;
|
||||
else return x * factorial(x - 1);
|
||||
}
|
||||
|
||||
function fibonacci(int x)
|
||||
{
|
||||
if x = 0 then return 0;
|
||||
if x = 1 then return 1;
|
||||
else return fibonacci(x - 1) + fibonacci(x - 2);
|
||||
}
|
||||
|
||||
function hanoi_init(int a; int b)
|
||||
{
|
||||
if b = 0
|
||||
then return +empty+;
|
||||
else return prepend(hanoi_init(a + 1, b - 1), a);
|
||||
}
|
||||
|
||||
function hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
|
||||
{
|
||||
# x -> return src or dst
|
||||
# y -> print state
|
||||
|
||||
if n = 0 then { if x then return h_src; else return h_dst; }
|
||||
|
||||
bgppath tmp1 = hanoi_solve(n - 1, h_src, h_aux, h_dst, true, y);
|
||||
bgppath tmp2 = hanoi_solve(n - 1, h_src, h_aux, h_dst, false, false);
|
||||
h_src = tmp1;
|
||||
h_aux = tmp2;
|
||||
|
||||
int v = h_src.first;
|
||||
# bt_assert(h_dst = +empty+ || v < h_dst.first);
|
||||
h_src = delete(h_src, v);
|
||||
h_dst = prepend(h_dst, v);
|
||||
|
||||
if y then
|
||||
print "move: ", v, " src: ", h_src, " dst:", h_dst, " aux:", h_aux;
|
||||
|
||||
tmp1 = hanoi_solve(n - 1, h_aux, h_dst, h_src, true, y);
|
||||
tmp2 = hanoi_solve(n - 1, h_aux, h_dst, h_src, false, false);
|
||||
h_aux = tmp1;
|
||||
h_dst = tmp2;
|
||||
|
||||
if x then return h_src; else return h_dst;
|
||||
}
|
||||
|
||||
function t_call_function()
|
||||
bgppath h_src;
|
||||
{
|
||||
bt_assert(fifteen() = 15);
|
||||
|
||||
@ -1347,6 +1517,17 @@ function t_call_function()
|
||||
bt_assert(callme(4, 4) = 16);
|
||||
bt_assert(callme(7, 2) = 14);
|
||||
bt_assert(callmeagain(1, 2, 3) = 6);
|
||||
local_vars(5);
|
||||
|
||||
bt_assert(factorial(5) = 120);
|
||||
bt_assert(factorial(10) = 3628800);
|
||||
|
||||
bt_assert(fibonacci(10) = 55);
|
||||
bt_assert(fibonacci(20) = 6765);
|
||||
|
||||
h_src = hanoi_init(1, 6);
|
||||
bt_assert(format(h_src) = "(path 1 2 3 4 5 6)");
|
||||
bt_assert(hanoi_solve(6, h_src, +empty+, +empty+, false, false) = h_src);
|
||||
}
|
||||
|
||||
bt_test_suite(t_call_function, "Testing calling functions");
|
||||
@ -1592,13 +1773,16 @@ filter vpn_filter
|
||||
bt_assert(net.type != NET_IP6);
|
||||
bt_assert(net.rd = 0:1:2);
|
||||
|
||||
bool b = false;
|
||||
case (net.type) {
|
||||
NET_IP4: print "IPV4";
|
||||
NET_IP6: print "IPV6";
|
||||
else: b = true;
|
||||
}
|
||||
bt_assert(b);
|
||||
|
||||
bt_check_assign(from, 10.20.30.40);
|
||||
bt_check_assign(gw, 55.55.55.44);
|
||||
# bt_check_assign(gw, 55.55.55.44);
|
||||
|
||||
bgp_community.add((3,5));
|
||||
bgp_ext_community.add((ro, 135, 999));
|
||||
|
@ -1,4 +1,4 @@
|
||||
src := a-path.c a-set.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
|
||||
src := a-path.c a-set.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c rcu.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
|
||||
|
41
lib/a-path.c
41
lib/a-path.c
@ -602,8 +602,10 @@ as_path_match_set(const struct adata *path, const struct f_tree *set)
|
||||
}
|
||||
|
||||
const struct adata *
|
||||
as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tree *set, u32 key, int pos)
|
||||
as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos)
|
||||
{
|
||||
ASSERT((set->type == T_SET) || (set->type == T_INT));
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
@ -629,13 +631,13 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
|
||||
u32 as = get_as(p);
|
||||
int match;
|
||||
|
||||
if (set)
|
||||
if (set->type == T_SET)
|
||||
{
|
||||
struct f_val v = { .type = T_INT, .val.i = as};
|
||||
match = !!find_tree(set, &v);
|
||||
match = !!find_tree(set->val.t, &v);
|
||||
}
|
||||
else
|
||||
match = (as == key);
|
||||
else /* T_INT */
|
||||
match = (as == set->val.i);
|
||||
|
||||
if (match == pos)
|
||||
{
|
||||
@ -667,6 +669,35 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
as_path_walk(const struct adata *path, uint *pos, uint *val)
|
||||
{
|
||||
if (!path)
|
||||
return 0;
|
||||
|
||||
const u8 *p = path->data;
|
||||
const u8 *q = p + path->length;
|
||||
uint n, x = *pos;
|
||||
|
||||
while (p < q)
|
||||
{
|
||||
n = p[1];
|
||||
p += 2;
|
||||
|
||||
if (x < n)
|
||||
{
|
||||
*val = get_as(p + x * BS);
|
||||
*pos += 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
p += n * BS;
|
||||
x -= n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct pm_pos
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "nest/rt.h"
|
||||
#include "lib/attrs.h"
|
||||
#include "lib/resource.h"
|
||||
#include "filter/data.h"
|
||||
|
||||
#define TESTS_NUM 30
|
||||
#define AS_PATH_LENGTH 1000
|
||||
@ -127,8 +128,9 @@ t_path_include(void)
|
||||
int counts_of_contains = count_asn_in_array(as_nums, as_nums[i]);
|
||||
bt_assert_msg(as_path_contains(as_path, as_nums[i], counts_of_contains), "AS Path should contains %d-times number %d", counts_of_contains, as_nums[i]);
|
||||
|
||||
bt_assert(as_path_filter(tmp_linpool, as_path, NULL, as_nums[i], 0) != NULL);
|
||||
bt_assert(as_path_filter(tmp_linpool, as_path, NULL, as_nums[i], 1) != NULL);
|
||||
struct f_val v = { .type = T_INT, .val.i = as_nums[i] };
|
||||
bt_assert(as_path_filter(tmp_linpool, as_path, &v, 0) != NULL);
|
||||
bt_assert(as_path_filter(tmp_linpool, as_path, &v, 1) != NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < 10000; i++)
|
||||
|
48
lib/a-set.c
48
lib/a-set.c
@ -693,3 +693,51 @@ lc_set_max(const struct adata *list, lcomm *val)
|
||||
*val = (lcomm) { res[0], res[1], res[2] };
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
int_set_walk(const struct adata *list, uint *pos, uint *val)
|
||||
{
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
if (*pos >= (uint) int_set_get_size(list))
|
||||
return 0;
|
||||
|
||||
u32 *res = int_set_get_data(list) + *pos;
|
||||
*val = *res;
|
||||
*pos += 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ec_set_walk(const struct adata *list, uint *pos, u64 *val)
|
||||
{
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
if (*pos >= (uint) int_set_get_size(list))
|
||||
return 0;
|
||||
|
||||
u32 *res = int_set_get_data(list) + *pos;
|
||||
*val = ec_generic(res[0], res[1]);
|
||||
*pos += 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lc_set_walk(const struct adata *list, uint *pos, lcomm *val)
|
||||
{
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
if (*pos >= (uint) int_set_get_size(list))
|
||||
return 0;
|
||||
|
||||
u32 *res = int_set_get_data(list) + *pos;
|
||||
*val = (lcomm) { res[0], res[1], res[2] };
|
||||
*pos += 3;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -221,6 +221,7 @@ t_set_ec_delete(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -60,6 +60,7 @@ static inline int adata_same(const struct adata *a, const struct adata *b)
|
||||
* to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details
|
||||
*/
|
||||
|
||||
struct f_val;
|
||||
struct f_tree;
|
||||
|
||||
int as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen);
|
||||
@ -81,7 +82,8 @@ int as_path_get_last(const struct adata *path, u32 *last_as);
|
||||
u32 as_path_get_last_nonaggregated(const struct adata *path);
|
||||
int as_path_contains(const struct adata *path, u32 as, int min);
|
||||
int as_path_match_set(const struct adata *path, const struct f_tree *set);
|
||||
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tree *set, u32 key, int pos);
|
||||
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos);
|
||||
int as_path_walk(const struct adata *path, uint *pos, uint *val);
|
||||
|
||||
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
|
||||
{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); }
|
||||
@ -256,6 +258,9 @@ int lc_set_min(const struct adata *list, lcomm *val);
|
||||
int int_set_max(const struct adata *list, u32 *val);
|
||||
int ec_set_max(const struct adata *list, u64 *val);
|
||||
int lc_set_max(const struct adata *list, lcomm *val);
|
||||
int int_set_walk(const struct adata *list, uint *pos, u32 *val);
|
||||
int ec_set_walk(const struct adata *list, uint *pos, u64 *val);
|
||||
int lc_set_walk(const struct adata *list, uint *pos, lcomm *val);
|
||||
|
||||
void ec_set_sort_x(struct adata *set); /* Sort in place */
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
/* Ugly structure offset handling macros */
|
||||
|
||||
#define OFFSETOF(s, i) ((size_t) &((s *)0)->i)
|
||||
#define SKIP_BACK(s, i, p) ((s *)((char *)p - OFFSETOF(s, i)))
|
||||
#define SKIP_BACK(s, i, p) ({ s *_ptr = ((s *)((char *)p - OFFSETOF(s, i))); ASSERT_DIE(&_ptr->i == p); _ptr; })
|
||||
#define BIRD_ALIGN(s, a) (((s)+a-1)&~(a-1))
|
||||
#define CPU_STRUCT_ALIGN (MAX_(_Alignof(void*), _Alignof(u64)))
|
||||
#define BIRD_CPU_ALIGN(s) BIRD_ALIGN((s), CPU_STRUCT_ALIGN)
|
||||
@ -86,6 +86,7 @@ static inline int u64_cmp(u64 i1, u64 i2)
|
||||
/* Macros for gcc attributes */
|
||||
|
||||
#define NORET __attribute__((noreturn))
|
||||
#define USE_RESULT __atribute__((warn_unused_result))
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define PACKED __attribute__((packed))
|
||||
#define NONNULL(...) __attribute__((nonnull((__VA_ARGS__))))
|
||||
@ -93,10 +94,6 @@ static inline int u64_cmp(u64 i1, u64 i2)
|
||||
#define STATIC_ASSERT(EXP) _Static_assert(EXP, #EXP)
|
||||
#define STATIC_ASSERT_MSG(EXP,MSG) _Static_assert(EXP, MSG)
|
||||
|
||||
#ifndef HAVE_THREAD_LOCAL
|
||||
#define _Thread_local
|
||||
#endif
|
||||
|
||||
/* Microsecond time */
|
||||
|
||||
typedef s64 btime;
|
||||
@ -178,8 +175,13 @@ void debug(const char *msg, ...); /* Printf to debug output */
|
||||
|
||||
#if defined(LOCAL_DEBUG) || defined(GLOBAL_DEBUG)
|
||||
#define DBG(x, y...) debug(x, ##y)
|
||||
#define DBGL(x, y...) debug(x "\n", ##y)
|
||||
#elif defined(DEBUG_TO_LOG)
|
||||
#define DBG(...) do { } while (0)
|
||||
#define DBGL(...) log(L_DEBUG __VA_ARGS__)
|
||||
#else
|
||||
#define DBG(x, y...) do { } while(0)
|
||||
#define DBG(...) do { } while(0)
|
||||
#define DBGL(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define ASSERT_DIE(x) do { if (!(x)) bug("Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
|
||||
|
212
lib/event.c
212
lib/event.c
@ -19,20 +19,95 @@
|
||||
* events in them and explicitly ask to run them.
|
||||
*/
|
||||
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/io-loop.h"
|
||||
|
||||
event_list global_event_list;
|
||||
event_list global_work_list;
|
||||
|
||||
STATIC_ASSERT(OFFSETOF(event_list, _sentinel.next) >= OFFSETOF(event_list, _end[0]));
|
||||
|
||||
void
|
||||
ev_init_list(event_list *el, struct birdloop *loop, const char *name)
|
||||
{
|
||||
el->name = name;
|
||||
el->loop = loop;
|
||||
|
||||
atomic_store_explicit(&el->receiver, &el->_sentinel, memory_order_relaxed);
|
||||
atomic_store_explicit(&el->_executor, &el->_sentinel, memory_order_relaxed);
|
||||
atomic_store_explicit(&el->_sentinel.next, NULL, memory_order_relaxed);
|
||||
}
|
||||
|
||||
/*
|
||||
* The event list should work as a message passing point. Sending a message
|
||||
* must be a fairly fast process with no locks and low waiting times. OTOH,
|
||||
* processing messages always involves running the assigned code and the
|
||||
* receiver is always a single one thread with no concurrency at all. There is
|
||||
* also a postponing requirement to synchronously remove an event from a queue,
|
||||
* yet we allow this only when the caller has its receiver event loop locked.
|
||||
* It still means that the event may get postponed from other event in the same
|
||||
* list, therefore we have to be careful.
|
||||
*/
|
||||
|
||||
static inline int
|
||||
ev_remove_from(event *e, event * _Atomic * head)
|
||||
{
|
||||
/* The head pointer stores where cur is pointed to from */
|
||||
event * _Atomic *prev = head;
|
||||
|
||||
/* The current event in queue to check */
|
||||
event *cur = atomic_load_explicit(prev, memory_order_acquire);
|
||||
|
||||
/* Pre-loaded next pointer; if NULL, this is sentinel */
|
||||
event *next = atomic_load_explicit(&cur->next, memory_order_acquire);
|
||||
|
||||
while (next)
|
||||
{
|
||||
if (e == cur)
|
||||
{
|
||||
/* Check whether we have collided with somebody else
|
||||
* adding an item to the queue. */
|
||||
if (!atomic_compare_exchange_strong_explicit(
|
||||
prev, &cur, next,
|
||||
memory_order_acq_rel, memory_order_acquire))
|
||||
{
|
||||
/* This may happen only on list head */
|
||||
ASSERT_DIE(prev == head);
|
||||
|
||||
/* Restart. The collision should never happen again. */
|
||||
return ev_remove_from(e, head);
|
||||
}
|
||||
|
||||
/* Successfully removed from the list; inactivate this event. */
|
||||
atomic_store_explicit(&cur->next, NULL, memory_order_release);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Go to the next event. */
|
||||
prev = &cur->next;
|
||||
cur = next;
|
||||
next = atomic_load_explicit(&cur->next, memory_order_acquire);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void
|
||||
ev_postpone(event *e)
|
||||
{
|
||||
if (ev_active(e))
|
||||
{
|
||||
rem_node(&e->n);
|
||||
e->n.next = NULL;
|
||||
}
|
||||
/* Find the list to remove the event from */
|
||||
event_list *sl = ev_get_list(e);
|
||||
if (!sl)
|
||||
return;
|
||||
|
||||
/* Postponing allowed only from the target loop */
|
||||
ASSERT_DIE(birdloop_inside(sl->loop));
|
||||
|
||||
/* Remove from one of these lists. */
|
||||
ASSERT(ev_remove_from(e, &sl->_executor) || ev_remove_from(e, &sl->receiver));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -43,7 +118,7 @@ ev_dump(resource *r)
|
||||
debug("(code %p, data %p, %s)\n",
|
||||
e->hook,
|
||||
e->data,
|
||||
e->n.next ? "scheduled" : "inactive");
|
||||
atomic_load_explicit(&e->next, memory_order_relaxed) ? "scheduled" : "inactive");
|
||||
}
|
||||
|
||||
static struct resclass ev_class = {
|
||||
@ -95,40 +170,21 @@ ev_run(event *e)
|
||||
* list @l which can be run by calling ev_run_list().
|
||||
*/
|
||||
inline void
|
||||
ev_enqueue(event_list *l, event *e)
|
||||
ev_send(event_list *l, event *e)
|
||||
{
|
||||
ev_postpone(e);
|
||||
add_tail(l, &e->n);
|
||||
}
|
||||
event_list *sl = ev_get_list(e);
|
||||
if (sl == l)
|
||||
return;
|
||||
if (sl)
|
||||
bug("Queuing an already queued event to another queue is not supported.");
|
||||
|
||||
/**
|
||||
* ev_schedule - schedule an event
|
||||
* @e: an event
|
||||
*
|
||||
* This function schedules an event by enqueueing it to a system-wide
|
||||
* event list which is run by the platform dependent code whenever
|
||||
* appropriate.
|
||||
*/
|
||||
void
|
||||
ev_schedule(event *e)
|
||||
{
|
||||
ev_enqueue(&global_event_list, e);
|
||||
}
|
||||
event *next = atomic_load_explicit(&l->receiver, memory_order_acquire);
|
||||
do atomic_store_explicit(&e->next, next, memory_order_relaxed);
|
||||
while (!atomic_compare_exchange_strong_explicit(
|
||||
&l->receiver, &next, e,
|
||||
memory_order_acq_rel, memory_order_acquire));
|
||||
|
||||
/**
|
||||
* ev_schedule_work - schedule a work-event.
|
||||
* @e: an event
|
||||
*
|
||||
* This function schedules an event by enqueueing it to a system-wide work-event
|
||||
* list which is run by the platform dependent code whenever appropriate. This
|
||||
* is designated for work-events instead of regular events. They are executed
|
||||
* less often in order to not clog I/O loop.
|
||||
*/
|
||||
void
|
||||
ev_schedule_work(event *e)
|
||||
{
|
||||
if (!ev_active(e))
|
||||
add_tail(&global_work_list, &e->n);
|
||||
birdloop_ping(l->loop);
|
||||
}
|
||||
|
||||
void io_log_event(void *hook, void *data);
|
||||
@ -139,63 +195,57 @@ void io_log_event(void *hook, void *data);
|
||||
*
|
||||
* This function calls ev_run() for all events enqueued in the list @l.
|
||||
*/
|
||||
int
|
||||
ev_run_list(event_list *l)
|
||||
{
|
||||
node *n;
|
||||
list tmp_list;
|
||||
|
||||
init_list(&tmp_list);
|
||||
add_tail_list(&tmp_list, l);
|
||||
init_list(l);
|
||||
WALK_LIST_FIRST(n, tmp_list)
|
||||
{
|
||||
event *e = SKIP_BACK(event, n, n);
|
||||
|
||||
/* This is ugly hack, we want to log just events executed from the main I/O loop */
|
||||
if ((l == &global_event_list) || (l == &global_work_list))
|
||||
io_log_event(e->hook, e->data);
|
||||
|
||||
ev_run(e);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
return !EMPTY_LIST(*l);
|
||||
}
|
||||
|
||||
int
|
||||
ev_run_list_limited(event_list *l, uint limit)
|
||||
{
|
||||
node *n;
|
||||
list tmp_list;
|
||||
event * _Atomic *ep = &l->_executor;
|
||||
|
||||
init_list(&tmp_list);
|
||||
add_tail_list(&tmp_list, l);
|
||||
init_list(l);
|
||||
/* No pending events, refill the queue. */
|
||||
if (atomic_load_explicit(ep, memory_order_relaxed) == &l->_sentinel)
|
||||
{
|
||||
/* Move the current event list aside and create a new one. */
|
||||
event *received = atomic_exchange_explicit(
|
||||
&l->receiver, &l->_sentinel, memory_order_acq_rel);
|
||||
|
||||
WALK_LIST_FIRST(n, tmp_list)
|
||||
/* No event to run. */
|
||||
if (received == &l->_sentinel)
|
||||
return 0;
|
||||
|
||||
/* Setup the executor queue */
|
||||
event *head = &l->_sentinel;
|
||||
|
||||
/* Flip the order of the events by relinking them one by one (push-pop) */
|
||||
while (received != &l->_sentinel)
|
||||
{
|
||||
event *e = SKIP_BACK(event, n, n);
|
||||
event *cur = received;
|
||||
received = atomic_exchange_explicit(&cur->next, head, memory_order_relaxed);
|
||||
head = cur;
|
||||
}
|
||||
|
||||
if (!limit)
|
||||
break;
|
||||
/* Store the executor queue to its designated place */
|
||||
atomic_store_explicit(ep, head, memory_order_relaxed);
|
||||
}
|
||||
|
||||
/* Run the events in order. */
|
||||
event *e;
|
||||
while ((e = atomic_load_explicit(ep, memory_order_relaxed)) != &l->_sentinel)
|
||||
{
|
||||
/* Check limit */
|
||||
if (!--limit)
|
||||
return 1;
|
||||
|
||||
/* This is ugly hack, we want to log just events executed from the main I/O loop */
|
||||
if ((l == &global_event_list) || (l == &global_work_list))
|
||||
io_log_event(e->hook, e->data);
|
||||
|
||||
ev_run(e);
|
||||
/* Inactivate the event */
|
||||
atomic_store_explicit(ep, atomic_load_explicit(&e->next, memory_order_relaxed), memory_order_relaxed);
|
||||
atomic_store_explicit(&e->next, NULL, memory_order_relaxed);
|
||||
|
||||
/* Run the event */
|
||||
e->hook(e->data);
|
||||
tmp_flush();
|
||||
limit--;
|
||||
}
|
||||
|
||||
if (!EMPTY_LIST(tmp_list))
|
||||
{
|
||||
/* Attach new items after the unprocessed old items */
|
||||
add_tail_list(&tmp_list, l);
|
||||
init_list(l);
|
||||
add_tail_list(l, &tmp_list);
|
||||
}
|
||||
|
||||
return !EMPTY_LIST(*l);
|
||||
return atomic_load_explicit(&l->receiver, memory_order_relaxed) != &l->_sentinel;
|
||||
}
|
||||
|
50
lib/event.h
50
lib/event.h
@ -10,33 +10,69 @@
|
||||
#define _BIRD_EVENT_H_
|
||||
|
||||
#include "lib/resource.h"
|
||||
#include "lib/locking.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
struct birdloop;
|
||||
|
||||
typedef struct event {
|
||||
resource r;
|
||||
void (*hook)(void *);
|
||||
void *data;
|
||||
node n; /* Internal link */
|
||||
struct event * _Atomic next;
|
||||
} event;
|
||||
|
||||
typedef list event_list;
|
||||
typedef union event_list {
|
||||
struct {
|
||||
event * _Atomic receiver; /* Event receive list */
|
||||
event * _Atomic _executor; /* Event execute list */
|
||||
const char *name;
|
||||
struct birdloop *loop; /* The executor loop */
|
||||
char _end[0];
|
||||
};
|
||||
event _sentinel; /* Sentinel node to actively detect list end */
|
||||
} event_list;
|
||||
|
||||
extern event_list global_event_list;
|
||||
extern event_list global_work_list;
|
||||
|
||||
event *ev_new(pool *);
|
||||
void ev_run(event *);
|
||||
#define ev_init_list(el) init_list(el)
|
||||
void ev_init_list(event_list *, struct birdloop *loop, const char *name);
|
||||
void ev_enqueue(event_list *, event *);
|
||||
void ev_schedule(event *);
|
||||
void ev_schedule_work(event *);
|
||||
#define ev_send ev_enqueue
|
||||
#define ev_send_loop(l, e) ev_send(birdloop_event_list((l)), (e))
|
||||
|
||||
#define ev_schedule(e) ({ ASSERT_THE_BIRD_LOCKED; if (!ev_active((e))) ev_send(&global_event_list, (e)); })
|
||||
#define ev_schedule_work(e) ({ ASSERT_THE_BIRD_LOCKED; if (!ev_active((e))) ev_send(&global_work_list, (e)); })
|
||||
|
||||
void ev_postpone(event *);
|
||||
int ev_run_list(event_list *);
|
||||
int ev_run_list_limited(event_list *, uint);
|
||||
#define ev_run_list(l) ev_run_list_limited((l), ~0)
|
||||
|
||||
#define LEGACY_EVENT_LIST(l) (((l) == &global_event_list) || ((l) == &global_work_list))
|
||||
|
||||
static inline int
|
||||
ev_active(event *e)
|
||||
{
|
||||
return e->n.next != NULL;
|
||||
return atomic_load_explicit(&e->next, memory_order_relaxed) != NULL;
|
||||
}
|
||||
|
||||
static inline event_list *
|
||||
ev_get_list(event *e)
|
||||
{
|
||||
/* We are looking for the sentinel node at the list end.
|
||||
* After this, we have s->next == NULL */
|
||||
event *s = e;
|
||||
for (event *sn; sn = atomic_load_explicit(&s->next, memory_order_acquire); s = sn)
|
||||
;
|
||||
|
||||
/* No sentinel, no list. */
|
||||
if (s == e)
|
||||
return NULL;
|
||||
else
|
||||
return SKIP_BACK(event_list, _sentinel, s);
|
||||
}
|
||||
|
||||
static inline event*
|
||||
|
@ -54,7 +54,6 @@ t_ev_run_list(void)
|
||||
int i;
|
||||
|
||||
olock_init();
|
||||
timer_init();
|
||||
rt_init();
|
||||
io_init();
|
||||
if_init();
|
||||
|
58
lib/io-loop.h
Normal file
58
lib/io-loop.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* BIRD -- I/O and event loop
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_IO_LOOP_H_
|
||||
#define _BIRD_IO_LOOP_H_
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/lists.h"
|
||||
#include "lib/locking.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/socket.h"
|
||||
|
||||
extern struct birdloop main_birdloop;
|
||||
|
||||
void sk_start(sock *s);
|
||||
void sk_stop(sock *s);
|
||||
void sk_reloop(sock *s, struct birdloop *loop);
|
||||
|
||||
/* Start a new birdloop owned by given pool and domain */
|
||||
struct birdloop *birdloop_new(pool *p, uint order, const char *name);
|
||||
|
||||
/* Stop the loop. At the end, the @stopped callback is called unlocked in tail
|
||||
* position to finish cleanup. Run birdloop_free() from that callback to free
|
||||
* the loop itself. */
|
||||
void birdloop_stop(struct birdloop *loop, void (*stopped)(void *data), void *data);
|
||||
void birdloop_stop_self(struct birdloop *loop, void (*stopped)(void *data), void *data);
|
||||
void birdloop_free(struct birdloop *loop);
|
||||
|
||||
/* Get birdloop's event list */
|
||||
event_list *birdloop_event_list(struct birdloop *loop);
|
||||
|
||||
/* Get birdloop's time heap */
|
||||
struct timeloop *birdloop_time_loop(struct birdloop *loop);
|
||||
|
||||
/* Enter and exit the birdloop */
|
||||
void birdloop_enter(struct birdloop *loop);
|
||||
void birdloop_leave(struct birdloop *loop);
|
||||
|
||||
_Bool birdloop_inside(struct birdloop *loop);
|
||||
|
||||
void birdloop_mask_wakeups(struct birdloop *loop);
|
||||
void birdloop_unmask_wakeups(struct birdloop *loop);
|
||||
|
||||
void birdloop_link(struct birdloop *loop);
|
||||
void birdloop_unlink(struct birdloop *loop);
|
||||
|
||||
void birdloop_ping(struct birdloop *loop);
|
||||
|
||||
void birdloop_init(void);
|
||||
|
||||
/* Yield for a little while. Use only in special cases. */
|
||||
void birdloop_yield(void);
|
||||
|
||||
#endif /* _BIRD_IO_LOOP_H_ */
|
15
lib/lists.c
15
lib/lists.c
@ -26,7 +26,7 @@
|
||||
|
||||
#define _BIRD_LISTS_C_
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/birdlib.h"
|
||||
#include "lib/lists.h"
|
||||
|
||||
LIST_INLINE int
|
||||
@ -35,11 +35,12 @@ check_list(list *l, node *n)
|
||||
if (!l)
|
||||
{
|
||||
ASSERT_DIE(n);
|
||||
ASSERT_DIE(n->prev);
|
||||
|
||||
do { n = n->prev; } while (n->prev);
|
||||
node *nn = n;
|
||||
while (nn->prev)
|
||||
nn = nn->prev;
|
||||
|
||||
l = SKIP_BACK(list, head_node, n);
|
||||
l = SKIP_BACK(list, head_node, nn);
|
||||
}
|
||||
|
||||
int seen = 0;
|
||||
@ -60,7 +61,7 @@ check_list(list *l, node *n)
|
||||
}
|
||||
|
||||
ASSERT_DIE(cur == &(l->tail_node));
|
||||
ASSERT_DIE(!n || (seen == 1));
|
||||
ASSERT_DIE(!n || (seen == 1) || (n == &l->head_node) || (n == &l->tail_node));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -120,7 +121,7 @@ add_head(list *l, node *n)
|
||||
LIST_INLINE void
|
||||
insert_node(node *n, node *after)
|
||||
{
|
||||
EXPENSIVE_CHECK(check_list(l, after));
|
||||
EXPENSIVE_CHECK(check_list(NULL, after));
|
||||
ASSUME(n->prev == NULL);
|
||||
ASSUME(n->next == NULL);
|
||||
|
||||
@ -141,7 +142,7 @@ insert_node(node *n, node *after)
|
||||
LIST_INLINE void
|
||||
rem_node(node *n)
|
||||
{
|
||||
EXPENSIVE_CHECK(check_list(NULL, n));
|
||||
EXPENSIVE_CHECK((n == n->prev) && (n == n->next) || check_list(NULL, n));
|
||||
|
||||
node *z = n->prev;
|
||||
node *x = n->next;
|
||||
|
12
lib/lists.h
12
lib/lists.h
@ -69,6 +69,18 @@ typedef union list { /* In fact two overlayed nodes */
|
||||
|
||||
#define EMPTY_LIST(list) (!(list).head->next)
|
||||
|
||||
static inline _Bool
|
||||
enlisted(node *n)
|
||||
{
|
||||
switch ((!!n->next) + (!!n->prev))
|
||||
{
|
||||
case 0: return 0;
|
||||
case 2: return 1;
|
||||
case 1: bug("Garbled event list node");
|
||||
}
|
||||
|
||||
bug("Maths is broken. And you should see a new heaven and a new earth: for the first heaven and the first earth had been passed away.");
|
||||
}
|
||||
|
||||
#ifndef _BIRD_LISTS_C_
|
||||
#define LIST_INLINE static inline
|
||||
|
63
lib/locking.h
Normal file
63
lib/locking.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* BIRD Library -- Locking
|
||||
*
|
||||
* (c) 2020--2021 Maria Matejka <mq@jmq.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_LOCKING_H_
|
||||
#define _BIRD_LOCKING_H_
|
||||
|
||||
struct domain_generic;
|
||||
|
||||
/* Here define the global lock order; first to last. */
|
||||
struct lock_order {
|
||||
struct domain_generic *the_bird;
|
||||
struct domain_generic *proto;
|
||||
struct domain_generic *rtable;
|
||||
struct domain_generic *resource;
|
||||
};
|
||||
|
||||
extern _Thread_local struct lock_order locking_stack;
|
||||
extern _Thread_local struct domain_generic **last_locked;
|
||||
|
||||
#define DOMAIN(type) struct domain__##type
|
||||
#define DEFINE_DOMAIN(type) DOMAIN(type) { struct domain_generic *type; }
|
||||
#define DOMAIN_ORDER(type) OFFSETOF(struct lock_order, type)
|
||||
|
||||
#define DOMAIN_NEW(type, name) (DOMAIN(type)) { .type = domain_new(name, DOMAIN_ORDER(type)) }
|
||||
struct domain_generic *domain_new(const char *name, uint order);
|
||||
|
||||
#define DOMAIN_FREE(type, d) domain_free((d).type)
|
||||
void domain_free(struct domain_generic *);
|
||||
|
||||
#define DOMAIN_NULL(type) (DOMAIN(type)) {}
|
||||
|
||||
#define LOCK_DOMAIN(type, d) do_lock(((d).type), &(locking_stack.type))
|
||||
#define UNLOCK_DOMAIN(type, d) do_unlock(((d).type), &(locking_stack.type))
|
||||
|
||||
#define DOMAIN_IS_LOCKED(type, d) (((d).type) == (locking_stack.type))
|
||||
#define DG_IS_LOCKED(d) ((d) == *(DG_LSP(d)))
|
||||
|
||||
/* Internal for locking */
|
||||
void do_lock(struct domain_generic *dg, struct domain_generic **lsp);
|
||||
void do_unlock(struct domain_generic *dg, struct domain_generic **lsp);
|
||||
|
||||
uint dg_order(struct domain_generic *dg);
|
||||
|
||||
#define DG_LSP(d) ((struct domain_generic **) (((void *) &locking_stack) + dg_order(d)))
|
||||
#define DG_LOCK(d) do_lock(d, DG_LSP(d))
|
||||
#define DG_UNLOCK(d) do_unlock(d, DG_LSP(d))
|
||||
|
||||
/* Use with care. To be removed in near future. */
|
||||
DEFINE_DOMAIN(the_bird);
|
||||
extern DOMAIN(the_bird) the_bird_domain;
|
||||
|
||||
#define the_bird_lock() LOCK_DOMAIN(the_bird, the_bird_domain)
|
||||
#define the_bird_unlock() UNLOCK_DOMAIN(the_bird, the_bird_domain)
|
||||
#define the_bird_locked() DOMAIN_IS_LOCKED(the_bird, the_bird_domain)
|
||||
|
||||
#define ASSERT_THE_BIRD_LOCKED ({ if (!the_bird_locked()) bug("The BIRD lock must be locked here: %s:%d", __FILE__, __LINE__); })
|
||||
|
||||
#endif
|
79
lib/rcu.c
Normal file
79
lib/rcu.c
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* BIRD Library -- Read-Copy-Update Basic Operations
|
||||
*
|
||||
* (c) 2021 Maria Matejka <mq@jmq.cz>
|
||||
* (c) 2021 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
* Note: all the relevant patents shall be expired.
|
||||
*
|
||||
* Using the Supplementary Material for User-Level Implementations of Read-Copy-Update
|
||||
* by Matthieu Desnoyers, Paul E. McKenney, Alan S. Stern, Michel R. Dagenais and Jonathan Walpole
|
||||
* obtained from https://www.efficios.com/pub/rcu/urcu-supp-accepted.pdf
|
||||
*/
|
||||
|
||||
#include "lib/rcu.h"
|
||||
#include "lib/io-loop.h"
|
||||
#include "lib/locking.h"
|
||||
|
||||
_Atomic uint rcu_gp_ctl = RCU_NEST_CNT;
|
||||
_Thread_local struct rcu_birdloop *this_rcu_birdloop = NULL;
|
||||
|
||||
static list rcu_birdloop_list;
|
||||
|
||||
static struct rcu_birdloop main_rcu_birdloop;
|
||||
|
||||
DEFINE_DOMAIN(resource);
|
||||
static DOMAIN(resource) rcu_domain;
|
||||
|
||||
static int
|
||||
rcu_gp_ongoing(_Atomic uint *ctl)
|
||||
{
|
||||
uint val = atomic_load(ctl);
|
||||
return (val & RCU_NEST_CNT) && ((val ^ rcu_gp_ctl) & RCU_GP_PHASE);
|
||||
}
|
||||
|
||||
static void
|
||||
update_counter_and_wait(void)
|
||||
{
|
||||
atomic_fetch_xor(&rcu_gp_ctl, RCU_GP_PHASE);
|
||||
struct rcu_birdloop *rc;
|
||||
WALK_LIST(rc, rcu_birdloop_list)
|
||||
while (rcu_gp_ongoing(&rc->ctl))
|
||||
birdloop_yield();
|
||||
}
|
||||
|
||||
void
|
||||
synchronize_rcu(void)
|
||||
{
|
||||
LOCK_DOMAIN(resource, rcu_domain);
|
||||
update_counter_and_wait();
|
||||
update_counter_and_wait();
|
||||
UNLOCK_DOMAIN(resource, rcu_domain);
|
||||
}
|
||||
|
||||
void
|
||||
rcu_birdloop_start(struct rcu_birdloop *rc)
|
||||
{
|
||||
LOCK_DOMAIN(resource, rcu_domain);
|
||||
add_tail(&rcu_birdloop_list, &rc->n);
|
||||
this_rcu_birdloop = rc;
|
||||
UNLOCK_DOMAIN(resource, rcu_domain);
|
||||
}
|
||||
|
||||
void
|
||||
rcu_birdloop_stop(struct rcu_birdloop *rc)
|
||||
{
|
||||
LOCK_DOMAIN(resource, rcu_domain);
|
||||
this_rcu_birdloop = NULL;
|
||||
rem_node(&rc->n);
|
||||
UNLOCK_DOMAIN(resource, rcu_domain);
|
||||
}
|
||||
|
||||
void
|
||||
rcu_init(void)
|
||||
{
|
||||
rcu_domain = DOMAIN_NEW(resource, "Read-Copy-Update");
|
||||
init_list(&rcu_birdloop_list);
|
||||
rcu_birdloop_start(&main_rcu_birdloop);
|
||||
}
|
55
lib/rcu.h
Normal file
55
lib/rcu.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* BIRD Library -- Read-Copy-Update Basic Operations
|
||||
*
|
||||
* (c) 2021 Maria Matejka <mq@jmq.cz>
|
||||
* (c) 2021 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
* Note: all the relevant patents shall be expired.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_RCU_H_
|
||||
#define _BIRD_RCU_H_
|
||||
|
||||
#include "lib/birdlib.h"
|
||||
#include "lib/lists.h"
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define RCU_GP_PHASE 0x100000
|
||||
#define RCU_NEST_MASK 0x0fffff
|
||||
#define RCU_NEST_CNT 0x000001
|
||||
|
||||
extern _Atomic uint rcu_gp_ctl;
|
||||
|
||||
struct rcu_birdloop {
|
||||
node n;
|
||||
_Atomic uint ctl;
|
||||
};
|
||||
|
||||
extern _Thread_local struct rcu_birdloop *this_rcu_birdloop;
|
||||
|
||||
static inline void rcu_read_lock(void)
|
||||
{
|
||||
uint cmp = atomic_load_explicit(&this_rcu_birdloop->ctl, memory_order_acquire);
|
||||
|
||||
if (cmp & RCU_NEST_MASK)
|
||||
atomic_store_explicit(&this_rcu_birdloop->ctl, cmp + RCU_NEST_CNT, memory_order_relaxed);
|
||||
else
|
||||
atomic_store(&this_rcu_birdloop->ctl, atomic_load_explicit(&rcu_gp_ctl, memory_order_acquire));
|
||||
}
|
||||
|
||||
static inline void rcu_read_unlock(void)
|
||||
{
|
||||
atomic_fetch_sub(&this_rcu_birdloop->ctl, RCU_NEST_CNT);
|
||||
}
|
||||
|
||||
void synchronize_rcu(void);
|
||||
|
||||
/* Registering and unregistering a birdloop. To be called from birdloop implementation */
|
||||
void rcu_birdloop_start(struct rcu_birdloop *);
|
||||
void rcu_birdloop_stop(struct rcu_birdloop *);
|
||||
|
||||
/* Run this from resource init */
|
||||
void rcu_init(void);
|
||||
|
||||
#endif
|
@ -14,6 +14,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/rcu.h"
|
||||
|
||||
/**
|
||||
* DOC: Resource pools
|
||||
@ -29,12 +30,6 @@
|
||||
* is freed upon shutdown of the module.
|
||||
*/
|
||||
|
||||
struct pool {
|
||||
resource r;
|
||||
list inside;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static void pool_dump(resource *);
|
||||
static void pool_free(resource *);
|
||||
static resource *pool_lookup(resource *, unsigned long);
|
||||
@ -285,6 +280,7 @@ void
|
||||
resource_init(void)
|
||||
{
|
||||
resource_sys_init();
|
||||
rcu_init();
|
||||
|
||||
root_pool.r.class = &pool_class;
|
||||
root_pool.name = "Root";
|
||||
|
@ -40,7 +40,12 @@ struct resclass {
|
||||
|
||||
/* Generic resource manipulation */
|
||||
|
||||
typedef struct pool pool;
|
||||
typedef struct pool {
|
||||
resource r;
|
||||
list inside;
|
||||
const char *name;
|
||||
} pool;
|
||||
|
||||
|
||||
void resource_init(void);
|
||||
pool *rp_new(pool *, const char *); /* Create new pool */
|
||||
@ -115,6 +120,7 @@ void sl_free(void *);
|
||||
void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size);
|
||||
|
||||
/* Allocator of whole pages; for use in slabs and other high-level allocators. */
|
||||
#define PAGE_HEAD(x) ((void *) (((uintptr_t) (x)) & ~(page_size-1)))
|
||||
extern long page_size;
|
||||
void *alloc_page(void);
|
||||
void free_page(void *);
|
||||
|
@ -29,12 +29,10 @@ typedef struct rte {
|
||||
u8 generation; /* If this route import is based on other previously exported route,
|
||||
this value should be 1 + MAX(generation of the parent routes).
|
||||
Otherwise the route is independent and this value is zero. */
|
||||
u8 stale_cycle; /* Auxiliary value for route refresh */
|
||||
} rte;
|
||||
|
||||
#define REF_FILTERED 2 /* Route is rejected by import filter */
|
||||
#define REF_STALE 4 /* Route is stale in a refresh cycle */
|
||||
#define REF_DISCARD 8 /* Route is scheduled for discard */
|
||||
#define REF_MODIFY 16 /* Route is scheduled for modify */
|
||||
#define REF_PENDING 32 /* Route has not propagated completely yet */
|
||||
|
||||
/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
|
||||
@ -425,7 +423,7 @@ static inline int ea_is_cached(const ea_list *r) { return r->flags & EALF_CACHED
|
||||
static inline struct ea_storage *ea_get_storage(ea_list *r)
|
||||
{
|
||||
ASSERT_DIE(ea_is_cached(r));
|
||||
return SKIP_BACK(struct ea_storage, l, r);
|
||||
return SKIP_BACK(struct ea_storage, l[0], r);
|
||||
}
|
||||
|
||||
static inline ea_list *ea_clone(ea_list *r) { ea_get_storage(r)->uc++; return r; }
|
||||
|
@ -197,7 +197,7 @@ static struct resclass sl_class = {
|
||||
slab_memsize
|
||||
};
|
||||
|
||||
#define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(page_size-1)))
|
||||
#define SL_GET_HEAD(x) PAGE_HEAD(x)
|
||||
|
||||
#define SL_HEAD_CHANGE_STATE(_s, _h, _from, _to) ({ \
|
||||
ASSERT_DIE(_h->state == slh_##_from); \
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "lib/resource.h"
|
||||
#include "lib/event.h"
|
||||
#ifdef HAVE_LIBSSH
|
||||
#define LIBSSH_LEGACY_0_4
|
||||
#include <libssh/libssh.h>
|
||||
@ -79,6 +80,7 @@ typedef struct birdsock {
|
||||
const char *password; /* Password for MD5 authentication */
|
||||
const char *err; /* Error message */
|
||||
struct ssh_sock *ssh; /* Used in SK_SSH */
|
||||
struct event reloop; /* Reloop event */
|
||||
} sock;
|
||||
|
||||
sock *sock_new(pool *); /* Allocate new socket */
|
||||
@ -129,6 +131,7 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
|
||||
#define SKF_TRUNCATED 0x200 /* Received packet was truncated, set by IO layer */
|
||||
#define SKF_HDRINCL 0x400 /* Used internally */
|
||||
#define SKF_PKTINFO 0x800 /* Used internally */
|
||||
#define SKF_PASSIVE_THREAD 0x1000 /* Child sockets used in thread, do not add to main loop */
|
||||
|
||||
/*
|
||||
* Socket types SA SP DA DP IF TTL SendTo (?=may, -=must not, *=must)
|
||||
|
114
lib/timer.c
114
lib/timer.c
@ -36,57 +36,13 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/timer.h"
|
||||
|
||||
|
||||
struct timeloop main_timeloop;
|
||||
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/* Data accessed and modified from proto/bfd/io.c */
|
||||
pthread_key_t current_time_key;
|
||||
|
||||
static inline struct timeloop *
|
||||
timeloop_current(void)
|
||||
{
|
||||
return pthread_getspecific(current_time_key);
|
||||
}
|
||||
|
||||
static inline void
|
||||
timeloop_init_current(void)
|
||||
{
|
||||
pthread_key_create(¤t_time_key, NULL);
|
||||
pthread_setspecific(current_time_key, &main_timeloop);
|
||||
}
|
||||
_Atomic btime last_time;
|
||||
_Atomic btime real_time;
|
||||
|
||||
void wakeup_kick_current(void);
|
||||
|
||||
#else
|
||||
|
||||
/* Just use main timelooop */
|
||||
static inline struct timeloop * timeloop_current(void) { return &main_timeloop; }
|
||||
static inline void timeloop_init_current(void) { }
|
||||
|
||||
#endif
|
||||
|
||||
btime
|
||||
current_time(void)
|
||||
{
|
||||
return timeloop_current()->last_time;
|
||||
}
|
||||
|
||||
btime
|
||||
current_real_time(void)
|
||||
{
|
||||
struct timeloop *loop = timeloop_current();
|
||||
|
||||
if (!loop->real_time)
|
||||
times_update_real_time(loop);
|
||||
|
||||
return loop->real_time;
|
||||
}
|
||||
|
||||
|
||||
#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, \
|
||||
@ -112,7 +68,7 @@ tm_dump(resource *r)
|
||||
if (t->recurrent)
|
||||
debug("recur %d, ", t->recurrent);
|
||||
if (t->expires)
|
||||
debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
|
||||
debug("in loop %p expires in %d ms)\n", t->loop, (t->expires - current_time()) TO_MS);
|
||||
else
|
||||
debug("inactive)\n");
|
||||
}
|
||||
@ -135,41 +91,40 @@ tm_new(pool *p)
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
tm_set(timer *t, btime when)
|
||||
static void
|
||||
tm_set_in_tl(timer *t, btime when, struct timeloop *local_timeloop)
|
||||
{
|
||||
struct timeloop *loop = timeloop_current();
|
||||
uint tc = timers_count(loop);
|
||||
uint tc = timers_count(local_timeloop);
|
||||
|
||||
if (!t->expires)
|
||||
{
|
||||
t->index = ++tc;
|
||||
t->expires = when;
|
||||
BUFFER_PUSH(loop->timers) = t;
|
||||
HEAP_INSERT(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP);
|
||||
BUFFER_PUSH(local_timeloop->timers) = t;
|
||||
HEAP_INSERT(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP);
|
||||
}
|
||||
else if (t->expires < when)
|
||||
{
|
||||
t->expires = when;
|
||||
HEAP_INCREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
HEAP_INCREASE(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
}
|
||||
else if (t->expires > when)
|
||||
{
|
||||
t->expires = when;
|
||||
HEAP_DECREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
HEAP_DECREASE(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BFD
|
||||
/* Hack to notify BFD loops */
|
||||
if ((loop != &main_timeloop) && (t->index == 1))
|
||||
wakeup_kick_current();
|
||||
#endif
|
||||
t->loop = local_timeloop;
|
||||
|
||||
if (t->index == 1)
|
||||
birdloop_ping(local_timeloop->loop);
|
||||
}
|
||||
|
||||
void
|
||||
tm_start(timer *t, btime after)
|
||||
tm_set_in(timer *t, btime when, struct birdloop *loop)
|
||||
{
|
||||
tm_set(t, current_time() + MAX(after, 0));
|
||||
ASSERT_DIE(birdloop_inside(loop));
|
||||
tm_set_in_tl(t, when, birdloop_time_loop(loop));
|
||||
}
|
||||
|
||||
void
|
||||
@ -178,20 +133,22 @@ tm_stop(timer *t)
|
||||
if (!t->expires)
|
||||
return;
|
||||
|
||||
struct timeloop *loop = timeloop_current();
|
||||
uint tc = timers_count(loop);
|
||||
TLOCK_TIMER_ASSERT(t->loop);
|
||||
|
||||
HEAP_DELETE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
BUFFER_POP(loop->timers);
|
||||
uint tc = timers_count(t->loop);
|
||||
|
||||
HEAP_DELETE(t->loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
|
||||
BUFFER_POP(t->loop->timers);
|
||||
|
||||
t->index = -1;
|
||||
t->expires = 0;
|
||||
t->loop = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
timers_init(struct timeloop *loop, pool *p)
|
||||
{
|
||||
times_init(loop);
|
||||
TLOCK_TIMER_ASSERT(loop);
|
||||
|
||||
BUFFER_INIT(loop->timers, p, 4);
|
||||
BUFFER_PUSH(loop->timers) = NULL;
|
||||
@ -200,13 +157,15 @@ timers_init(struct timeloop *loop, pool *p)
|
||||
void io_log_event(void *hook, void *data);
|
||||
|
||||
void
|
||||
timers_fire(struct timeloop *loop)
|
||||
timers_fire(struct timeloop *loop, int io_log)
|
||||
{
|
||||
TLOCK_TIMER_ASSERT(loop);
|
||||
|
||||
btime base_time;
|
||||
timer *t;
|
||||
|
||||
times_update(loop);
|
||||
base_time = loop->last_time;
|
||||
times_update();
|
||||
base_time = current_time();
|
||||
|
||||
while (t = timers_first(loop))
|
||||
{
|
||||
@ -217,19 +176,19 @@ timers_fire(struct timeloop *loop)
|
||||
{
|
||||
btime when = t->expires + t->recurrent;
|
||||
|
||||
if (when <= loop->last_time)
|
||||
when = loop->last_time + t->recurrent;
|
||||
if (when <= base_time)
|
||||
when = base_time + t->recurrent;
|
||||
|
||||
if (t->randomize)
|
||||
when += random() % (t->randomize + 1);
|
||||
|
||||
tm_set(t, when);
|
||||
tm_set_in_tl(t, when, loop);
|
||||
}
|
||||
else
|
||||
tm_stop(t);
|
||||
|
||||
/* This is ugly hack, we want to log just timers executed from the main I/O loop */
|
||||
if (loop == &main_timeloop)
|
||||
if (io_log)
|
||||
io_log_event(t->hook, t->data);
|
||||
|
||||
t->hook(t);
|
||||
@ -237,13 +196,6 @@ timers_fire(struct timeloop *loop)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
timer_init(void)
|
||||
{
|
||||
timers_init(&main_timeloop, &root_pool);
|
||||
timeloop_init_current();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tm_parse_time - parse a date and time
|
||||
|
40
lib/timer.h
40
lib/timer.h
@ -12,8 +12,14 @@
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/buffer.h"
|
||||
#include "lib/io-loop.h"
|
||||
#include "lib/locking.h"
|
||||
#include "lib/resource.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
extern _Atomic btime last_time;
|
||||
extern _Atomic btime real_time;
|
||||
|
||||
typedef struct timer
|
||||
{
|
||||
@ -25,36 +31,42 @@ typedef struct timer
|
||||
uint randomize; /* Amount of randomization */
|
||||
uint recurrent; /* Timer recurrence */
|
||||
|
||||
struct timeloop *loop; /* Loop where the timer is active */
|
||||
|
||||
int index;
|
||||
} timer;
|
||||
|
||||
struct timeloop
|
||||
{
|
||||
BUFFER_(timer *) timers;
|
||||
btime last_time;
|
||||
btime real_time;
|
||||
struct domain_generic *domain;
|
||||
struct birdloop *loop;
|
||||
};
|
||||
|
||||
#define TLOCK_TIMER_ASSERT(loop) ASSERT_DIE((loop)->domain && DG_IS_LOCKED((loop)->domain))
|
||||
#define TLOCK_LOCAL_ASSERT(loop) ASSERT_DIE(!(loop)->domain || DG_IS_LOCKED((loop)->domain))
|
||||
|
||||
static inline uint timers_count(struct timeloop *loop)
|
||||
{ return loop->timers.used - 1; }
|
||||
{ TLOCK_TIMER_ASSERT(loop); return loop->timers.used - 1; }
|
||||
|
||||
static inline timer *timers_first(struct timeloop *loop)
|
||||
{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
|
||||
{ TLOCK_TIMER_ASSERT(loop); return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
|
||||
|
||||
extern struct timeloop main_timeloop;
|
||||
|
||||
btime current_time(void);
|
||||
btime current_real_time(void);
|
||||
#define current_time() atomic_load_explicit(&last_time, memory_order_acquire)
|
||||
#define current_real_time() atomic_load_explicit(&real_time, memory_order_acquire)
|
||||
|
||||
//#define now (current_time() TO_S)
|
||||
//#define now_real (current_real_time() TO_S)
|
||||
extern btime boot_time;
|
||||
|
||||
timer *tm_new(pool *p);
|
||||
void tm_set(timer *t, btime when);
|
||||
void tm_start(timer *t, btime after);
|
||||
#define tm_set(t, when) tm_set_in((t), (when), &main_birdloop)
|
||||
#define tm_start(t, after) tm_start_in((t), (after), &main_birdloop)
|
||||
void tm_stop(timer *t);
|
||||
|
||||
void tm_set_in(timer *t, btime when, struct birdloop *loop);
|
||||
#define tm_start_in(t, after, loop) tm_set_in((t), (current_time() + MAX_((after), 0)), loop)
|
||||
|
||||
static inline int
|
||||
tm_active(timer *t)
|
||||
{
|
||||
@ -94,15 +106,11 @@ tm_start_max(timer *t, btime after)
|
||||
}
|
||||
|
||||
/* In sysdep code */
|
||||
void times_init(struct timeloop *loop);
|
||||
void times_update(struct timeloop *loop);
|
||||
void times_update_real_time(struct timeloop *loop);
|
||||
void times_update(void);
|
||||
|
||||
/* For I/O loop */
|
||||
void timers_init(struct timeloop *loop, pool *p);
|
||||
void timers_fire(struct timeloop *loop);
|
||||
|
||||
void timer_init(void);
|
||||
void timers_fire(struct timeloop *loop, int io_log);
|
||||
|
||||
|
||||
struct timeformat {
|
||||
|
@ -1,6 +1,6 @@
|
||||
Summary: BIRD Internet Routing Daemon
|
||||
Name: bird
|
||||
Version: 2.0.9
|
||||
Version: 2.0.10
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Networking/Daemons
|
||||
|
@ -6,7 +6,11 @@ $(call proto-build,dev_build)
|
||||
|
||||
$(proto-build-c): $(lastword $(MAKEFILE_LIST))
|
||||
$(E)echo GEN $@
|
||||
$(Q)echo "#include \"lib/birdlib.h\"\n$(patsubst %,void %(void);\n,$(PROTO_BUILD)) void protos_build_gen(void) { $(patsubst %, %();\n,$(PROTO_BUILD))}" > $@
|
||||
$(Q)echo "#include \"lib/birdlib.h\"" > $@
|
||||
$(Q)$(patsubst %,echo 'void %(void);' >> $@;,$(PROTO_BUILD))
|
||||
$(Q)echo "void protos_build_gen(void) {" >> $@
|
||||
$(Q)$(patsubst %,echo ' %();'>>$@;,$(PROTO_BUILD))
|
||||
$(Q)echo "}" >> $@
|
||||
|
||||
tests_src :=
|
||||
tests_targets := $(tests_targets) $(tests-target-files)
|
||||
|
@ -125,7 +125,7 @@ CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
|
||||
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS)
|
||||
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
|
||||
CF_KEYWORDS(CHECK, LINK)
|
||||
CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME)
|
||||
CF_KEYWORDS(CORK, SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD)
|
||||
|
||||
/* For r_args_channel */
|
||||
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
|
||||
@ -165,7 +165,7 @@ rtrid:
|
||||
|
||||
idval:
|
||||
NUM { $$ = $1; }
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
|
||||
| IP4 { $$ = ip4_to_u32($1); }
|
||||
| CF_SYM_KNOWN {
|
||||
if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD))
|
||||
@ -229,6 +229,12 @@ table_opt:
|
||||
}
|
||||
| MIN SETTLE TIME expr_us { this_table->min_settle_time = $4; }
|
||||
| MAX SETTLE TIME expr_us { this_table->max_settle_time = $4; }
|
||||
| GC THRESHOLD expr { this_table->gc_threshold = $3; }
|
||||
| GC PERIOD expr_us { this_table->gc_period = (uint) $3; if ($3 > 3600 S_) cf_error("GC period must be at most 3600 s"); }
|
||||
| CORK THRESHOLD expr expr {
|
||||
if ($3 > $4) cf_error("Cork low threshold must be lower than the high threshold.");
|
||||
this_table->cork_threshold.low = $3;
|
||||
this_table->cork_threshold.high = $4; }
|
||||
;
|
||||
|
||||
table_opts:
|
||||
@ -384,7 +390,7 @@ debug_default:
|
||||
DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
|
||||
| DEBUG CHANNELS debug_mask { new_config->channel_default_debug = $3; }
|
||||
| DEBUG COMMANDS expr { new_config->cli_debug = $3; }
|
||||
| DEBUG PIPE bool { new_config->pipe_debug = $3; }
|
||||
| DEBUG TABLES bool { new_config->table_debug = $3; }
|
||||
;
|
||||
|
||||
/* MRTDUMP PROTOCOLS is in systep/unix/config.Y */
|
||||
@ -470,6 +476,7 @@ proto: dev_proto '}' ;
|
||||
dev_proto_start: proto_start DIRECT {
|
||||
this_proto = proto_config_new(&proto_device, $1);
|
||||
init_list(&DIRECT_CFG->iface_list);
|
||||
this_proto->late_if_feed = 1;
|
||||
}
|
||||
;
|
||||
|
||||
@ -875,7 +882,7 @@ CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
|
||||
{ filters_dump_all(); cli_msg(0, ""); } ;
|
||||
|
||||
CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
|
||||
{ cmd_eval(f_linearize($2)); } ;
|
||||
{ cmd_eval(f_linearize($2, 1)); } ;
|
||||
|
||||
CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])
|
||||
CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug|trace|info|remote|warning|error|auth [, ...] }) [<buffer-size>], [[Control echoing of log messages]]) {
|
||||
|
160
nest/proto.c
160
nest/proto.c
@ -57,7 +57,28 @@ static void channel_feed_end(struct channel *c);
|
||||
static void channel_export_stopped(struct rt_export_request *req);
|
||||
|
||||
static inline int proto_is_done(struct proto *p)
|
||||
{ return (p->proto_state == PS_DOWN) && (p->active_channels == 0); }
|
||||
{ return (p->proto_state == PS_DOWN) && proto_is_inactive(p); }
|
||||
|
||||
static inline event_list *proto_event_list(struct proto *p)
|
||||
{ return p->loop == &main_birdloop ? &global_event_list : birdloop_event_list(p->loop); }
|
||||
|
||||
static inline event_list *proto_work_list(struct proto *p)
|
||||
{ return p->loop == &main_birdloop ? &global_work_list : birdloop_event_list(p->loop); }
|
||||
|
||||
static inline void proto_send_event(struct proto *p)
|
||||
{ ev_send(proto_event_list(p), p->event); }
|
||||
|
||||
#define PROTO_ENTER_FROM_MAIN(p) ({ \
|
||||
ASSERT_DIE(birdloop_inside(&main_birdloop)); \
|
||||
struct birdloop *_loop = (p)->loop; \
|
||||
if (_loop != &main_birdloop) birdloop_enter(_loop); \
|
||||
_loop; \
|
||||
})
|
||||
|
||||
#define PROTO_LEAVE_FROM_MAIN(loop) ({ if (loop != &main_birdloop) birdloop_leave(loop); })
|
||||
|
||||
#define PROTO_LOCKED_FROM_MAIN(p) for (struct birdloop *_proto_loop = PROTO_ENTER_FROM_MAIN(p); _proto_loop; PROTO_LEAVE_FROM_MAIN(_proto_loop), (_proto_loop = NULL))
|
||||
|
||||
|
||||
static inline int channel_is_active(struct channel *c)
|
||||
{ return (c->channel_state != CS_DOWN); }
|
||||
@ -438,7 +459,6 @@ channel_start_import(struct channel *c)
|
||||
.dump_req = channel_dump_import_req,
|
||||
.log_state_change = channel_import_log_state_change,
|
||||
.preimport = channel_preimport,
|
||||
.rte_modify = c->proto->rte_modify,
|
||||
};
|
||||
|
||||
ASSERT(c->channel_state == CS_UP);
|
||||
@ -468,6 +488,7 @@ channel_start_export(struct channel *c)
|
||||
|
||||
c->out_req = (struct rt_export_request) {
|
||||
.name = rn,
|
||||
.list = proto_work_list(c->proto),
|
||||
.addr = c->out_subprefix,
|
||||
.addr_mode = c->out_subprefix ? TE_ADDR_IN : TE_ADDR_NONE,
|
||||
.trace_routes = c->debug | c->proto->debug,
|
||||
@ -514,7 +535,7 @@ channel_check_stopped(struct channel *c)
|
||||
return;
|
||||
|
||||
channel_set_state(c, CS_DOWN);
|
||||
ev_schedule(c->proto->event);
|
||||
proto_send_event(c->proto);
|
||||
|
||||
break;
|
||||
case CS_PAUSE:
|
||||
@ -634,6 +655,7 @@ channel_setup_in_table(struct channel *c)
|
||||
{
|
||||
c->reload_req = (struct rt_export_request) {
|
||||
.name = mb_sprintf(c->proto->pool, "%s.%s.import", c->proto->name, c->name),
|
||||
.list = proto_work_list(c->proto),
|
||||
.trace_routes = c->debug | c->proto->debug,
|
||||
.export_bulk = channel_reload_export_bulk,
|
||||
.dump_req = channel_reload_dump_req,
|
||||
@ -698,8 +720,6 @@ channel_do_stop(struct channel *c)
|
||||
|
||||
CALL(c->channel->shutdown, c);
|
||||
|
||||
/* This have to be done in here, as channel pool is freed before channel_do_down() */
|
||||
c->out_table = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -721,7 +741,7 @@ channel_do_down(struct channel *c)
|
||||
|
||||
/* Schedule protocol shutddown */
|
||||
if (proto_is_done(c->proto))
|
||||
ev_schedule(c->proto->event);
|
||||
proto_send_event(c->proto);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1032,18 +1052,35 @@ proto_configure_channel(struct proto *p, struct channel **pc, struct channel_con
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
proto_cleanup(struct proto *p)
|
||||
{
|
||||
rfree(p->pool);
|
||||
p->pool = NULL;
|
||||
|
||||
p->active = 0;
|
||||
proto_log_state_change(p);
|
||||
proto_rethink_goal(p);
|
||||
}
|
||||
|
||||
static void
|
||||
proto_loop_stopped(void *ptr)
|
||||
{
|
||||
struct proto *p = ptr;
|
||||
|
||||
birdloop_enter(&main_birdloop);
|
||||
|
||||
p->loop = &main_birdloop;
|
||||
proto_cleanup(p);
|
||||
|
||||
birdloop_leave(&main_birdloop);
|
||||
}
|
||||
|
||||
static void
|
||||
proto_event(void *ptr)
|
||||
{
|
||||
struct proto *p = ptr;
|
||||
|
||||
if (p->do_start)
|
||||
{
|
||||
if_feed_baby(p);
|
||||
p->do_start = 0;
|
||||
}
|
||||
|
||||
if (p->do_stop)
|
||||
{
|
||||
if (p->proto == &proto_unix_iface)
|
||||
@ -1052,14 +1089,10 @@ proto_event(void *ptr)
|
||||
}
|
||||
|
||||
if (proto_is_done(p))
|
||||
{
|
||||
rfree(p->pool);
|
||||
p->pool = NULL;
|
||||
|
||||
p->active = 0;
|
||||
proto_log_state_change(p);
|
||||
proto_rethink_goal(p);
|
||||
}
|
||||
if (p->loop != &main_birdloop)
|
||||
birdloop_stop_self(p->loop, proto_loop_stopped, p);
|
||||
else
|
||||
proto_cleanup(p);
|
||||
}
|
||||
|
||||
|
||||
@ -1100,6 +1133,7 @@ proto_init(struct proto_config *c, node *n)
|
||||
struct protocol *pr = c->protocol;
|
||||
struct proto *p = pr->init(c);
|
||||
|
||||
p->loop = &main_birdloop;
|
||||
p->proto_state = PS_DOWN;
|
||||
p->last_state_change = current_time();
|
||||
p->vrf = c->vrf;
|
||||
@ -1116,11 +1150,19 @@ proto_init(struct proto_config *c, node *n)
|
||||
static void
|
||||
proto_start(struct proto *p)
|
||||
{
|
||||
/* Here we cannot use p->cf->name since it won't survive reconfiguration */
|
||||
p->pool = rp_new(proto_pool, p->proto->name);
|
||||
DBG("Kicking %s up\n", p->name);
|
||||
PD(p, "Starting");
|
||||
|
||||
p->pool = rp_newf(proto_pool, "Protocol %s", p->cf->name);
|
||||
|
||||
if (graceful_restart_state == GRS_INIT)
|
||||
p->gr_recovery = 1;
|
||||
|
||||
if (p->cf->loop_order != DOMAIN_ORDER(the_bird))
|
||||
p->loop = birdloop_new(p->pool, p->cf->loop_order, p->pool->name);
|
||||
|
||||
PROTO_LOCKED_FROM_MAIN(p)
|
||||
proto_notify_state(p, (p->proto->start ? p->proto->start(p) : PS_UP));
|
||||
}
|
||||
|
||||
|
||||
@ -1156,6 +1198,7 @@ proto_config_new(struct protocol *pr, int class)
|
||||
cf->class = class;
|
||||
cf->debug = new_config->proto_default_debug;
|
||||
cf->mrtdump = new_config->proto_default_mrtdump;
|
||||
cf->loop_order = DOMAIN_ORDER(the_bird);
|
||||
|
||||
init_list(&cf->channels);
|
||||
|
||||
@ -1444,12 +1487,21 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
|
||||
proto_rethink_goal(p);
|
||||
}
|
||||
|
||||
static void
|
||||
proto_shutdown(struct proto *p)
|
||||
{
|
||||
if (p->proto_state == PS_START || p->proto_state == PS_UP)
|
||||
{
|
||||
/* Going down */
|
||||
DBG("Kicking %s down\n", p->name);
|
||||
PD(p, "Shutting down");
|
||||
proto_notify_state(p, (p->proto->shutdown ? p->proto->shutdown(p) : PS_DOWN));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
proto_rethink_goal(struct proto *p)
|
||||
{
|
||||
struct protocol *q;
|
||||
byte goal;
|
||||
|
||||
if (p->reconfiguring && !p->active)
|
||||
{
|
||||
struct proto_config *nc = p->cf_new;
|
||||
@ -1469,32 +1521,12 @@ proto_rethink_goal(struct proto *p)
|
||||
|
||||
/* Determine what state we want to reach */
|
||||
if (p->disabled || p->reconfiguring)
|
||||
goal = PS_DOWN;
|
||||
else
|
||||
goal = PS_UP;
|
||||
|
||||
q = p->proto;
|
||||
if (goal == PS_UP)
|
||||
{
|
||||
if (!p->active)
|
||||
{
|
||||
/* Going up */
|
||||
DBG("Kicking %s up\n", p->name);
|
||||
PD(p, "Starting");
|
||||
proto_start(p);
|
||||
proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p->proto_state == PS_START || p->proto_state == PS_UP)
|
||||
{
|
||||
/* Going down */
|
||||
DBG("Kicking %s down\n", p->name);
|
||||
PD(p, "Shutting down");
|
||||
proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
|
||||
}
|
||||
PROTO_LOCKED_FROM_MAIN(p)
|
||||
proto_shutdown(p);
|
||||
}
|
||||
else if (!p->active)
|
||||
proto_start(p);
|
||||
}
|
||||
|
||||
struct proto *
|
||||
@ -1699,7 +1731,7 @@ protos_dump_all(void)
|
||||
#define DPF(x) (p->x ? " " #x : "")
|
||||
debug(" protocol %s (%p) state %s with %d active channels flags: %s%s%s%s%s\n",
|
||||
p->name, p, p_states[p->proto_state], p->active_channels,
|
||||
DPF(disabled), DPF(active), DPF(do_start), DPF(do_stop), DPF(reconfiguring));
|
||||
DPF(disabled), DPF(active), DPF(do_stop), DPF(reconfiguring));
|
||||
#undef DPF
|
||||
|
||||
struct channel *c;
|
||||
@ -1935,8 +1967,8 @@ static inline void
|
||||
proto_do_start(struct proto *p)
|
||||
{
|
||||
p->active = 1;
|
||||
p->do_start = 1;
|
||||
ev_schedule(p->event);
|
||||
if (!p->cf->late_if_feed)
|
||||
if_feed_baby(p);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1949,6 +1981,9 @@ proto_do_up(struct proto *p)
|
||||
}
|
||||
|
||||
proto_start_channels(p);
|
||||
|
||||
if (p->cf->late_if_feed)
|
||||
if_feed_baby(p);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -1963,9 +1998,6 @@ proto_do_stop(struct proto *p)
|
||||
p->down_sched = 0;
|
||||
p->gr_recovery = 0;
|
||||
|
||||
p->do_stop = 1;
|
||||
ev_schedule(p->event);
|
||||
|
||||
if (p->main_source)
|
||||
{
|
||||
rt_unlock_source(p->main_source);
|
||||
@ -1973,6 +2005,9 @@ proto_do_stop(struct proto *p)
|
||||
}
|
||||
|
||||
proto_stop_channels(p);
|
||||
|
||||
p->do_stop = 1;
|
||||
proto_send_event(p);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1983,7 +2018,7 @@ proto_do_down(struct proto *p)
|
||||
|
||||
/* Shutdown is finished in the protocol event */
|
||||
if (proto_is_done(p))
|
||||
ev_schedule(p->event);
|
||||
proto_send_event(p);
|
||||
}
|
||||
|
||||
|
||||
@ -2222,7 +2257,7 @@ proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED)
|
||||
p->disabled = 1;
|
||||
p->down_code = PDC_CMD_DISABLE;
|
||||
proto_set_message(p, (char *) arg, -1);
|
||||
proto_rethink_goal(p);
|
||||
proto_shutdown(p);
|
||||
cli_msg(-9, "%s: disabled", p->name);
|
||||
}
|
||||
|
||||
@ -2255,9 +2290,9 @@ proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED)
|
||||
p->disabled = 1;
|
||||
p->down_code = PDC_CMD_RESTART;
|
||||
proto_set_message(p, (char *) arg, -1);
|
||||
proto_rethink_goal(p);
|
||||
proto_shutdown(p);
|
||||
p->disabled = 0;
|
||||
proto_rethink_goal(p);
|
||||
/* After the protocol shuts down, proto_rethink_goal() is run from proto_event. */
|
||||
cli_msg(-12, "%s: restarted", p->name);
|
||||
}
|
||||
|
||||
@ -2332,7 +2367,9 @@ proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uint
|
||||
|
||||
if (s->proto->proto)
|
||||
{
|
||||
cmd(s->proto->proto, arg, 0);
|
||||
struct proto *p = s->proto->proto;
|
||||
PROTO_LOCKED_FROM_MAIN(p)
|
||||
cmd(p, arg, 0);
|
||||
cli_msg(0, "");
|
||||
}
|
||||
else
|
||||
@ -2347,7 +2384,8 @@ proto_apply_cmd_patt(const char *patt, void (* cmd)(struct proto *, uintptr_t, i
|
||||
|
||||
WALK_LIST(p, proto_list)
|
||||
if (!patt || patmatch(patt, p->name))
|
||||
cmd(p, arg, cnt++);
|
||||
PROTO_LOCKED_FROM_MAIN(p)
|
||||
cmd(p, arg, cnt++);
|
||||
|
||||
if (!cnt)
|
||||
cli_msg(8003, "No protocols match");
|
||||
|
@ -102,8 +102,10 @@ struct proto_config {
|
||||
u8 net_type; /* Protocol network type (NET_*), 0 for undefined */
|
||||
u8 disabled; /* Protocol enabled/disabled by default */
|
||||
u8 vrf_set; /* Related VRF instance (below) is defined */
|
||||
u8 late_if_feed; /* Delay interface feed after channels are up */
|
||||
u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */
|
||||
u32 router_id; /* Protocol specific router ID */
|
||||
uint loop_order; /* Launch a birdloop on this locking level; use DOMAIN_ORDER(the_bird) for mainloop */
|
||||
|
||||
list channels; /* List of channel configs (struct channel_config) */
|
||||
struct iface *vrf; /* Related VRF instance, NULL if global */
|
||||
@ -121,6 +123,7 @@ struct proto {
|
||||
struct proto_config *cf_new; /* Configuration we want to switch to after shutdown (NULL=delete) */
|
||||
pool *pool; /* Pool containing local objects */
|
||||
event *event; /* Protocol event */
|
||||
struct birdloop *loop; /* BIRDloop running this protocol */
|
||||
|
||||
list channels; /* List of channels to rtables (struct channel) */
|
||||
struct channel *main_channel; /* Primary channel */
|
||||
@ -131,12 +134,12 @@ struct proto {
|
||||
u32 debug; /* Debugging flags */
|
||||
u32 mrtdump; /* MRTDump flags */
|
||||
uint active_channels; /* Number of active channels */
|
||||
uint active_loops; /* Number of active IO loops */
|
||||
byte net_type; /* Protocol network type (NET_*), 0 for undefined */
|
||||
byte disabled; /* Manually disabled */
|
||||
byte vrf_set; /* Related VRF instance (above) is defined */
|
||||
byte proto_state; /* Protocol state machine (PS_*, see below) */
|
||||
byte active; /* From PS_START to cleanup after PS_STOP */
|
||||
byte do_start; /* Start actions are scheduled */
|
||||
byte do_stop; /* Stop actions are scheduled */
|
||||
byte reconfiguring; /* We're shutting down due to reconfiguration */
|
||||
byte gr_recovery; /* Protocol should participate in graceful restart recovery */
|
||||
@ -189,7 +192,6 @@ struct proto {
|
||||
int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *);
|
||||
int (*rte_better)(struct rte *, struct rte *);
|
||||
int (*rte_mergable)(struct rte *, struct rte *);
|
||||
struct rte *(*rte_modify)(struct rte *, struct linpool *);
|
||||
void (*rte_insert)(struct network *, struct rte *);
|
||||
void (*rte_remove)(struct network *, struct rte *);
|
||||
u32 (*rte_igp_metric)(const struct rte *);
|
||||
@ -339,6 +341,8 @@ void proto_notify_state(struct proto *p, unsigned state);
|
||||
* as a result of received ROUTE-REFRESH request).
|
||||
*/
|
||||
|
||||
static inline int proto_is_inactive(struct proto *p)
|
||||
{ return (p->active_channels == 0) && (p->active_loops == 0); }
|
||||
|
||||
|
||||
/*
|
||||
|
@ -77,7 +77,11 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
|
||||
e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
|
||||
|
||||
if (d->verbose)
|
||||
{
|
||||
ea_show_list(c, a);
|
||||
cli_printf(c, -1008, "\tInternal route handling values: %uL %uG %uS id %u",
|
||||
e->src->private_id, e->src->global_id, e->stale_cycle, e->id);
|
||||
}
|
||||
else if (dest == RTD_UNICAST)
|
||||
ea_show_nexthop_list(c, nhad);
|
||||
else if (had)
|
||||
@ -255,6 +259,18 @@ rt_show_dump_req(struct rt_export_request *req)
|
||||
debug(" CLI Show Route Feed %p\n", req);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_show_done(struct rt_show_data *d)
|
||||
{
|
||||
/* No more action */
|
||||
d->cli->cleanup = NULL;
|
||||
d->cli->cont = NULL;
|
||||
d->cli->rover = NULL;
|
||||
|
||||
/* Write pending messages */
|
||||
cli_write_trigger(d->cli);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_show_cont(struct rt_show_data *d)
|
||||
{
|
||||
@ -263,18 +279,13 @@ rt_show_cont(struct rt_show_data *d)
|
||||
if (d->running_on_config && (d->running_on_config != config))
|
||||
{
|
||||
cli_printf(c, 8004, "Stopped due to reconfiguration");
|
||||
|
||||
/* No more action */
|
||||
c->cleanup = NULL;
|
||||
c->cont = NULL;
|
||||
c->rover = NULL;
|
||||
cli_write_trigger(c);
|
||||
return;
|
||||
return rt_show_done(d);
|
||||
}
|
||||
|
||||
d->req = (struct rt_export_request) {
|
||||
.addr = d->addr,
|
||||
.name = "CLI Show Route",
|
||||
.list = &global_work_list,
|
||||
.export_bulk = rt_show_net_export_bulk,
|
||||
.dump_req = rt_show_dump_req,
|
||||
.log_state_change = rt_show_log_state_change,
|
||||
@ -316,7 +327,6 @@ rt_show_export_stopped(struct rt_export_request *req)
|
||||
if (NODE_VALID(d->tab))
|
||||
return rt_show_cont(d);
|
||||
|
||||
|
||||
/* Printout total stats */
|
||||
if (d->stats && (d->table_counter > 1))
|
||||
{
|
||||
@ -329,7 +339,8 @@ rt_show_export_stopped(struct rt_export_request *req)
|
||||
else
|
||||
cli_printf(d->cli, 0, "");
|
||||
|
||||
cli_write_trigger(d->cli);
|
||||
/* No more route showing */
|
||||
rt_show_done(d);
|
||||
}
|
||||
|
||||
struct rt_show_data_rtable *
|
||||
|
1181
nest/rt-table.c
1181
nest/rt-table.c
File diff suppressed because it is too large
Load Diff
108
nest/rt.h
108
nest/rt.h
@ -17,6 +17,10 @@
|
||||
#include "lib/type.h"
|
||||
#include "lib/fib.h"
|
||||
#include "lib/route.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/rcu.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
struct ea_list;
|
||||
struct protocol;
|
||||
@ -30,6 +34,10 @@ struct f_trie;
|
||||
struct f_trie_walk_state;
|
||||
struct cli;
|
||||
|
||||
struct rt_cork_threshold {
|
||||
u64 low, high;
|
||||
};
|
||||
|
||||
/*
|
||||
* Master Routing Tables. Generally speaking, each of them contains a FIB
|
||||
* with each entry pointing to a list of route entries representing routes
|
||||
@ -47,13 +55,14 @@ struct rtable_config {
|
||||
struct rtable *table;
|
||||
struct proto_config *krt_attached; /* Kernel syncer attached to this table */
|
||||
uint addr_type; /* Type of address data stored in table (NET_*) */
|
||||
int gc_max_ops; /* Maximum number of operations before GC is run */
|
||||
int gc_min_time; /* Minimum time between two consecutive GC runs */
|
||||
uint gc_threshold; /* Maximum number of operations before GC is run */
|
||||
uint gc_period; /* Approximate time between two consecutive GC runs */
|
||||
byte sorted; /* Routes of network are sorted according to rte_better() */
|
||||
byte internal; /* Internal table of a protocol */
|
||||
byte trie_used; /* Rtable has attached trie */
|
||||
btime min_settle_time; /* Minimum settle time for notifications */
|
||||
btime max_settle_time; /* Maximum settle time for notifications */
|
||||
btime export_settle_time; /* Delay before exports are announced */
|
||||
struct rt_cork_threshold cork_threshold; /* Cork threshold values */
|
||||
};
|
||||
|
||||
struct rt_export_hook;
|
||||
@ -62,9 +71,17 @@ struct rt_export_request;
|
||||
struct rt_exporter {
|
||||
list hooks; /* Registered route export hooks */
|
||||
uint addr_type; /* Type of address data exported (NET_*) */
|
||||
|
||||
struct rt_export_hook *(*start)(struct rt_exporter *, struct rt_export_request *);
|
||||
void (*stop)(struct rt_export_hook *);
|
||||
void (*done)(struct rt_export_hook *);
|
||||
void (*used)(struct rt_exporter *);
|
||||
|
||||
list pending; /* List of packed struct rt_pending_export */
|
||||
struct timer *export_timer;
|
||||
|
||||
struct rt_pending_export *first; /* First export to announce */
|
||||
u64 next_seq; /* The next export will have this ID */
|
||||
};
|
||||
|
||||
typedef struct rtable {
|
||||
@ -90,15 +107,21 @@ typedef struct rtable {
|
||||
* obstacle from this routing table.
|
||||
*/
|
||||
struct event *rt_event; /* Routing table event */
|
||||
struct event *uncork_event; /* Called when uncork happens */
|
||||
struct timer *prune_timer; /* Timer for periodic pruning / GC */
|
||||
btime last_rt_change; /* Last time when route changed */
|
||||
btime base_settle_time; /* Start time of rtable settling interval */
|
||||
btime gc_time; /* Time of last GC */
|
||||
int gc_counter; /* Number of operations since last GC */
|
||||
uint gc_counter; /* Number of operations since last GC */
|
||||
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
|
||||
byte prune_trie; /* Prune prefix trie during next table prune */
|
||||
byte hcu_scheduled; /* Hostcache update is scheduled */
|
||||
byte hcu_corked; /* Hostcache update is corked with this state */
|
||||
byte nhu_state; /* Next Hop Update state */
|
||||
byte internal; /* This table is internal for some other object */
|
||||
byte nhu_corked; /* Next Hop Update is corked with this state */
|
||||
byte export_used; /* Pending Export pruning is scheduled */
|
||||
byte cork_active; /* Cork has been activated */
|
||||
struct rt_cork_threshold cork_threshold; /* Threshold for table cork */
|
||||
struct fib_iterator prune_fit; /* Rtable prune FIB iterator */
|
||||
struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */
|
||||
struct f_trie *trie_new; /* New prefix trie defined during pruning */
|
||||
@ -127,13 +150,48 @@ struct rt_flowspec_link {
|
||||
u32 uc;
|
||||
};
|
||||
|
||||
extern struct rt_cork {
|
||||
_Atomic uint active;
|
||||
event_list queue;
|
||||
event run;
|
||||
} rt_cork;
|
||||
|
||||
static inline void rt_cork_acquire(void)
|
||||
{
|
||||
atomic_fetch_add_explicit(&rt_cork.active, 1, memory_order_acq_rel);
|
||||
}
|
||||
|
||||
static inline void rt_cork_release(void)
|
||||
{
|
||||
if (atomic_fetch_sub_explicit(&rt_cork.active, 1, memory_order_acq_rel) == 1)
|
||||
{
|
||||
synchronize_rcu();
|
||||
ev_schedule_work(&rt_cork.run);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int rt_cork_check(event *e)
|
||||
{
|
||||
rcu_read_lock();
|
||||
|
||||
int corked = (atomic_load_explicit(&rt_cork.active, memory_order_acquire) > 0);
|
||||
if (corked)
|
||||
ev_send(&rt_cork.queue, e);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return corked;
|
||||
}
|
||||
|
||||
|
||||
#define NHU_CLEAN 0
|
||||
#define NHU_SCHEDULED 1
|
||||
#define NHU_RUNNING 2
|
||||
#define NHU_DIRTY 3
|
||||
|
||||
typedef struct network {
|
||||
struct rte_storage *routes; /* Available routes for this network */
|
||||
struct rte_storage *routes; /* Available routes for this network */
|
||||
struct rt_pending_export *first, *last;
|
||||
struct fib_node n; /* FIB flags reserved for kernel syncer */
|
||||
} net;
|
||||
|
||||
@ -168,8 +226,10 @@ struct rte_storage {
|
||||
struct rte rte; /* Route data */
|
||||
};
|
||||
|
||||
#define RTE_COPY(r, l) ((r) ? (((*(l)) = (r)->rte), (l)) : NULL)
|
||||
#define RTE_OR_NULL(r) ((r) ? &((r)->rte) : NULL)
|
||||
#define RTE_COPY(r) ((r) ? (r)->rte : (rte) {})
|
||||
#define RTE_COPY_VALID(r) (((r) && (rte_is_valid(&(r)->rte))) ? (r)->rte : (rte) {})
|
||||
#define RTE_OR_NULL(r) ((r) ? &((r)->rte) : NULL)
|
||||
#define RTE_VALID_OR_NULL(r) (((r) && (rte_is_valid(&(r)->rte))) ? &((r)->rte) : NULL)
|
||||
|
||||
/* Table-channel connections */
|
||||
|
||||
@ -183,7 +243,6 @@ struct rt_import_request {
|
||||
/* Preimport is called when the @new route is just-to-be inserted, replacing @old.
|
||||
* Return a route (may be different or modified in-place) to continue or NULL to withdraw. */
|
||||
int (*preimport)(struct rt_import_request *req, struct rte *new, struct rte *old);
|
||||
struct rte *(*rte_modify)(struct rte *, struct linpool *);
|
||||
};
|
||||
|
||||
struct rt_import_hook {
|
||||
@ -200,15 +259,22 @@ struct rt_import_hook {
|
||||
u32 withdraws_accepted; /* Number of route withdraws accepted and processed */
|
||||
} stats;
|
||||
|
||||
u64 flush_seq; /* Table export seq when the channel announced flushing */
|
||||
btime last_state_change; /* Time of last state transition */
|
||||
|
||||
u8 import_state; /* IS_* */
|
||||
u8 stale_set; /* Set this stale_cycle to imported routes */
|
||||
u8 stale_valid; /* Routes with this stale_cycle and bigger are considered valid */
|
||||
u8 stale_pruned; /* Last prune finished when this value was set at stale_valid */
|
||||
u8 stale_pruning; /* Last prune started when this value was set at stale_valid */
|
||||
|
||||
void (*stopped)(struct rt_import_request *); /* Stored callback when import is stopped */
|
||||
};
|
||||
|
||||
struct rt_pending_export {
|
||||
struct rt_pending_export * _Atomic next; /* Next export for the same destination */
|
||||
struct rte_storage *new, *new_best, *old, *old_best;
|
||||
u64 seq; /* Sequential ID (table-local) of the pending export */
|
||||
};
|
||||
|
||||
struct rt_export_request {
|
||||
@ -218,6 +284,8 @@ struct rt_export_request {
|
||||
u8 trace_routes;
|
||||
u8 addr_mode; /* Network prefilter mode (TE_ADDR_*) */
|
||||
|
||||
event_list *list; /* Where to schedule export events */
|
||||
|
||||
/* There are two methods of export. You can either request feeding every single change
|
||||
* or feeding the whole route feed. In case of regular export, &export_one is preferred.
|
||||
* Anyway, when feeding, &export_bulk is preferred, falling back to &export_one.
|
||||
@ -237,7 +305,6 @@ struct rt_export_hook {
|
||||
struct rt_exporter *table; /* The connected table */
|
||||
|
||||
pool *pool;
|
||||
linpool *lp;
|
||||
|
||||
struct rt_export_request *req; /* The requestor */
|
||||
|
||||
@ -256,10 +323,15 @@ struct rt_export_hook {
|
||||
u32 hash_iter; /* Iterator over hash */
|
||||
};
|
||||
|
||||
struct bmap seq_map; /* Keep track which exports were already procesed */
|
||||
|
||||
struct rt_pending_export * _Atomic last_export;/* Last export processed */
|
||||
struct rt_pending_export *rpe_next; /* Next pending export to process */
|
||||
|
||||
btime last_state_change; /* Time of last state transition */
|
||||
|
||||
u8 refeed_pending; /* Refeeding and another refeed is scheduled */
|
||||
u8 export_state; /* Route export state (TES_*, see below) */
|
||||
_Atomic u8 export_state; /* Route export state (TES_*, see below) */
|
||||
u8 feed_type; /* Which feeding method is used (TFT_*, see below) */
|
||||
|
||||
struct event *event; /* Event running all the export operations */
|
||||
@ -310,6 +382,15 @@ void rt_set_export_state(struct rt_export_hook *hook, u8 state);
|
||||
|
||||
void rte_import(struct rt_import_request *req, const net_addr *net, rte *new, struct rte_src *src);
|
||||
|
||||
/* Get next rpe. If src is given, it must match. */
|
||||
struct rt_pending_export *rpe_next(struct rt_pending_export *rpe, struct rte_src *src);
|
||||
|
||||
/* Mark the pending export processed */
|
||||
void rpe_mark_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
|
||||
|
||||
/* Get pending export seen status */
|
||||
int rpe_get_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
|
||||
|
||||
/* Types of route announcement, also used as flags */
|
||||
#define RA_UNDEF 0 /* Undefined RA type */
|
||||
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
|
||||
@ -363,6 +444,7 @@ struct config;
|
||||
|
||||
void rt_init(void);
|
||||
void rt_preconfig(struct config *);
|
||||
void rt_postconfig(struct config *);
|
||||
void rt_commit(struct config *new, struct config *old);
|
||||
void rt_lock_table(rtable *);
|
||||
void rt_unlock_table(rtable *);
|
||||
@ -383,8 +465,8 @@ net *net_get(rtable *tab, const net_addr *addr);
|
||||
net *net_route(rtable *tab, const net_addr *n);
|
||||
int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
|
||||
rte *rt_export_merged(struct channel *c, rte ** feed, uint count, linpool *pool, int silent);
|
||||
void rt_refresh_begin(rtable *t, struct rt_import_request *);
|
||||
void rt_refresh_end(rtable *t, struct rt_import_request *);
|
||||
void rt_refresh_begin(struct rt_import_request *);
|
||||
void rt_refresh_end(struct rt_import_request *);
|
||||
void rt_modify_stale(rtable *t, struct rt_import_request *);
|
||||
void rt_schedule_prune(rtable *t);
|
||||
void rte_dump(struct rte_storage *);
|
||||
|
@ -315,7 +315,9 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
|
||||
|
||||
/* Found older */
|
||||
rem_node(NODE sr);
|
||||
rem_node(&sr->nbr_node);
|
||||
|
||||
if (sr->nbr)
|
||||
rem_node(&sr->nbr_node);
|
||||
|
||||
goto found;
|
||||
}
|
||||
@ -455,10 +457,7 @@ babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
|
||||
|
||||
struct babel_seqno_request *sr;
|
||||
WALK_LIST_FIRST2(sr, nbr_node, nbr->requests)
|
||||
{
|
||||
sr->nbr = NULL;
|
||||
rem_node(&sr->nbr_node);
|
||||
}
|
||||
babel_remove_seqno_request(p, sr);
|
||||
|
||||
nbr->ifa = NULL;
|
||||
rem_node(NODE nbr);
|
||||
@ -2258,9 +2257,9 @@ babel_kick_timer(struct babel_proto *p)
|
||||
|
||||
|
||||
static int
|
||||
babel_preexport(struct channel *c, struct rte *new)
|
||||
babel_preexport(struct channel *C, struct rte *new)
|
||||
{
|
||||
if (new->src->proto != c->proto)
|
||||
if (new->src->proto != C->proto)
|
||||
return 0;
|
||||
|
||||
/* Reject our own unreachable routes */
|
||||
|
@ -1,4 +1,4 @@
|
||||
src := bfd.c io.c packets.c
|
||||
src := bfd.c packets.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
$(cf-local)
|
||||
|
165
proto/bfd/bfd.c
165
proto/bfd/bfd.c
@ -113,8 +113,16 @@
|
||||
#define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2
|
||||
#define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n)
|
||||
|
||||
static list STATIC_LIST_INIT(bfd_proto_list);
|
||||
static list STATIC_LIST_INIT(bfd_wait_list);
|
||||
DEFINE_DOMAIN(rtable);
|
||||
#define BFD_LOCK LOCK_DOMAIN(rtable, bfd_global.lock)
|
||||
#define BFD_UNLOCK UNLOCK_DOMAIN(rtable, bfd_global.lock)
|
||||
|
||||
static struct {
|
||||
DOMAIN(rtable) lock;
|
||||
list wait_list;
|
||||
list pickup_list;
|
||||
list proto_list;
|
||||
} bfd_global;
|
||||
|
||||
const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" };
|
||||
|
||||
@ -188,7 +196,7 @@ bfd_session_update_tx_interval(struct bfd_session *s)
|
||||
return;
|
||||
|
||||
/* Set timer relative to last tx_timer event */
|
||||
tm_set(s->tx_timer, s->last_tx + tx_int_l);
|
||||
tm_set_in(s->tx_timer, s->last_tx + tx_int_l, s->ifa->bfd->p.loop);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -202,7 +210,7 @@ bfd_session_update_detection_time(struct bfd_session *s, int kick)
|
||||
if (!s->last_rx)
|
||||
return;
|
||||
|
||||
tm_set(s->hold_timer, s->last_rx + timeout);
|
||||
tm_set_in(s->hold_timer, s->last_rx + timeout, s->ifa->bfd->p.loop);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -226,7 +234,7 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset)
|
||||
if (reset || !tm_active(s->tx_timer))
|
||||
{
|
||||
s->last_tx = 0;
|
||||
tm_start(s->tx_timer, 0);
|
||||
tm_start_in(s->tx_timer, 0, s->ifa->bfd->p.loop);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -419,7 +427,7 @@ bfd_get_free_id(struct bfd_proto *p)
|
||||
static struct bfd_session *
|
||||
bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface, struct bfd_options *opts)
|
||||
{
|
||||
birdloop_enter(p->loop);
|
||||
ASSERT_DIE(birdloop_inside(p->p.loop));
|
||||
|
||||
struct bfd_iface *ifa = bfd_get_iface(p, local, iface);
|
||||
|
||||
@ -454,8 +462,6 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
|
||||
|
||||
TRACE(D_EVENTS, "Session to %I added", s->addr);
|
||||
|
||||
birdloop_leave(p->loop);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -463,38 +469,34 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
|
||||
static void
|
||||
bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
|
||||
{
|
||||
birdloop_enter(p->loop);
|
||||
birdloop_enter(p->p.loop);
|
||||
|
||||
s->opened = 1;
|
||||
|
||||
bfd_session_control_tx_timer(s);
|
||||
|
||||
birdloop_leave(p->loop);
|
||||
birdloop_leave(p->p.loop);
|
||||
}
|
||||
|
||||
static void
|
||||
bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
|
||||
{
|
||||
birdloop_enter(p->loop);
|
||||
birdloop_enter(p->p.loop);
|
||||
|
||||
s->opened = 0;
|
||||
|
||||
bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN);
|
||||
bfd_session_control_tx_timer(s);
|
||||
|
||||
birdloop_leave(p->loop);
|
||||
birdloop_leave(p->p.loop);
|
||||
}
|
||||
*/
|
||||
|
||||
static void
|
||||
bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
|
||||
bfd_remove_session_locked(struct bfd_proto *p, struct bfd_session *s)
|
||||
{
|
||||
ip_addr ip = s->addr;
|
||||
|
||||
/* Caller should ensure that request list is empty */
|
||||
|
||||
birdloop_enter(p->loop);
|
||||
|
||||
/* Remove session from notify list if scheduled for notification */
|
||||
/* No need for bfd_lock_sessions(), we are already protected by birdloop_enter() */
|
||||
if (NODE_VALID(&s->n))
|
||||
@ -508,11 +510,17 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
|
||||
HASH_REMOVE(p->session_hash_id, HASH_ID, s);
|
||||
HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
|
||||
|
||||
TRACE(D_EVENTS, "Session to %I removed", s->addr);
|
||||
|
||||
sl_free(s);
|
||||
}
|
||||
|
||||
TRACE(D_EVENTS, "Session to %I removed", ip);
|
||||
|
||||
birdloop_leave(p->loop);
|
||||
static void
|
||||
bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
|
||||
{
|
||||
birdloop_enter(p->p.loop);
|
||||
bfd_remove_session_locked(p, s);
|
||||
birdloop_leave(p->p.loop);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -521,7 +529,7 @@ bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
|
||||
if (EMPTY_LIST(s->request_list))
|
||||
return;
|
||||
|
||||
birdloop_enter(p->loop);
|
||||
birdloop_enter(p->p.loop);
|
||||
|
||||
struct bfd_request *req = SKIP_BACK(struct bfd_request, n, HEAD(s->request_list));
|
||||
s->cf = bfd_merge_options(s->ifa->cf, &req->opts);
|
||||
@ -534,7 +542,7 @@ bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
|
||||
|
||||
bfd_session_control_tx_timer(s, 0);
|
||||
|
||||
birdloop_leave(p->loop);
|
||||
birdloop_leave(p->p.loop);
|
||||
|
||||
TRACE(D_EVENTS, "Session to %I reconfigured", s->addr);
|
||||
}
|
||||
@ -627,9 +635,9 @@ bfd_reconfigure_iface(struct bfd_proto *p, struct bfd_iface *ifa, struct bfd_con
|
||||
(new->passive != old->passive);
|
||||
|
||||
/* This should be probably changed to not access ifa->cf from the BFD thread */
|
||||
birdloop_enter(p->loop);
|
||||
birdloop_enter(p->p.loop);
|
||||
ifa->cf = new;
|
||||
birdloop_leave(p->loop);
|
||||
birdloop_leave(p->p.loop);
|
||||
}
|
||||
|
||||
|
||||
@ -690,41 +698,68 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
|
||||
}
|
||||
|
||||
static void
|
||||
bfd_submit_request(struct bfd_request *req)
|
||||
bfd_pickup_requests(void *_data UNUSED)
|
||||
{
|
||||
node *n;
|
||||
WALK_LIST(n, bfd_global.proto_list)
|
||||
{
|
||||
struct bfd_proto *p = SKIP_BACK(struct bfd_proto, bfd_node, n);
|
||||
birdloop_enter(p->p.loop);
|
||||
BFD_LOCK;
|
||||
|
||||
WALK_LIST(n, bfd_proto_list)
|
||||
if (bfd_add_request(SKIP_BACK(struct bfd_proto, bfd_node, n), req))
|
||||
return;
|
||||
node *rn, *rnxt;
|
||||
WALK_LIST_DELSAFE(rn, rnxt, bfd_global.pickup_list)
|
||||
bfd_add_request(p, SKIP_BACK(struct bfd_request, n, rn));
|
||||
|
||||
rem_node(&req->n);
|
||||
add_tail(&bfd_wait_list, &req->n);
|
||||
req->session = NULL;
|
||||
bfd_request_notify(req, BFD_STATE_ADMIN_DOWN, 0);
|
||||
BFD_UNLOCK;
|
||||
birdloop_leave(p->p.loop);
|
||||
}
|
||||
|
||||
BFD_LOCK;
|
||||
node *rn, *rnxt;
|
||||
WALK_LIST_DELSAFE(rn, rnxt, bfd_global.pickup_list)
|
||||
{
|
||||
rem_node(rn);
|
||||
add_tail(&bfd_global.wait_list, rn);
|
||||
bfd_request_notify(SKIP_BACK(struct bfd_request, n, rn), BFD_STATE_ADMIN_DOWN, 0);
|
||||
}
|
||||
BFD_UNLOCK;
|
||||
}
|
||||
|
||||
static event bfd_pickup_event = { .hook = bfd_pickup_requests };
|
||||
|
||||
static void
|
||||
bfd_take_requests(struct bfd_proto *p)
|
||||
{
|
||||
node *n, *nn;
|
||||
|
||||
WALK_LIST_DELSAFE(n, nn, bfd_wait_list)
|
||||
BFD_LOCK;
|
||||
WALK_LIST_DELSAFE(n, nn, bfd_global.wait_list)
|
||||
bfd_add_request(p, SKIP_BACK(struct bfd_request, n, n));
|
||||
BFD_UNLOCK;
|
||||
}
|
||||
|
||||
static void
|
||||
bfd_drop_requests(struct bfd_proto *p)
|
||||
{
|
||||
node *n;
|
||||
|
||||
HASH_WALK(p->session_hash_id, next_id, s)
|
||||
BFD_LOCK;
|
||||
HASH_WALK_DELSAFE(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));
|
||||
{
|
||||
struct bfd_request *req = SKIP_BACK(struct bfd_request, n, n);
|
||||
rem_node(&req->n);
|
||||
add_tail(&bfd_global.pickup_list, &req->n);
|
||||
req->session = NULL;
|
||||
bfd_request_notify(req, BFD_STATE_ADMIN_DOWN, 0);
|
||||
}
|
||||
|
||||
ev_send(&global_event_list, &bfd_pickup_event);
|
||||
|
||||
bfd_remove_session_locked(p, s);
|
||||
}
|
||||
HASH_WALK_END;
|
||||
BFD_UNLOCK;
|
||||
}
|
||||
|
||||
static struct resclass bfd_request_class;
|
||||
@ -737,9 +772,6 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
|
||||
{
|
||||
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;
|
||||
@ -748,11 +780,16 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
|
||||
if (opts)
|
||||
req->opts = *opts;
|
||||
|
||||
bfd_submit_request(req);
|
||||
|
||||
req->hook = hook;
|
||||
req->data = data;
|
||||
|
||||
req->session = NULL;
|
||||
|
||||
BFD_LOCK;
|
||||
add_tail(&bfd_global.pickup_list, &req->n);
|
||||
ev_send(&global_event_list, &bfd_pickup_event);
|
||||
BFD_UNLOCK;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
@ -1023,10 +1060,10 @@ 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->tpool = rp_new(NULL, "BFD thread root");
|
||||
pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
p->tpool = rp_new(P->pool, "BFD loop pool");
|
||||
|
||||
p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
|
||||
HASH_INIT(p->session_hash_id, P->pool, 8);
|
||||
HASH_INIT(p->session_hash_ip, P->pool, 8);
|
||||
@ -1036,9 +1073,7 @@ bfd_start(struct proto *P)
|
||||
init_list(&p->notify_list);
|
||||
bfd_notify_init(p);
|
||||
|
||||
add_tail(&bfd_proto_list, &p->bfd_node);
|
||||
|
||||
birdloop_enter(p->loop);
|
||||
add_tail(&bfd_global.proto_list, &p->bfd_node);
|
||||
|
||||
if (!cf->strict_bind)
|
||||
{
|
||||
@ -1055,42 +1090,33 @@ bfd_start(struct proto *P)
|
||||
p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
|
||||
}
|
||||
|
||||
birdloop_leave(p->loop);
|
||||
|
||||
bfd_take_requests(p);
|
||||
|
||||
struct bfd_neighbor *n;
|
||||
WALK_LIST(n, cf->neigh_list)
|
||||
bfd_start_neighbor(p, n);
|
||||
|
||||
birdloop_start(p->loop);
|
||||
|
||||
return PS_UP;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
bfd_shutdown(struct 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->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);
|
||||
struct bfd_neighbor *bn;
|
||||
WALK_LIST(bn, cf->neigh_list)
|
||||
bfd_stop_neighbor(p, bn);
|
||||
|
||||
bfd_drop_requests(p);
|
||||
|
||||
/* FIXME: This is hack */
|
||||
birdloop_enter(p->loop);
|
||||
rfree(p->tpool);
|
||||
birdloop_leave(p->loop);
|
||||
|
||||
birdloop_free(p->loop);
|
||||
if (p->rx4_1) sk_stop(p->rx4_1);
|
||||
if (p->rx4_m) sk_stop(p->rx4_m);
|
||||
if (p->rx6_1) sk_stop(p->rx6_1);
|
||||
if (p->rx6_m) sk_stop(p->rx6_m);
|
||||
|
||||
return PS_DOWN;
|
||||
}
|
||||
@ -1111,7 +1137,7 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
|
||||
(new->strict_bind != old->strict_bind))
|
||||
return 0;
|
||||
|
||||
birdloop_mask_wakeups(p->loop);
|
||||
birdloop_mask_wakeups(p->p.loop);
|
||||
|
||||
WALK_LIST(ifa, p->iface_list)
|
||||
bfd_reconfigure_iface(p, ifa, new);
|
||||
@ -1125,7 +1151,7 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
|
||||
|
||||
bfd_reconfigure_neighbors(p, new);
|
||||
|
||||
birdloop_unmask_wakeups(p->loop);
|
||||
birdloop_unmask_wakeups(p->p.loop);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -1196,4 +1222,9 @@ void
|
||||
bfd_build(void)
|
||||
{
|
||||
proto_build(&proto_bfd);
|
||||
|
||||
bfd_global.lock = DOMAIN_NEW(rtable, "BFD Global");
|
||||
init_list(&bfd_global.wait_list);
|
||||
init_list(&bfd_global.pickup_list);
|
||||
init_list(&bfd_global.proto_list);
|
||||
}
|
||||
|
@ -17,12 +17,12 @@
|
||||
#include "nest/password.h"
|
||||
#include "conf/conf.h"
|
||||
#include "lib/hash.h"
|
||||
#include "lib/io-loop.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/socket.h"
|
||||
#include "lib/string.h"
|
||||
|
||||
#include "nest/bfd.h"
|
||||
#include "io.h"
|
||||
|
||||
|
||||
#define BFD_CONTROL_PORT 3784
|
||||
@ -88,9 +88,11 @@ struct bfd_neighbor
|
||||
struct bfd_proto
|
||||
{
|
||||
struct proto p;
|
||||
struct birdloop *loop;
|
||||
pool *tpool;
|
||||
|
||||
pthread_spinlock_t lock;
|
||||
|
||||
pool *tpool;
|
||||
|
||||
node bfd_node;
|
||||
|
||||
slab *session_slab;
|
||||
|
@ -37,6 +37,7 @@ proto: bfd_proto ;
|
||||
bfd_proto_start: proto_start BFD
|
||||
{
|
||||
this_proto = proto_config_new(&proto_bfd, $1);
|
||||
this_proto->loop_order = DOMAIN_ORDER(proto);
|
||||
init_list(&BFD_CFG->patt_list);
|
||||
init_list(&BFD_CFG->neigh_list);
|
||||
BFD_CFG->accept_ipv4 = BFD_CFG->accept_ipv6 = 1;
|
||||
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* BIRD -- I/O and event loop
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_BFD_IO_H_
|
||||
#define _BIRD_BFD_IO_H_
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/lists.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/socket.h"
|
||||
|
||||
|
||||
void ev2_schedule(event *e);
|
||||
|
||||
void sk_start(sock *s);
|
||||
void sk_stop(sock *s);
|
||||
|
||||
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);
|
||||
void birdloop_unmask_wakeups(struct birdloop *loop);
|
||||
|
||||
|
||||
#endif /* _BIRD_BFD_IO_H_ */
|
@ -412,7 +412,7 @@ bfd_err_hook(sock *sk, int err)
|
||||
sock *
|
||||
bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
|
||||
{
|
||||
sock *sk = sk_new(p->tpool);
|
||||
sock *sk = sk_new(p->p.pool);
|
||||
sk->type = SK_UDP;
|
||||
sk->subtype = af;
|
||||
sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
|
||||
@ -475,7 +475,7 @@ bfd_open_rx_sk_bound(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)
|
||||
{
|
||||
sock *sk = sk_new(p->tpool);
|
||||
sock *sk = sk_new(p->p.pool);
|
||||
sk->type = SK_UDP;
|
||||
sk->saddr = local;
|
||||
sk->dport = ifa ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
|
||||
|
@ -1901,7 +1901,6 @@ bgp_out_table_export_start(struct rt_exporter *re, struct rt_export_request *req
|
||||
pool *p = rp_new(c->c.proto->pool, "Export hook");
|
||||
struct rt_export_hook *hook = mb_allocz(p, sizeof(struct rt_export_hook));
|
||||
hook->pool = p;
|
||||
hook->lp = lp_new_default(p);
|
||||
hook->event = ev_new_init(p, bgp_out_table_feed, hook);
|
||||
hook->feed_type = TFT_HASH;
|
||||
|
||||
@ -1919,6 +1918,7 @@ bgp_setup_out_table(struct bgp_channel *c)
|
||||
};
|
||||
|
||||
init_list(&c->prefix_exporter.hooks);
|
||||
init_list(&c->prefix_exporter.pending);
|
||||
|
||||
c->c.out_table = &c->prefix_exporter;
|
||||
}
|
||||
@ -1929,10 +1929,10 @@ bgp_setup_out_table(struct bgp_channel *c)
|
||||
*/
|
||||
|
||||
int
|
||||
bgp_preexport(struct channel *c, rte *e)
|
||||
bgp_preexport(struct channel *C, rte *e)
|
||||
{
|
||||
struct proto *SRC = e->src->proto;
|
||||
struct bgp_proto *p = (struct bgp_proto *) (c->proto);
|
||||
struct bgp_proto *p = (struct bgp_proto *) C->proto;
|
||||
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
|
||||
|
||||
/* Reject our routes */
|
||||
@ -2546,27 +2546,57 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
|
||||
return !old_suppressed;
|
||||
}
|
||||
|
||||
rte *
|
||||
bgp_rte_modify_stale(struct rte *r, struct linpool *pool)
|
||||
void
|
||||
bgp_rte_modify_stale(struct rt_export_request *req, const net_addr *n, struct rt_pending_export *rpe UNUSED, rte **feed, uint count)
|
||||
{
|
||||
eattr *ea = ea_find(r->attrs, BGP_EA_ID(BA_COMMUNITY));
|
||||
const struct adata *ad = ea ? ea->u.ptr : NULL;
|
||||
uint flags = ea ? ea->flags : BAF_PARTIAL;
|
||||
struct bgp_channel *c = SKIP_BACK(struct bgp_channel, stale_feed, req);
|
||||
struct rt_import_hook *irh = c->c.in_req.hook;
|
||||
|
||||
if (ad && int_set_contains(ad, BGP_COMM_NO_LLGR))
|
||||
return NULL;
|
||||
/* Find our routes among others */
|
||||
for (uint i=0; i<count; i++)
|
||||
{
|
||||
rte *r = feed[i];
|
||||
|
||||
if (ad && int_set_contains(ad, BGP_COMM_LLGR_STALE))
|
||||
return r;
|
||||
if (
|
||||
!rte_is_valid(r) || /* Not a valid route */
|
||||
(r->sender != irh) || /* Not our route */
|
||||
(r->stale_cycle == irh->stale_set)) /* A new route, do not mark as stale */
|
||||
continue;
|
||||
|
||||
_Thread_local static rte e0;
|
||||
e0 = *r;
|
||||
eattr *ea = ea_find(r->attrs, BGP_EA_ID(BA_COMMUNITY));
|
||||
const struct adata *ad = ea ? ea->u.ptr : NULL;
|
||||
uint flags = ea ? ea->flags : BAF_PARTIAL;
|
||||
|
||||
bgp_set_attr_ptr(&e0.attrs, BA_COMMUNITY, flags,
|
||||
int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
|
||||
e0.pflags |= BGP_REF_STALE;
|
||||
/* LLGR not allowed, withdraw the route */
|
||||
if (ad && int_set_contains(ad, BGP_COMM_NO_LLGR))
|
||||
{
|
||||
rte_import(&c->c.in_req, n, NULL, r->src);
|
||||
continue;
|
||||
}
|
||||
|
||||
return &e0;
|
||||
/* Route already marked as LLGR, do nothing */
|
||||
if (ad && int_set_contains(ad, BGP_COMM_LLGR_STALE))
|
||||
continue;
|
||||
|
||||
/* Store the tmp_linpool state to aggresively save memory */
|
||||
struct lp_state tmpp;
|
||||
lp_save(tmp_linpool, &tmpp);
|
||||
|
||||
/* Mark the route as LLGR */
|
||||
rte e0 = *r;
|
||||
bgp_set_attr_ptr(&e0.attrs, BA_COMMUNITY, flags, int_set_add(tmp_linpool, ad, BGP_COMM_LLGR_STALE));
|
||||
e0.pflags &= ~BGP_REF_NOT_STALE;
|
||||
e0.pflags |= BGP_REF_STALE;
|
||||
|
||||
/* We need to update the route but keep it stale. */
|
||||
ASSERT_DIE(irh->stale_set == irh->stale_valid + 1);
|
||||
irh->stale_set--;
|
||||
rte_import(&c->c.in_req, n, &e0, r->src);
|
||||
irh->stale_set++;
|
||||
|
||||
/* Restore the memory state */
|
||||
lp_restore(tmp_linpool, &tmpp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -139,6 +139,9 @@ static void bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *bfd);
|
||||
static int bgp_incoming_connection(sock *sk, uint dummy UNUSED);
|
||||
static void bgp_listen_sock_err(sock *sk UNUSED, int err);
|
||||
|
||||
static void bgp_graceful_restart_feed(struct bgp_channel *c);
|
||||
|
||||
|
||||
/**
|
||||
* bgp_open - open a BGP instance
|
||||
* @p: BGP instance
|
||||
@ -373,6 +376,7 @@ bgp_close_conn(struct bgp_conn *conn)
|
||||
conn->keepalive_timer = NULL;
|
||||
rfree(conn->hold_timer);
|
||||
conn->hold_timer = NULL;
|
||||
|
||||
rfree(conn->tx_ev);
|
||||
conn->tx_ev = NULL;
|
||||
rfree(conn->sk);
|
||||
@ -511,6 +515,7 @@ void
|
||||
bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len)
|
||||
{
|
||||
proto_notify_state(&p->p, PS_STOP);
|
||||
p->uncork_ev->data = NULL;
|
||||
bgp_graceful_close_conn(&p->outgoing_conn, subcode, data, len);
|
||||
bgp_graceful_close_conn(&p->incoming_conn, subcode, data, len);
|
||||
ev_schedule(p->event);
|
||||
@ -760,25 +765,25 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
|
||||
{
|
||||
case BGP_GRS_NONE:
|
||||
c->gr_active = BGP_GRS_ACTIVE;
|
||||
rt_refresh_begin(c->c.table, &c->c.in_req);
|
||||
rt_refresh_begin(&c->c.in_req);
|
||||
break;
|
||||
|
||||
case BGP_GRS_ACTIVE:
|
||||
rt_refresh_end(c->c.table, &c->c.in_req);
|
||||
rt_refresh_begin(c->c.table, &c->c.in_req);
|
||||
rt_refresh_end(&c->c.in_req);
|
||||
rt_refresh_begin(&c->c.in_req);
|
||||
break;
|
||||
|
||||
case BGP_GRS_LLGR:
|
||||
rt_refresh_begin(c->c.table, &c->c.in_req);
|
||||
rt_modify_stale(c->c.table, &c->c.in_req);
|
||||
rt_refresh_begin(&c->c.in_req);
|
||||
bgp_graceful_restart_feed(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just flush the routes */
|
||||
rt_refresh_begin(c->c.table, &c->c.in_req);
|
||||
rt_refresh_end(c->c.table, &c->c.in_req);
|
||||
rt_refresh_begin(&c->c.in_req);
|
||||
rt_refresh_end(&c->c.in_req);
|
||||
}
|
||||
|
||||
/* Reset bucket and prefix tables */
|
||||
@ -796,6 +801,53 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
|
||||
tm_start(p->gr_timer, p->conn->remote_caps->gr_time S);
|
||||
}
|
||||
|
||||
static void
|
||||
bgp_graceful_restart_feed_done(struct rt_export_request *req)
|
||||
{
|
||||
req->hook = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
bgp_graceful_restart_feed_dump_req(struct rt_export_request *req)
|
||||
{
|
||||
struct bgp_channel *c = SKIP_BACK(struct bgp_channel, stale_feed, req);
|
||||
debug(" BGP-GR %s.%s export request %p\n", c->c.proto->name, c->c.name, req);
|
||||
}
|
||||
|
||||
static void
|
||||
bgp_graceful_restart_feed_log_state_change(struct rt_export_request *req, u8 state)
|
||||
{
|
||||
struct bgp_channel *c = SKIP_BACK(struct bgp_channel, stale_feed, req);
|
||||
struct bgp_proto *p = (void *) c->c.proto;
|
||||
BGP_TRACE(D_EVENTS, "Long-lived graceful restart export state changed to %s", rt_export_state_name(state));
|
||||
|
||||
if (state == TES_READY)
|
||||
rt_stop_export(req, bgp_graceful_restart_feed_done);
|
||||
}
|
||||
|
||||
static void
|
||||
bgp_graceful_restart_drop_export(struct rt_export_request *req UNUSED, const net_addr *n UNUSED, struct rt_pending_export *rpe UNUSED)
|
||||
{ /* Nothing to do */ }
|
||||
|
||||
static void
|
||||
bgp_graceful_restart_feed(struct bgp_channel *c)
|
||||
{
|
||||
c->stale_feed = (struct rt_export_request) {
|
||||
.name = "BGP-GR",
|
||||
.list = &global_work_list,
|
||||
.trace_routes = c->c.debug | c->c.proto->debug,
|
||||
.dump_req = bgp_graceful_restart_feed_dump_req,
|
||||
.log_state_change = bgp_graceful_restart_feed_log_state_change,
|
||||
.export_bulk = bgp_rte_modify_stale,
|
||||
.export_one = bgp_graceful_restart_drop_export,
|
||||
};
|
||||
|
||||
rt_request_export(&c->c.table->exporter, &c->stale_feed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* bgp_graceful_restart_done - finish active BGP graceful restart
|
||||
* @c: BGP channel
|
||||
@ -819,7 +871,7 @@ bgp_graceful_restart_done(struct bgp_channel *c)
|
||||
BGP_TRACE(D_EVENTS, "Neighbor graceful restart done");
|
||||
|
||||
tm_stop(c->stale_timer);
|
||||
rt_refresh_end(c->c.table, &c->c.in_req);
|
||||
rt_refresh_end(&c->c.in_req);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -861,7 +913,7 @@ bgp_graceful_restart_timeout(timer *t)
|
||||
/* Channel is in GR, and supports LLGR -> start LLGR */
|
||||
c->gr_active = BGP_GRS_LLGR;
|
||||
tm_start(c->stale_timer, c->stale_time S);
|
||||
rt_modify_stale(c->c.table, &c->c.in_req);
|
||||
bgp_graceful_restart_feed(c);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -899,7 +951,7 @@ bgp_refresh_begin(struct bgp_channel *c)
|
||||
{ log(L_WARN "%s: BEGIN-OF-RR received before END-OF-RIB, ignoring", p->p.name); return; }
|
||||
|
||||
c->load_state = BFS_REFRESHING;
|
||||
rt_refresh_begin(c->c.table, &c->c.in_req);
|
||||
rt_refresh_begin(&c->c.in_req);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -920,7 +972,7 @@ bgp_refresh_end(struct bgp_channel *c)
|
||||
{ log(L_WARN "%s: END-OF-RR received without prior BEGIN-OF-RR, ignoring", p->p.name); return; }
|
||||
|
||||
c->load_state = BFS_NONE;
|
||||
rt_refresh_end(c->c.table, &c->c.in_req);
|
||||
rt_refresh_end(&c->c.in_req);
|
||||
}
|
||||
|
||||
|
||||
@ -1526,6 +1578,8 @@ bgp_start(struct proto *P)
|
||||
p->last_rx_update = 0;
|
||||
|
||||
p->event = ev_new_init(p->p.pool, bgp_decision, p);
|
||||
p->uncork_ev = ev_new_init(p->p.pool, bgp_uncork, p);
|
||||
|
||||
p->startup_timer = tm_new_init(p->p.pool, bgp_startup_timeout, p, 0, 0);
|
||||
p->gr_timer = tm_new_init(p->p.pool, bgp_graceful_restart_timeout, p, 0, 0);
|
||||
|
||||
@ -1672,7 +1726,6 @@ bgp_init(struct proto_config *CF)
|
||||
P->rte_better = bgp_rte_better;
|
||||
P->rte_mergable = bgp_rte_mergable;
|
||||
P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
|
||||
P->rte_modify = bgp_rte_modify_stale;
|
||||
P->rte_igp_metric = bgp_rte_igp_metric;
|
||||
|
||||
p->cf = cf;
|
||||
|
@ -319,6 +319,7 @@ struct bgp_proto {
|
||||
struct bgp_socket *sock; /* Shared listening socket */
|
||||
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
|
||||
struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */
|
||||
event *uncork_ev; /* Uncork event in case of congestion */
|
||||
struct bgp_stats stats; /* BGP statistics */
|
||||
btime last_established; /* Last time of enter/leave of established state */
|
||||
btime last_rx_update; /* Last time of RX update */
|
||||
@ -371,6 +372,7 @@ struct bgp_channel {
|
||||
|
||||
timer *stale_timer; /* Long-lived stale timer for LLGR */
|
||||
u32 stale_time; /* Stored LLGR stale time from last session */
|
||||
struct rt_export_request stale_feed; /* Feeder request for stale route modification */
|
||||
|
||||
u8 add_path_rx; /* Session expects receive of ADD-PATH extended NLRI */
|
||||
u8 add_path_tx; /* Session expects transmit of ADD-PATH extended NLRI */
|
||||
@ -576,7 +578,7 @@ void bgp_done_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bu
|
||||
int bgp_rte_better(struct rte *, struct rte *);
|
||||
int bgp_rte_mergable(rte *pri, rte *sec);
|
||||
int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
|
||||
struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool);
|
||||
void bgp_rte_modify_stale(struct rt_export_request *req, const net_addr *n, struct rt_pending_export *rpe UNUSED, rte **feed, uint count);
|
||||
u32 bgp_rte_igp_metric(const rte *);
|
||||
void bgp_rt_notify(struct proto *P, struct channel *C, const net_addr *n, rte *new, const rte *old);
|
||||
int bgp_preexport(struct channel *, struct rte *);
|
||||
@ -609,6 +611,7 @@ void bgp_schedule_packet(struct bgp_conn *conn, struct bgp_channel *c, int type)
|
||||
void bgp_kick_tx(void *vconn);
|
||||
void bgp_tx(struct birdsock *sk);
|
||||
int bgp_rx(struct birdsock *sk, uint size);
|
||||
void bgp_uncork(void *vp);
|
||||
const char * bgp_error_dsc(unsigned code, unsigned subcode);
|
||||
void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len);
|
||||
|
||||
|
@ -3175,6 +3175,21 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
bgp_uncork(void *vp)
|
||||
{
|
||||
struct bgp_proto *p = vp;
|
||||
|
||||
if (p && p->conn && (p->conn->state == BS_ESTABLISHED) && !p->conn->sk->rx_hook)
|
||||
{
|
||||
struct birdsock *sk = p->conn->sk;
|
||||
ASSERT_DIE(sk->rpos > sk->rbuf);
|
||||
sk->rx_hook = bgp_rx;
|
||||
bgp_rx(sk, sk->rpos - sk->rbuf);
|
||||
BGP_TRACE(D_PACKETS, "Uncorked");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bgp_rx - handle received data
|
||||
* @sk: socket
|
||||
@ -3189,6 +3204,7 @@ int
|
||||
bgp_rx(sock *sk, uint size)
|
||||
{
|
||||
struct bgp_conn *conn = sk->data;
|
||||
struct bgp_proto *p = conn->bgp;
|
||||
byte *pkt_start = sk->rbuf;
|
||||
byte *end = pkt_start + size;
|
||||
uint i, len;
|
||||
@ -3198,6 +3214,12 @@ bgp_rx(sock *sk, uint size)
|
||||
{
|
||||
if ((conn->state == BS_CLOSE) || (conn->sk != sk))
|
||||
return 0;
|
||||
if ((conn->state == BS_ESTABLISHED) && rt_cork_check(conn->bgp->uncork_ev))
|
||||
{
|
||||
sk->rx_hook = NULL;
|
||||
BGP_TRACE(D_PACKETS, "Corked");
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<16; i++)
|
||||
if (pkt_start[i] != 0xff)
|
||||
{
|
||||
|
@ -486,13 +486,13 @@ ospf_disp(timer * timer)
|
||||
* import to the filters.
|
||||
*/
|
||||
static int
|
||||
ospf_preexport(struct channel *c, rte *e)
|
||||
ospf_preexport(struct channel *C, rte *e)
|
||||
{
|
||||
struct ospf_proto *p = (struct ospf_proto *) c->proto;
|
||||
struct ospf_proto *p = (struct ospf_proto *) C->proto;
|
||||
struct ospf_area *oa = ospf_main_area(p);
|
||||
|
||||
/* Reject our own routes */
|
||||
if (e->src->proto == c->proto)
|
||||
if (e->src->proto == &p->p)
|
||||
return -1;
|
||||
|
||||
/* Do not export routes to stub areas */
|
||||
|
@ -73,12 +73,12 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *n, rte *
|
||||
}
|
||||
|
||||
static int
|
||||
pipe_preexport(struct channel *c, rte *e)
|
||||
pipe_preexport(struct channel *C, rte *e)
|
||||
{
|
||||
struct pipe_proto *p = (void *) c->proto;
|
||||
struct pipe_proto *p = (void *) C->proto;
|
||||
|
||||
/* Avoid direct loopbacks */
|
||||
if (e->sender == c->in_req.hook)
|
||||
if (e->sender == C->in_req.hook)
|
||||
return -1;
|
||||
|
||||
/* Indirection check */
|
||||
@ -86,8 +86,8 @@ pipe_preexport(struct channel *c, rte *e)
|
||||
if (e->generation >= max_generation)
|
||||
{
|
||||
log_rl(&p->rl_gen, L_ERR "Route overpiped (%u hops of %u configured in %s) in table %s: %N %s/%u:%u",
|
||||
e->generation, max_generation, c->proto->name,
|
||||
c->table->name, e->net, e->src->proto->name, e->src->private_id, e->src->global_id);
|
||||
e->generation, max_generation, C->proto->name,
|
||||
C->table->name, e->net, e->src->proto->name, e->src->private_id, e->src->global_id);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -394,10 +394,10 @@ radv_net_match_trigger(struct radv_config *cf, const net_addr *n)
|
||||
}
|
||||
|
||||
int
|
||||
radv_preexport(struct channel *c, rte *new)
|
||||
radv_preexport(struct channel *C, rte *new)
|
||||
{
|
||||
// struct radv_proto *p = (struct radv_proto *) P;
|
||||
struct radv_config *cf = (struct radv_config *) (c->proto->cf);
|
||||
struct radv_config *cf = (struct radv_config *) (C->proto->cf);
|
||||
|
||||
if (radv_net_match_trigger(cf, new->net))
|
||||
return RIC_PROCESS;
|
||||
|
@ -661,9 +661,9 @@ rpki_handle_cache_response_pdu(struct rpki_cache *cache, const struct pdu_cache_
|
||||
* a refresh cycle.
|
||||
*/
|
||||
if (cache->p->roa4_channel)
|
||||
rt_refresh_begin(cache->p->roa4_channel->table, &cache->p->roa4_channel->in_req);
|
||||
rt_refresh_begin(&cache->p->roa4_channel->in_req);
|
||||
if (cache->p->roa6_channel)
|
||||
rt_refresh_begin(cache->p->roa6_channel->table, &cache->p->roa6_channel->in_req);
|
||||
rt_refresh_begin(&cache->p->roa6_channel->in_req);
|
||||
|
||||
cache->p->refresh_channels = 1;
|
||||
}
|
||||
@ -846,9 +846,9 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da
|
||||
{
|
||||
cache->p->refresh_channels = 0;
|
||||
if (cache->p->roa4_channel)
|
||||
rt_refresh_end(cache->p->roa4_channel->table, &cache->p->roa4_channel->in_req);
|
||||
rt_refresh_end(&cache->p->roa4_channel->in_req);
|
||||
if (cache->p->roa6_channel)
|
||||
rt_refresh_end(cache->p->roa6_channel->table, &cache->p->roa6_channel->in_req);
|
||||
rt_refresh_end(&cache->p->roa6_channel->in_req);
|
||||
}
|
||||
|
||||
cache->last_update = current_time();
|
||||
|
@ -40,7 +40,7 @@ static_route_finish(void)
|
||||
if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest)
|
||||
cf_error("Unexpected or missing nexthop/type");
|
||||
|
||||
this_srt->cmds = f_linearize(this_srt_cmds);
|
||||
this_srt->cmds = f_linearize(this_srt_cmds, 0);
|
||||
}
|
||||
|
||||
CF_DECLS
|
||||
|
@ -13,7 +13,7 @@
|
||||
#ifdef GIT_LABEL
|
||||
#define BIRD_VERSION XSTR1(GIT_LABEL)
|
||||
#else
|
||||
#define BIRD_VERSION "2.0.9"
|
||||
#define BIRD_VERSION "2.0.10"
|
||||
#endif
|
||||
|
||||
/* Include parameters determined by configure script */
|
||||
|
@ -1,4 +1,4 @@
|
||||
src := alloc.c io.c krt.c log.c main.c random.c
|
||||
src := alloc.c io.c io-loop.c krt.c log.c main.c random.c domain.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
$(cf-local)
|
||||
|
@ -97,7 +97,7 @@ alloc_page(void)
|
||||
struct free_page *fp = SKIP_BACK(struct free_page, n, HEAD(fps->pages));
|
||||
rem_node(&fp->n);
|
||||
if ((--fps->cnt < fps->min) && !shutting_down)
|
||||
ev_schedule(&fps->cleanup);
|
||||
ev_send(&global_work_list, &fps->cleanup);
|
||||
|
||||
bzero(fp, page_size);
|
||||
return fp;
|
||||
@ -124,7 +124,7 @@ free_page(void *ptr)
|
||||
add_tail(&fps->pages, &fp->n);
|
||||
|
||||
if ((++fps->cnt > fps->max) && !shutting_down)
|
||||
ev_schedule(&fps->cleanup);
|
||||
ev_send(&global_work_list, &fps->cleanup);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
116
sysdep/unix/domain.c
Normal file
116
sysdep/unix/domain.c
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* BIRD Locking
|
||||
*
|
||||
* (c) 2020 Maria Matejka <mq@jmq.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
#undef DEBUG_LOCKING
|
||||
|
||||
#include "lib/birdlib.h"
|
||||
#include "lib/locking.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/timer.h"
|
||||
|
||||
#include "conf/conf.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Locking subsystem
|
||||
*/
|
||||
|
||||
_Thread_local struct lock_order locking_stack = {};
|
||||
_Thread_local struct domain_generic **last_locked = NULL;
|
||||
|
||||
#define ASSERT_NO_LOCK ASSERT_DIE(last_locked == NULL)
|
||||
|
||||
struct domain_generic {
|
||||
pthread_mutex_t mutex;
|
||||
uint order;
|
||||
struct domain_generic **prev;
|
||||
struct lock_order *locked_by;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#define DOMAIN_INIT(_name, _order) { .mutex = PTHREAD_MUTEX_INITIALIZER, .name = _name, .order = _order }
|
||||
|
||||
static struct domain_generic the_bird_domain_gen = DOMAIN_INIT("The BIRD", OFFSETOF(struct lock_order, the_bird));
|
||||
|
||||
DOMAIN(the_bird) the_bird_domain = { .the_bird = &the_bird_domain_gen };
|
||||
|
||||
struct domain_generic *
|
||||
domain_new(const char *name, uint order)
|
||||
{
|
||||
ASSERT_DIE(order < sizeof(struct lock_order));
|
||||
struct domain_generic *dg = xmalloc(sizeof(struct domain_generic));
|
||||
*dg = (struct domain_generic) DOMAIN_INIT(name, order);
|
||||
return dg;
|
||||
}
|
||||
|
||||
void
|
||||
domain_free(struct domain_generic *dg)
|
||||
{
|
||||
pthread_mutex_destroy(&dg->mutex);
|
||||
xfree(dg);
|
||||
}
|
||||
|
||||
uint dg_order(struct domain_generic *dg)
|
||||
{
|
||||
return dg->order;
|
||||
}
|
||||
|
||||
void do_lock(struct domain_generic *dg, struct domain_generic **lsp)
|
||||
{
|
||||
if ((char *) lsp - (char *) &locking_stack != dg->order)
|
||||
bug("Trying to lock on bad position: order=%u, lsp=%p, base=%p", dg->order, lsp, &locking_stack);
|
||||
|
||||
if (lsp <= last_locked)
|
||||
bug("Trying to lock in a bad order");
|
||||
if (*lsp)
|
||||
bug("Inconsistent locking stack state on lock");
|
||||
|
||||
btime lock_begin = current_time();
|
||||
pthread_mutex_lock(&dg->mutex);
|
||||
btime duration = current_time() - lock_begin;
|
||||
if (config && (duration > config->watchdog_warning))
|
||||
log(L_WARN "Locking of %s took %d ms", dg->name, (int) (duration TO_MS));
|
||||
|
||||
if (dg->prev || dg->locked_by)
|
||||
bug("Previous unlock not finished correctly");
|
||||
dg->prev = last_locked;
|
||||
*lsp = dg;
|
||||
last_locked = lsp;
|
||||
dg->locked_by = &locking_stack;
|
||||
}
|
||||
|
||||
void do_unlock(struct domain_generic *dg, struct domain_generic **lsp)
|
||||
{
|
||||
if ((char *) lsp - (char *) &locking_stack != dg->order)
|
||||
bug("Trying to unlock on bad position: order=%u, lsp=%p, base=%p", dg->order, lsp, &locking_stack);
|
||||
|
||||
if (dg->locked_by != &locking_stack)
|
||||
bug("Inconsistent domain state on unlock");
|
||||
if ((last_locked != lsp) || (*lsp != dg))
|
||||
bug("Inconsistent locking stack state on unlock");
|
||||
dg->locked_by = NULL;
|
||||
last_locked = dg->prev;
|
||||
*lsp = NULL;
|
||||
dg->prev = NULL;
|
||||
pthread_mutex_unlock(&dg->mutex);
|
||||
}
|
@ -15,7 +15,6 @@
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "proto/bfd/io.h"
|
||||
|
||||
#include "lib/buffer.h"
|
||||
#include "lib/lists.h"
|
||||
@ -24,56 +23,41 @@
|
||||
#include "lib/timer.h"
|
||||
#include "lib/socket.h"
|
||||
|
||||
#include "lib/io-loop.h"
|
||||
#include "sysdep/unix/io-loop.h"
|
||||
#include "conf/conf.h"
|
||||
|
||||
struct birdloop
|
||||
{
|
||||
pool *pool;
|
||||
pthread_t thread;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
u8 stop_called;
|
||||
u8 poll_active;
|
||||
u8 wakeup_masked;
|
||||
int wakeup_fds[2];
|
||||
|
||||
struct timeloop time;
|
||||
list event_list;
|
||||
list sock_list;
|
||||
uint sock_num;
|
||||
|
||||
BUFFER(sock *) poll_sk;
|
||||
BUFFER(struct pollfd) poll_fd;
|
||||
u8 poll_changed;
|
||||
u8 close_scheduled;
|
||||
};
|
||||
|
||||
#define THREAD_STACK_SIZE 65536 /* To be lowered in near future */
|
||||
|
||||
/*
|
||||
* Current thread context
|
||||
*/
|
||||
|
||||
static pthread_key_t current_loop_key;
|
||||
extern pthread_key_t current_time_key;
|
||||
_Thread_local struct birdloop *birdloop_current;
|
||||
static _Thread_local struct birdloop *birdloop_wakeup_masked;
|
||||
static _Thread_local uint birdloop_wakeup_masked_count;
|
||||
|
||||
static inline struct birdloop *
|
||||
birdloop_current(void)
|
||||
event_list *
|
||||
birdloop_event_list(struct birdloop *loop)
|
||||
{
|
||||
return pthread_getspecific(current_loop_key);
|
||||
return &loop->event_list;
|
||||
}
|
||||
|
||||
static inline void
|
||||
birdloop_set_current(struct birdloop *loop)
|
||||
struct timeloop *
|
||||
birdloop_time_loop(struct birdloop *loop)
|
||||
{
|
||||
pthread_setspecific(current_loop_key, loop);
|
||||
pthread_setspecific(current_time_key, loop ? &loop->time : &main_timeloop);
|
||||
return &loop->time;
|
||||
}
|
||||
|
||||
static inline void
|
||||
birdloop_init_current(void)
|
||||
_Bool
|
||||
birdloop_inside(struct birdloop *loop)
|
||||
{
|
||||
pthread_key_create(¤t_loop_key, NULL);
|
||||
}
|
||||
for (struct birdloop *c = birdloop_current; c; c = c->prev_loop)
|
||||
if (loop == c)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wakeup code for birdloop
|
||||
@ -150,60 +134,24 @@ wakeup_do_kick(struct birdloop *loop)
|
||||
}
|
||||
|
||||
static inline void
|
||||
wakeup_kick(struct birdloop *loop)
|
||||
birdloop_do_ping(struct birdloop *loop)
|
||||
{
|
||||
if (!loop->wakeup_masked)
|
||||
wakeup_do_kick(loop);
|
||||
if (atomic_fetch_add_explicit(&loop->ping_sent, 1, memory_order_acq_rel))
|
||||
return;
|
||||
|
||||
if (loop == birdloop_wakeup_masked)
|
||||
birdloop_wakeup_masked_count++;
|
||||
else
|
||||
loop->wakeup_masked = 2;
|
||||
}
|
||||
|
||||
/* For notifications from outside */
|
||||
void
|
||||
wakeup_kick_current(void)
|
||||
{
|
||||
struct birdloop *loop = birdloop_current();
|
||||
|
||||
if (loop && loop->poll_active)
|
||||
wakeup_kick(loop);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Events
|
||||
*/
|
||||
|
||||
static inline uint
|
||||
events_waiting(struct birdloop *loop)
|
||||
{
|
||||
return !EMPTY_LIST(loop->event_list);
|
||||
}
|
||||
|
||||
static inline void
|
||||
events_init(struct birdloop *loop)
|
||||
{
|
||||
init_list(&loop->event_list);
|
||||
}
|
||||
|
||||
static void
|
||||
events_fire(struct birdloop *loop)
|
||||
{
|
||||
times_update(&loop->time);
|
||||
ev_run_list(&loop->event_list);
|
||||
wakeup_do_kick(loop);
|
||||
}
|
||||
|
||||
void
|
||||
ev2_schedule(event *e)
|
||||
birdloop_ping(struct birdloop *loop)
|
||||
{
|
||||
struct birdloop *loop = birdloop_current();
|
||||
|
||||
if (loop->poll_active && EMPTY_LIST(loop->event_list))
|
||||
wakeup_kick(loop);
|
||||
|
||||
if (e->n.next)
|
||||
rem_node(&e->n);
|
||||
|
||||
add_tail(&loop->event_list, &e->n);
|
||||
if (birdloop_inside(loop) && !loop->ping_pending)
|
||||
loop->ping_pending++;
|
||||
else
|
||||
birdloop_do_ping(loop);
|
||||
}
|
||||
|
||||
|
||||
@ -231,16 +179,14 @@ sockets_add(struct birdloop *loop, sock *s)
|
||||
s->index = -1;
|
||||
loop->poll_changed = 1;
|
||||
|
||||
if (loop->poll_active)
|
||||
wakeup_kick(loop);
|
||||
birdloop_ping(loop);
|
||||
}
|
||||
|
||||
void
|
||||
sk_start(sock *s)
|
||||
{
|
||||
struct birdloop *loop = birdloop_current();
|
||||
|
||||
sockets_add(loop, s);
|
||||
ASSERT_DIE(birdloop_current != &main_birdloop);
|
||||
sockets_add(birdloop_current, s);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -250,30 +196,21 @@ sockets_remove(struct birdloop *loop, sock *s)
|
||||
loop->sock_num--;
|
||||
|
||||
if (s->index >= 0)
|
||||
{
|
||||
loop->poll_sk.data[s->index] = NULL;
|
||||
|
||||
s->index = -1;
|
||||
loop->poll_changed = 1;
|
||||
|
||||
/* Wakeup moved to sk_stop() */
|
||||
s->index = -1;
|
||||
loop->poll_changed = 1;
|
||||
loop->close_scheduled = 1;
|
||||
birdloop_ping(loop);
|
||||
}
|
||||
else
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
void
|
||||
sk_stop(sock *s)
|
||||
{
|
||||
struct birdloop *loop = birdloop_current();
|
||||
|
||||
sockets_remove(loop, s);
|
||||
|
||||
if (loop->poll_active)
|
||||
{
|
||||
loop->close_scheduled = 1;
|
||||
wakeup_kick(loop);
|
||||
}
|
||||
else
|
||||
close(s->fd);
|
||||
|
||||
s->fd = -1;
|
||||
sockets_remove(birdloop_current, s);
|
||||
}
|
||||
|
||||
static inline uint sk_want_events(sock *s)
|
||||
@ -354,7 +291,7 @@ sockets_fire(struct birdloop *loop)
|
||||
sock **psk = loop->poll_sk.data;
|
||||
int poll_num = loop->poll_fd.used - 1;
|
||||
|
||||
times_update(&loop->time);
|
||||
times_update();
|
||||
|
||||
/* Last fd is internal wakeup fd */
|
||||
if (pfd[poll_num].revents & POLLIN)
|
||||
@ -373,12 +310,15 @@ sockets_fire(struct birdloop *loop)
|
||||
|
||||
if (pfd->revents & POLLIN)
|
||||
while (e && *psk && (*psk)->rx_hook)
|
||||
e = sk_read(*psk, 0);
|
||||
e = sk_read(*psk, pfd->revents);
|
||||
|
||||
e = 1;
|
||||
if (pfd->revents & POLLOUT)
|
||||
{
|
||||
loop->poll_changed = 1;
|
||||
while (e && *psk)
|
||||
e = sk_write(*psk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,90 +327,176 @@ sockets_fire(struct birdloop *loop)
|
||||
* Birdloop
|
||||
*/
|
||||
|
||||
static void * birdloop_main(void *arg);
|
||||
struct birdloop main_birdloop;
|
||||
|
||||
static void birdloop_enter_locked(struct birdloop *loop);
|
||||
|
||||
void
|
||||
birdloop_init(void)
|
||||
{
|
||||
wakeup_init(&main_birdloop);
|
||||
|
||||
main_birdloop.time.domain = the_bird_domain.the_bird;
|
||||
main_birdloop.time.loop = &main_birdloop;
|
||||
|
||||
times_update();
|
||||
timers_init(&main_birdloop.time, &root_pool);
|
||||
|
||||
birdloop_enter_locked(&main_birdloop);
|
||||
}
|
||||
|
||||
static void *birdloop_main(void *arg);
|
||||
|
||||
struct birdloop *
|
||||
birdloop_new(void)
|
||||
birdloop_new(pool *pp, uint order, const char *name)
|
||||
{
|
||||
/* FIXME: this init should be elsewhere and thread-safe */
|
||||
static int init = 0;
|
||||
if (!init)
|
||||
{ birdloop_init_current(); init = 1; }
|
||||
struct domain_generic *dg = domain_new(name, order);
|
||||
|
||||
pool *p = rp_new(NULL, "Birdloop root");
|
||||
pool *p = rp_new(pp, name);
|
||||
struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
|
||||
loop->pool = p;
|
||||
pthread_mutex_init(&loop->mutex, NULL);
|
||||
|
||||
loop->time.domain = dg;
|
||||
loop->time.loop = loop;
|
||||
|
||||
birdloop_enter(loop);
|
||||
|
||||
wakeup_init(loop);
|
||||
|
||||
events_init(loop);
|
||||
ev_init_list(&loop->event_list, loop, name);
|
||||
timers_init(&loop->time, p);
|
||||
sockets_init(loop);
|
||||
|
||||
int e = 0;
|
||||
|
||||
if (e = pthread_attr_init(&loop->thread_attr))
|
||||
die("pthread_attr_init() failed: %M", e);
|
||||
|
||||
if (e = pthread_attr_setstacksize(&loop->thread_attr, THREAD_STACK_SIZE))
|
||||
die("pthread_attr_setstacksize(%u) failed: %M", THREAD_STACK_SIZE, e);
|
||||
|
||||
if (e = pthread_attr_setdetachstate(&loop->thread_attr, PTHREAD_CREATE_DETACHED))
|
||||
die("pthread_attr_setdetachstate(PTHREAD_CREATE_DETACHED) failed: %M", e);
|
||||
|
||||
if (e = pthread_create(&loop->thread_id, &loop->thread_attr, birdloop_main, loop))
|
||||
die("pthread_create() failed: %M", e);
|
||||
|
||||
birdloop_leave(loop);
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_start(struct birdloop *loop)
|
||||
static void
|
||||
birdloop_do_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
|
||||
{
|
||||
int rv = pthread_create(&loop->thread, NULL, birdloop_main, loop);
|
||||
if (rv)
|
||||
die("pthread_create(): %M", rv);
|
||||
loop->stopped = stopped;
|
||||
loop->stop_data = data;
|
||||
wakeup_do_kick(loop);
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_stop(struct birdloop *loop)
|
||||
birdloop_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
|
||||
{
|
||||
pthread_mutex_lock(&loop->mutex);
|
||||
loop->stop_called = 1;
|
||||
wakeup_do_kick(loop);
|
||||
pthread_mutex_unlock(&loop->mutex);
|
||||
DG_LOCK(loop->time.domain);
|
||||
birdloop_do_stop(loop, stopped, data);
|
||||
DG_UNLOCK(loop->time.domain);
|
||||
}
|
||||
|
||||
int rv = pthread_join(loop->thread, NULL);
|
||||
if (rv)
|
||||
die("pthread_join(): %M", rv);
|
||||
void
|
||||
birdloop_stop_self(struct birdloop *loop, void (*stopped)(void *data), void *data)
|
||||
{
|
||||
ASSERT_DIE(loop == birdloop_current);
|
||||
ASSERT_DIE(DG_IS_LOCKED(loop->time.domain));
|
||||
|
||||
birdloop_do_stop(loop, stopped, data);
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_free(struct birdloop *loop)
|
||||
{
|
||||
ASSERT_DIE(loop->links == 0);
|
||||
ASSERT_DIE(pthread_equal(pthread_self(), loop->thread_id));
|
||||
|
||||
rcu_birdloop_stop(&loop->rcu);
|
||||
pthread_attr_destroy(&loop->thread_attr);
|
||||
|
||||
domain_free(loop->time.domain);
|
||||
rfree(loop->pool);
|
||||
}
|
||||
|
||||
static void
|
||||
birdloop_enter_locked(struct birdloop *loop)
|
||||
{
|
||||
ASSERT_DIE(DG_IS_LOCKED(loop->time.domain));
|
||||
ASSERT_DIE(!birdloop_inside(loop));
|
||||
|
||||
/* Store the old context */
|
||||
loop->prev_loop = birdloop_current;
|
||||
|
||||
/* Put the new context */
|
||||
birdloop_current = loop;
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_enter(struct birdloop *loop)
|
||||
{
|
||||
/* TODO: these functions could save and restore old context */
|
||||
pthread_mutex_lock(&loop->mutex);
|
||||
birdloop_set_current(loop);
|
||||
DG_LOCK(loop->time.domain);
|
||||
return birdloop_enter_locked(loop);
|
||||
}
|
||||
|
||||
static void
|
||||
birdloop_leave_locked(struct birdloop *loop)
|
||||
{
|
||||
/* Check the current context */
|
||||
ASSERT_DIE(birdloop_current == loop);
|
||||
|
||||
/* Send pending pings */
|
||||
if (loop->ping_pending)
|
||||
{
|
||||
loop->ping_pending = 0;
|
||||
birdloop_do_ping(loop);
|
||||
}
|
||||
|
||||
/* Restore the old context */
|
||||
birdloop_current = loop->prev_loop;
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_leave(struct birdloop *loop)
|
||||
{
|
||||
/* TODO: these functions could save and restore old context */
|
||||
birdloop_set_current(NULL);
|
||||
pthread_mutex_unlock(&loop->mutex);
|
||||
birdloop_leave_locked(loop);
|
||||
DG_UNLOCK(loop->time.domain);
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_mask_wakeups(struct birdloop *loop)
|
||||
{
|
||||
pthread_mutex_lock(&loop->mutex);
|
||||
loop->wakeup_masked = 1;
|
||||
pthread_mutex_unlock(&loop->mutex);
|
||||
ASSERT_DIE(birdloop_wakeup_masked == NULL);
|
||||
birdloop_wakeup_masked = loop;
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_unmask_wakeups(struct birdloop *loop)
|
||||
{
|
||||
pthread_mutex_lock(&loop->mutex);
|
||||
if (loop->wakeup_masked == 2)
|
||||
ASSERT_DIE(birdloop_wakeup_masked == loop);
|
||||
birdloop_wakeup_masked = NULL;
|
||||
if (birdloop_wakeup_masked_count)
|
||||
wakeup_do_kick(loop);
|
||||
loop->wakeup_masked = 0;
|
||||
pthread_mutex_unlock(&loop->mutex);
|
||||
|
||||
birdloop_wakeup_masked_count = 0;
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_link(struct birdloop *loop)
|
||||
{
|
||||
ASSERT_DIE(birdloop_inside(loop));
|
||||
loop->links++;
|
||||
}
|
||||
|
||||
void
|
||||
birdloop_unlink(struct birdloop *loop)
|
||||
{
|
||||
ASSERT_DIE(birdloop_inside(loop));
|
||||
loop->links--;
|
||||
}
|
||||
|
||||
static void *
|
||||
@ -480,18 +506,17 @@ birdloop_main(void *arg)
|
||||
timer *t;
|
||||
int rv, timeout;
|
||||
|
||||
birdloop_set_current(loop);
|
||||
rcu_birdloop_start(&loop->rcu);
|
||||
|
||||
btime loop_begin = current_time();
|
||||
|
||||
tmp_init(loop->pool);
|
||||
|
||||
pthread_mutex_lock(&loop->mutex);
|
||||
birdloop_enter(loop);
|
||||
while (1)
|
||||
{
|
||||
events_fire(loop);
|
||||
timers_fire(&loop->time);
|
||||
|
||||
times_update(&loop->time);
|
||||
if (events_waiting(loop))
|
||||
timers_fire(&loop->time, 0);
|
||||
if (ev_run_list(&loop->event_list))
|
||||
timeout = 0;
|
||||
else if (t = timers_first(&loop->time))
|
||||
timeout = (tm_remains(t) TO_MS) + 1;
|
||||
@ -501,8 +526,11 @@ birdloop_main(void *arg)
|
||||
if (loop->poll_changed)
|
||||
sockets_prepare(loop);
|
||||
|
||||
loop->poll_active = 1;
|
||||
pthread_mutex_unlock(&loop->mutex);
|
||||
btime duration = current_time() - loop_begin;
|
||||
if (duration > config->watchdog_warning)
|
||||
log(L_WARN "I/O loop cycle took %d ms", (int) (duration TO_MS));
|
||||
|
||||
birdloop_leave(loop);
|
||||
|
||||
try:
|
||||
rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
|
||||
@ -513,25 +541,38 @@ birdloop_main(void *arg)
|
||||
die("poll: %m");
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&loop->mutex);
|
||||
loop->poll_active = 0;
|
||||
birdloop_enter(loop);
|
||||
|
||||
if (loop->close_scheduled)
|
||||
sockets_close_fds(loop);
|
||||
|
||||
if (loop->stop_called)
|
||||
if (loop->stopped)
|
||||
break;
|
||||
|
||||
loop_begin = current_time();
|
||||
|
||||
if (rv)
|
||||
sockets_fire(loop);
|
||||
|
||||
timers_fire(&loop->time);
|
||||
atomic_exchange_explicit(&loop->ping_sent, 0, memory_order_acq_rel);
|
||||
}
|
||||
|
||||
loop->stop_called = 0;
|
||||
pthread_mutex_unlock(&loop->mutex);
|
||||
/* Flush remaining events */
|
||||
ASSERT_DIE(!ev_run_list(&loop->event_list));
|
||||
|
||||
/* No timers allowed */
|
||||
ASSERT_DIE(timers_count(&loop->time) == 0);
|
||||
ASSERT_DIE(EMPTY_LIST(loop->sock_list));
|
||||
ASSERT_DIE(loop->sock_num == 0);
|
||||
|
||||
birdloop_leave(loop);
|
||||
loop->stopped(loop->stop_data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
birdloop_yield(void)
|
||||
{
|
||||
usleep(100);
|
||||
}
|
43
sysdep/unix/io-loop.h
Normal file
43
sysdep/unix/io-loop.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* BIRD -- I/O and event loop
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_SYSDEP_UNIX_IO_LOOP_H_
|
||||
#define _BIRD_SYSDEP_UNIX_IO_LOOP_H_
|
||||
|
||||
#include "lib/rcu.h"
|
||||
|
||||
struct birdloop
|
||||
{
|
||||
pool *pool;
|
||||
|
||||
struct timeloop time;
|
||||
event_list event_list;
|
||||
list sock_list;
|
||||
uint sock_num;
|
||||
|
||||
BUFFER(sock *) poll_sk;
|
||||
BUFFER(struct pollfd) poll_fd;
|
||||
u8 poll_changed;
|
||||
u8 close_scheduled;
|
||||
|
||||
uint ping_pending;
|
||||
_Atomic u32 ping_sent;
|
||||
int wakeup_fds[2];
|
||||
|
||||
pthread_t thread_id;
|
||||
pthread_attr_t thread_attr;
|
||||
|
||||
struct rcu_birdloop rcu;
|
||||
|
||||
uint links;
|
||||
|
||||
void (*stopped)(void *data);
|
||||
void *stop_data;
|
||||
|
||||
struct birdloop *prev_loop;
|
||||
};
|
||||
|
||||
#endif
|
181
sysdep/unix/io.c
181
sysdep/unix/io.c
@ -36,12 +36,14 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/socket.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/locking.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/string.h"
|
||||
#include "nest/iface.h"
|
||||
#include "conf/conf.h"
|
||||
|
||||
#include "sysdep/unix/unix.h"
|
||||
#include "sysdep/unix/io-loop.h"
|
||||
#include CONFIG_INCLUDE_SYSIO_H
|
||||
|
||||
/* Maximum number of calls of tx handler for one socket in one
|
||||
@ -122,55 +124,50 @@ rf_fileno(struct rfile *f)
|
||||
|
||||
btime boot_time;
|
||||
|
||||
|
||||
void
|
||||
times_init(struct timeloop *loop)
|
||||
times_update(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
btime old_time = current_time();
|
||||
btime old_real_time = current_real_time();
|
||||
|
||||
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (rv < 0)
|
||||
die("Monotonic clock is missing");
|
||||
|
||||
if ((ts.tv_sec < 0) || (((u64) ts.tv_sec) > ((u64) 1 << 40)))
|
||||
log(L_WARN "Monotonic clock is crazy");
|
||||
|
||||
loop->last_time = ts.tv_sec S + ts.tv_nsec NS;
|
||||
loop->real_time = 0;
|
||||
}
|
||||
|
||||
void
|
||||
times_update(struct timeloop *loop)
|
||||
{
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (rv < 0)
|
||||
die("clock_gettime: %m");
|
||||
|
||||
|
||||
btime new_time = ts.tv_sec S + ts.tv_nsec NS;
|
||||
|
||||
if (new_time < loop->last_time)
|
||||
if (new_time < old_time)
|
||||
log(L_ERR "Monotonic clock is broken");
|
||||
|
||||
loop->last_time = new_time;
|
||||
loop->real_time = 0;
|
||||
}
|
||||
|
||||
void
|
||||
times_update_real_time(struct timeloop *loop)
|
||||
{
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
rv = clock_gettime(CLOCK_REALTIME, &ts);
|
||||
if (rv < 0)
|
||||
die("clock_gettime: %m");
|
||||
|
||||
loop->real_time = ts.tv_sec S + ts.tv_nsec NS;
|
||||
}
|
||||
btime new_real_time = ts.tv_sec S + ts.tv_nsec NS;
|
||||
|
||||
if (!atomic_compare_exchange_strong_explicit(
|
||||
&last_time,
|
||||
&old_time,
|
||||
new_time,
|
||||
memory_order_acq_rel,
|
||||
memory_order_relaxed))
|
||||
DBG("Time update collision: last_time");
|
||||
|
||||
if (!atomic_compare_exchange_strong_explicit(
|
||||
&real_time,
|
||||
&old_real_time,
|
||||
new_real_time,
|
||||
memory_order_acq_rel,
|
||||
memory_order_relaxed))
|
||||
DBG("Time update collision: real_time");
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: Sockets
|
||||
@ -804,18 +801,16 @@ sk_free(resource *r)
|
||||
sk_ssh_free(s);
|
||||
#endif
|
||||
|
||||
if (s->fd < 0)
|
||||
if ((s->fd < 0) || (s->flags & SKF_THREAD))
|
||||
return;
|
||||
|
||||
/* FIXME: we should call sk_stop() for SKF_THREAD sockets */
|
||||
if (!(s->flags & SKF_THREAD))
|
||||
{
|
||||
if (s == current_sock)
|
||||
current_sock = sk_next(s);
|
||||
if (s == stored_sock)
|
||||
stored_sock = sk_next(s);
|
||||
if (s == current_sock)
|
||||
current_sock = sk_next(s);
|
||||
if (s == stored_sock)
|
||||
stored_sock = sk_next(s);
|
||||
|
||||
if (enlisted(&s->n))
|
||||
rem_node(&s->n);
|
||||
}
|
||||
|
||||
if (s->type != SK_SSH && s->type != SK_SSH_ACTIVE)
|
||||
close(s->fd);
|
||||
@ -1108,7 +1103,11 @@ sk_passive_connected(sock *s, int type)
|
||||
return 1;
|
||||
}
|
||||
|
||||
sk_insert(t);
|
||||
if (s->flags & SKF_PASSIVE_THREAD)
|
||||
t->flags |= SKF_THREAD;
|
||||
else
|
||||
sk_insert(t);
|
||||
|
||||
sk_alloc_bufs(t);
|
||||
s->rx_hook(t, 0);
|
||||
return 1;
|
||||
@ -1516,6 +1515,36 @@ sk_open_unix(sock *s, char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sk_reloop_hook(void *_vs)
|
||||
{
|
||||
sock *s = _vs;
|
||||
if (birdloop_inside(&main_birdloop))
|
||||
{
|
||||
s->flags &= ~SKF_THREAD;
|
||||
sk_insert(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
s->flags |= SKF_THREAD;
|
||||
sk_start(s);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sk_reloop(sock *s, struct birdloop *loop)
|
||||
{
|
||||
if (enlisted(&s->n))
|
||||
rem_node(&s->n);
|
||||
|
||||
s->reloop = (event) {
|
||||
.hook = sk_reloop_hook,
|
||||
.data = s,
|
||||
};
|
||||
|
||||
ev_send_loop(loop, &s->reloop);
|
||||
}
|
||||
|
||||
|
||||
#define CMSG_RX_SPACE MAX(CMSG4_SPACE_PKTINFO+CMSG4_SPACE_TTL, \
|
||||
CMSG6_SPACE_PKTINFO+CMSG6_SPACE_TTL)
|
||||
@ -2037,34 +2066,21 @@ struct event_log_entry
|
||||
static struct event_log_entry event_log[EVENT_LOG_LENGTH];
|
||||
static struct event_log_entry *event_open;
|
||||
static int event_log_pos, event_log_num, watchdog_active;
|
||||
static btime last_time;
|
||||
static btime last_io_time;
|
||||
static btime loop_time;
|
||||
|
||||
static void
|
||||
io_update_time(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
int rv;
|
||||
|
||||
/*
|
||||
* This is third time-tracking procedure (after update_times() above and
|
||||
* times_update() in BFD), dedicated to internal event log and latency
|
||||
* tracking. Hopefully, we consolidate these sometimes.
|
||||
*/
|
||||
|
||||
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (rv < 0)
|
||||
die("clock_gettime: %m");
|
||||
|
||||
last_time = ts.tv_sec S + ts.tv_nsec NS;
|
||||
last_io_time = current_time();
|
||||
|
||||
if (event_open)
|
||||
{
|
||||
event_open->duration = last_time - event_open->timestamp;
|
||||
event_open->duration = last_io_time - event_open->timestamp;
|
||||
|
||||
if (event_open->duration > config->latency_limit)
|
||||
log(L_WARN "Event 0x%p 0x%p took %d ms",
|
||||
event_open->hook, event_open->data, (int) (event_open->duration TO_MS));
|
||||
log(L_WARN "Event 0x%p 0x%p took %u.%03u ms",
|
||||
event_open->hook, event_open->data, (uint) (event_open->duration TO_MS), (uint) (event_open->duration % 1000));
|
||||
|
||||
event_open = NULL;
|
||||
}
|
||||
@ -2089,7 +2105,7 @@ io_log_event(void *hook, void *data)
|
||||
|
||||
en->hook = hook;
|
||||
en->data = data;
|
||||
en->timestamp = last_time;
|
||||
en->timestamp = last_io_time;
|
||||
en->duration = 0;
|
||||
|
||||
event_log_num++;
|
||||
@ -2117,14 +2133,14 @@ io_log_dump(void)
|
||||
struct event_log_entry *en = event_log + (event_log_pos + i) % EVENT_LOG_LENGTH;
|
||||
if (en->hook)
|
||||
log(L_DEBUG " Event 0x%p 0x%p at %8d for %d ms", en->hook, en->data,
|
||||
(int) ((last_time - en->timestamp) TO_MS), (int) (en->duration TO_MS));
|
||||
(int) ((last_io_time - en->timestamp) TO_MS), (int) (en->duration TO_MS));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
watchdog_sigalrm(int sig UNUSED)
|
||||
{
|
||||
/* Update last_time and duration, but skip latency check */
|
||||
/* Update last_io_time and duration, but skip latency check */
|
||||
config->latency_limit = 0xffffffff;
|
||||
io_update_time();
|
||||
|
||||
@ -2137,7 +2153,7 @@ watchdog_start1(void)
|
||||
{
|
||||
io_update_time();
|
||||
|
||||
loop_time = last_time;
|
||||
loop_time = last_io_time;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -2145,7 +2161,7 @@ watchdog_start(void)
|
||||
{
|
||||
io_update_time();
|
||||
|
||||
loop_time = last_time;
|
||||
loop_time = last_io_time;
|
||||
event_log_num = 0;
|
||||
|
||||
if (config->watchdog_timeout)
|
||||
@ -2166,10 +2182,10 @@ watchdog_stop(void)
|
||||
watchdog_active = 0;
|
||||
}
|
||||
|
||||
btime duration = last_time - loop_time;
|
||||
btime duration = last_io_time - loop_time;
|
||||
if (duration > config->watchdog_warning)
|
||||
log(L_WARN "I/O loop cycle took %d ms for %d events",
|
||||
(int) (duration TO_MS), event_log_num);
|
||||
log(L_WARN "I/O loop cycle took %u.%03u ms for %d events",
|
||||
(uint) (duration TO_MS), (uint) (duration % 1000), event_log_num);
|
||||
}
|
||||
|
||||
|
||||
@ -2181,8 +2197,9 @@ void
|
||||
io_init(void)
|
||||
{
|
||||
init_list(&sock_list);
|
||||
init_list(&global_event_list);
|
||||
init_list(&global_work_list);
|
||||
ev_init_list(&global_event_list, &main_birdloop, "Global event list");
|
||||
ev_init_list(&global_work_list, &main_birdloop, "Global work list");
|
||||
ev_init_list(&main_birdloop.event_list, &main_birdloop, "Global fast event list");
|
||||
krt_io_init();
|
||||
// XXX init_times();
|
||||
// XXX update_times();
|
||||
@ -2196,6 +2213,8 @@ static int short_loops = 0;
|
||||
#define SHORT_LOOP_MAX 10
|
||||
#define WORK_EVENTS_MAX 10
|
||||
|
||||
void pipe_drain(int fd);
|
||||
|
||||
void
|
||||
io_loop(void)
|
||||
{
|
||||
@ -2210,22 +2229,28 @@ io_loop(void)
|
||||
watchdog_start1();
|
||||
for(;;)
|
||||
{
|
||||
times_update(&main_timeloop);
|
||||
times_update();
|
||||
events = ev_run_list(&global_event_list);
|
||||
events = ev_run_list_limited(&global_work_list, WORK_EVENTS_MAX) || events;
|
||||
timers_fire(&main_timeloop);
|
||||
events = ev_run_list(&main_birdloop.event_list) || events;
|
||||
timers_fire(&main_birdloop.time, 1);
|
||||
io_close_event();
|
||||
|
||||
// FIXME
|
||||
poll_tout = (events ? 0 : 3000); /* Time in milliseconds */
|
||||
if (t = timers_first(&main_timeloop))
|
||||
if (t = timers_first(&main_birdloop.time))
|
||||
{
|
||||
times_update(&main_timeloop);
|
||||
times_update();
|
||||
timeout = (tm_remains(t) TO_MS) + 1;
|
||||
poll_tout = MIN(poll_tout, timeout);
|
||||
}
|
||||
|
||||
nfds = 0;
|
||||
/* A hack to reload main io_loop() when something has changed asynchronously. */
|
||||
pfd[0].fd = main_birdloop.wakeup_fds[0];
|
||||
pfd[0].events = POLLIN;
|
||||
|
||||
nfds = 1;
|
||||
|
||||
WALK_LIST(n, sock_list)
|
||||
{
|
||||
pfd[nfds] = (struct pollfd) { .fd = -1 }; /* everything other set to 0 by this */
|
||||
@ -2284,7 +2309,9 @@ io_loop(void)
|
||||
|
||||
/* And finally enter poll() to find active sockets */
|
||||
watchdog_stop();
|
||||
birdloop_leave(&main_birdloop);
|
||||
pout = poll(pfd, nfds, poll_tout);
|
||||
birdloop_enter(&main_birdloop);
|
||||
watchdog_start();
|
||||
|
||||
if (pout < 0)
|
||||
@ -2295,7 +2322,15 @@ io_loop(void)
|
||||
}
|
||||
if (pout)
|
||||
{
|
||||
times_update(&main_timeloop);
|
||||
if (pfd[0].revents & POLLIN)
|
||||
{
|
||||
/* IO loop reload requested */
|
||||
pipe_drain(main_birdloop.wakeup_fds[0]);
|
||||
atomic_exchange_explicit(&main_birdloop.ping_sent, 0, memory_order_acq_rel);
|
||||
continue;
|
||||
}
|
||||
|
||||
times_update();
|
||||
|
||||
/* guaranteed to be non-empty */
|
||||
current_sock = SKIP_BACK(sock, n, HEAD(sock_list));
|
||||
|
@ -681,9 +681,9 @@ krt_scan_timer_kick(struct krt_proto *p)
|
||||
*/
|
||||
|
||||
static int
|
||||
krt_preexport(struct channel *c, rte *e)
|
||||
krt_preexport(struct channel *C, rte *e)
|
||||
{
|
||||
if (e->src->proto == c->proto)
|
||||
if (e->src->proto == C->proto)
|
||||
return -1;
|
||||
|
||||
if (!krt_capable(e))
|
||||
|
@ -15,6 +15,7 @@
|
||||
* user's manual.
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
@ -35,8 +36,10 @@ static FILE *dbgf;
|
||||
static list *current_log_list;
|
||||
static char *current_syslog_name; /* NULL -> syslog closed */
|
||||
|
||||
static _Atomic uint max_thread_id = ATOMIC_VAR_INIT(1);
|
||||
static _Thread_local uint this_thread_id;
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
#define THIS_THREAD_ID (this_thread_id ?: (this_thread_id = atomic_fetch_add_explicit(&max_thread_id, 1, memory_order_acq_rel)))
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
@ -48,15 +51,6 @@ static pthread_t main_thread;
|
||||
void main_thread_init(void) { main_thread = pthread_self(); }
|
||||
static int main_thread_self(void) { return pthread_equal(pthread_self(), main_thread); }
|
||||
|
||||
#else
|
||||
|
||||
static inline void log_lock(void) { }
|
||||
static inline void log_unlock(void) { }
|
||||
void main_thread_init(void) { }
|
||||
static int main_thread_self(void) { return 1; }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
#include <sys/syslog.h>
|
||||
@ -189,7 +183,7 @@ log_commit(int class, buffer *buf)
|
||||
l->pos += msg_len;
|
||||
}
|
||||
|
||||
fprintf(l->fh, "%s <%s> ", tbuf, class_names[class]);
|
||||
fprintf(l->fh, "%s [%04x] <%s> ", tbuf, THIS_THREAD_ID, class_names[class]);
|
||||
}
|
||||
fputs(buf->start, l->fh);
|
||||
fputc('\n', l->fh);
|
||||
@ -299,6 +293,8 @@ die(const char *msg, ...)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static struct timespec dbg_time_start;
|
||||
|
||||
/**
|
||||
* debug - write to debug output
|
||||
* @msg: a printf-like message
|
||||
@ -311,12 +307,33 @@ debug(const char *msg, ...)
|
||||
{
|
||||
#define MAX_DEBUG_BUFSIZE 16384
|
||||
va_list args;
|
||||
char buf[MAX_DEBUG_BUFSIZE];
|
||||
char buf[MAX_DEBUG_BUFSIZE], *pos = buf;
|
||||
int max = MAX_DEBUG_BUFSIZE;
|
||||
|
||||
va_start(args, msg);
|
||||
if (dbgf)
|
||||
{
|
||||
if (bvsnprintf(buf, MAX_DEBUG_BUFSIZE, msg, args) < 0)
|
||||
struct timespec dbg_time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &dbg_time);
|
||||
uint nsec;
|
||||
uint sec;
|
||||
|
||||
if (dbg_time.tv_nsec > dbg_time_start.tv_nsec)
|
||||
{
|
||||
nsec = dbg_time.tv_nsec - dbg_time_start.tv_nsec;
|
||||
sec = dbg_time.tv_sec - dbg_time_start.tv_sec;
|
||||
}
|
||||
else
|
||||
{
|
||||
nsec = 1000000000 + dbg_time.tv_nsec - dbg_time_start.tv_nsec;
|
||||
sec = dbg_time.tv_sec - dbg_time_start.tv_sec - 1;
|
||||
}
|
||||
|
||||
int n = bsnprintf(pos, max, "%u.%09u: [%04x] ", sec, nsec, THIS_THREAD_ID);
|
||||
pos += n;
|
||||
max -= n;
|
||||
|
||||
if (bvsnprintf(pos, max, msg, args) < 0)
|
||||
bug("Extremely long debug output, split it.");
|
||||
|
||||
fputs(buf, dbgf);
|
||||
@ -422,6 +439,8 @@ done:
|
||||
void
|
||||
log_init_debug(char *f)
|
||||
{
|
||||
clock_gettime(CLOCK_MONOTONIC, &dbg_time_start);
|
||||
|
||||
if (dbgf && dbgf != stderr)
|
||||
fclose(dbgf);
|
||||
if (!f)
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/socket.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/locking.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/string.h"
|
||||
#include "nest/rt.h"
|
||||
@ -116,7 +117,7 @@ add_num_const(char *name, int val, const char *file, const uint line)
|
||||
struct f_val *v = cfg_alloc(sizeof(struct f_val));
|
||||
*v = (struct f_val) { .type = T_INT, .val.i = val };
|
||||
struct symbol *sym = cf_get_symbol(name);
|
||||
if (sym->class && (sym->scope == conf_this_scope))
|
||||
if (sym->class && cf_symbol_is_local(sym))
|
||||
cf_error("Error reading value for %s from %s:%d: already defined", name, file, line);
|
||||
|
||||
cf_define_symbol(sym, SYM_CONSTANT | T_INT, val, v);
|
||||
@ -873,13 +874,16 @@ main(int argc, char **argv)
|
||||
dmalloc_debug(0x2f03d00);
|
||||
#endif
|
||||
|
||||
times_update();
|
||||
parse_args(argc, argv);
|
||||
log_switch(1, NULL, NULL);
|
||||
|
||||
the_bird_lock();
|
||||
|
||||
random_init();
|
||||
net_init();
|
||||
resource_init();
|
||||
timer_init();
|
||||
birdloop_init();
|
||||
olock_init();
|
||||
rt_init();
|
||||
io_init();
|
||||
@ -927,6 +931,7 @@ main(int argc, char **argv)
|
||||
dup2(0, 2);
|
||||
}
|
||||
|
||||
|
||||
main_thread_init();
|
||||
|
||||
write_pid_file();
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "test/birdtest.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/io-loop.h"
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h>
|
||||
@ -64,6 +65,9 @@ bt_init(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
|
||||
/* We have no interest in stdin */
|
||||
close(0);
|
||||
|
||||
initstate(BT_RANDOM_SEED, (char *) bt_random_state, sizeof(bt_random_state));
|
||||
|
||||
bt_verbose = 0;
|
||||
@ -120,9 +124,11 @@ bt_init(int argc, char *argv[])
|
||||
clock_gettime(CLOCK_MONOTONIC, &bt_begin);
|
||||
bt_suite_case_begin = bt_suite_begin = bt_begin;
|
||||
|
||||
the_bird_lock();
|
||||
resource_init();
|
||||
ev_init_list(&global_event_list);
|
||||
|
||||
ev_init_list(&global_event_list, &main_birdloop, "Global event list in unit tests");
|
||||
ev_init_list(&global_work_list, &main_birdloop, "Global work list in unit tests");
|
||||
birdloop_init();
|
||||
return;
|
||||
|
||||
usage:
|
||||
|
@ -53,6 +53,8 @@ cf_file_read(byte *dest, uint max_len, int fd)
|
||||
return l;
|
||||
}
|
||||
|
||||
void resource_sys_init(void);
|
||||
|
||||
void
|
||||
bt_bird_init(void)
|
||||
{
|
||||
@ -61,7 +63,6 @@ bt_bird_init(void)
|
||||
log_switch(bt_verbose != 0, NULL, NULL);
|
||||
|
||||
olock_init();
|
||||
timer_init();
|
||||
rt_init();
|
||||
io_init();
|
||||
if_init();
|
||||
@ -73,6 +74,7 @@ bt_bird_init(void)
|
||||
void bt_bird_cleanup(void)
|
||||
{
|
||||
config = new_config = NULL;
|
||||
the_bird_unlock();
|
||||
}
|
||||
|
||||
static char *
|
||||
|
Loading…
Reference in New Issue
Block a user