pkg-fedora-34-amd64:
<<: *pkg-rpm
needs: [build-fedora-34-amd64]
- image: registry.labs.nic.cz/labs/bird:fedora-34-amd64
+ image: registry.nic.cz/labs/bird:fedora-34-amd64
-#pkg-centos-7-amd64:
-# <<: *pkg-rpm-wa
-# variables:
-# LC_ALL: en_US.UTF-8
-# needs: [build-centos-7-amd64]
-# image: registry.nic.cz/labs/bird:centos-7-amd64
-
pkg-centos-8-amd64:
<<: *pkg-rpm-wa
needs: [build-centos-8-amd64]
+Version 3.0alpha2 (2023-05-11)
+ o Fixed memory leaks and use-after free bugs
+ o Simple thread work balancing
+ o MRT switched off
+ o Slow kernel route synchronization to be fixed later
+
+Version 3.0alpha1 (2023-04-18)
+ o Worker threads for BGP, Pipe, RPKI and BFD
+ o Configurable number of threads
+ o Asynchronous route export
+ o Flat attribute structure
+ o Inline import tables
+ o Export tables merged with BGP prefix / attribute buckets
+ o Fixed ROA check locking inversion in route table dumps
+ o MRT switched off
+
+Version 3.0-alpha0 (2022-02-07)
+ o Removal of fixed protocol-specific route attributes
+ o Asynchronous route export
+ o Explicit table import / export hooks
+ o Partially lockless route attribute cache
+ o Thread-safe resource management
+ o Thread-safe interface notifications
+ o Thread-safe protocol API
+ o Adoption of BFD IO loop for general use
+ o Parallel Pipe protocol
+ o Parallel RPKI protocol
+ o Parallel BGP protocol
+ o Lots of refactoring
+ o Bugfixes and improvements as they came along
+
+ Version 2.13.1 (2023-06-23)
+ o BGP: Fix role check when no capability option is present
+ o Filter: Fixed segfault when a case option had an empty block
+
+ This is a bugfix version.
+
Version 2.13 (2023-04-21)
o Babel: IPv4 via IPv6 extension (RFC 9229)
o Babel: Improve authentication on lossy networks
struct proto_spec ps;
struct channel_limit cl;
struct timeformat *tf;
- mpls_label_stack *mls;
+ struct settle_config settle;
+ struct adata *ad;
- struct bytestring *bs;
+ const struct bytestring *bs;
}
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
# Executing another filter line. This replaces the recursion
# that was needed in the former implementation.
m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
- m4_define(LINEX_, `do {
+ m4_define(LINEX_, `do if ($1) {
+ 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;
struct timeloop
{
BUFFER_(timer *) timers;
- btime last_time;
- btime real_time;
+ struct domain_generic *domain;
+ struct birdloop *loop;
};
+#define TLOCK_TIMER_ASSERT(loop) ASSERT_DIE((loop)->domain && DG_IS_LOCKED((loop)->domain))
+#define TLOCK_LOCAL_ASSERT(loop) ASSERT_DIE(!(loop)->domain || DG_IS_LOCKED((loop)->domain))
+
static inline uint timers_count(struct timeloop *loop)
-{ return loop->timers.used - 1; }
+{ TLOCK_TIMER_ASSERT(loop); return loop->timers.used - 1; }
static inline timer *timers_first(struct timeloop *loop)
-{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
-
-extern struct timeloop main_timeloop;
+{ TLOCK_TIMER_ASSERT(loop); return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
-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)
+ /* In sysdep code */
+ btime current_time_now(void);
+
//#define now (current_time() TO_S)
//#define now_real (current_real_time() TO_S)
extern btime boot_time;
prepare: $(o)proto-build.c
- tests_src :=
-tests_src := a-set_test.c a-path_test.c rt-fib_test.c
++tests_src := rt-fib_test.c
tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files)
--- /dev/null
-#include "nest/route.h"
+ /*
+ * BIRD -- Forwarding Information Base -- Tests
+ *
+ * (c) 2023 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+ #include "test/birdtest.h"
+ #include "test/bt-utils.h"
+
- bt_bird_init();
++#include "nest/rt.h"
+
+
+ #define TESTS_NUM 10
+ #define PREFIXES_NUM 400000
+ #define PREFIX_TESTS_NUM 200000
+ #define PREFIX_BENCH_MAX 1000000
+ #define PREFIX_BENCH_NUM 10000000
+
+ struct test_node
+ {
+ int pos;
+ struct fib_node n;
+ };
+
+ static inline int net_match(struct test_node *tn, net_addr *query, net_addr *data)
+ { return (tn->pos < PREFIXES_NUM) && net_equal(query, &data[tn->pos]); }
+
+ static int
+ t_match_random_net(void)
+ {
- pool *p = rp_new(&root_pool, "FIB pool");
+ bt_config_parse(BT_CONFIG_SIMPLE);
+
+ for (int round = 0; round < TESTS_NUM; round++)
+ {
+ int type = !(round & 1) ? NET_IP4 : NET_IP6;
+
- bt_bird_cleanup();
++ pool *p = rp_new(&root_pool, the_bird_domain.the_bird, "FIB pool");
+ net_addr *nets = bt_random_nets(type, PREFIXES_NUM);
+
+ /* Make FIB structure */
+ struct fib f;
+ fib_init(&f, &root_pool, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 4, NULL);
+
+ for (int i = 0; i < PREFIXES_NUM; i++)
+ {
+ struct test_node *tn = fib_get(&f, &nets[i]);
+ bt_assert(!tn->pos || net_match(tn, &nets[i], nets));
+ tn->pos = i;
+ }
+
+ /* Test (mostly) negative matches */
+ for (int i = 0; i < PREFIX_TESTS_NUM; i++)
+ {
+ net_addr net;
+ bt_random_net(&net, type);
+
+ struct test_node *tn = fib_find(&f, &net);
+ bt_assert(!tn || net_match(tn, &net, nets));
+ }
+
+ /* Test positive matches */
+ for (int i = 0; i < PREFIX_TESTS_NUM; i++)
+ {
+ int j = bt_random_n(PREFIXES_NUM);
+
+ struct test_node *tn = fib_find(&f, &nets[j]);
+ bt_assert(tn && net_match(tn, &nets[j], nets));
+ }
+
+ rfree(p);
+ tmp_flush();
+ }
+
- bt_bird_init();
+ return 1;
+ }
+
+ static int
+ t_fib_walk(void)
+ {
- pool *p = rp_new(&root_pool, "FIB pool");
+ bt_config_parse(BT_CONFIG_SIMPLE);
+
+ for (int round = 0; round < TESTS_NUM; round++)
+ {
+ int type = !(round & 1) ? NET_IP4 : NET_IP6;
+
- bt_bird_cleanup();
++ pool *p = rp_new(&root_pool, the_bird_domain.the_bird, "FIB pool");
+ net_addr *nets = bt_random_nets(type, PREFIXES_NUM);
+ byte *marks = tmp_allocz(PREFIXES_NUM);
+
+ /* Make FIB structure */
+ struct fib f;
+ fib_init(&f, p, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 4, NULL);
+
+ for (int i = 1; i < PREFIXES_NUM; i++)
+ {
+ struct test_node *tn = fib_get(&f, &nets[i]);
+ bt_assert(!tn->pos || net_match(tn, &nets[i], nets));
+ if (tn->pos)
+ {
+ /* Mark dupicate nets */
+ bt_assert(!marks[tn->pos]);
+ marks[tn->pos] = 1;
+ }
+ tn->pos = i;
+ }
+
+ /* Walk FIB and mark nets */
+ FIB_WALK(&f, struct test_node, tn)
+ {
+ bt_assert(!marks[tn->pos]);
+ marks[tn->pos] = 1;
+ }
+ FIB_WALK_END;
+
+ /* Check in all nets are marked */
+ for (int i = 1; i < PREFIXES_NUM; i++)
+ bt_assert(marks[i]);
+
+ rfree(p);
+ tmp_flush();
+ }
+
- pool *p = rp_new(&root_pool, "FIB pool");
+ return 1;
+ }
+
+ static int
+ benchmark_fib_dataset(const char *filename, int type)
+ {
+ net_addr *nets, *test_r, *test_s;
+ uint n = PREFIX_BENCH_MAX;
+ int tn = PREFIX_BENCH_NUM;
+ int match;
+
+ bt_reset_suite_case_timer();
+ bt_log_suite_case_result(1, "Reading %s", filename, n);
+ nets = bt_read_net_file(filename, type, &n);
+ bt_log_suite_case_result(1, "Read net data, %u nets", n);
+ bt_reset_suite_case_timer();
+
- bt_bird_init();
++ pool *p = rp_new(&root_pool, the_bird_domain.the_bird, "FIB pool");
+
+ /* Make FIB structure */
+ struct fib f;
+ fib_init(&f, p, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 0, NULL);
+
+ for (int i = 0; i < (int) n; i++)
+ {
+ struct test_node *tn = fib_get(&f, &nets[i]);
+ tn->pos = i;
+ }
+
+ bt_log_suite_case_result(1, "Fill FIB structure, %u nets, order %u", n, f.hash_order);
+ bt_reset_suite_case_timer();
+
+ /* Compute FIB size */
+ size_t fib_size = rmemsize(p).effective * 1000 / (1024*1024);
+ bt_log_suite_case_result(1, "FIB size: %u.%03u MB", (uint) (fib_size / 1000), (uint) (fib_size % 1000));
+
+ /* Compute FIB histogram */
+ uint hist[16] = {};
+ uint sum = 0;
+ for (uint i = 0; i < f.hash_size; i++)
+ {
+ int len = 0;
+ for (struct fib_node *fn = f.hash_table[i]; fn; fn = fn->next)
+ len++;
+
+ sum += len;
+ len = MIN(len, 15);
+ hist[len]++;
+ }
+ bt_log_suite_case_result(1, "FIB histogram:");
+ for (uint i = 0; i < 16; i++)
+ if (hist[i])
+ bt_log_suite_case_result(1, "%02u: %8u", i, hist[i]);
+
+ uint avg = (sum * 1000) / (f.hash_size - hist[0]);
+ bt_log_suite_case_result(1, "FIB chain length: %u.%03u", (uint) (avg / 1000), (uint) (avg % 1000));
+ bt_reset_suite_case_timer();
+
+ /* Make test data */
+ test_r = bt_random_nets(type, tn);
+ test_s = bt_random_net_subset(nets, n, tn);
+
+ bt_log_suite_case_result(1, "Make test data, 2x %u nets", tn);
+ bt_reset_suite_case_timer();
+
+ /* Test (mostly negative) random matches */
+ match = 0;
+ for (int i = 0; i < tn; i++)
+ if (fib_find(&f, &test_r[i]))
+ match++;
+
+ bt_log_suite_case_result(1, "Random match, %d / %d matches", match, tn);
+ bt_reset_suite_case_timer();
+
+ /* Test (positive) subset matches */
+ match = 0;
+ for (int i = 0; i < tn; i++)
+ if (fib_find(&f, &test_s[i]))
+ match++;
+
+ bt_log_suite_case_result(1, "Subset match, %d / %d matches", match, tn);
+ bt_log_suite_case_result(1, "");
+ bt_reset_suite_case_timer();
+
+ rfree(p);
+ tmp_flush();
+ return 1;
+ }
+
+ static int UNUSED
+ t_bench_fib_datasets(void)
+ {
- bt_bird_cleanup();
-
+ bt_config_parse(BT_CONFIG_SIMPLE);
+
+ /* Specific datasets, not included */
+ benchmark_fib_dataset("fib-data-bgp-v4-1", NET_IP4);
+ benchmark_fib_dataset("fib-data-bgp-v4-10", NET_IP4);
+ benchmark_fib_dataset("fib-data-bgp-v6-1", NET_IP6);
+ benchmark_fib_dataset("fib-data-bgp-v6-10", NET_IP6);
+
+ return 1;
+ }
+
+ int
+ main(int argc, char *argv[])
+ {
+ bt_init(argc, argv);
++ bt_bird_init();
+
+ bt_test_suite(t_match_random_net, "Testing random prefix matching");
+ bt_test_suite(t_fib_walk, "Testing FIB_WALK() on random FIB");
+
+ // bt_test_suite(t_bench_fib_datasets, "Benchmark FIB from datasets by random subset of nets");
+
+ return bt_exit_value();
+ }
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
- NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
+ NEXT, HOP, IPV4, IPV6, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
- EXTENDED)
+ EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS)
CF_GRAMMAR
RETRANS, TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, LOCAL,
TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH, PROPAGATE, ROUTE,
- ROUTES)
- ROUTES, RA_PREFERENCE, RA_LIFETIME, CUSTOM, OPTION, TYPE, VALUE)
++ ROUTES, CUSTOM, OPTION, TYPE, VALUE)
CF_ENUM(T_ENUM_RA_PREFERENCE, RA_PREF_, LOW, MEDIUM, HIGH)
}
if (a[RTA_GATEWAY])
- ra->nh.gw = rta_get_ipa(a[RTA_GATEWAY]);
+ nhad.nh.gw = rta_get_ipa(a[RTA_GATEWAY]);
- #ifdef HAVE_MPLS_KERNEL
if (a[RTA_VIA])
- ra->nh.gw = rta_get_via(a[RTA_VIA]);
+ nhad.nh.gw = rta_get_via(a[RTA_VIA]);
- #endif
if (i->rtm_flags & RTNH_F_ONLINK)
- ra->nh.flags |= RNF_ONLINK;
+ nhad.nh.flags |= RNF_ONLINK;
- if (ipa_nonzero(ra->nh.gw))
+ if (ipa_nonzero(nhad.nh.gw))
{
/* Silently skip strange 6to4 routes */
const net_addr_ip6 sit = NET_ADDR_IP6(IP6_NONE, 96);
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");
}
-
+ btime
+ current_time_now(void)
+ {
+ struct timespec ts;
+ int rv;
+
+ rv = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (rv < 0)
+ die("clock_gettime: %m");
+
+ return ts.tv_sec S + ts.tv_nsec NS;
+ }
+
/**
* DOC: Sockets
*
uint bt_naive_pow(uint base, uint power);
void bt_bytes_to_hex(char *buf, const byte *in_data, size_t size);
+ void bt_random_net(net_addr *net, int type);
+ net_addr *bt_random_nets(int type, uint n);
+ net_addr *bt_random_net_subset(net_addr *src, uint sn, uint dn);
+ void bt_read_net(const char *str, net_addr *net, int type);
+ net_addr *bt_read_nets(FILE *f, int type, uint *n);
+ net_addr *bt_read_net_file(const char *filename, int type, uint *n);
void bt_bird_init(void);
-void bt_bird_cleanup(void);
struct config *bt_config_parse(const char *cfg);
struct config *bt_config_file_parse(const char *filepath);