summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml25
-rw-r--r--INSTALL4
-rw-r--r--aclocal.m414
-rw-r--r--bird-gdb.py325
-rw-r--r--conf/conf.c4
-rw-r--r--conf/conf.h2
-rw-r--r--configure.ac46
-rw-r--r--doc/bird.sgml17
-rw-r--r--filter/config.Y19
-rw-r--r--filter/data.h1
-rw-r--r--filter/decl.m424
-rw-r--r--filter/f-inst.c37
-rw-r--r--filter/filter.c176
-rw-r--r--filter/filter.h4
-rw-r--r--filter/filter_test.c1
-rw-r--r--filter/test.conf1
-rw-r--r--filter/tree_test.c2
-rw-r--r--lib/Makefile2
-rw-r--r--lib/birdlib.h8
-rw-r--r--lib/coro.h31
-rw-r--r--lib/event.c193
-rw-r--r--lib/event.h71
-rw-r--r--lib/event_test.c7
-rw-r--r--lib/flowspec_test.c11
-rw-r--r--lib/hash.h6
-rw-r--r--lib/hash_test.c6
-rw-r--r--lib/io-loop.h57
-rw-r--r--lib/lists.c24
-rw-r--r--lib/lists.h13
-rw-r--r--lib/locking.h66
-rw-r--r--lib/mempool.c21
-rw-r--r--lib/rcu.c79
-rw-r--r--lib/rcu.h55
-rw-r--r--lib/resource.c133
-rw-r--r--lib/resource.h22
-rw-r--r--lib/slab.c19
-rw-r--r--lib/socket.h4
-rw-r--r--lib/timer.c115
-rw-r--r--lib/timer.h41
-rw-r--r--nest/a-path_test.c17
-rw-r--r--nest/a-set_test.c17
-rw-r--r--nest/cli.c8
-rw-r--r--nest/cmds.c13
-rw-r--r--nest/config.Y20
-rw-r--r--nest/iface.c15
-rw-r--r--nest/iface.h2
-rw-r--r--nest/limit.h49
-rw-r--r--nest/neighbor.c10
-rw-r--r--nest/proto-hooks.c40
-rw-r--r--nest/proto.c1325
-rw-r--r--nest/protocol.h207
-rw-r--r--nest/route.h521
-rw-r--r--nest/rt-attr.c394
-rw-r--r--nest/rt-dev.c19
-rw-r--r--nest/rt-show.c103
-rw-r--r--nest/rt-table.c3163
-rw-r--r--proto/babel/babel.c150
-rw-r--r--proto/babel/babel.h1
-rw-r--r--proto/bfd/Makefile4
-rw-r--r--proto/bfd/bfd.c172
-rw-r--r--proto/bfd/bfd.h6
-rw-r--r--proto/bfd/config.Y1
-rw-r--r--proto/bfd/io.c535
-rw-r--r--proto/bfd/io.h34
-rw-r--r--proto/bfd/packets.c4
-rw-r--r--proto/bgp/attrs.c203
-rw-r--r--proto/bgp/bgp.c125
-rw-r--r--proto/bgp/bgp.h33
-rw-r--r--proto/bgp/packets.c25
-rw-r--r--proto/mrt/mrt.c93
-rw-r--r--proto/mrt/mrt.h7
-rw-r--r--proto/ospf/iface.c9
-rw-r--r--proto/ospf/neighbor.c4
-rw-r--r--proto/ospf/ospf.c93
-rw-r--r--proto/ospf/ospf.h2
-rw-r--r--proto/ospf/rt.c62
-rw-r--r--proto/ospf/topology.c12
-rw-r--r--proto/ospf/topology.h2
-rw-r--r--proto/perf/perf.c19
-rw-r--r--proto/pipe/config.Y8
-rw-r--r--proto/pipe/pipe.c136
-rw-r--r--proto/pipe/pipe.h2
-rw-r--r--proto/radv/radv.c27
-rw-r--r--proto/rip/rip.c110
-rw-r--r--proto/rip/rip.h1
-rw-r--r--proto/rpki/config.Y1
-rw-r--r--proto/rpki/packets.c46
-rw-r--r--proto/rpki/rpki.c66
-rw-r--r--proto/rpki/rpki.h2
-rw-r--r--proto/rpki/ssh_transport.c2
-rw-r--r--proto/rpki/tcp_transport.c2
-rw-r--r--proto/rpki/transport.c2
-rw-r--r--proto/static/static.c76
-rw-r--r--sysdep/bsd/krt-sock.c26
-rw-r--r--sysdep/linux/netlink.c77
-rw-r--r--sysdep/unix/Makefile2
-rw-r--r--sysdep/unix/alloc.c222
-rw-r--r--sysdep/unix/coroutine.c205
-rw-r--r--sysdep/unix/io-loop.c583
-rw-r--r--sysdep/unix/io-loop.h59
-rw-r--r--sysdep/unix/io.c176
-rw-r--r--sysdep/unix/krt.c434
-rw-r--r--sysdep/unix/krt.h14
-rw-r--r--sysdep/unix/log.c76
-rw-r--r--sysdep/unix/main.c28
-rw-r--r--sysdep/unix/unix.h1
-rw-r--r--test/birdtest.c5
-rw-r--r--test/bt-utils.c7
108 files changed, 7108 insertions, 4493 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 65a0a05b..39202098 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -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.labs.nic.cz/labs/bird:fedora-33-amd64
-build-centos-7-amd64:
- <<: *build-linux
- image: registry.labs.nic.cz/labs/bird:centos-7-amd64
-
build-centos-8-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:centos-8-amd64
-build-ubuntu-14_04-amd64:
- <<: *build-linux
- image: registry.labs.nic.cz/labs/bird:ubuntu-14.04-amd64
-
build-ubuntu-16_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-16.04-amd64
@@ -468,13 +450,6 @@ pkg-fedora-34-amd64:
needs: [build-fedora-34-amd64]
image: registry.labs.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.labs.nic.cz/labs/bird:centos-7-amd64
-
pkg-centos-8-amd64:
<<: *pkg-rpm-wa
needs: [build-centos-8-amd64]
diff --git a/INSTALL b/INSTALL
index 7a179284..c1094ee5 100644
--- a/INSTALL
+++ b/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
diff --git a/aclocal.m4 b/aclocal.m4
index 1613d680..3405b85b 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -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_THREAD_LOCAL],
[
@@ -9,14 +10,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]
- )
+ )])
)
])
diff --git a/bird-gdb.py b/bird-gdb.py
index 3cf65a9c..fe7827fe 100644
--- a/bird-gdb.py
+++ b/bird-gdb.py
@@ -4,9 +4,10 @@ class BIRDPrinter:
@classmethod
def lookup(cls, val):
- if val.type.code != cls.typeCode:
+ t = val.type.strip_typedefs()
+ if t.code != cls.typeCode:
return None
- if val.type.tag != cls.typeTag:
+ if t.tag != cls.typeTag:
return None
return cls(val)
@@ -123,7 +124,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"
@@ -141,6 +142,323 @@ 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()
+
+class BIRDResourceSize():
+ def __init__(self, netto, overhead, free):
+ self.netto = netto
+ self.overhead = overhead
+ self.free = free
+
+ def __str__(self):
+ ns = str(self.netto)
+ os = str(self.overhead)
+ fs = str(self.free)
+
+ return "{: >12s} | {: >12s} | {: >12s}".format(ns, os, fs)
+
+ def __add__(self, val):
+ return BIRDResourceSize(self.netto + val.netto, self.overhead + val.overhead, self.free + val.free)
+
+class BIRDResource():
+ def __init__(self, val):
+ self.val = val
+
+ def __str__(self):
+ return f"Item {self.val.address} of class \"{self.val['class']['name'].string()}\""
+
+ def memsize(self):
+ if str(self.val["class"]["memsize"]) == '0x0':
+ size = self.val["class"]["size"]
+ ressize = gdb.lookup_type("struct resource").sizeof
+ return BIRDResourceSize(size - ressize, ressize, 0)
+ else:
+ raise Exception(f"Resource class {self.val['class']['name']} with defined memsize() not known by Python")
+
+ def parse(self):
+ pass
+
+class BIRDMBResource(BIRDResource):
+ def __init__(self, val):
+ self.mbtype = gdb.lookup_type("struct mblock")
+ self.val = val.cast(self.mbtype)
+
+ def memsize(self):
+ return BIRDResourceSize(self.val["size"], 8 + self.mbtype.sizeof, 0)
+
+ def __str__(self):
+ return f"Standalone memory block {self.val.address} of size {self.val['size']}, data at {self.val['data'].address}"
+
+class BIRDLinPoolResource(BIRDResource):
+ def __init__(self, val):
+ self.lptype = gdb.lookup_type("struct linpool")
+ self.val = val.cast(self.lptype)
+ self.info = None
+
+ def count_chunk(self, which):
+ cnt = 0
+ chunk = self.val[which]
+ while str(chunk) != '0x0':
+ cnt += 1
+ chunk = chunk.dereference()["next"]
+ return cnt
+
+ def parse(self):
+ self.info = {
+ "std_chunks": self.count_chunk("first"),
+ "large_chunks": self.count_chunk("first_large"),
+ }
+
+ def memsize(self):
+ if self.info is None:
+ self.parse()
+
+ overhead = (8 - 8*self.val["use_pages"]) + gdb.lookup_type("struct lp_chunk").sizeof
+ return BIRDResourceSize(
+ self.val["total"] + self.val["total_large"],
+ (self.info["std_chunks"] + self.info["large_chunks"]) * overhead,
+ 0)
+
+ def __str__(self):
+ if self.info is None:
+ self.parse()
+
+ return f"Linpool {self.val.address} with {self.info['std_chunks']} standard chunks of size {self.val['chunk_size']} and {self.info['large_chunks']} large chunks"
+
+class BIRDSlabResource(BIRDResource):
+ def __init__(self, val):
+ self.slabtype = gdb.lookup_type("struct slab")
+ self.val = val.cast(self.slabtype)
+ self.info = None
+
+ def count_heads_item(self, item):
+ self.hcnt += 1
+ self.used += item.dereference().cast(self.slheadtype)["num_full"]
+
+ def count_heads(self, which):
+ self.hcnt = 0
+ self.used = 0
+ BIRDList(self.val[which + "_heads"]).walk(self.count_heads_item)
+ self.info[which + "_heads"] = self.hcnt
+ self.info[which + "_used"] = self.used
+ return (self.hcnt, self.used)
+
+ def parse(self):
+ self.page_size = gdb.lookup_symbol("page_size")[0].value()
+ self.slheadtype = gdb.lookup_type("struct sl_head")
+ self.info = {}
+ self.count_heads("empty")
+ self.count_heads("partial")
+ self.count_heads("full")
+
+ def memsize(self):
+ if self.info is None:
+ self.parse()
+
+ total_used = self.info["empty_used"] + self.info["partial_used"] + self.info["full_used"]
+ total_heads = self.info["empty_heads"] + self.info["partial_heads"] + self.info["full_heads"]
+
+ eff_size = total_used * self.val["obj_size"]
+ free_size = self.info["empty_heads"] * self.page_size
+ total_size = total_heads * self.page_size + self.slabtype.sizeof
+
+ return BIRDResourceSize( eff_size, total_size - free_size - eff_size, free_size)
+
+ def __str__(self):
+ if self.info is None:
+ self.parse()
+
+ return f"Slab {self.val.address} " + ", ".join([
+ f"{self.info[x + '_heads']} {x} heads" for x in [ "empty", "partial", "full" ]]) + \
+ f", {self.val['objs_per_slab']} objects of size {self.val['obj_size']} per head"
+
+
+class BIRDPoolResource(BIRDResource):
+ def __init__(self, val):
+ self.pooltype = gdb.lookup_type("struct pool")
+ self.resptrtype = gdb.lookup_type("struct resource").pointer()
+ self.page_size = gdb.lookup_symbol("page_size")[0].value()
+ self.val = val.cast(self.pooltype)
+ self.items = None
+
+ def parse_inside(self, val):
+ self.items.append(BIRDNewResource(val.cast(self.resptrtype).dereference()))
+
+ def parse(self):
+ self.items = []
+ BIRDList(self.val["inside"]).walk(self.parse_inside)
+
+ def free_pages(self):
+ if str(self.val['pages']) == '0x0':
+ return 0
+ else:
+ return self.val['pages'].dereference()['free']
+
+ def memsize(self):
+ if self.items is None:
+ self.parse()
+
+ sum = BIRDResourceSize(0, self.pooltype.sizeof, self.free_pages() * self.page_size)
+# for i in self.items:
+# sum += i.memsize()
+
+ return sum
+
+ def __str__(self):
+ if self.items is None:
+ self.parse()
+
+# for i in self.items:
+# print(i)
+
+ return f"Resource pool {self.val.address} \"{self.val['name'].string()}\" containing {len(self.items)} items and {self.free_pages()} free pages"
+
+BIRDResourceMap = {
+ "mbl_memsize": BIRDMBResource,
+ "pool_memsize": BIRDPoolResource,
+ "lp_memsize": BIRDLinPoolResource,
+ "slab_memsize": BIRDSlabResource,
+ }
+
+def BIRDNewResource(res):
+ cms = res["class"].dereference()["memsize"]
+ for cx in BIRDResourceMap:
+ if cms == gdb.lookup_symbol(cx)[0].value():
+ return BIRDResourceMap[cx](res)
+
+ return BIRDResource(res)
+
+
+class BIRDResourcePrinter(BIRDPrinter):
+ "Print BIRD's resource"
+ typeCode = gdb.TYPE_CODE_STRUCT
+ typeTag = "resource"
+
+ def __init__(self, val):
+ super(BIRDResourcePrinter, self).__init__(val)
+ self.resource = BIRDNewResource(val)
+ self.resource.parse()
+ self.resourcetype = gdb.lookup_type("struct resource")
+
+ if type(self.resource) == BIRDPoolResource:
+ self.children = self.pool_children
+
+ def pool_children(self):
+ return iter([ ("\n", i.val.cast(self.resourcetype)) for i in self.resource.items ])
+
+ def to_string(self):
+ return f"[ {str(self.resource.memsize())} ] {str(self.resource)}"
+
+
def register_printers(objfile):
objfile.pretty_printers.append(BIRDFInstPrinter.lookup)
objfile.pretty_printers.append(BIRDFValPrinter.lookup)
@@ -148,6 +466,7 @@ def register_printers(objfile):
objfile.pretty_printers.append(BIRDFLineItemPrinter.lookup)
objfile.pretty_printers.append(BIRDFLinePrinter.lookup)
objfile.pretty_printers.append(BIRDFExecStackPrinter.lookup)
+ objfile.pretty_printers.append(BIRDResourcePrinter.lookup)
register_printers(gdb.current_objfile())
diff --git a/conf/conf.c b/conf/conf.c
index a2b01667..e8f1559a 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -89,7 +89,7 @@ int undo_available; /* Undo was not requested from last reconfiguration */
struct config *
config_alloc(const char *name)
{
- pool *p = rp_new(&root_pool, "Config");
+ pool *p = rp_new(&root_pool, &main_birdloop, "Config");
linpool *l = lp_new_default(p);
struct config *c = lp_allocz(l, sizeof(struct config));
@@ -196,7 +196,7 @@ void
config_free(struct config *c)
{
if (c)
- rfree(c->pool);
+ rp_free(c->pool, &root_pool);
}
void
diff --git a/conf/conf.h b/conf/conf.h
index 3bc37959..4f6aa6eb 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -35,6 +35,7 @@ struct config {
u32 proto_default_debug; /* Default protocol debug mask */
u32 proto_default_mrtdump; /* Default protocol mrtdump mask */
u32 channel_default_debug; /* Default channel debug mask */
+ u16 filter_vstk, filter_estk; /* Filter stack depth */
struct timeformat tf_route; /* Time format for 'show route' */
struct timeformat tf_proto; /* Time format for 'show protocol' */
struct timeformat tf_log; /* Time format for the logfile */
@@ -44,6 +45,7 @@ struct config {
int cli_debug; /* Tracing of CLI connections and commands */
int latency_debug; /* I/O loop tracks duration of each event */
+ 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) */
diff --git a/configure.ac b/configure.ac
index 64181d29..5c0cf002 100644
--- a/configure.ac
+++ b/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
@@ -351,9 +338,15 @@ 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_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])],
@@ -402,7 +395,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
@@ -467,7 +460,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])
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 39dadaf2..d1a3b70f 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -145,6 +145,13 @@ BIRD executable by configuring out routing protocols you don't use, and
<p>You can pass several command-line options to bird:
<descrip>
+ <tag><label id="argv-block">-B <m/exp/</tag>
+ allocate memory using 2^<cf/exp/ byte sized blocks;
+ if you're expecting high memory load, raise this to
+ reduce number of allocated memory pages. For a million routes
+ in one table, the recommended setting is 18.
+ Default is your system page size, typically 12 for 4096 bytes.
+
<tag><label id="argv-config">-c <m/config name/</tag>
use given configuration file instead of <it/prefix/<file>/etc/bird.conf</file>.
@@ -1684,7 +1691,7 @@ Common route attributes are:
<tag><label id="rta-source"><m/enum/ source</tag>
what protocol has told me about this route. Possible values:
- <cf/RTS_DUMMY/, <cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
+ <cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
<cf/RTS_RIP/, <cf/RTS_OSPF/, <cf/RTS_OSPF_IA/, <cf/RTS_OSPF_EXT1/,
<cf/RTS_OSPF_EXT2/, <cf/RTS_BGP/, <cf/RTS_PIPE/, <cf/RTS_BABEL/.
@@ -4117,6 +4124,14 @@ include standard channel config options; see the example below.
<tag><label id="pipe-peer-table">peer table <m/table/</tag>
Defines secondary routing table to connect to. The primary one is
selected by the <cf/table/ keyword.
+
+ <tag><label id="pipe-max-generation">max generation <m/expr/</tag>
+ Sets maximal generation of route that may pass through this pipe.
+ The generation value is increased by one by each pipe on its path.
+ Not meeting this requirement causes an error message complaining about
+ an overpiped route. If you have long chains of pipes, you probably want
+ to raise this value; anyway the default of 16 should be enough for even
+ most strange uses. Maximum is 254.
</descrip>
<sect1>Attributes
diff --git a/filter/config.Y b/filter/config.Y
index 7820e719..8034b790 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -248,10 +248,6 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
getter = f_new_inst(FI_VAR_GET, lval->sym);
break;
- case F_LVAL_PREFERENCE:
- setter = f_new_inst(FI_PREF_SET, expr);
- getter = f_new_inst(FI_PREF_GET);
- break;
case F_LVAL_SA:
setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
getter = f_new_inst(FI_RTA_GET, lval->sa);
@@ -288,7 +284,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
EMPTY,
FILTER, WHERE, EVAL, ATTRIBUTE,
- BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT)
+ BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT, STACKS)
%nonassoc THEN
%nonassoc ELSE
@@ -312,6 +308,12 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
CF_GRAMMAR
+conf: FILTER STACKS expr expr ';' {
+ new_config->filter_vstk = $3;
+ new_config->filter_estk = $4;
+ }
+ ;
+
conf: filter_def ;
filter_def:
FILTER symbol { $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL); cf_push_scope( $2 ); }
@@ -751,6 +753,7 @@ static_attr:
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
+ | PREFERENCE { $$ = f_new_static_attr(T_INT, SA_PREF, 0); }
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
;
@@ -777,8 +780,6 @@ term:
| constant { $$ = $1; }
| constructor { $$ = $1; }
- | PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
-
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
| dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
@@ -871,9 +872,6 @@ cmd:
cf_error( "This static attribute is read-only.");
$$ = f_new_inst(FI_RTA_SET, $3, $1);
}
- | PREFERENCE '=' term ';' {
- $$ = f_new_inst(FI_PREF_SET, $3);
- }
| UNSET '(' dynamic_attr ')' ';' {
$$ = f_new_inst(FI_EA_UNSET, $3);
}
@@ -916,7 +914,6 @@ get_cf_position:
lvalue:
CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
- | PREFERENCE { $$ = (struct f_lval) { .type = F_LVAL_PREFERENCE }; }
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
| dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
diff --git a/filter/data.h b/filter/data.h
index d296776d..45246f9f 100644
--- a/filter/data.h
+++ b/filter/data.h
@@ -100,6 +100,7 @@ enum f_sa_code {
SA_IFNAME,
SA_IFINDEX,
SA_WEIGHT,
+ SA_PREF,
SA_GW_MPLS,
} PACKED;
diff --git a/filter/decl.m4 b/filter/decl.m4
index 5242c04c..44537aaa 100644
--- a/filter/decl.m4
+++ b/filter/decl.m4
@@ -32,6 +32,7 @@ m4_divert(-1)m4_dnl
#
# 101 content of per-inst struct
# 102 constructor arguments
+# 110 constructor attributes
# 103 constructor body
# 104 dump line item content
# (there may be nothing in dump-line content and
@@ -45,6 +46,7 @@ m4_divert(-1)m4_dnl
# Here are macros to allow you to _divert to the right directions.
m4_define(FID_STRUCT_IN, `m4_divert(101)')
m4_define(FID_NEW_ARGS, `m4_divert(102)')
+m4_define(FID_NEW_ATTRIBUTES, `m4_divert(110)')
m4_define(FID_NEW_BODY, `m4_divert(103)')
m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
@@ -106,15 +108,18 @@ FID_STRUCT_IN()m4_dnl
struct f_inst * f$1;
FID_NEW_ARGS()m4_dnl
, struct f_inst * f$1
+FID_NEW_ATTRIBUTES()m4_dnl
+NONNULL(m4_eval($1+1))
FID_NEW_BODY()m4_dnl
whati->f$1 = f$1;
-for (const struct f_inst *child = f$1; child; child = child->next) {
- what->size += child->size;
+const struct f_inst *child$1 = f$1;
+do {
+ what->size += child$1->size;
FID_IFCONST([[
- if (child->fi_code != FI_CONSTANT)
+ if (child$1->fi_code != FI_CONSTANT)
constargs = 0;
]])
-}
+} while (child$1 = child$1->next);
FID_LINEARIZE_BODY
pos = linearize(dest, whati->f$1, pos);
FID_INTERPRET_BODY()')
@@ -190,6 +195,7 @@ FID_INTERPRET_BODY()')
# that was needed in the former implementation.
m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
m4_define(LINEX_, `do {
+ if (fstk->ecnt + 1 >= fstk->elen) runtime("Filter execution stack overflow");
fstk->estk[fstk->ecnt].pos = 0;
fstk->estk[fstk->ecnt].line = $1;
fstk->estk[fstk->ecnt].ventry = fstk->vcnt;
@@ -227,7 +233,7 @@ FID_INTERPRET_BODY()')
# state the result and put it to the right place.
m4_define(RESULT, `RESULT_TYPE([[$1]]) RESULT_([[$1]],[[$2]],[[$3]])')
m4_define(RESULT_, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])')
-m4_define(RESULT_VAL, `FID_HIC(, [[do { res = $1; fstk->vcnt++; } while (0)]],
+m4_define(RESULT_VAL, `FID_HIC(, [[do { res = $1; f_vcnt_check_overflow(1); fstk->vcnt++; } while (0)]],
[[return fi_constant(what, $1)]])')
m4_define(RESULT_VOID, `RESULT_VAL([[ (struct f_val) { .type = T_VOID } ]])')
@@ -309,7 +315,9 @@ m4_undivert(107)m4_dnl
FID_NEW()m4_dnl Constructor and interpreter code together
FID_HIC(
[[m4_dnl Public declaration of constructor in H file
-struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
+struct f_inst *
+m4_undivert(110)m4_dnl
+f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
m4_undivert(102)m4_dnl
);]],
[[m4_dnl The one case in The Big Switch inside interpreter
@@ -321,7 +329,9 @@ m4_undivert(102)m4_dnl
break;
]],
[[m4_dnl Constructor itself
-struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
+struct f_inst *
+m4_undivert(110)m4_dnl
+f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
m4_undivert(102)m4_dnl
)
{
diff --git a/filter/f-inst.c b/filter/f-inst.c
index b876a937..0341a2f1 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -519,20 +519,21 @@
{
STATIC_ATTR;
ACCESS_RTE;
- struct rta *rta = (*fs->rte)->attrs;
+ struct rta *rta = fs->rte->attrs;
switch (sa.sa_code)
{
case SA_FROM: RESULT(sa.f_type, ip, rta->from); break;
case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break;
- case SA_NET: RESULT(sa.f_type, net, (*fs->rte)->net->n.addr); break;
- case SA_PROTO: RESULT(sa.f_type, s, rta->src->proto->name); break;
+ case SA_NET: RESULT(sa.f_type, net, fs->rte->net); break;
+ case SA_PROTO: RESULT(sa.f_type, s, fs->rte->src->owner->name); break;
case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break;
case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break;
case SA_DEST: RESULT(sa.f_type, i, rta->dest); break;
case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break;
case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break;
case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break;
+ case SA_PREF: RESULT(sa.f_type, i, rta->pref); break;
case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break;
default:
@@ -549,7 +550,7 @@
f_rta_cow(fs);
{
- struct rta *rta = (*fs->rte)->attrs;
+ struct rta *rta = fs->rte->attrs;
switch (sa.sa_code)
{
@@ -561,7 +562,8 @@
{
ip_addr ip = v1.val.ip;
struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL;
- neighbor *n = neigh_find(rta->src->proto, ip, ifa, 0);
+ /* XXX this code supposes that every owner is a protocol XXX */
+ neighbor *n = neigh_find(SKIP_BACK(struct proto, sources, fs->rte->src->owner), ip, ifa, 0);
if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" );
@@ -637,6 +639,10 @@
}
break;
+ case SA_PREF:
+ rta->pref = v1.val.i;
+ break;
+
default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
}
@@ -804,20 +810,6 @@
}
}
- INST(FI_PREF_GET, 0, 1) {
- ACCESS_RTE;
- RESULT(T_INT, i, (*fs->rte)->pref);
- }
-
- INST(FI_PREF_SET, 1, 0) {
- ACCESS_RTE;
- ARG(1,T_INT);
- if (v1.val.i > 0xFFFF)
- runtime( "Setting preference value out of bounds" );
- f_rte_cow(fs);
- (*fs->rte)->pref = v1.val.i;
- }
-
INST(FI_LENGTH, 1, 1) { /* Get length of */
ARG_ANY(1);
switch(v1.type) {
@@ -1003,6 +995,7 @@
curline.vbase = curline.ventry;
/* Storage for local variables */
+ f_vcnt_check_overflow(sym->function->vars);
memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars);
fstk->vcnt += sym->function->vars;
}
@@ -1219,10 +1212,10 @@
INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */
NEVER_CONSTANT;
RTC(1);
- struct rtable *table = rtc->table;
+ rtable *table = rtc->table;
ACCESS_RTE;
ACCESS_EATTRS;
- const net_addr *net = (*fs->rte)->net->n.addr;
+ const net_addr *net = fs->rte->net;
/* We ignore temporary attributes, probably not a problem here */
/* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
@@ -1251,7 +1244,7 @@
ARG(1, T_NET);
ARG(2, T_INT);
RTC(3);
- struct rtable *table = rtc->table;
+ rtable *table = rtc->table;
u32 as = v2.val.i;
diff --git a/filter/filter.c b/filter/filter.c
index e505d570..625b3ade 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -50,36 +50,31 @@ enum f_exception {
FE_RETURN = 0x1,
};
-
-struct filter_stack {
- /* Value stack for execution */
-#define F_VAL_STACK_MAX 4096
- uint vcnt; /* Current value stack size; 0 for empty */
- uint ecnt; /* Current execute stack size; 0 for empty */
-
- struct f_val vstk[F_VAL_STACK_MAX]; /* The stack itself */
-
- /* Instruction stack for execution */
-#define F_EXEC_STACK_MAX 4096
- struct {
- const struct f_line *line; /* The line that is being executed */
- uint pos; /* Instruction index in the line */
- uint ventry; /* Value stack depth on entry */
- uint vbase; /* Where to index variable positions from */
- enum f_exception emask; /* Exception mask */
- } estk[F_EXEC_STACK_MAX];
+struct filter_exec_stack {
+ const struct f_line *line; /* The line that is being executed */
+ uint pos; /* Instruction index in the line */
+ uint ventry; /* Value stack depth on entry */
+ uint vbase; /* Where to index variable positions from */
+ enum f_exception emask; /* Exception mask */
};
/* Internal filter state, to be allocated on stack when executing filters */
struct filter_state {
/* Stacks needed for execution */
- struct filter_stack *stack;
+ struct filter_stack {
+ /* Current filter stack depth */
- /* The route we are processing. This may be NULL to indicate no route available. */
- struct rte **rte;
+ /* Value stack */
+ uint vcnt, vlen;
+ struct f_val *vstk;
- /* The old rta to be freed after filters are done. */
- struct rta *old_rta;
+ /* Instruction stack for execution */
+ uint ecnt, elen;
+ struct filter_exec_stack *estk;
+ } stack;
+
+ /* The route we are processing. This may be NULL to indicate no route available. */
+ struct rte *rte;
/* Cached pointer to ea_list */
struct ea_list **eattrs;
@@ -95,21 +90,16 @@ struct filter_state {
};
_Thread_local static struct filter_state filter_state;
-_Thread_local static struct filter_stack filter_stack;
void (*bt_assert_hook)(int result, const struct f_line_item *assert);
-static inline void f_cache_eattrs(struct filter_state *fs)
-{
- fs->eattrs = &((*fs->rte)->attrs->eattrs);
-}
+#define _f_stack_init(fs, px, def) ((fs).stack.px##stk = alloca(sizeof(*(fs).stack.px##stk) * ((fs).stack.px##len = (config && config->filter_##px##stk) ? config->filter_##px##stk : (def))))
-static inline void f_rte_cow(struct filter_state *fs)
-{
- if (!((*fs->rte)->flags & REF_COW))
- return;
+#define f_stack_init(fs) ( _f_stack_init(fs, v, 128), _f_stack_init(fs, e, 128) )
- *fs->rte = rte_cow(*fs->rte);
+static inline void f_cache_eattrs(struct filter_state *fs)
+{
+ fs->eattrs = &(fs->rte->attrs->eattrs);
}
/*
@@ -118,22 +108,16 @@ static inline void f_rte_cow(struct filter_state *fs)
static void
f_rta_cow(struct filter_state *fs)
{
- if (!rta_is_cached((*fs->rte)->attrs))
+ if (!rta_is_cached(fs->rte->attrs))
return;
- /* Prepare to modify rte */
- f_rte_cow(fs);
-
- /* Store old rta to free it later, it stores reference from rte_cow() */
- fs->old_rta = (*fs->rte)->attrs;
-
/*
* Get shallow copy of rta. Fields eattrs and nexthops of rta are shared
* with fs->old_rta (they will be copied when the cached rta will be obtained
* at the end of f_run()), also the lock of hostentry is inherited (we
* suppose hostentry is not changed by filters).
*/
- (*fs->rte)->attrs = rta_do_cow((*fs->rte)->attrs, fs->pool);
+ fs->rte->attrs = rta_do_cow(fs->rte->attrs, fs->pool);
/* Re-cache the ea_list */
f_cache_eattrs(fs);
@@ -163,15 +147,17 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
ASSERT(line->args == 0);
/* Initialize the filter stack */
- struct filter_stack *fstk = fs->stack;
+ struct filter_stack *fstk = &fs->stack;
fstk->vcnt = line->vars;
memset(fstk->vstk, 0, sizeof(struct f_val) * line->vars);
/* The same as with the value stack. Not resetting the stack for performance reasons. */
fstk->ecnt = 1;
- fstk->estk[0].line = line;
- fstk->estk[0].pos = 0;
+ fstk->estk[0] = (struct filter_exec_stack) {
+ .line = line,
+ .pos = 0,
+ };
#define curline fstk->estk[fstk->ecnt-1]
@@ -191,6 +177,8 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
#define v2 vv(1)
#define v3 vv(2)
+#define f_vcnt_check_overflow(n) do { if (fstk->vcnt + n >= fstk->vlen) runtime("Filter execution stack overflow"); } while (0)
+
#define runtime(fmt, ...) do { \
if (!(fs->flags & FF_SILENT)) \
log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \
@@ -241,29 +229,15 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
/**
* f_run - run a filter for a route
* @filter: filter to run
- * @rte: route being filtered, may be modified
+ * @rte: route being filtered, must be write-able
* @tmp_pool: all filter allocations go from this pool
* @flags: flags
*
- * If filter needs to modify the route, there are several
- * posibilities. @rte might be read-only (with REF_COW flag), in that
- * case rw copy is obtained by rte_cow() and @rte is replaced. If
- * @rte is originally rw, it may be directly modified (and it is never
- * copied).
- *
- * The returned rte may reuse the (possibly cached, cloned) rta, or
- * (if rta was modified) contains a modified uncached rta, which
- * uses parts allocated from @tmp_pool and parts shared from original
- * rta. There is one exception - if @rte is rw but contains a cached
- * rta and that is modified, rta in returned rte is also cached.
- *
- * Ownership of cached rtas is consistent with rte, i.e.
- * if a new rte is returned, it has its own clone of cached rta
- * (and cached rta of read-only source rte is intact), if rte is
- * modified in place, old cached rta is possibly freed.
+ * If @rte->attrs is cached, the returned rte allocates a new rta on
+ * tmp_pool, otherwise the filters may modify it.
*/
enum filter_return
-f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags)
+f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, int flags)
{
if (filter == FILTER_ACCEPT)
return F_ACCEPT;
@@ -271,48 +245,22 @@ f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, i
if (filter == FILTER_REJECT)
return F_REJECT;
- int rte_cow = ((*rte)->flags & REF_COW);
DBG( "Running filter `%s'...", filter->name );
/* Initialize the filter state */
filter_state = (struct filter_state) {
- .stack = &filter_stack,
.rte = rte,
.pool = tmp_pool,
.flags = flags,
};
+ f_stack_init(filter_state);
+
LOG_BUFFER_INIT(filter_state.buf);
/* Run the interpreter itself */
enum filter_return fret = interpret(&filter_state, filter->root, NULL);
- if (filter_state.old_rta) {
- /*
- * Cached rta was modified and filter_state->rte contains now an uncached one,
- * sharing some part with the cached one. The cached rta should
- * be freed (if rte was originally COW, filter_state->old_rta is a clone
- * obtained during rte_cow()).
- *
- * This also implements the exception mentioned in f_run()
- * description. The reason for this is that rta reuses parts of
- * filter_state->old_rta, and these may be freed during rta_free(filter_state->old_rta).
- * This is not the problem if rte was COW, because original rte
- * also holds the same rta.
- */
- if (!rte_cow) {
- /* Cache the new attrs */
- (*filter_state.rte)->attrs = rta_lookup((*filter_state.rte)->attrs);
-
- /* Drop cached ea_list pointer */
- filter_state.eattrs = NULL;
- }
-
- /* Uncache the old attrs and drop the pointer as it is invalid now. */
- rta_free(filter_state.old_rta);
- filter_state.old_rta = NULL;
- }
-
/* Process the filter output, log it and return */
if (fret < F_ACCEPT) {
if (!(filter_state.flags & FF_SILENT))
@@ -337,18 +285,18 @@ f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, i
*/
enum filter_return
-f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool)
+f_eval_rte(const struct f_line *expr, struct rte *rte, struct linpool *tmp_pool)
{
filter_state = (struct filter_state) {
- .stack = &filter_stack,
.rte = rte,
.pool = tmp_pool,
};
+ f_stack_init(filter_state);
+
LOG_BUFFER_INIT(filter_state.buf);
- ASSERT(!((*rte)->flags & REF_COW));
- ASSERT(!rta_is_cached((*rte)->attrs));
+ ASSERT(!rta_is_cached(rte->attrs));
return interpret(&filter_state, expr, NULL);
}
@@ -363,10 +311,11 @@ enum filter_return
f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres)
{
filter_state = (struct filter_state) {
- .stack = &filter_stack,
.pool = tmp_pool,
};
+ f_stack_init(filter_state);
+
LOG_BUFFER_INIT(filter_state.buf);
enum filter_return fret = interpret(&filter_state, expr, pres);
@@ -383,10 +332,11 @@ f_eval_int(const struct f_line *expr)
{
/* Called independently in parse-time to eval expressions */
filter_state = (struct filter_state) {
- .stack = &filter_stack,
.pool = cfg_mem,
};
+ f_stack_init(filter_state);
+
struct f_val val;
LOG_BUFFER_INIT(filter_state.buf);
@@ -475,6 +425,23 @@ filter_commit(struct config *new, struct config *old)
}
}
+void channel_filter_dump(const struct filter *f)
+{
+ if (f == FILTER_ACCEPT)
+ debug(" ALL");
+ else if (f == FILTER_REJECT)
+ debug(" NONE");
+ else if (f == FILTER_UNDEF)
+ debug(" UNDEF");
+ else if (f->sym) {
+ ASSERT(f->sym->filter == f);
+ debug(" named filter %s", f->sym->name);
+ } else {
+ debug("\n");
+ f_dump_line(f->root, 2);
+ }
+}
+
void filters_dump_all(void)
{
struct symbol *sym;
@@ -494,19 +461,10 @@ void filters_dump_all(void)
struct channel *c;
WALK_LIST(c, sym->proto->proto->channels) {
debug(" Channel %s (%s) IMPORT", c->name, net_label[c->net_type]);
- if (c->in_filter == FILTER_ACCEPT)
- debug(" ALL\n");
- else if (c->in_filter == FILTER_REJECT)
- debug(" NONE\n");
- else if (c->in_filter == FILTER_UNDEF)
- debug(" UNDEF\n");
- else if (c->in_filter->sym) {
- ASSERT(c->in_filter->sym->filter == c->in_filter);
- debug(" named filter %s\n", c->in_filter->sym->name);
- } else {
- debug("\n");
- f_dump_line(c->in_filter->root, 2);
- }
+ channel_filter_dump(c->in_filter);
+ debug(" EXPORT", c->name, net_label[c->net_type]);
+ channel_filter_dump(c->out_filter);
+ debug("\n");
}
}
}
diff --git a/filter/filter.h b/filter/filter.h
index 26c1037b..9964831c 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -51,8 +51,8 @@ struct filter {
struct rte;
-enum filter_return f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags);
-enum filter_return f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool);
+enum filter_return f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, int flags);
+enum filter_return f_eval_rte(const struct f_line *expr, struct rte *rte, struct linpool *tmp_pool);
uint f_eval_int(const struct f_line *expr);
enum filter_return f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf);
diff --git a/filter/filter_test.c b/filter/filter_test.c
index 7e4af092..2a0b5431 100644
--- a/filter/filter_test.c
+++ b/filter/filter_test.c
@@ -72,6 +72,7 @@ int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+
bt_bird_init();
bt_assert_hook = bt_assert_filter;
diff --git a/filter/test.conf b/filter/test.conf
index 3a8804a1..93e7a770 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -378,7 +378,6 @@ bt_test_suite(t_ip_set, "Testing sets of ip address");
function t_enum()
{
- bt_assert(format(RTS_DUMMY) = "(enum 30)0");
bt_assert(format(RTS_STATIC) = "(enum 30)1");
bt_assert(format(NET_IP4) = "(enum 36)1");
bt_assert(format(NET_VPN6) = "(enum 36)4");
diff --git a/filter/tree_test.c b/filter/tree_test.c
index 6472d17e..883d13bf 100644
--- a/filter/tree_test.c
+++ b/filter/tree_test.c
@@ -20,7 +20,7 @@ start_conf_env(void)
{
bt_bird_init();
- pool *p = rp_new(&root_pool, "helper_pool");
+ pool *p = rp_new(&root_pool, &main_birdloop, "helper_pool");
linpool *l = lp_new_default(p);
cfg_mem = l;
}
diff --git a/lib/Makefile b/lib/Makefile
index 4378a7bd..98c5db3c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,4 +1,4 @@
-src := 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 := 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 rcu.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)
diff --git a/lib/birdlib.h b/lib/birdlib.h
index 431b7c0d..385bf75c 100644
--- a/lib/birdlib.h
+++ b/lib/birdlib.h
@@ -9,6 +9,7 @@
#ifndef _BIRD_BIRDLIB_H_
#define _BIRD_BIRDLIB_H_
+#include "sysdep/config.h"
#include "lib/alloca.h"
/* Ugly structure offset handling macros */
@@ -16,7 +17,7 @@
struct align_probe { char x; long int y; };
#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 (sizeof(struct align_probe))
@@ -70,6 +71,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__))))
@@ -77,10 +79,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;
diff --git a/lib/coro.h b/lib/coro.h
new file mode 100644
index 00000000..b36f1d2c
--- /dev/null
+++ b/lib/coro.h
@@ -0,0 +1,31 @@
+/*
+ * BIRD Coroutines
+ *
+ * (c) 2017 Martin Mares <mj@ucw.cz>
+ * (c) 2020-2021 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_CORO_H_
+#define _BIRD_CORO_H_
+
+#include "lib/resource.h"
+
+/* A completely opaque coroutine handle. */
+struct coroutine;
+
+/* Coroutines are independent threads bound to pools.
+ * You request a coroutine by calling coro_run().
+ * It is forbidden to free a running coroutine from outside.
+ * The running coroutine must free itself by rfree() before returning.
+ */
+struct coroutine *coro_run(pool *, void (*entry)(void *), void *data);
+
+/* Get self. */
+extern _Thread_local struct coroutine *this_coro;
+
+/* Just wait for a little while. Not intended for general use; use events if possible. */
+void coro_yield(void);
+
+#endif
diff --git a/lib/event.c b/lib/event.c
index 273447e0..5031f314 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -19,8 +19,14 @@
* events in them and explicitly ask to run them.
*/
+#undef LOCAL_DEBUG
+
#include "nest/bird.h"
#include "lib/event.h"
+#include "lib/locking.h"
+#include "lib/io-loop.h"
+
+extern _Thread_local struct coroutine *this_coro;
event_list global_event_list;
event_list global_work_list;
@@ -28,11 +34,16 @@ event_list global_work_list;
inline void
ev_postpone(event *e)
{
+ event_list *el = e->list;
+ if (!el)
+ return;
+
+ ASSERT_DIE(birdloop_inside(el->loop));
+
+ LOCK_DOMAIN(event, el->lock);
if (ev_active(e))
- {
- rem_node(&e->n);
- e->n.next = NULL;
- }
+ rem_node(&e->n);
+ UNLOCK_DOMAIN(event, el->lock);
}
static void
@@ -95,40 +106,50 @@ 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);
-}
+ DBG("ev_send(%p, %p)\n", l, e);
+ ASSERT_DIE(e->hook);
+ ASSERT_DIE(!e->list || (e->list == l) || (e->list->loop == l->loop));
-/**
- * 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);
-}
+ e->list = l;
-/**
- * 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);
+ struct event_cork *ec = e->cork;
+
+ uint ping = 0;
+
+ if (ec)
+ {
+ LOCK_DOMAIN(cork, ec->lock);
+ LOCK_DOMAIN(event, l->lock);
+
+ if (!enlisted(&e->n))
+ if (ec->count)
+ add_tail(&ec->events, &e->n);
+ else
+ {
+ add_tail(&l->events, &e->n);
+ ping = 1;
+ }
+
+ UNLOCK_DOMAIN(event, l->lock);
+ UNLOCK_DOMAIN(cork, ec->lock);
+ }
+ else
+ {
+ LOCK_DOMAIN(event, l->lock);
+
+ if (!enlisted(&e->n))
+ {
+ add_tail(&l->events, &e->n);
+ ping = 1;
+ }
+
+ UNLOCK_DOMAIN(event, l->lock);
+ }
+
+ if (ping)
+ birdloop_ping(l->loop);
}
void io_log_event(void *hook, void *data);
@@ -142,35 +163,64 @@ void io_log_event(void *hook, void *data);
int
ev_run_list(event_list *l)
{
+ const _Bool legacy = LEGACY_EVENT_LIST(l);
+
+ if (legacy)
+ ASSERT_THE_BIRD_LOCKED;
+
node *n;
- list tmp_list;
+ list tmp_list;
init_list(&tmp_list);
- add_tail_list(&tmp_list, l);
- init_list(l);
+
+ /* Move the event list contents to a local list to avoid executing repeatedly added events */
+ LOCK_DOMAIN(event, l->lock);
+ add_tail_list(&tmp_list, &l->events);
+ init_list(&l->events);
+ UNLOCK_DOMAIN(event, l->lock);
+
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))
+ if (legacy)
+ {
+ /* The legacy way of event execution */
io_log_event(e->hook, e->data);
-
- ev_run(e);
+ ev_postpone(e);
+ e->hook(e->data);
+ }
+ else
+ {
+ // io_log_event(e->hook, e->data); /* TODO: add support for event logging in other io loops */
+ ASSERT_DIE(e->list == l);
+ LOCK_DOMAIN(event, l->lock);
+ rem_node(&e->n);
+ UNLOCK_DOMAIN(event, l->lock);
+ e->hook(e->data);
+ }
}
- return !EMPTY_LIST(*l);
+ LOCK_DOMAIN(event, l->lock);
+ int repeat = ! EMPTY_LIST(l->events);
+ UNLOCK_DOMAIN(event, l->lock);
+ return repeat;
}
int
ev_run_list_limited(event_list *l, uint limit)
{
+ ASSERT_DIE(LEGACY_EVENT_LIST(l));
+ ASSERT_THE_BIRD_LOCKED;
+
node *n;
list tmp_list;
+ LOCK_DOMAIN(event, l->lock);
init_list(&tmp_list);
- add_tail_list(&tmp_list, l);
- init_list(l);
+ add_tail_list(&tmp_list, &l->events);
+ init_list(&l->events);
+ UNLOCK_DOMAIN(event, l->lock);
WALK_LIST_FIRST(n, tmp_list)
{
@@ -179,21 +229,60 @@ ev_run_list_limited(event_list *l, uint limit)
if (!limit)
break;
- /* 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);
+ io_log_event(e->hook, e->data);
ev_run(e);
limit--;
}
+ LOCK_DOMAIN(event, l->lock);
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);
+ add_tail_list(&tmp_list, &l->events);
+ init_list(&l->events);
+ add_tail_list(&l->events, &tmp_list);
+ }
+
+ int repeat = ! EMPTY_LIST(l->events);
+ UNLOCK_DOMAIN(event, l->lock);
+
+ return repeat;
+}
+
+void ev_cork(struct event_cork *ec)
+{
+ LOCK_DOMAIN(cork, ec->lock);
+ ec->count++;
+ UNLOCK_DOMAIN(cork, ec->lock);
+}
+
+void ev_uncork(struct event_cork *ec)
+{
+ LOCK_DOMAIN(cork, ec->lock);
+
+ if (--ec->count)
+ {
+ UNLOCK_DOMAIN(cork, ec->lock);
+ return;
}
- return !EMPTY_LIST(*l);
+ node *n;
+ WALK_LIST_FIRST(n, ec->events)
+ {
+ event *e = SKIP_BACK(event, n, n);
+ event_list *el = e->list;
+
+ rem_node(&e->n);
+
+ LOCK_DOMAIN(event, el->lock);
+ add_tail(&el->events, &e->n);
+ UNLOCK_DOMAIN(event, el->lock);
+
+ birdloop_ping(el->loop);
+ }
+
+ UNLOCK_DOMAIN(cork, ec->lock);
+
+ birdloop_ping(&main_birdloop);
}
diff --git a/lib/event.h b/lib/event.h
index 5f3b78d8..cd85bf78 100644
--- a/lib/event.h
+++ b/lib/event.h
@@ -10,33 +10,92 @@
#define _BIRD_EVENT_H_
#include "lib/resource.h"
+#include "lib/locking.h"
+
+#include <stdatomic.h>
+
+DEFINE_DOMAIN(event);
+DEFINE_DOMAIN(cork);
typedef struct event {
resource r;
void (*hook)(void *);
void *data;
node n; /* Internal link */
+ struct event_list *list; /* List where this event is put in */
+ struct event_cork *cork; /* Event execution limiter */
+ node cork_node;
} event;
-typedef list event_list;
+typedef struct event_list {
+ list events;
+ pool *pool;
+ struct birdloop *loop;
+ DOMAIN(event) lock;
+} event_list;
+
+struct event_cork {
+ DOMAIN(cork) lock;
+ u32 count;
+ list events;
+};
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_enqueue(event_list *, event *);
-void ev_schedule(event *);
-void ev_schedule_work(event *);
+
+static inline void ev_init_list(event_list *el, struct birdloop *loop, const char *name)
+{
+ init_list(&el->events);
+ el->loop = loop;
+ el->lock = DOMAIN_NEW(event, name);
+}
+
+static inline void ev_init_cork(struct event_cork *ec, const char *name)
+{
+ init_list(&ec->events);
+ ec->lock = DOMAIN_NEW(cork, name);
+ ec->count = 0;
+};
+
+void ev_send(event_list *, event *);
+#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 LEGACY_EVENT_LIST(l) (((l) == &global_event_list) || ((l) == &global_work_list))
+
+void ev_cork(struct event_cork *);
+void ev_uncork(struct event_cork *);
+
+static inline u32 ev_corked(struct event_cork *ec)
+{
+ if (!ec)
+ return 0;
+
+ LOCK_DOMAIN(cork, ec->lock);
+ u32 out = ec->count;
+ UNLOCK_DOMAIN(cork, ec->lock);
+ return out;
+}
+
+_Bool birdloop_inside(struct birdloop *loop);
+
static inline int
ev_active(event *e)
{
- return e->n.next != NULL;
+ if (e->list == NULL)
+ return 0;
+
+ ASSERT_DIE(birdloop_inside(e->list->loop));
+ return enlisted(&e->n);
}
static inline event*
diff --git a/lib/event_test.c b/lib/event_test.c
index e1215bba..f8bb303b 100644
--- a/lib/event_test.c
+++ b/lib/event_test.c
@@ -48,14 +48,17 @@ init_event_check_points(void)
event_check_points[i] = 0;
}
+void resource_sys_init(void);
+
static int
t_ev_run_list(void)
{
int i;
+ resource_sys_init();
resource_init();
+ birdloop_init();
olock_init();
- timer_init();
io_init();
rt_init();
if_init();
@@ -82,7 +85,9 @@ main(int argc, char *argv[])
{
bt_init(argc, argv);
+ the_bird_lock();
bt_test_suite(t_ev_run_list, "Schedule and run 3 events in right order.");
+ the_bird_unlock();
return bt_exit_value();
}
diff --git a/lib/flowspec_test.c b/lib/flowspec_test.c
index ed4afe51..518b8fc7 100644
--- a/lib/flowspec_test.c
+++ b/lib/flowspec_test.c
@@ -8,6 +8,7 @@
#include "test/birdtest.h"
#include "lib/flowspec.h"
+#include "lib/io-loop.h"
#define NET_ADDR_FLOW4_(what,prefix,pxlen,data_) \
do \
@@ -446,8 +447,6 @@ t_validation6(void)
static int
t_builder4(void)
{
- resource_init();
-
struct flow_builder *fb = flow_builder_init(&root_pool);
linpool *lp = lp_new_default(&root_pool);
@@ -529,7 +528,6 @@ t_builder6(void)
{
net_addr_ip6 ip;
- resource_init();
linpool *lp = lp_new_default(&root_pool);
struct flow_builder *fb = flow_builder_init(&root_pool);
fb->ipv6 = 1;
@@ -666,10 +664,16 @@ t_formatting6(void)
return 1;
}
+void resource_sys_init(void);
+
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+ resource_sys_init();
+ resource_init();
+ the_bird_lock();
+ birdloop_init();
bt_test_suite(t_read_length, "Testing get NLRI length");
bt_test_suite(t_write_length, "Testing set NLRI length");
@@ -685,5 +689,6 @@ main(int argc, char *argv[])
bt_test_suite(t_formatting4, "Formatting Flow Specification (IPv4) into text representation");
bt_test_suite(t_formatting6, "Formatting Flow Specification (IPv6) into text representation");
+ the_bird_unlock();
return bt_exit_value();
}
diff --git a/lib/hash.h b/lib/hash.h
index ea4ca6dd..8febb33f 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -215,6 +215,12 @@ mem_hash_mix(u64 *h, const void *p, uint s)
*h = *h * multiplier + pp[i];
}
+static inline void
+mem_hash_mix_num(u64 *h, u64 val)
+{
+ mem_hash_mix(h, &val, sizeof(val));
+}
+
static inline uint
mem_hash_value(u64 *h)
{
diff --git a/lib/hash_test.c b/lib/hash_test.c
index 59beb7c0..7ef54662 100644
--- a/lib/hash_test.c
+++ b/lib/hash_test.c
@@ -9,7 +9,9 @@
#undef LOCAL_DEBUG
#include "test/birdtest.h"
+#include "test/bt-utils.h"
+#include "lib/io-loop.h"
#include "lib/hash.h"
struct test_node {
@@ -61,8 +63,7 @@ dump_nodes(void)
static void
init_hash_(uint order)
{
- resource_init();
- my_pool = rp_new(&root_pool, "Test pool");
+ my_pool = rp_new(&root_pool, &main_birdloop, "Test pool");
HASH_INIT(hash, my_pool, order);
@@ -290,6 +291,7 @@ int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+ bt_bird_init();
bt_test_suite(t_insert_find, "HASH_INSERT and HASH_FIND");
bt_test_suite(t_insert_find_random, "HASH_INSERT pseudo-random keys and HASH_FIND");
diff --git a/lib/io-loop.h b/lib/io-loop.h
new file mode 100644
index 00000000..386a31d5
--- /dev/null
+++ b/lib/io-loop.h
@@ -0,0 +1,57 @@
+/*
+ * 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"
+
+void sk_start(sock *s);
+void sk_stop(sock *s);
+void sk_reloop(sock *s, struct birdloop *loop);
+
+extern struct birdloop main_birdloop;
+
+/* Start a new birdloop owned by given pool and domain.
+ * The loop allocates its internal pool for local allocations
+ * which is freed when the loop itself is stopped. */
+struct birdloop *birdloop_new(pool *p, uint order, const char *name);
+
+/* Stop the loop. At the end, the @stopped callback is called with locked
+ * parent to finish cleanup. The loop then frees itself together with its pool. */
+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);
+
+/* 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);
+
+/* Get birdloop's resource pool */
+pool *birdloop_pool(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);
+#endif /* _BIRD_IO_LOOP_H_ */
diff --git a/lib/lists.c b/lib/lists.c
index 200576cf..dc2e4cbb 100644
--- a/lib/lists.c
+++ b/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;
}
@@ -109,6 +110,15 @@ add_head(list *l, node *n)
l->head = n;
}
+LIST_INLINE void
+self_link(node *n)
+{
+ ASSUME(n->prev == NULL);
+ ASSUME(n->next == NULL);
+
+ n->prev = n->next = n;
+}
+
/**
* insert_node - insert a node to a list
* @n: a new list node
@@ -120,7 +130,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 +151,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;
diff --git a/lib/lists.h b/lib/lists.h
index 479f4ed1..dc49ec8a 100644
--- a/lib/lists.h
+++ b/lib/lists.h
@@ -68,6 +68,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
@@ -78,6 +90,7 @@ typedef union list { /* In fact two overlayed nodes */
#define LIST_INLINE
void add_tail(list *, node *);
void add_head(list *, node *);
+void self_link(node *);
void rem_node(node *);
void add_tail_list(list *, list *);
void init_list(list *);
diff --git a/lib/locking.h b/lib/locking.h
new file mode 100644
index 00000000..1a8bdcd4
--- /dev/null
+++ b/lib/locking.h
@@ -0,0 +1,66 @@
+/*
+ * 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 *attrs;
+ struct domain_generic *cork;
+ struct domain_generic *event;
+ 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
diff --git a/lib/mempool.c b/lib/mempool.c
index 90d7c774..6d157e5c 100644
--- a/lib/mempool.c
+++ b/lib/mempool.c
@@ -37,9 +37,10 @@ const int lp_chunk_size = sizeof(struct lp_chunk);
struct linpool {
resource r;
byte *ptr, *end;
+ pool *p;
struct lp_chunk *first, *current; /* Normal (reusable) chunks */
struct lp_chunk *first_large; /* Large chunks */
- uint chunk_size, threshold, total, total_large;
+ uint chunk_size, threshold, total:31, use_pages:1, total_large;
};
static void lp_free(resource *);
@@ -69,6 +70,13 @@ linpool
*lp_new(pool *p, uint blk)
{
linpool *m = ralloc(p, &lp_class);
+ m->p = p;
+ if (!blk)
+ {
+ m->use_pages = 1;
+ blk = page_size - lp_chunk_size;
+ }
+
m->chunk_size = blk;
m->threshold = 3*blk/4;
return m;
@@ -121,7 +129,11 @@ lp_alloc(linpool *m, uint size)
else
{
/* Need to allocate a new chunk */
- c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
+ if (m->use_pages)
+ c = alloc_page();
+ else
+ c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
+
m->total += m->chunk_size;
c->next = NULL;
c->size = m->chunk_size;
@@ -258,7 +270,10 @@ lp_free(resource *r)
for(d=m->first; d; d = c)
{
c = d->next;
- xfree(d);
+ if (m->use_pages)
+ free_page(d);
+ else
+ xfree(d);
}
for(d=m->first_large; d; d = c)
{
diff --git a/lib/rcu.c b/lib/rcu.c
new file mode 100644
index 00000000..69f3442f
--- /dev/null
+++ b/lib/rcu.c
@@ -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/coro.h"
+#include "lib/locking.h"
+
+_Atomic uint rcu_gp_ctl = RCU_NEST_CNT;
+_Thread_local struct rcu_coro *this_rcu_coro = NULL;
+
+static list rcu_coro_list;
+
+static struct rcu_coro main_rcu_coro;
+
+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_coro *rc;
+ WALK_LIST(rc, rcu_coro_list)
+ while (rcu_gp_ongoing(&rc->ctl))
+ coro_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_coro_start(struct rcu_coro *rc)
+{
+ LOCK_DOMAIN(resource, rcu_domain);
+ add_tail(&rcu_coro_list, &rc->n);
+ this_rcu_coro = rc;
+ UNLOCK_DOMAIN(resource, rcu_domain);
+}
+
+void
+rcu_coro_stop(struct rcu_coro *rc)
+{
+ LOCK_DOMAIN(resource, rcu_domain);
+ this_rcu_coro = 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_coro_list);
+ rcu_coro_start(&main_rcu_coro);
+}
diff --git a/lib/rcu.h b/lib/rcu.h
new file mode 100644
index 00000000..ac8fc9ce
--- /dev/null
+++ b/lib/rcu.h
@@ -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_coro {
+ node n;
+ _Atomic uint ctl;
+};
+
+extern _Thread_local struct rcu_coro *this_rcu_coro;
+
+static inline void rcu_read_lock(void)
+{
+ uint cmp = atomic_load_explicit(&this_rcu_coro->ctl, memory_order_acquire);
+
+ if (cmp & RCU_NEST_MASK)
+ atomic_store_explicit(&this_rcu_coro->ctl, cmp + RCU_NEST_CNT, memory_order_relaxed);
+ else
+ atomic_store(&this_rcu_coro->ctl, atomic_load_explicit(&rcu_gp_ctl, memory_order_acquire));
+}
+
+static inline void rcu_read_unlock(void)
+{
+ atomic_fetch_sub(&this_rcu_coro->ctl, RCU_NEST_CNT);
+}
+
+void synchronize_rcu(void);
+
+/* Registering and unregistering a coroutine. To be called from coroutine implementation */
+void rcu_coro_start(struct rcu_coro *);
+void rcu_coro_stop(struct rcu_coro *);
+
+/* Run this from resource init */
+void rcu_init(void);
+
+#endif
diff --git a/lib/resource.c b/lib/resource.c
index 5d4c7780..9a4c7717 100644
--- a/lib/resource.c
+++ b/lib/resource.c
@@ -14,6 +14,8 @@
#include "nest/bird.h"
#include "lib/resource.h"
#include "lib/string.h"
+#include "lib/rcu.h"
+#include "lib/io-loop.h"
/**
* DOC: Resource pools
@@ -29,12 +31,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);
@@ -56,26 +52,39 @@ static int indent;
/**
* rp_new - create a resource pool
* @p: parent pool
+ * @l: loop to assign
* @name: pool name (to be included in debugging dumps)
*
* rp_new() creates a new resource pool inside the specified
* parent pool.
*/
pool *
-rp_new(pool *p, const char *name)
+rp_new(pool *p, struct birdloop *loop, const char *name)
{
+ ASSERT_DIE(birdloop_inside(p->loop));
+ ASSERT_DIE(birdloop_inside(loop));
+
pool *z = ralloc(p, &pool_class);
+ z->loop = loop;
z->name = name;
init_list(&z->inside);
return z;
}
+_Thread_local static pool *pool_parent = NULL;
+
static void
pool_free(resource *P)
{
+ ASSERT_DIE(pool_parent);
+
pool *p = (pool *) P;
- resource *r, *rr;
+ ASSERT_DIE(birdloop_inside(p->loop));
+
+ pool *parent = pool_parent;
+ pool_parent = p;
+ resource *r, *rr;
r = HEAD(p->inside);
while (rr = (resource *) r->n.next)
{
@@ -83,14 +92,25 @@ pool_free(resource *P)
xfree(r);
r = rr;
}
+
+ pool_parent = parent;
+}
+
+void
+rp_free(pool *p, pool *parent)
+{
+ ASSERT_DIE(pool_parent == NULL);
+ pool_parent = parent;
+ rfree(p);
+ ASSERT_DIE(pool_parent == parent);
+ pool_parent = NULL;
}
static void
-pool_dump(resource *P)
+pool_dump_locked(pool *p)
{
- pool *p = (pool *) P;
resource *r;
-
+
debug("%s\n", p->name);
indent += 3;
WALK_LIST(r, p->inside)
@@ -98,10 +118,47 @@ pool_dump(resource *P)
indent -= 3;
}
-static struct resmem
-pool_memsize(resource *P)
+static void
+pool_dump(resource *P)
{
pool *p = (pool *) P;
+
+ if (p->loop != pool_parent->loop)
+ birdloop_enter(p->loop);
+
+ pool *parent = pool_parent;
+ pool_parent = p;
+
+ pool_dump_locked(p);
+
+ pool_parent = parent;
+
+ if (p->loop != pool_parent->loop)
+ birdloop_leave(p->loop);
+}
+
+void
+rp_dump(pool *p)
+{
+ int inside = birdloop_inside(p->loop);
+ if (!inside)
+ birdloop_enter(p->loop);
+
+ ASSERT_DIE(pool_parent == NULL);
+ pool_parent = p;
+
+ pool_dump_locked(p);
+
+ ASSERT_DIE(pool_parent == p);
+ pool_parent = NULL;
+
+ if (!inside)
+ birdloop_leave(p->loop);
+}
+
+static struct resmem
+pool_memsize_locked(pool *p)
+{
resource *r;
struct resmem sum = {
.effective = 0,
@@ -118,6 +175,46 @@ pool_memsize(resource *P)
return sum;
}
+static struct resmem
+pool_memsize(resource *P)
+{
+ pool *p = (pool *) P;
+
+ pool *parent = pool_parent;
+ pool_parent = p;
+
+ if (p->loop != parent->loop)
+ birdloop_enter(p->loop);
+
+ struct resmem sum = pool_memsize_locked(p);
+
+ if (p->loop != parent->loop)
+ birdloop_leave(p->loop);
+
+ pool_parent = parent;
+
+ return sum;
+}
+
+struct resmem
+rp_memsize(pool *p)
+{
+ int inside = birdloop_inside(p->loop);
+ if (!inside)
+ birdloop_enter(p->loop);
+
+ ASSERT_DIE(pool_parent == NULL);
+ pool_parent = p;
+ struct resmem sum = pool_memsize_locked(p);
+ ASSERT_DIE(pool_parent == p);
+ pool_parent = NULL;
+
+ if (!inside)
+ birdloop_leave(p->loop);
+
+ return sum;
+}
+
static resource *
pool_lookup(resource *P, unsigned long a)
{
@@ -228,12 +325,15 @@ rmemsize(void *res)
void *
ralloc(pool *p, struct resclass *c)
{
+ ASSERT_DIE(p);
+ ASSERT_DIE(birdloop_inside(p->loop));
+
resource *r = xmalloc(c->size);
bzero(r, c->size);
r->class = c;
- if (p)
- add_tail(&p->inside, &r->n);
+ add_tail(&p->inside, &r->n);
+
return r;
}
@@ -270,6 +370,8 @@ rlookup(unsigned long a)
void
resource_init(void)
{
+ rcu_init();
+
root_pool.r.class = &pool_class;
root_pool.name = "Root";
init_list(&root_pool.inside);
@@ -440,7 +542,6 @@ mb_free(void *m)
}
-
#define STEP_UP(x) ((x) + (x)/2 + 4)
void
diff --git a/lib/resource.h b/lib/resource.h
index 9ec41ed8..b80ff8c6 100644
--- a/lib/resource.h
+++ b/lib/resource.h
@@ -40,10 +40,16 @@ struct resclass {
/* Generic resource manipulation */
-typedef struct pool pool;
+typedef struct pool {
+ resource r;
+ list inside;
+ struct pool_pages *pages;
+ struct birdloop *loop;
+ const char *name;
+} pool;
void resource_init(void);
-pool *rp_new(pool *, const char *); /* Create new pool */
+
void rfree(void *); /* Free single resource */
void rdump(void *); /* Dump to debug output */
struct resmem rmemsize(void *res); /* Return size of memory used by the resource */
@@ -52,6 +58,11 @@ void rmove(void *, pool *); /* Move to a different pool */
void *ralloc(pool *, struct resclass *);
+pool *rp_new(pool *, struct birdloop *loop, const char *); /* Create new pool */
+void rp_free(pool *p, pool *parent); /* Free parent pool */
+struct resmem rp_memsize(pool *p); /* Return size of memory used by the pool */
+void rp_dump(pool *p); /* Dump pool to debug output */
+
extern pool root_pool;
/* Normal memory blocks */
@@ -82,7 +93,7 @@ void lp_restore(linpool *m, lp_state *p); /* Restore state */
extern const int lp_chunk_size;
#define LP_GAS 1024
#define LP_GOOD_SIZE(x) (((x + LP_GAS - 1) & (~(LP_GAS - 1))) - lp_chunk_size)
-#define lp_new_default(p) lp_new(p, LP_GOOD_SIZE(LP_GAS*4))
+#define lp_new_default(p) lp_new(p, 0)
/* Slabs */
@@ -100,11 +111,12 @@ void sl_free(slab *, void *);
void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size);
+extern long page_size;
+
/* Allocator of whole pages; for use in slabs and other high-level allocators. */
-u64 get_page_size(void);
void *alloc_page(void);
void free_page(void *);
-extern uint pages_kept;
+#define PAGE_HEAD(x) ((void *) (((intptr_t) (x)) & ~(page_size-1)))
#ifdef HAVE_LIBDMALLOC
/*
diff --git a/lib/slab.c b/lib/slab.c
index 6cab6b7b..c3947162 100644
--- a/lib/slab.c
+++ b/lib/slab.c
@@ -155,6 +155,7 @@ slab_memsize(resource *r)
struct slab {
resource r;
+ pool *p;
uint obj_size, head_size, head_bitfield_len;
uint objs_per_slab, num_empty_heads, data_size;
list empty_heads, partial_heads, full_heads;
@@ -180,7 +181,7 @@ struct sl_alignment { /* Magic structure for testing of alignment */
int x[0];
};
-#define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(get_page_size()-1)))
+#define SL_GET_HEAD(x) ((struct sl_head *) PAGE_HEAD(x))
/**
* sl_new - create a new Slab
@@ -194,6 +195,7 @@ slab *
sl_new(pool *p, uint size)
{
slab *s = ralloc(p, &sl_class);
+ s->p = p;
uint align = sizeof(struct sl_alignment);
if (align < sizeof(int))
align = sizeof(int);
@@ -202,7 +204,6 @@ sl_new(pool *p, uint size)
s->obj_size = size;
s->head_size = sizeof(struct sl_head);
- u64 page_size = get_page_size();
do {
s->objs_per_slab = (page_size - s->head_size) / size;
@@ -272,6 +273,9 @@ no_partial:
goto okay;
}
h = alloc_page();
+#ifdef POISON
+ memset(h, 0xba, page_size);
+#endif
ASSERT_DIE(SL_GET_HEAD(h) == h);
memset(h, 0, s->head_size);
add_head(&s->partial_heads, &h->n);
@@ -327,7 +331,12 @@ sl_free(slab *s, void *oo)
{
rem_node(&h->n);
if (s->num_empty_heads >= MAX_EMPTY_HEADS)
+ {
+#ifdef POISON
+ memset(h, 0xde, page_size);
+#endif
free_page(h);
+ }
else
{
add_head(&s->empty_heads, &h->n);
@@ -391,7 +400,7 @@ slab_memsize(resource *r)
return (struct resmem) {
.effective = eff,
- .overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * get_page_size() - eff,
+ .overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * page_size - eff,
};
}
@@ -402,10 +411,10 @@ slab_lookup(resource *r, unsigned long a)
struct sl_head *h;
WALK_LIST(h, s->partial_heads)
- if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
+ if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r;
WALK_LIST(h, s->full_heads)
- if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
+ if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r;
return NULL;
}
diff --git a/lib/socket.h b/lib/socket.h
index 96fedeeb..ff07660f 100644
--- a/lib/socket.h
+++ b/lib/socket.h
@@ -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>
@@ -56,6 +57,7 @@ typedef struct birdsock {
uint fast_rx; /* RX has higher priority in event loop */
uint rbsize;
int (*rx_hook)(struct birdsock *, uint size); /* NULL=receiving turned off, returns 1 to clear rx buffer */
+ struct event_cork *cork; /* Cork to temporarily stop receiving data */
byte *tbuf, *tpos; /* NULL=allocate automatically */
byte *ttx; /* Internal */
@@ -79,6 +81,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 */
@@ -128,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)
diff --git a/lib/timer.c b/lib/timer.c
index 381163d0..eb7ea690 100644
--- a/lib/timer.c
+++ b/lib/timer.c
@@ -32,61 +32,18 @@
#include "nest/bird.h"
+#include "lib/coro.h"
#include "lib/heap.h"
#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(&current_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 +69,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 +92,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) && (local_timeloop->coro != this_coro))
+ 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 +134,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 +158,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,32 +177,25 @@ 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);
}
}
-void
-timer_init(void)
-{
- timers_init(&main_timeloop, &root_pool);
- timeloop_init_current();
-}
-
/**
* tm_parse_time - parse a date and time
diff --git a/lib/timer.h b/lib/timer.h
index c5ea430c..04544ace 100644
--- a/lib/timer.h
+++ b/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,43 @@ 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;
+ struct coroutine *coro;
};
+#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 +107,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 {
diff --git a/nest/a-path_test.c b/nest/a-path_test.c
index 9ed0a786..2533dbae 100644
--- a/nest/a-path_test.c
+++ b/nest/a-path_test.c
@@ -12,6 +12,7 @@
#include "nest/route.h"
#include "nest/attrs.h"
#include "lib/resource.h"
+#include "lib/io-loop.h"
#define TESTS_NUM 30
#define AS_PATH_LENGTH 1000
@@ -23,8 +24,6 @@
static int
t_as_path_match(void)
{
- resource_init();
-
int round;
for (round = 0; round < TESTS_NUM; round++)
{
@@ -70,8 +69,6 @@ t_as_path_match(void)
static int
t_path_format(void)
{
- resource_init();
-
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
@@ -116,8 +113,6 @@ count_asn_in_array(const u32 *array, u32 asn)
static int
t_path_include(void)
{
- resource_init();
-
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
@@ -161,8 +156,6 @@ t_path_include(void)
static int
t_as_path_converting(void)
{
- resource_init();
-
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
@@ -204,10 +197,18 @@ t_as_path_converting(void)
}
#endif
+void resource_sys_init(void);
+void io_init(void);
+
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+ resource_sys_init();
+ resource_init();
+ the_bird_lock();
+ birdloop_init();
+ io_init();
bt_test_suite(t_as_path_match, "Testing AS path matching and some a-path utilities.");
bt_test_suite(t_path_format, "Testing formating as path into byte buffer");
diff --git a/nest/a-set_test.c b/nest/a-set_test.c
index 96b6a727..f8f6e781 100644
--- a/nest/a-set_test.c
+++ b/nest/a-set_test.c
@@ -13,6 +13,7 @@
#include "nest/route.h"
#include "nest/attrs.h"
#include "lib/resource.h"
+#include "lib/io-loop.h"
#define SET_SIZE 10
static const struct adata *set_sequence; /* <0; SET_SIZE) */
@@ -71,7 +72,6 @@ t_set_int_contains(void)
{
int i;
- resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
bt_assert(int_set_get_size(set_sequence) == SET_SIZE);
@@ -92,7 +92,6 @@ t_set_int_contains(void)
static int
t_set_int_union(void)
{
- resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
const struct adata *set_union;
@@ -111,7 +110,6 @@ t_set_int_union(void)
static int
t_set_int_format(void)
{
- resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
bt_assert(int_set_format(set_sequence, 0, 0, buf, BUFFER_SIZE) == 0);
@@ -132,7 +130,6 @@ t_set_int_format(void)
static int
t_set_int_delete(void)
{
- resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
const struct adata *deleting_sequence = set_sequence;
@@ -160,7 +157,6 @@ t_set_ec_contains(void)
{
u32 i;
- resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
bt_assert(ec_set_get_size(set_sequence) == SET_SIZE);
@@ -181,7 +177,6 @@ t_set_ec_contains(void)
static int
t_set_ec_union(void)
{
- resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
const struct adata *set_union;
@@ -200,8 +195,6 @@ t_set_ec_union(void)
static int
t_set_ec_format(void)
{
- resource_init();
-
const struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new_default(&root_pool);
@@ -222,7 +215,6 @@ t_set_ec_format(void)
static int
t_set_ec_delete(void)
{
- resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
const struct adata *deleting_sequence = set_sequence;
@@ -240,10 +232,17 @@ t_set_ec_delete(void)
return 1;
}
+
+void resource_sys_init(void);
+
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+ resource_sys_init();
+ resource_init();
+ the_bird_lock();
+ birdloop_init();
bt_test_suite(t_set_int_contains, "Testing sets of integers: contains, get_data");
bt_test_suite(t_set_int_format, "Testing sets of integers: format");
diff --git a/nest/cli.c b/nest/cli.c
index b54a0d76..7e5d2151 100644
--- a/nest/cli.c
+++ b/nest/cli.c
@@ -262,7 +262,7 @@ cli_command(struct cli *c)
log(L_TRACE "CLI: %s", c->rx_buf);
bzero(&f, sizeof(f));
f.mem = c->parser_pool;
- f.pool = rp_new(c->pool, "Config");
+ f.pool = rp_new(c->pool, &main_birdloop, "Config");
init_list(&f.symbols);
cf_read_hook = cli_cmd_read_hook;
cli_rh_pos = c->rx_buf;
@@ -308,7 +308,7 @@ cli_event(void *data)
cli *
cli_new(void *priv)
{
- pool *p = rp_new(cli_pool, "CLI");
+ pool *p = rp_new(cli_pool, &main_birdloop, "CLI");
cli *c = mb_alloc(p, sizeof(cli));
bzero(c, sizeof(cli));
@@ -413,7 +413,7 @@ cli_free(cli *c)
c->cleanup(c);
if (c == cmd_reconfig_stored_cli)
cmd_reconfig_stored_cli = NULL;
- rfree(c->pool);
+ rp_free(c->pool, &root_pool);
}
/**
@@ -425,7 +425,7 @@ cli_free(cli *c)
void
cli_init(void)
{
- cli_pool = rp_new(&root_pool, "CLI");
+ cli_pool = rp_new(&root_pool, &main_birdloop, "CLI");
init_list(&cli_log_hooks);
cli_log_inited = 1;
}
diff --git a/nest/cmds.c b/nest/cmds.c
index 1a16f9c7..77c92077 100644
--- a/nest/cmds.c
+++ b/nest/cmds.c
@@ -114,15 +114,10 @@ cmd_show_memory(void)
{
cli_msg(-1018, "BIRD memory usage");
cli_msg(-1018, "%-17s Effective Overhead", "");
- print_size("Routing tables:", rmemsize(rt_table_pool));
- print_size("Route attributes:", rmemsize(rta_pool));
- print_size("Protocols:", rmemsize(proto_pool));
- struct resmem total = rmemsize(&root_pool);
-#ifdef HAVE_MMAP
- print_size("Standby memory:", (struct resmem) { .overhead = get_page_size() * pages_kept });
- total.overhead += get_page_size() * pages_kept;
-#endif
- print_size("Total:", total);
+ print_size("Routing tables:", rp_memsize(rt_table_pool));
+ print_size("Route attributes:", rp_memsize(rta_pool));
+ print_size("Protocols:", rp_memsize(proto_pool));
+ print_size("Total:", rp_memsize(&root_pool));
cli_msg(0, "");
}
diff --git a/nest/config.Y b/nest/config.Y
index 7ead8589..f9ed0e69 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -111,7 +111,7 @@ proto_postconfig(void)
CF_DECLS
-CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
+CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT, PIPE)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
@@ -128,7 +128,7 @@ CF_KEYWORDS(CHECK, LINK)
/* 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)
-CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
+CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
@@ -266,8 +266,8 @@ proto_item:
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
| ROUTER ID idval { this_proto->router_id = $3; }
| DESCRIPTION text { this_proto->dsc = $2; }
- | VRF text { this_proto->vrf = if_get_by_name($2); this_proto->vrf_set = 1; }
- | VRF DEFAULT { this_proto->vrf = NULL; this_proto->vrf_set = 1; }
+ | VRF text { this_proto->vrf = if_get_by_name($2); }
+ | VRF DEFAULT { this_proto->vrf = &default_vrf; }
;
@@ -348,6 +348,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 TABLES bool { new_config->table_debug = $3; }
;
/* MRTDUMP PROTOCOLS is in systep/unix/config.Y */
@@ -433,6 +434,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;
}
;
@@ -643,12 +645,12 @@ r_args:
}
| r_args IMPORT TABLE channel_arg {
if (!$4->in_table) cf_error("No import table in channel %s.%s", $4->proto->name, $4->name);
- rt_show_add_table($$, $4->in_table);
+ rt_show_add_table($$, $4->in_table->tab);
$$->tables_defined_by = RSD_TDB_DIRECT;
}
| r_args EXPORT TABLE channel_arg {
if (!$4->out_table) cf_error("No export table in channel %s.%s", $4->proto->name, $4->name);
- rt_show_add_table($$, $4->out_table);
+ rt_show_add_table($$, $4->out_table->tab);
$$->tables_defined_by = RSD_TDB_DIRECT;
}
| r_args FILTER filter {
@@ -808,7 +810,7 @@ sym_args:
CF_CLI_HELP(DUMP, ..., [[Dump debugging information]])
CF_CLI(DUMP RESOURCES,,, [[Dump all allocated resource]])
-{ rdump(&root_pool); cli_msg(0, ""); } ;
+{ rp_dump(&root_pool); cli_msg(0, ""); } ;
CF_CLI(DUMP SOCKETS,,, [[Dump open sockets]])
{ sk_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP EVENTS,,, [[Dump event log]])
@@ -819,8 +821,10 @@ CF_CLI(DUMP NEIGHBORS,,, [[Dump neighbor cache]])
{ neigh_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP ATTRIBUTES,,, [[Dump attribute cache]])
{ rta_dump_all(); cli_msg(0, ""); } ;
-CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
+CF_CLI(DUMP ROUTES,,, [[Dump routes]])
{ rt_dump_all(); cli_msg(0, ""); } ;
+CF_CLI(DUMP TABLES,,, [[Dump table connections]])
+{ rt_dump_hooks_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
{ protos_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
diff --git a/nest/iface.c b/nest/iface.c
index 83a633a3..dd4af243 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -37,6 +37,7 @@
static pool *if_pool;
list iface_list;
+struct iface default_vrf;
static void if_recalc_preferred(struct iface *i);
@@ -147,7 +148,7 @@ ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
{
if (p->ifa_notify &&
(p->proto_state != PS_DOWN) &&
- (!p->vrf_set || p->vrf == a->iface->master))
+ (!p->vrf || p->vrf == a->iface->master))
{
if (p->debug & D_IFACES)
log(L_TRACE "%s < address %N on interface %s %s",
@@ -185,7 +186,7 @@ if_send_notify(struct proto *p, unsigned c, struct iface *i)
{
if (p->if_notify &&
(p->proto_state != PS_DOWN) &&
- (!p->vrf_set || p->vrf == i->master))
+ (!p->vrf || p->vrf == i->master))
{
if (p->debug & D_IFACES)
log(L_TRACE "%s < interface %s %s", p->name, i->name,
@@ -243,7 +244,7 @@ if_recalc_flags(struct iface *i UNUSED, uint flags)
{
if ((flags & IF_ADMIN_UP) &&
!(flags & (IF_SHUTDOWN | IF_TMP_DOWN)) &&
- !(i->master_index && !i->master))
+ !(i->master_index && i->master == &default_vrf))
flags |= IF_UP;
else
flags &= ~IF_UP;
@@ -301,6 +302,9 @@ if_update(struct iface *new)
struct iface *i;
unsigned c;
+ if (!new->master)
+ new->master = &default_vrf;
+
WALK_LIST(i, iface_list)
if (!strcmp(new->name, i->name))
{
@@ -709,8 +713,9 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id)
void
if_init(void)
{
- if_pool = rp_new(&root_pool, "Interfaces");
+ if_pool = rp_new(&root_pool, &main_birdloop, "Interfaces");
init_list(&iface_list);
+ strcpy(default_vrf.name, "default");
neigh_init(if_pool);
}
@@ -843,7 +848,7 @@ if_show(void)
continue;
char mbuf[16 + sizeof(i->name)] = {};
- if (i->master)
+ if (i->master != &default_vrf)
bsprintf(mbuf, " master=%s", i->master->name);
else if (i->master_index)
bsprintf(mbuf, " master=#%u", i->master_index);
diff --git a/nest/iface.h b/nest/iface.h
index 1189cdd4..13f3bd12 100644
--- a/nest/iface.h
+++ b/nest/iface.h
@@ -28,6 +28,8 @@ struct ifa { /* Interface address */
unsigned flags; /* Analogous to iface->flags */
};
+extern struct iface default_vrf;
+
struct iface {
node n;
char name[16];
diff --git a/nest/limit.h b/nest/limit.h
new file mode 100644
index 00000000..5838ad3b
--- /dev/null
+++ b/nest/limit.h
@@ -0,0 +1,49 @@
+/*
+ * BIRD Internet Routing Daemon -- Limits
+ *
+ * (c) 1998--2000 Martin Mares <mj@ucw.cz>
+ * (c) 2021 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_LIMIT_H_
+#define _BIRD_LIMIT_H_
+
+struct limit {
+ u32 max;
+ u32 count;
+ int (*action)(struct limit *, void *data);
+};
+
+static inline int limit_do_action(struct limit *l, void *data)
+{
+ return l->action ? l->action(l, data) : 1;
+}
+
+static inline int limit_push(struct limit *l, void *data)
+{
+ if ((l->count >= l->max) && limit_do_action(l, data))
+ return 1;
+
+ l->count++;
+ return 0;
+}
+
+static inline void limit_pop(struct limit *l)
+{
+ --l->count;
+}
+
+static inline void limit_reset(struct limit *l)
+{
+ l->count = 0;
+}
+
+static inline void limit_update(struct limit *l, void *data, u32 max)
+{
+ if (l->count > (l->max = max))
+ limit_do_action(l, data);
+}
+
+#endif
diff --git a/nest/neighbor.c b/nest/neighbor.c
index 1a31fb79..cb2d1b2b 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -142,7 +142,7 @@ if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
}
static inline int
-if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **iface, struct ifa **addr, uint flags)
+if_connected_any(ip_addr a, struct iface *vrf, struct iface **iface, struct ifa **addr, uint flags)
{
struct iface *i;
struct ifa *b;
@@ -153,7 +153,7 @@ if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **ifac
/* Prefer SCOPE_HOST or longer prefix */
WALK_LIST(i, iface_list)
- if ((!vrf_set || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
+ if ((!vrf || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
if (scope_better(s, scope) || (scope_remote(s, scope) && ifa_better(b, *addr)))
{
*iface = i;
@@ -245,7 +245,7 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
iface = (scope < 0) ? NULL : iface;
}
else
- scope = if_connected_any(a, p->vrf, p->vrf_set, &iface, &addr, flags);
+ scope = if_connected_any(a, p->vrf, &iface, &addr, flags);
/* scope < 0 means i don't know neighbor */
/* scope >= 0 <=> iface != NULL */
@@ -369,7 +369,7 @@ neigh_update(neighbor *n, struct iface *iface)
return;
/* VRF-bound neighbors ignore changes in other VRFs */
- if (p->vrf_set && (p->vrf != iface->master))
+ if (p->vrf && (p->vrf != iface->master))
return;
scope = if_connected(n->addr, iface, &ifa, n->flags);
@@ -379,7 +379,7 @@ neigh_update(neighbor *n, struct iface *iface)
{
/* When neighbor is going down, try to respawn it on other ifaces */
if ((scope < 0) && (n->scope >= 0) && !n->ifreq && (n->flags & NEF_STICKY))
- scope = if_connected_any(n->addr, p->vrf, p->vrf_set, &iface, &ifa, n->flags);
+ scope = if_connected_any(n->addr, p->vrf, &iface, &ifa, n->flags);
}
else
{
diff --git a/nest/proto-hooks.c b/nest/proto-hooks.c
index bc88b4b4..716ce86c 100644
--- a/nest/proto-hooks.c
+++ b/nest/proto-hooks.c
@@ -76,16 +76,6 @@ void dump(struct proto *p)
{ DUMMY; }
/**
- * dump_attrs - dump protocol-dependent attributes
- * @e: a route entry
- *
- * This hook dumps all attributes in the &rte which belong to this
- * protocol to the debug output.
- */
-void dump_attrs(rte *e)
-{ DUMMY; }
-
-/**
* start - request instance startup
* @p: protocol instance
*
@@ -228,36 +218,6 @@ void neigh_notify(neighbor *neigh)
{ DUMMY; }
/**
- * make_tmp_attrs - convert embedded attributes to temporary ones
- * @e: route entry
- * @pool: linear pool to allocate attribute memory in
- *
- * This hook is called by the routing table functions if they need
- * to convert the protocol attributes embedded directly in the &rte
- * to temporary extended attributes in order to distribute them
- * to other protocols or to filters. make_tmp_attrs() creates
- * an &ea_list in the linear pool @pool, fills it with values of the
- * temporary attributes and returns a pointer to it.
- */
-ea_list *make_tmp_attrs(rte *e, struct linpool *pool)
-{ DUMMY; }
-
-/**
- * store_tmp_attrs - convert temporary attributes to embedded ones
- * @e: route entry
- * @attrs: temporary attributes to be converted
- *
- * This hook is an exact opposite of make_tmp_attrs() -- it takes
- * a list of extended attributes and converts them to attributes
- * embedded in the &rte corresponding to this protocol.
- *
- * You must be prepared for any of the attributes being missing
- * from the list and use default values instead.
- */
-void store_tmp_attrs(rte *e, ea_list *attrs)
-{ DUMMY; }
-
-/**
* preexport - pre-filtering decisions before route export
* @p: protocol instance the route is going to be exported to
* @e: the route in question
diff --git a/nest/proto.c b/nest/proto.c
index 31ee1fa1..a18d9332 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -15,6 +15,7 @@
#include "lib/event.h"
#include "lib/timer.h"
#include "lib/string.h"
+#include "lib/coro.h"
#include "conf/conf.h"
#include "nest/route.h"
#include "nest/iface.h"
@@ -43,24 +44,46 @@ static int graceful_restart_state;
static u32 graceful_restart_locks;
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
-static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" };
-static char *e_states[] = { "DOWN", "FEEDING", "READY" };
+static char *c_states[] = { "DOWN", "START", "UP", "STOP", "RESTART" };
extern struct protocol proto_unix_iface;
-static void channel_request_reload(struct channel *c);
+static void channel_aux_request_refeed(struct channel_aux_table *cat);
static void proto_shutdown_loop(timer *);
static void proto_rethink_goal(struct proto *p);
static char *proto_state_name(struct proto *p);
-static void channel_verify_limits(struct channel *c);
-static inline void channel_reset_limit(struct channel_limit *l);
-
+static void channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf);
+static void channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf);
+static void channel_reset_limit(struct channel *c, struct limit *l, int dir);
+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_START) || (c->channel_state == CS_UP); }
+{ return (c->channel_state != CS_DOWN); }
static inline int channel_reloadable(struct channel *c)
{ return c->proto->reload_routes && c->reloadable; }
@@ -68,10 +91,48 @@ static inline int channel_reloadable(struct channel *c)
static inline void
channel_log_state_change(struct channel *c)
{
- if (c->export_state)
- CD(c, "State changed to %s/%s", c_states[c->channel_state], e_states[c->export_state]);
- else
- CD(c, "State changed to %s", c_states[c->channel_state]);
+ CD(c, "State changed to %s", c_states[c->channel_state]);
+}
+
+void
+channel_import_log_state_change(struct rt_import_request *req, u8 state)
+{
+ struct channel *c = SKIP_BACK(struct channel, in_req, req);
+ CD(c, "Channel import state changed to %s", rt_import_state_name(state));
+}
+
+void
+channel_export_log_state_change(struct rt_export_request *req, u8 state)
+{
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
+ CD(c, "Channel export state changed to %s", rt_export_state_name(state));
+
+ switch (state)
+ {
+ case TES_FEEDING:
+ if (c->out_table)
+ rt_refresh_begin(&c->out_table->push);
+ else if (c->proto->feed_begin)
+ c->proto->feed_begin(c, !c->refeeding);
+ break;
+ case TES_READY:
+ channel_feed_end(c);
+ break;
+ }
+}
+
+static void
+channel_dump_import_req(struct rt_import_request *req)
+{
+ struct channel *c = SKIP_BACK(struct channel, in_req, req);
+ debug(" Channel %s.%s import request %p\n", c->proto->name, c->name, req);
+}
+
+static void
+channel_dump_export_req(struct rt_export_request *req)
+{
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
+ debug(" Channel %s.%s export request %p\n", c->proto->name, c->name, req);
}
static void
@@ -111,7 +172,7 @@ proto_cf_find_channel(struct proto_config *pc, uint net_type)
* Returns pointer to channel or NULL
*/
struct channel *
-proto_find_channel_by_table(struct proto *p, struct rtable *t)
+proto_find_channel_by_table(struct proto *p, rtable *t)
{
struct channel *c;
@@ -141,6 +202,16 @@ proto_find_channel_by_name(struct proto *p, const char *n)
return NULL;
}
+rte * channel_preimport(struct rt_import_request *req, rte *new, rte *old);
+rte * channel_in_preimport(struct rt_import_request *req, rte *new, rte *old);
+
+void rt_notify_optimal(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
+void rt_notify_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
+void rt_feed_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
+void rt_notify_accepted(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
+void rt_notify_merged(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
+
+
/**
* proto_add_channel - connect protocol to a routing table
* @p: protocol instance
@@ -166,11 +237,17 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->proto = p;
c->table = cf->table->table;
+ RT_LOCKED(c->table, t)
+ rt_lock_table(t);
+
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
- c->rx_limit = cf->rx_limit;
- c->in_limit = cf->in_limit;
- c->out_limit = cf->out_limit;
+
+ channel_init_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit);
+ channel_init_limit(c, &c->in_limit, PLD_IN, &cf->in_limit);
+ channel_init_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit);
+
+ c->rte_update_pool = lp_new_default(proto_pool);
c->net_type = cf->net_type;
c->ra_mode = cf->ra_mode;
@@ -181,7 +258,6 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->rpki_reload = cf->rpki_reload;
c->channel_state = CS_DOWN;
- c->export_state = ES_DOWN;
c->last_state_change = current_time();
c->reloadable = 1;
@@ -203,6 +279,9 @@ proto_remove_channel(struct proto *p UNUSED, struct channel *c)
CD(c, "Removed", c->name);
+ RT_LOCKED(c->table, t)
+ rt_unlock_table(t);
+
rem_node(&c->n);
mb_free(c);
}
@@ -223,7 +302,7 @@ proto_pause_channels(struct proto *p)
struct channel *c;
WALK_LIST(c, p->channels)
if (!c->disabled && channel_is_active(c))
- channel_set_state(c, CS_START);
+ channel_set_state(c, CS_PAUSE);
}
static void
@@ -232,7 +311,7 @@ proto_stop_channels(struct proto *p)
struct channel *c;
WALK_LIST(c, p->channels)
if (!c->disabled && channel_is_active(c))
- channel_set_state(c, CS_FLUSHING);
+ channel_set_state(c, CS_STOP);
}
static void
@@ -244,96 +323,25 @@ proto_remove_channels(struct proto *p)
}
static void
-channel_schedule_feed(struct channel *c, int initial)
-{
- // DBG("%s: Scheduling meal\n", p->name);
- ASSERT(c->channel_state == CS_UP);
-
- c->export_state = ES_FEEDING;
- c->refeeding = !initial;
-
- ev_schedule_work(c->feed_event);
-}
-
-static void
-channel_feed_loop(void *ptr)
-{
- struct channel *c = ptr;
-
- if (c->export_state != ES_FEEDING)
- return;
-
- /* Start feeding */
- if (!c->feed_active)
- {
- if (c->proto->feed_begin)
- c->proto->feed_begin(c, !c->refeeding);
-
- c->refeed_pending = 0;
- }
-
- // DBG("Feeding protocol %s continued\n", p->name);
- if (!rt_feed_channel(c))
- {
- ev_schedule_work(c->feed_event);
- return;
- }
-
- /* Reset export limit if the feed ended with acceptable number of exported routes */
- struct channel_limit *l = &c->out_limit;
- if (c->refeeding &&
- (l->state == PLS_BLOCKED) &&
- (c->refeed_count <= l->limit) &&
- (c->stats.exp_routes <= l->limit))
- {
- log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit);
- channel_reset_limit(&c->out_limit);
-
- /* Continue in feed - it will process routing table again from beginning */
- c->refeed_count = 0;
- ev_schedule_work(c->feed_event);
- return;
- }
-
- // DBG("Feeding protocol %s finished\n", p->name);
- c->export_state = ES_READY;
- channel_log_state_change(c);
-
- if (c->proto->feed_end)
- c->proto->feed_end(c);
-
- /* Restart feeding */
- if (c->refeed_pending)
- channel_request_feeding(c);
-}
-
-
-static void
-channel_roa_in_changed(struct rt_subscription *s)
+channel_roa_in_changed(void *_data)
{
- struct channel *c = s->data;
- int active = c->reload_event && ev_active(c->reload_event);
+ struct channel *c = _data;
- CD(c, "Reload triggered by RPKI change%s", active ? " - already active" : "");
+ CD(c, "Reload triggered by RPKI change");
- if (!active)
- channel_request_reload(c);
- else
- c->reload_pending = 1;
+ channel_request_reload(c);
}
static void
-channel_roa_out_changed(struct rt_subscription *s)
+channel_roa_out_changed(void *_data)
{
- struct channel *c = s->data;
- int active = (c->export_state == ES_FEEDING);
+ struct channel *c = _data;
+ CD(c, "Feeding triggered by RPKI change");
- CD(c, "Feeding triggered by RPKI change%s", active ? " - already active" : "");
+ c->refeed_pending = 1;
- if (!active)
- channel_request_feeding(c);
- else
- c->refeed_pending = 1;
+ if (c->out_req.hook)
+ rt_stop_export(&c->out_req, channel_export_stopped);
}
/* Temporary code, subscriptions should be changed to resources */
@@ -345,14 +353,14 @@ struct roa_subscription {
static int
channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir)
{
- void (*hook)(struct rt_subscription *) =
+ void (*hook)(void *) =
dir ? channel_roa_in_changed : channel_roa_out_changed;
struct roa_subscription *s;
node *n;
WALK_LIST2(s, n, c->roa_subscriptions, roa_node)
- if ((s->s.tab == tab) && (s->s.hook == hook))
+ if ((s->s.tab == tab) && (s->s.event->hook == hook))
return 1;
return 0;
@@ -366,9 +374,9 @@ channel_roa_subscribe(struct channel *c, rtable *tab, int dir)
return;
struct roa_subscription *s = mb_allocz(c->proto->pool, sizeof(struct roa_subscription));
+ s->s.event = ev_new_init(c->proto->pool, dir ? channel_roa_in_changed : channel_roa_out_changed, c);
+ s->s.event->list = proto_work_list(c->proto);
- s->s.hook = dir ? channel_roa_in_changed : channel_roa_out_changed;
- s->s.data = c;
rt_subscribe(tab, &s->s);
add_tail(&c->roa_subscriptions, &s->roa_node);
@@ -379,6 +387,7 @@ channel_roa_unsubscribe(struct roa_subscription *s)
{
rt_unsubscribe(&s->s);
rem_node(&s->roa_node);
+ rfree(s->s.event);
mb_free(s);
}
@@ -386,7 +395,7 @@ static void
channel_roa_subscribe_filter(struct channel *c, int dir)
{
const struct filter *f = dir ? c->in_filter : c->out_filter;
- struct rtable *tab;
+ rtable *tab;
int valid = 1, found = 0;
if ((f == FILTER_ACCEPT) || (f == FILTER_REJECT))
@@ -445,119 +454,530 @@ channel_roa_unsubscribe_all(struct channel *c)
}
static void
-channel_start_export(struct channel *c)
+channel_start_import(struct channel *c)
{
+ if (c->in_req.hook)
+ {
+ log(L_WARN "%s.%s: Attempted to start channel's already started import", c->proto->name, c->name);
+ return;
+ }
+
+ int nlen = strlen(c->name) + strlen(c->proto->name) + 2;
+ char *rn = mb_allocz(c->proto->pool, nlen);
+ bsprintf(rn, "%s.%s", c->proto->name, c->name);
+
+ c->in_req = (struct rt_import_request) {
+ .name = rn,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_dump_import_req,
+ .log_state_change = channel_import_log_state_change,
+ .preimport = channel_preimport,
+ };
+
ASSERT(c->channel_state == CS_UP);
- ASSERT(c->export_state == ES_DOWN);
- channel_schedule_feed(c, 1); /* Sets ES_FEEDING */
+ channel_reset_limit(c, &c->rx_limit, PLD_RX);
+ channel_reset_limit(c, &c->in_limit, PLD_IN);
+
+ memset(&c->import_stats, 0, sizeof(struct channel_import_stats));
+
+ DBG("%s.%s: Channel start import req=%p\n", c->proto->name, c->name, &c->in_req);
+ rt_request_import(c->table, &c->in_req);
}
static void
-channel_stop_export(struct channel *c)
+channel_start_export(struct channel *c)
{
- /* Need to abort feeding */
- if (c->export_state == ES_FEEDING)
- rt_feed_channel_abort(c);
+ if (c->out_req.hook)
+ {
+ c->restart_export = 1;
+ log(L_WARN "%s.%s: Fast channel export restart", c->proto->name, c->name);
+ return;
+ }
+
+ ASSERT(c->channel_state == CS_UP);
+ int nlen = strlen(c->name) + strlen(c->proto->name) + 2;
+ char *rn = mb_allocz(c->proto->pool, nlen);
+ bsprintf(rn, "%s.%s", c->proto->name, c->name);
+
+ c->out_req = (struct rt_export_request) {
+ .name = rn,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_dump_export_req,
+ .log_state_change = channel_export_log_state_change,
+ };
+
+ bmap_init(&c->export_map, c->proto->pool, 1024);
+ bmap_init(&c->export_reject_map, c->proto->pool, 1024);
+
+ channel_reset_limit(c, &c->out_limit, PLD_OUT);
- c->export_state = ES_DOWN;
- c->stats.exp_routes = 0;
- bmap_reset(&c->export_map, 1024);
+ memset(&c->export_stats, 0, sizeof(struct channel_export_stats));
+
+ switch (c->ra_mode) {
+ case RA_OPTIMAL:
+ c->out_req.export_one = rt_notify_optimal;
+ break;
+ case RA_ANY:
+ c->out_req.export_one = rt_notify_any;
+ c->out_req.export_bulk = rt_feed_any;
+ break;
+ case RA_ACCEPTED:
+ c->out_req.export_bulk = rt_notify_accepted;
+ break;
+ case RA_MERGED:
+ c->out_req.export_bulk = rt_notify_merged;
+ break;
+ default:
+ bug("Unknown route announcement mode");
+ }
+
+ DBG("%s.%s: Channel start export req=%p\n", c->proto->name, c->name, &c->out_req);
+ rt_request_export(c->table, &c->out_req);
}
+static void
+channel_check_stopped(struct channel *c)
+{
+ switch (c->channel_state)
+ {
+ case CS_STOP:
+ if (c->out_req.hook || c->in_req.hook || c->out_table || c->in_table)
+ return;
+
+ channel_set_state(c, CS_DOWN);
+ proto_send_event(c->proto);
+
+ break;
+ case CS_PAUSE:
+ if (c->out_req.hook)
+ return;
+
+ channel_set_state(c, CS_START);
+ break;
+ default:
+ bug("Stopped channel in a bad state: %d", c->channel_state);
+ }
+
+ DBG("%s.%s: Channel requests/hooks stopped (in state %s)\n", c->proto->name, c->name, c_states[c->channel_state]);
+}
-/* Called by protocol for reload from in_table */
void
-channel_schedule_reload(struct channel *c)
+channel_import_stopped(void *_c)
{
- ASSERT(c->channel_state == CS_UP);
+ struct channel *c = _c;
+
+ c->in_req.hook = NULL;
- rt_reload_channel_abort(c);
- ev_schedule_work(c->reload_event);
+ mb_free(c->in_req.name);
+ c->in_req.name = NULL;
+
+ channel_check_stopped(c);
}
static void
-channel_reload_loop(void *ptr)
+channel_export_stopped(struct rt_export_request *req)
{
- struct channel *c = ptr;
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
- /* Start reload */
- if (!c->reload_active)
- c->reload_pending = 0;
+ /* The hook has already stopped */
+ req->hook = NULL;
- if (!rt_reload_channel(c))
+ if (c->refeed_pending)
{
- ev_schedule_work(c->reload_event);
+ c->refeeding = 1;
+ c->refeed_pending = 0;
+
+ bmap_reset(&c->export_map, 1024);
+ bmap_reset(&c->export_reject_map, 1024);
+
+ rt_request_export(c->table, req);
return;
}
- /* Restart reload */
- if (c->reload_pending)
- channel_request_reload(c);
+ mb_free(c->out_req.name);
+ c->out_req.name = NULL;
+
+ bmap_free(&c->export_map);
+ bmap_free(&c->export_reject_map);
+
+ if (c->restart_export)
+ {
+ c->restart_export = 0;
+ channel_start_export(c);
+ }
+ else
+ channel_check_stopped(c);
+}
+
+static void
+channel_feed_end(struct channel *c)
+{
+ struct rt_export_request *req = &c->out_req;
+
+ /* Reset export limit if the feed ended with acceptable number of exported routes */
+ struct limit *l = &c->out_limit;
+ if (c->refeeding &&
+ (c->limit_active & (1 << PLD_OUT)) &&
+ (c->refeed_count <= l->max) &&
+ (l->count <= l->max))
+ {
+ log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->max);
+ channel_reset_limit(c, &c->out_limit, PLD_OUT);
+
+ c->refeed_pending = 1;
+ rt_stop_export(req, channel_export_stopped);
+ return;
+ }
+
+ if (c->out_table)
+ rt_refresh_end(&c->out_table->push);
+ else if (c->proto->feed_end)
+ c->proto->feed_end(c);
+
+ if (c->refeed_pending)
+ rt_stop_export(req, channel_export_stopped);
+}
+
+#define CHANNEL_AUX_TABLE_DUMP_REQ(inout, imex, pgimex, pushget) static void \
+ channel_##inout##_##pushget##_dump_req(struct rt_##pgimex##_request *req) { \
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, pushget, req); \
+ debug(" Channel %s.%s " #imex " table " #pushget " request %p\n", cat->c->proto->name, cat->c->name, req); }
+
+CHANNEL_AUX_TABLE_DUMP_REQ(in, import, import, push)
+CHANNEL_AUX_TABLE_DUMP_REQ(in, import, export, get)
+CHANNEL_AUX_TABLE_DUMP_REQ(out, export, import, push)
+CHANNEL_AUX_TABLE_DUMP_REQ(out, export, export, get)
+
+#undef CHANNEL_AUX_TABLE_DUMP_REQ
+
+static uint channel_aux_imex(struct channel_aux_table *cat)
+{
+ if (cat->c->in_table == cat)
+ return 0;
+ else if (cat->c->out_table == cat)
+ return 1;
+ else
+ bug("Channel aux table must be in_table or out_table");
}
static void
-channel_reset_import(struct channel *c)
+channel_aux_stopped(void *data)
{
- /* Need to abort feeding */
- ev_postpone(c->reload_event);
- rt_reload_channel_abort(c);
+ struct channel_aux_table *cat;
+ RT_LOCKED((rtable *) data, t)
+ cat = t->config->owner;
+
+ struct channel *c = cat->c;
+
+ if (channel_aux_imex(cat))
+ c->out_table = NULL;
+ else
+ c->in_table = NULL;
- rt_prune_sync(c->in_table, 1);
+ mb_free(cat);
+ channel_check_stopped(c);
}
static void
-channel_reset_export(struct channel *c)
+channel_aux_import_stopped(void *_cat)
{
- /* Just free the routes */
- rt_prune_sync(c->out_table, 1);
+ struct channel_aux_table *cat = _cat;
+ cat->push.hook = NULL;
}
-/* Called by protocol to activate in_table */
-void
-channel_setup_in_table(struct channel *c)
+static void
+channel_aux_export_stopped(struct rt_export_request *req)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ req->hook = NULL;
+
+ int del;
+ RT_LOCKED(cat->tab, t)
+ del = !!t->delete;
+
+ if (del)
+ return;
+
+ ASSERT_DIE(cat->refeed_pending);
+ cat->refeed_pending = 0;
+ rt_request_export(cat->tab, req);
+}
+
+static void
+channel_aux_stop(struct channel_aux_table *cat)
+{
+ RT_LOCKED(cat->tab, t)
+ t->delete = channel_aux_stopped;
+
+ cat->push_stopped = (event) {
+ .hook = channel_aux_import_stopped,
+ .data = cat,
+ .list = proto_event_list(cat->c->proto),
+ };
+
+ rt_stop_import(&cat->push, &cat->push_stopped);
+ rt_stop_export(&cat->get, channel_aux_export_stopped);
+}
+
+static void
+channel_push_log_state_change(struct rt_import_request *req, u8 state)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, push, req);
+ const char *imex = channel_aux_imex(cat) ? "export" : "import";
+ CD(cat->c, "Channel %s table import state changed to %s", imex, rt_import_state_name(state));
+}
+
+static void
+channel_get_log_state_change(struct rt_export_request *req, u8 state)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ const char *imex = channel_aux_imex(cat) ? "export" : "import";
+ CD(cat->c, "Channel %s table export state changed to %s", imex, rt_export_state_name(state));
+
+ switch (state)
+ {
+ case TES_FEEDING:
+ if (imex && cat->c->proto->feed_begin)
+ cat->c->proto->feed_begin(cat->c, !cat->c->refeeding);
+ else if (!imex)
+ rt_refresh_begin(&cat->c->in_req);
+ break;
+
+ case TES_READY:
+ if (imex && cat->c->proto->feed_end)
+ cat->c->proto->feed_end(cat->c);
+ else if (!imex)
+ rt_refresh_end(&cat->c->in_req);
+
+ if (cat->refeed_pending)
+ rt_stop_export(&cat->get, channel_aux_export_stopped);
+
+ break;
+ }
+}
+
+void rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
+
+static int
+channel_aux_export_one_any(struct rt_export_request *req, struct rt_pending_export *rpe, rte **new, rte **old)
+{
+ struct rte_src *src = rpe->new ? rpe->new->rte.src : rpe->old->rte.src;
+ *old = RTES_OR_NULL(rpe->old);
+ struct rte_storage *new_stored;
+
+ while (rpe)
+ {
+ new_stored = rpe->new;
+ rpe_mark_seen(req->hook, rpe);
+ rpe = rpe_next(rpe, src);
+ }
+
+ *new = RTES_CLONE(new_stored, *new);
+
+ return (*new || *old) && (&new_stored->rte != *old);
+}
+
+static int
+channel_aux_export_one_best(struct rt_export_request *req, struct rt_pending_export *rpe, rte **new, rte **old)
+{
+ *old = RTES_OR_NULL(rpe->old_best);
+ struct rte_storage *new_stored;
+
+ while (rpe)
+ {
+ new_stored = rpe->new_best;
+ rpe_mark_seen(req->hook, rpe);
+ rpe = rpe_next(rpe, NULL);
+ }
+
+ *new = RTES_CLONE(new_stored, *new);
+
+ return (*new || *old) && (&new_stored->rte != *old);
+}
+
+static void
+channel_in_export_one_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+
+ rte n0, *new = &n0, *old;
+ if (channel_aux_export_one_any(req, rpe, &new, &old))
+ rte_update_direct(cat->c, net, new, old ? old->src : new->src);
+}
+
+static void
+channel_in_export_one_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+
+ rte n0, *new = &n0, *old;
+ if (channel_aux_export_one_best(req, rpe, &new, &old))
+ rte_update_direct(cat->c, net, new, old ? old->src : new->src);
+}
+
+static void
+channel_in_export_bulk_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ for (uint i=0; i<count; i++)
+ {
+ rte n0 = *feed[i];
+ rte_update_direct(cat->c, net, &n0, n0.src);
+ }
+}
+
+static void
+channel_in_export_bulk_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ if (!count)
+ return;
+
+ rte n0 = *feed[0];
+ rte_update_direct(cat->c, net, &n0, n0.src);
+}
+
+void do_rt_notify_direct(struct channel *c, const net_addr *net, rte *new, const rte *old);
+
+static void
+channel_out_export_one_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ rte n0, *new = &n0, *old;
+ if (channel_aux_export_one_any(req, rpe, &new, &old))
+ do_rt_notify_direct(cat->c, net, new, old);
+}
+
+static void
+channel_out_export_one_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ rte n0, *new = &n0, *old;
+ if (channel_aux_export_one_best(req, rpe, &new, &old))
+ do_rt_notify_direct(cat->c, net, new, old);
+}
+
+static void
+channel_out_export_bulk(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count)
{
- struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req);
+ if (cat->c->ra_mode != RA_ANY)
+ ASSERT_DIE(count <= 1);
+
+ for (uint i=0; i<count; i++)
+ {
+ rte n0 = *feed[i];
+ do_rt_notify_direct(cat->c, net, &n0, NULL);
+ }
+}
- cf->name = "import";
- cf->addr_type = c->net_type;
- cf->internal = 1;
+/* Called by protocol to activate in_table */
+void
+channel_setup_in_table(struct channel *c, int best)
+{
+ int nlen = sizeof("import") + strlen(c->name) + strlen(c->proto->name) + 3;
+
+ struct {
+ struct channel_aux_table cat;
+ struct rtable_config tab_cf;
+ char name[0];
+ } *cat = mb_allocz(c->proto->pool, sizeof(*cat) + nlen);
+
+ bsprintf(cat->name, "%s.%s.import", c->proto->name, c->name);
+
+ cat->tab_cf.owner = cat;
+ cat->tab_cf.name = cat->name;
+ cat->tab_cf.addr_type = c->net_type;
+ cat->tab_cf.cork_limit = 4 * page_size / sizeof(struct rt_pending_export);
+
+ c->in_table = &cat->cat;
+ c->in_table->push = (struct rt_import_request) {
+ .name = cat->name,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_in_push_dump_req,
+ .log_state_change = channel_push_log_state_change,
+ .preimport = channel_in_preimport,
+ };
+ c->in_table->get = (struct rt_export_request) {
+ .name = cat->name,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_in_get_dump_req,
+ .log_state_change = channel_get_log_state_change,
+ .export_one = best ? channel_in_export_one_best : channel_in_export_one_any,
+ .export_bulk = best ? channel_in_export_bulk_best : channel_in_export_bulk_any,
+ };
- c->in_table = rt_setup(c->proto->pool, cf);
+ c->in_table->c = c;
+ c->in_table->tab = rt_setup(c->proto->pool, &cat->tab_cf);
- c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c);
+ rt_request_import(c->in_table->tab, &c->in_table->push);
+ rt_request_export(c->in_table->tab, &c->in_table->get);
}
/* Called by protocol to activate out_table */
void
channel_setup_out_table(struct channel *c)
{
- struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
- cf->name = "export";
- cf->addr_type = c->net_type;
- cf->internal = 1;
+ int nlen = sizeof("export") + strlen(c->name) + strlen(c->proto->name) + 3;
+
+ struct {
+ struct channel_aux_table cat;
+ struct rtable_config tab_cf;
+ char name[0];
+ } *cat = mb_allocz(c->proto->pool, sizeof(*cat) + nlen);
+
+ bsprintf(cat->name, "%s.%s.export", c->proto->name, c->name);
+
+ cat->tab_cf.owner = cat;
+ cat->tab_cf.name = cat->name;
+ cat->tab_cf.addr_type = c->net_type;
+ cat->tab_cf.cork_limit = 4 * page_size / sizeof(struct rt_pending_export);
- c->out_table = rt_setup(c->proto->pool, cf);
+ c->out_table = &cat->cat;
+ c->out_table->push = (struct rt_import_request) {
+ .name = cat->name,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_out_push_dump_req,
+ .log_state_change = channel_push_log_state_change,
+ };
+ c->out_table->get = (struct rt_export_request) {
+ .name = cat->name,
+ .list = proto_work_list(c->proto),
+ .trace_routes = c->debug | c->proto->debug,
+ .dump_req = channel_out_get_dump_req,
+ .log_state_change = channel_get_log_state_change,
+ .export_one = (c->ra_mode == RA_ANY) ? channel_out_export_one_any : channel_out_export_one_best,
+ .export_bulk = channel_out_export_bulk,
+ };
+
+ c->out_table->c = c;
+ c->out_table->tab = rt_setup(c->proto->pool, &cat->tab_cf);
+
+ rt_request_import(c->out_table->tab, &c->out_table->push);
+ rt_request_export(c->out_table->tab, &c->out_table->get);
}
+static void
+channel_aux_request_refeed(struct channel_aux_table *cat)
+{
+ cat->refeed_pending = 1;
+ rt_stop_export(&cat->get, channel_aux_export_stopped);
+}
static void
channel_do_start(struct channel *c)
{
- rt_lock_table(c->table);
- add_tail(&c->table->channels, &c->table_node);
c->proto->active_channels++;
- c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c);
-
- bmap_init(&c->export_map, c->proto->pool, 1024);
- memset(&c->stats, 0, sizeof(struct proto_stats));
-
- channel_reset_limit(&c->rx_limit);
- channel_reset_limit(&c->in_limit);
- channel_reset_limit(&c->out_limit);
-
CALL(c->channel->start, c);
+
+ channel_start_import(c);
}
static void
@@ -572,9 +992,38 @@ channel_do_up(struct channel *c)
}
static void
-channel_do_flush(struct channel *c)
+channel_do_pause(struct channel *c)
+{
+ /* Stop export */
+ if (c->out_req.hook)
+ {
+ rt_stop_export(&c->out_req, channel_export_stopped);
+ c->refeeding = 0;
+ }
+
+ channel_roa_unsubscribe_all(c);
+}
+
+static void
+channel_do_stop(struct channel *c)
{
- rt_schedule_prune(c->table);
+ /* Drop auxiliary tables */
+ if (c->in_table)
+ channel_aux_stop(c->in_table);
+
+ if (c->out_table)
+ channel_aux_stop(c->out_table);
+
+ /* Stop import */
+ if (c->in_req.hook)
+ {
+ c->in_stopped = (event) {
+ .hook = channel_import_stopped,
+ .data = c,
+ .list = proto_event_list(c->proto),
+ };
+ rt_stop_import(&c->in_req, &c->in_stopped);
+ }
c->gr_wait = 0;
if (c->gr_lock)
@@ -582,48 +1031,30 @@ channel_do_flush(struct channel *c)
CALL(c->channel->shutdown, c);
- /* This have to be done in here, as channel pool is freed before channel_do_down() */
- bmap_free(&c->export_map);
- c->in_table = NULL;
- c->reload_event = NULL;
- c->out_table = NULL;
-
channel_roa_unsubscribe_all(c);
}
static void
channel_do_down(struct channel *c)
{
- ASSERT(!c->feed_active && !c->reload_active);
+ ASSERT(!c->out_req.hook && !c->in_req.hook && !c->out_table && !c->in_table);
- rem_node(&c->table_node);
- rt_unlock_table(c->table);
c->proto->active_channels--;
- if ((c->stats.imp_routes + c->stats.filt_routes) != 0)
- log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name);
-
- // bmap_free(&c->export_map);
- memset(&c->stats, 0, sizeof(struct proto_stats));
-
- c->in_table = NULL;
- c->reload_event = NULL;
- c->out_table = NULL;
-
- /* The in_table and out_table are going to be freed by freeing their resource pools. */
+ memset(&c->import_stats, 0, sizeof(struct channel_import_stats));
+ memset(&c->export_stats, 0, sizeof(struct channel_export_stats));
CALL(c->channel->cleanup, c);
/* Schedule protocol shutddown */
if (proto_is_done(c->proto))
- ev_schedule(c->proto->event);
+ proto_send_event(c->proto);
}
void
channel_set_state(struct channel *c, uint state)
{
uint cs = c->channel_state;
- uint es = c->export_state;
DBG("%s reporting channel %s state transition %s -> %s\n", c->proto->name, c->name, c_states[cs], c_states[state]);
if (state == cs)
@@ -635,24 +1066,15 @@ channel_set_state(struct channel *c, uint state)
switch (state)
{
case CS_START:
- ASSERT(cs == CS_DOWN || cs == CS_UP);
+ ASSERT(cs == CS_DOWN || cs == CS_PAUSE);
if (cs == CS_DOWN)
channel_do_start(c);
- if (es != ES_DOWN)
- channel_stop_export(c);
-
- if (c->in_table && (cs == CS_UP))
- channel_reset_import(c);
-
- if (c->out_table && (cs == CS_UP))
- channel_reset_export(c);
-
break;
case CS_UP:
- ASSERT(cs == CS_DOWN || cs == CS_START);
+ ASSERT(cs == CS_DOWN || cs == CS_START || cs == CS_PAUSE);
if (cs == CS_DOWN)
channel_do_start(c);
@@ -663,23 +1085,24 @@ channel_set_state(struct channel *c, uint state)
channel_do_up(c);
break;
- case CS_FLUSHING:
- ASSERT(cs == CS_START || cs == CS_UP);
+ case CS_PAUSE:
+ ASSERT(cs == CS_UP);
- if (es != ES_DOWN)
- channel_stop_export(c);
+ if (cs == CS_UP)
+ channel_do_pause(c);
+ break;
- if (c->in_table && (cs == CS_UP))
- channel_reset_import(c);
+ case CS_STOP:
+ ASSERT(cs == CS_UP || cs == CS_START || cs == CS_PAUSE);
- if (c->out_table && (cs == CS_UP))
- channel_reset_export(c);
+ if (cs == CS_UP)
+ channel_do_pause(c);
- channel_do_flush(c);
+ channel_do_stop(c);
break;
case CS_DOWN:
- ASSERT(cs == CS_FLUSHING);
+ ASSERT(cs == CS_STOP);
channel_do_down(c);
break;
@@ -701,50 +1124,62 @@ channel_set_state(struct channel *c, uint state)
* completed, it will switch back to ES_READY. This function can be called
* even when feeding is already running, in that case it is restarted.
*/
-void
-channel_request_feeding(struct channel *c)
+static void
+channel_request_table_feeding(struct channel *c)
{
- ASSERT(c->channel_state == CS_UP);
+ ASSERT(c->out_req.hook);
- CD(c, "Feeding requested");
+ c->refeed_pending = 1;
+ rt_stop_export(&c->out_req, channel_export_stopped);
+}
- /* Do nothing if we are still waiting for feeding */
- if (c->export_state == ES_DOWN)
+void
+channel_request_feeding(struct channel *c)
+{
+ if (c->gr_wait || !c->proto->rt_notify)
return;
- /* If we are already feeding, we want to restart it */
- if (c->export_state == ES_FEEDING)
- {
- /* Unless feeding is in initial state */
- if (!c->feed_active)
- return;
-
- rt_feed_channel_abort(c);
- }
+ CD(c, "Refeed requested");
- /* Track number of exported routes during refeed */
- c->refeed_count = 0;
+ ASSERT_DIE(c->out_req.hook);
- channel_schedule_feed(c, 0); /* Sets ES_FEEDING */
- channel_log_state_change(c);
+ if (c->out_table)
+ channel_aux_request_refeed(c->out_table);
+ else
+ channel_request_table_feeding(c);
}
-static void
+void
channel_request_reload(struct channel *c)
{
- ASSERT(c->channel_state == CS_UP);
+ ASSERT(c->in_req.hook);
ASSERT(channel_reloadable(c));
CD(c, "Reload requested");
- c->proto->reload_routes(c);
+ if (c->in_table)
+ channel_aux_request_refeed(c->in_table);
+ else
+ c->proto->reload_routes(c);
+}
- /*
- * Should this be done before reload_routes() hook?
- * Perhaps, but routes are updated asynchronously.
- */
- channel_reset_limit(&c->rx_limit);
- channel_reset_limit(&c->in_limit);
+void
+channel_refresh_begin(struct channel *c)
+{
+ CD(c, "Channel route refresh begin");
+ if (c->in_table)
+ rt_refresh_begin(&c->in_table->push);
+ else
+ rt_refresh_begin(&c->in_req);
+}
+
+void
+channel_refresh_end(struct channel *c)
+{
+ if (c->in_table)
+ rt_refresh_end(&c->in_table->push);
+ else
+ rt_refresh_end(&c->in_req);
}
const struct channel_class channel_basic = {
@@ -847,19 +1282,19 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
/* Reconfigure channel fields */
c->in_filter = cf->in_filter;
c->out_filter = cf->out_filter;
- c->rx_limit = cf->rx_limit;
- c->in_limit = cf->in_limit;
- c->out_limit = cf->out_limit;
+
+ channel_update_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit);
+ channel_update_limit(c, &c->in_limit, PLD_IN, &cf->in_limit);
+ channel_update_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit);
// c->ra_mode = cf->ra_mode;
c->merge_limit = cf->merge_limit;
c->preference = cf->preference;
c->debug = cf->debug;
+ c->in_req.trace_routes = c->out_req.trace_routes = c->debug | c->proto->debug;
c->in_keep_filtered = cf->in_keep_filtered;
c->rpki_reload = cf->rpki_reload;
- channel_verify_limits(c);
-
/* Execute channel-specific reconfigure hook */
if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed))
return 0;
@@ -902,7 +1337,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
channel_request_reload(c);
if (export_changed)
- channel_request_feeding(c);
+ channel_request_table_feeding(c);
done:
CD(c, "Reconfigured");
@@ -950,34 +1385,50 @@ proto_configure_channel(struct proto *p, struct channel **pc, struct channel_con
return 1;
}
+static void
+proto_cleanup(struct proto *p)
+{
+ p->active = 0;
+ proto_log_state_change(p);
+ proto_rethink_goal(p);
+}
static void
-proto_event(void *ptr)
+proto_loop_stopped(void *ptr)
{
struct proto *p = ptr;
- if (p->do_start)
- {
- if_feed_baby(p);
- p->do_start = 0;
- }
+ ASSERT_DIE(birdloop_inside(&main_birdloop));
+
+ p->loop = &main_birdloop;
+ p->pool = NULL;
+ p->event->list = NULL;
+
+ proto_cleanup(p);
+}
+
+static void
+proto_event(void *ptr)
+{
+ struct proto *p = ptr;
if (p->do_stop)
{
if (p->proto == &proto_unix_iface)
if_flush_ifaces(p);
+
p->do_stop = 0;
}
if (proto_is_done(p))
- {
- if (p->proto->cleanup)
- p->proto->cleanup(p);
-
- 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
+ {
+ rp_free(p->pool, proto_pool);
+ p->pool = NULL;
+ proto_cleanup(p);
+ }
}
@@ -1018,10 +1469,10 @@ 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;
- p->vrf_set = c->vrf_set;
insert_node(&p->n, n);
p->event = ev_new_init(proto_pool, proto_event, p);
@@ -1034,11 +1485,30 @@ 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");
+
+ int ns = strlen("Protocol ") + strlen(p->cf->name) + 1;
+ void *nb = mb_alloc(proto_pool, ns);
+ ASSERT_DIE(ns - 1 == bsnprintf(nb, ns, "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->pool = rp_new(proto_pool, &main_birdloop, nb);
+ else
+ {
+ p->loop = birdloop_new(proto_pool, p->cf->loop_order, nb);
+ p->pool = birdloop_pool(p->loop);
+ }
+
+ p->event->list = proto_event_list(p);
+
+ mb_move(nb, p->pool);
+
+ PROTO_LOCKED_FROM_MAIN(p)
+ proto_notify_state(p, (p->proto->start ? p->proto->start(p) : PS_UP));
}
@@ -1074,6 +1544,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);
@@ -1189,8 +1660,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
if ((nc->protocol != oc->protocol) ||
(nc->net_type != oc->net_type) ||
(nc->disabled != p->disabled) ||
- (nc->vrf != oc->vrf) ||
- (nc->vrf_set != oc->vrf_set))
+ (nc->vrf != oc->vrf))
return 0;
p->name = nc->name;
@@ -1363,11 +1833,20 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
}
static void
-proto_rethink_goal(struct proto *p)
+proto_shutdown(struct proto *p)
{
- struct protocol *q;
- byte goal;
+ 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)
+{
if (p->reconfiguring && !p->active)
{
struct proto_config *nc = p->cf_new;
@@ -1387,32 +1866,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 *
@@ -1524,7 +1983,7 @@ graceful_restart_done(timer *t UNUSED)
WALK_LIST(c, p->channels)
{
/* Resume postponed export of routes */
- if ((c->channel_state == CS_UP) && c->gr_wait && c->proto->rt_notify)
+ if ((c->channel_state == CS_UP) && c->gr_wait && p->rt_notify)
channel_start_export(c);
/* Cleanup */
@@ -1614,7 +2073,11 @@ protos_dump_all(void)
struct proto *p;
WALK_LIST(p, proto_list)
{
- debug(" protocol %s state %s\n", p->name, p_states[p->proto_state]);
+#define DPF(x) (p->x ? " " #x : "")
+ debug(" protocol %s (%p) state %s with %d active channels flags: %s%s%s%s\n",
+ p->name, p, p_states[p->proto_state], p->active_channels,
+ DPF(disabled), DPF(active), DPF(do_stop), DPF(reconfiguring));
+#undef DPF
struct channel *c;
WALK_LIST(c, p->channels)
@@ -1624,6 +2087,23 @@ protos_dump_all(void)
debug("\tInput filter: %s\n", filter_name(c->in_filter));
if (c->out_filter)
debug("\tOutput filter: %s\n", filter_name(c->out_filter));
+ debug("\tChannel state: %s/%s/%s\n", c_states[c->channel_state],
+ c->in_req.hook ? rt_import_state_name(rt_import_get_state(c->in_req.hook)) : "-",
+ c->out_req.hook ? rt_export_state_name(rt_export_get_state(c->out_req.hook)) : "-");
+ if (c->in_table)
+ {
+ debug("\tInput aux table:\n");
+ rt_dump_hooks(c->in_table->tab);
+ rt_dump(c->in_table->tab);
+ debug("\tEnd of input aux table.\n");
+ }
+ if (c->out_table)
+ {
+ debug("\tOutput aux table:\n");
+ rt_dump_hooks(c->in_table->tab);
+ rt_dump(c->in_table->tab);
+ debug("\tEnd of output aux table.\n");
+ }
}
if (p->proto->dump && (p->proto_state != PS_DOWN))
@@ -1702,7 +2182,7 @@ protos_build(void)
proto_build(&proto_perf);
#endif
- proto_pool = rp_new(&root_pool, "Protocols");
+ proto_pool = rp_new(&root_pool, &main_birdloop, "Protocols");
proto_shutdown_timer = tm_new(proto_pool);
proto_shutdown_timer->hook = proto_shutdown_loop;
}
@@ -1780,108 +2260,132 @@ proto_set_message(struct proto *p, char *msg, int len)
}
-static const char *
-channel_limit_name(struct channel_limit *l)
-{
- const char *actions[] = {
- [PLA_WARN] = "warn",
- [PLA_BLOCK] = "block",
- [PLA_RESTART] = "restart",
- [PLA_DISABLE] = "disable",
- };
+static const char * channel_limit_name[] = {
+ [PLA_WARN] = "warn",
+ [PLA_BLOCK] = "block",
+ [PLA_RESTART] = "restart",
+ [PLA_DISABLE] = "disable",
+};
- return actions[l->action];
-}
-/**
- * channel_notify_limit: notify about limit hit and take appropriate action
- * @c: channel
- * @l: limit being hit
- * @dir: limit direction (PLD_*)
- * @rt_count: the number of routes
- *
- * The function is called by the route processing core when limit @l
- * is breached. It activates the limit and tooks appropriate action
- * according to @l->action.
- */
-void
-channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count)
+static void
+channel_log_limit(struct channel *c, struct limit *l, int dir)
{
const char *dir_name[PLD_MAX] = { "receive", "import" , "export" };
- const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
- struct proto *p = c->proto;
+ log(L_WARN "Channel %s.%s hits route %s limit (%d), action: %s",
+ c->proto->name, c->name, dir_name[dir], l->max, channel_limit_name[c->limit_actions[dir]]);
+}
- if (l->state == PLS_BLOCKED)
+static void
+channel_activate_limit(struct channel *c, struct limit *l, int dir)
+{
+ if (c->limit_active & (1 << dir))
return;
- /* For warning action, we want the log message every time we hit the limit */
- if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
- log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
- p->name, dir_name[dir], l->limit, channel_limit_name(l));
+ c->limit_active |= (1 << dir);
+ channel_log_limit(c, l, dir);
+}
- switch (l->action)
- {
- case PLA_WARN:
- l->state = PLS_ACTIVE;
- break;
+static int
+channel_limit_warn(struct limit *l, void *data)
+{
+ struct channel_limit_data *cld = data;
+ struct channel *c = cld->c;
+ int dir = cld->dir;
- case PLA_BLOCK:
- l->state = PLS_BLOCKED;
- break;
+ channel_log_limit(c, l, dir);
- case PLA_RESTART:
- case PLA_DISABLE:
- l->state = PLS_BLOCKED;
- if (p->proto_state == PS_UP)
- proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
- break;
- }
+ return 0;
}
-static void
-channel_verify_limits(struct channel *c)
+static int
+channel_limit_block(struct limit *l, void *data)
+{
+ struct channel_limit_data *cld = data;
+ struct channel *c = cld->c;
+ int dir = cld->dir;
+
+ channel_activate_limit(c, l, dir);
+
+ return 1;
+}
+
+static const byte chl_dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
+
+static int
+channel_limit_down(struct limit *l, void *data)
{
- struct channel_limit *l;
- u32 all_routes = c->stats.imp_routes + c->stats.filt_routes;
+ struct channel_limit_data *cld = data;
+ struct channel *c = cld->c;
+ struct proto *p = c->proto;
+ int dir = cld->dir;
- l = &c->rx_limit;
- if (l->action && (all_routes > l->limit))
- channel_notify_limit(c, l, PLD_RX, all_routes);
+ channel_activate_limit(c, l, dir);
- l = &c->in_limit;
- if (l->action && (c->stats.imp_routes > l->limit))
- channel_notify_limit(c, l, PLD_IN, c->stats.imp_routes);
+ if (p->proto_state == PS_UP)
+ proto_schedule_down(p, c->limit_actions[dir] == PLA_RESTART, chl_dir_down[dir]);
- l = &c->out_limit;
- if (l->action && (c->stats.exp_routes > l->limit))
- channel_notify_limit(c, l, PLD_OUT, c->stats.exp_routes);
+ return 1;
}
-static inline void
-channel_reset_limit(struct channel_limit *l)
+static int (*channel_limit_action[])(struct limit *, void *) = {
+ [PLA_NONE] = NULL,
+ [PLA_WARN] = channel_limit_warn,
+ [PLA_BLOCK] = channel_limit_block,
+ [PLA_RESTART] = channel_limit_down,
+ [PLA_DISABLE] = channel_limit_down,
+};
+
+static void
+channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf)
+{
+ l->action = channel_limit_action[cf->action];
+ c->limit_actions[dir] = cf->action;
+
+ struct channel_limit_data cld = { .c = c, .dir = dir };
+ limit_update(l, &cld, cf->action ? cf->limit : ~((u32) 0));
+}
+
+static void
+channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf)
{
- if (l->action)
- l->state = PLS_INITIAL;
+ channel_reset_limit(c, l, dir);
+ channel_update_limit(c, l, dir, cf);
}
+static void
+channel_reset_limit(struct channel *c, struct limit *l, int dir)
+{
+ limit_reset(l);
+ c->limit_active &= ~(1 << dir);
+}
+
+static struct rte_owner_class default_rte_owner_class;
+
static inline void
proto_do_start(struct proto *p)
{
p->active = 1;
- p->do_start = 1;
- ev_schedule(p->event);
+
+ rt_init_sources(&p->sources, p->name, proto_work_list(p));
+ if (!p->sources.class)
+ p->sources.class = &default_rte_owner_class;
+
+ if (!p->cf->late_if_feed)
+ if_feed_baby(p);
}
static void
proto_do_up(struct proto *p)
{
if (!p->main_source)
- {
p->main_source = rt_get_source(p, 0);
- rt_lock_source(p->main_source);
- }
+ // Locked automaticaly
proto_start_channels(p);
+
+ if (p->cf->late_if_feed)
+ if_feed_baby(p);
}
static inline void
@@ -1896,9 +2400,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);
@@ -1906,6 +2407,10 @@ proto_do_stop(struct proto *p)
}
proto_stop_channels(p);
+ rt_destroy_sources(&p->sources, p->event);
+
+ p->do_stop = 1;
+ proto_send_event(p);
}
static void
@@ -1913,12 +2418,10 @@ proto_do_down(struct proto *p)
{
p->down_code = 0;
neigh_prune();
- rfree(p->pool);
- p->pool = NULL;
/* Shutdown is finished in the protocol event */
if (proto_is_done(p))
- ev_schedule(p->event);
+ proto_send_event(p);
}
@@ -2009,38 +2512,58 @@ proto_state_name(struct proto *p)
static void
channel_show_stats(struct channel *c)
{
- struct proto_stats *s = &c->stats;
+ struct channel_import_stats *ch_is = &c->import_stats;
+ struct channel_export_stats *ch_es = &c->export_stats;
+ struct rt_import_stats *rt_is = c->in_req.hook ? &c->in_req.hook->stats : NULL;
+ struct rt_export_stats *rt_es = c->out_req.hook ? &c->out_req.hook->stats : NULL;
+
+#define SON(ie, item) ((ie) ? (ie)->item : 0)
+#define SCI(item) SON(ch_is, item)
+#define SCE(item) SON(ch_es, item)
+#define SRI(item) SON(rt_is, item)
+#define SRE(item) SON(rt_es, item)
+
+ u32 rx_routes = c->rx_limit.count;
+ u32 in_routes = c->in_limit.count;
+ u32 out_routes = c->out_limit.count;
if (c->in_keep_filtered)
cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
- s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes);
+ in_routes, (rx_routes - in_routes), out_routes, SRI(pref));
else
cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
- s->imp_routes, s->exp_routes, s->pref_routes);
-
- cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
- cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
- s->imp_updates_received, s->imp_updates_invalid,
- s->imp_updates_filtered, s->imp_updates_ignored,
- s->imp_updates_accepted);
- cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
- s->imp_withdraws_received, s->imp_withdraws_invalid,
- s->imp_withdraws_ignored, s->imp_withdraws_accepted);
- cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u",
- s->exp_updates_received, s->exp_updates_rejected,
- s->exp_updates_filtered, s->exp_updates_accepted);
- cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u",
- s->exp_withdraws_received, s->exp_withdraws_accepted);
+ in_routes, out_routes, SRI(pref));
+
+ cli_msg(-1006, " Route change stats: received rejected filtered ignored limited accepted");
+ cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u %10u",
+ SCI(updates_received), SCI(updates_invalid),
+ SCI(updates_filtered), SRI(updates_ignored),
+ SCI(updates_limited_rx) + SCI(updates_limited_in),
+ SRI(updates_accepted));
+ cli_msg(-1006, " Import withdraws: %10u %10u --- %10u --- %10u",
+ SCI(withdraws_received), SCI(withdraws_invalid),
+ SRI(withdraws_ignored), SRI(withdraws_accepted));
+ cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u %10u",
+ SRE(updates_received), SCE(updates_rejected),
+ SCE(updates_filtered), SCE(updates_limited), SCE(updates_accepted));
+ cli_msg(-1006, " Export withdraws: %10u --- --- --- ---%10u",
+ SRE(withdraws_received), SCE(withdraws_accepted));
+
+#undef SRI
+#undef SRE
+#undef SCI
+#undef SCE
+#undef SON
}
void
-channel_show_limit(struct channel_limit *l, const char *dsc)
+channel_show_limit(struct limit *l, const char *dsc, int active, int action)
{
if (!l->action)
return;
- cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : "");
- cli_msg(-1006, " Action: %s", channel_limit_name(l));
+ cli_msg(-1006, " %-16s%d%s", dsc, l->max, active ? " [HIT]" : "");
+ cli_msg(-1006, " Action: %s", channel_limit_name[action]);
}
void
@@ -2048,6 +2571,8 @@ channel_show_info(struct channel *c)
{
cli_msg(-1006, " Channel %s", c->name);
cli_msg(-1006, " State: %s", c_states[c->channel_state]);
+ cli_msg(-1006, " Import state: %s", rt_import_state_name(rt_import_get_state(c->in_req.hook)));
+ cli_msg(-1006, " Export state: %s", rt_export_state_name(rt_export_get_state(c->out_req.hook)));
cli_msg(-1006, " Table: %s", c->table->name);
cli_msg(-1006, " Preference: %d", c->preference);
cli_msg(-1006, " Input filter: %s", filter_name(c->in_filter));
@@ -2058,9 +2583,9 @@ channel_show_info(struct channel *c)
c->gr_lock ? " pending" : "",
c->gr_wait ? " waiting" : "");
- channel_show_limit(&c->rx_limit, "Receive limit:");
- channel_show_limit(&c->in_limit, "Import limit:");
- channel_show_limit(&c->out_limit, "Export limit:");
+ channel_show_limit(&c->rx_limit, "Receive limit:", c->limit_active & (1 << PLD_RX), c->limit_actions[PLD_RX]);
+ channel_show_limit(&c->in_limit, "Import limit:", c->limit_active & (1 << PLD_IN), c->limit_actions[PLD_IN]);
+ channel_show_limit(&c->out_limit, "Export limit:", c->limit_active & (1 << PLD_OUT), c->limit_actions[PLD_OUT]);
if (c->channel_state != CS_DOWN)
channel_show_stats(c);
@@ -2106,8 +2631,8 @@ proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
cli_msg(-1006, " Message: %s", p->message);
if (p->cf->router_id)
cli_msg(-1006, " Router ID: %R", p->cf->router_id);
- if (p->vrf_set)
- cli_msg(-1006, " VRF: %s", p->vrf ? p->vrf->name : "default");
+ if (p->vrf)
+ cli_msg(-1006, " VRF: %s", p->vrf->name);
if (p->proto->show_proto_info)
p->proto->show_proto_info(p);
@@ -2135,7 +2660,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);
}
@@ -2168,9 +2693,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);
}
@@ -2243,8 +2768,15 @@ proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uint
return;
}
- cmd(s->proto->proto, arg, 0);
- cli_msg(0, "");
+ if (s->proto->proto)
+ {
+ struct proto *p = s->proto->proto;
+ PROTO_LOCKED_FROM_MAIN(p)
+ cmd(p, arg, 0);
+ cli_msg(0, "");
+ }
+ else
+ cli_msg(9002, "%s does not exist", s->name);
}
static void
@@ -2255,7 +2787,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");
diff --git a/nest/protocol.h b/nest/protocol.h
index abcc505d..1d4f2059 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -13,11 +13,11 @@
#include "lib/resource.h"
#include "lib/event.h"
#include "nest/route.h"
+#include "nest/limit.h"
#include "conf/conf.h"
struct iface;
struct ifa;
-struct rtable;
struct rte;
struct neighbor;
struct rta;
@@ -74,12 +74,9 @@ struct protocol {
struct proto * (*init)(struct proto_config *); /* Create new instance */
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */
void (*dump)(struct proto *); /* Debugging dump */
- void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
int (*start)(struct proto *); /* Start the instance */
int (*shutdown)(struct proto *); /* Stop the instance */
- void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
- void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
int (*get_attr)(const struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */
@@ -120,9 +117,10 @@ struct proto_config {
int class; /* SYM_PROTO or SYM_TEMPLATE */
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 */
@@ -133,31 +131,6 @@ struct proto_config {
};
/* Protocol statistics */
-struct proto_stats {
- /* Import - from protocol to core */
- u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
- u32 filt_routes; /* Number of routes rejected in import filter but kept in the routing table */
- u32 pref_routes; /* Number of routes selected as best in the (adjacent) routing table */
- u32 imp_updates_received; /* Number of route updates received */
- u32 imp_updates_invalid; /* Number of route updates rejected as invalid */
- u32 imp_updates_filtered; /* Number of route updates rejected by filters */
- u32 imp_updates_ignored; /* Number of route updates rejected as already in route table */
- u32 imp_updates_accepted; /* Number of route updates accepted and imported */
- u32 imp_withdraws_received; /* Number of route withdraws received */
- u32 imp_withdraws_invalid; /* Number of route withdraws rejected as invalid */
- u32 imp_withdraws_ignored; /* Number of route withdraws rejected as already not in route table */
- u32 imp_withdraws_accepted; /* Number of route withdraws accepted and processed */
-
- /* Export - from core to protocol */
- u32 exp_routes; /* Number of routes successfully exported to the protocol */
- u32 exp_updates_received; /* Number of route updates received */
- u32 exp_updates_rejected; /* Number of route updates rejected by protocol */
- u32 exp_updates_filtered; /* Number of route updates rejected by filters */
- u32 exp_updates_accepted; /* Number of route updates accepted and exported */
- u32 exp_withdraws_received; /* Number of route withdraws received */
- u32 exp_withdraws_accepted; /* Number of route withdraws accepted and processed */
-};
-
struct proto {
node n; /* Node in global proto_list */
struct protocol *proto; /* Protocol */
@@ -165,22 +138,23 @@ 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 */
struct rte_src *main_source; /* Primary route source */
+ struct rte_owner sources; /* Route source owner structure */
struct iface *vrf; /* Related VRF instance, NULL if global */
const char *name; /* Name of this instance (== cf->name) */
u32 debug; /* Debugging flags */
u32 mrtdump; /* MRTDump flags */
uint active_channels; /* Number of active channels */
+ uint active_coroutines; /* Number of active coroutines */
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 */
@@ -198,12 +172,11 @@ struct proto {
* ifa_notify Notify protocol about interface address changes.
* rt_notify Notify protocol about routing table updates.
* neigh_notify Notify protocol about neighbor cache events.
- * make_tmp_attrs Add attributes to rta from from private attrs stored in rte. The route and rta MUST NOT be cached.
- * store_tmp_attrs Store private attrs back to rte and undef added attributes. The route and rta MUST NOT be cached.
- * preexport Called as the first step of the route exporting process.
- * It can construct a new rte, add private attributes and
- * decide whether the route shall be exported: 1=yes, -1=no,
- * 0=process it through the export filter set by the user.
+ * preexport Called as the first step of the route exporting process.
+ * It can decide whether the route shall be exported:
+ * -1 = reject,
+ * 0 = continue to export filter
+ * 1 = accept immediately
* reload_routes Request channel to reload all its routes to the core
* (using rte_update()). Returns: 0=reload cannot be done,
* 1= reload is scheduled and will happen (asynchronously).
@@ -213,11 +186,9 @@ struct proto {
void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
- void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
+ void (*rt_notify)(struct proto *, struct channel *, const net_addr *net, struct rte *new, const struct rte *old);
void (*neigh_notify)(struct neighbor *neigh);
- void (*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
- void (*store_tmp_attrs)(struct rte *rt, struct linpool *pool);
- int (*preexport)(struct proto *, struct rte **rt, struct linpool *pool);
+ int (*preexport)(struct channel *, struct rte *rt);
void (*reload_routes)(struct channel *);
void (*feed_begin)(struct channel *, int initial);
void (*feed_end)(struct channel *);
@@ -233,13 +204,12 @@ struct proto {
* rte_remove Called whenever a rte is removed from the routing table.
*/
- int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *);
+ int (*rte_recalculate)(rtable *, struct network *, struct rte *, struct rte *, struct rte *);
int (*rte_better)(struct rte *, struct rte *);
- int (*rte_same)(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)(struct rte *);
/* Hic sunt protocol-specific data */
};
@@ -279,7 +249,7 @@ void channel_graceful_restart_unlock(struct channel *c);
#define DEFAULT_GR_WAIT 240
-void channel_show_limit(struct channel_limit *l, const char *dsc);
+void channel_show_limit(struct limit *l, const char *dsc, int active, int action);
void channel_show_info(struct channel *c);
void channel_cmd_debug(struct channel *c, uint mask);
@@ -386,6 +356,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_coroutines == 0) && (p->sources.uc == 0); }
/*
@@ -434,19 +406,30 @@ extern struct proto_config *cf_dev_proto;
#define PLA_RESTART 4 /* Force protocol restart */
#define PLA_DISABLE 5 /* Shutdown and disable protocol */
-#define PLS_INITIAL 0 /* Initial limit state after protocol start */
-#define PLS_ACTIVE 1 /* Limit was hit */
-#define PLS_BLOCKED 2 /* Limit is active and blocking new routes */
-
struct channel_limit {
u32 limit; /* Maximum number of prefixes */
u8 action; /* Action to take (PLA_*) */
- u8 state; /* State of limit (PLS_*) */
};
-void channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count);
+struct channel_limit_data {
+ struct channel *c;
+ int dir;
+};
+
+#define CLP__RX(_c) (&(_c)->rx_limit)
+#define CLP__IN(_c) (&(_c)->in_limit)
+#define CLP__OUT(_c) (&(_c)->out_limit)
+#if 0
+#define CHANNEL_LIMIT_LOG(_c, _dir, _op) log(L_TRACE "%s.%s: %s limit %s %u", (_c)->proto->name, (_c)->name, #_dir, _op, (CLP__##_dir(_c))->count)
+#else
+#define CHANNEL_LIMIT_LOG(_c, _dir, _op)
+#endif
+
+#define CHANNEL_LIMIT_PUSH(_c, _dir) ({ CHANNEL_LIMIT_LOG(_c, _dir, "push from"); struct channel_limit_data cld = { .c = (_c), .dir = PLD_##_dir }; limit_push(CLP__##_dir(_c), &cld); })
+#define CHANNEL_LIMIT_POP(_c, _dir) ({ limit_pop(CLP__##_dir(_c)); CHANNEL_LIMIT_LOG(_c, _dir, "pop to"); })
+
/*
* Channels
*/
@@ -469,7 +452,6 @@ struct channel_class {
void (*dump)(struct proto *); /* Debugging dump */
- void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
@@ -489,6 +471,7 @@ struct channel_config {
struct proto_config *parent; /* Where channel is defined (proto or template) */
struct rtable_config *table; /* Table we're attached to */
const struct filter *in_filter, *out_filter; /* Attached filters */
+
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
(relevant when in_keep_filtered is active) */
struct channel_limit in_limit; /* Limit for importing routes from protocol */
@@ -505,23 +488,50 @@ struct channel_config {
struct channel {
node n; /* Node in proto->channels */
- node table_node; /* Node in table->channels */
const char *name; /* Channel name (may be NULL) */
const struct channel_class *channel;
struct proto *proto;
- struct rtable *table;
+ rtable *table;
const struct filter *in_filter; /* Input filter */
const struct filter *out_filter; /* Output filter */
- struct bmap export_map; /* Keeps track which routes passed export filter */
- struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */
- struct channel_limit in_limit; /* Input limit */
- struct channel_limit out_limit; /* Output limit */
-
- struct event *feed_event; /* Event responsible for feeding */
- struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
- struct proto_stats stats; /* Per-channel protocol statistics */
+ struct bmap export_map; /* Keeps track which routes were really exported */
+ struct bmap export_reject_map; /* Keeps track which routes were rejected by export filter */
+
+ struct limit rx_limit; /* Receive limit (for in_keep_filtered) */
+ struct limit in_limit; /* Input limit */
+ struct limit out_limit; /* Output limit */
+
+ u8 limit_actions[PLD_MAX]; /* Limit actions enum */
+ u8 limit_active; /* Flags for active limits */
+
+ linpool *rte_update_pool;
+ uint rte_update_nest_cnt;
+
+ struct channel_import_stats {
+ /* Import - from protocol to core */
+ u32 updates_received; /* Number of route updates received */
+ u32 updates_invalid; /* Number of route updates rejected as invalid */
+ u32 updates_filtered; /* Number of route updates rejected by filters */
+ u32 updates_limited_rx; /* Number of route updates exceeding the rx_limit */
+ u32 updates_limited_in; /* Number of route updates exceeding the in_limit */
+ u32 withdraws_received; /* Number of route withdraws received */
+ u32 withdraws_invalid; /* Number of route withdraws rejected as invalid */
+ } import_stats;
+
+ struct channel_export_stats {
+ /* Export - from core to protocol */
+ u32 updates_rejected; /* Number of route updates rejected by protocol */
+ u32 updates_filtered; /* Number of route updates rejected by filters */
+ u32 updates_accepted; /* Number of route updates accepted and exported */
+ u32 updates_limited; /* Number of route updates exceeding the out_limit */
+ u32 withdraws_accepted; /* Number of route withdraws accepted and processed */
+ } export_stats;
+
+ struct rt_import_request in_req; /* Table import connection */
+ struct rt_export_request out_req; /* Table export connection */
+
u32 refeed_count; /* Number of routes exported during refeed regardless of out_limit */
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
@@ -534,31 +544,35 @@ struct channel {
u8 stale; /* Used in reconfiguration */
u8 channel_state;
- u8 export_state; /* Route export state (ES_*, see below) */
- u8 feed_active;
- u8 flush_active;
- u8 refeeding; /* We are refeeding (valid only if export_state == ES_FEEDING) */
+ u8 refeeding; /* Refeeding the channel. */
u8 reloadable; /* Hook reload_routes() is allowed on the channel */
u8 gr_lock; /* Graceful restart mechanism should wait for this channel */
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
+ u8 restart_export; /* Route export should restart as soon as it stops */
btime last_state_change; /* Time of last state transition */
- struct rtable *in_table; /* Internal table for received routes */
- struct event *reload_event; /* Event responsible for reloading from in_table */
- struct fib_iterator reload_fit; /* FIB iterator in in_table used during reloading */
- struct rte *reload_next_rte; /* Route iterator in in_table used during reloading */
- u8 reload_active; /* Iterator reload_fit is linked */
+ struct channel_aux_table *in_table; /* Internal table for received routes */
+ struct event in_stopped; /* Import stop callback */
u8 reload_pending; /* Reloading and another reload is scheduled */
u8 refeed_pending; /* Refeeding and another refeed is scheduled */
u8 rpki_reload; /* RPKI changes trigger channel reload */
- struct rtable *out_table; /* Internal table for exported routes */
+ struct channel_aux_table *out_table; /* Internal table for exported routes */
list roa_subscriptions; /* List of active ROA table subscriptions based on filters roa_check() */
};
+struct channel_aux_table {
+ struct channel *c;
+ struct rt_import_request push;
+ struct rt_export_request get;
+ event push_stopped;
+ rtable *tab;
+ event *stop;
+ u8 refeed_pending;
+};
/*
* Channel states
@@ -585,70 +599,59 @@ struct channel {
* restricted by that and is on volition of the protocol. Generally, channels
* are opened in protocols' start() hooks when going to PS_UP.
*
- * CS_FLUSHING - The transitional state between initialized channel and closed
+ * CS_STOP - The transitional state between initialized channel and closed
* channel. The channel is still initialized, but no route exchange is allowed.
* Instead, the associated table is running flush loop to remove routes imported
* through the channel. After that, the channel changes state to CS_DOWN and
* is detached from the table (the table is unlocked and the channel is unlinked
- * from it). Unlike other states, the CS_FLUSHING state is not explicitly
+ * from it). Unlike other states, the CS_STOP state is not explicitly
* entered or left by the protocol. A protocol may request to close a channel
* (by calling channel_close()), which causes the channel to change state to
- * CS_FLUSHING and later to CS_DOWN. Also note that channels are closed
+ * CS_STOP and later to CS_DOWN. Also note that channels are closed
* automatically by the core when the protocol is going down.
*
+ * CS_PAUSE - Almost the same as CS_STOP, just the table import is kept and
+ * the table export is stopped before transitioning to CS_START.
+ *
* Allowed transitions:
*
* CS_DOWN -> CS_START / CS_UP
- * CS_START -> CS_UP / CS_FLUSHING
- * CS_UP -> CS_START / CS_FLUSHING
- * CS_FLUSHING -> CS_DOWN (automatic)
+ * CS_START -> CS_UP / CS_STOP
+ * CS_UP -> CS_PAUSE / CS_STOP
+ * CS_PAUSE -> CS_START (automatic)
+ * CS_STOP -> CS_DOWN (automatic)
*/
#define CS_DOWN 0
#define CS_START 1
#define CS_UP 2
-#define CS_FLUSHING 3
-
-#define ES_DOWN 0
-#define ES_FEEDING 1
-#define ES_READY 2
-
+#define CS_STOP 3
+#define CS_PAUSE 4
struct channel_config *proto_cf_find_channel(struct proto_config *p, uint net_type);
static inline struct channel_config *proto_cf_main_channel(struct proto_config *pc)
{ return proto_cf_find_channel(pc, pc->net_type); }
-struct channel *proto_find_channel_by_table(struct proto *p, struct rtable *t);
+struct channel *proto_find_channel_by_table(struct proto *p, rtable *t);
struct channel *proto_find_channel_by_name(struct proto *p, const char *n);
struct channel *proto_add_channel(struct proto *p, struct channel_config *cf);
int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf);
void channel_set_state(struct channel *c, uint state);
-void channel_setup_in_table(struct channel *c);
+void channel_setup_in_table(struct channel *c, int best);
void channel_setup_out_table(struct channel *c);
void channel_schedule_reload(struct channel *c);
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); }
-static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
+static inline void channel_close(struct channel *c) { channel_set_state(c, CS_STOP); }
void channel_request_feeding(struct channel *c);
+void channel_request_reload(struct channel *c);
+void channel_refresh_begin(struct channel *c);
+void channel_refresh_end(struct channel *c);
void *channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
void *channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
int channel_reconfigure(struct channel *c, struct channel_config *cf);
-
-/* Moved from route.h to avoid dependency conflicts */
-static inline void rte_update(struct proto *p, const net_addr *n, rte *new) { rte_update2(p->main_channel, n, new, p->main_source); }
-
-static inline void
-rte_update3(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
-{
- if (c->in_table && !rte_update_in(c, n, new, src))
- return;
-
- rte_update2(c, n, new, src);
-}
-
-
#endif
diff --git a/nest/route.h b/nest/route.h
index f5fc9e31..f060ad63 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -2,6 +2,7 @@
* BIRD Internet Routing Daemon -- Routing Table
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
+ * (c) 2019--2021 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@@ -10,13 +11,19 @@
#define _BIRD_ROUTE_H_
#include "lib/lists.h"
+#include "lib/event.h"
#include "lib/bitmap.h"
#include "lib/resource.h"
#include "lib/net.h"
+#include "lib/hash.h"
+#include "lib/event.h"
+
+#include <stdatomic.h>
struct ea_list;
struct protocol;
struct proto;
+struct channel;
struct rte_src;
struct symbol;
struct timer;
@@ -139,61 +146,90 @@ void fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src)
* It's guaranteed that there is at most one RTE for every (prefix,proto) pair.
*/
-struct rtable_config {
- node n;
- char *name;
- 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 */
- byte sorted; /* Routes of network are sorted according to rte_better() */
- byte internal; /* Internal table of a protocol */
- btime min_settle_time; /* Minimum settle time for notifications */
- btime max_settle_time; /* Maximum settle time for notifications */
-};
-
-typedef struct rtable {
- resource r;
- node n; /* Node in list of all tables */
+typedef struct rtable_private {
+#define RTABLE_PUBLIC \
+ resource r; \
+ node n; /* Node in list of all tables */ \
+ struct birdloop *loop; /* This loop runs the table */ \
+ char *name; /* Name of this table */ \
+ uint addr_type; /* Type of address data stored in table (NET_*) */ \
+ struct rtable_config *config; /* Configuration of this table */ \
+ struct event *nhu_event; /* Event to update next hops */ \
+ _Atomic byte nhu_state; /* Next Hop Update state */ \
+
+ RTABLE_PUBLIC;
pool *rp; /* Resource pool to allocate everything from, including itself */
+ struct slab *rte_slab; /* Slab to allocate route objects */
struct fib fib;
- char *name; /* Name of this table */
- list channels; /* List of attached channels (struct channel) */
- uint addr_type; /* Type of address data stored in table (NET_*) */
- int pipe_busy; /* Pipe loop detection */
int use_count; /* Number of protocols using this table */
u32 rt_count; /* Number of routes in the table */
+ u32 rr_count; /* Number of running route refresh requests */
- byte internal; /* Internal table of a protocol */
+ list imports; /* Registered route importers */
+ list exports; /* Registered route exporters */
struct hmap id_map;
struct hostcache *hostcache;
- struct rtable_config *config; /* Configuration of this table */
- struct config *deleted; /* Table doesn't exist in current configuration,
- * delete as soon as use_count becomes 0 and remove
- * obstacle from this routing table.
- */
- struct event *rt_event; /* Routing table event */
+ struct event *prune_event; /* Event to prune abandoned routes */
+ struct event *announce_event; /* Event to announce pending exports */
+ struct event *ec_event; /* Event to prune finished exports */
+ struct event *hcu_event; /* Event to update host cache */
+ void (*delete)(void *); /* Delete callback (in parent loop context) */
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 */
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
- byte hcu_scheduled; /* Hostcache update is scheduled */
- byte nhu_state; /* Next Hop Update state */
+
+ byte cork_active; /* Congestion control activated */
+
struct fib_iterator prune_fit; /* Rtable prune FIB iterator */
struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */
+ struct tbf rl_pipe; /* Rate limiting token buffer for pipe collisions */
+
+ linpool *nhu_lp; /* Linpool used for NHU */
list subscribers; /* Subscribers for notifications */
struct timer *settle_timer; /* Settle time for notifications */
+
+ list pending_exports; /* List of packed struct rt_pending_export */
+
+ struct rt_pending_export *first_export; /* First export to announce */
+ u64 next_export_seq; /* The next export will have this ID */
+} rtable_private;
+
+typedef union {
+ struct { RTABLE_PUBLIC };
+ rtable_private priv;
} rtable;
+#define RT_LOCK(tab) ({ birdloop_enter((tab)->loop); &(tab)->priv; })
+#define RT_UNLOCK(tab) birdloop_leave((tab)->loop)
+#define RT_PRIV(tab) ({ ASSERT_DIE(birdloop_inside((tab)->loop)); &(tab)->priv; })
+
+#define RT_LOCKED(tpub, tpriv) for (rtable_private *tpriv = RT_LOCK(tpub); tpriv; RT_UNLOCK(tpriv), (tpriv = NULL))
+
+struct rtable_config {
+ node n;
+ char *name;
+ void *owner; /* Main config if global table, channel_aux_table if channel table */
+ 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 */
+ byte sorted; /* Routes of network are sorted according to rte_better() */
+ btime min_settle_time; /* Minimum settle time for notifications */
+ btime max_settle_time; /* Maximum settle time for notifications */
+ btime min_rr_settle_time; /* Minimum settle time for notifications when route refresh is running */
+ btime max_rr_settle_time; /* Maximum settle time for notifications when route refresh is running */
+ uint cork_limit; /* Amount of routes to be pending on export to cork imports */
+};
+
struct rt_subscription {
node n;
rtable *tab;
- void (*hook)(struct rt_subscription *b);
- void *data;
+ event *event;
};
#define NHU_CLEAN 0
@@ -202,7 +238,8 @@ struct rt_subscription {
#define NHU_DIRTY 3
typedef struct network {
- struct rte *routes; /* Available routes for this network */
+ struct rte_storage *routes; /* Available routes for this network */
+ struct rt_pending_export *last, *first; /* Routes with unfinished exports */
struct fib_node n; /* FIB flags reserved for kernel syncer */
} net;
@@ -223,7 +260,7 @@ struct hostentry {
ip_addr addr; /* IP address of host, part of key */
ip_addr link; /* (link-local) IP address of host, used as gw
if host is directly attached */
- struct rtable *tab; /* Dependent table, part of key */
+ rtable *tab; /* Dependent table, part of key */
struct hostentry *next; /* Next in hash chain */
unsigned hash_key; /* Hash key */
unsigned uc; /* Use count */
@@ -234,64 +271,178 @@ struct hostentry {
};
typedef struct rte {
- struct rte *next;
- net *net; /* Network this RTE belongs to */
- struct channel *sender; /* Channel used to send the route to the routing table */
struct rta *attrs; /* Attributes of this route */
+ const net_addr *net; /* Network this RTE belongs to */
+ struct rte_src *src; /* Route source that created the route */
+ struct rt_import_hook *sender; /* Import hook used to send the route to the routing table */
+ btime lastmod; /* Last modified (set by table) */
u32 id; /* Table specific route id */
- byte flags; /* Flags (REF_...) */
+ byte flags; /* Table-specific flags */
byte pflags; /* Protocol-specific flags */
- word pref; /* Route preference */
- btime lastmod; /* Last modified */
- union { /* Protocol-dependent data (metrics etc.) */
-#ifdef CONFIG_RIP
- struct {
- struct iface *from; /* Incoming iface */
- u8 metric; /* RIP metric */
- u16 tag; /* External route tag */
- } rip;
-#endif
-#ifdef CONFIG_OSPF
- struct {
- u32 metric1, metric2; /* OSPF Type 1 and Type 2 metrics */
- u32 tag; /* External route tag */
- u32 router_id; /* Router that originated this route */
- } ospf;
-#endif
-#ifdef CONFIG_BGP
- struct {
- u8 suppressed; /* Used for deterministic MED comparison */
- s8 stale; /* Route is LLGR_STALE, -1 if unknown */
- } bgp;
-#endif
-#ifdef CONFIG_BABEL
- struct {
- u16 seqno; /* Babel seqno */
- u16 metric; /* Babel metric */
- u64 router_id; /* Babel router id */
- } babel;
-#endif
- struct { /* Routes generated by krt sync (both temporary and inherited ones) */
- s8 src; /* Alleged route source (see krt.h) */
- u8 proto; /* Kernel source protocol ID */
- u8 seen; /* Seen during last scan */
- u8 best; /* Best route in network, propagated to core */
- u32 metric; /* Kernel metric */
- } krt;
- } u;
+ 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_COW 1 /* Copy this rte on write */
+struct rte_storage {
+ struct rte_storage *next; /* Next in chain */
+ struct rte rte; /* Route data */
+};
+
+#define RTES_CLONE(r, l) ((r) ? (((*(l)) = (r)->rte), (l)) : NULL)
+#define RTES_OR_NULL(r) ((r) ? &((r)->rte) : NULL)
+
#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_USE_STALE 4 /* Do not reset route's stale_cycle to the actual value */
/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
-static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); }
+static inline int rte_is_valid(const rte *r) { return r && !(r->flags & REF_FILTERED); }
/* Route just has REF_FILTERED flag */
-static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); }
+static inline int rte_is_filtered(const rte *r) { return !!(r->flags & REF_FILTERED); }
+
+
+/* Table-channel connections */
+
+struct rt_import_request {
+ struct rt_import_hook *hook; /* The table part of importer */
+ char *name;
+ u8 trace_routes;
+
+ event_list *list; /* Where to schedule import events */
+
+ void (*dump_req)(struct rt_import_request *req);
+ void (*log_state_change)(struct rt_import_request *req, u8 state);
+ /* 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. */
+ struct rte *(*preimport)(struct rt_import_request *req, struct rte *new, struct rte *old);
+};
+
+struct rt_import_hook {
+ node n;
+ rtable *table; /* The connected table */
+ struct rt_import_request *req; /* The requestor */
+
+ struct rt_import_stats {
+ /* Import - from protocol to core */
+ u32 pref; /* Number of routes selected as best in the (adjacent) routing table */
+ u32 updates_ignored; /* Number of route updates rejected as already in route table */
+ u32 updates_accepted; /* Number of route updates accepted and imported */
+ u32 withdraws_ignored; /* Number of route withdraws rejected as already not in route table */
+ 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 */
+
+ struct event *export_announce_event; /* Event to run to announce new exports */
+ struct event *stopped; /* Event to run 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 {
+ struct rt_export_hook *hook; /* Table part of the export */
+ char *name;
+ u8 trace_routes;
+
+ 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.
+ * Thus, for RA_OPTIMAL, &export_one is only set,
+ * for RA_MERGED and RA_ACCEPTED, &export_bulk is only set
+ * and for RA_ANY, both are set to accomodate for feeding all routes but receiving single changes
+ */
+ void (*export_one)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
+ void (*export_bulk)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
+
+ void (*dump_req)(struct rt_export_request *req);
+ void (*log_state_change)(struct rt_export_request *req, u8);
+};
+
+struct rt_export_hook {
+ node n;
+ rtable *table; /* The connected table */
+
+ pool *pool;
+
+ struct rt_export_request *req; /* The requestor */
+
+ struct rt_export_stats {
+ /* Export - from core to protocol */
+ u32 updates_received; /* Number of route updates received */
+ u32 withdraws_received; /* Number of route withdraws received */
+ } stats;
+
+ struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
+
+ 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 */
+ _Atomic u8 export_state; /* Route export state (TES_*, see below) */
+
+ struct event *event; /* Event running all the export operations */
+
+ void (*stopped)(struct rt_export_request *); /* Stored callback when export is stopped */
+};
+
+extern struct event_cork rt_cork;
+
+#define TIS_DOWN 0
+#define TIS_UP 1
+#define TIS_STOP 2
+#define TIS_FLUSHING 3
+#define TIS_WAITING 4
+#define TIS_CLEARED 5
+#define TIS_MAX 6
+
+#define TES_DOWN 0
+#define TES_HUNGRY 1
+#define TES_FEEDING 2
+#define TES_READY 3
+#define TES_STOP 4
+#define TES_MAX 5
+
+void rt_request_import(rtable *tab, struct rt_import_request *req);
+void rt_request_export(rtable *tab, struct rt_export_request *req);
+
+void rt_stop_import(struct rt_import_request *, struct event *stopped);
+void rt_stop_export(struct rt_export_request *, void (*stopped)(struct rt_export_request *));
+
+const char *rt_import_state_name(u8 state);
+const char *rt_export_state_name(u8 state);
+
+static inline u8 rt_import_get_state(struct rt_import_hook *ih) { return ih ? ih->import_state : TIS_DOWN; }
+static inline u8 rt_export_get_state(struct rt_export_hook *eh) { return eh ? eh->export_state : TES_DOWN; }
+
+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 */
@@ -307,56 +458,75 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED);
#define RIC_REJECT -1 /* Rejected by protocol */
#define RIC_DROP -2 /* Silently dropped by protocol */
+#define rte_update channel_rte_import
+/**
+ * rte_update - enter a new update to a routing table
+ * @c: channel doing the update
+ * @net: network address
+ * @rte: a &rte representing the new route
+ * @src: old route source identifier
+ *
+ * This function imports a new route to the appropriate table (via the channel).
+ * Table keys are @net (obligatory) and @rte->attrs->src.
+ * Both the @net and @rte pointers can be local.
+ *
+ * The route attributes (@rte->attrs) are obligatory. They can be also allocated
+ * locally. Anyway, if you use an already-cached attribute object, you shall
+ * call rta_clone() on that object yourself. (This semantics may change in future.)
+ *
+ * If the route attributes are local, you may set @rte->attrs->src to NULL, then
+ * the protocol's default route source will be supplied.
+ *
+ * When rte_update() gets a route, it automatically validates it. This includes
+ * checking for validity of the given network and next hop addresses and also
+ * checking for host-scope or link-scope routes. Then the import filters are
+ * processed and if accepted, the route is passed to route table recalculation.
+ *
+ * The accepted routes are then inserted into the table, replacing the old route
+ * for the same @net identified by @src. Then the route is announced
+ * to all the channels connected to the table using the standard export mechanism.
+ * Setting @rte to NULL makes this a withdraw, otherwise @rte->src must be the same
+ * as @src.
+ *
+ * All memory used for temporary allocations is taken from a special linpool
+ * @rte_update_pool and freed when rte_update() finishes.
+ */
+void rte_update(struct channel *c, const net_addr *net, struct rte *rte, struct rte_src *src);
+
extern list routing_tables;
struct config;
void rt_init(void);
void rt_preconfig(struct config *);
void rt_commit(struct config *new, struct config *old);
-void rt_lock_table(rtable *);
-void rt_unlock_table(rtable *);
+void rt_lock_table(rtable_private *);
+void rt_unlock_table(rtable_private *);
void rt_subscribe(rtable *tab, struct rt_subscription *s);
void rt_unsubscribe(struct rt_subscription *s);
rtable *rt_setup(pool *, struct rtable_config *);
-static inline void rt_shutdown(rtable *r) { rfree(r->rp); }
-static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
-static inline net *net_find_valid(rtable *tab, const net_addr *addr)
-{ net *n = net_find(tab, addr); return (n && rte_is_valid(n->routes)) ? n : NULL; }
-static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); }
-void *net_route(rtable *tab, const net_addr *n);
+static inline net *net_find(rtable_private *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
+static inline net *net_find_valid(rtable_private *tab, const net_addr *addr)
+{ net *n = net_find(tab, addr); return (n && n->routes && rte_is_valid(&n->routes->rte)) ? n : NULL; }
+static inline net *net_get(rtable_private *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); }
+void *net_route(rtable_private *tab, const net_addr *n);
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
-rte *rte_find(net *net, struct rte_src *src);
-rte *rte_get_temp(struct rta *);
-void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
-/* rte_update() moved to protocol.h to avoid dependency conflicts */
-int rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter);
-rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
-void rt_refresh_begin(rtable *t, struct channel *c);
-void rt_refresh_end(rtable *t, struct channel *c);
-void rt_modify_stale(rtable *t, struct channel *c);
-void rt_schedule_prune(rtable *t);
-void rte_dump(rte *);
-void rte_free(rte *);
-rte *rte_do_cow(rte *);
-static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
-rte *rte_cow_rta(rte *r, linpool *lp);
-void rte_init_tmp_attrs(struct rte *r, linpool *lp, uint max);
-void rte_make_tmp_attr(struct rte *r, uint id, uint type, uintptr_t val);
-void rte_make_tmp_attrs(struct rte **r, struct linpool *pool, struct rta **old_attrs);
-uintptr_t rte_store_tmp_attr(struct rte *r, uint id);
+int rt_examine(rtable_private *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(struct rt_import_request *);
+void rt_refresh_end(struct rt_import_request *);
+void rt_schedule_prune(rtable_private *t);
+void rte_dump(struct rte_storage *);
+void rte_free(struct rte_storage *, rtable_private *);
+struct rte_storage *rte_store(const rte *, net *net, rtable_private *);
void rt_dump(rtable *);
void rt_dump_all(void);
-int rt_feed_channel(struct channel *c);
-void rt_feed_channel_abort(struct channel *c);
-int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
-int rt_reload_channel(struct channel *c);
-void rt_reload_channel_abort(struct channel *c);
+void rt_dump_hooks(rtable *);
+void rt_dump_hooks_all(void);
void rt_prune_sync(rtable *t, int all);
-int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
-
/* Default limit for ECMP next hops, defined in sysdep code */
extern const int rt_default_ecmp;
@@ -379,6 +549,7 @@ struct rt_show_data {
struct channel *export_channel;
struct config *running_on_config;
struct krt_proto *kernel;
+ struct rt_export_hook *kernel_export_hook;
int export_mode, primary_only, filtered, stats, show_for;
int table_open; /* Iteration (fit) is open */
@@ -430,30 +601,29 @@ struct nexthop {
struct rte_src {
struct rte_src *next; /* Hash chain */
- struct proto *proto; /* Protocol the source is based on */
+ struct rte_owner *owner; /* Route source owner */
u32 private_id; /* Private ID, assigned by the protocol */
u32 global_id; /* Globally unique ID of the source */
- unsigned uc; /* Use count */
+ _Atomic u64 uc; /* Use count */
};
typedef struct rta {
- struct rta *next, **pprev; /* Hash chain */
- u32 uc; /* Use count */
+ struct rta * _Atomic next, * _Atomic *pprev; /* Hash chain */
+ _Atomic u32 uc; /* Use count */
u32 hash_key; /* Hash over important fields */
struct ea_list *eattrs; /* Extended Attribute chain */
- struct rte_src *src; /* Route source that created the route */
struct hostentry *hostentry; /* Hostentry for recursive next-hops */
ip_addr from; /* Advertising router */
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
- u8 source; /* Route source (RTS_...) */
- u8 scope; /* Route scope (SCOPE_... -- see ip.h) */
- u8 dest; /* Route destination type (RTD_...) */
- u8 aflags;
+ u16 cached:1; /* Are attributes cached? */
+ u16 source:7; /* Route source (RTS_...) */
+ u16 scope:4; /* Route scope (SCOPE_... -- see ip.h) */
+ u16 dest:4; /* Route destination type (RTD_...) */
+ word pref;
struct nexthop nh; /* Next hop */
} rta;
-#define RTS_DUMMY 0 /* Dummy route to be removed soon */
#define RTS_STATIC 1 /* Normal static route */
#define RTS_INHERIT 2 /* Route inherited from kernel */
#define RTS_DEVICE 3 /* Device route */
@@ -471,11 +641,6 @@ typedef struct rta {
#define RTS_PERF 15 /* Perf checker */
#define RTS_MAX 16
-#define RTC_UNICAST 0
-#define RTC_BROADCAST 1
-#define RTC_MULTICAST 2
-#define RTC_ANYCAST 3 /* IPv6 Anycast */
-
#define RTD_NONE 0 /* Undefined next hop */
#define RTD_UNICAST 1 /* Next hop is neighbor router */
#define RTD_BLACKHOLE 2 /* Silently drop packets */
@@ -483,8 +648,6 @@ typedef struct rta {
#define RTD_PROHIBIT 4 /* Administratively prohibited */
#define RTD_MAX 5
-#define RTAF_CACHED 1 /* This is a cached rta */
-
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
protocol-specific metric is availabe */
@@ -508,8 +671,8 @@ typedef struct eattr {
byte flags; /* Protocol-dependent flags */
byte type; /* Attribute type and several flags (EAF_...) */
union {
- u32 data;
- const struct adata *ptr; /* Attribute data elsewhere */
+ uintptr_t data;
+ const struct adata *ptr; /* Attribute data elsewhere */
} u;
} eattr;
@@ -517,7 +680,6 @@ typedef struct eattr {
#define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_ID(ea) ((ea) & 0xff)
#define EA_PROTO(ea) ((ea) >> 8)
-#define EA_ID_FLAG(ea) (1 << EA_ID(ea))
#define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT)
#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
#define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT)
@@ -540,6 +702,7 @@ const char *ea_custom_name(uint ea);
#define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */
#define EAF_TYPE_BITFIELD 0x09 /* 32-bit embedded bitfield */
#define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */
+#define EAF_TYPE_PTR 0x0d /* Pointer to an object */
#define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */
#define EAF_TYPE_LC_SET 0x12 /* Set of triplets of u32's - large community list */
#define EAF_TYPE_UNDEF 0x1f /* `force undefined' entry */
@@ -578,13 +741,52 @@ typedef struct ea_list {
#define EALF_SORTED 1 /* Attributes are sorted by code */
#define EALF_BISECT 2 /* Use interval bisection for searching */
#define EALF_CACHED 4 /* Attributes belonging to cached rta */
-#define EALF_TEMP 8 /* Temporary ea_list added by make_tmp_attrs hooks */
-struct rte_src *rt_find_source(struct proto *p, u32 id);
-struct rte_src *rt_get_source(struct proto *p, u32 id);
-static inline void rt_lock_source(struct rte_src *src) { src->uc++; }
-static inline void rt_unlock_source(struct rte_src *src) { src->uc--; }
-void rt_prune_sources(void);
+struct rte_owner_class {
+ void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
+ int (*rte_better)(struct rte *, struct rte *);
+ int (*rte_mergable)(struct rte *, struct rte *);
+ u32 (*rte_igp_metric)(struct rte *);
+};
+
+struct rte_owner {
+ struct rte_owner_class *class;
+ int (*rte_recalculate)(rtable_private *, struct network *, struct rte *, struct rte *, struct rte *);
+ HASH(struct rte_src) hash;
+ const char *name;
+ u32 hash_key;
+ u32 uc;
+ event_list *list;
+ event *prune;
+ event *stop;
+};
+
+#define RTE_SRC_PU_SHIFT 44
+#define RTE_SRC_IN_PROGRESS (1ULL << RTE_SRC_PU_SHIFT)
+
+struct rte_src *rt_get_source_o(struct rte_owner *o, u32 id);
+#define rt_get_source(p, id) rt_get_source_o(&(p)->sources, (id))
+static inline void rt_lock_source(struct rte_src *src)
+{
+ u64 uc = atomic_fetch_add_explicit(&src->uc, 1, memory_order_acq_rel);
+ ASSERT_DIE(uc > 0);
+}
+
+static inline void rt_unlock_source(struct rte_src *src)
+{
+ u64 uc = atomic_fetch_add_explicit(&src->uc, RTE_SRC_IN_PROGRESS, memory_order_acq_rel);
+ u64 pending = uc >> RTE_SRC_PU_SHIFT;
+ uc &= RTE_SRC_IN_PROGRESS - 1;
+
+ ASSERT_DIE(uc > pending);
+ if (uc == pending + 1)
+ ev_send(src->owner->list, src->owner->prune);
+
+ atomic_fetch_sub_explicit(&src->uc, RTE_SRC_IN_PROGRESS + 1, memory_order_acq_rel);
+}
+
+void rt_init_sources(struct rte_owner *, const char *name, event_list *list);
+void rt_destroy_sources(struct rte_owner *, event *);
struct ea_walk_state {
ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */
@@ -594,7 +796,7 @@ struct ea_walk_state {
eattr *ea_find(ea_list *, unsigned ea);
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
-int ea_get_int(ea_list *, unsigned ea, int def);
+uintptr_t ea_get_int(ea_list *, unsigned ea, uintptr_t def);
void ea_dump(ea_list *);
void ea_sort(ea_list *); /* Sort entries in all sub-lists */
unsigned ea_scan(ea_list *); /* How many bytes do we need for merged ea_list */
@@ -673,24 +875,39 @@ void rta_init(void);
static inline size_t rta_size(const rta *a) { return sizeof(rta) + sizeof(u32)*a->nh.labels; }
#define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */
-static inline int rta_is_cached(rta *r) { return r->aflags & RTAF_CACHED; }
-static inline rta *rta_clone(rta *r) { r->uc++; return r; }
+static inline int rta_is_cached(rta *r) { return r->cached; }
+
+static inline rta *rta_clone(rta *r) {
+ u32 uc = atomic_fetch_add_explicit(&r->uc, 1, memory_order_acq_rel);
+ ASSERT_DIE(uc > 0);
+ return r;
+}
+
void rta__free(rta *r);
-static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); }
+static inline void rta_free(rta *r) {
+ if (!r)
+ return;
+
+ u32 uc = atomic_fetch_sub_explicit(&r->uc, 1, memory_order_acq_rel);
+ if (uc == 1)
+ rta__free(r);
+}
+
rta *rta_do_cow(rta *o, linpool *lp);
static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; }
-void rta_dump(rta *);
+static inline void rta_uncache(rta *r) { r->cached = 0; r->uc = 0; }
+void rta_dump(const rta *);
void rta_dump_all(void);
-void rta_show(struct cli *, rta *);
+void rta_show(struct cli *, const rta *);
-u32 rt_get_igp_metric(rte *rt);
+u32 rt_get_igp_metric(rte *);
struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
-void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
+void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls, linpool *lp);
static inline void
-rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls)
+rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls, linpool *lp)
{
- rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls);
+ rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls, lp);
}
/*
diff --git a/nest/rt-attr.c b/nest/rt-attr.c
index c630aa95..357cd216 100644
--- a/nest/rt-attr.c
+++ b/nest/rt-attr.c
@@ -54,6 +54,7 @@
#include "lib/hash.h"
#include "lib/idm.h"
#include "lib/resource.h"
+#include "lib/rcu.h"
#include "lib/string.h"
#include <stddef.h>
@@ -61,7 +62,6 @@
const adata null_adata; /* adata of length 0 */
const char * const rta_src_names[RTS_MAX] = {
- [RTS_DUMMY] = "",
[RTS_STATIC] = "static",
[RTS_INHERIT] = "inherit",
[RTS_DEVICE] = "device",
@@ -86,7 +86,14 @@ const char * rta_dest_names[RTD_MAX] = {
[RTD_PROHIBIT] = "prohibited",
};
+DEFINE_DOMAIN(attrs);
+static DOMAIN(attrs) src_domain;
+
+#define SRC_LOCK LOCK_DOMAIN(attrs, src_domain)
+#define SRC_UNLOCK UNLOCK_DOMAIN(attrs, src_domain)
+
pool *rta_pool;
+pool *src_pool;
static slab *rta_slab_[4];
static slab *nexthop_slab_[4];
@@ -97,72 +104,151 @@ static struct idm src_ids;
/* rte source hash */
-#define RSH_KEY(n) n->proto, n->private_id
+#define RSH_KEY(n) n->private_id
#define RSH_NEXT(n) n->next
-#define RSH_EQ(p1,n1,p2,n2) p1 == p2 && n1 == n2
-#define RSH_FN(p,n) p->hash_key ^ u32_hash(n)
+#define RSH_EQ(n1,n2) n1 == n2
+#define RSH_FN(n) u32_hash(n)
#define RSH_REHASH rte_src_rehash
#define RSH_PARAMS /2, *2, 1, 1, 8, 20
-#define RSH_INIT_ORDER 6
-
-static HASH(struct rte_src) src_hash;
+#define RSH_INIT_ORDER 2
static void
rte_src_init(void)
{
- rte_src_slab = sl_new(rta_pool, sizeof(struct rte_src));
-
- idm_init(&src_ids, rta_pool, SRC_ID_INIT_SIZE);
+ src_domain = DOMAIN_NEW(attrs, "Route sources");
+ src_pool = rp_new(&root_pool, &main_birdloop, "Route sources");
+ rte_src_slab = sl_new(src_pool, sizeof(struct rte_src));
- HASH_INIT(src_hash, rta_pool, RSH_INIT_ORDER);
+ idm_init(&src_ids, src_pool, SRC_ID_INIT_SIZE);
}
-
HASH_DEFINE_REHASH_FN(RSH, struct rte_src)
-struct rte_src *
-rt_find_source(struct proto *p, u32 id)
+static struct rte_src *
+rt_find_source(struct rte_owner *p, u32 id)
{
- return HASH_FIND(src_hash, RSH, p, id);
+ return HASH_FIND(p->hash, RSH, id);
}
struct rte_src *
-rt_get_source(struct proto *p, u32 id)
+rt_get_source_o(struct rte_owner *p, u32 id)
{
+ if (p->stop)
+ bug("Stopping route owner asked for another source.");
+
struct rte_src *src = rt_find_source(p, id);
if (src)
+ {
+ UNUSED u64 uc = atomic_fetch_add_explicit(&src->uc, 1, memory_order_acq_rel);
return src;
+ }
+ SRC_LOCK;
src = sl_allocz(rte_src_slab);
- src->proto = p;
+ src->owner = p;
src->private_id = id;
src->global_id = idm_alloc(&src_ids);
- src->uc = 0;
- HASH_INSERT2(src_hash, RSH, rta_pool, src);
+ atomic_store_explicit(&src->uc, 1, memory_order_release);
+ p->uc++;
+
+ HASH_INSERT2(p->hash, RSH, src_pool, src);
+ if (config->table_debug)
+ log(L_TRACE "Allocated new rte_src for %s, ID %uL %uG, have %u sources now",
+ p->name, src->private_id, src->global_id, p->uc);
+
+ SRC_UNLOCK;
return src;
}
+static inline void
+rt_done_sources(struct rte_owner *o)
+{
+ if (o->stop->list)
+ ev_send(o->stop->list, o->stop);
+ else
+ ev_send(o->list, o->stop);
+}
+
void
-rt_prune_sources(void)
+rt_prune_sources(void *data)
{
- HASH_WALK_FILTER(src_hash, next, src, sp)
+ struct rte_owner *o = data;
+
+ HASH_WALK_FILTER(o->hash, next, src, sp)
{
- if (src->uc == 0)
+ u64 uc;
+ while ((uc = atomic_load_explicit(&src->uc, memory_order_acquire)) >> RTE_SRC_PU_SHIFT)
+ ;
+
+ if (uc == 0)
{
- HASH_DO_REMOVE(src_hash, RSH, sp);
+ o->uc--;
+
+ HASH_DO_REMOVE(o->hash, RSH, sp);
+
+ SRC_LOCK;
idm_free(&src_ids, src->global_id);
sl_free(rte_src_slab, src);
+ SRC_UNLOCK;
}
}
HASH_WALK_FILTER_END;
- HASH_MAY_RESIZE_DOWN(src_hash, RSH, rta_pool);
+ SRC_LOCK;
+ HASH_MAY_RESIZE_DOWN(o->hash, RSH, src_pool);
+
+ if (o->stop && !o->uc)
+ {
+ rfree(o->prune);
+ SRC_UNLOCK;
+
+ if (config->table_debug)
+ log(L_TRACE "All rte_src's for %s pruned, scheduling stop event", o->name);
+
+ rt_done_sources(o);
+ }
+ else
+ SRC_UNLOCK;
+}
+
+void
+rt_init_sources(struct rte_owner *o, const char *name, event_list *list)
+{
+ SRC_LOCK;
+ HASH_INIT(o->hash, src_pool, RSH_INIT_ORDER);
+ o->hash_key = random_u32();
+ o->uc = 0;
+ o->name = name;
+ o->prune = ev_new_init(src_pool, rt_prune_sources, o);
+ o->stop = NULL;
+ o->list = list;
+ SRC_UNLOCK;
}
+void
+rt_destroy_sources(struct rte_owner *o, event *done)
+{
+ o->stop = done;
+
+ if (!o->uc)
+ {
+ if (config->table_debug)
+ log(L_TRACE "Source owner %s destroy requested. All rte_src's already pruned, scheduling stop event", o->name);
+
+ SRC_LOCK;
+ rfree(o->prune);
+ SRC_UNLOCK;
+
+ rt_done_sources(o);
+ }
+ else
+ if (config->table_debug)
+ log(L_TRACE "Source owner %s destroy requested. Remaining %u rte_src's to prune.", o->name, o->uc);
+}
/*
* Multipath Next Hop
@@ -541,8 +627,8 @@ ea_walk(struct ea_walk_state *s, uint id, uint max)
* by calling ea_find() to find the attribute, extracting its value or returning
* a provided default if no such attribute is present.
*/
-int
-ea_get_int(ea_list *e, unsigned id, int def)
+uintptr_t
+ea_get_int(ea_list *e, unsigned id, uintptr_t def)
{
eattr *a = ea_find(e, id);
if (!a)
@@ -1081,21 +1167,28 @@ ea_append(ea_list *to, ea_list *what)
* rta's
*/
-static uint rta_cache_count;
-static uint rta_cache_size = 32;
-static uint rta_cache_limit;
-static uint rta_cache_mask;
-static rta **rta_hash_table;
+static DOMAIN(attrs) attrs_domain;
-static void
-rta_alloc_hash(void)
+#define RTA_LOCK LOCK_DOMAIN(attrs, attrs_domain)
+#define RTA_UNLOCK UNLOCK_DOMAIN(attrs, attrs_domain)
+
+struct rta_cache {
+ u32 count;
+ u32 size;
+ u32 limit;
+ u32 mask;
+ rta * _Atomic table[0];
+} * _Atomic rta_cache;
+// rta_aux, rta_cache = { .size = ATOMIC_VAR_INIT(32), };
+
+static struct rta_cache *
+rta_alloc_hash(u32 size)
{
- rta_hash_table = mb_allocz(rta_pool, sizeof(rta *) * rta_cache_size);
- if (rta_cache_size < 32768)
- rta_cache_limit = rta_cache_size * 2;
- else
- rta_cache_limit = ~0;
- rta_cache_mask = rta_cache_size - 1;
+ struct rta_cache *c = mb_allocz(rta_pool, sizeof(struct rta_cache) + sizeof(rta * _Atomic) * size);
+ c->size = size;
+ c->limit = (size >> 20) ? (~0U) : (size * 2);
+ c->mask = size - 1;
+ return c;
}
static inline uint
@@ -1104,13 +1197,14 @@ rta_hash(rta *a)
u64 h;
mem_hash_init(&h);
#define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f));
- MIX(src);
+#define BMIX(f) mem_hash_mix_num(&h, a->f);
MIX(hostentry);
MIX(from);
MIX(igp_metric);
- MIX(source);
- MIX(scope);
- MIX(dest);
+ BMIX(source);
+ BMIX(scope);
+ BMIX(dest);
+ MIX(pref);
#undef MIX
return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs);
@@ -1119,8 +1213,7 @@ rta_hash(rta *a)
static inline int
rta_same(rta *x, rta *y)
{
- return (x->src == y->src &&
- x->source == y->source &&
+ return (x->source == y->source &&
x->scope == y->scope &&
x->dest == y->dest &&
x->igp_metric == y->igp_metric &&
@@ -1149,34 +1242,88 @@ rta_copy(rta *o)
}
static inline void
-rta_insert(rta *r)
+rta_insert(rta *r, struct rta_cache *c)
{
- uint h = r->hash_key & rta_cache_mask;
- r->next = rta_hash_table[h];
- if (r->next)
- r->next->pprev = &r->next;
- r->pprev = &rta_hash_table[h];
- rta_hash_table[h] = r;
+ uint h = r->hash_key & c->mask;
+ rta *next = atomic_load_explicit(&c->table[h], memory_order_relaxed);
+
+ atomic_store_explicit(&r->next, next, memory_order_relaxed);
+ r->pprev = &c->table[h];
+
+ if (next)
+ next->pprev = &r->next;
+
+ /* This store MUST be the last and MUST have release order for thread-safety */
+ atomic_store_explicit(&c->table[h], r, memory_order_release);
}
static void
-rta_rehash(void)
+rta_rehash(struct rta_cache *c)
{
- uint ohs = rta_cache_size;
- uint h;
- rta *r, *n;
- rta **oht = rta_hash_table;
-
- rta_cache_size = 2*rta_cache_size;
- DBG("Rehashing rta cache from %d to %d entries.\n", ohs, rta_cache_size);
- rta_alloc_hash();
- for(h=0; h<ohs; h++)
- for(r=oht[h]; r; r=n)
+ u32 os = c->size;
+
+ struct rta_cache *nc = rta_alloc_hash(os * 2);
+ nc->count = c->count;
+
+ /* First we simply copy every chain to both new locations */
+ for (u32 h = 0; h < os; h++)
+ {
+ rta *r = atomic_load_explicit(&c->table[h], memory_order_relaxed);
+ atomic_store_explicit(&nc->table[h], r, memory_order_relaxed);
+ atomic_store_explicit(&nc->table[h + os], r, memory_order_relaxed);
+ }
+
+ /* Then we exchange the hashes; release semantics forces the previous code to be already done */
+ atomic_store_explicit(&rta_cache, nc, memory_order_release);
+
+ /* And now we pass through both chains and filter them */
+ for (u32 h = 0; h < c->size; h++)
+ {
+ rta * _Atomic * ap = &nc->table[h];
+ rta * _Atomic * bp = &nc->table[h + os];
+
+ rta *r = atomic_load_explicit(ap, memory_order_relaxed);
+ ASSERT_DIE(r == atomic_load_explicit(bp, memory_order_relaxed));
+
+ while (r)
+ {
+ if (r->hash_key & os)
{
- n = r->next;
- rta_insert(r);
+ r->pprev = bp;
+ atomic_store_explicit(bp, r, memory_order_release);
+ bp = &r->next;
}
- mb_free(oht);
+ else
+ {
+ r->pprev = ap;
+ atomic_store_explicit(ap, r, memory_order_release);
+ ap = &r->next;
+ }
+
+ r = atomic_load_explicit(&r->next, memory_order_acquire);
+ }
+
+ atomic_store_explicit(ap, NULL, memory_order_release);
+ atomic_store_explicit(bp, NULL, memory_order_release);
+ }
+
+ synchronize_rcu();
+ mb_free(c);
+}
+
+static rta *
+rta_find(rta *o, u32 h, struct rta_cache *c)
+{
+ rta *r = NULL;
+
+ for (r = atomic_load_explicit(&c->table[h & c->mask], memory_order_acquire); r; r = atomic_load_explicit(&r->next, memory_order_acquire))
+ if (r->hash_key == h && rta_same(r, o))
+ {
+ atomic_fetch_add_explicit(&r->uc, 1, memory_order_acq_rel);
+ return r;
+ }
+
+ return NULL;
}
/**
@@ -1198,43 +1345,89 @@ rta_lookup(rta *o)
rta *r;
uint h;
- ASSERT(!(o->aflags & RTAF_CACHED));
+ ASSERT(!o->cached);
if (o->eattrs)
ea_normalize(o->eattrs);
h = rta_hash(o);
- for(r=rta_hash_table[h & rta_cache_mask]; r; r=r->next)
- if (r->hash_key == h && rta_same(r, o))
- return rta_clone(r);
+ /* Lockless lookup */
+ rcu_read_lock();
+ r = rta_find(o, h, atomic_load_explicit(&rta_cache, memory_order_acquire));
+ rcu_read_unlock();
+
+ if (r)
+ return r;
+
+ RTA_LOCK;
+
+ /* Locked lookup to avoid duplicates if possible */
+ struct rta_cache *c = atomic_load_explicit(&rta_cache, memory_order_acquire);
+ r = rta_find(o, h, c);
+ if (r)
+ {
+ RTA_UNLOCK;
+ return r;
+ }
+
+ /* Store the rta */
r = rta_copy(o);
r->hash_key = h;
- r->aflags = RTAF_CACHED;
- rt_lock_source(r->src);
+ r->cached = 1;
rt_lock_hostentry(r->hostentry);
- rta_insert(r);
+ rta_insert(r, c);
- if (++rta_cache_count > rta_cache_limit)
- rta_rehash();
+ if (++c->count > c->limit)
+ rta_rehash(c);
+ RTA_UNLOCK;
return r;
}
void
rta__free(rta *a)
{
- ASSERT(rta_cache_count && (a->aflags & RTAF_CACHED));
- rta_cache_count--;
- *a->pprev = a->next;
- if (a->next)
- a->next->pprev = a->pprev;
+ ASSERT(a->cached);
+
+ RTA_LOCK;
+ struct rta_cache *c = atomic_load_explicit(&rta_cache, memory_order_acquire);
+
+ if (atomic_load_explicit(&a->uc, memory_order_acquire))
+ {
+ /* Acquired inbetween */
+ RTA_UNLOCK;
+ return;
+ }
+
+ /* Relink the forward pointer */
+ rta *next = atomic_load_explicit(&a->next, memory_order_acquire);
+ atomic_store_explicit(a->pprev, next, memory_order_release);
+
+ /* Relink the backwards pointer */
+ if (next)
+ next->pprev = a->pprev;
+
+ /* Wait until nobody knows about us */
+ synchronize_rcu();
+
+ if (atomic_load_explicit(&a->uc, memory_order_acquire))
+ {
+ /* Acquired inbetween, relink back */
+ rta_insert(a, c);
+ RTA_UNLOCK;
+ return;
+ }
+
+ /* Cleared to free the memory */
rt_unlock_hostentry(a->hostentry);
- rt_unlock_source(a->src);
if (a->nh.next)
nexthop_free(a->nh.next);
ea_free(a->eattrs);
- a->aflags = 0; /* Poison the entry */
+ a->cached = 0;
+ c->count--;
sl_free(rta_slab(a), a);
+
+ RTA_UNLOCK;
}
rta *
@@ -1248,8 +1441,7 @@ rta_do_cow(rta *o, linpool *lp)
memcpy(*nhn, nho, nexthop_size(nho));
nhn = &((*nhn)->next);
}
- r->aflags = 0;
- r->uc = 0;
+ rta_uncache(r);
return r;
}
@@ -1260,22 +1452,22 @@ rta_do_cow(rta *o, linpool *lp)
* This function takes a &rta and dumps its contents to the debug output.
*/
void
-rta_dump(rta *a)
+rta_dump(const rta *a)
{
- static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
+ static char *rts[] = { "", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
- debug("p=%s uc=%d %s %s%s h=%04x",
- a->src->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope),
+ debug("pref=%d uc=%d %s %s%s h=%04x",
+ a->pref, a->uc, rts[a->source], ip_scope_text(a->scope),
rtd[a->dest], a->hash_key);
- if (!(a->aflags & RTAF_CACHED))
+ if (!a->cached)
debug(" !CACHED");
debug(" <-%I", a->from);
if (a->dest == RTD_UNICAST)
- for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
+ for (const struct nexthop *nh = &(a->nh); nh; nh = nh->next)
{
if (ipa_nonzero(nh->gw)) debug(" ->%I", nh->gw);
if (nh->labels) debug(" L %d", nh->label[0]);
@@ -1302,19 +1494,27 @@ rta_dump_all(void)
rta *a;
uint h;
- debug("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit);
- for(h=0; h<rta_cache_size; h++)
- for(a=rta_hash_table[h]; a; a=a->next)
+ RTA_LOCK;
+
+ struct rta_cache *c = atomic_load_explicit(&rta_cache, memory_order_acquire);
+
+ debug("Route attribute cache (%d entries, rehash at %d):\n", c->count, c->limit);
+ for(h=0; h<c->size; h++)
+ for(a = atomic_load_explicit(&c->table[h], memory_order_acquire);
+ a;
+ a = atomic_load_explicit(&a->next, memory_order_acquire))
{
debug("%p ", a);
rta_dump(a);
debug("\n");
}
debug("\n");
+
+ RTA_UNLOCK;
}
void
-rta_show(struct cli *c, rta *a)
+rta_show(struct cli *c, const rta *a)
{
cli_printf(c, -1008, "\tType: %s %s", rta_src_names[a->source], ip_scope_text(a->scope));
@@ -1332,7 +1532,9 @@ rta_show(struct cli *c, rta *a)
void
rta_init(void)
{
- rta_pool = rp_new(&root_pool, "Attributes");
+ attrs_domain = DOMAIN_NEW(attrs, "Attributes");
+
+ rta_pool = rp_new(&root_pool, &main_birdloop, "Attributes");
rta_slab_[0] = sl_new(rta_pool, sizeof(rta));
rta_slab_[1] = sl_new(rta_pool, sizeof(rta) + sizeof(u32));
@@ -1344,7 +1546,7 @@ rta_init(void)
nexthop_slab_[2] = sl_new(rta_pool, sizeof(struct nexthop) + sizeof(u32)*2);
nexthop_slab_[3] = sl_new(rta_pool, sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK);
- rta_alloc_hash();
+ atomic_store_explicit(&rta_cache, rta_alloc_hash(32), memory_order_relaxed);
rte_src_init();
}
diff --git a/nest/rt-dev.c b/nest/rt-dev.c
index 61f025ce..c1251675 100644
--- a/nest/rt-dev.c
+++ b/nest/rt-dev.c
@@ -67,13 +67,11 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
/* Use iface ID as local source ID */
struct rte_src *src = rt_get_source(P, ad->iface->index);
- rte_update2(c, net, NULL, src);
+ rte_update(c, net, NULL, src);
+ rt_unlock_source(src);
}
else if (flags & IF_CHANGE_UP)
{
- rta *a;
- rte *e;
-
DBG("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip);
if (cf->check_link && !(ad->iface->flags & IF_LINK_UP))
@@ -83,17 +81,20 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
struct rte_src *src = rt_get_source(P, ad->iface->index);
rta a0 = {
- .src = src,
+ .pref = c->preference,
.source = RTS_DEVICE,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
.nh.iface = ad->iface,
};
- a = rta_lookup(&a0);
- e = rte_get_temp(a);
- e->pflags = 0;
- rte_update2(c, net, e, src);
+ rte e0 = {
+ .attrs = rta_lookup(&a0),
+ .src = src,
+ };
+
+ rte_update(c, net, &e0, src);
+ rt_unlock_source(src);
}
}
diff --git a/nest/rt-show.c b/nest/rt-show.c
index 7691878d..65b59af4 100644
--- a/nest/rt-show.c
+++ b/nest/rt-show.c
@@ -56,17 +56,17 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
if (d->verbose && !rta_is_cached(a) && a->eattrs)
ea_normalize(a->eattrs);
- get_route_info = a->src->proto->proto->get_route_info;
+ get_route_info = e->src->owner->class ? e->src->owner->class->get_route_info : NULL;
if (get_route_info)
get_route_info(e, info);
else
- bsprintf(info, " (%d)", e->pref);
+ bsprintf(info, " (%d)", a->pref);
if (d->last_table != d->tab)
rt_show_table(c, d);
cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
- a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
+ e->src->owner->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
if (a->dest == RTD_UNICAST)
for (nh = &(a->nh); nh; nh = nh->next)
@@ -95,13 +95,38 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
}
if (d->verbose)
+ {
+ cli_printf(c, -1008, "\tInternal route ID: %uL %uG %uS", e->src->private_id, e->src->global_id, e->stale_cycle);
rta_show(c, a);
+ }
+}
+
+static uint
+rte_feed_count(net *n)
+{
+ uint count = 0;
+ for (struct rte_storage *e = n->routes; e; e = e->next)
+ if (rte_is_valid(RTES_OR_NULL(e)))
+ count++;
+ return count;
+}
+
+static void
+rte_feed_obtain(net *n, rte **feed, uint count)
+{
+ uint i = 0;
+ for (struct rte_storage *e = n->routes; e; e = e->next)
+ if (rte_is_valid(RTES_OR_NULL(e)))
+ {
+ ASSERT_DIE(i < count);
+ feed[i++] = &e->rte;
+ }
+ ASSERT_DIE(i == count);
}
static void
rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{
- rte *e, *ee;
byte ia[NET_MAX_TEXT_LENGTH+1];
struct channel *ec = d->tab->export_channel;
@@ -114,9 +139,9 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
bsnprintf(ia, sizeof(ia), "%N", n->n.addr);
- for (e = n->routes; e; e = e->next)
+ for (struct rte_storage *er = n->routes; er; er = er->next)
{
- if (rte_is_filtered(e) != d->filtered)
+ if (rte_is_filtered(&er->rte) != d->filtered)
continue;
d->rt_counter++;
@@ -126,16 +151,15 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
if (pass)
continue;
- ee = e;
- rte_make_tmp_attrs(&e, c->show_pool, NULL);
+ struct rte e = er->rte;
/* Export channel is down, do not try to export routes to it */
- if (ec && (ec->export_state == ES_DOWN))
+ if (ec && !ec->out_req.hook)
goto skip;
if (d->export_mode == RSEM_EXPORTED)
{
- if (!bmap_test(&ec->export_map, ee->id))
+ if (!bmap_test(&ec->export_map, e.id))
goto skip;
// if (ec->ra_mode != RA_ANY)
@@ -144,17 +168,24 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
else if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
{
/* Special case for merged export */
- rte *rt_free;
- e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1);
pass = 1;
+ uint count = rte_feed_count(n);
+ if (!count)
+ goto skip;
- if (!e)
- { e = ee; goto skip; }
+ rte **feed = alloca(count * sizeof(rte *));
+ rte_feed_obtain(n, feed, count);
+ rte *em = rt_export_merged(ec, feed, count, c->show_pool, 1);
+
+ if (em)
+ e = *em;
+ else
+ goto skip;
}
else if (d->export_mode)
{
struct proto *ep = ec->proto;
- int ic = ep->preexport ? ep->preexport(ep, &e, c->show_pool) : 0;
+ int ic = ep->preexport ? ep->preexport(ec, &e) : 0;
if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
pass = 1;
@@ -180,24 +211,19 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
}
}
- if (d->show_protocol && (d->show_protocol != e->attrs->src->proto))
+ if (d->show_protocol && (&d->show_protocol->sources != e.src->owner))
goto skip;
if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
goto skip;
if (d->stats < 2)
- rt_show_rte(c, ia, e, d, (e->net->routes == ee));
+ rt_show_rte(c, ia, &e, d, (n->routes == er));
d->show_counter++;
ia[0] = 0;
skip:
- if (e != ee)
- {
- rte_free(e);
- e = ee;
- }
lp_flush(c->show_pool);
if (d->primary_only)
@@ -213,11 +239,13 @@ rt_show_cleanup(struct cli *c)
/* Unlink the iterator */
if (d->table_open)
- fit_get(&d->tab->table->fib, &d->fit);
+ RT_LOCKED(d->tab->table, t)
+ fit_get(&t->fib, &d->fit);
/* Unlock referenced tables */
WALK_LIST(tab, d->tables)
- rt_unlock_table(tab->table);
+ RT_LOCKED(tab->table, t)
+ rt_unlock_table(t);
}
static void
@@ -229,8 +257,6 @@ rt_show_cont(struct cli *c)
#else
unsigned max = 64;
#endif
- struct fib *fib = &d->tab->table->fib;
- struct fib_iterator *it = &d->fit;
if (d->running_on_config && (d->running_on_config != config))
{
@@ -238,9 +264,14 @@ rt_show_cont(struct cli *c)
goto done;
}
+ rtable_private *t = RT_LOCK(d->tab->table);
+
+ struct fib *fib = &t->fib;
+ struct fib_iterator *it = &d->fit;
+
if (!d->table_open)
{
- FIB_ITERATE_INIT(&d->fit, &d->tab->table->fib);
+ FIB_ITERATE_INIT(&d->fit, fib);
d->table_open = 1;
d->table_counter++;
d->kernel = rt_show_get_kernel(d);
@@ -258,6 +289,7 @@ rt_show_cont(struct cli *c)
if (!max--)
{
FIB_ITERATE_PUT(it);
+ RT_UNLOCK(d->tab->table);
return;
}
rt_show_net(c, n, d);
@@ -274,6 +306,8 @@ rt_show_cont(struct cli *c)
d->net_counter - d->net_counter_last, d->tab->table->name);
}
+ RT_UNLOCK(d->tab->table);
+
d->kernel = NULL;
d->table_open = 0;
d->tab = NODE_NEXT(d->tab);
@@ -322,7 +356,7 @@ rt_show_get_default_tables(struct rt_show_data *d)
{
WALK_LIST(c, d->export_protocol->channels)
{
- if (c->export_state == ES_DOWN)
+ if (!c->out_req.hook)
continue;
tab = rt_show_add_table(d, c->table);
@@ -339,7 +373,7 @@ rt_show_get_default_tables(struct rt_show_data *d)
}
for (int i=1; i<NET_MAX; i++)
- if (config->def_tables[i])
+ if (config->def_tables[i] && config->def_tables[i]->table)
rt_show_add_table(d, config->def_tables[i]->table);
}
@@ -405,7 +439,8 @@ rt_show(struct rt_show_data *d)
if (!d->addr)
{
WALK_LIST(tab, d->tables)
- rt_lock_table(tab->table);
+ RT_LOCKED(tab->table, t)
+ rt_lock_table(t);
/* There is at least one table */
d->tab = HEAD(d->tables);
@@ -420,13 +455,17 @@ rt_show(struct rt_show_data *d)
d->tab = tab;
d->kernel = rt_show_get_kernel(d);
+ RT_LOCK(tab->table);
+
if (d->show_for)
- n = net_route(tab->table, d->addr);
+ n = net_route(RT_PRIV(tab->table), d->addr);
else
- n = net_find(tab->table, d->addr);
+ n = net_find(RT_PRIV(tab->table), d->addr);
if (n)
rt_show_net(this_cli, n, d);
+
+ RT_UNLOCK(tab->table);
}
if (d->rt_counter)
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 390b3277..ada54396 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -45,28 +45,84 @@
#include "lib/string.h"
#include "lib/alloca.h"
-#ifdef CONFIG_BGP
-#include "proto/bgp/bgp.h"
-#endif
+#include <stdatomic.h>
pool *rt_table_pool;
-static slab *rte_slab;
-static linpool *rte_update_pool;
-
list routing_tables;
-static void rt_free_hostcache(rtable *tab);
-static void rt_notify_hostcache(rtable *tab, net *net);
-static void rt_update_hostcache(rtable *tab);
-static void rt_next_hop_update(rtable *tab);
-static inline void rt_prune_table(rtable *tab);
-static inline void rt_schedule_notify(rtable *tab);
+/* Data structures for export journal */
+#define RT_PENDING_EXPORT_ITEMS (page_size - sizeof(struct rt_export_block)) / sizeof(struct rt_pending_export)
+
+struct rt_export_block {
+ node n;
+ _Atomic u32 end;
+ _Atomic _Bool not_last;
+ struct rt_pending_export export[];
+};
+
+static void rt_free_hostcache(rtable_private *tab);
+static void rt_notify_hostcache(rtable_private *tab, net *net);
+static void rt_update_hostcache(void *tab);
+static void rt_next_hop_update(void *tab);
+static inline void rt_prune_table(void *tab);
+static inline void rt_schedule_notify(rtable_private *tab);
+static void rt_feed_channel(void *);
+
+static inline void rt_export_used(rtable_private *tab);
+static void rt_export_cleanup(void *tab);
+
+const char *rt_import_state_name_array[TIS_MAX] = {
+ [TIS_DOWN] = "DOWN",
+ [TIS_UP] = "UP",
+ [TIS_STOP] = "STOP",
+ [TIS_FLUSHING] = "FLUSHING",
+ [TIS_WAITING] = "WAITING",
+ [TIS_CLEARED] = "CLEARED",
+};
+
+const char *rt_export_state_name_array[TES_MAX] = {
+ [TES_DOWN] = "DOWN",
+ [TES_HUNGRY] = "HUNGRY",
+ [TES_FEEDING] = "FEEDING",
+ [TES_READY] = "READY",
+ [TES_STOP] = "STOP"
+};
+
+const char *rt_import_state_name(u8 state)
+{
+ if (state >= TIS_MAX)
+ return "!! INVALID !!";
+ else
+ return rt_import_state_name_array[state];
+}
+
+const char *rt_export_state_name(u8 state)
+{
+ if (state >= TES_MAX)
+ return "!! INVALID !!";
+ else
+ return rt_export_state_name_array[state];
+}
+
+struct event_cork rt_cork;
+
+static inline void
+rte_update_lock(struct channel *c)
+{
+ c->rte_update_nest_cnt++;
+}
+static inline void
+rte_update_unlock(struct channel *c)
+{
+ if (!--c->rte_update_nest_cnt)
+ lp_flush(c->rte_update_pool);
+}
/* Like fib_route(), but skips empty net entries */
static inline void *
-net_route_ip4(rtable *t, net_addr_ip4 *n)
+net_route_ip4(rtable_private *t, net_addr_ip4 *n)
{
net *r;
@@ -80,7 +136,7 @@ net_route_ip4(rtable *t, net_addr_ip4 *n)
}
static inline void *
-net_route_ip6(rtable *t, net_addr_ip6 *n)
+net_route_ip6(rtable_private *t, net_addr_ip6 *n)
{
net *r;
@@ -94,7 +150,7 @@ net_route_ip6(rtable *t, net_addr_ip6 *n)
}
static inline void *
-net_route_ip6_sadr(rtable *t, net_addr_ip6_sadr *n)
+net_route_ip6_sadr(rtable_private *t, net_addr_ip6_sadr *n)
{
struct fib_node *fn;
@@ -133,7 +189,7 @@ net_route_ip6_sadr(rtable *t, net_addr_ip6_sadr *n)
}
void *
-net_route(rtable *tab, const net_addr *n)
+net_route(rtable_private *tab, const net_addr *n)
{
ASSERT(tab->addr_type == n->type);
@@ -162,12 +218,15 @@ net_route(rtable *tab, const net_addr *n)
static int
-net_roa_check_ip4(rtable *tab, const net_addr_ip4 *px, u32 asn)
+net_roa_check_ip4(rtable *t, const net_addr_ip4 *px, u32 asn)
{
struct net_addr_roa4 n = NET_ADDR_ROA4(px->prefix, px->pxlen, 0, 0);
struct fib_node *fn;
int anything = 0;
+ RT_LOCK(t);
+ rtable_private *tab = RT_PRIV(t);
+
while (1)
{
for (fn = fib_get_chain(&tab->fib, (net_addr *) &n); fn; fn = fn->next)
@@ -175,11 +234,14 @@ net_roa_check_ip4(rtable *tab, const net_addr_ip4 *px, u32 asn)
net_addr_roa4 *roa = (void *) fn->addr;
net *r = fib_node_to_user(&tab->fib, fn);
- if (net_equal_prefix_roa4(roa, &n) && rte_is_valid(r->routes))
+ if (net_equal_prefix_roa4(roa, &n) && r->routes && rte_is_valid(&r->routes->rte))
{
anything = 1;
if (asn && (roa->asn == asn) && (roa->max_pxlen >= px->pxlen))
+ {
+ RT_UNLOCK(tab);
return ROA_VALID;
+ }
}
}
@@ -190,16 +252,20 @@ net_roa_check_ip4(rtable *tab, const net_addr_ip4 *px, u32 asn)
ip4_clrbit(&n.prefix, n.pxlen);
}
+ RT_UNLOCK(tab);
return anything ? ROA_INVALID : ROA_UNKNOWN;
}
static int
-net_roa_check_ip6(rtable *tab, const net_addr_ip6 *px, u32 asn)
+net_roa_check_ip6(rtable *t, const net_addr_ip6 *px, u32 asn)
{
struct net_addr_roa6 n = NET_ADDR_ROA6(px->prefix, px->pxlen, 0, 0);
struct fib_node *fn;
int anything = 0;
+ RT_LOCK(t);
+ rtable_private *tab = RT_PRIV(t);
+
while (1)
{
for (fn = fib_get_chain(&tab->fib, (net_addr *) &n); fn; fn = fn->next)
@@ -207,11 +273,14 @@ net_roa_check_ip6(rtable *tab, const net_addr_ip6 *px, u32 asn)
net_addr_roa6 *roa = (void *) fn->addr;
net *r = fib_node_to_user(&tab->fib, fn);
- if (net_equal_prefix_roa6(roa, &n) && rte_is_valid(r->routes))
+ if (net_equal_prefix_roa6(roa, &n) && r->routes && rte_is_valid(&r->routes->rte))
{
anything = 1;
if (asn && (roa->asn == asn) && (roa->max_pxlen >= px->pxlen))
+ {
+ RT_UNLOCK(tab);
return ROA_VALID;
+ }
}
}
@@ -222,6 +291,7 @@ net_roa_check_ip6(rtable *tab, const net_addr_ip6 *px, u32 asn)
ip6_clrbit(&n.prefix, n.pxlen);
}
+ RT_UNLOCK(tab);
return anything ? ROA_INVALID : ROA_UNKNOWN;
}
@@ -256,253 +326,55 @@ net_roa_check(rtable *tab, const net_addr *n, u32 asn)
* @net: network node
* @src: route source
*
- * The rte_find() function returns a route for destination @net
- * which is from route source @src.
+ * The rte_find() function returns a pointer to a route for destination @net
+ * which is from route source @src. List end pointer is returned if no route is found.
*/
-rte *
+static struct rte_storage **
rte_find(net *net, struct rte_src *src)
{
- rte *e = net->routes;
-
- while (e && e->attrs->src != src)
- e = e->next;
- return e;
-}
+ struct rte_storage **e = &net->routes;
-/**
- * rte_get_temp - get a temporary &rte
- * @a: attributes to assign to the new route (a &rta; in case it's
- * un-cached, rte_update() will create a cached copy automatically)
- *
- * Create a temporary &rte and bind it with the attributes @a.
- * Also set route preference to the default preference set for
- * the protocol.
- */
-rte *
-rte_get_temp(rta *a)
-{
- rte *e = sl_alloc(rte_slab);
+ while ((*e) && (*e)->rte.src != src)
+ e = &(*e)->next;
- e->attrs = a;
- e->id = 0;
- e->flags = 0;
- e->pref = 0;
return e;
}
-rte *
-rte_do_cow(rte *r)
-{
- rte *e = sl_alloc(rte_slab);
-
- memcpy(e, r, sizeof(rte));
- e->attrs = rta_clone(r->attrs);
- e->flags = 0;
- return e;
-}
-/**
- * rte_cow_rta - get a private writable copy of &rte with writable &rta
- * @r: a route entry to be copied
- * @lp: a linpool from which to allocate &rta
- *
- * rte_cow_rta() takes a &rte and prepares it and associated &rta for
- * modification. There are three possibilities: First, both &rte and &rta are
- * private copies, in that case they are returned unchanged. Second, &rte is
- * private copy, but &rta is cached, in that case &rta is duplicated using
- * rta_do_cow(). Third, both &rte is shared and &rta is cached, in that case
- * both structures are duplicated by rte_do_cow() and rta_do_cow().
- *
- * Note that in the second case, cached &rta loses one reference, while private
- * copy created by rta_do_cow() is a shallow copy sharing indirect data (eattrs,
- * nexthops, ...) with it. To work properly, original shared &rta should have
- * another reference during the life of created private copy.
- *
- * Result: a pointer to the new writable &rte with writable &rta.
- */
-rte *
-rte_cow_rta(rte *r, linpool *lp)
+struct rte_storage *
+rte_store(const rte *r, net *net, rtable_private *tab)
{
- if (!rta_is_cached(r->attrs))
- return r;
-
- r = rte_cow(r);
- rta *a = rta_do_cow(r->attrs, lp);
- rta_free(r->attrs);
- r->attrs = a;
- return r;
-}
+ struct rte_storage *e = sl_alloc(tab->rte_slab);
+ e->rte = *r;
+ e->rte.net = net->n.addr;
-/**
- * rte_init_tmp_attrs - initialize temporary ea_list for route
- * @r: route entry to be modified
- * @lp: linpool from which to allocate attributes
- * @max: maximum number of added temporary attribus
- *
- * This function is supposed to be called from make_tmp_attrs() and
- * store_tmp_attrs() hooks before rte_make_tmp_attr() / rte_store_tmp_attr()
- * functions. It allocates &ea_list with length for @max items for temporary
- * attributes and puts it on top of eattrs stack.
- */
-void
-rte_init_tmp_attrs(rte *r, linpool *lp, uint max)
-{
- struct ea_list *e = lp_alloc(lp, sizeof(struct ea_list) + max * sizeof(eattr));
-
- e->next = r->attrs->eattrs;
- e->flags = EALF_SORTED | EALF_TEMP;
- e->count = 0;
+ rt_lock_source(e->rte.src);
- r->attrs->eattrs = e;
-}
+ if (e->rte.attrs->cached)
+ e->rte.attrs = rta_clone(e->rte.attrs);
+ else
+ e->rte.attrs = rta_lookup(e->rte.attrs);
-/**
- * rte_make_tmp_attr - make temporary eattr from private route fields
- * @r: route entry to be modified
- * @id: attribute ID
- * @type: attribute type
- * @val: attribute value (u32 or adata ptr)
- *
- * This function is supposed to be called from make_tmp_attrs() hook for
- * each temporary attribute, after temporary &ea_list was initialized by
- * rte_init_tmp_attrs(). It checks whether temporary attribute is supposed to
- * be defined (based on route pflags) and if so then it fills &eattr field in
- * preallocated temporary &ea_list on top of route @r eattrs stack.
- *
- * Note that it may require free &eattr in temporary &ea_list, so it must not be
- * called more times than @max argument of rte_init_tmp_attrs().
- */
-void
-rte_make_tmp_attr(rte *r, uint id, uint type, uintptr_t val)
-{
- if (r->pflags & EA_ID_FLAG(id))
- {
- ea_list *e = r->attrs->eattrs;
- eattr *a = &e->attrs[e->count++];
- a->id = id;
- a->type = type;
- a->flags = 0;
-
- if (type & EAF_EMBEDDED)
- a->u.data = (u32) val;
- else
- a->u.ptr = (struct adata *) val;
- }
+ return e;
}
/**
- * rte_store_tmp_attr - store temporary eattr to private route fields
- * @r: route entry to be modified
- * @id: attribute ID
- *
- * This function is supposed to be called from store_tmp_attrs() hook for
- * each temporary attribute, after temporary &ea_list was initialized by
- * rte_init_tmp_attrs(). It checks whether temporary attribute is defined in
- * route @r eattrs stack, updates route pflags accordingly, undefines it by
- * filling &eattr field in preallocated temporary &ea_list on top of the eattrs
- * stack, and returns the value. Caller is supposed to store it in the
- * appropriate private field.
+ * rte_free - delete a &rte
+ * @e: &struct rte_storage to be deleted
+ * @tab: the table which the rte belongs to
*
- * Note that it may require free &eattr in temporary &ea_list, so it must not be
- * called more times than @max argument of rte_init_tmp_attrs()
+ * rte_free() deletes the given &rte from the routing table it's linked to.
*/
-uintptr_t
-rte_store_tmp_attr(rte *r, uint id)
-{
- ea_list *e = r->attrs->eattrs;
- eattr *a = ea_find(e->next, id);
-
- if (a)
- {
- e->attrs[e->count++] = (struct eattr) { .id = id, .type = EAF_TYPE_UNDEF };
- r->pflags |= EA_ID_FLAG(id);
- return (a->type & EAF_EMBEDDED) ? a->u.data : (uintptr_t) a->u.ptr;
- }
- else
- {
- r->pflags &= ~EA_ID_FLAG(id);
- return 0;
- }
-}
-/**
- * rte_make_tmp_attrs - prepare route by adding all relevant temporary route attributes
- * @r: route entry to be modified (may be replaced if COW)
- * @lp: linpool from which to allocate attributes
- * @old_attrs: temporary ref to old &rta (may be NULL)
- *
- * This function expands privately stored protocol-dependent route attributes
- * to a uniform &eattr / &ea_list representation. It is essentially a wrapper
- * around protocol make_tmp_attrs() hook, which does some additional work like
- * ensuring that route @r is writable.
- *
- * The route @r may be read-only (with %REF_COW flag), in that case rw copy is
- * obtained by rte_cow() and @r is replaced. If @rte is originally rw, it may be
- * directly modified (and it is never copied).
- *
- * If the @old_attrs ptr is supplied, the function obtains another reference of
- * old cached &rta, that is necessary in some cases (see rte_cow_rta() for
- * details). It is freed by rte_store_tmp_attrs(), or manually by rta_free().
- *
- * Generally, if caller ensures that @r is read-only (e.g. in route export) then
- * it may ignore @old_attrs (and set it to NULL), but must handle replacement of
- * @r. If caller ensures that @r is writable (e.g. in route import) then it may
- * ignore replacement of @r, but it must handle @old_attrs.
- */
void
-rte_make_tmp_attrs(rte **r, linpool *lp, rta **old_attrs)
+rte_free(struct rte_storage *e, rtable_private *tab)
{
- void (*make_tmp_attrs)(rte *r, linpool *lp);
- make_tmp_attrs = (*r)->attrs->src->proto->make_tmp_attrs;
-
- if (!make_tmp_attrs)
- return;
-
- /* We may need to keep ref to old attributes, will be freed in rte_store_tmp_attrs() */
- if (old_attrs)
- *old_attrs = rta_is_cached((*r)->attrs) ? rta_clone((*r)->attrs) : NULL;
-
- *r = rte_cow_rta(*r, lp);
- make_tmp_attrs(*r, lp);
+ rt_unlock_source(e->rte.src);
+ rta_free(e->rte.attrs);
+ sl_free(tab->rte_slab, e);
}
-/**
- * rte_store_tmp_attrs - store temporary route attributes back to private route fields
- * @r: route entry to be modified
- * @lp: linpool from which to allocate attributes
- * @old_attrs: temporary ref to old &rta
- *
- * This function stores temporary route attributes that were expanded by
- * rte_make_tmp_attrs() back to private route fields and also undefines them.
- * It is essentially a wrapper around protocol store_tmp_attrs() hook, which
- * does some additional work like shortcut if there is no change and cleanup
- * of @old_attrs reference obtained by rte_make_tmp_attrs().
- */
-static void
-rte_store_tmp_attrs(rte *r, linpool *lp, rta *old_attrs)
-{
- void (*store_tmp_attrs)(rte *rt, linpool *lp);
- store_tmp_attrs = r->attrs->src->proto->store_tmp_attrs;
-
- if (!store_tmp_attrs)
- return;
-
- ASSERT(!rta_is_cached(r->attrs));
-
- /* If there is no new ea_list, we just skip the temporary ea_list */
- ea_list *ea = r->attrs->eattrs;
- if (ea && (ea->flags & EALF_TEMP))
- r->attrs->eattrs = ea->next;
- else
- store_tmp_attrs(r, lp);
-
- /* Free ref we got in rte_make_tmp_attrs(), have to do rta_lookup() first */
- r->attrs = rta_lookup(r->attrs);
- rta_free(old_attrs);
-}
-
-
static int /* Actually better or at least as good as */
rte_better(rte *new, rte *old)
{
@@ -513,20 +385,20 @@ rte_better(rte *new, rte *old)
if (!rte_is_valid(new))
return 0;
- if (new->pref > old->pref)
+ if (new->attrs->pref > old->attrs->pref)
return 1;
- if (new->pref < old->pref)
+ if (new->attrs->pref < old->attrs->pref)
return 0;
- if (new->attrs->src->proto->proto != old->attrs->src->proto->proto)
+ if (new->src->owner->class != old->src->owner->class)
{
/*
* If the user has configured protocol preferences, so that two different protocols
* have the same preference, try to break the tie by comparing addresses. Not too
* useful, but keeps the ordering of routes unambiguous.
*/
- return new->attrs->src->proto->proto > old->attrs->src->proto->proto;
+ return new->src->owner->class > old->src->owner->class;
}
- if (better = new->attrs->src->proto->rte_better)
+ if (better = new->src->owner->class->rte_better)
return better(new, old);
return 0;
}
@@ -539,268 +411,300 @@ rte_mergable(rte *pri, rte *sec)
if (!rte_is_valid(pri) || !rte_is_valid(sec))
return 0;
- if (pri->pref != sec->pref)
+ if (pri->attrs->pref != sec->attrs->pref)
return 0;
- if (pri->attrs->src->proto->proto != sec->attrs->src->proto->proto)
+ if (pri->src->owner->class != sec->src->owner->class)
return 0;
- if (mergable = pri->attrs->src->proto->rte_mergable)
+ if (mergable = pri->src->owner->class->rte_mergable)
return mergable(pri, sec);
return 0;
}
static void
-rte_trace(struct channel *c, rte *e, int dir, char *msg)
+rte_trace(const char *name, const rte *e, int dir, const char *msg)
{
- log(L_TRACE "%s.%s %c %s %N %s",
- c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr,
- rta_dest_name(e->attrs->dest));
+ log(L_TRACE "%s %c %s %N src %uL %uG %uS id %u %s%s",
+ name, dir, msg, e->net,
+ e->src->private_id, e->src->global_id, e->stale_cycle, e->id,
+ rta_dest_name(e->attrs->dest),
+ rte_is_filtered(e) ? " (filtered)" : "");
}
static inline void
-rte_trace_in(uint flag, struct channel *c, rte *e, char *msg)
+channel_rte_trace_in(uint flag, struct channel *c, const rte *e, const char *msg)
{
if ((c->debug & flag) || (c->proto->debug & flag))
- rte_trace(c, e, '>', msg);
+ rte_trace(c->in_req.name, e, '>', msg);
}
static inline void
-rte_trace_out(uint flag, struct channel *c, rte *e, char *msg)
+channel_rte_trace_out(uint flag, struct channel *c, const rte *e, const char *msg)
{
if ((c->debug & flag) || (c->proto->debug & flag))
- rte_trace(c, e, '<', msg);
+ rte_trace(c->out_req.name, e, '<', msg);
+}
+
+static inline void
+rt_rte_trace_in(uint flag, struct rt_import_request *req, const rte *e, const char *msg)
+{
+ if (req->trace_routes & flag)
+ rte_trace(req->name, e, '>', msg);
+}
+
+#if 0
+// seems to be unused at all
+static inline void
+rt_rte_trace_out(uint flag, struct rt_export_request *req, const rte *e, const char *msg)
+{
+ if (req->trace_routes & flag)
+ rte_trace(req->name, e, '<', msg);
+}
+#endif
+
+static uint
+rte_feed_count(net *n)
+{
+ uint count = 0;
+ for (struct rte_storage *e = n->routes; e; e = e->next)
+ if (rte_is_valid(RTES_OR_NULL(e)))
+ count++;
+ return count;
+}
+
+static void
+rte_feed_obtain(net *n, struct rte **feed, uint count)
+{
+ uint i = 0;
+ for (struct rte_storage *e = n->routes; e; e = e->next)
+ if (rte_is_valid(RTES_OR_NULL(e)))
+ {
+ ASSERT_DIE(i < count);
+ feed[i++] = &e->rte;
+ }
+ ASSERT_DIE(i == count);
}
static rte *
-export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int silent)
+export_filter_(struct channel *c, rte *rt, linpool *pool, int silent)
{
struct proto *p = c->proto;
const struct filter *filter = c->out_filter;
- struct proto_stats *stats = &c->stats;
- rte *rt;
- int v;
+ struct channel_export_stats *stats = &c->export_stats;
- rt = rt0;
- *rt_free = NULL;
+ /* Do nothing if we have already rejected the route */
+ if (silent && bmap_test(&c->export_reject_map, rt->id))
+ goto reject_noset;
- v = p->preexport ? p->preexport(p, &rt, pool) : 0;
+ int v = p->preexport ? p->preexport(c, rt) : 0;
if (v < 0)
{
if (silent)
- goto reject;
+ goto reject_noset;
- stats->exp_updates_rejected++;
+ stats->updates_rejected++;
if (v == RIC_REJECT)
- rte_trace_out(D_FILTERS, c, rt, "rejected by protocol");
+ channel_rte_trace_out(D_FILTERS, c, rt, "rejected by protocol");
goto reject;
+
}
if (v > 0)
{
if (!silent)
- rte_trace_out(D_FILTERS, c, rt, "forced accept by protocol");
+ channel_rte_trace_out(D_FILTERS, c, rt, "forced accept by protocol");
goto accept;
}
- rte_make_tmp_attrs(&rt, pool, NULL);
-
v = filter && ((filter == FILTER_REJECT) ||
- (f_run(filter, &rt, pool,
+ (f_run(filter, rt, pool,
(silent ? FF_SILENT : 0)) > F_ACCEPT));
if (v)
{
if (silent)
goto reject;
- stats->exp_updates_filtered++;
- rte_trace_out(D_FILTERS, c, rt, "filtered out");
+ stats->updates_filtered++;
+ channel_rte_trace_out(D_FILTERS, c, rt, "filtered out");
goto reject;
}
-#ifdef CONFIG_PIPE
- /* Pipes need rte with stored tmpattrs, remaining protocols need expanded tmpattrs */
- if (p->proto == &proto_pipe)
- rte_store_tmp_attrs(rt, pool, NULL);
-#endif
-
accept:
- if (rt != rt0)
- *rt_free = rt;
+ /* We have accepted the route */
+ bmap_clear(&c->export_reject_map, rt->id);
return rt;
reject:
+ /* We have rejected the route by filter */
+ bmap_set(&c->export_reject_map, rt->id);
+
+reject_noset:
/* Discard temporary rte */
- if (rt != rt0)
- rte_free(rt);
return NULL;
}
static inline rte *
-export_filter(struct channel *c, rte *rt0, rte **rt_free, int silent)
+export_filter(struct channel *c, rte *rt, int silent)
{
- return export_filter_(c, rt0, rt_free, rte_update_pool, silent);
+ return export_filter_(c, rt, c->rte_update_pool, silent);
}
+void do_rt_notify_direct(struct channel *c, const net_addr *net, rte *new, const rte *old);
+
static void
-do_rt_notify(struct channel *c, net *net, rte *new, rte *old, int refeed)
+do_rt_notify(struct channel *c, const net_addr *net, rte *new, const rte *old)
{
- struct proto *p = c->proto;
- struct proto_stats *stats = &c->stats;
+ struct channel_export_stats *stats = &c->export_stats;
- if (refeed && new)
+ if (c->refeeding && new)
c->refeed_count++;
- /* Apply export limit */
- struct channel_limit *l = &c->out_limit;
- if (l->action && !old && new)
- {
- if (stats->exp_routes >= l->limit)
- channel_notify_limit(c, l, PLD_OUT, stats->exp_routes);
-
- if (l->state == PLS_BLOCKED)
+ if (!old && new)
+ if (CHANNEL_LIMIT_PUSH(c, OUT))
{
- stats->exp_updates_rejected++;
- rte_trace_out(D_FILTERS, c, new, "rejected [limit]");
+ stats->updates_rejected++;
+ channel_rte_trace_out(D_FILTERS, c, new, "rejected [limit]");
return;
}
- }
- /* Apply export table */
- if (c->out_table && !rte_update_out(c, net->n.addr, new, old, refeed))
- return;
-
- if (new)
- stats->exp_updates_accepted++;
- else
- stats->exp_withdraws_accepted++;
+ if (!new && old)
+ CHANNEL_LIMIT_POP(c, OUT);
+ /* Store route export state */
if (old)
- {
bmap_clear(&c->export_map, old->id);
- stats->exp_routes--;
- }
if (new)
- {
bmap_set(&c->export_map, new->id);
- stats->exp_routes++;
- }
+
+ /* Apply export table */
+ if (c->out_table)
+ rte_import(&c->out_table->push, net, new, old ? old->src : new->src);
+ else
+ do_rt_notify_direct(c, net, new, old);
+}
+
+void
+do_rt_notify_direct(struct channel *c, const net_addr *net, rte *new, const rte *old)
+{
+ struct proto *p = c->proto;
+ struct channel_export_stats *stats = &c->export_stats;
+
+ if (new)
+ stats->updates_accepted++;
+ else
+ stats->withdraws_accepted++;
if (p->debug & D_ROUTES)
{
if (new && old)
- rte_trace_out(D_ROUTES, c, new, "replaced");
+ channel_rte_trace_out(D_ROUTES, c, new, "replaced");
else if (new)
- rte_trace_out(D_ROUTES, c, new, "added");
+ channel_rte_trace_out(D_ROUTES, c, new, "added");
else if (old)
- rte_trace_out(D_ROUTES, c, old, "removed");
+ channel_rte_trace_out(D_ROUTES, c, old, "removed");
}
p->rt_notify(p, c, net, new, old);
}
static void
-rt_notify_basic(struct channel *c, net *net, rte *new, rte *old, int refeed)
+rt_notify_basic(struct channel *c, const net_addr *net, rte *new, rte *old)
{
- // struct proto *p = c->proto;
- rte *new_free = NULL;
-
- if (new)
- c->stats.exp_updates_received++;
- else
- c->stats.exp_withdraws_received++;
-
if (new)
- new = export_filter(c, new, &new_free, 0);
+ new = export_filter(c, new, 0);
if (old && !bmap_test(&c->export_map, old->id))
old = NULL;
+ if (old && (old->sender == c->in_req.hook))
+ bug("bad-behaved pipe");
+
if (!new && !old)
return;
- do_rt_notify(c, net, new, old, refeed);
-
- /* Discard temporary rte */
- if (new_free)
- rte_free(new_free);
+ do_rt_notify(c, net, new, old);
}
static void
-rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_changed, int refeed)
+channel_rpe_mark_seen(struct rt_export_request *req, struct rt_pending_export *rpe)
{
- // struct proto *p = c->proto;
- rte *new_best = NULL;
- rte *old_best = NULL;
- rte *new_free = NULL;
- int new_first = 0;
-
- /*
- * We assume that there are no changes in net route order except (added)
- * new_changed and (removed) old_changed. Therefore, the function is not
- * compatible with deterministic_med (where nontrivial reordering can happen
- * as a result of a route change) and with recomputation of recursive routes
- * due to next hop update (where many routes can be changed in one step).
- *
- * Note that we need this assumption just for optimizations, we could just
- * run full new_best recomputation otherwise.
- *
- * There are three cases:
- * feed or old_best is old_changed -> we need to recompute new_best
- * old_best is before new_changed -> new_best is old_best, ignore
- * old_best is after new_changed -> try new_changed, otherwise old_best
- */
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
- if (net->routes)
- c->stats.exp_updates_received++;
- else
- c->stats.exp_withdraws_received++;
+ rpe_mark_seen(req->hook, rpe);
+ if (rpe->old)
+ bmap_clear(&c->export_reject_map, rpe->old->rte.id);
+}
- /* Find old_best - either old_changed, or route for net->routes */
- if (old_changed && bmap_test(&c->export_map, old_changed->id))
- old_best = old_changed;
- else
+void
+rt_notify_accepted(struct rt_export_request *req, const net_addr *n, struct rt_pending_export *rpe,
+ struct rte **feed, uint count)
+{
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
+
+ rte_update_lock(c);
+
+ rte nb0, *new_best = NULL;
+ const rte *old_best = NULL;
+
+ for (uint i = 0; i < count; i++)
{
- for (rte *r = net->routes; rte_is_valid(r); r = r->next)
+ if (!rte_is_valid(feed[i]))
+ continue;
+
+ /* Has been already rejected, won't bother with it */
+ if (!c->refeeding && bmap_test(&c->export_reject_map, feed[i]->id))
+ continue;
+
+ /* Previously exported */
+ if (!old_best && bmap_test(&c->export_map, feed[i]->id))
{
- if (bmap_test(&c->export_map, r->id))
+ /* is still best */
+ if (!new_best)
{
- old_best = r;
- break;
+ DBG("rt_notify_accepted: idempotent\n");
+ goto done;
}
- /* Note if new_changed found before old_best */
- if (r == new_changed)
- new_first = 1;
+ /* is superseded */
+ old_best = feed[i];
+ break;
}
- }
- /* Find new_best */
- if ((new_changed == old_changed) || (old_best == old_changed))
- {
- /* Feed or old_best changed -> find first accepted by filters */
- for (rte *r = net->routes; rte_is_valid(r); r = r->next)
- if (new_best = export_filter(c, r, &new_free, 0))
- break;
+ /* Have no new best route yet */
+ if (!new_best)
+ {
+ /* Try this route not seen before */
+ nb0 = *feed[i];
+ new_best = export_filter(c, &nb0, 0);
+ DBG("rt_notify_accepted: checking route id %u: %s\n", feed[i]->id, new_best ? "ok" : "no");
+ }
}
- else
+
+done:
+ /* Check obsolete routes for previously exported */
+ while (rpe)
{
- /* Other cases -> either new_changed, or old_best (and nothing changed) */
- if (new_first && (new_changed = export_filter(c, new_changed, &new_free, 0)))
- new_best = new_changed;
- else
- return;
+ channel_rpe_mark_seen(req, rpe);
+ if (rpe->old)
+ {
+ if (bmap_test(&c->export_map, rpe->old->rte.id))
+ {
+ ASSERT_DIE(old_best == NULL);
+ old_best = &rpe->old->rte;
+ }
+ }
+ rpe = rpe_next(rpe, NULL);
}
- if (!new_best && !old_best)
- return;
-
- do_rt_notify(c, net, new_best, old_best, refeed);
+ /* Nothing to export */
+ if (new_best || old_best)
+ do_rt_notify(c, n, new_best, old_best);
+ else
+ DBG("rt_notify_accepted: nothing to export\n");
- /* Discard temporary rte */
- if (new_free)
- rte_free(new_free);
+ rte_update_unlock(c);
}
@@ -811,38 +715,45 @@ nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
}
rte *
-rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent)
+rt_export_merged(struct channel *c, struct rte **feed, uint count, linpool *pool, int silent)
{
+ _Thread_local static rte rloc;
+
// struct proto *p = c->proto;
struct nexthop *nhs = NULL;
- rte *best0, *best, *rt0, *rt, *tmp;
-
- best0 = net->routes;
- *rt_free = NULL;
+ rte *best0 = feed[0];
+ rte *best = NULL;
if (!rte_is_valid(best0))
return NULL;
- best = export_filter_(c, best0, rt_free, pool, silent);
+ /* Already rejected, no need to re-run the filter */
+ if (!c->refeeding && bmap_test(&c->export_reject_map, best0->id))
+ return NULL;
+
+ rloc = *best0;
+ best = export_filter_(c, &rloc, pool, silent);
+
+ if (!best)
+ /* Best route doesn't pass the filter */
+ return NULL;
- if (!best || !rte_is_reachable(best))
+ if (!rte_is_reachable(best))
+ /* Unreachable routes can't be merged */
return best;
- for (rt0 = best0->next; rt0; rt0 = rt0->next)
+ for (uint i = 1; i < count; i++)
{
- if (!rte_mergable(best0, rt0))
+ if (!rte_mergable(best0, feed[i]))
continue;
- rt = export_filter_(c, rt0, &tmp, pool, 1);
+ rte tmp0 = *feed[i];
+ rte *tmp = export_filter_(c, &tmp0, pool, 1);
- if (!rt)
+ if (!tmp || !rte_is_reachable(tmp))
continue;
- if (rte_is_reachable(rt))
- nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
-
- if (tmp)
- rte_free(tmp);
+ nhs = nexthop_merge_rta(nhs, tmp->attrs, pool, c->merge_limit);
}
if (nhs)
@@ -851,66 +762,208 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int
if (nhs->next)
{
- best = rte_cow_rta(best, pool);
+ best->attrs = rta_cow(best->attrs, pool);
nexthop_link(best->attrs, nhs);
}
}
- if (best != best0)
- *rt_free = best;
-
return best;
}
-
-static void
-rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed,
- rte *new_best, rte *old_best, int refeed)
+void
+rt_notify_merged(struct rt_export_request *req, const net_addr *n, struct rt_pending_export *rpe,
+ struct rte **feed, uint count)
{
- // struct proto *p = c->proto;
- rte *new_free = NULL;
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
- /* We assume that all rte arguments are either NULL or rte_is_valid() */
-
- /* This check should be done by the caller */
- if (!new_best && !old_best)
- return;
+ rte_update_lock(c);
+ // struct proto *p = c->proto;
+#if 0 /* TODO: Find whether this check is possible when processing multiple changes at once. */
/* Check whether the change is relevant to the merged route */
if ((new_best == old_best) &&
(new_changed != old_changed) &&
!rte_mergable(new_best, new_changed) &&
!rte_mergable(old_best, old_changed))
return;
+#endif
- if (new_best)
- c->stats.exp_updates_received++;
- else
- c->stats.exp_withdraws_received++;
+ rte *old_best = NULL;
+ /* Find old best route */
+ for (uint i = 0; i < count; i++)
+ if (bmap_test(&c->export_map, feed[i]->id))
+ {
+ old_best = feed[i];
+ break;
+ }
+
+ /* Check obsolete routes for previously exported */
+ while (rpe)
+ {
+ channel_rpe_mark_seen(req, rpe);
+ if (rpe->old)
+ {
+ if (bmap_test(&c->export_map, rpe->old->rte.id))
+ {
+ ASSERT_DIE(old_best == NULL);
+ old_best = &rpe->old->rte;
+ }
+ }
+ rpe = rpe_next(rpe, NULL);
+ }
/* Prepare new merged route */
- if (new_best)
- new_best = rt_export_merged(c, net, &new_free, rte_update_pool, 0);
+ rte *new_merged = count ? rt_export_merged(c, feed, count, c->rte_update_pool, 0) : NULL;
- /* Check old merged route */
- if (old_best && !bmap_test(&c->export_map, old_best->id))
- old_best = NULL;
+ if (new_merged || old_best)
+ do_rt_notify(c, n, new_merged, old_best);
- if (!new_best && !old_best)
- return;
+ rte_update_unlock(c);
+}
- do_rt_notify(c, net, new_best, old_best, refeed);
+void
+rt_notify_optimal(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
+ rte_update_lock(c);
+ rte *old = RTES_OR_NULL(rpe->old_best);
+ struct rte_storage *new_best = rpe->new_best;
- /* Discard temporary rte */
- if (new_free)
- rte_free(new_free);
+ while (rpe)
+ {
+ channel_rpe_mark_seen(req, rpe);
+ new_best = rpe->new_best;
+ rpe = rpe_next(rpe, NULL);
+ }
+
+ if (&new_best->rte != old)
+ {
+ rte n0, *new = RTES_CLONE(new_best, &n0);
+ rt_notify_basic(c, net, new, old);
+ }
+
+ rte_update_unlock(c);
+}
+
+void
+rt_notify_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe)
+{
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
+ rte_update_lock(c);
+ struct rte_src *src = rpe->new ? rpe->new->rte.src : rpe->old->rte.src;
+ rte *old = RTES_OR_NULL(rpe->old);
+ struct rte_storage *new_any = rpe->new;
+
+ while (rpe)
+ {
+ channel_rpe_mark_seen(req, rpe);
+ new_any = rpe->new;
+ rpe = rpe_next(rpe, src);
+ }
+
+ if (&new_any->rte != old)
+ {
+ rte n0, *new = RTES_CLONE(new_any, &n0);
+ rt_notify_basic(c, net, new, old);
+ }
+
+ rte_update_unlock(c);
+}
+
+void
+rt_feed_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count)
+{
+ struct channel *c = SKIP_BACK(struct channel, out_req, req);
+ rte_update_lock(c);
+
+ for (uint i=0; i<count; i++)
+ {
+ rte n0 = *feed[i];
+ rt_notify_basic(c, net, &n0, NULL);
+ }
+
+ rte_update_unlock(c);
+}
+
+void
+rpe_mark_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe)
+{
+ bmap_set(&hook->seq_map, rpe->seq);
+}
+
+struct rt_pending_export *
+rpe_next(struct rt_pending_export *rpe, struct rte_src *src)
+{
+ struct rt_pending_export *next = atomic_load_explicit(&rpe->next, memory_order_acquire);
+
+ if (!next)
+ return NULL;
+
+ if (!src)
+ return next;
+
+ while (rpe = next)
+ if (src == (rpe->new ? rpe->new->rte.src : rpe->old->rte.src))
+ return rpe;
+ else
+ next = atomic_load_explicit(&rpe->next, memory_order_acquire);
+
+ return NULL;
}
+static struct rt_pending_export * rt_next_export_fast(struct rt_pending_export *last);
+static void
+rte_export(struct rt_export_hook *hook, struct rt_pending_export *rpe)
+{
+ if (bmap_test(&hook->seq_map, rpe->seq))
+ goto seen;
+
+ const net_addr *n = rpe->new_best ? rpe->new_best->rte.net : rpe->old_best->rte.net;
+
+ if (rpe->new)
+ hook->stats.updates_received++;
+ else
+ hook->stats.withdraws_received++;
+
+ if (hook->req->export_one)
+ hook->req->export_one(hook->req, n, rpe);
+ else if (hook->req->export_bulk)
+ {
+ RT_LOCK(hook->table);
+ net *net = SKIP_BACK(struct network, n.addr, (net_addr (*)[0]) n);
+ uint count = rte_feed_count(net);
+ rte **feed = NULL;
+ if (count)
+ {
+ feed = alloca(count * sizeof(rte *));
+ rte_feed_obtain(net, feed, count);
+ }
+ RT_UNLOCK(hook->table);
+ hook->req->export_bulk(hook->req, n, rpe, feed, count);
+ }
+ else
+ bug("Export request must always provide an export method");
+
+seen:
+ /* Get the next export if exists */
+ hook->rpe_next = rt_next_export_fast(rpe);
+
+ /* The last block may be available to free */
+ if (PAGE_HEAD(hook->rpe_next) != PAGE_HEAD(rpe))
+ {
+ RT_LOCK(hook->table);
+ rt_export_used(RT_PRIV(hook->table));
+ RT_UNLOCK(hook->table);
+ }
+
+ /* Releasing this export for cleanup routine */
+ DBG("store hook=%p last_export=%p seq=%lu\n", hook, rpe, rpe->seq);
+ atomic_store_explicit(&hook->last_export, rpe, memory_order_release);
+}
/**
* rte_announce - announce a routing table change
* @tab: table the route has been added to
- * @type: type of route announcement (RA_UNDEF or RA_ANY)
* @net: network in question
* @new: the new or changed route
* @old: the previous route replaced by the new one
@@ -926,13 +979,6 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
* and @new_best and @old_best describes best routes. Other routes are not
* affected, but in sorted table the order of other routes might change.
*
- * Second, There is a bulk change of multiple routes in @net, with shared best
- * route selection. In such case separate route changes are described using
- * @type of %RA_ANY, with @new and @old specifying the changed route, while
- * @new_best and @old_best are NULL. After that, another notification is done
- * where @new_best and @old_best are filled (may be the same), but @new and @old
- * are NULL.
- *
* The function announces the change to all associated channels. For each
* channel, an appropriate preprocessing is done according to channel &ra_mode.
* For example, %RA_OPTIMAL channels receive just changes of best routes.
@@ -947,130 +993,304 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
* done outside of scope of rte_announce().
*/
static void
-rte_announce(rtable *tab, uint type, net *net, rte *new, rte *old,
- rte *new_best, rte *old_best)
+rte_announce(rtable_private *tab, net *net, struct rte_storage *new, struct rte_storage *old,
+ struct rte_storage *new_best, struct rte_storage *old_best)
{
- if (!rte_is_valid(new))
- new = NULL;
-
- if (!rte_is_valid(old))
- old = NULL;
-
- if (!rte_is_valid(new_best))
+ if (!new_best || !rte_is_valid(&new_best->rte))
new_best = NULL;
- if (!rte_is_valid(old_best))
+ if (!old_best || !rte_is_valid(&old_best->rte))
old_best = NULL;
- if (!new && !old && !new_best && !old_best)
+ if (!new || !rte_is_valid(&new->rte))
+ new = NULL;
+
+ if (old && !rte_is_valid(&old->rte))
+ {
+ /* Filtered old route isn't announced, should be freed immediately. */
+ rte_free(old, tab);
+ old = NULL;
+ }
+
+ if ((new == old) && (new_best == old_best))
return;
if (new_best != old_best)
{
if (new_best)
- new_best->sender->stats.pref_routes++;
+ new_best->rte.sender->stats.pref++;
if (old_best)
- old_best->sender->stats.pref_routes--;
+ old_best->rte.sender->stats.pref--;
if (tab->hostcache)
rt_notify_hostcache(tab, net);
}
+ if (EMPTY_LIST(tab->exports) && EMPTY_LIST(tab->pending_exports))
+ {
+ /* No export hook and no pending exports to cleanup. We may free the route immediately. */
+ if (!old)
+ return;
+
+ hmap_clear(&tab->id_map, old->rte.id);
+ rte_free(old, tab);
+ return;
+ }
+
+ /* Get the pending export structure */
+ struct rt_export_block *rpeb = NULL, *rpebsnl = NULL;
+ u32 end = 0;
+
+ if (!EMPTY_LIST(tab->pending_exports))
+ {
+ rpeb = TAIL(tab->pending_exports);
+ end = atomic_load_explicit(&rpeb->end, memory_order_relaxed);
+ if (end >= RT_PENDING_EXPORT_ITEMS)
+ {
+ ASSERT_DIE(end == RT_PENDING_EXPORT_ITEMS);
+ rpebsnl = rpeb;
+
+ rpeb = NULL;
+ end = 0;
+ }
+ }
+
+ if (!rpeb)
+ {
+ rpeb = alloc_page();
+ *rpeb = (struct rt_export_block) {};
+ add_tail(&tab->pending_exports, &rpeb->n);
+ }
+
+ /* Fill the pending export */
+ struct rt_pending_export *rpe = &rpeb->export[rpeb->end];
+ *rpe = (struct rt_pending_export) {
+ .new = new,
+ .new_best = new_best,
+ .old = old,
+ .old_best = old_best,
+ .seq = tab->next_export_seq++,
+ };
+
+ DBG("rte_announce: table=%s net=%N new=%p from %p old=%p from %p new_best=%p old_best=%p seq=%lu\n", tab->name, net->n.addr, new, new ? new->sender : NULL, old, old ? old->sender : NULL, new_best, old_best, rpe->seq);
+
+ ASSERT_DIE(atomic_fetch_add_explicit(&rpeb->end, 1, memory_order_release) == end);
+
+ if (rpebsnl)
+ {
+ _Bool f = 0;
+ ASSERT_DIE(atomic_compare_exchange_strong_explicit(&rpebsnl->not_last, &f, 1,
+ memory_order_release, memory_order_relaxed));
+ }
+
+ /* Append to the same-network squasher list */
+ if (net->last)
+ {
+ struct rt_pending_export *rpenull = NULL;
+ ASSERT_DIE(atomic_compare_exchange_strong_explicit(
+ &net->last->next, &rpenull, rpe,
+ memory_order_relaxed,
+ memory_order_relaxed));
+
+ }
+
+ net->last = rpe;
+
+ if (!net->first)
+ net->first = rpe;
+
+ if (tab->first_export == NULL)
+ tab->first_export = rpe;
+
+ if (!EMPTY_LIST(tab->exports) &&
+ (tab->first_export->seq + tab->config->cork_limit <= tab->next_export_seq) &&
+ !tab->cork_active)
+ {
+ if (config->table_debug)
+ log(L_TRACE "%s: cork activated", tab->name);
+
+ ev_cork(&rt_cork);
+ tab->cork_active = 1;
+ }
+}
+
+static struct rt_pending_export *
+rt_next_export_fast(struct rt_pending_export *last)
+{
+ /* Get the whole export block and find our position in there. */
+ struct rt_export_block *rpeb = PAGE_HEAD(last);
+ u32 pos = (last - &rpeb->export[0]);
+ u32 end = atomic_load_explicit(&rpeb->end, memory_order_acquire);
+ ASSERT_DIE(pos < end);
+
+ /* Next is in the same block. */
+ if (++pos < end)
+ return &rpeb->export[pos];
+
+ /* There is another block. */
+ if (atomic_load_explicit(&rpeb->not_last, memory_order_acquire))
+ {
+ /* This is OK to do non-atomically because of the not_last flag. */
+ rpeb = NODE_NEXT(rpeb);
+ return &rpeb->export[0];
+ }
+
+ /* There is nothing more. */
+ return NULL;
+}
+
+static struct rt_pending_export *
+rt_next_export(struct rt_export_hook *hook, rtable_private *tab)
+{
+ /* As the table is locked, it is safe to reload the last export pointer */
+ struct rt_pending_export *last = atomic_load_explicit(&hook->last_export, memory_order_acquire);
+
+ /* It is still valid, let's reuse it */
+ if (last)
+ return rt_next_export_fast(last);
+
+ /* No, therefore we must process the table's first pending export */
+ else
+ return tab->first_export;
+}
+
+static inline void
+rt_send_export_event(struct rt_export_hook *hook)
+{
+ ev_send(hook->req->list, hook->event);
+}
+
+static void
+rt_announce_exports(void *data)
+{
+ rtable_private *tab = data;
+ ASSERT_DIE(birdloop_inside(tab->loop));
+
rt_schedule_notify(tab);
- struct channel *c; node *n;
- WALK_LIST2(c, n, tab->channels, table_node)
+ struct rt_export_hook *c; node *n;
+ WALK_LIST2(c, n, tab->exports, n)
{
- if (c->export_state == ES_DOWN)
+ if (atomic_load_explicit(&c->export_state, memory_order_acquire) != TES_READY)
continue;
- if (type && (type != c->ra_mode))
- continue;
+ rt_send_export_event(c);
+ }
+}
- switch (c->ra_mode)
+static void
+rt_import_announce_exports(void *data)
+{
+ struct rt_import_hook *hook = data;
+ RT_LOCKED(hook->table, tab)
+ {
+ if (hook->import_state == TIS_CLEARED)
{
- case RA_OPTIMAL:
- if (new_best != old_best)
- rt_notify_basic(c, net, new_best, old_best, 0);
- break;
+ rfree(hook->export_announce_event);
- case RA_ANY:
- if (new != old)
- rt_notify_basic(c, net, new, old, 0);
- break;
+ ev_send(hook->stopped->list, hook->stopped);
+ rem_node(&hook->n);
+ mb_free(hook);
+ rt_unlock_table(tab);
+ }
+ else
+ ev_send_loop(tab->loop, tab->announce_event);
+ }
+}
- case RA_ACCEPTED:
- rt_notify_accepted(c, net, new, old, 0);
- break;
+static struct rt_pending_export *
+rt_last_export(rtable_private *tab)
+{
+ struct rt_pending_export *rpe = NULL;
- case RA_MERGED:
- rt_notify_merged(c, net, new, old, new_best, old_best, 0);
- break;
+ if (!EMPTY_LIST(tab->pending_exports))
+ {
+ /* We'll continue processing exports from this export on */
+ struct rt_export_block *reb = TAIL(tab->pending_exports);
+ ASSERT_DIE(reb->end);
+ rpe = &reb->export[reb->end - 1];
+ }
+
+ return rpe;
+}
+
+#define RT_EXPORT_BULK 1024
+
+static void
+rt_export_hook(void *_data)
+{
+ struct rt_export_hook *c = _data;
+
+ ASSERT_DIE(atomic_load_explicit(&c->export_state, memory_order_relaxed) == TES_READY);
+
+ if (!c->rpe_next)
+ {
+ RT_LOCK(c->table);
+ c->rpe_next = rt_next_export(c, RT_PRIV(c->table));
+
+ if (!c->rpe_next)
+ {
+ rt_export_used(RT_PRIV(c->table));
+ RT_UNLOCK(c->table);
+ return;
}
+
+ RT_UNLOCK(c->table);
+ }
+
+ /* Process the export */
+ for (uint i=0; i<RT_EXPORT_BULK; i++)
+ {
+ rte_export(c, c->rpe_next);
+
+ if (!c->rpe_next)
+ break;
}
+
+ rt_send_export_event(c);
}
+
static inline int
-rte_validate(rte *e)
+rte_validate(struct channel *ch, rte *e)
{
int c;
- net *n = e->net;
+ const net_addr *n = e->net;
- if (!net_validate(n->n.addr))
+ if (!net_validate(n))
{
log(L_WARN "Ignoring bogus prefix %N received via %s",
- n->n.addr, e->sender->proto->name);
+ n, ch->proto->name);
return 0;
}
/* FIXME: better handling different nettypes */
- c = !net_is_flow(n->n.addr) ?
- net_classify(n->n.addr): (IADDR_HOST | SCOPE_UNIVERSE);
+ c = !net_is_flow(n) ?
+ net_classify(n): (IADDR_HOST | SCOPE_UNIVERSE);
if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
{
log(L_WARN "Ignoring bogus route %N received via %s",
- n->n.addr, e->sender->proto->name);
+ n, ch->proto->name);
return 0;
}
- if (net_type_match(n->n.addr, NB_DEST) == !e->attrs->dest)
+ if (net_type_match(n, NB_DEST) == !e->attrs->dest)
{
log(L_WARN "Ignoring route %N with invalid dest %d received via %s",
- n->n.addr, e->attrs->dest, e->sender->proto->name);
+ n, e->attrs->dest, ch->proto->name);
return 0;
}
if ((e->attrs->dest == RTD_UNICAST) && !nexthop_is_sorted(&(e->attrs->nh)))
{
log(L_WARN "Ignoring unsorted multipath route %N received via %s",
- n->n.addr, e->sender->proto->name);
+ n, ch->proto->name);
return 0;
}
return 1;
}
-/**
- * rte_free - delete a &rte
- * @e: &rte to be deleted
- *
- * rte_free() deletes the given &rte from the routing table it's linked to.
- */
-void
-rte_free(rte *e)
-{
- if (rta_is_cached(e->attrs))
- rta_free(e->attrs);
- sl_free(rte_slab, e);
-}
-
-static inline void
-rte_free_quick(rte *e)
-{
- rta_free(e->attrs);
- sl_free(rte_slab, e);
-}
-
static int
rte_same(rte *x, rte *y)
{
@@ -1078,175 +1298,105 @@ rte_same(rte *x, rte *y)
return
x->attrs == y->attrs &&
x->pflags == y->pflags &&
- x->pref == y->pref &&
- (!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)) &&
+ x->src == y->src &&
rte_is_filtered(x) == rte_is_filtered(y);
}
static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
static void
-rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
+rte_recalculate(rtable_private *table, struct rt_import_hook *c, net *net, rte *new, struct rte_src *src)
{
- struct proto *p = c->proto;
- struct rtable *table = c->table;
- struct proto_stats *stats = &c->stats;
- static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS;
- rte *before_old = NULL;
- rte *old_best = net->routes;
+ struct rt_import_request *req = c->req;
+ struct rt_import_stats *stats = &c->stats;
+ struct rte_storage *old_best_stored = net->routes, *old_stored = NULL;
+ rte *old_best = old_best_stored ? &old_best_stored->rte : NULL;
rte *old = NULL;
- rte **k;
- k = &net->routes; /* Find and remove original route from the same protocol */
- while (old = *k)
+ /* Set the stale cycle unless already set */
+ if (new && !(new->flags & REF_USE_STALE))
+ new->stale_cycle = c->stale_set;
+
+ /* Find and remove original route from the same protocol */
+ struct rte_storage **before_old = rte_find(net, src);
+
+ if (*before_old)
{
- if (old->attrs->src == src)
+ old = &(old_stored = (*before_old))->rte;
+
+ /* If there is the same route in the routing table but from
+ * a different sender, then there are two paths from the
+ * source protocol to this routing table through transparent
+ * pipes, which is not allowed.
+ * We log that and ignore the route. */
+ if (old->sender != c)
{
- /* If there is the same route in the routing table but from
- * a different sender, then there are two paths from the
- * source protocol to this routing table through transparent
- * pipes, which is not allowed.
- *
- * We log that and ignore the route. If it is withdraw, we
- * ignore it completely (there might be 'spurious withdraws',
- * see FIXME in do_rte_announce())
- */
- if (old->sender->proto != p)
- {
- if (new)
- {
- log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %N to table %s",
- net->n.addr, table->name);
- rte_free_quick(new);
- }
- return;
- }
+ if (!old->generation && !new->generation)
+ bug("Two protocols claim to author a route with the same rte_src in table %s: %N %s/%u:%u",
+ c->table->name, net->n.addr, old->src->owner->name, old->src->private_id, old->src->global_id);
+
+ log_rl(&table->rl_pipe, L_ERR "Route source collision in table %s: %N %s/%u:%u",
+ c->table->name, net->n.addr, old->src->owner->name, old->src->private_id, old->src->global_id);
+ }
if (new && rte_same(old, new))
{
/* No changes, ignore the new route and refresh the old one */
-
- old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
+ old->stale_cycle = new->stale_cycle;
if (!rte_is_filtered(new))
{
- stats->imp_updates_ignored++;
- rte_trace_in(D_ROUTES, c, new, "ignored");
+ stats->updates_ignored++;
+ rt_rte_trace_in(D_ROUTES, req, new, "ignored");
}
-
- rte_free_quick(new);
return;
- }
- *k = old->next;
- table->rt_count--;
- break;
- }
- k = &old->next;
- before_old = old;
- }
-
- /* Save the last accessed position */
- rte **pos = k;
+ }
- if (!old)
- before_old = NULL;
+ *before_old = (*before_old)->next;
+ table->rt_count--;
+ }
if (!old && !new)
{
- stats->imp_withdraws_ignored++;
+ stats->withdraws_ignored++;
return;
}
+ if (req->preimport)
+ new = req->preimport(req, new, old);
+
int new_ok = rte_is_ok(new);
int old_ok = rte_is_ok(old);
- struct channel_limit *l = &c->rx_limit;
- if (l->action && !old && new && !c->in_table)
- {
- u32 all_routes = stats->imp_routes + stats->filt_routes;
-
- if (all_routes >= l->limit)
- channel_notify_limit(c, l, PLD_RX, all_routes);
-
- if (l->state == PLS_BLOCKED)
- {
- /* In receive limit the situation is simple, old is NULL so
- we just free new and exit like nothing happened */
-
- stats->imp_updates_ignored++;
- rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
- rte_free_quick(new);
- return;
- }
- }
-
- l = &c->in_limit;
- if (l->action && !old_ok && new_ok)
- {
- if (stats->imp_routes >= l->limit)
- channel_notify_limit(c, l, PLD_IN, stats->imp_routes);
-
- if (l->state == PLS_BLOCKED)
- {
- /* In import limit the situation is more complicated. We
- shouldn't just drop the route, we should handle it like
- it was filtered. We also have to continue the route
- processing if old or new is non-NULL, but we should exit
- if both are NULL as this case is probably assumed to be
- already handled. */
-
- stats->imp_updates_ignored++;
- rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
-
- if (c->in_keep_filtered)
- new->flags |= REF_FILTERED;
- else
- { rte_free_quick(new); new = NULL; }
-
- /* Note that old && !new could be possible when
- c->in_keep_filtered changed in the recent past. */
-
- if (!old && !new)
- return;
-
- new_ok = 0;
- goto skip_stats1;
- }
- }
-
if (new_ok)
- stats->imp_updates_accepted++;
+ stats->updates_accepted++;
else if (old_ok)
- stats->imp_withdraws_accepted++;
+ stats->withdraws_accepted++;
else
- stats->imp_withdraws_ignored++;
+ stats->withdraws_ignored++;
if (old_ok || new_ok)
table->last_rt_change = current_time();
- skip_stats1:
-
- if (new)
- rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
- if (old)
- rte_is_filtered(old) ? stats->filt_routes-- : stats->imp_routes--;
+ struct rte_storage *new_stored = new ? rte_store(new, net, table) : NULL;
if (table->config->sorted)
{
/* If routes are sorted, just insert new route to appropriate position */
- if (new)
+ if (new_stored)
{
- if (before_old && !rte_better(new, before_old))
- k = &before_old->next;
+ struct rte_storage **k;
+ if ((before_old != &net->routes) && !rte_better(new, &SKIP_BACK(struct rte_storage, next, before_old)->rte))
+ k = before_old;
else
k = &net->routes;
for (; *k; k=&(*k)->next)
- if (rte_better(new, *k))
+ if (rte_better(new, &(*k)->rte))
break;
- new->next = *k;
- *k = new;
+ new_stored->next = *k;
+ *k = new_stored;
table->rt_count++;
}
@@ -1256,16 +1406,17 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
/* If routes are not sorted, find the best route and move it on
the first position. There are several optimized cases. */
- if (src->proto->rte_recalculate && src->proto->rte_recalculate(table, net, new, old, old_best))
+ if (src->owner->rte_recalculate &&
+ src->owner->rte_recalculate(table, net, new_stored ? &new_stored->rte : NULL, old, old_best))
goto do_recalculate;
- if (new && rte_better(new, old_best))
+ if (new_stored && rte_better(&new_stored->rte, old_best))
{
/* The first case - the new route is cleary optimal,
we link it at the first position */
- new->next = net->routes;
- net->routes = new;
+ new_stored->next = net->routes;
+ net->routes = new_stored;
table->rt_count++;
}
@@ -1279,10 +1430,10 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
do_recalculate:
/* Add the new route to the list */
- if (new)
+ if (new_stored)
{
- new->next = *pos;
- *pos = new;
+ new_stored->next = *before_old;
+ *before_old = new_stored;
table->rt_count++;
}
@@ -1290,335 +1441,390 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
/* Find a new optimal route (if there is any) */
if (net->routes)
{
- rte **bp = &net->routes;
- for (k=&(*bp)->next; *k; k=&(*k)->next)
- if (rte_better(*k, *bp))
+ struct rte_storage **bp = &net->routes;
+ for (struct rte_storage **k=&(*bp)->next; *k; k=&(*k)->next)
+ if (rte_better(&(*k)->rte, &(*bp)->rte))
bp = k;
/* And relink it */
- rte *best = *bp;
+ struct rte_storage *best = *bp;
*bp = best->next;
best->next = net->routes;
net->routes = best;
}
}
- else if (new)
+ else if (new_stored)
{
/* The third case - the new route is not better than the old
best route (therefore old_best != NULL) and the old best
route was not removed (therefore old_best == net->routes).
We just link the new route to the old/last position. */
- new->next = *pos;
- *pos = new;
+ new_stored->next = *before_old;
+ *before_old = new_stored;
table->rt_count++;
}
/* The fourth (empty) case - suboptimal route was removed, nothing to do */
}
- if (new)
+ if (new_stored)
{
- new->lastmod = current_time();
-
- if (!old)
- {
- new->id = hmap_first_zero(&table->id_map);
- hmap_set(&table->id_map, new->id);
- }
- else
- new->id = old->id;
+ new_stored->rte.lastmod = current_time();
+ new_stored->rte.id = hmap_first_zero(&table->id_map);
+ hmap_set(&table->id_map, new_stored->rte.id);
}
+ _Bool nb = (new_stored == net->routes);
+ _Bool ob = (old_best == old);
+
/* Log the route change */
- if ((c->debug & D_ROUTES) || (p->debug & D_ROUTES))
+ if (new_ok && old_ok)
{
- if (new_ok)
- rte_trace(c, new, '>', new == net->routes ? "added [best]" : "added");
- else if (old_ok)
- {
- if (old != old_best)
- rte_trace(c, old, '>', "removed");
- else if (rte_is_ok(net->routes))
- rte_trace(c, old, '>', "removed [replaced]");
- else
- rte_trace(c, old, '>', "removed [sole]");
- }
+ const char *best_indicator[2][2] = { { "updated", "updated [-best]" }, { "updated [+best]", "updated [best]" } };
+ rt_rte_trace_in(D_ROUTES, req, &new_stored->rte, best_indicator[nb][ob]);
}
+ else if (new_ok)
+ rt_rte_trace_in(D_ROUTES, req, &new_stored->rte,
+ (!net->routes->next || !rte_is_ok(&net->routes->next->rte)) ? "added [sole]" :
+ nb ? "added [best]" : "added");
+ else if (old_ok)
+ rt_rte_trace_in(D_ROUTES, req, old,
+ (!net->routes || !rte_is_ok(&net->routes->rte)) ? "removed [sole]" :
+ ob ? "removed [best]" : "removed");
/* Propagate the route change */
- rte_announce(table, RA_UNDEF, net, new, old, net->routes, old_best);
+ rte_announce(table, net, new_stored, old_stored,
+ net->routes, old_best_stored);
+
+ ev_send(req->list, c->export_announce_event);
if (!net->routes &&
(table->gc_counter++ >= table->config->gc_max_ops) &&
(table->gc_time + table->config->gc_min_time <= current_time()))
rt_schedule_prune(table);
+#if 0
+ /* Enable and reimplement these callbacks if anybody wants to use them */
if (old_ok && p->rte_remove)
p->rte_remove(net, old);
if (new_ok && p->rte_insert)
- p->rte_insert(net, new);
-
- if (old)
- {
- if (!new)
- hmap_clear(&table->id_map, old->id);
+ p->rte_insert(net, &new_stored->rte);
+#endif
- rte_free_quick(old);
- }
}
-static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */
-
-static inline void
-rte_update_lock(void)
-{
- rte_update_nest_cnt++;
-}
-
-static inline void
-rte_update_unlock(void)
+rte *
+channel_preimport(struct rt_import_request *req, rte *new, rte *old)
{
- if (!--rte_update_nest_cnt)
- lp_flush(rte_update_pool);
-}
+ struct channel *c = SKIP_BACK(struct channel, in_req, req);
-static inline void
-rte_hide_dummy_routes(net *net, rte **dummy)
-{
- if (net->routes && net->routes->attrs->source == RTS_DUMMY)
+ if (!c->in_table)
{
- *dummy = net->routes;
- net->routes = (*dummy)->next;
+ if (new && !old)
+ if (CHANNEL_LIMIT_PUSH(c, RX))
+ return NULL;
+
+ if (!new && old)
+ CHANNEL_LIMIT_POP(c, RX);
}
+
+ int new_in = new && !rte_is_filtered(new);
+ int old_in = old && !rte_is_filtered(old);
+
+ if (new_in && !old_in)
+ if (CHANNEL_LIMIT_PUSH(c, IN))
+ if (c->in_keep_filtered)
+ {
+ new->flags |= REF_FILTERED;
+ return new;
+ }
+ else
+ return NULL;
+
+ if (!new_in && old_in)
+ CHANNEL_LIMIT_POP(c, IN);
+
+ return new;
}
-static inline void
-rte_unhide_dummy_routes(net *net, rte **dummy)
+rte *
+channel_in_preimport(struct rt_import_request *req, rte *new, rte *old)
{
- if (*dummy)
- {
- (*dummy)->next = net->routes;
- net->routes = *dummy;
- }
+ struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, push, req);
+
+ if (new && !old)
+ if (CHANNEL_LIMIT_PUSH(cat->c, RX))
+ return NULL;
+
+ if (!new && old)
+ CHANNEL_LIMIT_POP(cat->c, RX);
+
+ return new;
}
-/**
- * rte_update - enter a new update to a routing table
- * @table: table to be updated
- * @c: channel doing the update
- * @net: network node
- * @p: protocol submitting the update
- * @src: protocol originating the update
- * @new: a &rte representing the new route or %NULL for route removal.
- *
- * This function is called by the routing protocols whenever they discover
- * a new route or wish to update/remove an existing route. The right announcement
- * sequence is to build route attributes first (either un-cached with @aflags set
- * to zero or a cached one using rta_lookup(); in this case please note that
- * you need to increase the use count of the attributes yourself by calling
- * rta_clone()), call rte_get_temp() to obtain a temporary &rte, fill in all
- * the appropriate data and finally submit the new &rte by calling rte_update().
- *
- * @src specifies the protocol that originally created the route and the meaning
- * of protocol-dependent data of @new. If @new is not %NULL, @src have to be the
- * same value as @new->attrs->proto. @p specifies the protocol that called
- * rte_update(). In most cases it is the same protocol as @src. rte_update()
- * stores @p in @new->sender;
- *
- * When rte_update() gets any route, it automatically validates it (checks,
- * whether the network and next hop address are valid IP addresses and also
- * whether a normal routing protocol doesn't try to smuggle a host or link
- * scope route to the table), converts all protocol dependent attributes stored
- * in the &rte to temporary extended attributes, consults import filters of the
- * protocol to see if the route should be accepted and/or its attributes modified,
- * stores the temporary attributes back to the &rte.
- *
- * Now, having a "public" version of the route, we
- * automatically find any old route defined by the protocol @src
- * for network @n, replace it by the new one (or removing it if @new is %NULL),
- * recalculate the optimal route for this destination and finally broadcast
- * the change (if any) to all routing protocols by calling rte_announce().
- *
- * All memory used for attribute lists and other temporary allocations is taken
- * from a special linear pool @rte_update_pool and freed when rte_update()
- * finishes.
- */
+void rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
void
-rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
+rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
{
- // struct proto *p = c->proto;
- struct proto_stats *stats = &c->stats;
- const struct filter *filter = c->in_filter;
- rte *dummy = NULL;
- net *nn;
+ if (!c->in_req.hook)
+ return;
ASSERT(c->channel_state == CS_UP);
- rte_update_lock();
+ if (c->in_table)
+ rte_import(&c->in_table->push, n, new, src);
+ else
+ rte_update_direct(c, n, new, src);
+}
+
+void
+rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
+{
+ const struct filter *filter = c->in_filter;
+ struct channel_import_stats *stats = &c->import_stats;
+
+ rte_update_lock(c);
if (new)
{
- /* Create a temporary table node */
- nn = alloca(sizeof(net) + n->length);
- memset(nn, 0, sizeof(net) + n->length);
- net_copy(nn->n.addr, n);
-
- new->net = nn;
- new->sender = c;
+ new->net = n;
- if (!new->pref)
- new->pref = c->preference;
+ int fr;
- stats->imp_updates_received++;
- if (!rte_validate(new))
+ stats->updates_received++;
+ if (!rte_validate(c, new))
{
- rte_trace_in(D_FILTERS, c, new, "invalid");
- stats->imp_updates_invalid++;
- goto drop;
+ channel_rte_trace_in(D_FILTERS, c, new, "invalid");
+ stats->updates_invalid++;
+ new = NULL;
}
-
- if (filter == FILTER_REJECT)
+ else if ((filter == FILTER_REJECT) ||
+ ((fr = f_run(filter, new, c->rte_update_pool, 0)) > F_ACCEPT))
{
- stats->imp_updates_filtered++;
- rte_trace_in(D_FILTERS, c, new, "filtered out");
-
- if (! c->in_keep_filtered)
- goto drop;
+ stats->updates_filtered++;
+ channel_rte_trace_in(D_FILTERS, c, new, "filtered out");
- /* new is a private copy, i could modify it */
- new->flags |= REF_FILTERED;
+ if (c->in_keep_filtered)
+ new->flags |= REF_FILTERED;
+ else
+ new = NULL;
}
- else if (filter)
- {
- rta *old_attrs = NULL;
- rte_make_tmp_attrs(&new, rte_update_pool, &old_attrs);
+ }
+ else
+ stats->withdraws_received++;
- int fr = f_run(filter, &new, rte_update_pool, 0);
- if (fr > F_ACCEPT)
- {
- stats->imp_updates_filtered++;
- rte_trace_in(D_FILTERS, c, new, "filtered out");
+ rte_import(&c->in_req, n, new, src);
- if (! c->in_keep_filtered)
- {
- rta_free(old_attrs);
- goto drop;
- }
+ rte_update_unlock(c);
+}
- new->flags |= REF_FILTERED;
- }
+void
+rte_import(struct rt_import_request *req, const net_addr *n, rte *new, struct rte_src *src)
+{
+ struct rt_import_hook *hook = req->hook;
+ if (!hook)
+ return;
- rte_store_tmp_attrs(new, rte_update_pool, old_attrs);
- }
- if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
- new->attrs = rta_lookup(new->attrs);
- new->flags |= REF_COW;
+ RT_LOCK(hook->table);
+ rtable_private *tab = RT_PRIV(hook->table);
+ net *nn;
+ if (new)
+ {
/* Use the actual struct network, not the dummy one */
- nn = net_get(c->table, n);
- new->net = nn;
+ nn = net_get(tab, n);
+ new->net = nn->n.addr;
+ new->sender = hook;
}
- else
+ else if (!(nn = net_find(tab, n)))
{
- stats->imp_withdraws_received++;
-
- if (!(nn = net_find(c->table, n)) || !src)
- {
- stats->imp_withdraws_ignored++;
- rte_update_unlock();
- return;
- }
+ req->hook->stats.withdraws_ignored++;
+ RT_UNLOCK(tab);
+ return;
}
- recalc:
/* And recalculate the best route */
- rte_hide_dummy_routes(nn, &dummy);
- rte_recalculate(c, nn, new, src);
- rte_unhide_dummy_routes(nn, &dummy);
+ rte_recalculate(tab, hook, nn, new, src);
+ RT_UNLOCK(tab);
+}
+
+/* Check rtable for best route to given net whether it would be exported do p */
+int
+rt_examine(rtable_private *t, net_addr *a, struct channel *c, const struct filter *filter)
+{
+ net *n = net_find(t, a);
+
+ if (!n || !n->routes)
+ return 0;
+
+ rte rt = n->routes->rte;
+
+ if (!rte_is_valid(&rt))
+ return 0;
- rte_update_unlock();
- return;
+ rte_update_lock(c);
- drop:
- rte_free(new);
- new = NULL;
- if (nn = net_find(c->table, n))
- goto recalc;
+ /* Rest is stripped down export_filter() */
+ int v = c->proto->preexport ? c->proto->preexport(c, &rt) : 0;
+ if (v == RIC_PROCESS)
+ v = (f_run(filter, &rt, c->rte_update_pool, FF_SILENT) <= F_ACCEPT);
- rte_update_unlock();
+ rte_update_unlock(c);
+
+ return v > 0;
}
-/* Independent call to rte_announce(), used from next hop
- recalculation, outside of rte_update(). new must be non-NULL */
-static inline void
-rte_announce_i(rtable *tab, uint type, net *net, rte *new, rte *old,
- rte *new_best, rte *old_best)
+static void
+rt_export_stopped(void *data)
{
- rte_update_lock();
- rte_announce(tab, type, net, new, old, new_best, old_best);
- rte_update_unlock();
+ struct rt_export_hook *hook = data;
+
+ RT_LOCKED(hook->table, tab)
+ {
+ /* Drop pending exports */
+ rt_export_used(tab);
+
+ /* Unlist */
+ rem_node(&hook->n);
+ }
+
+ /* Report the channel as stopped. */
+ hook->stopped(hook->req);
+
+ RT_LOCKED(hook->table, tab)
+ {
+ /* Free the hook together with its coroutine. */
+ rp_free(hook->pool, tab->rp);
+ rt_unlock_table(tab);
+
+ DBG("Export hook %p in table %s finished uc=%u\n", hook, tab->name, tab->use_count);
+ }
}
+
static inline void
-rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */
+rt_set_import_state(struct rt_import_hook *hook, u8 state)
{
- rte_update_lock();
- rte_recalculate(old->sender, old->net, NULL, old->attrs->src);
- rte_update_unlock();
+ hook->last_state_change = current_time();
+ hook->import_state = state;
+
+ if (hook->req->log_state_change)
+ hook->req->log_state_change(hook->req, state);
}
-/* Modify existing route by protocol hook, used for long-lived graceful restart */
static inline void
-rte_modify(rte *old)
+rt_set_export_state(struct rt_export_hook *hook, u8 state)
{
- rte_update_lock();
+ hook->last_state_change = current_time();
+ atomic_store_explicit(&hook->export_state, state, memory_order_release);
- rte *new = old->sender->proto->rte_modify(old, rte_update_pool);
- if (new != old)
- {
- if (new)
- {
- if (!rta_is_cached(new->attrs))
- new->attrs = rta_lookup(new->attrs);
- new->flags = (old->flags & ~REF_MODIFY) | REF_COW;
- }
+ if (hook->req->log_state_change)
+ hook->req->log_state_change(hook->req, state);
+}
- rte_recalculate(old->sender, old->net, new, old->attrs->src);
- }
+void
+rt_request_import(rtable *t, struct rt_import_request *req)
+{
+ RT_LOCK(t);
+ rtable_private *tab = RT_PRIV(t);
+ rt_lock_table(tab);
+
+ struct rt_import_hook *hook = req->hook = mb_allocz(tab->rp, sizeof(struct rt_import_hook));
+
+ DBG("Lock table %s for import %p req=%p uc=%u\n", tab->name, hook, req, tab->use_count);
+
+ hook->req = req;
+ hook->table = t;
+
+ hook->export_announce_event = ev_new_init(tab->rp, rt_import_announce_exports, hook);
+
+ if (!hook->stale_set)
+ hook->stale_set = hook->stale_valid = hook->stale_pruning = hook->stale_pruned = 1;
+
+ rt_set_import_state(hook, TIS_UP);
+
+ hook->n = (node) {};
+ add_tail(&tab->imports, &hook->n);
- rte_update_unlock();
+ RT_UNLOCK(t);
}
-/* Check rtable for best route to given net whether it would be exported do p */
-int
-rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter)
+void
+rt_stop_import(struct rt_import_request *req, event *stopped)
{
- net *n = net_find(t, a);
- rte *rt = n ? n->routes : NULL;
+ ASSERT_DIE(req->hook);
+ struct rt_import_hook *hook = req->hook;
- if (!rte_is_valid(rt))
- return 0;
+ rtable_private *tab = RT_LOCK(hook->table);
- rte_update_lock();
+ rt_schedule_prune(tab);
- /* Rest is stripped down export_filter() */
- int v = p->preexport ? p->preexport(p, &rt, rte_update_pool) : 0;
- if (v == RIC_PROCESS)
- {
- rte_make_tmp_attrs(&rt, rte_update_pool, NULL);
- v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
- }
+ rt_set_import_state(hook, TIS_STOP);
+ hook->stopped = stopped;
- /* Discard temporary rte */
- if (rt != n->routes)
- rte_free(rt);
+ if (hook->stale_set < hook->stale_valid)
+ if (!--tab->rr_count)
+ rt_schedule_notify(tab);
- rte_update_unlock();
+ RT_UNLOCK(tab);
+}
- return v > 0;
+void
+rt_request_export(rtable *t, struct rt_export_request *req)
+{
+ RT_LOCK(t);
+ rtable_private *tab = RT_PRIV(t);
+ rt_lock_table(tab);
+
+ pool *p = rp_new(tab->rp, tab->loop, "Export hook");
+ struct rt_export_hook *hook = req->hook = mb_allocz(p, sizeof(struct rt_export_hook));
+ hook->pool = p;
+
+ hook->req = req;
+ hook->table = t;
+
+ /* stats zeroed by mb_allocz */
+
+ bmap_init(&hook->seq_map, p, 1024);
+
+ rt_set_export_state(hook, TES_HUNGRY);
+
+ hook->n = (node) {};
+ add_tail(&tab->exports, &hook->n);
+
+ DBG("New export hook %p req %p in table %s uc=%u\n", hook, req, tab->name, tab->use_count);
+
+ hook->event = ev_new_init(p, rt_feed_channel, hook);
+ RT_UNLOCK(t);
+
+ rt_send_export_event(hook);
}
+void
+rt_stop_export(struct rt_export_request *req, void (*stopped)(struct rt_export_request *))
+{
+ ASSERT_DIE(req->hook);
+ struct rt_export_hook *hook = req->hook;
+
+ RT_LOCK(hook->table);
+ rtable_private *tab = RT_PRIV(hook->table);
+
+ /* Stop feeding */
+ ev_postpone(hook->event);
+
+ if (atomic_load_explicit(&hook->export_state, memory_order_relaxed) == TES_FEEDING)
+ fit_get(&tab->fib, &hook->feed_fit);
+
+ hook->event->hook = rt_export_stopped;
+ hook->stopped = stopped;
+
+ rt_send_export_event(hook);
+
+ RT_UNLOCK(hook->table);
+
+ rt_set_export_state(hook, TES_STOP);
+}
/**
* rt_refresh_begin - start a refresh cycle
@@ -1630,21 +1836,48 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter)
* routes to the routing table (by rte_update()). After that, all protocol
* routes (more precisely routes with @c as @sender) not sent during the
* refresh cycle but still in the table from the past are pruned. This is
- * implemented by marking all related routes as stale by REF_STALE flag in
- * rt_refresh_begin(), then marking all related stale routes with REF_DISCARD
- * flag in rt_refresh_end() and then removing such routes in the prune loop.
- */
+ * implemented by setting rte->stale_cycle to req->stale_set in rte_update()
+ * and then dropping all routes with old stale_cycle values in table prune loop. */
void
-rt_refresh_begin(rtable *t, struct channel *c)
+rt_refresh_begin(struct rt_import_request *req)
{
- FIB_WALK(&t->fib, net, n)
- {
- rte *e;
- for (e = n->routes; e; e = e->next)
- if (e->sender == c)
- e->flags |= REF_STALE;
- }
- FIB_WALK_END;
+ struct rt_import_hook *hook = req->hook;
+ ASSERT_DIE(hook);
+
+ RT_LOCK(hook->table);
+ rtable_private *tab = RT_PRIV(hook->table);
+
+ ASSERT_DIE(hook->stale_set == hook->stale_valid);
+
+ /* If the pruning routine is too slow */
+ if ((hook->stale_pruned < hook->stale_valid) && (hook->stale_pruned + 128 < hook->stale_valid)
+ || (hook->stale_pruned > hook->stale_valid) && (hook->stale_pruned > hook->stale_valid + 128))
+ {
+ log(L_WARN "Route refresh flood in table %s", tab->name);
+ FIB_WALK(&tab->fib, net, n)
+ {
+ for (struct rte_storage *e = n->routes; e; e = e->next)
+ if (e->rte.sender == req->hook)
+ e->rte.stale_cycle = 0;
+ }
+ FIB_WALK_END;
+ hook->stale_set = 1;
+ hook->stale_valid = 0;
+ hook->stale_pruned = 0;
+ }
+ else if (!++hook->stale_set)
+ {
+ /* Let's reserve the stale_cycle zero value for always-invalid routes */
+ hook->stale_set = 1;
+ hook->stale_valid = 0;
+ }
+
+ tab->rr_count++;
+
+ if (req->trace_routes & D_STATES)
+ log(L_TRACE "%s: route refresh begin [%u]", req->name, hook->stale_set);
+
+ RT_UNLOCK(tab);
}
/**
@@ -1656,45 +1889,24 @@ rt_refresh_begin(rtable *t, struct channel *c)
* hook. See rt_refresh_begin() for description of refresh cycles.
*/
void
-rt_refresh_end(rtable *t, struct channel *c)
+rt_refresh_end(struct rt_import_request *req)
{
- int prune = 0;
+ struct rt_import_hook *hook = req->hook;
+ ASSERT_DIE(hook);
- FIB_WALK(&t->fib, net, n)
- {
- rte *e;
- for (e = n->routes; e; e = e->next)
- if ((e->sender == c) && (e->flags & REF_STALE))
- {
- e->flags |= REF_DISCARD;
- prune = 1;
- }
- }
- FIB_WALK_END;
+ rtable_private *tab = RT_LOCK(hook->table);
+ hook->stale_valid++;
+ ASSERT_DIE(hook->stale_set == hook->stale_valid);
- if (prune)
- rt_schedule_prune(t);
-}
+ rt_schedule_prune(tab);
-void
-rt_modify_stale(rtable *t, struct channel *c)
-{
- int prune = 0;
+ if (req->trace_routes & D_STATES)
+ log(L_TRACE "%s: route refresh end [%u]", req->name, hook->stale_valid);
- FIB_WALK(&t->fib, net, n)
- {
- rte *e;
- for (e = n->routes; e; e = e->next)
- if ((e->sender == c) && (e->flags & REF_STALE) && !(e->flags & REF_FILTERED))
- {
- e->flags |= REF_MODIFY;
- prune = 1;
- }
- }
- FIB_WALK_END;
+ if (!--tab->rr_count)
+ rt_schedule_notify(tab);
- if (prune)
- rt_schedule_prune(t);
+ RT_UNLOCK(tab);
}
/**
@@ -1704,14 +1916,11 @@ rt_modify_stale(rtable *t, struct channel *c)
* This functions dumps contents of a &rte to debug output.
*/
void
-rte_dump(rte *e)
-{
- net *n = e->net;
- debug("%-1N ", n->n.addr);
- debug("PF=%02x pref=%d ", e->pflags, e->pref);
- rta_dump(e->attrs);
- if (e->attrs->src->proto->proto->dump_attrs)
- e->attrs->src->proto->proto->dump_attrs(e);
+rte_dump(struct rte_storage *e)
+{
+ debug("%-1N ", e->rte.net);
+ debug("PF=%02x ", e->rte.pflags);
+ rta_dump(e->rte.attrs);
debug("\n");
}
@@ -1722,20 +1931,22 @@ rte_dump(rte *e)
* This function dumps contents of a given routing table to debug output.
*/
void
-rt_dump(rtable *t)
+rt_dump(rtable *tab)
{
- debug("Dump of routing table <%s>\n", t->name);
+ RT_LOCK(tab);
+ rtable_private *t = RT_PRIV(tab);
+ debug("Dump of routing table <%s>%s\n", t->name, t->delete ? " (deleted)" : "");
#ifdef DEBUGGING
fib_check(&t->fib);
#endif
FIB_WALK(&t->fib, net, n)
{
- rte *e;
- for(e=n->routes; e; e=e->next)
+ for(struct rte_storage *e=n->routes; e; e=e->next)
rte_dump(e);
}
FIB_WALK_END;
debug("\n");
+ RT_UNLOCK(tab);
}
/**
@@ -1753,73 +1964,105 @@ rt_dump_all(void)
rt_dump(t);
}
-static inline void
-rt_schedule_hcu(rtable *tab)
+void
+rt_dump_hooks(rtable *t)
+{
+ RT_LOCK(t);
+ rtable_private *tab = RT_PRIV(t);
+ debug("Dump of hooks in routing table <%s>%s\n", tab->name, tab->delete ? " (deleted)" : "");
+ debug(" nhu_state=%u hcu_scheduled=%u use_count=%d rt_count=%u\n",
+ atomic_load(&tab->nhu_state), ev_active(tab->hcu_event), tab->use_count, tab->rt_count);
+ debug(" last_rt_change=%t gc_time=%t gc_counter=%d prune_state=%u\n",
+ tab->last_rt_change, tab->gc_time, tab->gc_counter, tab->prune_state);
+
+ struct rt_import_hook *ih;
+ WALK_LIST(ih, tab->imports)
+ {
+ ih->req->dump_req(ih->req);
+ debug(" Import hook %p requested by %p: pref=%u"
+ " last_state_change=%t import_state=%u stopped=%p\n",
+ ih, ih->req, ih->stats.pref,
+ ih->last_state_change, ih->import_state, ih->stopped);
+ }
+
+ struct rt_export_hook *eh;
+ WALK_LIST(eh, tab->exports)
+ {
+ eh->req->dump_req(eh->req);
+ debug(" Export hook %p requested by %p:"
+ " refeed_pending=%u last_state_change=%t export_state=%u\n",
+ eh, eh->req, eh->refeed_pending, eh->last_state_change, atomic_load_explicit(&eh->export_state, memory_order_relaxed));
+ }
+ debug("\n");
+ RT_UNLOCK(t);
+}
+
+void
+rt_dump_hooks_all(void)
{
- if (tab->hcu_scheduled)
- return;
+ rtable *t;
+ node *n;
+
+ debug("Dump of all table hooks\n");
- tab->hcu_scheduled = 1;
- ev_schedule(tab->rt_event);
+ WALK_LIST2(t, n, routing_tables, n)
+ rt_dump_hooks(t);
}
static inline void
rt_schedule_nhu(rtable *tab)
{
- if (tab->nhu_state == NHU_CLEAN)
- ev_schedule(tab->rt_event);
+ atomic_fetch_or_explicit(&tab->nhu_state, NHU_SCHEDULED, memory_order_acq_rel);
+ ev_send_loop(tab->loop, tab->nhu_event);
/* state change:
* NHU_CLEAN -> NHU_SCHEDULED
* NHU_RUNNING -> NHU_DIRTY
*/
- tab->nhu_state |= NHU_SCHEDULED;
}
void
-rt_schedule_prune(rtable *tab)
+rt_schedule_prune(rtable_private *tab)
{
if (tab->prune_state == 0)
- ev_schedule(tab->rt_event);
+ ev_send_loop(tab->loop, tab->prune_event);
/* state change 0->1, 2->3 */
tab->prune_state |= 1;
}
-
-static void
-rt_event(void *ptr)
+void
+rt_export_used(rtable_private *tab)
{
- rtable *tab = ptr;
-
- rt_lock_table(tab);
+ if (config->table_debug)
+ log(L_TRACE "%s: Export cleanup requested", tab->name);
- if (tab->hcu_scheduled)
- rt_update_hostcache(tab);
-
- if (tab->nhu_state)
- rt_next_hop_update(tab);
-
- if (tab->prune_state)
- rt_prune_table(tab);
-
- rt_unlock_table(tab);
+ ev_send_loop(tab->loop, tab->ec_event);
}
-
static inline btime
-rt_settled_time(rtable *tab)
+rt_settled_time(rtable_private *tab)
{
ASSUME(tab->base_settle_time != 0);
- return MIN(tab->last_rt_change + tab->config->min_settle_time,
- tab->base_settle_time + tab->config->max_settle_time);
+ btime min_settle_time = tab->rr_count ? tab->config->min_rr_settle_time : tab->config->min_settle_time;
+ btime max_settle_time = tab->rr_count ? tab->config->max_rr_settle_time : tab->config->max_settle_time;
+
+ DBG("settled time computed from %t %t %t %t as %t / %t, now is %t\n",
+ tab->name, tab->last_rt_change, min_settle_time,
+ tab->base_settle_time, max_settle_time,
+ tab->last_rt_change + min_settle_time,
+ tab->base_settle_time + max_settle_time, current_time());
+
+ return MIN(tab->last_rt_change + min_settle_time,
+ tab->base_settle_time + max_settle_time);
}
static void
rt_settle_timer(timer *t)
{
- rtable *tab = t->data;
+ rtable_private *tab = t->data;
+ ASSERT_DIE(birdloop_inside(tab->loop));
if (!tab->base_settle_time)
return;
@@ -1827,7 +2070,7 @@ rt_settle_timer(timer *t)
btime settled_time = rt_settled_time(tab);
if (current_time() < settled_time)
{
- tm_set(tab->settle_timer, settled_time);
+ tm_set_in(tab->settle_timer, settled_time, tab->loop);
return;
}
@@ -1836,11 +2079,11 @@ rt_settle_timer(timer *t)
struct rt_subscription *s;
WALK_LIST(s, tab->subscribers)
- s->hook(s);
+ ev_send(s->event->list, s->event);
}
static void
-rt_kick_settle_timer(rtable *tab)
+rt_kick_settle_timer(rtable_private *tab)
{
tab->base_settle_time = current_time();
@@ -1848,11 +2091,11 @@ rt_kick_settle_timer(rtable *tab)
tab->settle_timer = tm_new_init(tab->rp, rt_settle_timer, tab, 0, 0);
if (!tm_active(tab->settle_timer))
- tm_set(tab->settle_timer, rt_settled_time(tab));
+ tm_set_in(tab->settle_timer, rt_settled_time(tab), tab->loop);
}
static inline void
-rt_schedule_notify(rtable *tab)
+rt_schedule_notify(rtable_private *tab)
{
if (EMPTY_LIST(tab->subscribers))
return;
@@ -1864,33 +2107,40 @@ rt_schedule_notify(rtable *tab)
}
void
-rt_subscribe(rtable *tab, struct rt_subscription *s)
+rt_subscribe(rtable *t, struct rt_subscription *s)
{
- s->tab = tab;
- rt_lock_table(tab);
- add_tail(&tab->subscribers, &s->n);
+ s->tab = t;
+ RT_LOCKED(t, tab)
+ {
+ rt_lock_table(tab);
+ DBG("rt_subscribe(%s)\n", tab->name);
+ add_tail(&tab->subscribers, &s->n);
+ }
}
void
rt_unsubscribe(struct rt_subscription *s)
{
- rem_node(&s->n);
- rt_unlock_table(s->tab);
+ RT_LOCKED(s->tab, tab)
+ {
+ rem_node(&s->n);
+ if (EMPTY_LIST(tab->subscribers) && tm_active(tab->settle_timer))
+ tm_stop(tab->settle_timer);
+ rt_unlock_table(tab);
+ }
}
static void
rt_free(resource *_r)
{
- rtable *r = (rtable *) _r;
+ rtable_private *r = (rtable_private *) _r;
DBG("Deleting routing table %s\n", r->name);
ASSERT_DIE(r->use_count == 0);
-
- if (r->internal)
- return;
-
- r->config->table = NULL;
- rem_node(&r->n);
+ ASSERT_DIE(r->rt_count == 0);
+ ASSERT_DIE(!r->cork_active);
+ ASSERT_DIE(EMPTY_LIST(r->imports));
+ ASSERT_DIE(EMPTY_LIST(r->exports));
if (r->hostcache)
rt_free_hostcache(r);
@@ -1907,14 +2157,14 @@ rt_free(resource *_r)
static void
rt_res_dump(resource *_r)
{
- rtable *r = (rtable *) _r;
+ rtable_private *r = RT_PRIV((rtable *) _r);
debug("name \"%s\", addr_type=%s, rt_count=%u, use_count=%d\n",
r->name, net_label[r->addr_type], r->rt_count, r->use_count);
}
static struct resclass rt_class = {
.name = "Routing table",
- .size = sizeof(struct rtable),
+ .size = sizeof(rtable_private),
.free = rt_free,
.dump = rt_res_dump,
.lookup = NULL,
@@ -1928,11 +2178,16 @@ rt_setup(pool *pp, struct rtable_config *cf)
void *nb = mb_alloc(pp, ns);
ASSERT_DIE(ns - 1 == bsnprintf(nb, ns, "Routing table %s", cf->name));
- pool *p = rp_new(pp, nb);
- mb_move(nb, p);
+ struct birdloop *l = birdloop_new(pp, DOMAIN_ORDER(rtable), nb);
+ pool *p = birdloop_pool(l);
- rtable *t = ralloc(p, &rt_class);
+ birdloop_enter(l);
+
+ rtable_private *t = ralloc(p, &rt_class);
t->rp = p;
+ t->loop = l;
+
+ t->rte_slab = sl_new(p, sizeof(struct rte_storage));
t->name = cf->name;
t->config = cf;
@@ -1940,19 +2195,35 @@ rt_setup(pool *pp, struct rtable_config *cf)
fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
- if (!(t->internal = cf->internal))
- {
- init_list(&t->channels);
- hmap_init(&t->id_map, p, 1024);
- hmap_set(&t->id_map, 0);
+ init_list(&t->imports);
+ init_list(&t->exports);
- init_list(&t->subscribers);
+ hmap_init(&t->id_map, p, 1024);
+ hmap_set(&t->id_map, 0);
- t->rt_event = ev_new_init(p, rt_event, t);
- t->last_rt_change = t->gc_time = current_time();
- }
+ init_list(&t->pending_exports);
+ init_list(&t->subscribers);
+
+ t->announce_event = ev_new_init(p, rt_announce_exports, t);
+ t->ec_event = ev_new_init(p, rt_export_cleanup, t);
+ t->prune_event = ev_new_init(p, rt_prune_table, t);
+ t->hcu_event = ev_new_init(p, rt_update_hostcache, t);
+ t->nhu_event = ev_new_init(p, rt_next_hop_update, t);
+
+ t->nhu_event->cork = &rt_cork;
+ t->prune_event->cork = &rt_cork;
+
+ t->last_rt_change = t->gc_time = current_time();
+ t->next_export_seq = 1;
- return t;
+ t->rl_pipe = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
+
+ t->nhu_lp = lp_new_default(p);
+
+ mb_move(nb, p);
+ birdloop_leave(l);
+
+ return (rtable *) t;
}
/**
@@ -1965,13 +2236,11 @@ void
rt_init(void)
{
rta_init();
- rt_table_pool = rp_new(&root_pool, "Routing tables");
- rte_update_pool = lp_new_default(rt_table_pool);
- rte_slab = sl_new(rt_table_pool, sizeof(rte));
+ rt_table_pool = rp_new(&root_pool, &main_birdloop, "Routing tables");
init_list(&routing_tables);
+ ev_init_cork(&rt_cork, "Route Table Cork");
}
-
/**
* rt_prune_table - prune a routing table
*
@@ -1987,12 +2256,15 @@ rt_init(void)
* iteration.
*/
static void
-rt_prune_table(rtable *tab)
+rt_prune_table(void *data)
{
+ rtable_private *tab = data;
+ ASSERT_DIE(birdloop_inside(tab->loop));
+
struct fib_iterator *fit = &tab->prune_fit;
int limit = 512;
- struct channel *c;
+ struct rt_import_hook *ih;
node *n, *x;
DBG("Pruning route table %s\n", tab->name);
@@ -2003,12 +2275,21 @@ rt_prune_table(rtable *tab)
if (tab->prune_state == 0)
return;
+ rt_lock_table(tab);
+
if (tab->prune_state == 1)
{
/* Mark channels to flush */
- WALK_LIST2(c, n, tab->channels, table_node)
- if (c->channel_state == CS_FLUSHING)
- c->flush_active = 1;
+ WALK_LIST2(ih, n, tab->imports, n)
+ if (ih->import_state == TIS_STOP)
+ rt_set_import_state(ih, TIS_FLUSHING);
+ else if ((ih->stale_valid != ih->stale_pruning) && (ih->stale_pruning == ih->stale_pruned))
+ {
+ ih->stale_pruning = ih->stale_valid;
+
+ if (ih->req->trace_routes & D_STATES)
+ log(L_TRACE "%s: table prune after refresh begin [%u]", ih->req->name, ih->stale_pruning);
+ }
FIB_ITERATE_INIT(fit, &tab->fib);
tab->prune_state = 2;
@@ -2017,43 +2298,31 @@ rt_prune_table(rtable *tab)
again:
FIB_ITERATE_START(&tab->fib, fit, net, n)
{
- rte *e;
-
rescan:
- for (e=n->routes; e; e=e->next)
+ for (struct rte_storage *e=n->routes; e; e=e->next)
{
- if (e->sender->flush_active || (e->flags & REF_DISCARD))
- {
- if (limit <= 0)
- {
- FIB_ITERATE_PUT(fit);
- ev_schedule(tab->rt_event);
- return;
- }
-
- rte_discard(e);
- limit--;
+ struct rt_import_hook *s = e->rte.sender;
- goto rescan;
- }
-
- if (e->flags & REF_MODIFY)
+ if ((s->import_state == TIS_FLUSHING) ||
+ (e->rte.stale_cycle < s->stale_valid) ||
+ (e->rte.stale_cycle > s->stale_set))
{
if (limit <= 0)
{
FIB_ITERATE_PUT(fit);
- ev_schedule(tab->rt_event);
+ ev_send_loop(tab->loop, tab->prune_event);
+ rt_unlock_table(tab);
return;
}
- rte_modify(e);
+ rte_recalculate(tab, e->rte.sender, n, NULL, e->rte.src);
limit--;
goto rescan;
}
}
- if (!n->routes) /* Orphaned FIB entry */
+ if (!n->routes && !n->first) /* Orphaned FIB entry */
{
FIB_ITERATE_PUT(fit);
fib_delete(&tab->fib, n);
@@ -2070,23 +2339,202 @@ again:
tab->gc_time = current_time();
/* state change 2->0, 3->1 */
- tab->prune_state &= 1;
-
- if (tab->prune_state > 0)
- ev_schedule(tab->rt_event);
+ if (tab->prune_state &= 1)
+ ev_send_loop(tab->loop, tab->prune_event);
- /* FIXME: This should be handled in a better way */
- rt_prune_sources();
+ uint flushed_channels = 0;
/* Close flushed channels */
- WALK_LIST2_DELSAFE(c, n, x, tab->channels, table_node)
- if (c->flush_active)
+ WALK_LIST2_DELSAFE(ih, n, x, tab->imports, n)
+ if (ih->import_state == TIS_FLUSHING)
+ {
+ ih->flush_seq = tab->next_export_seq;
+ rt_set_import_state(ih, TIS_WAITING);
+ flushed_channels++;
+ }
+ else if (ih->stale_pruning != ih->stale_pruned)
+ {
+ ih->stale_pruned = ih->stale_pruning;
+
+ if (ih->req->trace_routes & D_STATES)
+ log(L_TRACE "%s: table prune after refresh end [%u]", ih->req->name, ih->stale_pruned);
+ }
+
+ /* In some cases, we may want to directly proceed to export cleanup */
+ if (EMPTY_LIST(tab->exports) && flushed_channels)
+ rt_export_cleanup(tab);
+
+ rt_unlock_table(tab);
+}
+
+static void
+rt_export_cleanup(void *data)
+{
+ rtable_private *tab = data;
+ ASSERT_DIE(birdloop_inside(tab->loop));
+
+ u64 min_seq = ~((u64) 0);
+ struct rt_pending_export *last_export_to_free = NULL;
+ struct rt_pending_export *first_export = tab->first_export;
+
+ struct rt_export_hook *eh;
+ node *n;
+ WALK_LIST2(eh, n, tab->exports, n)
+ {
+ switch (atomic_load_explicit(&eh->export_state, memory_order_acquire))
+ {
+ case TES_DOWN:
+ case TES_HUNGRY:
+ continue;
+
+ case TES_READY:
+ {
+ struct rt_pending_export *last = atomic_load_explicit(&eh->last_export, memory_order_acquire);
+ if (!last)
+ /* No last export means that the channel has exported nothing since last cleanup */
+ goto done;
+
+ else if (min_seq > last->seq)
+ {
+ min_seq = last->seq;
+ last_export_to_free = last;
+ }
+ continue;
+ }
+
+ default:
+ /* It's only safe to cleanup when the export state is idle or regular. No feeding or stopping allowed. */
+ goto done;
+ }
+ }
+
+ tab->first_export = last_export_to_free ? rt_next_export_fast(last_export_to_free) : NULL;
+
+ if (config->table_debug)
+ log(L_TRACE "%s: Export cleanup, old first_export seq %lu, new %lu, min_seq %ld",
+ tab->name,
+ first_export ? first_export->seq : 0,
+ tab->first_export ? tab->first_export->seq : 0,
+ min_seq);
+
+ WALK_LIST2(eh, n, tab->exports, n)
+ {
+ if (atomic_load_explicit(&eh->export_state, memory_order_acquire) != TES_READY)
+ continue;
+
+ struct rt_pending_export *last = atomic_load_explicit(&eh->last_export, memory_order_acquire);
+ if (last == last_export_to_free)
+ {
+ /* This may fail when the channel managed to export more inbetween. This is OK. */
+ atomic_compare_exchange_strong_explicit(
+ &eh->last_export, &last, NULL,
+ memory_order_release,
+ memory_order_relaxed);
+
+ DBG("store hook=%p last_export=NULL\n", eh);
+ }
+ }
+
+ while (first_export && (first_export->seq <= min_seq))
+ {
+ ASSERT_DIE(first_export->new || first_export->old);
+
+ const net_addr *n = first_export->new ?
+ first_export->new->rte.net :
+ first_export->old->rte.net;
+ net *net = SKIP_BACK(struct network, n.addr, (net_addr (*)[0]) n);
+
+ ASSERT_DIE(net->first == first_export);
+
+ if (first_export == net->last)
+ /* The only export here */
+ net->last = net->first = NULL;
+ else
+ /* First is now the next one */
+ net->first = atomic_load_explicit(&first_export->next, memory_order_relaxed);
+
+ /* For now, the old route may be finally freed */
+ if (first_export->old)
+ {
+ rt_rte_trace_in(D_ROUTES, first_export->old->rte.sender->req, &first_export->old->rte, "freed");
+ hmap_clear(&tab->id_map, first_export->old->rte.id);
+ rte_free(first_export->old, tab);
+ }
+
+#ifdef LOCAL_DEBUG
+ memset(first_export, 0xbd, sizeof(struct rt_pending_export));
+#endif
+
+ struct rt_export_block *reb = HEAD(tab->pending_exports);
+ ASSERT_DIE(reb == PAGE_HEAD(first_export));
+
+ u32 pos = (first_export - &reb->export[0]);
+ u32 end = atomic_load_explicit(&reb->end, memory_order_relaxed);
+ ASSERT_DIE(pos < end);
+
+ struct rt_pending_export *next = NULL;
+
+ if (++pos < end)
+ next = &reb->export[pos];
+ else
+ {
+ rem_node(&reb->n);
+
+#ifdef LOCAL_DEBUG
+ memset(reb, 0xbe, page_size);
+#endif
+
+ free_page(reb);
+
+ if (EMPTY_LIST(tab->pending_exports))
{
- c->flush_active = 0;
- channel_set_state(c, CS_DOWN);
+ if (config->table_debug)
+ log(L_TRACE "%s: Resetting export seq", tab->name);
+
+ node *n;
+ WALK_LIST2(eh, n, tab->exports, n)
+ {
+ if (atomic_load_explicit(&eh->export_state, memory_order_acquire) != TES_READY)
+ continue;
+
+ ASSERT_DIE(atomic_load_explicit(&eh->last_export, memory_order_acquire) == NULL);
+ bmap_reset(&eh->seq_map, 1024);
+ }
+
+ tab->next_export_seq = 1;
+ }
+ else
+ {
+ reb = HEAD(tab->pending_exports);
+ next = &reb->export[0];
+ }
+ }
+
+ first_export = next;
+ }
+
+done:;
+ struct rt_import_hook *ih; node *x;
+ WALK_LIST2_DELSAFE(ih, n, x, tab->imports, n)
+ if (ih->import_state == TIS_WAITING)
+ if (!first_export || (first_export->seq >= ih->flush_seq))
+ {
+ ih->import_state = TIS_CLEARED;
+ ev_send(ih->req->list, ih->export_announce_event);
}
- return;
+ if (EMPTY_LIST(tab->pending_exports) && ev_active(tab->announce_event))
+ ev_postpone(tab->announce_event);
+
+ /* If reduced to at most one export block pending */
+ if (tab->cork_active &&
+ ((!tab->first_export) || (tab->first_export->seq + 128 > tab->next_export_seq)))
+ {
+ tab->cork_active = 0;
+ ev_uncork(&rt_cork);
+ if (config->table_debug)
+ log(L_TRACE "%s: cork released", tab->name);
+ }
}
void
@@ -2120,7 +2568,7 @@ rta_next_hop_outdated(rta *a)
}
void
-rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls)
+rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls, linpool *lp)
{
a->hostentry = he;
a->dest = he->dest;
@@ -2155,7 +2603,7 @@ no_nexthop:
else
{
nhr = nhp;
- nhp = (nhp ? (nhp->next = lp_alloc(rte_update_pool, NEXTHOP_MAX_SIZE)) : &(a->nh));
+ nhp = (nhp ? (nhp->next = lp_alloc(lp, NEXTHOP_MAX_SIZE)) : &(a->nh));
}
memset(nhp, 0, NEXTHOP_MAX_SIZE);
@@ -2210,8 +2658,8 @@ no_nexthop:
}
}
-static inline rte *
-rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
+static inline struct rte_storage *
+rt_next_hop_update_rte(rtable_private *tab, net *n, rte *old)
{
rta *a = alloca(RTA_MAX_SIZE);
memcpy(a, old->attrs, rta_size(old->attrs));
@@ -2219,58 +2667,71 @@ rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
mpls_label_stack mls = { .len = a->nh.labels_orig };
memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32));
- rta_apply_hostentry(a, old->attrs->hostentry, &mls);
- a->aflags = 0;
+ rta_apply_hostentry(a, old->attrs->hostentry, &mls, tab->nhu_lp);
+ a->cached = 0;
- rte *e = sl_alloc(rte_slab);
- memcpy(e, old, sizeof(rte));
- e->attrs = rta_lookup(a);
+ rte e0 = *old;
+ e0.attrs = a;
- return e;
+ return rte_store(&e0, n, tab);
}
static inline int
-rt_next_hop_update_net(rtable *tab, net *n)
+rt_next_hop_update_net(rtable_private *tab, net *n)
{
- rte **k, *e, *new, *old_best, **new_best;
+ struct rte_storage *new;
int count = 0;
- int free_old_best = 0;
- old_best = n->routes;
+ struct rte_storage *old_best = n->routes;
if (!old_best)
return 0;
- for (k = &n->routes; e = *k; k = &e->next)
- if (rta_next_hop_outdated(e->attrs))
- {
- new = rt_next_hop_update_rte(tab, e);
- *k = new;
+ for (struct rte_storage *e, **k = &n->routes; e = *k; k = &e->next)
+ if (rta_next_hop_outdated(e->rte.attrs))
+ count++;
+
+ if (!count)
+ return 0;
+
+ struct rte_multiupdate {
+ struct rte_storage *old, *new;
+ } *updates = alloca(sizeof(struct rte_multiupdate) * count);
- rte_trace_in(D_ROUTES, new->sender, new, "updated");
- rte_announce_i(tab, RA_ANY, n, new, e, NULL, NULL);
+ int pos = 0;
+ for (struct rte_storage *e, **k = &n->routes; e = *k; k = &e->next)
+ if (rta_next_hop_outdated(e->rte.attrs))
+ {
+ struct rte_storage *new = rt_next_hop_update_rte(tab, n, &e->rte);
/* Call a pre-comparison hook */
/* Not really an efficient way to compute this */
- if (e->attrs->src->proto->rte_recalculate)
- e->attrs->src->proto->rte_recalculate(tab, n, new, e, NULL);
+ if (e->rte.src->owner->rte_recalculate)
+ e->rte.src->owner->rte_recalculate(tab, n, &new->rte, &e->rte, &old_best->rte);
+
+ updates[pos++] = (struct rte_multiupdate) {
+ .old = e,
+ .new = new,
+ };
- if (e != old_best)
- rte_free_quick(e);
- else /* Freeing of the old best rte is postponed */
- free_old_best = 1;
+ /* Replace the route in the list */
+ new->next = e->next;
+ *k = e = new;
- e = new;
- count++;
+ /* Get a new ID for the route */
+ new->rte.lastmod = current_time();
+ new->rte.id = hmap_first_zero(&tab->id_map);
+ hmap_set(&tab->id_map, new->rte.id);
+
+ lp_flush(tab->nhu_lp);
}
- if (!count)
- return 0;
+ ASSERT_DIE(pos == count);
/* Find the new best route */
- new_best = NULL;
- for (k = &n->routes; e = *k; k = &e->next)
+ struct rte_storage **new_best = NULL;
+ for (struct rte_storage *e, **k = &n->routes; e = *k; k = &e->next)
{
- if (!new_best || rte_better(e, *new_best))
+ if (!new_best || rte_better(&e->rte, &(*new_best)->rte))
new_best = k;
}
@@ -2283,32 +2744,39 @@ rt_next_hop_update_net(rtable *tab, net *n)
n->routes = new;
}
- /* Announce the new best route */
- if (new != old_best)
- rte_trace_in(D_ROUTES, new->sender, new, "updated [best]");
-
- /* Propagate changes */
- rte_announce_i(tab, RA_UNDEF, n, NULL, NULL, n->routes, old_best);
-
- if (free_old_best)
- rte_free_quick(old_best);
+ /* Announce the changes */
+ for (int i=0; i<count; i++)
+ {
+ _Bool nb = (new == updates[i].new), ob = (old_best == updates[i].old);
+ const char *best_indicator[2][2] = {
+ { "autoupdated", "autoupdated [-best]" },
+ { "autoupdated [+best]", "autoupdated [best]" }
+ };
+ rt_rte_trace_in(D_ROUTES, updates[i].new->rte.sender->req, &updates[i].new->rte, best_indicator[nb][ob]);
+ rte_announce(tab, n, updates[i].new, updates[i].old, new, old_best);
+ }
return count;
}
static void
-rt_next_hop_update(rtable *tab)
+rt_next_hop_update(void *data)
{
+ rtable_private *tab = data;
+ ASSERT_DIE(birdloop_inside(tab->loop));
+
struct fib_iterator *fit = &tab->nhu_fit;
int max_feed = 32;
- if (tab->nhu_state == NHU_CLEAN)
+ if (atomic_load_explicit(&tab->nhu_state, memory_order_acquire) == NHU_CLEAN)
return;
- if (tab->nhu_state == NHU_SCHEDULED)
+ rt_lock_table(tab);
+
+ if (atomic_load_explicit(&tab->nhu_state, memory_order_acquire) == NHU_SCHEDULED)
{
FIB_ITERATE_INIT(fit, &tab->fib);
- tab->nhu_state = NHU_RUNNING;
+ ASSERT_DIE(atomic_exchange_explicit(&tab->nhu_state, NHU_RUNNING, memory_order_acq_rel) == NHU_SCHEDULED);
}
FIB_ITERATE_START(&tab->fib, fit, net, n)
@@ -2316,7 +2784,8 @@ rt_next_hop_update(rtable *tab)
if (max_feed <= 0)
{
FIB_ITERATE_PUT(fit);
- ev_schedule(tab->rt_event);
+ ev_send_loop(tab->loop, tab->nhu_event);
+ rt_unlock_table(tab);
return;
}
max_feed -= rt_next_hop_update_net(tab, n);
@@ -2327,10 +2796,12 @@ rt_next_hop_update(rtable *tab)
* NHU_DIRTY -> NHU_SCHEDULED
* NHU_RUNNING -> NHU_CLEAN
*/
- tab->nhu_state &= 1;
+ if (atomic_fetch_and_explicit(&tab->nhu_state, NHU_SCHEDULED, memory_order_acq_rel) != NHU_RUNNING)
+ ev_send_loop(tab->loop, tab->nhu_event);
- if (tab->nhu_state != NHU_CLEAN)
- ev_schedule(tab->rt_event);
+ ev_send_loop(tab->loop, tab->announce_event);
+
+ rt_unlock_table(tab);
}
@@ -2352,6 +2823,10 @@ rt_new_table(struct symbol *s, uint addr_type)
c->gc_min_time = 5;
c->min_settle_time = 1 S;
c->max_settle_time = 20 S;
+ c->min_rr_settle_time = 30 S;
+ c->max_rr_settle_time = 90 S;
+ c->cork_limit = 4 * page_size / sizeof(struct rt_pending_export);
+ c->owner = new_config;
add_tail(&new_config->tables, &c->n);
@@ -2371,7 +2846,7 @@ rt_new_table(struct symbol *s, uint addr_type)
* configuration.
*/
void
-rt_lock_table(rtable *r)
+rt_lock_table(rtable_private *r)
{
r->use_count++;
}
@@ -2385,16 +2860,11 @@ rt_lock_table(rtable *r)
* for deletion by configuration changes.
*/
void
-rt_unlock_table(rtable *r)
+rt_unlock_table(rtable_private *r)
{
- if (!--r->use_count && r->deleted)
- {
- struct config *conf = r->deleted;
-
- /* Delete the routing table by freeing its pool */
- rt_shutdown(r);
- config_del_obstacle(conf);
- }
+ if (!--r->use_count && r->delete &&
+ !r->prune_state && !atomic_load_explicit(&r->nhu_state, memory_order_acquire))
+ birdloop_stop_self(r->loop, r->delete, r);
}
static struct rtable_config *
@@ -2404,6 +2874,21 @@ rt_find_table_config(struct config *cf, char *name)
return (sym && (sym->class == SYM_TABLE)) ? sym->table : NULL;
}
+static void
+rt_done(void *data)
+{
+ RT_LOCKED((rtable *) data, t)
+ {
+ struct rtable_config *tc = t->config;
+ struct config *c = tc->owner;
+
+ tc->table = NULL;
+ rem_node(&t->n);
+
+ config_del_obstacle(c);
+ }
+}
+
/**
* rt_commit - commit new routing table configuration
* @new: new configuration
@@ -2426,14 +2911,15 @@ rt_commit(struct config *new, struct config *old)
{
WALK_LIST(o, old->tables)
{
- rtable *ot = o->table;
- if (!ot->deleted)
+ RT_LOCK(o->table);
+ rtable_private *ot = RT_PRIV(o->table);
+ if (!ot->delete)
{
r = rt_find_table_config(new, o->name);
if (r && (r->addr_type == o->addr_type) && !new->shutdown)
{
DBG("\t%s: same\n", o->name);
- r->table = ot;
+ r->table = (rtable *) ot;
ot->name = r->name;
ot->config = r;
if (o->sorted != r->sorted)
@@ -2442,12 +2928,13 @@ rt_commit(struct config *new, struct config *old)
else
{
DBG("\t%s: deleted\n", o->name);
- ot->deleted = old;
- config_add_obstacle(old);
rt_lock_table(ot);
+ ot->delete = rt_done;
+ config_add_obstacle(old);
rt_unlock_table(ot);
}
}
+ RT_UNLOCK(o->table);
}
}
@@ -2461,19 +2948,6 @@ rt_commit(struct config *new, struct config *old)
DBG("\tdone\n");
}
-static inline void
-do_feed_channel(struct channel *c, net *n, rte *e)
-{
- rte_update_lock();
- if (c->ra_mode == RA_ACCEPTED)
- rt_notify_accepted(c, n, NULL, NULL, c->refeeding);
- else if (c->ra_mode == RA_MERGED)
- rt_notify_merged(c, n, NULL, NULL, e, e, c->refeeding);
- else /* RA_BASIC */
- rt_notify_basic(c, n, e, e, c->refeeding);
- rte_update_unlock();
-}
-
/**
* rt_feed_channel - advertise all routes to a channel
* @c: channel to be fed
@@ -2483,370 +2957,120 @@ do_feed_channel(struct channel *c, net *n, rte *e)
* has something to do. (We avoid transferring all the routes in single pass in
* order not to monopolize CPU time.)
*/
-int
-rt_feed_channel(struct channel *c)
+static void
+rt_feed_channel(void *data)
{
+ struct rt_export_hook *c = data;
+
struct fib_iterator *fit = &c->feed_fit;
int max_feed = 256;
- ASSERT(c->export_state == ES_FEEDING);
-
- if (!c->feed_active)
- {
- FIB_ITERATE_INIT(fit, &c->table->fib);
- c->feed_active = 1;
- }
-
- FIB_ITERATE_START(&c->table->fib, fit, net, n)
- {
- rte *e = n->routes;
- if (max_feed <= 0)
- {
- FIB_ITERATE_PUT(fit);
- return 0;
- }
-
- if ((c->ra_mode == RA_OPTIMAL) ||
- (c->ra_mode == RA_ACCEPTED) ||
- (c->ra_mode == RA_MERGED))
- if (rte_is_valid(e))
- {
- /* In the meantime, the protocol may fell down */
- if (c->export_state != ES_FEEDING)
- goto done;
-
- do_feed_channel(c, n, e);
- max_feed--;
- }
-
- if (c->ra_mode == RA_ANY)
- for(e = n->routes; e; e = e->next)
- {
- /* In the meantime, the protocol may fell down */
- if (c->export_state != ES_FEEDING)
- goto done;
-
- if (!rte_is_valid(e))
- continue;
-
- do_feed_channel(c, n, e);
- max_feed--;
- }
- }
- FIB_ITERATE_END;
-
-done:
- c->feed_active = 0;
- return 1;
-}
-
-/**
- * rt_feed_baby_abort - abort protocol feeding
- * @c: channel
- *
- * This function is called by the protocol code when the protocol stops or
- * ceases to exist during the feeding.
- */
-void
-rt_feed_channel_abort(struct channel *c)
-{
- if (c->feed_active)
- {
- /* Unlink the iterator */
- fit_get(&c->table->fib, &c->feed_fit);
- c->feed_active = 0;
- }
-}
-
-
-/*
- * Import table
- */
-
-int
-rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
-{
- struct rtable *tab = c->in_table;
- rte *old, **pos;
- net *net;
-
- if (new)
+ rtable_private *tab;
+ if (c->export_state == TES_HUNGRY)
{
- net = net_get(tab, n);
+ rt_set_export_state(c, TES_FEEDING);
+
+ tab = RT_LOCK(c->table);
- if (!new->pref)
- new->pref = c->preference;
+ struct rt_pending_export *rpe = rt_last_export(tab);
+ DBG("store hook=%p last_export=%p seq=%lu\n", c, rpe, rpe ? rpe->seq : 0);
+ atomic_store_explicit(&c->last_export, rpe, memory_order_relaxed);
- if (!rta_is_cached(new->attrs))
- new->attrs = rta_lookup(new->attrs);
+ FIB_ITERATE_INIT(&c->feed_fit, &tab->fib);
}
else
- {
- net = net_find(tab, n);
+ tab = RT_LOCK(c->table);
- if (!net)
- goto drop_withdraw;
- }
+ ASSERT_DIE(c->export_state == TES_FEEDING);
- /* Find the old rte */
- for (pos = &net->routes; old = *pos; pos = &old->next)
- if (old->attrs->src == src)
+redo:
+ FIB_ITERATE_START(&tab->fib, fit, net, n)
{
- if (new && rte_same(old, new))
- {
- /* Refresh the old rte, continue with update to main rtable */
- if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
+ if (max_feed <= 0)
{
- old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
- return 1;
- }
-
- goto drop_update;
- }
-
- /* Move iterator if needed */
- if (old == c->reload_next_rte)
- c->reload_next_rte = old->next;
-
- /* Remove the old rte */
- *pos = old->next;
- rte_free_quick(old);
- tab->rt_count--;
-
- break;
- }
-
- if (!new)
- {
- if (!old)
- goto drop_withdraw;
-
- if (!net->routes)
- fib_delete(&tab->fib, net);
-
- return 1;
- }
-
- struct channel_limit *l = &c->rx_limit;
- if (l->action && !old)
- {
- if (tab->rt_count >= l->limit)
- channel_notify_limit(c, l, PLD_RX, tab->rt_count);
-
- if (l->state == PLS_BLOCKED)
- {
- /* Required by rte_trace_in() */
- new->net = net;
-
- rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
- goto drop_update;
- }
- }
-
- /* Insert the new rte */
- rte *e = rte_do_cow(new);
- e->flags |= REF_COW;
- e->net = net;
- e->sender = c;
- e->lastmod = current_time();
- e->next = *pos;
- *pos = e;
- tab->rt_count++;
- return 1;
-
-drop_update:
- c->stats.imp_updates_received++;
- c->stats.imp_updates_ignored++;
- rte_free(new);
-
- if (!net->routes)
- fib_delete(&tab->fib, net);
-
- return 0;
-
-drop_withdraw:
- c->stats.imp_withdraws_received++;
- c->stats.imp_withdraws_ignored++;
- return 0;
-}
-
-int
-rt_reload_channel(struct channel *c)
-{
- struct rtable *tab = c->in_table;
- struct fib_iterator *fit = &c->reload_fit;
- int max_feed = 64;
-
- ASSERT(c->channel_state == CS_UP);
+ FIB_ITERATE_PUT(fit);
+ rt_send_export_event(c);
- if (!c->reload_active)
- {
- FIB_ITERATE_INIT(fit, &tab->fib);
- c->reload_active = 1;
- }
+ RT_UNLOCK(c->table);
+ return;
+ }
- do {
- for (rte *e = c->reload_next_rte; e; e = e->next)
- {
- if (max_feed-- <= 0)
+ if (atomic_load_explicit(&c->export_state, memory_order_acquire) != TES_FEEDING)
{
- c->reload_next_rte = e;
- debug("%s channel reload burst split (max_feed=%d)", c->proto->name, max_feed);
- return 0;
+ RT_UNLOCK(c->table);
+ return;
}
- rte_update2(c, e->net->n.addr, rte_do_cow(e), e->attrs->src);
- }
-
- c->reload_next_rte = NULL;
-
- FIB_ITERATE_START(&tab->fib, fit, net, n)
- {
- if (c->reload_next_rte = n->routes)
+ if (!n->routes || !rte_is_valid(&n->routes->rte))
+ ; /* if no route, do nothing */
+ else if (c->req->export_bulk)
{
- FIB_ITERATE_PUT_NEXT(fit, &tab->fib);
- break;
- }
- }
- FIB_ITERATE_END;
- }
- while (c->reload_next_rte);
-
- c->reload_active = 0;
- return 1;
-}
-
-void
-rt_reload_channel_abort(struct channel *c)
-{
- if (c->reload_active)
- {
- /* Unlink the iterator */
- fit_get(&c->in_table->fib, &c->reload_fit);
- c->reload_next_rte = NULL;
- c->reload_active = 0;
- }
-}
-
-void
-rt_prune_sync(rtable *t, int all)
-{
- struct fib_iterator fit;
-
- FIB_ITERATE_INIT(&fit, &t->fib);
+ uint count = rte_feed_count(n);
+ if (count)
+ {
+ rte **feed = alloca(count * sizeof(rte *));
+ rte_feed_obtain(n, feed, count);
-again:
- FIB_ITERATE_START(&t->fib, &fit, net, n)
- {
- rte *e, **ee = &n->routes;
+ struct rt_pending_export *rpe_last, *rpe_first = n->first;
+ for (struct rt_pending_export *rpe = rpe_first; rpe; rpe = rpe_next(rpe, NULL))
+ rpe_last = rpe;
- while (e = *ee)
- {
- if (all || (e->flags & (REF_STALE | REF_DISCARD)))
- {
- *ee = e->next;
- rte_free_quick(e);
- t->rt_count--;
- }
- else
- ee = &e->next;
- }
+ FIB_ITERATE_PUT_NEXT(fit, &tab->fib);
+ RT_UNLOCK(c->table);
- if (all || !n->routes)
- {
- FIB_ITERATE_PUT(&fit);
- fib_delete(&t->fib, n);
- goto again;
- }
- }
- FIB_ITERATE_END;
-}
+ c->req->export_bulk(c->req, n->n.addr, NULL, feed, count);
+ RT_LOCK(c->table);
-/*
- * Export table
- */
+ for (struct rt_pending_export *rpe = rpe_first; rpe; rpe = rpe_next(rpe, NULL))
+ {
+ rpe_mark_seen(c, rpe);
+ if (rpe == rpe_last)
+ break;
+ ASSERT_DIE(rpe->seq < rpe_last->seq);
+ }
-int
-rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed)
-{
- struct rtable *tab = c->out_table;
- struct rte_src *src;
- rte *old, **pos;
- net *net;
+ max_feed -= count;
- if (new)
- {
- net = net_get(tab, n);
- src = new->attrs->src;
+ goto redo;
+ }
+ }
+ else if (c->req->export_one)
+ {
+ struct rt_pending_export rpe = { .new = n->routes, .new_best = n->routes };
- rte_store_tmp_attrs(new, rte_update_pool, NULL);
+ struct rt_pending_export *rpe_last, *rpe_first = n->first;
+ for (struct rt_pending_export *rpe = rpe_first; rpe; rpe = rpe_next(rpe, NULL))
+ rpe_last = rpe;
- if (!rta_is_cached(new->attrs))
- new->attrs = rta_lookup(new->attrs);
- }
- else
- {
- net = net_find(tab, n);
- src = old0->attrs->src;
+ FIB_ITERATE_PUT_NEXT(fit, &tab->fib);
+ RT_UNLOCK(c->table);
- if (!net)
- goto drop_withdraw;
- }
+ c->req->export_one(c->req, n->n.addr, &rpe);
- /* Find the old rte */
- for (pos = &net->routes; old = *pos; pos = &old->next)
- if ((c->ra_mode != RA_ANY) || (old->attrs->src == src))
- {
- if (new && rte_same(old, new))
- {
- /* REF_STALE / REF_DISCARD not used in export table */
- /*
- if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
+ RT_LOCK(c->table);
+ for (struct rt_pending_export *rpe = rpe_first; rpe; rpe = rpe_next(rpe, NULL))
{
- old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
- return 1;
+ rpe_mark_seen(c, rpe);
+ if (rpe == rpe_last)
+ break;
+ ASSERT_DIE(rpe->seq < rpe_last->seq);
}
- */
- goto drop_update;
+ max_feed--;
+ goto redo;
}
-
- /* Remove the old rte */
- *pos = old->next;
- rte_free_quick(old);
- tab->rt_count--;
-
- break;
+ else
+ bug("Export request must always provide an export method");
}
+ FIB_ITERATE_END;
- if (!new)
- {
- if (!old)
- goto drop_withdraw;
-
- if (!net->routes)
- fib_delete(&tab->fib, net);
-
- return 1;
- }
-
- /* Insert the new rte */
- rte *e = rte_do_cow(new);
- e->flags |= REF_COW;
- e->net = net;
- e->sender = c;
- e->lastmod = current_time();
- e->next = *pos;
- *pos = e;
- tab->rt_count++;
- return 1;
+ c->event->hook = rt_export_hook;
+ rt_send_export_event(c);
-drop_update:
- return refeed;
+ RT_UNLOCK(c->table);
-drop_withdraw:
- return 0;
+ rt_set_export_state(c, TES_READY);
}
@@ -2953,7 +3177,7 @@ hc_delete_hostentry(struct hostcache *hc, pool *p, struct hostentry *he)
}
static void
-rt_init_hostcache(rtable *tab)
+rt_init_hostcache(rtable_private *tab)
{
struct hostcache *hc = mb_allocz(tab->rp, sizeof(struct hostcache));
init_list(&hc->hostentries);
@@ -2969,7 +3193,7 @@ rt_init_hostcache(rtable *tab)
}
static void
-rt_free_hostcache(rtable *tab)
+rt_free_hostcache(rtable_private *tab)
{
struct hostcache *hc = tab->hostcache;
@@ -2992,13 +3216,13 @@ rt_free_hostcache(rtable *tab)
}
static void
-rt_notify_hostcache(rtable *tab, net *net)
+rt_notify_hostcache(rtable_private *tab, net *net)
{
- if (tab->hcu_scheduled)
+ if (ev_active(tab->hcu_event))
return;
if (trie_match_net(tab->hostcache->trie, net->n.addr))
- rt_schedule_hcu(tab);
+ ev_send_loop(tab->loop, tab->hcu_event);
}
static int
@@ -3021,41 +3245,17 @@ rt_get_igp_metric(rte *rt)
if (ea)
return ea->u.data;
- rta *a = rt->attrs;
-
-#ifdef CONFIG_OSPF
- if ((a->source == RTS_OSPF) ||
- (a->source == RTS_OSPF_IA) ||
- (a->source == RTS_OSPF_EXT1))
- return rt->u.ospf.metric1;
-#endif
-
-#ifdef CONFIG_RIP
- if (a->source == RTS_RIP)
- return rt->u.rip.metric;
-#endif
-
-#ifdef CONFIG_BGP
- if (a->source == RTS_BGP)
- {
- u64 metric = bgp_total_aigp_metric(rt);
- return (u32) MIN(metric, (u64) IGP_METRIC_UNKNOWN);
- }
-#endif
-
-#ifdef CONFIG_BABEL
- if (a->source == RTS_BABEL)
- return rt->u.babel.metric;
-#endif
-
- if (a->source == RTS_DEVICE)
+ if (rt->attrs->source == RTS_DEVICE)
return 0;
+ if (rt->src->owner->class->rte_igp_metric)
+ return rt->src->owner->class->rte_igp_metric(rt);
+
return IGP_METRIC_UNKNOWN;
}
static int
-rt_update_hostentry(rtable *tab, struct hostentry *he)
+rt_update_hostentry(rtable_private *tab, struct hostentry *he)
{
rta *old_src = he->src;
int direct = 0;
@@ -3072,11 +3272,12 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
net *n = net_route(tab, &he_addr);
if (n)
{
- rte *e = n->routes;
- rta *a = e->attrs;
- pxlen = n->n.addr->pxlen;
+ struct rte_storage *e = n->routes;
+ rta *a = e->rte.attrs;
+ word pref = a->pref;
- if (a->hostentry)
+ for (struct rte_storage *ee = n->routes; ee; ee = ee->next)
+ if ((ee->rte.attrs->pref >= pref) && ee->rte.attrs->hostentry)
{
/* Recursive route should not depend on another recursive route */
log(L_WARN "Next hop address %I resolvable through recursive route for %N",
@@ -3084,6 +3285,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
goto done;
}
+ pxlen = n->n.addr->pxlen;
+
if (a->dest == RTD_UNICAST)
{
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
@@ -3104,7 +3307,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
he->src = rta_clone(a);
he->dest = a->dest;
he->nexthop_linkable = !direct;
- he->igp_metric = rt_get_igp_metric(e);
+ he->igp_metric = rt_get_igp_metric(&e->rte);
}
done:
@@ -3116,8 +3319,11 @@ done:
}
static void
-rt_update_hostcache(rtable *tab)
+rt_update_hostcache(void *data)
{
+ rtable_private *tab = data;
+ ASSERT_DIE(birdloop_inside(tab->loop));
+
struct hostcache *hc = tab->hostcache;
struct hostentry *he;
node *n, *x;
@@ -3138,15 +3344,15 @@ rt_update_hostcache(rtable *tab)
if (rt_update_hostentry(tab, he))
rt_schedule_nhu(he->tab);
}
-
- tab->hcu_scheduled = 0;
}
struct hostentry *
-rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
+rt_get_hostentry(rtable *t, ip_addr a, ip_addr ll, rtable *dep)
{
struct hostentry *he;
+ rtable_private *tab = RT_LOCK(t);
+
if (!tab->hostcache)
rt_init_hostcache(tab);
@@ -3154,10 +3360,13 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
struct hostcache *hc = tab->hostcache;
for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next)
if (ipa_equal(he->addr, a) && (he->tab == dep))
- return he;
+ goto done;
he = hc_new_hostentry(hc, tab->rp, a, ipa_zero(ll) ? a : ll, dep, k);
rt_update_hostentry(tab, he);
+
+done:
+ RT_UNLOCK(t);
return he;
}
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index 1e87212c..515b27e5 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -641,13 +641,36 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (r)
{
rta a0 = {
- .src = p->p.main_source,
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
+ .pref = c->preference,
.from = r->neigh->addr,
.nh.gw = r->next_hop,
.nh.iface = r->neigh->ifa->iface,
+ .eattrs = alloca(sizeof(ea_list) + 3*sizeof(eattr)),
+ };
+
+ *a0.eattrs = (ea_list) { .count = 3 };
+ a0.eattrs->attrs[0] = (eattr) {
+ .id = EA_BABEL_METRIC,
+ .type = EAF_TYPE_INT,
+ .u.data = r->metric,
+ };
+
+ struct adata *ad = alloca(sizeof(struct adata) + sizeof(u64));
+ ad->length = sizeof(u64);
+ memcpy(ad->data, &(r->router_id), sizeof(u64));
+ a0.eattrs->attrs[1] = (eattr) {
+ .id = EA_BABEL_ROUTER_ID,
+ .type = EAF_TYPE_OPAQUE,
+ .u.ptr = ad,
+ };
+
+ a0.eattrs->attrs[2] = (eattr) {
+ .id = EA_BABEL_SEQNO,
+ .type = EAF_TYPE_INT,
+ .u.data = r->seqno,
};
/*
@@ -658,40 +681,37 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (!neigh_find(&p->p, r->next_hop, r->neigh->ifa->iface, 0))
a0.nh.flags = RNF_ONLINK;
- rta *a = rta_lookup(&a0);
- rte *rte = rte_get_temp(a);
- rte->u.babel.seqno = r->seqno;
- rte->u.babel.metric = r->metric;
- rte->u.babel.router_id = r->router_id;
- rte->pflags = EA_ID_FLAG(EA_BABEL_METRIC) | EA_ID_FLAG(EA_BABEL_ROUTER_ID);
+ rte e0 = {
+ .attrs = &a0,
+ .src = p->p.main_source,
+ };
e->unreachable = 0;
- rte_update2(c, e->n.addr, rte, p->p.main_source);
+ rte_update(c, e->n.addr, &e0, p->p.main_source);
}
else if (e->valid && (e->router_id != p->router_id))
{
/* Unreachable */
rta a0 = {
- .src = p->p.main_source,
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNREACHABLE,
+ .pref = 1,
};
- rta *a = rta_lookup(&a0);
- rte *rte = rte_get_temp(a);
- memset(&rte->u.babel, 0, sizeof(rte->u.babel));
- rte->pflags = 0;
- rte->pref = 1;
+ rte e0 = {
+ .attrs = &a0,
+ .src = p->p.main_source,
+ };
e->unreachable = 1;
- rte_update2(c, e->n.addr, rte, p->p.main_source);
+ rte_update(c, e->n.addr, &e0, p->p.main_source);
}
else
{
/* Retraction */
e->unreachable = 0;
- rte_update2(c, e->n.addr, NULL, p->p.main_source);
+ rte_update(c, e->n.addr, NULL, p->p.main_source);
}
}
@@ -701,7 +721,7 @@ babel_announce_retraction(struct babel_proto *p, struct babel_entry *e)
{
struct channel *c = (e->n.addr->type == NET_IP4) ? p->ip4_channel : p->ip6_channel;
e->unreachable = 0;
- rte_update2(c, e->n.addr, NULL, p->p.main_source);
+ rte_update(c, e->n.addr, NULL, p->p.main_source);
}
@@ -1727,7 +1747,7 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con
TRACE(D_EVENTS, "Adding interface %s", new->name);
- pool *pool = rp_new(p->p.pool, new->name);
+ pool *pool = rp_new(p->p.pool, p->p.loop, new->name);
ifa = mb_allocz(pool, sizeof(struct babel_iface));
ifa->proto = p;
@@ -2010,7 +2030,13 @@ babel_dump(struct proto *P)
static void
babel_get_route_info(rte *rte, byte *buf)
{
- buf += bsprintf(buf, " (%d/%d) [%lR]", rte->pref, rte->u.babel.metric, rte->u.babel.router_id);
+ u64 rid = 0;
+ eattr *e = ea_find(rte->attrs->eattrs, EA_BABEL_ROUTER_ID);
+ if (e)
+ memcpy(&rid, e->u.ptr->data, sizeof(u64));
+
+ buf += bsprintf(buf, " (%d/%d) [%lR]", rte->attrs->pref,
+ ea_get_int(rte->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY), rid);
}
static int
@@ -2018,6 +2044,9 @@ babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
{
switch (a->id)
{
+ case EA_BABEL_SEQNO:
+ return GA_FULL;
+
case EA_BABEL_METRIC:
bsprintf(buf, "metric: %d", a->u.data);
return GA_FULL;
@@ -2231,45 +2260,23 @@ babel_kick_timer(struct babel_proto *p)
static int
-babel_preexport(struct proto *P, struct rte **new, struct linpool *pool UNUSED)
+babel_preexport(struct channel *c, struct rte *new)
{
- struct rta *a = (*new)->attrs;
-
+ struct rta *a = new->attrs;
/* Reject our own unreachable routes */
- if ((a->dest == RTD_UNREACHABLE) && (a->src->proto == P))
+ if ((a->dest == RTD_UNREACHABLE) && (new->src->owner == &c->proto->sources))
return -1;
return 0;
}
-static void
-babel_make_tmp_attrs(struct rte *rt, struct linpool *pool)
-{
- struct adata *id = lp_alloc_adata(pool, sizeof(u64));
- memcpy(id->data, &rt->u.babel.router_id, sizeof(u64));
-
- rte_init_tmp_attrs(rt, pool, 2);
- rte_make_tmp_attr(rt, EA_BABEL_METRIC, EAF_TYPE_INT, rt->u.babel.metric);
- rte_make_tmp_attr(rt, EA_BABEL_ROUTER_ID, EAF_TYPE_OPAQUE, (uintptr_t) id);
-}
-
-static void
-babel_store_tmp_attrs(struct rte *rt, struct linpool *pool)
-{
- rte_init_tmp_attrs(rt, pool, 2);
- rt->u.babel.metric = rte_store_tmp_attr(rt, EA_BABEL_METRIC);
-
- /* EA_BABEL_ROUTER_ID is read-only, we do not really save the value */
- rte_store_tmp_attr(rt, EA_BABEL_ROUTER_ID);
-}
-
/*
* babel_rt_notify - core tells us about new route (possibly our own),
* so store it into our data structures.
*/
static void
-babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
- struct rte *new, struct rte *old UNUSED)
+babel_rt_notify(struct proto *P, struct channel *c UNUSED, const net_addr *net,
+ struct rte *new, const struct rte *old UNUSED)
{
struct babel_proto *p = (void *) P;
struct babel_entry *e;
@@ -2277,19 +2284,31 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
if (new)
{
/* Update */
- uint internal = (new->attrs->src->proto == P);
- uint rt_seqno = internal ? new->u.babel.seqno : p->update_seqno;
+ uint rt_seqno;
uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
- u64 rt_router_id = internal ? new->u.babel.router_id : p->router_id;
+ u64 rt_router_id = 0;
+
+ if (new->src->owner == &P->sources)
+ {
+ rt_seqno = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
+ eattr *e = ea_find(new->attrs->eattrs, EA_BABEL_ROUTER_ID);
+ if (e)
+ memcpy(&rt_router_id, e->u.ptr->data, sizeof(u64));
+ }
+ else
+ {
+ rt_seqno = p->update_seqno;
+ rt_router_id = p->router_id;
+ }
if (rt_metric > BABEL_INFINITY)
{
log(L_WARN "%s: Invalid babel_metric value %u for route %N",
- p->p.name, rt_metric, net->n.addr);
+ p->p.name, rt_metric, net);
rt_metric = BABEL_INFINITY;
}
- e = babel_get_entry(p, net->n.addr);
+ e = babel_get_entry(p, net);
/* Activate triggered updates */
if ((e->valid != BABEL_ENTRY_VALID) ||
@@ -2307,7 +2326,7 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
else
{
/* Withdraw */
- e = babel_find_entry(p, net->n.addr);
+ e = babel_find_entry(p, net);
if (!e || e->valid != BABEL_ENTRY_VALID)
return;
@@ -2323,15 +2342,16 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
static int
babel_rte_better(struct rte *new, struct rte *old)
{
- return new->u.babel.metric < old->u.babel.metric;
+ uint new_metric = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
+ uint old_metric = ea_find(old->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
+
+ return new_metric < old_metric;
}
-static int
-babel_rte_same(struct rte *new, struct rte *old)
+static u32
+babel_rte_igp_metric(struct rte *rt)
{
- return ((new->u.babel.seqno == old->u.babel.seqno) &&
- (new->u.babel.metric == old->u.babel.metric) &&
- (new->u.babel.router_id == old->u.babel.router_id));
+ return ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
}
@@ -2352,6 +2372,12 @@ babel_postconfig(struct proto_config *CF)
cf->ip6_channel = ip6 ?: ip6_sadr;
}
+static struct rte_owner_class babel_rte_owner_class = {
+ .get_route_info = babel_get_route_info,
+ .rte_better = babel_rte_better,
+ .rte_igp_metric = babel_rte_igp_metric,
+};
+
static struct proto *
babel_init(struct proto_config *CF)
{
@@ -2365,10 +2391,8 @@ babel_init(struct proto_config *CF)
P->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify;
P->preexport = babel_preexport;
- P->make_tmp_attrs = babel_make_tmp_attrs;
- P->store_tmp_attrs = babel_store_tmp_attrs;
- P->rte_better = babel_rte_better;
- P->rte_same = babel_rte_same;
+
+ P->sources.class = &babel_rte_owner_class;
return P;
}
@@ -2461,7 +2485,6 @@ babel_reconfigure(struct proto *P, struct proto_config *CF)
return 1;
}
-
struct protocol proto_babel = {
.name = "Babel",
.template = "babel%d",
@@ -2476,6 +2499,5 @@ struct protocol proto_babel = {
.start = babel_start,
.shutdown = babel_shutdown,
.reconfigure = babel_reconfigure,
- .get_route_info = babel_get_route_info,
.get_attr = babel_get_attr
};
diff --git a/proto/babel/babel.h b/proto/babel/babel.h
index 84feb085..8b6da3c8 100644
--- a/proto/babel/babel.h
+++ b/proto/babel/babel.h
@@ -28,6 +28,7 @@
#define EA_BABEL_METRIC EA_CODE(PROTOCOL_BABEL, 0)
#define EA_BABEL_ROUTER_ID EA_CODE(PROTOCOL_BABEL, 1)
+#define EA_BABEL_SEQNO EA_CODE(PROTOCOL_BABEL, 2)
#define BABEL_MAGIC 42
#define BABEL_VERSION 2
diff --git a/proto/bfd/Makefile b/proto/bfd/Makefile
index 402122fc..267dff98 100644
--- a/proto/bfd/Makefile
+++ b/proto/bfd/Makefile
@@ -1,6 +1,6 @@
-src := bfd.c io.c packets.c
+src := bfd.c packets.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
-tests_objs := $(tests_objs) $(src-o-files) \ No newline at end of file
+tests_objs := $(tests_objs) $(src-o-files)
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
index dac184c5..c9b12aa1 100644
--- a/proto/bfd/bfd.c
+++ b/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 bfd_proto_list;
-static list 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);
@@ -444,8 +452,8 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
s->passive = s->cf.passive;
s->tx_csn = random_u32();
- s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
- s->hold_timer = tm_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
+ s->tx_timer = tm_new_init(p->p.pool, bfd_tx_timer_hook, s, 0, 0);
+ s->hold_timer = tm_new_init(p->p.pool, bfd_hold_timer_hook, s, 0, 0);
bfd_session_update_tx_interval(s);
bfd_session_control_tx_timer(s, 1);
@@ -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);
- sl_free(p->session_slab, s);
+ TRACE(D_EVENTS, "Session to %I removed", s->addr);
- TRACE(D_EVENTS, "Session to %I removed", ip);
+ sl_free(p->session_slab, s);
+}
- 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);
}
@@ -573,7 +581,7 @@ bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface)
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
struct bfd_iface_config *ic = bfd_find_iface_config(cf, iface);
- ifa = mb_allocz(p->tpool, sizeof(struct bfd_iface));
+ ifa = mb_allocz(p->p.pool, sizeof(struct bfd_iface));
ifa->local = local;
ifa->iface = iface;
ifa->cf = ic;
@@ -618,9 +626,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);
}
@@ -650,7 +658,7 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
{
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
- if (p->p.vrf_set && (p->p.vrf != req->vrf))
+ if (p->p.vrf && (p->p.vrf != req->vrf))
return 0;
if (ipa_is_ip4(req->addr) ? !cf->accept_ipv4 : !cf->accept_ipv6)
@@ -681,41 +689,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;
@@ -728,9 +763,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;
@@ -739,11 +771,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;
}
@@ -1001,8 +1038,10 @@ bfd_notify_init(struct bfd_proto *p)
void
bfd_init_all(void)
{
- init_list(&bfd_proto_list);
- init_list(&bfd_wait_list);
+ 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);
}
static struct proto *
@@ -1021,8 +1060,6 @@ 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->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
@@ -1034,9 +1071,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->accept_ipv4 && cf->accept_direct)
p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
@@ -1050,42 +1085,33 @@ bfd_start(struct proto *P)
if (cf->accept_ipv6 && cf->accept_multihop)
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;
}
@@ -1105,7 +1131,7 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
(new->accept_multihop != old->accept_multihop))
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);
@@ -1119,7 +1145,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;
}
diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h
index 91fdaa60..475b3a11 100644
--- a/proto/bfd/bfd.h
+++ b/proto/bfd/bfd.h
@@ -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
@@ -87,9 +87,9 @@ struct bfd_neighbor
struct bfd_proto
{
struct proto p;
- struct birdloop *loop;
- pool *tpool;
+
pthread_spinlock_t lock;
+
node bfd_node;
slab *session_slab;
diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y
index df1cba42..ed5479fb 100644
--- a/proto/bfd/config.Y
+++ b/proto/bfd/config.Y
@@ -36,6 +36,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;
diff --git a/proto/bfd/io.c b/proto/bfd/io.c
deleted file mode 100644
index 1cd9365a..00000000
--- a/proto/bfd/io.c
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * BIRD -- I/O and event loop
- *
- * Can be freely distributed and used under the terms of the GNU GPL.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <pthread.h>
-#include <time.h>
-#include <sys/time.h>
-
-#include "nest/bird.h"
-#include "proto/bfd/io.h"
-
-#include "lib/buffer.h"
-#include "lib/lists.h"
-#include "lib/resource.h"
-#include "lib/event.h"
-#include "lib/timer.h"
-#include "lib/socket.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;
-};
-
-
-/*
- * Current thread context
- */
-
-static pthread_key_t current_loop_key;
-extern pthread_key_t current_time_key;
-
-static inline struct birdloop *
-birdloop_current(void)
-{
- return pthread_getspecific(current_loop_key);
-}
-
-static inline void
-birdloop_set_current(struct birdloop *loop)
-{
- pthread_setspecific(current_loop_key, loop);
- pthread_setspecific(current_time_key, loop ? &loop->time : &main_timeloop);
-}
-
-static inline void
-birdloop_init_current(void)
-{
- pthread_key_create(&current_loop_key, NULL);
-}
-
-
-/*
- * Wakeup code for birdloop
- */
-
-static void
-pipe_new(int *pfds)
-{
- int rv = pipe(pfds);
- if (rv < 0)
- die("pipe: %m");
-
- if (fcntl(pfds[0], F_SETFL, O_NONBLOCK) < 0)
- die("fcntl(O_NONBLOCK): %m");
-
- if (fcntl(pfds[1], F_SETFL, O_NONBLOCK) < 0)
- die("fcntl(O_NONBLOCK): %m");
-}
-
-void
-pipe_drain(int fd)
-{
- char buf[64];
- int rv;
-
- try:
- rv = read(fd, buf, 64);
- if (rv < 0)
- {
- if (errno == EINTR)
- goto try;
- if (errno == EAGAIN)
- return;
- die("wakeup read: %m");
- }
- if (rv == 64)
- goto try;
-}
-
-void
-pipe_kick(int fd)
-{
- u64 v = 1;
- int rv;
-
- try:
- rv = write(fd, &v, sizeof(u64));
- if (rv < 0)
- {
- if (errno == EINTR)
- goto try;
- if (errno == EAGAIN)
- return;
- die("wakeup write: %m");
- }
-}
-
-static inline void
-wakeup_init(struct birdloop *loop)
-{
- pipe_new(loop->wakeup_fds);
-}
-
-static inline void
-wakeup_drain(struct birdloop *loop)
-{
- pipe_drain(loop->wakeup_fds[0]);
-}
-
-static inline void
-wakeup_do_kick(struct birdloop *loop)
-{
- pipe_kick(loop->wakeup_fds[1]);
-}
-
-static inline void
-wakeup_kick(struct birdloop *loop)
-{
- if (!loop->wakeup_masked)
- wakeup_do_kick(loop);
- 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);
-}
-
-void
-ev2_schedule(event *e)
-{
- 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);
-}
-
-
-/*
- * Sockets
- */
-
-static void
-sockets_init(struct birdloop *loop)
-{
- init_list(&loop->sock_list);
- loop->sock_num = 0;
-
- BUFFER_INIT(loop->poll_sk, loop->pool, 4);
- BUFFER_INIT(loop->poll_fd, loop->pool, 4);
- loop->poll_changed = 1; /* add wakeup fd */
-}
-
-static void
-sockets_add(struct birdloop *loop, sock *s)
-{
- add_tail(&loop->sock_list, &s->n);
- loop->sock_num++;
-
- s->index = -1;
- loop->poll_changed = 1;
-
- if (loop->poll_active)
- wakeup_kick(loop);
-}
-
-void
-sk_start(sock *s)
-{
- struct birdloop *loop = birdloop_current();
-
- sockets_add(loop, s);
-}
-
-static void
-sockets_remove(struct birdloop *loop, sock *s)
-{
- rem_node(&s->n);
- 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() */
-}
-
-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;
-}
-
-static inline uint sk_want_events(sock *s)
-{ return (s->rx_hook ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); }
-
-/*
-FIXME: this should be called from sock code
-
-static void
-sockets_update(struct birdloop *loop, sock *s)
-{
- if (s->index >= 0)
- loop->poll_fd.data[s->index].events = sk_want_events(s);
-}
-*/
-
-static void
-sockets_prepare(struct birdloop *loop)
-{
- BUFFER_SET(loop->poll_sk, loop->sock_num + 1);
- BUFFER_SET(loop->poll_fd, loop->sock_num + 1);
-
- struct pollfd *pfd = loop->poll_fd.data;
- sock **psk = loop->poll_sk.data;
- uint i = 0;
- node *n;
-
- WALK_LIST(n, loop->sock_list)
- {
- sock *s = SKIP_BACK(sock, n, n);
-
- ASSERT(i < loop->sock_num);
-
- s->index = i;
- *psk = s;
- pfd->fd = s->fd;
- pfd->events = sk_want_events(s);
- pfd->revents = 0;
-
- pfd++;
- psk++;
- i++;
- }
-
- ASSERT(i == loop->sock_num);
-
- /* Add internal wakeup fd */
- *psk = NULL;
- pfd->fd = loop->wakeup_fds[0];
- pfd->events = POLLIN;
- pfd->revents = 0;
-
- loop->poll_changed = 0;
-}
-
-static void
-sockets_close_fds(struct birdloop *loop)
-{
- struct pollfd *pfd = loop->poll_fd.data;
- sock **psk = loop->poll_sk.data;
- int poll_num = loop->poll_fd.used - 1;
-
- int i;
- for (i = 0; i < poll_num; i++)
- if (psk[i] == NULL)
- close(pfd[i].fd);
-
- loop->close_scheduled = 0;
-}
-
-int sk_read(sock *s, int revents);
-int sk_write(sock *s);
-
-static void
-sockets_fire(struct birdloop *loop)
-{
- struct pollfd *pfd = loop->poll_fd.data;
- sock **psk = loop->poll_sk.data;
- int poll_num = loop->poll_fd.used - 1;
-
- times_update(&loop->time);
-
- /* Last fd is internal wakeup fd */
- if (pfd[poll_num].revents & POLLIN)
- wakeup_drain(loop);
-
- int i;
- for (i = 0; i < poll_num; pfd++, psk++, i++)
- {
- int e = 1;
-
- if (! pfd->revents)
- continue;
-
- if (pfd->revents & POLLNVAL)
- die("poll: invalid fd %d", pfd->fd);
-
- if (pfd->revents & POLLIN)
- while (e && *psk && (*psk)->rx_hook)
- e = sk_read(*psk, 0);
-
- e = 1;
- if (pfd->revents & POLLOUT)
- while (e && *psk)
- e = sk_write(*psk);
- }
-}
-
-
-/*
- * Birdloop
- */
-
-static void * birdloop_main(void *arg);
-
-struct birdloop *
-birdloop_new(void)
-{
- /* FIXME: this init should be elsewhere and thread-safe */
- static int init = 0;
- if (!init)
- { birdloop_init_current(); init = 1; }
-
- pool *p = rp_new(NULL, "Birdloop root");
- struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
- loop->pool = p;
- pthread_mutex_init(&loop->mutex, NULL);
-
- wakeup_init(loop);
-
- events_init(loop);
- timers_init(&loop->time, p);
- sockets_init(loop);
-
- return loop;
-}
-
-void
-birdloop_start(struct birdloop *loop)
-{
- int rv = pthread_create(&loop->thread, NULL, birdloop_main, loop);
- if (rv)
- die("pthread_create(): %M", rv);
-}
-
-void
-birdloop_stop(struct birdloop *loop)
-{
- pthread_mutex_lock(&loop->mutex);
- loop->stop_called = 1;
- wakeup_do_kick(loop);
- pthread_mutex_unlock(&loop->mutex);
-
- int rv = pthread_join(loop->thread, NULL);
- if (rv)
- die("pthread_join(): %M", rv);
-}
-
-void
-birdloop_free(struct birdloop *loop)
-{
- rfree(loop->pool);
-}
-
-
-void
-birdloop_enter(struct birdloop *loop)
-{
- /* TODO: these functions could save and restore old context */
- pthread_mutex_lock(&loop->mutex);
- birdloop_set_current(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);
-}
-
-void
-birdloop_mask_wakeups(struct birdloop *loop)
-{
- pthread_mutex_lock(&loop->mutex);
- loop->wakeup_masked = 1;
- pthread_mutex_unlock(&loop->mutex);
-}
-
-void
-birdloop_unmask_wakeups(struct birdloop *loop)
-{
- pthread_mutex_lock(&loop->mutex);
- if (loop->wakeup_masked == 2)
- wakeup_do_kick(loop);
- loop->wakeup_masked = 0;
- pthread_mutex_unlock(&loop->mutex);
-}
-
-static void *
-birdloop_main(void *arg)
-{
- struct birdloop *loop = arg;
- timer *t;
- int rv, timeout;
-
- birdloop_set_current(loop);
-
- pthread_mutex_lock(&loop->mutex);
- while (1)
- {
- events_fire(loop);
- timers_fire(&loop->time);
-
- times_update(&loop->time);
- if (events_waiting(loop))
- timeout = 0;
- else if (t = timers_first(&loop->time))
- timeout = (tm_remains(t) TO_MS) + 1;
- else
- timeout = -1;
-
- if (loop->poll_changed)
- sockets_prepare(loop);
-
- loop->poll_active = 1;
- pthread_mutex_unlock(&loop->mutex);
-
- try:
- rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
- if (rv < 0)
- {
- if (errno == EINTR || errno == EAGAIN)
- goto try;
- die("poll: %m");
- }
-
- pthread_mutex_lock(&loop->mutex);
- loop->poll_active = 0;
-
- if (loop->close_scheduled)
- sockets_close_fds(loop);
-
- if (loop->stop_called)
- break;
-
- if (rv)
- sockets_fire(loop);
-
- timers_fire(&loop->time);
- }
-
- loop->stop_called = 0;
- pthread_mutex_unlock(&loop->mutex);
-
- return NULL;
-}
-
-
diff --git a/proto/bfd/io.h b/proto/bfd/io.h
deleted file mode 100644
index ec706e9a..00000000
--- a/proto/bfd/io.h
+++ /dev/null
@@ -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_ */
diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c
index 7618e20f..37d77f37 100644
--- a/proto/bfd/packets.c
+++ b/proto/bfd/packets.c
@@ -410,7 +410,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;
@@ -441,7 +441,7 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
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;
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 24ba00ba..1080db77 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -333,26 +333,26 @@ bgp_aigp_set_metric(struct linpool *pool, const struct adata *ad, u64 metric)
}
int
-bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad)
+bgp_total_aigp_metric_(struct rta *a, u64 *metric, const struct adata **ad)
{
- eattr *a = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AIGP));
- if (!a)
+ eattr *ea = ea_find(a->eattrs, EA_CODE(PROTOCOL_BGP, BA_AIGP));
+ if (!ea)
return 0;
- const byte *b = bgp_aigp_get_tlv(a->u.ptr, BGP_AIGP_METRIC);
+ const byte *b = bgp_aigp_get_tlv(ea->u.ptr, BGP_AIGP_METRIC);
if (!b)
return 0;
u64 aigp = get_u64(b + 3);
- u64 step = e->attrs->igp_metric;
+ u64 step = a->igp_metric;
- if (!rte_resolvable(e) || (step >= IGP_METRIC_UNKNOWN))
+ if (!rta_resolvable(a) || (step >= IGP_METRIC_UNKNOWN))
step = BGP_AIGP_MAX;
if (!step)
step = 1;
- *ad = a->u.ptr;
+ *ad = ea->u.ptr;
*metric = aigp + step;
if (*metric < aigp)
*metric = BGP_AIGP_MAX;
@@ -371,6 +371,13 @@ bgp_init_aigp_metric(rte *e, u64 *metric, const struct adata **ad)
return *metric < IGP_METRIC_UNKNOWN;
}
+u32
+bgp_rte_igp_metric(struct rte *rt)
+{
+ u64 metric = bgp_total_aigp_metric(rt->attrs);
+ return (u32) MIN(metric, (u64) IGP_METRIC_UNKNOWN);
+}
+
/*
* Attribute hooks
@@ -896,7 +903,7 @@ bgp_decode_large_community(struct bgp_parse_state *s, uint code UNUSED, uint fla
static void
bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
{
- net_addr *n = s->route->net->n.addr;
+ const net_addr *n = s->route->net;
u32 *labels = (u32 *) a->u.ptr->data;
uint lnum = a->u.ptr->length / 4;
@@ -1617,7 +1624,7 @@ bgp_free_prefix_table(struct bgp_channel *c)
}
static struct bgp_prefix *
-bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
+bgp_get_prefix(struct bgp_channel *c, const net_addr *net, u32 path_id)
{
u32 hash = net_hash(net) ^ u32_hash(path_id);
struct bgp_prefix *px = HASH_FIND(c->prefix_hash, PXH, net, path_id, hash);
@@ -1661,12 +1668,10 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
*/
int
-bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
+bgp_preexport(struct channel *c, rte *e)
{
- rte *e = *new;
- struct proto *SRC = e->attrs->src->proto;
- struct bgp_proto *p = (struct bgp_proto *) P;
- struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
+ struct bgp_proto *p = (struct bgp_proto *) (c->proto);
+ struct bgp_proto *src = bgp_rte_proto(e);
/* Reject our routes */
if (src == p)
@@ -1690,11 +1695,11 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
}
/* Handle well-known communities, RFC 1997 */
- struct eattr *c;
+ struct eattr *com;
if (p->cf->interpret_communities &&
- (c = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
+ (com = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
{
- const struct adata *d = c->u.ptr;
+ const struct adata *d = com->u.ptr;
/* Do not export anywhere */
if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
@@ -1719,8 +1724,7 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
static ea_list *
bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool)
{
- struct proto *SRC = e->attrs->src->proto;
- struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL;
+ struct bgp_proto *src = bgp_rte_proto(e);
struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
ea_list *attrs = attrs0;
eattr *a;
@@ -1774,7 +1778,7 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
/* AIGP attribute - accumulate local metric or originate new one */
u64 metric;
if (s.local_next_hop &&
- (bgp_total_aigp_metric_(e, &metric, &ad) ||
+ (bgp_total_aigp_metric_(e->attrs, &metric, &ad) ||
(c->cf->aigp_originate && bgp_init_aigp_metric(e, &metric, &ad))))
{
ad = bgp_aigp_set_metric(pool, ad, metric);
@@ -1833,7 +1837,7 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
}
void
-bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old)
+bgp_rt_notify(struct proto *P, struct channel *C, const net_addr *n, rte *new, const rte *old)
{
struct bgp_proto *p = (void *) P;
struct bgp_channel *c = (void *) C;
@@ -1847,17 +1851,17 @@ bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old)
/* If attributes are invalid, we fail back to withdraw */
buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
- path = new->attrs->src->global_id;
+ path = new->src->global_id;
lp_flush(bgp_linpool2);
}
else
{
buck = bgp_get_withdraw_bucket(c);
- path = old->attrs->src->global_id;
+ path = old->src->global_id;
}
- px = bgp_get_prefix(c, n->n.addr, c->add_path_tx ? path : 0);
+ px = bgp_get_prefix(c, n, c->add_path_tx ? path : 0);
add_tail(&buck->prefixes, &px->buck_node);
bgp_schedule_packet(p->conn, c, PKT_UPDATE);
@@ -1874,42 +1878,52 @@ bgp_get_neighbor(rte *r)
return as;
/* If AS_PATH is not defined, we treat rte as locally originated */
- struct bgp_proto *p = (void *) r->attrs->src->proto;
+ struct bgp_proto *p = bgp_rte_proto(r);
return p->cf->confederation ?: p->local_as;
}
static inline int
rte_stale(rte *r)
{
- if (r->u.bgp.stale < 0)
+ if (r->pflags & BGP_REF_STALE)
+ return 1;
+
+ if (r->pflags & BGP_REF_NOT_STALE)
+ return 0;
+
+ /* If staleness is unknown, compute and cache it */
+ eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
+ if (a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE))
{
- /* If staleness is unknown, compute and cache it */
- eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
- r->u.bgp.stale = a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE);
+ r->pflags |= BGP_REF_STALE;
+ return 1;
+ }
+ else
+ {
+ r->pflags |= BGP_REF_NOT_STALE;
+ return 0;
}
-
- return r->u.bgp.stale;
}
int
bgp_rte_better(rte *new, rte *old)
{
- struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->src->proto;
- struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->src->proto;
+ struct bgp_proto *new_bgp = bgp_rte_proto(new);
+ struct bgp_proto *old_bgp = bgp_rte_proto(old);
eattr *x, *y;
u32 n, o;
/* Skip suppressed routes (see bgp_rte_recalculate()) */
- n = new->u.bgp.suppressed;
- o = old->u.bgp.suppressed;
+ n = new->pflags & BGP_REF_SUPPRESSED;
+ o = old->pflags & BGP_REF_SUPPRESSED;
if (n > o)
return 0;
if (n < o)
return 1;
/* RFC 4271 9.1.2.1. Route resolvability test */
- n = rte_resolvable(new);
- o = rte_resolvable(old);
+ n = rta_resolvable(new->attrs);
+ o = rta_resolvable(old->attrs);
if (n > o)
return 1;
if (n < o)
@@ -1934,8 +1948,8 @@ bgp_rte_better(rte *new, rte *old)
return 0;
/* RFC 7311 4.1 - Apply AIGP metric */
- u64 n2 = bgp_total_aigp_metric(new);
- u64 o2 = bgp_total_aigp_metric(old);
+ u64 n2 = bgp_total_aigp_metric(new->attrs);
+ u64 o2 = bgp_total_aigp_metric(old->attrs);
if (n2 < o2)
return 1;
if (n2 > o2)
@@ -2039,21 +2053,18 @@ bgp_rte_better(rte *new, rte *old)
int
bgp_rte_mergable(rte *pri, rte *sec)
{
- struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->attrs->src->proto;
- struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->attrs->src->proto;
+ struct bgp_proto *pri_bgp = bgp_rte_proto(pri);
+ struct bgp_proto *sec_bgp = bgp_rte_proto(sec);
eattr *x, *y;
u32 p, s;
/* Skip suppressed routes (see bgp_rte_recalculate()) */
- if (pri->u.bgp.suppressed != sec->u.bgp.suppressed)
+ /* LLGR draft - depreference stale routes */
+ if (pri->pflags != sec->pflags)
return 0;
/* RFC 4271 9.1.2.1. Route resolvability test */
- if (rte_resolvable(pri) != rte_resolvable(sec))
- return 0;
-
- /* LLGR draft - depreference stale routes */
- if (rte_stale(pri) != rte_stale(sec))
+ if (rta_resolvable(pri->attrs) != rta_resolvable(sec->attrs))
return 0;
/* Start with local preferences */
@@ -2118,24 +2129,23 @@ bgp_rte_mergable(rte *pri, rte *sec)
static inline int
same_group(rte *r, u32 lpref, u32 lasn)
{
- return (r->pref == lpref) && (bgp_get_neighbor(r) == lasn);
+ return (r->attrs->pref == lpref) && (bgp_get_neighbor(r) == lasn);
}
static inline int
-use_deterministic_med(rte *r)
+use_deterministic_med(struct rte_storage *r)
{
- struct proto *P = r->attrs->src->proto;
- return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med;
+ struct bgp_proto *p = bgp_rte_proto(&r->rte);
+ return p && p->cf->deterministic_med;
}
int
-bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
+bgp_rte_recalculate(rtable_private *table, net *net, rte *new, rte *old, rte *old_best)
{
- rte *r, *s;
rte *key = new ? new : old;
- u32 lpref = key->pref;
+ u32 lpref = key->attrs->pref;
u32 lasn = bgp_get_neighbor(key);
- int old_suppressed = old ? old->u.bgp.suppressed : 0;
+ int old_suppressed = old ? !!(old->pflags & BGP_REF_SUPPRESSED) : 0;
/*
* Proper RFC 4271 path selection is a bit complicated, it cannot be
@@ -2187,11 +2197,11 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
*/
if (new)
- new->u.bgp.suppressed = 1;
+ new->pflags |= BGP_REF_SUPPRESSED;
if (old)
{
- old->u.bgp.suppressed = 1;
+ old->pflags |= BGP_REF_SUPPRESSED;
/* The fast case - replace not best with worse (or remove not best) */
if (old_suppressed && !(new && bgp_rte_better(new, old)))
@@ -2199,13 +2209,13 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
}
/* The default case - find a new best-in-group route */
- r = new; /* new may not be in the list */
- for (s=net->routes; rte_is_valid(s); s=s->next)
- if (use_deterministic_med(s) && same_group(s, lpref, lasn))
+ rte *r = new; /* new may not be in the list */
+ for (struct rte_storage *s = net->routes; rte_is_valid(&s->rte); s = s->next)
+ if (use_deterministic_med(s) && same_group(&s->rte, lpref, lasn))
{
- s->u.bgp.suppressed = 1;
- if (!r || bgp_rte_better(s, r))
- r = s;
+ s->rte.pflags |= BGP_REF_SUPPRESSED;
+ if (!r || bgp_rte_better(&s->rte, r))
+ r = &s->rte;
}
/* Simple case - the last route in group disappears */
@@ -2214,16 +2224,16 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
/* Found if new is mergable with best-in-group */
if (new && (new != r) && bgp_rte_mergable(r, new))
- new->u.bgp.suppressed = 0;
+ new->pflags &= ~BGP_REF_SUPPRESSED;
/* Found all existing routes mergable with best-in-group */
- for (s=net->routes; rte_is_valid(s); s=s->next)
- if (use_deterministic_med(s) && same_group(s, lpref, lasn))
- if ((s != r) && bgp_rte_mergable(r, s))
- s->u.bgp.suppressed = 0;
+ for (struct rte_storage *s = net->routes; rte_is_valid(&s->rte); s = s->next)
+ if (use_deterministic_med(s) && same_group(&s->rte, lpref, lasn))
+ if ((&s->rte != r) && bgp_rte_mergable(r, &s->rte))
+ s->rte.pflags &= ~BGP_REF_SUPPRESSED;
/* Found best-in-group */
- r->u.bgp.suppressed = 0;
+ r->pflags &= ~BGP_REF_SUPPRESSED;
/*
* There are generally two reasons why we have to force
@@ -2255,25 +2265,44 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
return !old_suppressed;
}
-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)
{
- eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
- const struct adata *ad = a ? a->u.ptr : NULL;
- uint flags = a ? a->flags : BAF_PARTIAL;
+ struct bgp_channel *c = SKIP_BACK(struct bgp_channel, stale_feed, req);
- if (ad && int_set_contains(ad, BGP_COMM_NO_LLGR))
- return NULL;
+ do {
+ rte *r = feed[--count];
+ if (r->sender != c->c.in_req.hook)
+ continue;
+
+ /* A new route, do not mark as stale */
+ if (r->stale_cycle == c->c.in_req.hook->stale_set)
+ continue;
+
+ eattr *ea = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
+ const struct adata *ad = ea ? ea->u.ptr : NULL;
+ uint flags = ea ? ea->flags : BAF_PARTIAL;
+
+ rte e0 = *r;
+ e0.flags |= REF_USE_STALE;
- if (ad && int_set_contains(ad, BGP_COMM_LLGR_STALE))
- return r;
+ if (ad && int_set_contains(ad, BGP_COMM_NO_LLGR))
+ rte_import(&c->c.in_req, n, NULL, r->src);
- r = rte_cow_rta(r, pool);
- bgp_set_attr_ptr(&(r->attrs->eattrs), pool, BA_COMMUNITY, flags,
- int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
- r->u.bgp.stale = 1;
+ else if (ad && int_set_contains(ad, BGP_COMM_LLGR_STALE))
+ rte_import(&c->c.in_req, n, &e0, r->src);
- return r;
+ else {
+ rta *a = e0.attrs = rta_do_cow(r->attrs, bgp_linpool);
+
+ bgp_set_attr_ptr(&(a->eattrs), bgp_linpool, BA_COMMUNITY, flags,
+ int_set_add(bgp_linpool, ad, BGP_COMM_LLGR_STALE));
+ e0.pflags |= BGP_REF_STALE;
+
+ rte_import(&c->c.in_req, n, &e0, r->src);
+ lp_flush(bgp_linpool);
+ }
+ } while (count);
}
@@ -2356,22 +2385,22 @@ bgp_get_route_info(rte *e, byte *buf)
eattr *o = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
u32 origas;
- buf += bsprintf(buf, " (%d", e->pref);
+ buf += bsprintf(buf, " (%d", e->attrs->pref);
- if (e->u.bgp.suppressed)
+ if (e->pflags & BGP_REF_SUPPRESSED)
buf += bsprintf(buf, "-");
if (rte_stale(e))
buf += bsprintf(buf, "s");
- u64 metric = bgp_total_aigp_metric(e);
+ u64 metric = bgp_total_aigp_metric(e->attrs);
if (metric < BGP_AIGP_MAX)
{
buf += bsprintf(buf, "/%lu", metric);
}
else if (e->attrs->igp_metric)
{
- if (!rte_resolvable(e))
+ if (!rta_resolvable(e->attrs))
buf += bsprintf(buf, "/-");
else if (e->attrs->igp_metric >= IGP_METRIC_UNKNOWN)
buf += bsprintf(buf, "/?");
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index e4d754b1..b825f778 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -140,6 +140,15 @@ 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);
+static inline void channel_refresh_end_reload(struct channel *c)
+{
+ channel_refresh_end(c);
+
+ if (c->in_table)
+ channel_request_reload(c);
+}
+
/**
* bgp_open - open a BGP instance
* @p: BGP instance
@@ -575,6 +584,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
p->link_addr = p->neigh->iface->llv6->ip;
conn->sk->fast_rx = 0;
+ conn->sk->cork = &rt_cork;
p->conn = conn;
p->last_error_class = 0;
@@ -775,25 +785,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);
+ channel_refresh_begin(&c->c);
break;
case BGP_GRS_ACTIVE:
- rt_refresh_end(c->c.table, &c->c);
- rt_refresh_begin(c->c.table, &c->c);
+ channel_refresh_end(&c->c);
+ channel_refresh_begin(&c->c);
break;
case BGP_GRS_LLGR:
- rt_refresh_begin(c->c.table, &c->c);
- rt_modify_stale(c->c.table, &c->c);
+ channel_refresh_begin(&c->c);
+ bgp_graceful_restart_feed(c);
break;
}
}
else
{
/* Just flush the routes */
- rt_refresh_begin(c->c.table, &c->c);
- rt_refresh_end(c->c.table, &c->c);
+ channel_refresh_begin(&c->c);
+ channel_refresh_end(&c->c);
}
/* Reset bucket and prefix tables */
@@ -811,6 +821,51 @@ 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, &c->stale_feed);
+}
+
+
/**
* bgp_graceful_restart_done - finish active BGP graceful restart
* @c: BGP channel
@@ -833,8 +888,11 @@ bgp_graceful_restart_done(struct bgp_channel *c)
if (!p->gr_active_num)
BGP_TRACE(D_EVENTS, "Neighbor graceful restart done");
+ if (c->stale_feed.hook)
+ rt_stop_export(&c->stale_feed, bgp_graceful_restart_feed_done);
+
tm_stop(c->stale_timer);
- rt_refresh_end(c->c.table, &c->c);
+ channel_refresh_end_reload(&c->c);
}
/**
@@ -876,7 +934,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);
+ bgp_graceful_restart_feed(c);
}
}
else
@@ -913,11 +971,11 @@ bgp_refresh_begin(struct bgp_channel *c)
if (c->load_state == BFS_LOADING)
{ 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);
+ if (c->load_state == BFS_REFRESHING)
+ channel_refresh_end(&c->c);
- if (c->c.in_table)
- rt_refresh_begin(c->c.in_table, &c->c);
+ c->load_state = BFS_REFRESHING;
+ channel_refresh_begin(&c->c);
}
/**
@@ -938,10 +996,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);
-
- if (c->c.in_table)
- rt_prune_sync(c->c.in_table, 0);
+ channel_refresh_end_reload(&c->c);
}
@@ -1408,12 +1463,9 @@ bgp_reload_routes(struct channel *C)
struct bgp_proto *p = (void *) C->proto;
struct bgp_channel *c = (void *) C;
- ASSERT(p->conn && (p->route_refresh || c->c.in_table));
+ ASSERT(p->conn && (p->route_refresh));
- if (c->c.in_table)
- channel_schedule_reload(C);
- else
- bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
+ bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
}
static void
@@ -1677,6 +1729,13 @@ done:
return p->p.proto_state;
}
+struct rte_owner_class bgp_rte_owner_class = {
+ .get_route_info = bgp_get_route_info,
+ .rte_better = bgp_rte_better,
+ .rte_mergable = bgp_rte_mergable,
+ .rte_igp_metric = bgp_rte_igp_metric,
+};
+
static struct proto *
bgp_init(struct proto_config *CF)
{
@@ -1690,10 +1749,9 @@ bgp_init(struct proto_config *CF)
P->reload_routes = bgp_reload_routes;
P->feed_begin = bgp_feed_begin;
P->feed_end = bgp_feed_end;
- 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->sources.class = &bgp_rte_owner_class;
+ P->sources.rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
p->cf = cf;
p->is_internal = (cf->local_as == cf->remote_as);
@@ -1745,17 +1803,19 @@ bgp_channel_start(struct channel *C)
ip_addr src = p->local_ip;
if (c->igp_table_ip4)
- rt_lock_table(c->igp_table_ip4);
+ RT_LOCKED(c->igp_table_ip4, t)
+ rt_lock_table(t);
if (c->igp_table_ip6)
- rt_lock_table(c->igp_table_ip6);
+ RT_LOCKED(c->igp_table_ip6, t)
+ rt_lock_table(t);
c->pool = p->p.pool; // XXXX
bgp_init_bucket_table(c);
bgp_init_prefix_table(c);
if (c->cf->import_table)
- channel_setup_in_table(C);
+ channel_setup_in_table(C, 0);
if (c->cf->export_table)
channel_setup_out_table(C);
@@ -1829,10 +1889,12 @@ bgp_channel_cleanup(struct channel *C)
struct bgp_channel *c = (void *) C;
if (c->igp_table_ip4)
- rt_unlock_table(c->igp_table_ip4);
+ RT_LOCKED(c->igp_table_ip4, t)
+ rt_unlock_table(t);
if (c->igp_table_ip6)
- rt_unlock_table(c->igp_table_ip6);
+ RT_LOCKED(c->igp_table_ip6, t)
+ rt_unlock_table(t);
c->index = 0;
@@ -2556,6 +2618,5 @@ struct protocol proto_bgp = {
.copy_config = bgp_copy_config,
.get_status = bgp_get_status,
.get_attr = bgp_get_attr,
- .get_route_info = bgp_get_route_info,
.show_proto_info = bgp_show_proto_info
};
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index cca4b448..60f93bce 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -200,6 +200,10 @@ struct bgp_channel_config {
#define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */
+/* rte->pflags */
+#define BGP_REF_SUPPRESSED 0x1 /* Used for deterministic MED comparison */
+#define BGP_REF_STALE 0x2 /* Route is LLGR_STATE */
+#define BGP_REF_NOT_STALE 0x4 /* Route is NOT LLGR_STATE */
struct bgp_af_caps {
u32 afi;
@@ -362,6 +366,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 */
@@ -513,11 +518,12 @@ struct rte_source *bgp_find_source(struct bgp_proto *p, u32 path_id);
struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
static inline int
-rte_resolvable(rte *rt)
+rta_resolvable(rta *a)
{
- return rt->attrs->dest == RTD_UNICAST;
+ return a->dest == RTD_UNICAST;
}
+extern struct rte_owner_class bgp_rte_owner_class;
#ifdef LOCAL_DEBUG
#define BGP_FORCE_DEBUG 1
@@ -580,24 +586,31 @@ void bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *bp);
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_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old);
-int bgp_preexport(struct proto *, struct rte **, struct linpool *);
+int bgp_rte_recalculate(rtable_private *table, net *net, rte *new, rte *old, rte *old_best);
+void bgp_rte_modify_stale(struct rt_export_request *, const net_addr *, struct rt_pending_export *, rte **, uint);
+u32 bgp_rte_igp_metric(struct 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 *);
int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
-void bgp_get_route_info(struct rte *, byte *buf);
-int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
+void bgp_get_route_info(struct rte *, byte *);
+int bgp_total_aigp_metric_(rta *a, u64 *metric, const struct adata **ad);
+
+static inline struct bgp_proto *bgp_rte_proto(struct rte *rte)
+{
+ return (rte->src->owner->class == &bgp_rte_owner_class) ?
+ SKIP_BACK(struct bgp_proto, p.sources, rte->src->owner) : NULL;
+}
#define BGP_AIGP_METRIC 1
#define BGP_AIGP_MAX U64(0xffffffffffffffff)
static inline u64
-bgp_total_aigp_metric(rte *r)
+bgp_total_aigp_metric(rta *a)
{
u64 metric = BGP_AIGP_MAX;
const struct adata *ad;
- bgp_total_aigp_metric_(r, &metric, &ad);
+ bgp_total_aigp_metric_(a, &metric, &ad);
return metric;
}
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 99b5d5b4..ea9adb4c 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -971,7 +971,7 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
s->hostentry = rt_get_hostentry(tab, gw, ll, c->c.table);
if (!s->mpls)
- rta_apply_hostentry(a, s->hostentry, NULL);
+ rta_apply_hostentry(a, s->hostentry, NULL, s->pool);
/* With MPLS, hostentry is applied later in bgp_apply_mpls_labels() */
}
@@ -1005,7 +1005,7 @@ bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 *labels, uint lnum)
ms.len = lnum;
memcpy(ms.stack, labels, 4*lnum);
- rta_apply_hostentry(a, s->hostentry, &ms);
+ rta_apply_hostentry(a, s->hostentry, &ms, s->pool);
}
}
@@ -1339,6 +1339,8 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
{
if (path_id != s->last_id)
{
+ rt_unlock_source(s->last_src);
+
s->last_src = rt_get_source(&s->proto->p, path_id);
s->last_id = path_id;
@@ -1349,28 +1351,25 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
if (!a0)
{
/* Route withdraw */
- rte_update3(&s->channel->c, n, NULL, s->last_src);
+ rte_update(&s->channel->c, n, NULL, s->last_src);
return;
}
/* Prepare cached route attributes */
if (s->cached_rta == NULL)
{
- a0->src = s->last_src;
-
/* Workaround for rta_lookup() breaking eattrs */
ea_list *ea = a0->eattrs;
s->cached_rta = rta_lookup(a0);
a0->eattrs = ea;
}
- rta *a = rta_clone(s->cached_rta);
- rte *e = rte_get_temp(a);
+ rte e0 = {
+ .attrs = s->cached_rta,
+ .src = s->last_src,
+ };
- e->pflags = 0;
- e->u.bgp.suppressed = 0;
- e->u.bgp.stale = -1;
- rte_update3(&s->channel->c, n, e, s->last_src);
+ rte_update(&s->channel->c, n, &e0, s->last_src);
}
static void
@@ -2424,6 +2423,7 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
s->last_id = 0;
s->last_src = s->proto->p.main_source;
+ rt_lock_source(s->last_src);
/*
* IPv4 BGP and MP-BGP may be used together in one update, therefore we do not
@@ -2440,6 +2440,7 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
a->scope = SCOPE_UNIVERSE;
a->from = s->proto->remote_ip;
a->eattrs = ea;
+ a->pref = c->c.preference;
c->desc->decode_next_hop(s, nh, nh_len, a);
bgp_finish_attrs(s, a);
@@ -2453,6 +2454,8 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
rta_free(s->cached_rta);
s->cached_rta = NULL;
+
+ rt_unlock_source(s->last_src);
}
static void
diff --git a/proto/mrt/mrt.c b/proto/mrt/mrt.c
index 8d97c860..e12f7743 100644
--- a/proto/mrt/mrt.c
+++ b/proto/mrt/mrt.c
@@ -113,13 +113,13 @@ mrt_buffer_flush(buffer *b)
}
#define MRT_DEFINE_TYPE(S, T) \
- static inline void mrt_put_##S##_(buffer *b, T x) \
+ UNUSED static inline void mrt_put_##S##_(buffer *b, T x) \
{ \
put_##S(b->pos, x); \
b->pos += sizeof(T); \
} \
\
- static inline void mrt_put_##S(buffer *b, T x) \
+ UNUSED static inline void mrt_put_##S(buffer *b, T x) \
{ \
mrt_buffer_need(b, sizeof(T)); \
put_##S(b->pos, x); \
@@ -228,7 +228,7 @@ mrt_next_table_(rtable *tab, rtable *tab_ptr, const char *pattern)
NODE_VALID(tn);
tn = tn->next)
{
- tab = SKIP_BACK(struct rtable, n, tn);
+ tab = SKIP_BACK(rtable, n, tn);
if (patmatch(pattern, tab->name) &&
((tab->addr_type == NET_IP4) || (tab->addr_type == NET_IP6)))
return tab;
@@ -243,13 +243,21 @@ mrt_next_table(struct mrt_table_dump_state *s)
rtable *tab = mrt_next_table_(s->table, s->table_ptr, s->table_expr);
if (s->table)
- rt_unlock_table(s->table);
+ {
+ RT_LOCK(s->table);
+ rt_unlock_table(RT_PRIV(s->table));
+ RT_UNLOCK(s->table);
+ }
s->table = tab;
s->ipv4 = tab ? (tab->addr_type == NET_IP4) : 0;
if (s->table)
- rt_lock_table(s->table);
+ {
+ RT_LOCK(s->table);
+ rt_lock_table(RT_PRIV(s->table));
+ RT_UNLOCK(s->table);
+ }
return s->table;
}
@@ -460,7 +468,7 @@ mrt_rib_table_entry_bgp_attrs(struct mrt_table_dump_state *s, rte *r)
return;
fail:
- mrt_log(s, "Attribute list too long for %N", r->net->n.addr);
+ mrt_log(s, "Attribute list too long for %N", r->net);
}
#endif
@@ -472,9 +480,9 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
#ifdef CONFIG_BGP
/* Find peer index */
- if (r->attrs->src->proto->proto == &proto_bgp)
+ struct bgp_proto *p = bgp_rte_proto(r);
+ if (p)
{
- struct bgp_proto *p = (void *) r->attrs->src->proto;
struct mrt_peer_entry *n =
HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip);
@@ -488,7 +496,7 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
/* Path Identifier */
if (s->add_path)
- mrt_put_u32(b, r->attrs->src->private_id);
+ mrt_put_u32(b, r->src->private_id);
/* Route Attributes */
mrt_put_u16(b, 0);
@@ -512,26 +520,21 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
mrt_init_message(&s->buf, MRT_TABLE_DUMP_V2, subtype);
mrt_rib_table_header(s, n->n.addr);
- rte *rt, *rt0;
- for (rt0 = n->routes; rt = rt0; rt0 = rt0->next)
+ for (struct rte_storage *rt, *rt0 = n->routes; rt = rt0; rt0 = rt0->next)
{
- if (rte_is_filtered(rt))
+ if (rte_is_filtered(&rt->rte))
continue;
/* Skip routes that should be reported in the other phase */
- if (!s->always_add_path && (!rt->attrs->src->private_id != !s->add_path))
+ if (!s->always_add_path && (!rt->rte.src->private_id != !s->add_path))
{
s->want_add_path = 1;
continue;
}
- rte_make_tmp_attrs(&rt, s->linpool, NULL);
-
- if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
- mrt_rib_table_entry(s, rt);
-
- if (rt != rt0)
- rte_free(rt);
+ rte e = rt->rte;
+ if (f_run(s->filter, &e, s->linpool, 0) <= F_ACCEPT)
+ mrt_rib_table_entry(s, &e);
lp_flush(s->linpool);
}
@@ -558,10 +561,11 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
static struct mrt_table_dump_state *
mrt_table_dump_init(pool *pp)
{
- pool *pool = rp_new(pp, "MRT Table Dump");
+ pool *pool = rp_new(pp, &main_birdloop, "MRT Table Dump");
struct mrt_table_dump_state *s = mb_allocz(pool, sizeof(struct mrt_table_dump_state));
s->pool = pool;
+ s->parent = pp;
s->linpool = lp_new(pool, 4080);
s->peer_lp = lp_new(pool, 4080);
mrt_buffer_init(&s->buf, pool, 2 * MRT_ATTR_BUFFER_SIZE);
@@ -578,18 +582,27 @@ mrt_table_dump_init(pool *pp)
static void
mrt_table_dump_free(struct mrt_table_dump_state *s)
{
- if (s->table_open)
- FIB_ITERATE_UNLINK(&s->fit, &s->table->fib);
-
if (s->table)
- rt_unlock_table(s->table);
+ {
+ RT_LOCK(s->table);
+
+ if (s->table_open)
+ FIB_ITERATE_UNLINK(&s->fit, &RT_PRIV(s->table)->fib);
+
+ rt_unlock_table(RT_PRIV(s->table));
+ RT_UNLOCK(s->table);
+ }
if (s->table_ptr)
- rt_unlock_table(s->table_ptr);
+ {
+ RT_LOCK(s->table_ptr);
+ rt_unlock_table(RT_PRIV(s->table_ptr));
+ RT_UNLOCK(s->table_ptr);
+ }
config_del_obstacle(s->config);
- rfree(s->pool);
+ rp_free(s->pool, s->parent);
}
@@ -601,8 +614,14 @@ mrt_table_dump_step(struct mrt_table_dump_state *s)
s->max = 2048;
s->bws = &bws;
+ rtable_private *tab;
+
if (s->table_open)
+ {
+ RT_LOCK(s->table);
+ tab = RT_PRIV(s->table);
goto step;
+ }
while (mrt_next_table(s))
{
@@ -611,15 +630,18 @@ mrt_table_dump_step(struct mrt_table_dump_state *s)
mrt_peer_table_dump(s);
- FIB_ITERATE_INIT(&s->fit, &s->table->fib);
+ RT_LOCK(s->table);
+ tab = RT_PRIV(s->table);
+ FIB_ITERATE_INIT(&s->fit, &tab->fib);
s->table_open = 1;
step:
- FIB_ITERATE_START(&s->table->fib, &s->fit, net, n)
+ FIB_ITERATE_START(&tab->fib, &s->fit, net, n)
{
if (s->max < 0)
{
FIB_ITERATE_PUT(&s->fit);
+ RT_UNLOCK(s->table);
return 0;
}
@@ -639,6 +661,7 @@ mrt_table_dump_step(struct mrt_table_dump_state *s)
mrt_peer_table_flush(s);
}
+ RT_UNLOCK(s->table);
return 1;
}
@@ -666,7 +689,11 @@ mrt_timer(timer *t)
s->always_add_path = cf->always_add_path;
if (s->table_ptr)
- rt_lock_table(s->table_ptr);
+ {
+ RT_LOCK(s->table_ptr);
+ rt_lock_table(RT_PRIV(s->table_ptr));
+ RT_UNLOCK(s->table_ptr);
+ }
p->table_dump = s;
ev_schedule(p->event);
@@ -739,7 +766,11 @@ mrt_dump_cmd(struct mrt_dump_data *d)
s->filename = d->filename;
if (s->table_ptr)
- rt_lock_table(s->table_ptr);
+ {
+ RT_LOCK(s->table_ptr);
+ rt_lock_table(RT_PRIV(s->table_ptr));
+ RT_UNLOCK(s->table_ptr);
+ }
this_cli->cont = mrt_dump_cont;
this_cli->cleanup = mrt_dump_cleanup;
diff --git a/proto/mrt/mrt.h b/proto/mrt/mrt.h
index 4ff94c12..2e616f6f 100644
--- a/proto/mrt/mrt.h
+++ b/proto/mrt/mrt.h
@@ -40,7 +40,7 @@ struct mrt_proto {
struct mrt_dump_data {
const char *table_expr;
- struct rtable *table_ptr;
+ rtable *table_ptr;
const struct filter *filter;
const char *filename;
};
@@ -60,20 +60,21 @@ struct mrt_table_dump_state {
/* Configuration information */
const char *table_expr; /* Wildcard for table name (or NULL) */
- struct rtable *table_ptr; /* Explicit table (or NULL) */
+ rtable *table_ptr; /* Explicit table (or NULL) */
const struct filter *filter; /* Optional filter */
const char *filename; /* Filename pattern */
int always_add_path; /* Always use *_ADDPATH message subtypes */
/* Allocated by mrt_table_dump_init() */
pool *pool; /* Pool for table dump */
+ pool *parent; /* Parent pool for cleanup */
linpool *linpool; /* Temporary linear pool */
linpool *peer_lp; /* Linear pool for peer entries in peer_hash */
buffer buf; /* Buffer for MRT messages */
HASH(struct mrt_peer_entry) peer_hash; /* Hash for peers to find the index */
- struct rtable *table; /* Processed table, NULL initially */
+ rtable *table; /* Processed table, NULL initially */
struct fib_iterator fit; /* Iterator in processed table */
int table_open; /* Whether iterator is linked */
diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c
index f38b8210..2aae341a 100644
--- a/proto/ospf/iface.c
+++ b/proto/ospf/iface.c
@@ -311,7 +311,7 @@ ospf_iface_remove(struct ospf_iface *ifa)
ospf_iface_sm(ifa, ISM_DOWN);
rem_node(NODE ifa);
- rfree(ifa->pool);
+ rp_free(ifa->pool, p->p.pool);
}
void
@@ -522,7 +522,10 @@ static inline void
add_nbma_node(struct ospf_iface *ifa, struct nbma_node *src, int found)
{
struct nbma_node *n = mb_alloc(ifa->pool, sizeof(struct nbma_node));
+
+ n->n = (node) {};
add_tail(&ifa->nbma_list, NODE n);
+
n->ip = src->ip;
n->eligible = src->eligible;
n->found = found;
@@ -564,7 +567,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
OSPF_TRACE(D_EVENTS, "Adding interface %s (%N) to area %R",
iface->name, &addr->prefix, oa->areaid);
- pool = rp_new(p->p.pool, "OSPF Interface");
+ pool = rp_new(p->p.pool, p->p.loop, "OSPF Interface");
ifa = mb_allocz(pool, sizeof(struct ospf_iface));
ifa->iface = iface;
ifa->addr = addr;
@@ -684,7 +687,7 @@ ospf_iface_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip)
/* Vlink ifname is stored just after the ospf_iface structure */
- pool = rp_new(p->p.pool, "OSPF Vlink");
+ pool = rp_new(p->p.pool, p->p.loop, "OSPF Vlink");
ifa = mb_allocz(pool, sizeof(struct ospf_iface) + 16);
ifa->oa = p->backbone;
ifa->cf = ip;
diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c
index ca369819..48e38556 100644
--- a/proto/ospf/neighbor.c
+++ b/proto/ospf/neighbor.c
@@ -80,7 +80,7 @@ struct ospf_neighbor *
ospf_neighbor_new(struct ospf_iface *ifa)
{
struct ospf_proto *p = ifa->oa->po;
- struct pool *pool = rp_new(p->p.pool, "OSPF Neighbor");
+ struct pool *pool = rp_new(p->p.pool, p->p.loop, "OSPF Neighbor");
struct ospf_neighbor *n = mb_allocz(pool, sizeof(struct ospf_neighbor));
n->pool = pool;
@@ -120,7 +120,7 @@ ospf_neigh_down(struct ospf_neighbor *n)
s_get(&(n->dbsi));
release_lsrtl(p, n);
rem_node(NODE n);
- rfree(n->pool);
+ rp_free(n->pool, p->p.pool);
OSPF_TRACE(D_EVENTS, "Neighbor %R on %s removed", rid, ifa->ifname);
}
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index ba8c2e2b..16774df6 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -107,12 +107,10 @@
#include <stdlib.h>
#include "ospf.h"
-static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool);
-static void ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
-static void ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool);
+static int ospf_preexport(struct channel *C, rte *new);
static void ospf_reload_routes(struct channel *C);
static int ospf_rte_better(struct rte *new, struct rte *old);
-static int ospf_rte_same(struct rte *new, struct rte *old);
+static u32 ospf_rte_igp_metric(struct rte *rt);
static void ospf_disp(timer *timer);
@@ -378,10 +376,8 @@ ospf_init(struct proto_config *CF)
P->reload_routes = ospf_reload_routes;
P->feed_begin = ospf_feed_begin;
P->feed_end = ospf_feed_end;
- P->make_tmp_attrs = ospf_make_tmp_attrs;
- P->store_tmp_attrs = ospf_store_tmp_attrs;
- P->rte_better = ospf_rte_better;
- P->rte_same = ospf_rte_same;
+
+ P->sources.class = &ospf_rte_owner_class;
return P;
}
@@ -390,7 +386,9 @@ ospf_init(struct proto_config *CF)
static int
ospf_rte_better(struct rte *new, struct rte *old)
{
- if (new->u.ospf.metric1 == LSINFINITY)
+ u32 new_metric1 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
+
+ if (new_metric1 == LSINFINITY)
return 0;
if(new->attrs->source < old->attrs->source) return 1;
@@ -398,27 +396,27 @@ ospf_rte_better(struct rte *new, struct rte *old)
if(new->attrs->source == RTS_OSPF_EXT2)
{
- if(new->u.ospf.metric2 < old->u.ospf.metric2) return 1;
- if(new->u.ospf.metric2 > old->u.ospf.metric2) return 0;
+ u32 old_metric2 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
+ u32 new_metric2 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
+ if(new_metric2 < old_metric2) return 1;
+ if(new_metric2 > old_metric2) return 0;
}
- if (new->u.ospf.metric1 < old->u.ospf.metric1)
+ u32 old_metric1 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
+ if (new_metric1 < old_metric1)
return 1;
return 0; /* Old is shorter or same */
}
-static int
-ospf_rte_same(struct rte *new, struct rte *old)
+static u32
+ospf_rte_igp_metric(struct rte *rt)
{
- /* new->attrs == old->attrs always */
- return
- new->u.ospf.metric1 == old->u.ospf.metric1 &&
- new->u.ospf.metric2 == old->u.ospf.metric2 &&
- new->u.ospf.tag == old->u.ospf.tag &&
- new->u.ospf.router_id == old->u.ospf.router_id;
-}
+ if (rt->attrs->source == RTS_OSPF_EXT2)
+ return IGP_METRIC_UNKNOWN;
+ return ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
+}
void
ospf_schedule_rtcalc(struct ospf_proto *p)
@@ -484,14 +482,13 @@ ospf_disp(timer * timer)
* import to the filters.
*/
static int
-ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
+ospf_preexport(struct channel *c, rte *e)
{
- struct ospf_proto *p = (struct ospf_proto *) P;
+ struct ospf_proto *p = (struct ospf_proto *) c->proto;
struct ospf_area *oa = ospf_main_area(p);
- rte *e = *new;
/* Reject our own routes */
- if (e->attrs->src->proto == P)
+ if (e->sender == c->in_req.hook)
return -1;
/* Do not export routes to stub areas */
@@ -501,26 +498,6 @@ ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
return 0;
}
-static void
-ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool)
-{
- rte_init_tmp_attrs(rt, pool, 4);
- rte_make_tmp_attr(rt, EA_OSPF_METRIC1, EAF_TYPE_INT, rt->u.ospf.metric1);
- rte_make_tmp_attr(rt, EA_OSPF_METRIC2, EAF_TYPE_INT, rt->u.ospf.metric2);
- rte_make_tmp_attr(rt, EA_OSPF_TAG, EAF_TYPE_INT, rt->u.ospf.tag);
- rte_make_tmp_attr(rt, EA_OSPF_ROUTER_ID, EAF_TYPE_ROUTER_ID, rt->u.ospf.router_id);
-}
-
-static void
-ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool)
-{
- rte_init_tmp_attrs(rt, pool, 4);
- rt->u.ospf.metric1 = rte_store_tmp_attr(rt, EA_OSPF_METRIC1);
- rt->u.ospf.metric2 = rte_store_tmp_attr(rt, EA_OSPF_METRIC2);
- rt->u.ospf.tag = rte_store_tmp_attr(rt, EA_OSPF_TAG);
- rt->u.ospf.router_id = rte_store_tmp_attr(rt, EA_OSPF_ROUTER_ID);
-}
-
/**
* ospf_shutdown - Finish of OSPF instance
* @P: OSPF protocol instance
@@ -558,6 +535,9 @@ ospf_shutdown(struct proto *P)
}
FIB_WALK_END;
+ if (tm_active(p->disp_timer))
+ tm_stop(p->disp_timer);
+
return PS_DOWN;
}
@@ -607,16 +587,20 @@ ospf_get_route_info(rte * rte, byte * buf)
}
buf += bsprintf(buf, " %s", type);
- buf += bsprintf(buf, " (%d/%d", rte->pref, rte->u.ospf.metric1);
+ buf += bsprintf(buf, " (%d/%d", rte->attrs->pref, ea_get_int(rte->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY));
if (rte->attrs->source == RTS_OSPF_EXT2)
- buf += bsprintf(buf, "/%d", rte->u.ospf.metric2);
+ buf += bsprintf(buf, "/%d", ea_get_int(rte->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY));
buf += bsprintf(buf, ")");
- if ((rte->attrs->source == RTS_OSPF_EXT1 || rte->attrs->source == RTS_OSPF_EXT2) && rte->u.ospf.tag)
+ if (rte->attrs->source == RTS_OSPF_EXT1 || rte->attrs->source == RTS_OSPF_EXT2)
{
- buf += bsprintf(buf, " [%x]", rte->u.ospf.tag);
+ eattr *ea = ea_find(rte->attrs->eattrs, EA_OSPF_TAG);
+ if (ea && (ea->u.data > 0))
+ buf += bsprintf(buf, " [%x]", ea->u.data);
}
- if (rte->u.ospf.router_id)
- buf += bsprintf(buf, " [%R]", rte->u.ospf.router_id);
+
+ eattr *ea = ea_find(rte->attrs->eattrs, EA_OSPF_ROUTER_ID);
+ if (ea)
+ buf += bsprintf(buf, " [%R]", ea->u.data);
}
static int
@@ -1533,6 +1517,12 @@ ospf_sh_lsadb(struct lsadb_show_data *ld)
}
+struct rte_owner_class ospf_rte_owner_class = {
+ .get_route_info = ospf_get_route_info,
+ .rte_better = ospf_rte_better,
+ .rte_igp_metric = ospf_rte_igp_metric,
+};
+
struct protocol proto_ospf = {
.name = "OSPF",
.template = "ospf%d",
@@ -1548,5 +1538,4 @@ struct protocol proto_ospf = {
.reconfigure = ospf_reconfigure,
.get_status = ospf_get_status,
.get_attr = ospf_get_attr,
- .get_route_info = ospf_get_route_info
};
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index 3e704ae8..a5f83e79 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -1007,6 +1007,8 @@ void ospf_sh_state(struct proto *P, int verbose, int reachable);
void ospf_sh_lsadb(struct lsadb_show_data *ld);
+extern struct rte_owner_class ospf_rte_owner_class;
+
/* iface.c */
void ospf_iface_chstate(struct ospf_iface *ifa, u8 state);
void ospf_iface_sm(struct ospf_iface *ifa, int event);
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index faee49dc..3e208023 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -144,7 +144,7 @@ orta_compare(const struct ospf_proto *p, const orta *new, const orta *old)
{
int r;
- if (old->type == RTS_DUMMY)
+ if (!old->type)
return 1;
/* Prefer intra-area to inter-area to externals */
@@ -195,7 +195,7 @@ orta_compare_asbr(const struct ospf_proto *p, const orta *new, const orta *old)
{
int r;
- if (old->type == RTS_DUMMY)
+ if (!old->type)
return 1;
if (!p->rfc1583)
@@ -225,7 +225,7 @@ orta_compare_ext(const struct ospf_proto *p, const orta *new, const orta *old)
{
int r;
- if (old->type == RTS_DUMMY)
+ if (!old->type)
return 1;
/* 16.4 (6a) - prefer routes with lower type */
@@ -2053,36 +2053,61 @@ again1:
if (nf->n.type) /* Add the route */
{
rta a0 = {
- .src = p->p.main_source,
.source = nf->n.type,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
.nh = *(nf->n.nhs),
+ .pref = p->p.main_channel->preference,
};
if (reload || ort_changed(nf, &a0))
{
- rta *a = rta_lookup(&a0);
- rte *e = rte_get_temp(a);
+ a0.eattrs = alloca(sizeof(ea_list) + 4 * sizeof(eattr));
+ memset(a0.eattrs, 0, sizeof(ea_list));
- rta_free(nf->old_rta);
- nf->old_rta = rta_clone(a);
- e->u.ospf.metric1 = nf->old_metric1 = nf->n.metric1;
- e->u.ospf.metric2 = nf->old_metric2 = nf->n.metric2;
- e->u.ospf.tag = nf->old_tag = nf->n.tag;
- e->u.ospf.router_id = nf->old_rid = nf->n.rid;
- e->pflags = EA_ID_FLAG(EA_OSPF_METRIC1) | EA_ID_FLAG(EA_OSPF_ROUTER_ID);
+ nf->old_metric1 = nf->n.metric1;
+ nf->old_metric2 = nf->n.metric2;
+ nf->old_tag = nf->n.tag;
+ nf->old_rid = nf->n.rid;
+
+ a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
+ .id = EA_OSPF_METRIC1,
+ .type = EAF_TYPE_INT,
+ .u.data = nf->n.metric1,
+ };
if (nf->n.type == RTS_OSPF_EXT2)
- e->pflags |= EA_ID_FLAG(EA_OSPF_METRIC2);
+ a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
+ .id = EA_OSPF_METRIC2,
+ .type = EAF_TYPE_INT,
+ .u.data = nf->n.metric2,
+ };
- /* Perhaps onfly if tag is non-zero? */
if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
- e->pflags |= EA_ID_FLAG(EA_OSPF_TAG);
+ a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
+ .id = EA_OSPF_TAG,
+ .type = EAF_TYPE_INT,
+ .u.data = nf->n.tag,
+ };
+
+ a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
+ .id = EA_OSPF_ROUTER_ID,
+ .type = EAF_TYPE_ROUTER_ID,
+ .u.data = nf->n.rid,
+ };
+
+ rta_free(nf->old_rta);
+ nf->old_rta = rta_lookup(&a0);
+
+ rte e0 = {
+ .attrs = nf->old_rta,
+ .src = p->p.main_source,
+ };
DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
- rte_update(&p->p, nf->fn.addr, e);
+
+ rte_update(p->p.main_channel, nf->fn.addr, &e0, p->p.main_source);
}
}
else if (nf->old_rta)
@@ -2091,7 +2116,7 @@ again1:
rta_free(nf->old_rta);
nf->old_rta = NULL;
- rte_update(&p->p, nf->fn.addr, NULL);
+ rte_update(p->p.main_channel, nf->fn.addr, NULL, p->p.main_source);
}
/* Remove unused rt entry, some special entries are persistent */
@@ -2107,7 +2132,6 @@ again1:
}
FIB_ITERATE_END;
-
WALK_LIST(oa, p->area_list)
{
/* Cleanup ASBR hash tables */
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index 52c2a0ce..bb88d20a 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -1300,7 +1300,7 @@ find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa)
}
void
-ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED)
+ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, const net_addr *n, rte *new, const rte *old UNUSED)
{
struct ospf_proto *p = (struct ospf_proto *) P;
struct ospf_area *oa = NULL; /* non-NULL for NSSA-LSA */
@@ -1319,7 +1319,7 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
if (!new)
{
- nf = fib_find(&p->rtf, n->n.addr);
+ nf = fib_find(&p->rtf, n);
if (!nf || !nf->external_rte)
return;
@@ -1346,14 +1346,14 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
if (m1 > LSINFINITY)
{
log(L_WARN "%s: Invalid ospf_metric1 value %u for route %N",
- p->p.name, m1, n->n.addr);
+ p->p.name, m1, n);
m1 = LSINFINITY;
}
if (m2 > LSINFINITY)
{
log(L_WARN "%s: Invalid ospf_metric2 value %u for route %N",
- p->p.name, m2, n->n.addr);
+ p->p.name, m2, n);
m2 = LSINFINITY;
}
@@ -1377,12 +1377,12 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
if (ipa_zero(fwd))
{
log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %N",
- p->p.name, n->n.addr);
+ p->p.name, n);
return;
}
}
- nf = fib_get(&p->rtf, n->n.addr);
+ nf = fib_get(&p->rtf, n);
ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1, p->vpn_pe);
nf->external_rte = 1;
}
diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h
index 535d1f1b..c36d0b50 100644
--- a/proto/ospf/topology.h
+++ b/proto/ospf/topology.h
@@ -200,7 +200,7 @@ void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 d
void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit, int dn);
void ospf_originate_gr_lsa(struct ospf_proto *p, struct ospf_iface *ifa);
-void ospf_rt_notify(struct proto *P, struct channel *ch, net *n, rte *new, rte *old);
+void ospf_rt_notify(struct proto *P, struct channel *ch, const net_addr *n, rte *new, const rte *old);
void ospf_update_topology(struct ospf_proto *p);
struct top_hash_entry *ospf_hash_find(struct top_graph *, u32 domain, u32 lsa, u32 rtr, u32 type);
diff --git a/proto/perf/perf.c b/proto/perf/perf.c
index ba401a8a..aa688d88 100644
--- a/proto/perf/perf.c
+++ b/proto/perf/perf.c
@@ -143,10 +143,10 @@ perf_loop(void *data)
if (!p->attrs_per_rte || !(i % p->attrs_per_rte)) {
struct rta a0 = {
- .src = p->p.main_source,
.source = RTS_PERF,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
+ .pref = p->p.main_channel->preference,
.nh.iface = p->ifa->iface,
.nh.gw = gw,
.nh.weight = 1,
@@ -160,18 +160,17 @@ perf_loop(void *data)
clock_gettime(CLOCK_MONOTONIC, &ts_generated);
- for (uint i=0; i<N; i++) {
- rte *e = rte_get_temp(p->data[i].a);
- e->pflags = 0;
-
- rte_update(P, &(p->data[i].net), e);
+ for (uint i=0; i<N; i++)
+ {
+ rte e0 = { .attrs = p->data[i].a, .src = P->main_source, };
+ rte_update(P->main_channel, &(p->data[i].net), &e0, P->main_source);
}
clock_gettime(CLOCK_MONOTONIC, &ts_update);
if (!p->keep)
for (uint i=0; i<N; i++)
- rte_update(P, &(p->data[i].net), NULL);
+ rte_update(P->main_channel, &(p->data[i].net), NULL, P->main_source);
clock_gettime(CLOCK_MONOTONIC, &ts_withdraw);
@@ -199,12 +198,14 @@ perf_loop(void *data)
p->exp++;
}
- rt_schedule_prune(P->main_channel->table);
+ RT_LOCK(P->main_channel->table);
+ rt_schedule_prune(RT_PRIV(P->main_channel->table));
+ RT_UNLOCK(P->main_channel->table);
ev_schedule(p->loop);
}
static void
-perf_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net UNUSED, struct rte *new UNUSED, struct rte *old UNUSED)
+perf_rt_notify(struct proto *P, struct channel *c UNUSED, const net_addr *net UNUSED, struct rte *new UNUSED, const struct rte *old UNUSED)
{
struct perf_proto *p = (struct perf_proto *) P;
p->exp++;
diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y
index 1202c169..fc08445f 100644
--- a/proto/pipe/config.Y
+++ b/proto/pipe/config.Y
@@ -16,7 +16,7 @@ CF_DEFINES
CF_DECLS
-CF_KEYWORDS(PIPE, PEER, TABLE)
+CF_KEYWORDS(PIPE, PEER, TABLE, MAX, GENERATION)
CF_GRAMMAR
@@ -25,6 +25,8 @@ proto: pipe_proto '}' { this_channel = NULL; } ;
pipe_proto_start: proto_start PIPE
{
this_proto = proto_config_new(&proto_pipe, $1);
+ this_proto->loop_order = DOMAIN_ORDER(proto);
+ PIPE_CFG->max_generation = 16;
}
proto_name
{
@@ -41,6 +43,10 @@ pipe_proto:
| pipe_proto proto_item ';'
| pipe_proto channel_item_ ';'
| pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; }
+ | pipe_proto MAX GENERATION expr ';' {
+ if (($4 < 1) || ($4 > 254)) cf_error("Max generation must be in range 1..254, got %u", $4);
+ PIPE_CFG->max_generation = $4;
+ }
;
CF_CODE
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 3532f114..270f7b92 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -43,67 +43,58 @@
#include "pipe.h"
+#ifdef CONFIG_BGP
+#include "proto/bgp/bgp.h"
+#endif
+
static void
-pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *old)
+pipe_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *n, rte *new, const rte *old)
{
struct pipe_proto *p = (void *) P;
struct channel *dst = (src_ch == p->pri) ? p->sec : p->pri;
- struct rte_src *src;
-
- rte *e;
- rta *a;
if (!new && !old)
return;
- if (dst->table->pipe_busy)
- {
- log(L_ERR "Pipe loop detected when sending %N to table %s",
- n->n.addr, dst->table->name);
- return;
- }
-
if (new)
{
- a = alloca(rta_size(new->attrs));
+ rta *a = alloca(rta_size(new->attrs));
memcpy(a, new->attrs, rta_size(new->attrs));
- a->aflags = 0;
+ a->cached = 0;
a->hostentry = NULL;
- e = rte_get_temp(a);
- e->pflags = 0;
-
- /* Copy protocol specific embedded attributes. */
- memcpy(&(e->u), &(new->u), sizeof(e->u));
- e->pref = new->pref;
- e->pflags = new->pflags;
-#ifdef CONFIG_BGP
- /* Hack to cleanup cached value */
- if (e->attrs->src->proto->proto == &proto_bgp)
- e->u.bgp.stale = -1;
-#endif
+ rte e0 = {
+ .attrs = a,
+ .src = new->src,
+ .generation = new->generation + 1,
+ };
- src = a->src;
+ rte_update(dst, n, &e0, new->src);
}
else
- {
- e = NULL;
- src = old->attrs->src;
- }
-
- src_ch->table->pipe_busy = 1;
- rte_update2(dst, n->n.addr, e, src);
- src_ch->table->pipe_busy = 0;
+ rte_update(dst, n, NULL, old->src);
}
static int
-pipe_preexport(struct proto *P, rte **ee, struct linpool *p UNUSED)
+pipe_preexport(struct channel *c, rte *e)
{
- struct proto *pp = (*ee)->sender->proto;
+ struct pipe_proto *p = (void *) c->proto;
+
+ /* Avoid direct loopbacks */
+ if (e->sender == c->in_req.hook)
+ return -1;
- if (pp == P)
- return -1; /* Avoid local loops automatically */
+ /* Indirection check */
+ uint max_generation = ((struct pipe_config *) p->p.cf)->max_generation;
+ 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->owner->name, e->src->private_id, e->src->global_id);
+
+ return -1;
+ }
return 0;
}
@@ -117,6 +108,23 @@ pipe_reload_routes(struct channel *C)
channel_request_feeding((C == p->pri) ? p->sec : p->pri);
}
+static void
+pipe_feed_begin(struct channel *C, int refeeding UNUSED)
+{
+ struct pipe_proto *p = (void *) C->proto;
+ struct channel *dst = (C == p->pri) ? p->sec : p->pri;
+
+ channel_refresh_begin(dst);
+}
+
+static void
+pipe_feed_end(struct channel *C)
+{
+ struct pipe_proto *p = (void *) C->proto;
+ struct channel *dst = (C == p->pri) ? p->sec : p->pri;
+
+ channel_refresh_end(dst);
+}
static void
pipe_postconfig(struct proto_config *CF)
@@ -187,6 +195,10 @@ pipe_init(struct proto_config *CF)
P->rt_notify = pipe_rt_notify;
P->preexport = pipe_preexport;
P->reload_routes = pipe_reload_routes;
+ P->feed_begin = pipe_feed_begin;
+ P->feed_end = pipe_feed_end;
+
+ p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
pipe_configure_channels(p, cf);
@@ -219,8 +231,18 @@ pipe_get_status(struct proto *P, byte *buf)
static void
pipe_show_stats(struct pipe_proto *p)
{
- struct proto_stats *s1 = &p->pri->stats;
- struct proto_stats *s2 = &p->sec->stats;
+ struct channel_import_stats *s1i = &p->pri->import_stats;
+ struct channel_export_stats *s1e = &p->pri->export_stats;
+ struct channel_import_stats *s2i = &p->sec->import_stats;
+ struct channel_export_stats *s2e = &p->sec->export_stats;
+
+ struct rt_import_stats *rs1i = p->pri->in_req.hook ? &p->pri->in_req.hook->stats : NULL;
+ struct rt_export_stats *rs1e = p->pri->out_req.hook ? &p->pri->out_req.hook->stats : NULL;
+ struct rt_import_stats *rs2i = p->sec->in_req.hook ? &p->sec->in_req.hook->stats : NULL;
+ struct rt_export_stats *rs2e = p->sec->out_req.hook ? &p->sec->out_req.hook->stats : NULL;
+
+ u32 pri_routes = p->pri->in_limit.count;
+ u32 sec_routes = p->sec->in_limit.count;
/*
* Pipe stats (as anything related to pipes) are a bit tricky. There
@@ -244,24 +266,22 @@ pipe_show_stats(struct pipe_proto *p)
*/
cli_msg(-1006, " Routes: %u imported, %u exported",
- s1->imp_routes, s2->imp_routes);
+ pri_routes, sec_routes);
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
- s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
- s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
+ rs2e->updates_received, s2e->updates_rejected + s1i->updates_invalid,
+ s2e->updates_filtered, rs1i->updates_ignored, rs1i->updates_accepted);
cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
- s2->exp_withdraws_received, s1->imp_withdraws_invalid,
- s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
+ rs2e->withdraws_received, s1i->withdraws_invalid,
+ rs1i->withdraws_ignored, rs1i->withdraws_accepted);
cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
- s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
- s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
+ rs1e->updates_received, s1e->updates_rejected + s2i->updates_invalid,
+ s1e->updates_filtered, rs2i->updates_ignored, rs2i->updates_accepted);
cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
- s1->exp_withdraws_received, s2->imp_withdraws_invalid,
- s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
+ rs1e->withdraws_received, s2i->withdraws_invalid,
+ rs2i->withdraws_ignored, rs2i->withdraws_accepted);
}
-static const char *pipe_feed_state[] = { [ES_DOWN] = "down", [ES_FEEDING] = "feed", [ES_READY] = "up" };
-
static void
pipe_show_proto_info(struct proto *P)
{
@@ -270,13 +290,17 @@ pipe_show_proto_info(struct proto *P)
cli_msg(-1006, " Channel %s", "main");
cli_msg(-1006, " Table: %s", p->pri->table->name);
cli_msg(-1006, " Peer table: %s", p->sec->table->name);
- cli_msg(-1006, " Import state: %s", pipe_feed_state[p->sec->export_state]);
- cli_msg(-1006, " Export state: %s", pipe_feed_state[p->pri->export_state]);
+ cli_msg(-1006, " Import state: %s", rt_export_state_name(rt_export_get_state(p->sec->out_req.hook)));
+ cli_msg(-1006, " Export state: %s", rt_export_state_name(rt_export_get_state(p->pri->out_req.hook)));
cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter));
cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter));
- channel_show_limit(&p->pri->in_limit, "Import limit:");
- channel_show_limit(&p->sec->in_limit, "Export limit:");
+
+
+ channel_show_limit(&p->pri->in_limit, "Import limit:",
+ (p->pri->limit_active & (1 << PLD_IN)), p->pri->limit_actions[PLD_IN]);
+ channel_show_limit(&p->sec->in_limit, "Export limit:",
+ (p->sec->limit_active & (1 << PLD_IN)), p->sec->limit_actions[PLD_IN]);
if (P->proto_state != PS_DOWN)
pipe_show_stats(p);
diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h
index 038c6666..60c857eb 100644
--- a/proto/pipe/pipe.h
+++ b/proto/pipe/pipe.h
@@ -12,12 +12,14 @@
struct pipe_config {
struct proto_config c;
struct rtable_config *peer; /* Table we're connected to */
+ u8 max_generation;
};
struct pipe_proto {
struct proto p;
struct channel *pri;
struct channel *sec;
+ struct tbf rl_gen;
};
#endif
diff --git a/proto/radv/radv.c b/proto/radv/radv.c
index 66e8eb4b..b0f15514 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -284,7 +284,7 @@ radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_conf
RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
- pool *pool = rp_new(p->p.pool, iface->name);
+ pool *pool = rp_new(p->p.pool, p->p.loop, iface->name);
ifa = mb_allocz(pool, sizeof(struct radv_iface));
ifa->pool = pool;
ifa->ra = p;
@@ -317,7 +317,7 @@ radv_iface_remove(struct radv_iface *ifa)
rem_node(NODE ifa);
- rfree(ifa->pool);
+ rp_free(ifa->pool, p->p.pool);
}
static void
@@ -385,18 +385,18 @@ radv_trigger_valid(struct radv_config *cf)
}
static inline int
-radv_net_match_trigger(struct radv_config *cf, net *n)
+radv_net_match_trigger(struct radv_config *cf, const net_addr *n)
{
- return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger);
+ return radv_trigger_valid(cf) && net_equal(n, &cf->trigger);
}
int
-radv_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
+radv_preexport(struct channel *c, rte *new)
{
// struct radv_proto *p = (struct radv_proto *) P;
- struct radv_config *cf = (struct radv_config *) (P->cf);
+ struct radv_config *cf = (struct radv_config *) (c->proto->cf);
- if (radv_net_match_trigger(cf, (*new)->net))
+ if (radv_net_match_trigger(cf, new->net))
return RIC_PROCESS;
if (cf->propagate_routes)
@@ -406,7 +406,7 @@ radv_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
}
static void
-radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED)
+radv_rt_notify(struct proto *P, struct channel *ch UNUSED, const net_addr *n, rte *new, const rte *old UNUSED)
{
struct radv_proto *p = (struct radv_proto *) P;
struct radv_config *cf = (struct radv_config *) (P->cf);
@@ -457,14 +457,14 @@ radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
(preference != RA_PREF_HIGH))
{
log(L_WARN "%s: Invalid ra_preference value %u on route %N",
- p->p.name, preference, n->n.addr);
+ p->p.name, preference, n);
preference = RA_PREF_MEDIUM;
preference_set = 1;
lifetime = 0;
lifetime_set = 1;
}
- rt = fib_get(&p->routes, n->n.addr);
+ rt = fib_get(&p->routes, n);
/* Ignore update if nothing changed */
if (rt->valid &&
@@ -487,7 +487,7 @@ radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
else
{
/* Withdraw */
- rt = fib_find(&p->routes, n->n.addr);
+ rt = fib_find(&p->routes, n);
if (!rt || !rt->valid)
return;
@@ -555,7 +555,10 @@ radv_check_active(struct radv_proto *p)
return 1;
struct channel *c = p->p.main_channel;
- return rt_examine(c->table, &cf->trigger, &p->p, c->out_filter);
+ RT_LOCK(c->table);
+ int active = rt_examine(RT_PRIV(c->table), &cf->trigger, c, c->out_filter);
+ RT_UNLOCK(c->table);
+ return active;
}
static void
diff --git a/proto/rip/rip.c b/proto/rip/rip.c
index e1a235a0..ee05f058 100644
--- a/proto/rip/rip.c
+++ b/proto/rip/rip.c
@@ -145,7 +145,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
{
/* Update */
rta a0 = {
- .src = p->p.main_source,
+ .pref = p->p.main_channel->preference,
.source = RTS_RIP,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
@@ -188,21 +188,34 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
a0.nh.iface = rt->from->ifa->iface;
}
- rta *a = rta_lookup(&a0);
- rte *e = rte_get_temp(a);
+ a0.eattrs = alloca(sizeof(ea_list) + 3*sizeof(eattr));
+ memset(a0.eattrs, 0, sizeof(ea_list)); /* Zero-ing only the ea_list header */
+ a0.eattrs->count = 3;
+ a0.eattrs->attrs[0] = (eattr) {
+ .id = EA_RIP_METRIC,
+ .type = EAF_TYPE_INT,
+ .u.data = rt_metric,
+ };
+ a0.eattrs->attrs[1] = (eattr) {
+ .id = EA_RIP_TAG,
+ .type = EAF_TYPE_INT,
+ .u.data = rt_tag,
+ };
+ a0.eattrs->attrs[2] = (eattr) {
+ .id = EA_RIP_FROM,
+ .type = EAF_TYPE_PTR,
+ .u.data = (uintptr_t) a0.nh.iface,
+ };
- e->u.rip.from = a0.nh.iface;
- e->u.rip.metric = rt_metric;
- e->u.rip.tag = rt_tag;
- e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG);
+ rte e0 = {
+ .attrs = &a0,
+ .src = p->p.main_source,
+ };
- rte_update(&p->p, en->n.addr, e);
+ rte_update(p->p.main_channel, en->n.addr, &e0, p->p.main_source);
}
else
- {
- /* Withdraw */
- rte_update(&p->p, en->n.addr, NULL);
- }
+ rte_update(p->p.main_channel, en->n.addr, NULL, p->p.main_source);
}
/**
@@ -297,8 +310,8 @@ rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from)
* it into our data structures.
*/
static void
-rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, struct rte *new,
- struct rte *old UNUSED)
+rip_rt_notify(struct proto *P, struct channel *ch UNUSED, const net_addr *net, struct rte *new,
+ const struct rte *old UNUSED)
{
struct rip_proto *p = (struct rip_proto *) P;
struct rip_entry *en;
@@ -307,20 +320,21 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
if (new)
{
/* Update */
- u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
u32 rt_tag = ea_get_int(new->attrs->eattrs, EA_RIP_TAG, 0);
+ u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
+ struct iface *rt_from = (struct iface *) ea_get_int(new->attrs->eattrs, EA_RIP_FROM, 0);
if (rt_metric > p->infinity)
{
log(L_WARN "%s: Invalid rip_metric value %u for route %N",
- p->p.name, rt_metric, net->n.addr);
+ p->p.name, rt_metric, net);
rt_metric = p->infinity;
}
if (rt_tag > 0xffff)
{
log(L_WARN "%s: Invalid rip_tag value %u for route %N",
- p->p.name, rt_tag, net->n.addr);
+ p->p.name, rt_tag, net);
rt_metric = p->infinity;
rt_tag = 0;
}
@@ -332,21 +346,21 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
* collection.
*/
- en = fib_get(&p->rtable, net->n.addr);
+ en = fib_get(&p->rtable, net);
old_metric = en->valid ? en->metric : -1;
en->valid = RIP_ENTRY_VALID;
en->metric = rt_metric;
en->tag = rt_tag;
- en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
+ en->from = (new->src->owner == &P->sources) ? rt_from : NULL;
en->iface = new->attrs->nh.iface;
en->next_hop = new->attrs->nh.gw;
}
else
{
/* Withdraw */
- en = fib_find(&p->rtable, net->n.addr);
+ en = fib_find(&p->rtable, net);
if (!en || en->valid != RIP_ENTRY_VALID)
return;
@@ -1068,37 +1082,33 @@ rip_reload_routes(struct channel *C)
rip_kick_timer(p);
}
-static void
-rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
-{
- rte_init_tmp_attrs(rt, pool, 2);
- rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric);
- rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag);
-}
+static struct rte_owner_class rip_rte_owner_class;
-static void
-rip_store_tmp_attrs(struct rte *rt, struct linpool *pool)
+static inline struct rip_proto *
+rip_rte_proto(struct rte *rte)
{
- rte_init_tmp_attrs(rt, pool, 2);
- rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC);
- rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG);
+ return (rte->src->owner->class == &rip_rte_owner_class) ?
+ SKIP_BACK(struct rip_proto, p.sources, rte->src->owner) : NULL;
}
static int
rip_rte_better(struct rte *new, struct rte *old)
{
- return new->u.rip.metric < old->u.rip.metric;
+ ASSERT_DIE(new->src == old->src);
+ struct rip_proto *p = rip_rte_proto(new);
+
+ u32 new_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, p->infinity);
+ u32 old_metric = ea_get_int(old->attrs->eattrs, EA_RIP_METRIC, p->infinity);
+
+ return new_metric < old_metric;
}
-static int
-rip_rte_same(struct rte *new, struct rte *old)
+static u32
+rip_rte_igp_metric(struct rte *rt)
{
- return ((new->u.rip.metric == old->u.rip.metric) &&
- (new->u.rip.tag == old->u.rip.tag) &&
- (new->u.rip.from == old->u.rip.from));
+ return ea_get_int(rt->attrs->eattrs, EA_RIP_METRIC, IGP_METRIC_UNKNOWN);
}
-
static void
rip_postconfig(struct proto_config *CF)
{
@@ -1120,10 +1130,7 @@ rip_init(struct proto_config *CF)
P->rt_notify = rip_rt_notify;
P->neigh_notify = rip_neigh_notify;
P->reload_routes = rip_reload_routes;
- P->make_tmp_attrs = rip_make_tmp_attrs;
- P->store_tmp_attrs = rip_store_tmp_attrs;
- P->rte_better = rip_rte_better;
- P->rte_same = rip_rte_same;
+ P->sources.class = &rip_rte_owner_class;
return P;
}
@@ -1198,10 +1205,14 @@ rip_reconfigure(struct proto *P, struct proto_config *CF)
static void
rip_get_route_info(rte *rte, byte *buf)
{
- buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric);
+ struct rip_proto *p = rip_rte_proto(rte);
+ u32 rt_metric = ea_get_int(rte->attrs->eattrs, EA_RIP_METRIC, p->infinity);
+ u32 rt_tag = ea_get_int(rte->attrs->eattrs, EA_RIP_TAG, 0);
+
+ buf += bsprintf(buf, " (%d/%d)", rte->attrs->pref, rt_metric);
- if (rte->u.rip.tag)
- bsprintf(buf, " [%04x]", rte->u.rip.tag);
+ if (rt_tag)
+ bsprintf(buf, " [%04x]", rt_tag);
}
static int
@@ -1321,6 +1332,12 @@ rip_dump(struct proto *P)
}
+static struct rte_owner_class rip_rte_owner_class = {
+ .get_route_info = rip_get_route_info,
+ .rte_better = rip_rte_better,
+ .rte_igp_metric = rip_rte_igp_metric,
+};
+
struct protocol proto_rip = {
.name = "RIP",
.template = "rip%d",
@@ -1335,6 +1352,5 @@ struct protocol proto_rip = {
.start = rip_start,
.shutdown = rip_shutdown,
.reconfigure = rip_reconfigure,
- .get_route_info = rip_get_route_info,
.get_attr = rip_get_attr
};
diff --git a/proto/rip/rip.h b/proto/rip/rip.h
index 8d347000..f8713c4a 100644
--- a/proto/rip/rip.h
+++ b/proto/rip/rip.h
@@ -197,6 +197,7 @@ struct rip_rte
#define EA_RIP_METRIC EA_CODE(PROTOCOL_RIP, 0)
#define EA_RIP_TAG EA_CODE(PROTOCOL_RIP, 1)
+#define EA_RIP_FROM EA_CODE(PROTOCOL_RIP, 2)
static inline int rip_is_v2(struct rip_proto *p)
{ return p->rip2; }
diff --git a/proto/rpki/config.Y b/proto/rpki/config.Y
index d6d326b8..743b5b42 100644
--- a/proto/rpki/config.Y
+++ b/proto/rpki/config.Y
@@ -42,6 +42,7 @@ proto: rpki_proto ;
rpki_proto_start: proto_start RPKI {
this_proto = proto_config_new(&proto_rpki, $1);
+ this_proto->loop_order = DOMAIN_ORDER(proto);
RPKI_CFG->retry_interval = RPKI_RETRY_INTERVAL;
RPKI_CFG->refresh_interval = RPKI_REFRESH_INTERVAL;
RPKI_CFG->expire_interval = RPKI_EXPIRE_INTERVAL;
diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c
index dd11f997..abe6abfc 100644
--- a/proto/rpki/packets.c
+++ b/proto/rpki/packets.c
@@ -233,7 +233,12 @@ static const size_t min_pdu_size[] = {
[ERROR] = 16,
};
-static int rpki_send_error_pdu(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...);
+static int rpki_send_error_pdu_(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...);
+
+#define rpki_send_error_pdu(cache, error_code, err_pdu_len, erroneous_pdu, fmt...) ({ \
+ rpki_send_error_pdu_(cache, error_code, err_pdu_len, erroneous_pdu, #fmt); \
+ CACHE_TRACE(D_PACKETS, cache, #fmt); \
+ })
static void
rpki_pdu_to_network_byte_order(struct pdu_header *pdu)
@@ -595,6 +600,7 @@ rpki_handle_error_pdu(struct rpki_cache *cache, const struct pdu_error *pdu)
case INTERNAL_ERROR:
case INVALID_REQUEST:
case UNSUPPORTED_PDU_TYPE:
+ CACHE_TRACE(D_PACKETS, cache, "Got UNSUPPORTED_PDU_TYPE");
rpki_cache_change_state(cache, RPKI_CS_ERROR_FATAL);
break;
@@ -652,21 +658,7 @@ rpki_handle_cache_response_pdu(struct rpki_cache *cache, const struct pdu_cache_
{
if (cache->request_session_id)
{
- if (cache->last_update)
- {
- /*
- * This isn't the first sync and we already received records. This point
- * is after Reset Query and before importing new records from cache
- * server. We need to load new ones and kick out missing ones. So start
- * a refresh cycle.
- */
- if (cache->p->roa4_channel)
- rt_refresh_begin(cache->p->roa4_channel->table, cache->p->roa4_channel);
- if (cache->p->roa6_channel)
- rt_refresh_begin(cache->p->roa6_channel->table, cache->p->roa6_channel);
-
- cache->p->refresh_channels = 1;
- }
+ rpki_start_refresh(cache->p);
cache->session_id = pdu->session_id;
cache->request_session_id = 0;
}
@@ -815,14 +807,7 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da
(cf->keep_expire_interval ? "keeps " : ""), cache->expire_interval);
}
- if (cache->p->refresh_channels)
- {
- cache->p->refresh_channels = 0;
- if (cache->p->roa4_channel)
- rt_refresh_end(cache->p->roa4_channel->table, cache->p->roa4_channel);
- if (cache->p->roa6_channel)
- rt_refresh_end(cache->p->roa6_channel->table, cache->p->roa6_channel);
- }
+ rpki_stop_refresh(cache->p);
cache->last_update = current_time();
cache->serial_num = pdu->serial_num;
@@ -897,6 +882,9 @@ rpki_rx_hook(struct birdsock *sk, uint size)
struct rpki_cache *cache = sk->data;
struct rpki_proto *p = cache->p;
+ if ((p->p.proto_state == PS_DOWN) || (p->cache != cache))
+ return 0;
+
byte *pkt_start = sk->rbuf;
byte *end = pkt_start + size;
@@ -953,6 +941,8 @@ rpki_err_hook(struct birdsock *sk, int error_num)
CACHE_TRACE(D_EVENTS, cache, "The other side closed a connection");
}
+ if (cache->p->cache != cache)
+ return;
rpki_cache_change_state(cache, RPKI_CS_ERROR_TRANSPORT);
}
@@ -972,6 +962,9 @@ rpki_tx_hook(sock *sk)
{
struct rpki_cache *cache = sk->data;
+ if (cache->p->cache != cache)
+ return;
+
while (rpki_fire_tx(cache) > 0)
;
}
@@ -981,6 +974,9 @@ rpki_connected_hook(sock *sk)
{
struct rpki_cache *cache = sk->data;
+ if (cache->p->cache != cache)
+ return;
+
CACHE_TRACE(D_EVENTS, cache, "Connected");
proto_notify_state(&cache->p->p, PS_UP);
@@ -1002,7 +998,7 @@ rpki_connected_hook(sock *sk)
* This function prepares Error PDU and sends it to a cache server.
*/
static int
-rpki_send_error_pdu(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...)
+rpki_send_error_pdu_(struct rpki_cache *cache, const enum pdu_error_type error_code, const u32 err_pdu_len, const struct pdu_header *erroneous_pdu, const char *fmt, ...)
{
va_list args;
char msg[128];
diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c
index ab0837f3..afba2216 100644
--- a/proto/rpki/rpki.c
+++ b/proto/rpki/rpki.c
@@ -109,6 +109,7 @@ static void rpki_schedule_next_expire_check(struct rpki_cache *cache);
static void rpki_stop_refresh_timer_event(struct rpki_cache *cache);
static void rpki_stop_retry_timer_event(struct rpki_cache *cache);
static void rpki_stop_expire_timer_event(struct rpki_cache *cache);
+static void rpki_stop_all_timers(struct rpki_cache *cache);
/*
@@ -121,27 +122,48 @@ rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_
struct rpki_proto *p = cache->p;
rta a0 = {
- .src = p->p.main_source,
+ .pref = channel->preference,
.source = RTS_RPKI,
.scope = SCOPE_UNIVERSE,
.dest = RTD_NONE,
};
- rta *a = rta_lookup(&a0);
- rte *e = rte_get_temp(a);
+ rte e0 = { .attrs = &a0, .src = p->p.main_source, };
- e->pflags = 0;
-
- rte_update2(channel, &pfxr->n, e, a0.src);
+ rte_update(channel, &pfxr->n, &e0, p->p.main_source);
}
void
rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr)
{
struct rpki_proto *p = cache->p;
- rte_update2(channel, &pfxr->n, NULL, p->p.main_source);
+ rte_update(channel, &pfxr->n, NULL, p->p.main_source);
+}
+
+void
+rpki_start_refresh(struct rpki_proto *p)
+{
+ if (p->roa4_channel)
+ rt_refresh_begin(&p->roa4_channel->in_req);
+ if (p->roa6_channel)
+ rt_refresh_begin(&p->roa6_channel->in_req);
+
+ p->refresh_channels = 1;
}
+void
+rpki_stop_refresh(struct rpki_proto *p)
+{
+ if (!p->refresh_channels)
+ return;
+
+ p->refresh_channels = 0;
+
+ if (p->roa4_channel)
+ rt_refresh_end(&p->roa4_channel->in_req);
+ if (p->roa6_channel)
+ rt_refresh_end(&p->roa6_channel->in_req);
+}
/*
* RPKI Protocol Logic
@@ -198,6 +220,8 @@ rpki_force_restart_proto(struct rpki_proto *p)
{
if (p->cache)
{
+ rpki_tr_close(p->cache->tr_sock);
+ rpki_stop_all_timers(p->cache);
CACHE_DBG(p->cache, "Connection object destroying");
}
@@ -321,7 +345,7 @@ rpki_schedule_next_refresh(struct rpki_cache *cache)
btime t = cache->refresh_interval S;
CACHE_DBG(cache, "after %t s", t);
- tm_start(cache->refresh_timer, t);
+ tm_start_in(cache->refresh_timer, t, cache->p->p.loop);
}
static void
@@ -330,7 +354,7 @@ rpki_schedule_next_retry(struct rpki_cache *cache)
btime t = cache->retry_interval S;
CACHE_DBG(cache, "after %t s", t);
- tm_start(cache->retry_timer, t);
+ tm_start_in(cache->retry_timer, t, cache->p->p.loop);
}
static void
@@ -341,7 +365,7 @@ rpki_schedule_next_expire_check(struct rpki_cache *cache)
t = MAX(t, 1 S);
CACHE_DBG(cache, "after %t s", t);
- tm_start(cache->expire_timer, t);
+ tm_start_in(cache->expire_timer, t, cache->p->p.loop);
}
static void
@@ -358,13 +382,21 @@ rpki_stop_retry_timer_event(struct rpki_cache *cache)
tm_stop(cache->retry_timer);
}
-static void UNUSED
+static void
rpki_stop_expire_timer_event(struct rpki_cache *cache)
{
CACHE_DBG(cache, "Stop");
tm_stop(cache->expire_timer);
}
+static void
+rpki_stop_all_timers(struct rpki_cache *cache)
+{
+ rpki_stop_refresh_timer_event(cache);
+ rpki_stop_retry_timer_event(cache);
+ rpki_stop_expire_timer_event(cache);
+}
+
static int
rpki_do_we_recv_prefix_pdu_in_last_seconds(struct rpki_cache *cache)
{
@@ -387,6 +419,9 @@ rpki_refresh_hook(timer *tm)
{
struct rpki_cache *cache = tm->data;
+ if (cache->p->cache != cache)
+ return;
+
CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
switch (cache->state)
@@ -433,6 +468,9 @@ rpki_retry_hook(timer *tm)
{
struct rpki_cache *cache = tm->data;
+ if (cache->p->cache != cache)
+ return;
+
CACHE_DBG(cache, "%s", rpki_cache_state_to_str(cache->state));
switch (cache->state)
@@ -478,6 +516,9 @@ rpki_expire_hook(timer *tm)
{
struct rpki_cache *cache = tm->data;
+ if (cache->p->cache != cache)
+ return;
+
if (!cache->last_update)
return;
@@ -555,7 +596,7 @@ rpki_check_expire_interval(uint seconds)
static struct rpki_cache *
rpki_init_cache(struct rpki_proto *p, struct rpki_config *cf)
{
- pool *pool = rp_new(p->p.pool, cf->hostname);
+ pool *pool = rp_new(p->p.pool, p->p.loop, cf->hostname);
struct rpki_cache *cache = mb_allocz(pool, sizeof(struct rpki_cache));
@@ -620,6 +661,7 @@ rpki_close_connection(struct rpki_cache *cache)
{
CACHE_TRACE(D_EVENTS, cache, "Closing a connection");
rpki_tr_close(cache->tr_sock);
+ rpki_stop_refresh(cache->p);
proto_notify_state(&cache->p->p, PS_START);
}
diff --git a/proto/rpki/rpki.h b/proto/rpki/rpki.h
index 8a5c38fd..a70a2027 100644
--- a/proto/rpki/rpki.h
+++ b/proto/rpki/rpki.h
@@ -83,6 +83,8 @@ const char *rpki_cache_state_to_str(enum rpki_cache_state state);
void rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
void rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
+void rpki_start_refresh(struct rpki_proto *p);
+void rpki_stop_refresh(struct rpki_proto *p);
/*
* RPKI Protocol Logic
diff --git a/proto/rpki/ssh_transport.c b/proto/rpki/ssh_transport.c
index 6333f367..223afa80 100644
--- a/proto/rpki/ssh_transport.c
+++ b/proto/rpki/ssh_transport.c
@@ -38,6 +38,8 @@ rpki_tr_ssh_open(struct rpki_tr_sock *tr)
if (sk_open(sk) != 0)
return RPKI_TR_ERROR;
+ sk_start(sk);
+
return RPKI_TR_SUCCESS;
}
diff --git a/proto/rpki/tcp_transport.c b/proto/rpki/tcp_transport.c
index 132f8e2d..4e850c44 100644
--- a/proto/rpki/tcp_transport.c
+++ b/proto/rpki/tcp_transport.c
@@ -31,6 +31,8 @@ rpki_tr_tcp_open(struct rpki_tr_sock *tr)
if (sk_open(sk) != 0)
return RPKI_TR_ERROR;
+ sk_start(sk);
+
return RPKI_TR_SUCCESS;
}
diff --git a/proto/rpki/transport.c b/proto/rpki/transport.c
index a1ac7587..b52495dc 100644
--- a/proto/rpki/transport.c
+++ b/proto/rpki/transport.c
@@ -85,6 +85,7 @@ rpki_tr_open(struct rpki_tr_sock *tr)
sk->rbsize = RPKI_RX_BUFFER_SIZE;
sk->tbsize = RPKI_TX_BUFFER_SIZE;
sk->tos = IP_PREC_INTERNET_CONTROL;
+ sk->flags |= SKF_THREAD;
if (ipa_zero(sk->daddr) && sk->host)
{
@@ -119,6 +120,7 @@ rpki_tr_close(struct rpki_tr_sock *tr)
if (tr->sk)
{
+ sk_stop(tr->sk);
rfree(tr->sk);
tr->sk = NULL;
}
diff --git a/proto/static/static.c b/proto/static/static.c
index 2789c1bb..bd7f3f5b 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -52,14 +52,18 @@ static linpool *static_lp;
static inline struct rte_src * static_get_source(struct static_proto *p, uint i)
{ return i ? rt_get_source(&p->p, i) : p->p.main_source; }
+static inline void static_free_source(struct rte_src *src, uint i)
+{ if (i) rt_unlock_source(src); }
+
static void
static_announce_rte(struct static_proto *p, struct static_route *r)
{
+ struct rte_src *src;
rta *a = allocz(RTA_MAX_SIZE);
- a->src = static_get_source(p, r->index);
a->source = RTS_STATIC;
a->scope = SCOPE_UNIVERSE;
a->dest = r->dest;
+ a->pref = p->p.main_channel->preference;
if (r->dest == RTD_UNICAST)
{
@@ -94,7 +98,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
if (r->dest == RTDX_RECURSIVE)
{
rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6;
- rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls);
+ rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls, static_lp);
}
/* Already announced */
@@ -102,24 +106,16 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
return;
/* We skip rta_lookup() here */
- rte *e = rte_get_temp(a);
- e->pflags = 0;
+ src = static_get_source(p, r->index);
+ rte e0 = { .attrs = a, .src = src, .net = r->net, }, *e = &e0;
+ /* Evaluate the filter */
if (r->cmds)
- {
- /* Create a temporary table node */
- e->net = alloca(sizeof(net) + r->net->length);
- memset(e->net, 0, sizeof(net) + r->net->length);
- net_copy(e->net->n.addr, r->net);
+ f_eval_rte(r->cmds, e, static_lp);
- /* Evaluate the filter */
- f_eval_rte(r->cmds, &e, static_lp);
+ rte_update(p->p.main_channel, r->net, e, src);
+ static_free_source(src, r->index);
- /* Remove the temporary node */
- e->net = NULL;
- }
-
- rte_update2(p->p.main_channel, r->net, e, a->src);
r->state = SRS_CLEAN;
if (r->cmds)
@@ -131,7 +127,9 @@ withdraw:
if (r->state == SRS_DOWN)
return;
- rte_update2(p->p.main_channel, r->net, NULL, a->src);
+ src = static_get_source(p, r->index);
+ rte_update(p->p.main_channel, r->net, NULL, src);
+ static_free_source(src, r->index);
r->state = SRS_DOWN;
}
@@ -297,7 +295,11 @@ static void
static_remove_rte(struct static_proto *p, struct static_route *r)
{
if (r->state)
- rte_update2(p->p.main_channel, r->net, NULL, static_get_source(p, r->index));
+ {
+ struct rte_src *src = static_get_source(p, r->index);
+ rte_update(p->p.main_channel, r->net, NULL, src);
+ static_free_source(src, r->index);
+ }
static_reset_rte(p, r);
}
@@ -454,6 +456,8 @@ static_postconfig(struct proto_config *CF)
static_index_routes(cf);
}
+static struct rte_owner_class static_rte_owner_class;
+
static struct proto *
static_init(struct proto_config *CF)
{
@@ -465,8 +469,7 @@ static_init(struct proto_config *CF)
P->neigh_notify = static_neigh_notify;
P->reload_routes = static_reload_routes;
- P->rte_better = static_rte_better;
- P->rte_mergable = static_rte_mergable;
+ P->sources.class = &static_rte_owner_class;
if (cf->igp_table_ip4)
p->igp_table_ip4 = cf->igp_table_ip4->table;
@@ -488,10 +491,12 @@ static_start(struct proto *P)
static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024));
if (p->igp_table_ip4)
- rt_lock_table(p->igp_table_ip4);
+ RT_LOCKED(p->igp_table_ip4, t)
+ rt_lock_table(t);
if (p->igp_table_ip6)
- rt_lock_table(p->igp_table_ip6);
+ RT_LOCKED(p->igp_table_ip6, t)
+ rt_lock_table(t);
p->event = ev_new_init(p->p.pool, static_announce_marked, p);
@@ -517,19 +522,15 @@ static_shutdown(struct proto *P)
WALK_LIST(r, cf->routes)
static_reset_rte(p, r);
- return PS_DOWN;
-}
-
-static void
-static_cleanup(struct proto *P)
-{
- struct static_proto *p = (void *) P;
-
if (p->igp_table_ip4)
- rt_unlock_table(p->igp_table_ip4);
+ RT_LOCKED(p->igp_table_ip4, t)
+ rt_unlock_table(t);
if (p->igp_table_ip6)
- rt_unlock_table(p->igp_table_ip6);
+ RT_LOCKED(p->igp_table_ip6, t)
+ rt_unlock_table(t);
+
+ return PS_DOWN;
}
static void
@@ -721,9 +722,9 @@ static_get_route_info(rte *rte, byte *buf)
{
eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC);
if (a)
- buf += bsprintf(buf, " (%d/%u)", rte->pref, a->u.data);
+ buf += bsprintf(buf, " (%d/%u)", rte->attrs->pref, a->u.data);
else
- buf += bsprintf(buf, " (%d)", rte->pref);
+ buf += bsprintf(buf, " (%d)", rte->attrs->pref);
}
static void
@@ -773,6 +774,11 @@ static_show(struct proto *P)
static_show_rt(r);
}
+static struct rte_owner_class static_rte_owner_class = {
+ .get_route_info = static_get_route_info,
+ .rte_better = static_rte_better,
+ .rte_mergable = static_rte_mergable,
+};
struct protocol proto_static = {
.name = "Static",
@@ -787,8 +793,6 @@ struct protocol proto_static = {
.dump = static_dump,
.start = static_start,
.shutdown = static_shutdown,
- .cleanup = static_cleanup,
.reconfigure = static_reconfigure,
.copy_config = static_copy_config,
- .get_route_info = static_get_route_info,
};
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index 5c905bc9..6f788ac2 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -374,7 +374,6 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
/* p is NULL iff KRT_SHARED_SOCKET and !scan */
int ipv6;
- rte *e;
net *net;
sockaddr dst, gate, mask;
ip_addr idst, igate, imask;
@@ -495,7 +494,6 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
net = net_get(p->p.main_channel->table, &ndst);
rta a = {
- .src = p->p.main_source,
.source = RTS_INHERIT,
.scope = SCOPE_UNIVERSE,
};
@@ -549,19 +547,23 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
}
}
- done:
- e = rte_get_temp(&a);
- e->net = net;
- e->u.krt.src = src;
- e->u.krt.proto = src2;
- e->u.krt.seen = 0;
- e->u.krt.best = 0;
- e->u.krt.metric = 0;
+ done:;
+ rte e0 = { .attrs = &a, .net = net, };
+
+ ea_list *ea = alloca(sizeof(ea_list) + 1 * sizeof(eattr));
+ *ea = (ea_list) { .count = 1, .next = e0.attrs->eattrs };
+ e0.attrs->eattrs = ea;
+
+ ea->attrs[0] = (eattr) {
+ .id = EA_KRT_SOURCE,
+ .type = EAF_TYPE_INT,
+ .u.data = src2,
+ };
if (scan)
- krt_got_route(p, e);
+ krt_got_route(p, &e0, src);
else
- krt_got_route_async(p, e, new);
+ krt_got_route_async(p, &e0, new, src);
}
static void
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index fdf3f2db..bff2d579 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -105,7 +105,7 @@ struct nl_parse_state
int scan;
int merge;
- net *net;
+ net_addr *net;
rta *attrs;
struct krt_proto *proto;
s8 new;
@@ -1233,10 +1233,9 @@ nh_bufsize(struct nexthop *nh)
}
static int
-nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
+nl_send_route(struct krt_proto *p, const rte *e, int op, int dest, struct nexthop *nh)
{
eattr *ea;
- net *net = e->net;
rta *a = e->attrs;
ea_list *eattrs = a->eattrs;
int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(&(a->nh));
@@ -1251,7 +1250,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
int rsize = sizeof(*r) + bufsize;
r = alloca(rsize);
- DBG("nl_send_route(%N,op=%x)\n", net->n.addr, op);
+ DBG("nl_send_route(%N,op=%x)\n", e->net, op);
bzero(&r->h, sizeof(r->h));
bzero(&r->r, sizeof(r->r));
@@ -1260,7 +1259,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
r->h.nlmsg_flags = op | NLM_F_REQUEST | NLM_F_ACK;
r->r.rtm_family = p->af;
- r->r.rtm_dst_len = net_pxlen(net->n.addr);
+ r->r.rtm_dst_len = net_pxlen(e->net);
r->r.rtm_protocol = RTPROT_BIRD;
r->r.rtm_scope = RT_SCOPE_NOWHERE;
#ifdef HAVE_MPLS_KERNEL
@@ -1272,7 +1271,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
* 2) Never use RTA_PRIORITY
*/
- u32 label = net_mpls(net->n.addr);
+ u32 label = net_mpls(e->net);
nl_add_attr_mpls(&r->h, rsize, RTA_DST, 1, &label);
r->r.rtm_scope = RT_SCOPE_UNIVERSE;
r->r.rtm_type = RTN_UNICAST;
@@ -1280,12 +1279,12 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
else
#endif
{
- nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr));
+ nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(e->net));
/* Add source address for IPv6 SADR routes */
- if (net->n.addr->type == NET_IP6_SADR)
+ if (e->net->type == NET_IP6_SADR)
{
- net_addr_ip6_sadr *a = (void *) &net->n.addr;
+ net_addr_ip6_sadr *a = (void *) &e->net;
nl_add_attr_ip6(&r->h, rsize, RTA_SRC, a->src_prefix);
r->r.rtm_src_len = a->src_pxlen;
}
@@ -1305,8 +1304,6 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
if (p->af == AF_MPLS)
priority = 0;
- else if (a->source == RTS_DUMMY)
- priority = e->u.krt.metric;
else if (KRT_CF->sys.metric)
priority = KRT_CF->sys.metric;
else if ((op != NL_OP_DELETE) && (ea = ea_find(eattrs, EA_KRT_METRIC)))
@@ -1408,7 +1405,7 @@ nl_add_rte(struct krt_proto *p, rte *e)
}
static inline int
-nl_delete_rte(struct krt_proto *p, rte *e)
+nl_delete_rte(struct krt_proto *p, const rte *e)
{
int err = 0;
@@ -1429,7 +1426,7 @@ nl_replace_rte(struct krt_proto *p, rte *e)
void
-krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
+krt_replace_rte(struct krt_proto *p, const net_addr *n UNUSED, rte *new, const rte *old)
{
int err = 0;
@@ -1468,7 +1465,7 @@ krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
}
static int
-nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint priority, uint krt_type, uint rtm_family)
+nl_mergable_route(struct nl_parse_state *s, const net_addr *net, struct krt_proto *p, uint priority, uint krt_type, uint rtm_family)
{
/* Route merging is used for IPv6 scans */
if (!s->scan || (rtm_family != AF_INET6))
@@ -1488,18 +1485,30 @@ nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint
static void
nl_announce_route(struct nl_parse_state *s)
{
- rte *e = rte_get_temp(s->attrs);
- e->net = s->net;
- e->u.krt.src = s->krt_src;
- e->u.krt.proto = s->krt_proto;
- e->u.krt.seen = 0;
- e->u.krt.best = 0;
- e->u.krt.metric = s->krt_metric;
+ rte e0 = {
+ .attrs = s->attrs,
+ .net = s->net,
+ };
+
+ ea_list *ea = alloca(sizeof(ea_list) + 2 * sizeof(eattr));
+ *ea = (ea_list) { .count = 2, .next = e0.attrs->eattrs };
+ e0.attrs->eattrs = ea;
+
+ ea->attrs[0] = (eattr) {
+ .id = EA_KRT_SOURCE,
+ .type = EAF_TYPE_INT,
+ .u.data = s->krt_proto,
+ };
+ ea->attrs[1] = (eattr) {
+ .id = EA_KRT_METRIC,
+ .type = EAF_TYPE_INT,
+ .u.data = s->krt_metric,
+ };
if (s->scan)
- krt_got_route(s->proto, e);
+ krt_got_route(s->proto, &e0, s->krt_src);
else
- krt_got_route_async(s->proto, e, s->new);
+ krt_got_route_async(s->proto, &e0, s->new, s->krt_src);
s->net = NULL;
s->attrs = NULL;
@@ -1645,21 +1654,18 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
krt_src = KRT_SRC_ALIEN;
}
- net_addr *n = &dst;
+ net_addr *net = &dst;
if (p->p.net_type == NET_IP6_SADR)
{
- n = alloca(sizeof(net_addr_ip6_sadr));
- net_fill_ip6_sadr(n, net6_prefix(&dst), net6_pxlen(&dst),
+ net = alloca(sizeof(net_addr_ip6_sadr));
+ net_fill_ip6_sadr(net, net6_prefix(&dst), net6_pxlen(&dst),
net6_prefix(&src), net6_pxlen(&src));
}
- net *net = net_get(p->p.main_channel->table, n);
-
if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type, i->rtm_family))
nl_announce_route(s);
rta *ra = lp_allocz(s->pool, RTA_MAX_SIZE);
- ra->src = p->p.main_source;
ra->source = RTS_INHERIT;
ra->scope = SCOPE_UNIVERSE;
@@ -1678,7 +1684,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
struct nexthop *nh = nl_parse_multipath(s, p, a[RTA_MULTIPATH], i->rtm_family);
if (!nh)
{
- log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
+ log(L_ERR "KRT: Received strange multipath route %N", net);
return;
}
@@ -1692,7 +1698,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
ra->nh.iface = if_find_by_index(oif);
if (!ra->nh.iface)
{
- log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif);
+ log(L_ERR "KRT: Received route %N with unknown ifindex %u", net, oif);
return;
}
@@ -1719,8 +1725,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
(ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
if (!nbr || (nbr->scope == SCOPE_HOST))
{
- log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr,
- ra->nh.gw);
+ log(L_ERR "KRT: Received route %N with strange next-hop %I", net, ra->nh.gw);
return;
}
}
@@ -1819,7 +1824,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0)
{
- log(L_ERR "KRT: Received route %N with strange RTA_METRICS attribute", net->n.addr);
+ log(L_ERR "KRT: Received route %N with strange RTA_METRICS attribute", net);
return;
}
@@ -1853,7 +1858,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if (!s->net)
{
/* Store the new route */
- s->net = net;
+ s->net = lp_alloc(s->pool, net->length);
+ net_copy(s->net, net);
+
s->attrs = ra;
s->proto = p;
s->new = new;
diff --git a/sysdep/unix/Makefile b/sysdep/unix/Makefile
index d0d36b5f..07f454ab 100644
--- a/sysdep/unix/Makefile
+++ b/sysdep/unix/Makefile
@@ -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 coroutine.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
diff --git a/sysdep/unix/alloc.c b/sysdep/unix/alloc.c
index 5dd70c99..d18f286b 100644
--- a/sysdep/unix/alloc.c
+++ b/sysdep/unix/alloc.c
@@ -11,69 +11,59 @@
#include "lib/lists.h"
#include "lib/event.h"
+#include "sysdep/unix/io-loop.h"
+
#include <stdlib.h>
#include <unistd.h>
+#include <stdatomic.h>
+#include <errno.h>
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
-#ifdef HAVE_MMAP
-#define KEEP_PAGES 512
+long page_size = 0;
-static u64 page_size = 0;
+#ifdef HAVE_MMAP
+#if DEBUGGING
+#define FP_NODE_OFFSET 42
+#else
+#define FP_NODE_OFFSET 1
+#endif
static _Bool use_fake = 0;
-
-uint pages_kept = 0;
-static list pages_list;
-
-static void cleanup_pages(void *data);
-static event page_cleanup_event = { .hook = cleanup_pages };
-
#else
-static const u64 page_size = 4096; /* Fake page size */
+static _Bool use_fake = 1;
#endif
-u64 get_page_size(void)
+static void *
+alloc_sys_page(void)
{
- if (page_size)
- return page_size;
+ void *ptr = mmap(NULL, page_size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-#ifdef HAVE_MMAP
- if (page_size = sysconf(_SC_PAGESIZE))
- {
- if ((u64_popcount(page_size) > 1) || (page_size > 16384))
- {
- /* Too big or strange page, use the aligned allocator instead */
- page_size = 4096;
- use_fake = 1;
- }
- return page_size;
- }
+ if (ptr == MAP_FAILED)
+ bug("mmap(%lu) failed: %m", page_size);
- bug("Page size must be non-zero");
-#endif
+ return ptr;
}
void *
alloc_page(void)
{
#ifdef HAVE_MMAP
- if (pages_kept)
- {
- node *page = TAIL(pages_list);
- rem_node(page);
- pages_kept--;
- memset(page, 0, get_page_size());
- return page;
- }
-
if (!use_fake)
{
- void *ret = mmap(NULL, get_page_size(), PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (ret == MAP_FAILED)
- bug("mmap(%lu) failed: %m", page_size);
- return ret;
+ struct free_pages *fp = &birdloop_current->pages;
+ if (!fp->cnt)
+ return alloc_sys_page();
+
+ node *n = HEAD(fp->list);
+ rem_node(n);
+ if (--fp->cnt < fp->min)
+ ev_send(&global_work_list, fp->cleanup);
+
+ void *ptr = n - FP_NODE_OFFSET;
+ memset(ptr, 0, page_size);
+ return ptr;
}
else
#endif
@@ -91,14 +81,14 @@ free_page(void *ptr)
#ifdef HAVE_MMAP
if (!use_fake)
{
- if (!pages_kept)
- init_list(&pages_list);
-
- memset(ptr, 0, sizeof(node));
- add_tail(&pages_list, ptr);
-
- if (++pages_kept > KEEP_PAGES)
- ev_schedule(&page_cleanup_event);
+ struct free_pages *fp = &birdloop_current->pages;
+ struct node *n = ptr;
+ n += FP_NODE_OFFSET;
+
+ memset(n, 0, sizeof(node));
+ add_tail(&fp->list, n);
+ if (++fp->cnt > fp->max)
+ ev_send(&global_work_list, fp->cleanup);
}
else
#endif
@@ -106,24 +96,136 @@ free_page(void *ptr)
}
#ifdef HAVE_MMAP
+
+#define GFP (&main_birdloop.pages)
+
+void
+flush_pages(struct birdloop *loop)
+{
+ ASSERT_DIE(birdloop_inside(&main_birdloop));
+
+ add_tail_list(&GFP->list, &loop->pages.list);
+ GFP->cnt += loop->pages.cnt;
+
+ loop->pages.cnt = 0;
+ loop->pages.list = (list) {};
+ loop->pages.min = 0;
+ loop->pages.max = 0;
+
+ rfree(loop->pages.cleanup);
+ loop->pages.cleanup = NULL;
+}
+
static void
-cleanup_pages(void *data UNUSED)
+cleanup_pages(void *data)
{
- for (uint seen = 0; (pages_kept > KEEP_PAGES) && (seen < KEEP_PAGES); seen++)
+ struct birdloop *loop = data;
+ birdloop_enter(loop);
+
+ struct free_pages *fp = &birdloop_current->pages;
+
+ while ((fp->cnt < fp->min) && (GFP->cnt > GFP->min))
{
- void *ptr = HEAD(pages_list);
- rem_node(ptr);
- if (munmap(ptr, get_page_size()) == 0)
- pages_kept--;
-#ifdef ENOMEM
+ node *n = HEAD(GFP->list);
+ rem_node(n);
+ add_tail(&fp->list, n);
+ fp->cnt++;
+ GFP->cnt--;
+ }
+
+ while (fp->cnt < fp->min)
+ {
+ node *n = alloc_sys_page();
+ add_tail(&fp->list, n + FP_NODE_OFFSET);
+ fp->cnt++;
+ }
+
+ while (fp->cnt > fp->max)
+ {
+ node *n = HEAD(fp->list);
+ rem_node(n);
+ add_tail(&GFP->list, n);
+ fp->cnt--;
+ GFP->cnt++;
+ }
+
+ birdloop_leave(loop);
+
+ if (GFP->cnt > GFP->max)
+ ev_send(&global_work_list, GFP->cleanup);
+}
+
+static void
+cleanup_global_pages(void *data UNUSED)
+{
+ while (GFP->cnt < GFP->max)
+ {
+ node *n = alloc_sys_page();
+ add_tail(&GFP->list, n + FP_NODE_OFFSET);
+ GFP->cnt++;
+ }
+
+ for (uint limit = GFP->cnt; (limit > 0) && (GFP->cnt > GFP->max); limit--)
+ {
+ node *n = TAIL(GFP->list);
+ rem_node(n);
+
+ if (munmap(n - FP_NODE_OFFSET, page_size) == 0)
+ GFP->cnt--;
else if (errno == ENOMEM)
- add_tail(&pages_list, ptr);
-#endif
+ add_head(&GFP->list, n);
else
- bug("munmap(%p) failed: %m", ptr);
+ bug("munmap(%p) failed: %m", n - FP_NODE_OFFSET);
}
+}
+
+void
+init_pages(struct birdloop *loop)
+{
+ struct free_pages *fp = &loop->pages;
+
+ init_list(&fp->list);
+ fp->cleanup = ev_new_init(&root_pool, cleanup_pages, loop);
+ fp->min = 4;
+ fp->max = 16;
- if (pages_kept > KEEP_PAGES)
- ev_schedule(&page_cleanup_event);
+ for (fp->cnt = 0; fp->cnt < fp->min; fp->cnt++)
+ {
+ node *n = alloc_sys_page();
+ add_tail(&fp->list, n + FP_NODE_OFFSET);
+ }
}
+
+static event global_free_pages_cleanup_event = { .hook = cleanup_global_pages };
+
+void resource_sys_init(void)
+{
+ if (!(page_size = sysconf(_SC_PAGESIZE)))
+ die("System page size must be non-zero");
+
+ if (u64_popcount(page_size) == 1)
+ {
+ init_list(&GFP->list);
+ GFP->cleanup = &global_free_pages_cleanup_event;
+ GFP->min = 0;
+ GFP->max = 256;
+ return;
+ }
+
+ log(L_WARN "Got strange memory page size (%lu), using the aligned allocator instead", page_size);
+
+ /* Too big or strange page, use the aligned allocator instead */
+ page_size = 4096;
+ use_fake = 1;
+}
+
+#else
+
+void
+resource_sys_init(void)
+{
+ page_size = 4096;
+ use_fake = 1;
+}
+
#endif
diff --git a/sysdep/unix/coroutine.c b/sysdep/unix/coroutine.c
new file mode 100644
index 00000000..e4657157
--- /dev/null
+++ b/sysdep/unix/coroutine.c
@@ -0,0 +1,205 @@
+/*
+ * BIRD Coroutines
+ *
+ * (c) 2017 Martin Mares <mj@ucw.cz>
+ * (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/coro.h"
+#include "lib/rcu.h"
+#include "lib/resource.h"
+#include "lib/timer.h"
+
+#include "conf/conf.h"
+
+#define CORO_STACK_SIZE 65536
+
+/*
+ * Implementation of coroutines based on POSIX threads
+ */
+
+#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);
+}
+
+/* Coroutines */
+struct coroutine {
+ resource r;
+ pthread_t id;
+ pthread_attr_t attr;
+ struct rcu_coro rcu;
+ void (*entry)(void *);
+ void *data;
+};
+
+static _Thread_local _Bool coro_cleaned_up = 0;
+
+static void coro_free(resource *r)
+{
+ struct coroutine *c = (void *) r;
+ rcu_coro_stop(&c->rcu);
+ ASSERT_DIE(pthread_equal(pthread_self(), c->id));
+ pthread_attr_destroy(&c->attr);
+ coro_cleaned_up = 1;
+}
+
+static void coro_dump(resource *r UNUSED) { }
+
+static struct resclass coro_class = {
+ .name = "Coroutine",
+ .size = sizeof(struct coroutine),
+ .free = coro_free,
+ .dump = coro_dump,
+};
+
+_Thread_local struct coroutine *this_coro = NULL;
+
+static void *coro_entry(void *p)
+{
+ struct coroutine *c = p;
+
+ ASSERT_DIE(c->entry);
+
+ this_coro = c;
+ rcu_coro_start(&c->rcu);
+
+ c->entry(c->data);
+ ASSERT_DIE(coro_cleaned_up);
+
+ return NULL;
+}
+
+struct coroutine *coro_run(pool *p, void (*entry)(void *), void *data)
+{
+ ASSERT_DIE(entry);
+ ASSERT_DIE(p);
+
+ struct coroutine *c = ralloc(p, &coro_class);
+
+ c->entry = entry;
+ c->data = data;
+
+ int e = 0;
+
+ if (e = pthread_attr_init(&c->attr))
+ die("pthread_attr_init() failed: %M", e);
+
+ if (e = pthread_attr_setstacksize(&c->attr, CORO_STACK_SIZE))
+ die("pthread_attr_setstacksize(%u) failed: %M", CORO_STACK_SIZE, e);
+
+ if (e = pthread_attr_setdetachstate(&c->attr, PTHREAD_CREATE_DETACHED))
+ die("pthread_attr_setdetachstate(PTHREAD_CREATE_DETACHED) failed: %M", e);
+
+ if (e = pthread_create(&c->id, &c->attr, coro_entry, c))
+ die("pthread_create() failed: %M", e);
+
+ return c;
+}
+
+void
+coro_yield(void)
+{
+ usleep(100);
+}
diff --git a/sysdep/unix/io-loop.c b/sysdep/unix/io-loop.c
new file mode 100644
index 00000000..a9927b39
--- /dev/null
+++ b/sysdep/unix/io-loop.c
@@ -0,0 +1,583 @@
+/*
+ * BIRD -- I/O and event loop
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "nest/bird.h"
+
+#include "lib/buffer.h"
+#include "lib/coro.h"
+#include "lib/lists.h"
+#include "lib/resource.h"
+#include "lib/event.h"
+#include "lib/timer.h"
+#include "lib/socket.h"
+
+#include "lib/io-loop.h"
+#include "sysdep/unix/io-loop.h"
+#include "conf/conf.h"
+
+/*
+ * Current thread context
+ */
+
+_Thread_local struct birdloop *birdloop_current = NULL;
+static _Thread_local struct birdloop *birdloop_wakeup_masked;
+static _Thread_local uint birdloop_wakeup_masked_count;
+
+event_list *
+birdloop_event_list(struct birdloop *loop)
+{
+ return &loop->event_list;
+}
+
+struct timeloop *
+birdloop_time_loop(struct birdloop *loop)
+{
+ return &loop->time;
+}
+
+pool *
+birdloop_pool(struct birdloop *loop)
+{
+ return loop->pool;
+}
+
+_Bool
+birdloop_inside(struct birdloop *loop)
+{
+ for (struct birdloop *c = birdloop_current; c; c = c->prev_loop)
+ if (loop == c)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Wakeup code for birdloop
+ */
+
+static void
+pipe_new(int *pfds)
+{
+ int rv = pipe(pfds);
+ if (rv < 0)
+ die("pipe: %m");
+
+ if (fcntl(pfds[0], F_SETFL, O_NONBLOCK) < 0)
+ die("fcntl(O_NONBLOCK): %m");
+
+ if (fcntl(pfds[1], F_SETFL, O_NONBLOCK) < 0)
+ die("fcntl(O_NONBLOCK): %m");
+}
+
+void
+pipe_drain(int fd)
+{
+ char buf[64];
+ int rv;
+
+ try:
+ rv = read(fd, buf, 64);
+ if (rv < 0)
+ {
+ if (errno == EINTR)
+ goto try;
+ if (errno == EAGAIN)
+ return;
+ die("wakeup read: %m");
+ }
+ if (rv == 64)
+ goto try;
+}
+
+void
+pipe_kick(int fd)
+{
+ u64 v = 1;
+ int rv;
+
+ try:
+ rv = write(fd, &v, sizeof(u64));
+ if (rv < 0)
+ {
+ if (errno == EINTR)
+ goto try;
+ if (errno == EAGAIN)
+ return;
+ die("wakeup write: %m");
+ }
+}
+
+static inline void
+wakeup_init(struct birdloop *loop)
+{
+ pipe_new(loop->wakeup_fds);
+}
+
+static inline void
+wakeup_drain(struct birdloop *loop)
+{
+ pipe_drain(loop->wakeup_fds[0]);
+}
+
+static inline void
+wakeup_do_kick(struct birdloop *loop)
+{
+ pipe_kick(loop->wakeup_fds[1]);
+}
+
+void
+birdloop_ping(struct birdloop *loop)
+{
+ u32 ping_sent = atomic_fetch_add_explicit(&loop->ping_sent, 1, memory_order_acq_rel);
+ if (ping_sent)
+ return;
+
+ if (loop == birdloop_wakeup_masked)
+ birdloop_wakeup_masked_count++;
+ else
+ wakeup_do_kick(loop);
+}
+
+
+/*
+ * Sockets
+ */
+
+static void
+sockets_init(struct birdloop *loop)
+{
+ init_list(&loop->sock_list);
+ loop->sock_num = 0;
+
+ BUFFER_INIT(loop->poll_sk, loop->pool, 4);
+ BUFFER_INIT(loop->poll_fd, loop->pool, 4);
+ loop->poll_changed = 1; /* add wakeup fd */
+}
+
+static void
+sockets_add(struct birdloop *loop, sock *s)
+{
+ add_tail(&loop->sock_list, &s->n);
+ loop->sock_num++;
+
+ s->index = -1;
+ loop->poll_changed = 1;
+
+ birdloop_ping(loop);
+}
+
+void
+sk_start(sock *s)
+{
+ ASSERT_DIE(birdloop_current != &main_birdloop);
+ sockets_add(birdloop_current, s);
+}
+
+static void
+sockets_remove(struct birdloop *loop, sock *s)
+{
+ rem_node(&s->n);
+ loop->sock_num--;
+
+ if (s->index >= 0)
+ {
+ loop->poll_sk.data[s->index] = NULL;
+ s->index = -1;
+ loop->poll_changed = 1;
+ loop->close_scheduled = 1;
+ birdloop_ping(loop);
+ }
+ else
+ close(s->fd);
+}
+
+void
+sk_stop(sock *s)
+{
+ sockets_remove(birdloop_current, s);
+}
+
+static inline uint sk_want_events(sock *s)
+{ return ((s->rx_hook && !ev_corked(s->cork)) ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); }
+
+/*
+FIXME: this should be called from sock code
+
+static void
+sockets_update(struct birdloop *loop, sock *s)
+{
+ if (s->index >= 0)
+ loop->poll_fd.data[s->index].events = sk_want_events(s);
+}
+*/
+
+static void
+sockets_prepare(struct birdloop *loop)
+{
+ BUFFER_SET(loop->poll_sk, loop->sock_num + 1);
+ BUFFER_SET(loop->poll_fd, loop->sock_num + 1);
+
+ struct pollfd *pfd = loop->poll_fd.data;
+ sock **psk = loop->poll_sk.data;
+ uint i = 0;
+ node *n;
+
+ WALK_LIST(n, loop->sock_list)
+ {
+ sock *s = SKIP_BACK(sock, n, n);
+
+ ASSERT(i < loop->sock_num);
+
+ s->index = i;
+ *psk = s;
+ pfd->fd = s->fd;
+ pfd->events = sk_want_events(s);
+ pfd->revents = 0;
+
+ pfd++;
+ psk++;
+ i++;
+ }
+
+ ASSERT(i == loop->sock_num);
+
+ /* Add internal wakeup fd */
+ *psk = NULL;
+ pfd->fd = loop->wakeup_fds[0];
+ pfd->events = POLLIN;
+ pfd->revents = 0;
+
+ loop->poll_changed = 0;
+}
+
+static void
+sockets_close_fds(struct birdloop *loop)
+{
+ struct pollfd *pfd = loop->poll_fd.data;
+ sock **psk = loop->poll_sk.data;
+ int poll_num = loop->poll_fd.used - 1;
+
+ int i;
+ for (i = 0; i < poll_num; i++)
+ if (psk[i] == NULL)
+ close(pfd[i].fd);
+
+ loop->close_scheduled = 0;
+}
+
+int sk_read(sock *s, int revents);
+int sk_write(sock *s);
+
+static void
+sockets_fire(struct birdloop *loop)
+{
+ struct pollfd *pfd = loop->poll_fd.data;
+ sock **psk = loop->poll_sk.data;
+ int poll_num = loop->poll_fd.used - 1;
+
+ times_update();
+
+ /* Last fd is internal wakeup fd */
+ if (pfd[poll_num].revents & POLLIN)
+ wakeup_drain(loop);
+
+ int i;
+ for (i = 0; i < poll_num; pfd++, psk++, i++)
+ {
+ int e = 1;
+
+ if (! pfd->revents)
+ continue;
+
+ if (pfd->revents & POLLNVAL)
+ die("poll: invalid fd %d", pfd->fd);
+
+ if (pfd->revents & POLLIN)
+ while (e && *psk && (*psk)->rx_hook)
+ e = sk_read(*psk, pfd->revents);
+
+ e = 1;
+ if (pfd->revents & POLLOUT)
+ {
+ loop->poll_changed = 1;
+ while (e && *psk)
+ e = sk_write(*psk);
+ }
+ }
+}
+
+
+/*
+ * Birdloop
+ */
+
+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);
+
+ root_pool.loop = &main_birdloop;
+ main_birdloop.pool = &root_pool;
+
+ birdloop_enter_locked(&main_birdloop);
+}
+
+static void birdloop_main(void *arg);
+
+void
+birdloop_free(resource *r)
+{
+ struct birdloop *loop = (void *) r;
+
+ ASSERT_DIE(loop->links == 0);
+ domain_free(loop->time.domain);
+}
+
+void
+birdloop_dump(resource *r)
+{
+ struct birdloop *loop = (void *) r;
+
+ debug("%s\n", loop->pool->name);
+}
+
+struct resclass birdloop_class = {
+ .name = "IO Loop",
+ .size = sizeof(struct birdloop),
+ .free = birdloop_free,
+ .dump = birdloop_dump,
+};
+
+struct birdloop *
+birdloop_new(pool *pp, uint order, const char *name)
+{
+ struct domain_generic *dg = domain_new(name, order);
+
+ struct birdloop *loop = ralloc(pp, &birdloop_class);
+
+ loop->time.domain = dg;
+ loop->time.loop = loop;
+
+ birdloop_enter(loop);
+
+ loop->pool = rp_new(pp, loop, name);
+ loop->parent = pp;
+ rmove(&loop->r, loop->pool);
+
+ wakeup_init(loop);
+ ev_init_list(&loop->event_list, loop, name);
+ timers_init(&loop->time, loop->pool);
+ sockets_init(loop);
+
+ init_pages(loop);
+
+ loop->time.coro = coro_run(loop->pool, birdloop_main, loop);
+
+ birdloop_leave(loop);
+
+ return loop;
+}
+
+static void
+birdloop_do_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
+{
+ loop->stopped = stopped;
+ loop->stop_data = data;
+ wakeup_do_kick(loop);
+}
+
+void
+birdloop_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
+{
+ DG_LOCK(loop->time.domain);
+ birdloop_do_stop(loop, stopped, data);
+ DG_UNLOCK(loop->time.domain);
+}
+
+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);
+}
+
+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)
+{
+ 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);
+
+ /* Restore the old context */
+ birdloop_current = loop->prev_loop;
+}
+
+void
+birdloop_leave(struct birdloop *loop)
+{
+ birdloop_leave_locked(loop);
+ DG_UNLOCK(loop->time.domain);
+}
+
+void
+birdloop_mask_wakeups(struct birdloop *loop)
+{
+ ASSERT_DIE(birdloop_wakeup_masked == NULL);
+ birdloop_wakeup_masked = loop;
+}
+
+void
+birdloop_unmask_wakeups(struct birdloop *loop)
+{
+ ASSERT_DIE(birdloop_wakeup_masked == loop);
+ birdloop_wakeup_masked = NULL;
+ if (birdloop_wakeup_masked_count)
+ wakeup_do_kick(loop);
+
+ 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
+birdloop_main(void *arg)
+{
+ struct birdloop *loop = arg;
+ timer *t;
+ int rv, timeout;
+
+ btime loop_begin = current_time();
+
+ birdloop_enter(loop);
+ while (1)
+ {
+ 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;
+ else
+ timeout = -1;
+
+ if (loop->poll_changed)
+ sockets_prepare(loop);
+
+ 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);
+ if (rv < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ goto try;
+ die("poll: %m");
+ }
+
+ birdloop_enter(loop);
+
+ if (loop->close_scheduled)
+ sockets_close_fds(loop);
+
+ if (loop->stopped)
+ break;
+
+ loop_begin = current_time();
+
+ if (rv)
+ sockets_fire(loop);
+
+ atomic_exchange_explicit(&loop->ping_sent, 0, memory_order_acq_rel);
+ }
+
+ /* 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);
+
+ /* Lock parent loop */
+ pool *parent = loop->parent;
+ birdloop_enter(parent->loop);
+
+ /* Move the loop temporarily to parent pool */
+ birdloop_enter(loop);
+ rmove(&loop->r, parent);
+ birdloop_leave(loop);
+
+ /* Announce loop stop */
+ loop->stopped(loop->stop_data);
+
+ /* Free the pool and loop */
+ birdloop_enter(loop);
+ rp_free(loop->pool, parent);
+ flush_pages(loop);
+ birdloop_leave(loop);
+ rfree(&loop->r);
+
+ /* And finally leave the parent loop before finishing */
+ birdloop_leave(parent->loop);
+}
diff --git a/sysdep/unix/io-loop.h b/sysdep/unix/io-loop.h
new file mode 100644
index 00000000..e5af52d1
--- /dev/null
+++ b/sysdep/unix/io-loop.h
@@ -0,0 +1,59 @@
+/*
+ * 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 "nest/bird.h"
+
+#include "lib/lists.h"
+#include "lib/event.h"
+#include "lib/timer.h"
+
+struct free_pages
+{
+ list list; /* List of empty pages */
+ event *cleanup; /* Event to call when number of pages is outside bounds */
+ u16 min, max; /* Minimal and maximal number of free pages kept */
+ uint cnt; /* Number of empty pages */
+};
+
+struct birdloop
+{
+ resource r;
+
+ pool *pool;
+ pool *parent;
+
+ 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;
+
+ _Atomic u32 ping_sent;
+ int wakeup_fds[2];
+
+ uint links;
+
+ struct free_pages pages;
+
+ void (*stopped)(void *data);
+ void *stop_data;
+
+ struct birdloop *prev_loop;
+};
+
+extern _Thread_local struct birdloop *birdloop_current;
+
+void init_pages(struct birdloop *loop);
+void flush_pages(struct birdloop *loop);
+
+#endif
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index 3d67d0a7..eee7b586 100644
--- a/sysdep/unix/io.c
+++ b/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;
@@ -1512,6 +1511,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)
@@ -2016,30 +2045,17 @@ 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",
@@ -2068,7 +2084,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++;
@@ -2096,14 +2112,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();
@@ -2116,7 +2132,7 @@ watchdog_start1(void)
{
io_update_time();
- loop_time = last_time;
+ loop_time = last_io_time;
}
static inline void
@@ -2124,7 +2140,7 @@ watchdog_start(void)
{
io_update_time();
- loop_time = last_time;
+ loop_time = last_io_time;
event_log_num = 0;
if (config->watchdog_timeout)
@@ -2145,7 +2161,7 @@ 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);
@@ -2160,8 +2176,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();
@@ -2175,6 +2192,9 @@ static int short_loops = 0;
#define SHORT_LOOP_MAX 10
#define WORK_EVENTS_MAX 10
+void pipe_drain(int fd);
+void check_stored_pages(void);
+
void
io_loop(void)
{
@@ -2189,27 +2209,33 @@ 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 */
s = SKIP_BACK(sock, n, n);
- if (s->rx_hook)
+ if (s->rx_hook && !ev_corked(s->cork))
{
pfd[nfds].fd = s->fd;
pfd[nfds].events |= POLLIN;
@@ -2263,7 +2289,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)
@@ -2274,7 +2302,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));
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index 7c2614b1..c0f2e930 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -74,7 +74,7 @@ static list krt_proto_list;
void
krt_io_init(void)
{
- krt_pool = rp_new(&root_pool, "Kernel Syncer");
+ krt_pool = rp_new(&root_pool, &main_birdloop, "Kernel Syncer");
krt_filter_lp = lp_new_default(krt_pool);
init_list(&krt_proto_list);
krt_sys_io_init();
@@ -251,14 +251,14 @@ static inline void
krt_trace_in(struct krt_proto *p, rte *e, char *msg)
{
if (p->p.debug & D_PACKETS)
- log(L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg);
+ log(L_TRACE "%s: %N: %s", p->p.name, e->net, msg);
}
static inline void
krt_trace_in_rl(struct tbf *f, struct krt_proto *p, rte *e, char *msg)
{
if (p->p.debug & D_PACKETS)
- log_rl(f, L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg);
+ log_rl(f, L_TRACE "%s: %N: %s", p->p.name, e->net, msg);
}
/*
@@ -277,261 +277,33 @@ static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
* the same key.
*/
-static inline int
-krt_same_key(rte *a, rte *b)
+static inline u32
+krt_metric(rte *a)
{
- return a->u.krt.metric == b->u.krt.metric;
+ eattr *ea = ea_find(a->attrs->eattrs, EA_KRT_METRIC);
+ return ea ? ea->u.data : 0;
}
static inline int
-krt_uptodate(rte *a, rte *b)
-{
- if (a->attrs != b->attrs)
- return 0;
-
- if (a->u.krt.proto != b->u.krt.proto)
- return 0;
-
- return 1;
-}
-
-static void
-krt_learn_announce_update(struct krt_proto *p, rte *e)
-{
- net *n = e->net;
- rta *aa = rta_clone(e->attrs);
- rte *ee = rte_get_temp(aa);
- ee->pflags = EA_ID_FLAG(EA_KRT_SOURCE) | EA_ID_FLAG(EA_KRT_METRIC);
- ee->u.krt = e->u.krt;
- rte_update(&p->p, n->n.addr, ee);
-}
-
-static void
-krt_learn_announce_delete(struct krt_proto *p, net *n)
+krt_rte_better(rte *a, rte *b)
{
- rte_update(&p->p, n->n.addr, NULL);
+ return (krt_metric(a) > krt_metric(b));
}
/* Called when alien route is discovered during scan */
static void
-krt_learn_scan(struct krt_proto *p, rte *e)
+krt_learn_rte(struct krt_proto *p, rte *e)
{
- net *n0 = e->net;
- net *n = net_get(p->krt_table, n0->n.addr);
- rte *m, **mm;
-
- e->attrs = rta_lookup(e->attrs);
-
- for(mm=&n->routes; m = *mm; mm=&m->next)
- if (krt_same_key(m, e))
- break;
- if (m)
- {
- if (krt_uptodate(m, e))
- {
- krt_trace_in_rl(&rl_alien, p, e, "[alien] seen");
- rte_free(e);
- m->u.krt.seen = 1;
- }
- else
- {
- krt_trace_in(p, e, "[alien] updated");
- *mm = m->next;
- rte_free(m);
- m = NULL;
- }
- }
- else
- krt_trace_in(p, e, "[alien] created");
- if (!m)
- {
- e->next = n->routes;
- n->routes = e;
- e->u.krt.seen = 1;
- }
-}
-
-static void
-krt_learn_prune(struct krt_proto *p)
-{
- struct fib *fib = &p->krt_table->fib;
- struct fib_iterator fit;
-
- KRT_TRACE(p, D_EVENTS, "Pruning inherited routes");
-
- FIB_ITERATE_INIT(&fit, fib);
-again:
- FIB_ITERATE_START(fib, &fit, net, n)
- {
- rte *e, **ee, *best, **pbest, *old_best;
-
- /*
- * Note that old_best may be NULL even if there was an old best route in
- * the previous step, because it might be replaced in krt_learn_scan().
- * But in that case there is a new valid best route.
- */
-
- old_best = NULL;
- best = NULL;
- pbest = NULL;
- ee = &n->routes;
- while (e = *ee)
- {
- if (e->u.krt.best)
- old_best = e;
-
- if (!e->u.krt.seen)
- {
- *ee = e->next;
- rte_free(e);
- continue;
- }
-
- if (!best || best->u.krt.metric > e->u.krt.metric)
- {
- best = e;
- pbest = ee;
- }
-
- e->u.krt.seen = 0;
- e->u.krt.best = 0;
- ee = &e->next;
- }
- if (!n->routes)
- {
- DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen);
- if (old_best)
- krt_learn_announce_delete(p, n);
-
- FIB_ITERATE_PUT(&fit);
- fib_delete(fib, n);
- goto again;
- }
-
- best->u.krt.best = 1;
- *pbest = best->next;
- best->next = n->routes;
- n->routes = best;
-
- if ((best != old_best) || p->reload)
- {
- DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
- krt_learn_announce_update(p, best);
- }
- else
- DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
- }
- FIB_ITERATE_END;
-
- p->reload = 0;
-}
-
-static void
-krt_learn_async(struct krt_proto *p, rte *e, int new)
-{
- net *n0 = e->net;
- net *n = net_get(p->krt_table, n0->n.addr);
- rte *g, **gg, *best, **bestp, *old_best;
-
- e->attrs = rta_lookup(e->attrs);
-
- old_best = n->routes;
- for(gg=&n->routes; g = *gg; gg = &g->next)
- if (krt_same_key(g, e))
- break;
- if (new)
- {
- if (g)
- {
- if (krt_uptodate(g, e))
- {
- krt_trace_in(p, e, "[alien async] same");
- rte_free(e);
- return;
- }
- krt_trace_in(p, e, "[alien async] updated");
- *gg = g->next;
- rte_free(g);
- }
- else
- krt_trace_in(p, e, "[alien async] created");
-
- e->next = n->routes;
- n->routes = e;
- }
- else if (!g)
- {
- krt_trace_in(p, e, "[alien async] delete failed");
- rte_free(e);
- return;
- }
- else
- {
- krt_trace_in(p, e, "[alien async] removed");
- *gg = g->next;
- rte_free(e);
- rte_free(g);
- }
- best = n->routes;
- bestp = &n->routes;
- for(gg=&n->routes; g=*gg; gg=&g->next)
- {
- if (best->u.krt.metric > g->u.krt.metric)
- {
- best = g;
- bestp = gg;
- }
-
- g->u.krt.best = 0;
- }
-
- if (best)
- {
- best->u.krt.best = 1;
- *bestp = best->next;
- best->next = n->routes;
- n->routes = best;
- }
-
- if (best != old_best)
- {
- DBG("krt_learn_async: distributing change\n");
- if (best)
- krt_learn_announce_update(p, best);
- else
- krt_learn_announce_delete(p, n);
- }
+ struct rte_src *src = e->src = rt_get_source(&p->p, krt_metric(e));
+ rte_update(p->p.main_channel, e->net, e, e->src);
+ rt_unlock_source(src);
}
static void
krt_learn_init(struct krt_proto *p)
{
if (KRT_CF->learn)
- {
- struct rtable_config *cf = mb_allocz(p->p.pool, sizeof(struct rtable_config));
- cf->name = "Inherited";
- cf->addr_type = p->p.net_type;
- cf->internal = 1;
-
- p->krt_table = rt_setup(p->p.pool, cf);
- }
-}
-
-static void
-krt_dump(struct proto *P)
-{
- struct krt_proto *p = (struct krt_proto *) P;
-
- if (!KRT_CF->learn)
- return;
- debug("KRT: Table of inheritable routes\n");
- rt_dump(p->krt_table);
-}
-
-static void
-krt_dump_attrs(rte *e)
-{
- debug(" [m=%d,p=%d]", e->u.krt.metric, e->u.krt.proto);
+ channel_setup_in_table(p->p.main_channel, 1);
}
#endif
@@ -543,47 +315,58 @@ krt_dump_attrs(rte *e)
static inline int
krt_is_installed(struct krt_proto *p, net *n)
{
- return n->routes && bmap_test(&p->p.main_channel->export_map, n->routes->id);
+ return n->routes && bmap_test(&p->p.main_channel->export_map, n->routes->rte.id);
}
-static void
-krt_flush_routes(struct krt_proto *p)
+static uint
+rte_feed_count(net *n)
{
- struct rtable *t = p->p.main_channel->table;
+ uint count = 0;
+ for (struct rte_storage *e = n->routes; e; e = e->next)
+ if (rte_is_valid(RTES_OR_NULL(e)))
+ count++;
+ return count;
+}
- KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
- FIB_WALK(&t->fib, net, n)
+static void
+rte_feed_obtain(net *n, rte **feed, uint count)
+{
+ uint i = 0;
+ for (struct rte_storage *e = n->routes; e; e = e->next)
+ if (rte_is_valid(RTES_OR_NULL(e)))
{
- if (krt_is_installed(p, n))
- {
- /* FIXME: this does not work if gw is changed in export filter */
- krt_replace_rte(p, n, NULL, n->routes);
- }
+ ASSERT_DIE(i < count);
+ feed[i++] = &e->rte;
}
- FIB_WALK_END;
+ ASSERT_DIE(i == count);
}
static struct rte *
-krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
+krt_export_net(struct krt_proto *p, net *net)
{
struct channel *c = p->p.main_channel;
const struct filter *filter = c->out_filter;
- rte *rt;
if (c->ra_mode == RA_MERGED)
- return rt_export_merged(c, net, rt_free, krt_filter_lp, 1);
+ {
+ uint count = rte_feed_count(net);
+ if (!count)
+ return NULL;
+
+ rte **feed = alloca(count * sizeof(rte *));
+ rte_feed_obtain(net, feed, count);
+ return rt_export_merged(c, feed, count, krt_filter_lp, 1);
+ }
- rt = net->routes;
- *rt_free = NULL;
+ static _Thread_local rte rt;
+ rt = net->routes->rte;
- if (!rte_is_valid(rt))
+ if (!rte_is_valid(&rt))
return NULL;
if (filter == FILTER_REJECT)
return NULL;
- rte_make_tmp_attrs(&rt, krt_filter_lp, NULL);
-
/* We could run krt_preexport() here, but it is already handled by krt_is_installed() */
if (filter == FILTER_ACCEPT)
@@ -594,13 +377,9 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
accept:
- if (rt != net->routes)
- *rt_free = rt;
- return rt;
+ return &rt;
reject:
- if (rt != net->routes)
- rte_free(rt);
return NULL;
}
@@ -624,13 +403,13 @@ krt_same_dest(rte *k, rte *e)
*/
void
-krt_got_route(struct krt_proto *p, rte *e)
+krt_got_route(struct krt_proto *p, rte *e, s8 src)
{
- rte *new = NULL, *rt_free = NULL;
- net *n = e->net;
+ rte *new = NULL;
+ e->pflags = 0;
#ifdef KRT_ALLOW_LEARN
- switch (e->u.krt.src)
+ switch (src)
{
case KRT_SRC_KERNEL:
goto ignore;
@@ -640,26 +419,28 @@ krt_got_route(struct krt_proto *p, rte *e)
case KRT_SRC_ALIEN:
if (KRT_CF->learn)
- krt_learn_scan(p, e);
+ krt_learn_rte(p, e);
else
- {
- krt_trace_in_rl(&rl_alien, p, e, "[alien] ignored");
- rte_free(e);
- }
+ krt_trace_in_rl(&rl_alien, p, e, "[alien] ignored");
return;
}
#endif
/* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
+ RT_LOCK(p->p.main_channel->table);
+ /* Deleting all routes if flush is requested */
+ if (p->flush_routes)
+ goto delete;
/* We wait for the initial feed to have correct installed state */
if (!p->ready)
goto ignore;
- if (!krt_is_installed(p, n))
+ net *net = net_find(RT_PRIV(p->p.main_channel->table), e->net);
+ if (!net || !krt_is_installed(p, net))
goto delete;
- new = krt_export_net(p, n, &rt_free);
+ new = krt_export_net(p, net);
/* Rejected by filters */
if (!new)
@@ -692,20 +473,16 @@ ignore:
update:
krt_trace_in(p, new, "updating");
- krt_replace_rte(p, n, new, e);
+ krt_replace_rte(p, e->net, new, e);
goto done;
delete:
krt_trace_in(p, e, "deleting");
- krt_replace_rte(p, n, NULL, e);
+ krt_replace_rte(p, e->net, NULL, e);
goto done;
done:
- rte_free(e);
-
- if (rt_free)
- rte_free(rt_free);
-
+ RT_UNLOCK(p->p.main_channel->table);
lp_flush(krt_filter_lp);
}
@@ -713,50 +490,65 @@ static void
krt_init_scan(struct krt_proto *p)
{
bmap_reset(&p->seen_map, 1024);
+
+#ifdef KRT_ALLOW_LEARN
+ if (KRT_CF->learn)
+ channel_refresh_begin(p->p.main_channel);
+#endif
}
static void
krt_prune(struct krt_proto *p)
{
- struct rtable *t = p->p.main_channel->table;
+ RT_LOCK(p->p.main_channel->table);
+ rtable_private *t = RT_PRIV(p->p.main_channel->table);
KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
FIB_WALK(&t->fib, net, n)
{
- if (p->ready && krt_is_installed(p, n) && !bmap_test(&p->seen_map, n->routes->id))
+ if (p->ready && krt_is_installed(p, n) && !bmap_test(&p->seen_map, n->routes->rte.id))
{
- rte *rt_free = NULL;
- rte *new = krt_export_net(p, n, &rt_free);
+ rte *new = krt_export_net(p, n);
if (new)
{
krt_trace_in(p, new, "installing");
- krt_replace_rte(p, n, new, NULL);
+ krt_replace_rte(p, n->n.addr, new, NULL);
}
- if (rt_free)
- rte_free(rt_free);
-
lp_flush(krt_filter_lp);
}
}
FIB_WALK_END;
+ RT_UNLOCK(p->p.main_channel->table);
+
#ifdef KRT_ALLOW_LEARN
if (KRT_CF->learn)
- krt_learn_prune(p);
+ channel_refresh_end(p->p.main_channel);
#endif
if (p->ready)
p->initialized = 1;
}
+static void
+krt_flush_routes(struct krt_proto *p)
+{
+ KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
+ p->flush_routes = 1;
+ krt_init_scan(p);
+ krt_do_scan(p);
+ /* No prune! */
+ p->flush_routes = 0;
+}
+
void
-krt_got_route_async(struct krt_proto *p, rte *e, int new)
+krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src)
{
- net *net = e->net;
+ e->pflags = 0;
- switch (e->u.krt.src)
+ switch (src)
{
case KRT_SRC_BIRD:
/* Should be filtered by the back end */
@@ -766,7 +558,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new)
if (new)
{
krt_trace_in(p, e, "[redirect] deleting");
- krt_replace_rte(p, net, NULL, e);
+ krt_replace_rte(p, e->net, NULL, e);
}
/* If !new, it is probably echo of our deletion */
break;
@@ -775,12 +567,11 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new)
case KRT_SRC_ALIEN:
if (KRT_CF->learn)
{
- krt_learn_async(p, e, new);
+ krt_learn_rte(p, e);
return;
}
#endif
}
- rte_free(e);
}
/*
@@ -886,29 +677,10 @@ krt_scan_timer_kick(struct krt_proto *p)
* Updates
*/
-static void
-krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
-{
- rte_init_tmp_attrs(rt, pool, 2);
- rte_make_tmp_attr(rt, EA_KRT_SOURCE, EAF_TYPE_INT, rt->u.krt.proto);
- rte_make_tmp_attr(rt, EA_KRT_METRIC, EAF_TYPE_INT, rt->u.krt.metric);
-}
-
-static void
-krt_store_tmp_attrs(struct rte *rt, struct linpool *pool)
-{
- rte_init_tmp_attrs(rt, pool, 2);
- rt->u.krt.proto = rte_store_tmp_attr(rt, EA_KRT_SOURCE);
- rt->u.krt.metric = rte_store_tmp_attr(rt, EA_KRT_METRIC);
-}
-
static int
-krt_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
+krt_preexport(struct channel *c, rte *e)
{
- // struct krt_proto *p = (struct krt_proto *) P;
- rte *e = *new;
-
- if (e->attrs->src->proto == P)
+ if (e->src->owner == &c->proto->sources)
return -1;
if (!krt_capable(e))
@@ -918,8 +690,8 @@ krt_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
}
static void
-krt_rt_notify(struct proto *P, struct channel *ch UNUSED, net *net,
- rte *new, rte *old)
+krt_rt_notify(struct proto *P, struct channel *ch UNUSED, const net_addr *net,
+ rte *new, const rte *old)
{
struct krt_proto *p = (struct krt_proto *) P;
@@ -983,14 +755,6 @@ krt_feed_end(struct channel *C)
}
-static int
-krt_rte_same(rte *a, rte *b)
-{
- /* src is always KRT_SRC_ALIEN and type is irrelevant */
- return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric);
-}
-
-
/*
* Protocol glue
*/
@@ -1049,9 +813,7 @@ krt_init(struct proto_config *CF)
p->p.if_notify = krt_if_notify;
p->p.reload_routes = krt_reload_routes;
p->p.feed_end = krt_feed_end;
- p->p.make_tmp_attrs = krt_make_tmp_attrs;
- p->p.store_tmp_attrs = krt_store_tmp_attrs;
- p->p.rte_same = krt_rte_same;
+ p->p.rte_better = krt_rte_better;
krt_sys_init(p);
return &p->p;
@@ -1207,8 +969,4 @@ struct protocol proto_unix_kernel = {
.reconfigure = krt_reconfigure,
.copy_config = krt_copy_config,
.get_attr = krt_get_attr,
-#ifdef KRT_ALLOW_LEARN
- .dump = krt_dump,
- .dump_attrs = krt_dump_attrs,
-#endif
};
diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h
index 62228f08..968c5b16 100644
--- a/sysdep/unix/krt.h
+++ b/sysdep/unix/krt.h
@@ -24,6 +24,9 @@ struct kif_proto;
#define EA_KRT_SOURCE EA_CODE(PROTOCOL_KERNEL, 0)
#define EA_KRT_METRIC EA_CODE(PROTOCOL_KERNEL, 1)
+#define KRT_REF_SEEN 0x1 /* Seen in table */
+#define KRT_REF_BEST 0x2 /* Best in table */
+
/* Whenever we recognize our own routes, we allow learing of foreign routes */
#ifdef CONFIG_SELF_CONSCIOUS
@@ -48,10 +51,6 @@ struct krt_proto {
struct proto p;
struct krt_state sys; /* Sysdep state */
-#ifdef KRT_ALLOW_LEARN
- struct rtable *krt_table; /* Internal table of inherited routes */
-#endif
-
#ifndef CONFIG_ALL_TABLES_AT_ONCE
timer *scan_timer;
#endif
@@ -63,6 +62,7 @@ struct krt_proto {
byte ready; /* Initial feed has been finished */
byte initialized; /* First scan has been finished */
byte reload; /* Next scan is doing reload */
+ byte flush_routes; /* Scanning to flush */
};
extern pool *krt_pool;
@@ -76,8 +76,8 @@ extern pool *krt_pool;
struct proto_config * kif_init_config(int class);
void kif_request_scan(void);
-void krt_got_route(struct krt_proto *p, struct rte *e);
-void krt_got_route_async(struct krt_proto *p, struct rte *e, int new);
+void krt_got_route(struct krt_proto *p, struct rte *e, s8 src);
+void krt_got_route_async(struct krt_proto *p, struct rte *e, int new, s8 src);
static inline int
krt_get_sync_error(struct krt_proto *p, struct rte *e)
@@ -140,7 +140,7 @@ void krt_sys_copy_config(struct krt_config *, struct krt_config *);
int krt_capable(rte *e);
void krt_do_scan(struct krt_proto *);
-void krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old);
+void krt_replace_rte(struct krt_proto *p, const net_addr *n, rte *new, const rte *old);
int krt_sys_get_attr(const eattr *a, byte *buf, int buflen);
diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c
index 14d18c01..68a04e78 100644
--- a/sysdep/unix/log.c
+++ b/sysdep/unix/log.c
@@ -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_coro_id = ATOMIC_VAR_INIT(1);
+static _Thread_local uint this_coro_id;
-#ifdef USE_PTHREADS
+#define THIS_CORO_ID (this_coro_id ?: (this_coro_id = atomic_fetch_add_explicit(&max_coro_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_CORO_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
@@ -309,22 +305,36 @@ die(const char *msg, ...)
void
debug(const char *msg, ...)
{
-#define MAX_DEBUG_BUFSIZE 65536
+#define MAX_DEBUG_BUFSIZE 16384
va_list args;
- static uint bufsize = 4096;
- static char *buf = NULL;
-
- if (!buf)
- buf = mb_alloc(&root_pool, bufsize);
+ char buf[MAX_DEBUG_BUFSIZE], *pos = buf;
+ int max = MAX_DEBUG_BUFSIZE;
va_start(args, msg);
if (dbgf)
{
- while (bvsnprintf(buf, bufsize, msg, args) < 0)
- if (bufsize >= MAX_DEBUG_BUFSIZE)
- bug("Extremely long debug output, split it.");
- else
- buf = mb_realloc(buf, (bufsize *= 2));
+ 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_CORO_ID);
+ pos += n;
+ max -= n;
+
+ if (bvsnprintf(pos, max, msg, args) < 0)
+ bug("Extremely long debug output, split it.");
fputs(buf, dbgf);
}
@@ -378,6 +388,21 @@ default_log_list(int initial, const char **syslog_name)
}
void
+log_cleanup(int syslog)
+{
+ struct log_config *l;
+
+ if (current_log_list)
+ WALK_LIST(l, *current_log_list)
+ if (l->rf)
+ log_close(l);
+
+ if (syslog && current_syslog_name)
+ closelog();
+}
+
+
+void
log_switch(int initial, list *logs, const char *new_syslog_name)
{
struct log_config *l;
@@ -389,10 +414,7 @@ log_switch(int initial, list *logs, const char *new_syslog_name)
logs = default_log_list(initial, &new_syslog_name);
/* Close the logs to avoid pinning them on disk when deleted */
- if (current_log_list)
- WALK_LIST(l, *current_log_list)
- if (l->rf)
- log_close(l);
+ log_cleanup(0);
/* Reopen the logs, needed for 'configure undo' */
if (logs)
@@ -429,6 +451,8 @@ done:
void
log_init_debug(char *f)
{
+ clock_gettime(CLOCK_MONOTONIC, &dbg_time_start);
+
if (dbgf && dbgf != stderr)
fclose(dbgf);
if (!f)
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index 392aff9d..c326ba2b 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -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/route.h"
@@ -51,7 +52,7 @@ async_dump(void)
{
debug("INTERNAL STATE DUMP\n\n");
- rdump(&root_pool);
+ rp_dump(&root_pool);
sk_dump_all();
// XXXX tm_dump_all();
if_dump_all();
@@ -201,7 +202,9 @@ sysdep_preconfig(struct config *c)
int
sysdep_commit(struct config *new, struct config *old UNUSED)
{
- log_switch(0, &new->logfiles, new->syslog_name);
+ if (!new->shutdown)
+ log_switch(0, &new->logfiles, new->syslog_name);
+
return 0;
}
@@ -479,6 +482,14 @@ cli_err(sock *s, int err)
cli_free(s->data);
}
+static void
+cli_connect_err(sock *s UNUSED, int err)
+{
+ ASSERT_DIE(err);
+ if (config->cli_debug)
+ log(L_INFO "Failed to accept CLI connection: %s", strerror(err));
+}
+
static int
cli_connect(sock *s, uint size UNUSED)
{
@@ -507,6 +518,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid)
s = cli_sk = sk_new(cli_pool);
s->type = SK_UNIX_PASSIVE;
s->rx_hook = cli_connect;
+ s->err_hook = cli_connect_err;
s->rbsize = 1024;
s->fast_rx = 1;
@@ -603,6 +615,7 @@ sysdep_shutdown_done(void)
unlink_pid_file();
unlink(path_control_socket);
log_msg(L_FATAL "Shutdown completed");
+ log_cleanup(1);
exit(0);
}
@@ -673,7 +686,7 @@ signal_init(void)
* Parsing of command-line arguments
*/
-static char *opt_list = "bc:dD:ps:P:u:g:flRh";
+static char *opt_list = "c:dD:ps:P:u:g:flRh";
int parse_and_exit;
char *bird_name;
static char *use_user;
@@ -852,6 +865,8 @@ parse_args(int argc, char **argv)
}
}
+void resource_sys_init(void);
+
/*
* Hic Est main()
*/
@@ -864,13 +879,17 @@ main(int argc, char **argv)
dmalloc_debug(0x2f03d00);
#endif
+ times_update();
+ resource_sys_init();
parse_args(argc, argv);
log_switch(1, NULL, NULL);
+ the_bird_lock();
+
random_init();
net_init();
resource_init();
- timer_init();
+ birdloop_init();
olock_init();
io_init();
rt_init();
@@ -920,6 +939,7 @@ main(int argc, char **argv)
dup2(0, 2);
}
+
main_thread_init();
write_pid_file();
diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h
index ad85d1ea..51ec1b1e 100644
--- a/sysdep/unix/unix.h
+++ b/sysdep/unix/unix.h
@@ -122,6 +122,7 @@ void krt_io_init(void);
void main_thread_init(void);
void log_init_debug(char *); /* Initialize debug dump to given file (NULL=stderr, ""=off) */
void log_switch(int initial, list *l, const char *);
+void log_cleanup(int syslog);
struct log_config {
node n;
diff --git a/test/birdtest.c b/test/birdtest.c
index a1da078f..c6a09684 100644
--- a/test/birdtest.c
+++ b/test/birdtest.c
@@ -58,11 +58,14 @@ u64 bt_random_state[] = {
0x53d9772877c1b647, 0xab8ce3eb466de6c5, 0xad02844c8a8e865f, 0xe8cc78080295065d
};
+void resource_sys_init(void);
+
void
bt_init(int argc, char *argv[])
{
int c;
+ resource_sys_init();
initstate(BT_RANDOM_SEED, (char *) bt_random_state, sizeof(bt_random_state));
bt_verbose = 0;
@@ -240,7 +243,7 @@ bt_log_result(int result, u64 time, const char *fmt, va_list argptr)
printf("%s\n", result_str);
if (do_die && !result)
- abort();
+ exit(1);
}
static u64
diff --git a/test/bt-utils.c b/test/bt-utils.c
index cbca3a6b..98aaed3d 100644
--- a/test/bt-utils.c
+++ b/test/bt-utils.c
@@ -53,16 +53,20 @@ cf_file_read(byte *dest, uint max_len, int fd)
return l;
}
+void resource_sys_init(void);
+
void
bt_bird_init(void)
{
+ resource_sys_init();
if(bt_verbose)
log_init_debug("");
log_switch(bt_verbose != 0, NULL, NULL);
+ the_bird_lock();
resource_init();
+ birdloop_init();
olock_init();
- timer_init();
io_init();
rt_init();
if_init();
@@ -79,6 +83,7 @@ void bt_bird_cleanup(void)
class_to_protocol[i] = NULL;
config = new_config = NULL;
+ the_bird_unlock();
}
static char *