summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Doc1
-rw-r--r--lib/Makefile7
-rw-r--r--lib/Modules35
-rw-r--r--lib/alloca.h2
-rw-r--r--lib/birdlib.h66
-rw-r--r--lib/bitops.c6
-rw-r--r--lib/bitops.h4
-rw-r--r--lib/bitops_test.c123
-rw-r--r--lib/buffer.h18
-rw-r--r--lib/buffer_test.c167
-rw-r--r--lib/checksum_test.c94
-rw-r--r--lib/event_test.c89
-rw-r--r--lib/fletcher16_test.c169
-rw-r--r--lib/flowspec.c1176
-rw-r--r--lib/flowspec.h152
-rw-r--r--lib/flowspec_test.c639
-rw-r--r--lib/hash.h36
-rw-r--r--lib/hash_test.c305
-rw-r--r--lib/heap_test.c186
-rw-r--r--lib/idm.c76
-rw-r--r--lib/idm.h25
-rw-r--r--lib/ip.c8
-rw-r--r--lib/ip.h189
-rw-r--r--lib/ip_test.c161
-rw-r--r--lib/lists.c12
-rw-r--r--lib/lists.h6
-rw-r--r--lib/lists_test.c287
-rw-r--r--lib/mac_test.c1159
-rw-r--r--lib/mempool.c72
-rw-r--r--lib/net.c308
-rw-r--r--lib/net.h550
-rw-r--r--lib/patmatch_test.c149
-rw-r--r--lib/printf.c141
-rw-r--r--lib/printf_test.c79
-rw-r--r--lib/resource.c4
-rw-r--r--lib/resource.h14
-rw-r--r--lib/slist_test.c384
-rw-r--r--lib/slists.c82
-rw-r--r--lib/socket.h87
-rw-r--r--lib/string.h11
-rw-r--r--lib/tbf.c30
-rw-r--r--lib/timer.c378
-rw-r--r--lib/timer.h127
-rw-r--r--lib/unaligned.h33
44 files changed, 7255 insertions, 392 deletions
diff --git a/lib/Doc b/lib/Doc
index 632bf1f6..3877f3a3 100644
--- a/lib/Doc
+++ b/lib/Doc
@@ -3,6 +3,7 @@ S ip.c
S lists.c
S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c
S mac.c
+S flowspec.c
D resource.sgml
S resource.c
S mempool.c
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 00000000..01f3114d
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,7 @@
+src := bitops.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 tbf.c timer.c xmalloc.c
+obj := $(src-o-files)
+$(all-daemon)
+
+tests_src := heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c
+tests_targets := $(tests_targets) $(tests-target-files)
+tests_objs := $(tests_objs) $(src-o-files)
diff --git a/lib/Modules b/lib/Modules
deleted file mode 100644
index 0e30b488..00000000
--- a/lib/Modules
+++ /dev/null
@@ -1,35 +0,0 @@
-sha256.c
-sha256.h
-sha512.c
-sha512.h
-sha1.c
-sha1.h
-birdlib.h
-bitops.c
-bitops.h
-ip.h
-ip.c
-lists.c
-lists.h
-mac.c
-mac.h
-md5.c
-md5.h
-mempool.c
-resource.c
-resource.h
-slab.c
-socket.h
-tbf.c
-unaligned.h
-xmalloc.c
-printf.c
-string.h
-patmatch.c
-slists.c
-slists.h
-event.c
-event.h
-checksum.c
-checksum.h
-alloca.h
diff --git a/lib/alloca.h b/lib/alloca.h
index f0d61bb4..e5557cdb 100644
--- a/lib/alloca.h
+++ b/lib/alloca.h
@@ -15,4 +15,6 @@
#include <stdlib.h>
#endif
+#define allocz(len) ({ void *_x = alloca(len); memset(_x, 0, len); _x; })
+
#endif
diff --git a/lib/birdlib.h b/lib/birdlib.h
index aaa7a0a3..428b3209 100644
--- a/lib/birdlib.h
+++ b/lib/birdlib.h
@@ -9,8 +9,7 @@
#ifndef _BIRD_BIRDLIB_H_
#define _BIRD_BIRDLIB_H_
-#include "timer.h"
-#include "alloca.h"
+#include "lib/alloca.h"
/* Ugly structure offset handling macros */
@@ -41,6 +40,12 @@ struct align_probe { char x; long int y; };
#define CALL(fn, args...) ({ if (fn) fn(args); })
#define ADVANCE(w, r, l) ({ r -= l; w += l; })
+static inline int uint_cmp(uint i1, uint i2)
+{ return (int)(i1 > i2) - (int)(i1 < i2); }
+
+static inline int u64_cmp(u64 i1, u64 i2)
+{ return (int)(i1 > i2) - (int)(i1 < i2); }
+
/* Bitfield macros */
@@ -55,34 +60,20 @@ struct align_probe { char x; long int y; };
#define NULL ((void *) 0)
#endif
-#ifndef IPV6
-#define IP_VERSION 4
-#else
-#define IP_VERSION 6
-#endif
-
-
/* Macros for gcc attributes */
#define NORET __attribute__((noreturn))
#define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed))
-#ifdef IPV6
-#define UNUSED4
-#define UNUSED6 UNUSED
-#else
-#define UNUSED4 UNUSED
-#define UNUSED6
-#endif
-
/* Microsecond time */
typedef s64 btime;
+//typedef s64 bird_clock_t;
-#define S_ *1000000
-#define MS_ *1000
-#define US_ *1
+#define S_ * (btime) 1000000
+#define MS_ * (btime) 1000
+#define US_ * (btime) 1
#define TO_S /1000000
#define TO_MS /1000
#define TO_US /1
@@ -91,39 +82,26 @@ typedef s64 btime;
#define S S_
#define MS MS_
#define US US_
+#define NS /1000
#endif
+#define TIME_INFINITY ((s64) 0x7fffffffffffffff)
+
/* Rate limiting */
struct tbf {
- bird_clock_t timestamp; /* Last update */
- u16 count; /* Available tokens */
+ btime timestamp; /* Last update */
+ u64 count; /* Available micro-tokens */
u16 burst; /* Max number of tokens */
- u16 rate; /* Rate of replenishment */
- u16 mark; /* Whether last op was limited */
+ u16 rate; /* Rate of replenishment (tokens / sec) */
+ u32 drop; /* Number of failed request since last successful */
};
/* Default TBF values for rate limiting log messages */
#define TBF_DEFAULT_LOG_LIMITS { .rate = 1, .burst = 5 }
-void tbf_update(struct tbf *f);
-
-static inline int
-tbf_limit(struct tbf *f)
-{
- tbf_update(f);
-
- if (!f->count)
- {
- f->mark = 1;
- return 1;
- }
-
- f->count--;
- f->mark = 0;
- return 0;
-}
+int tbf_limit(struct tbf *f);
/* Logging and dying */
@@ -163,7 +141,7 @@ void bug(const char *msg, ...) NORET;
#define L_FATAL "\010" /* Fatal errors */
#define L_BUG "\011" /* BIRD bugs */
-void debug(const char *msg, ...); /* Printf to debug output */
+void debug(const char *msg, ...); /* Printf to debug output */
/* Debugging */
@@ -174,9 +152,9 @@ void debug(const char *msg, ...); /* Printf to debug output */
#endif
#ifdef DEBUGGING
-#define ASSERT(x) do { if (!(x)) bug("Assertion `%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
+#define ASSERT(x) do { if (!(x)) bug("Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
#else
-#define ASSERT(x) do { } while(0)
+#define ASSERT(x) do { if (!(x)) log(L_BUG "Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
#endif
/* Pseudorandom numbers */
diff --git a/lib/bitops.c b/lib/bitops.c
index 81586e87..efb8710e 100644
--- a/lib/bitops.c
+++ b/lib/bitops.c
@@ -28,15 +28,15 @@ u32_mkmask(uint n)
*
* This function checks whether the given integer @x represents
* a valid bit mask (binary representation contains first ones, then
- * zeroes) and returns the number of ones or -1 if the mask is invalid.
+ * zeroes) and returns the number of ones or 255 if the mask is invalid.
*/
-int
+uint
u32_masklen(u32 x)
{
int l = 0;
u32 n = ~x;
- if (n & (n+1)) return -1;
+ if (n & (n+1)) return 255;
if (x & 0x0000ffff) { x &= 0x0000ffff; l += 16; }
if (x & 0x00ff00ff) { x &= 0x00ff00ff; l += 8; }
if (x & 0x0f0f0f0f) { x &= 0x0f0f0f0f; l += 4; }
diff --git a/lib/bitops.h b/lib/bitops.h
index ce13732a..af648c26 100644
--- a/lib/bitops.h
+++ b/lib/bitops.h
@@ -9,6 +9,8 @@
#ifndef _BIRD_BITOPTS_H_
#define _BIRD_BITOPTS_H_
+#include "sysdep/config.h"
+
/*
* Bit mask operations:
*
@@ -19,7 +21,7 @@
*/
u32 u32_mkmask(uint n);
-int u32_masklen(u32 x);
+uint u32_masklen(u32 x);
u32 u32_log2(u32 v);
diff --git a/lib/bitops_test.c b/lib/bitops_test.c
new file mode 100644
index 00000000..f816b9d1
--- /dev/null
+++ b/lib/bitops_test.c
@@ -0,0 +1,123 @@
+/*
+ * BIRD Library -- Generic Bit Operations Tests
+ *
+ * (c) 2015 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" /* naive_pow() */
+
+#include "lib/bitops.h"
+
+#define MAX_NUM 1000
+#define CHECK_BIT(var,pos) ((var) & (u32)(1<<(pos)))
+
+static int
+t_mkmask(void)
+{
+ int i;
+ u32 compute, expect;
+
+ bt_assert(u32_mkmask(0) == 0x00000000);
+ for (i = 1; i <= 32; i++)
+ {
+ compute = u32_mkmask(i);
+ expect = (u32) (0xffffffff << (32-i));
+ bt_assert_msg(compute == expect, "u32_mkmask(%d) = 0x%08X, expected 0x%08X", i, compute, expect);
+ }
+
+ return 1;
+}
+
+static int
+u32_masklen_expected(u32 mask)
+{
+ int j, expect = 0;
+
+ int valid = 0;
+ for (j = 0; j <= 32; j++)
+ if (mask == (j ? (0xffffffff << (32-j)) : 0)) /* Shifting 32-bit value by 32 bits is undefined behavior */
+ valid = 1;
+
+ if (!valid && mask != 0)
+ expect = 255;
+ else
+ for (j = 0; j <= 31; j++)
+ if (CHECK_BIT(mask, (31-j)))
+ expect = j+1;
+ else
+ break;
+ return expect;
+}
+
+static void
+check_mask(u32 mask)
+{
+ int expected, masklen;
+
+ expected = u32_masklen_expected(mask);
+ masklen = u32_masklen(mask);
+ int ok = (expected == masklen);
+ bt_debug("u32_masklen(Ox%08x) = %d, expected %d %s\n", mask, masklen, expected, ok ? "OK" : "FAIL!");
+ bt_assert(ok);
+}
+
+static int
+t_masklen(void)
+{
+ u32 i;
+
+ check_mask(0x82828282);
+ check_mask(0x00000000);
+
+ for (i = 0; i <= 32; i++)
+ check_mask(((u32) (i ? (0xffffffff << (32-i)) : 0)) & 0xffffffff); /* Shifting 32-bit value by 32 bits is undefined behavior */
+
+ for (i = 0; i <= MAX_NUM; i++)
+ check_mask(bt_random());
+
+ return 1;
+}
+
+static void
+check_log2(u32 n)
+{
+ u32 log = u32_log2(n);
+ u32 low = bt_naive_pow(2, log);
+ u32 high = bt_naive_pow(2, log+1);
+
+ bt_assert_msg(n >= low && n < high,
+ "u32_log2(%u) = %u, %u should be in the range <%u, %u)",
+ n, log, n, low, high);
+}
+
+static int
+t_log2(void)
+{
+ u32 i;
+
+ for (i = 0; i < 31; i++)
+ bt_assert(u32_log2(bt_naive_pow(2, i+1)) == i+1);
+
+ for (i = 1; i < MAX_NUM; i++)
+ check_log2(i);
+
+ for (i = 1; i < MAX_NUM; i++)
+ check_log2(((u32) bt_random()) % 0x0fffffff);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_mkmask, "u32_mkmask()");
+ bt_test_suite(t_masklen, "u32_masklen()");
+ bt_test_suite(t_log2, "u32_log2()");
+
+ return bt_exit_value();
+}
diff --git a/lib/buffer.h b/lib/buffer.h
index 2a53f211..cd9bab86 100644
--- a/lib/buffer.h
+++ b/lib/buffer.h
@@ -13,10 +13,14 @@
#include "lib/resource.h"
#include "sysdep/config.h"
-#define BUFFER(type) struct { type *data; uint used, size; }
-
+#define BUFFER_(type) struct { type *data; uint used, size; }
+#define BUFFER_TYPE(v) typeof(* (v).data)
#define BUFFER_SIZE(v) ((v).size * sizeof(* (v).data))
+#ifndef PARSER
+#define BUFFER(type) BUFFER_(type)
+#endif
+
#define BUFFER_INIT(v,pool,isize) \
({ \
(v).used = 0; \
@@ -46,4 +50,14 @@
#define BUFFER_FLUSH(v) ({ (v).used = 0; })
+#define BUFFER_WALK(v,n) \
+ for (BUFFER_TYPE(v) *_n = (v).data, n; _n < ((v).data + (v).used) && (n = *_n, 1); _n++)
+
+#define BUFFER_SHALLOW_COPY(dst, src) \
+ ({ \
+ (dst).used = (src).used; \
+ (dst).size = (src).size; \
+ (dst).data = (src).data; \
+ })
+
#endif /* _BIRD_BUFFER_H_ */
diff --git a/lib/buffer_test.c b/lib/buffer_test.c
new file mode 100644
index 00000000..5b7de330
--- /dev/null
+++ b/lib/buffer_test.c
@@ -0,0 +1,167 @@
+/*
+ * BIRD Library -- Buffer Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdlib.h>
+
+#include "test/birdtest.h"
+
+#include "lib/buffer.h"
+
+#define MAX_NUM 33
+
+typedef BUFFER(int) buffer_int;
+static int expected[MAX_NUM];
+static buffer_int buf;
+static struct pool *buffer_pool;
+
+static void
+show_buf(buffer_int *b)
+{
+ uint i;
+ bt_debug(".used = %d, .size = %d\n", b->used, b->size);
+
+ for (i = 0; i < b->used; i++)
+ bt_debug(" .data[%3u] = %-16d expected %-16d %s\n", i, b->data[i], expected[i], (b->data[i] == expected[i] ? "OK" : "FAIL!"));
+}
+
+static void
+fill_expected_array(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM; i++)
+ expected[i] = bt_random();
+}
+
+static void
+init_buffer(void)
+{
+ resource_init();
+ buffer_pool = &root_pool;
+ BUFFER_INIT(buf, buffer_pool, MAX_NUM);
+}
+
+static int
+is_buffer_as_expected(buffer_int *b)
+{
+ show_buf(b);
+
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ bt_assert(b->data[i] == expected[i]);
+ return 1;
+}
+
+static int
+t_buffer_push(void)
+{
+ int i;
+
+ init_buffer();
+ fill_expected_array();
+
+ for (i = 0; i < MAX_NUM; i++)
+ BUFFER_PUSH(buf) = expected[i];
+ is_buffer_as_expected(&buf);
+
+ return 1;
+}
+
+static int
+t_buffer_pop(void)
+{
+ int i;
+
+ init_buffer();
+ fill_expected_array();
+
+ /* POP a half of elements */
+ for (i = 0; i < MAX_NUM; i++)
+ BUFFER_PUSH(buf) = expected[i];
+ for (i = MAX_NUM-1; i >= MAX_NUM/2; i--)
+ BUFFER_POP(buf);
+ for (i = MAX_NUM/2; i < MAX_NUM; i++)
+ BUFFER_PUSH(buf) = expected[i] = bt_random();
+ is_buffer_as_expected(&buf);
+
+ /* POP all of elements */
+ for (i = MAX_NUM-1; i >= 0; i--)
+ BUFFER_POP(buf);
+ bt_assert(buf.used == 0);
+ for (i = 0; i < MAX_NUM; i++)
+ BUFFER_PUSH(buf) = expected[i];
+ is_buffer_as_expected(&buf);
+
+ return 1;
+}
+
+static int
+t_buffer_resize(void)
+{
+ int i;
+
+ init_buffer();
+ BUFFER_INIT(buf, buffer_pool, 0);
+ fill_expected_array();
+
+ for (i = 0; i < MAX_NUM; i++)
+ BUFFER_PUSH(buf) = expected[i];
+ is_buffer_as_expected(&buf);
+ bt_assert(buf.size >= MAX_NUM);
+
+ return 1;
+}
+
+static int
+t_buffer_flush(void)
+{
+ int i;
+
+ init_buffer();
+ fill_expected_array();
+ for (i = 0; i < MAX_NUM; i++)
+ BUFFER_PUSH(buf) = expected[i];
+
+ BUFFER_FLUSH(buf);
+ bt_assert(buf.used == 0);
+
+ return 1;
+}
+
+static int
+t_buffer_walk(void)
+{
+ int i;
+
+ init_buffer();
+ fill_expected_array();
+ for (i = 0; i < MAX_NUM; i++)
+ BUFFER_PUSH(buf) = expected[i];
+
+ i = 0;
+ BUFFER_WALK(buf, v)
+ bt_assert(v == expected[i++]);
+
+ bt_assert(i == MAX_NUM);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_buffer_push, "Pushing new elements");
+ bt_test_suite(t_buffer_pop, "Fill whole buffer (PUSH), a half of elements POP and PUSH new elements");
+ bt_test_suite(t_buffer_resize, "Init a small buffer and try overfill");
+ bt_test_suite(t_buffer_flush, "Fill and flush all elements");
+ bt_test_suite(t_buffer_walk, "Fill and walk through buffer");
+
+ return bt_exit_value();
+}
diff --git a/lib/checksum_test.c b/lib/checksum_test.c
new file mode 100644
index 00000000..7e5658eb
--- /dev/null
+++ b/lib/checksum_test.c
@@ -0,0 +1,94 @@
+/*
+ * BIRD Library -- IP One-Complement Checksum Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+
+#include "test/birdtest.h"
+
+#include "lib/checksum.h"
+
+#define MAX_NUM 10000
+
+static u16
+ipsum_calculate_expected(u32 *a)
+{
+ int i;
+ u32 sum = 0;
+
+ for(i = 0; i < MAX_NUM; i++)
+ {
+ sum += a[i] & 0xffff;
+ bt_debug("low) \t0x%08X \n", sum);
+
+ sum += a[i] >> 16;
+ bt_debug("high) \t0x%08X \n", sum);
+
+ u16 carry = sum >> 16;
+ sum = (sum & 0xffff) + carry;
+ bt_debug("carry) \t0x%08X \n\n", sum);
+ }
+ bt_debug("sum) \t0x%08X \n", sum);
+
+ sum = sum ^ 0xffff;
+ bt_debug("~sum) \t0x%08X \n", sum);
+
+ return sum;
+}
+
+static int
+t_calculate(void)
+{
+ u32 a[MAX_NUM];
+ int i;
+
+ for (i = 0; i < MAX_NUM; i++)
+ a[i] = bt_random();
+
+ u16 sum_calculated = ipsum_calculate(a, sizeof(a), NULL);
+ u16 sum_calculated_2 = ipsum_calculate(&a[0], sizeof(u32)*(MAX_NUM/2), &a[MAX_NUM/2], sizeof(u32)*(MAX_NUM - MAX_NUM/2), NULL);
+ bt_assert(sum_calculated == sum_calculated_2);
+
+ u16 sum_expected = ipsum_calculate_expected(a);
+
+ bt_debug("sum_calculated: %08X \n", sum_calculated);
+ bt_debug("sum_expected: %08X \n", sum_expected);
+
+ bt_assert(sum_calculated == sum_expected);
+
+ return 1;
+}
+
+static int
+t_verify(void)
+{
+ u32 a[MAX_NUM+1];
+ int i;
+
+ for (i = 0; i < MAX_NUM; i++)
+ a[i] = bt_random();
+
+ u16 sum = ipsum_calculate_expected(a);
+
+ a[MAX_NUM] = sum;
+
+ bt_assert(ipsum_verify(a, sizeof(a), NULL));
+
+ return 1;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_calculate, "Checksum of pseudo-random data");
+ bt_test_suite(t_verify, "Verification of pseudo-random data.");
+
+ return bt_exit_value();
+}
diff --git a/lib/event_test.c b/lib/event_test.c
new file mode 100644
index 00000000..e1215bba
--- /dev/null
+++ b/lib/event_test.c
@@ -0,0 +1,89 @@
+/*
+ * BIRD Library -- Event Processing Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+
+#include "test/birdtest.h"
+
+#include "lib/net.h"
+#include "lib/event.h"
+#include "conf/conf.h"
+#include "nest/locks.h"
+#include "sysdep/unix/unix.h"
+#include "nest/iface.h"
+#include "nest/route.h"
+
+#define MAX_NUM 4
+
+int event_check_points[MAX_NUM];
+
+#define event_hook_body(num) \
+ do { \
+ bt_debug("Event Hook " #num "\n"); \
+ event_check_points[num] = 1; \
+ bt_assert_msg(event_check_points[num-1], "Events should be run in right order"); \
+ } while (0)
+
+static void event_hook_1(void *data UNUSED) { event_hook_body(1); }
+static void event_hook_2(void *data UNUSED) { event_hook_body(2); }
+static void event_hook_3(void *data UNUSED) { event_hook_body(3); }
+
+#define schedule_event(num) \
+ do { \
+ struct event *event_##num = ev_new(&root_pool); \
+ event_##num->hook = event_hook_##num; \
+ ev_schedule(event_##num); \
+ } while (0)
+
+static void
+init_event_check_points(void)
+{
+ int i;
+ event_check_points[0] = 1;
+ for (i = 1; i < MAX_NUM; i++)
+ event_check_points[i] = 0;
+}
+
+static int
+t_ev_run_list(void)
+{
+ int i;
+
+ resource_init();
+ olock_init();
+ timer_init();
+ io_init();
+ rt_init();
+ if_init();
+// roa_init();
+ config_init();
+ config = config_alloc("");
+
+ init_event_check_points();
+
+ schedule_event(1);
+ schedule_event(2);
+ schedule_event(3);
+
+ ev_run_list(&global_event_list);
+
+ for (i = 1; i < MAX_NUM; i++)
+ bt_assert(event_check_points[i]);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_ev_run_list, "Schedule and run 3 events in right order.");
+
+ return bt_exit_value();
+}
+
diff --git a/lib/fletcher16_test.c b/lib/fletcher16_test.c
new file mode 100644
index 00000000..1020e6ec
--- /dev/null
+++ b/lib/fletcher16_test.c
@@ -0,0 +1,169 @@
+/*
+ * BIRD Library -- Fletcher-16 Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+#include "lib/fletcher16.h"
+
+static u16
+straightforward_fletcher16_compute(const char *data)
+{
+ int count = strlen(data);
+
+ u16 sum1 = 0;
+ u16 sum2 = 0;
+ int index;
+
+ for (index = 0; index < count; ++index)
+ {
+ sum1 = (sum1 + data[index]) % 255;
+ sum2 = (sum2 + sum1) % 255;
+ }
+
+ return (sum2 << 8) | sum1;
+}
+
+static u16
+straightforward_fletcher16_checksum(const char *data)
+{
+ u16 csum;
+ u8 c0,c1,f0,f1;
+
+ csum = straightforward_fletcher16_compute(data);
+ f0 = csum & 0xff;
+ f1 = (csum >> 8) & 0xff;
+ c0 = 0xff - ((f0 + f1) % 0xff);
+ c1 = 0xff - ((f0 + c0) % 0xff);
+
+ return (c1 << 8) | c0;
+}
+
+static int
+test_fletcher16(void *out_, const void *in_, const void *expected_out_)
+{
+ u16 *out = out_;
+ const char *in = in_;
+ const u16 *expected_out = expected_out_;
+
+ struct fletcher16_context ctxt;
+
+ fletcher16_init(&ctxt);
+ fletcher16_update(&ctxt, in, strlen(in));
+ put_u16(out, fletcher16_compute(&ctxt));
+
+ return *out == *expected_out;
+}
+
+static int
+test_fletcher16_checksum(void *out_, const void *in_, const void *expected_out_)
+{
+ u16 *out = out_;
+ const char *in = in_;
+ const u16 *expected_out = expected_out_;
+
+ struct fletcher16_context ctxt;
+ int len = strlen(in);
+
+ fletcher16_init(&ctxt);
+ fletcher16_update(&ctxt, in, len);
+ put_u16(out, fletcher16_final(&ctxt, len, len));
+
+ return *out == *expected_out;
+}
+
+static int
+t_fletcher16_compute(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "\001\002",
+ .out = & (const u16) { 0x0403 },
+ },
+ {
+ .in = "",
+ .out = & ((const u16) { straightforward_fletcher16_compute("") }),
+ },
+ {
+ .in = "a",
+ .out = & ((const u16) { straightforward_fletcher16_compute("a") }),
+ },
+ {
+ .in = "abcd",
+ .out = & ((const u16) { straightforward_fletcher16_compute("abcd") }),
+ },
+ {
+ .in = "message digest",
+ .out = & ((const u16) { straightforward_fletcher16_compute("message digest") }),
+ },
+ {
+ .in = "abcdefghijklmnopqrstuvwxyz",
+ .out = & ((const u16) { straightforward_fletcher16_compute("abcdefghijklmnopqrstuvwxyz") }),
+ },
+ {
+ .in = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ .out = & ((const u16) { straightforward_fletcher16_compute("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") }),
+ },
+ {
+ .in = "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ .out = & ((const u16) { straightforward_fletcher16_compute("12345678901234567890123456789012345678901234567890123456789012345678901234567890") }),
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_fletcher16, bt_fmt_str, bt_fmt_unsigned);
+}
+
+static int
+t_fletcher16_checksum(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "\001\002",
+ .out = & ((const u16) { straightforward_fletcher16_checksum("\001\002") }),
+ },
+ {
+ .in = "",
+ .out = & ((const u16) { straightforward_fletcher16_checksum("") }),
+ },
+ {
+ .in = "a",
+ .out = & ((const u16) { straightforward_fletcher16_checksum("a") }),
+ },
+ {
+ .in = "abcd",
+ .out = & ((const u16) { straightforward_fletcher16_checksum("abcd") }),
+ },
+ {
+ .in = "message digest",
+ .out = & ((const u16) { straightforward_fletcher16_checksum("message digest") }),
+ },
+ {
+ .in = "abcdefghijklmnopqrstuvwxyz",
+ .out = & ((const u16) { straightforward_fletcher16_checksum("abcdefghijklmnopqrstuvwxyz") }),
+ },
+ {
+ .in = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ .out = & ((const u16) { straightforward_fletcher16_checksum("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") }),
+ },
+ {
+ .in = "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ .out = & ((const u16) { straightforward_fletcher16_checksum("12345678901234567890123456789012345678901234567890123456789012345678901234567890") }),
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_fletcher16_checksum, bt_fmt_str, bt_fmt_unsigned);
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_fletcher16_compute, "Fletcher-16 Compute Tests");
+ bt_test_suite(t_fletcher16_checksum, "Fletcher-16 Checksum Tests");
+
+ return bt_exit_value();
+}
diff --git a/lib/flowspec.c b/lib/flowspec.c
new file mode 100644
index 00000000..87ce0206
--- /dev/null
+++ b/lib/flowspec.c
@@ -0,0 +1,1176 @@
+/*
+ * BIRD Library -- Flow specification (RFC 5575)
+ *
+ * (c) 2016 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: Flow specification (flowspec)
+ *
+ * Flowspec are rules (RFC 5575) for firewalls disseminated using BGP protocol.
+ * The |flowspec.c| is a library for handling flowspec binary streams and
+ * flowspec data structures. You will find there functions for validation
+ * incoming flowspec binary streams, iterators for jumping over components,
+ * functions for handling a length and functions for formatting flowspec data
+ * structure into user-friendly text representation.
+ *
+ * In this library, you will find also flowspec builder. In |confbase.Y|, there
+ * are grammar's rules for parsing and building new flowspec data structure
+ * from BIRD's configuration files and from BIRD's command line interface.
+ * Finalize function will assemble final &net_addr_flow4 or &net_addr_flow6
+ * data structure.
+ *
+ * The data structures &net_addr_flow4 and &net_addr_flow6 are defined in
+ * |net.h| file. The attribute length is size of whole data structure plus
+ * binary stream representation of flowspec including a compressed encoded
+ * length of flowspec.
+ *
+ * Sometimes in code, it is used expression flowspec type, it should mean
+ * flowspec component type.
+ */
+
+#include "nest/bird.h"
+#include "lib/flowspec.h"
+#include "conf/conf.h"
+
+
+static const char* flow4_type_str[] = {
+ [FLOW_TYPE_DST_PREFIX] = "dst",
+ [FLOW_TYPE_SRC_PREFIX] = "src",
+ [FLOW_TYPE_IP_PROTOCOL] = "proto",
+ [FLOW_TYPE_PORT] = "port",
+ [FLOW_TYPE_DST_PORT] = "dport",
+ [FLOW_TYPE_SRC_PORT] = "sport",
+ [FLOW_TYPE_ICMP_TYPE] = "icmp type",
+ [FLOW_TYPE_ICMP_CODE] = "icmp code",
+ [FLOW_TYPE_TCP_FLAGS] = "tcp flags",
+ [FLOW_TYPE_PACKET_LENGTH] = "length",
+ [FLOW_TYPE_DSCP] = "dscp",
+ [FLOW_TYPE_FRAGMENT] = "fragment"
+};
+
+static const char* flow6_type_str[] = {
+ [FLOW_TYPE_DST_PREFIX] = "dst",
+ [FLOW_TYPE_SRC_PREFIX] = "src",
+ [FLOW_TYPE_NEXT_HEADER] = "next header",
+ [FLOW_TYPE_PORT] = "port",
+ [FLOW_TYPE_DST_PORT] = "dport",
+ [FLOW_TYPE_SRC_PORT] = "sport",
+ [FLOW_TYPE_ICMP_TYPE] = "icmp type",
+ [FLOW_TYPE_ICMP_CODE] = "icmp code",
+ [FLOW_TYPE_TCP_FLAGS] = "tcp flags",
+ [FLOW_TYPE_PACKET_LENGTH] = "length",
+ [FLOW_TYPE_DSCP] = "dscp",
+ [FLOW_TYPE_FRAGMENT] = "fragment",
+ [FLOW_TYPE_LABEL] = "label"
+};
+
+/**
+ * flow_type_str - get stringified flowspec name of component
+ * @type: flowspec component type
+ * @ipv6: IPv4/IPv6 decide flag, use zero for IPv4 and one for IPv6
+ *
+ * This function returns flowspec name of component @type in string.
+ */
+const char *
+flow_type_str(enum flow_type type, int ipv6)
+{
+ return ipv6 ? flow6_type_str[type] : flow4_type_str[type];
+}
+
+/*
+ * Length
+ */
+
+/**
+ * flow_write_length - write compressed length value
+ * @data: destination buffer to write
+ * @len: the value of the length (0 to 0xfff) for writing
+ *
+ * This function writes appropriate as (1- or 2-bytes) the value of @len into
+ * buffer @data. The function returns number of written bytes, thus 1 or 2 bytes.
+ */
+uint
+flow_write_length(byte *data, u16 len)
+{
+ if (len >= 0xf0)
+ {
+ put_u16(data, len | 0xf000);
+ return 2;
+ }
+
+ *data = len;
+ return 1;
+}
+
+inline static uint
+get_value_length(const byte *op)
+{
+ return (1 << ((*op & 0x30) >> 4));
+}
+
+
+
+/*
+ * Flowspec iterators
+ */
+
+static inline u8 num_op(const byte *op) { return (*op & 0x07); }
+static inline int isset_and(const byte *op) { return ((*op & 0x40) == 0x40); }
+static inline int isset_end(const byte *op) { return ((*op & 0x80) == 0x80); }
+
+static const byte *
+flow_first_part(const byte *data)
+{
+ if (!data || flow_read_length(data) == 0)
+ return NULL;
+
+ /* It is allowed to encode the value of length less then 240 into 2-bytes too */
+ if ((data[0] & 0xf0) == 0xf0)
+ return data + 2;
+
+ return data + 1;
+}
+
+/**
+ * flow4_first_part - get position of the first flowspec component
+ * @f: flowspec data structure &net_addr_flow4
+ *
+ * This function return a position to the beginning of the first flowspec
+ * component in IPv4 flowspec @f.
+ */
+inline const byte *
+flow4_first_part(const net_addr_flow4 *f)
+{
+ return f ? flow_first_part(f->data) : NULL;
+}
+
+/**
+ * flow6_first_part - get position of the first flowspec component
+ * @f: flowspec data structure &net_addr_flow6
+ *
+ * This function return a position to the beginning of the first flowspec
+ * component in IPv6 flowspec @f.
+ */
+inline const byte *
+flow6_first_part(const net_addr_flow6 *f)
+{
+ return f ? flow_first_part(f->data) : NULL;
+}
+
+static const byte *
+flow_next_part(const byte *pos, const byte *end, int ipv6)
+{
+ switch (*pos++)
+ {
+ case FLOW_TYPE_DST_PREFIX:
+ case FLOW_TYPE_SRC_PREFIX:
+ {
+ uint pxlen = *pos++;
+ uint bytes = BYTES(pxlen);
+ if (ipv6)
+ {
+ uint offset = *pos++ / 8;
+ pos += bytes - offset;
+ }
+ else
+ {
+ pos += bytes;
+ }
+ break;
+ }
+
+ case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
+ case FLOW_TYPE_PORT:
+ case FLOW_TYPE_DST_PORT:
+ case FLOW_TYPE_SRC_PORT:
+ case FLOW_TYPE_ICMP_TYPE:
+ case FLOW_TYPE_ICMP_CODE:
+ case FLOW_TYPE_TCP_FLAGS:
+ case FLOW_TYPE_PACKET_LENGTH:
+ case FLOW_TYPE_DSCP:
+ case FLOW_TYPE_FRAGMENT:
+ case FLOW_TYPE_LABEL:
+ {
+ /* Is this the end of list operator-value pair? */
+ uint last = 0;
+
+ while (!last)
+ {
+ last = isset_end(pos);
+
+ /* Value length of operator */
+ uint len = get_value_length(pos);
+ pos += 1+len;
+ }
+ break;
+ }
+ default:
+ return NULL;
+ }
+
+ return (pos < end) ? pos : NULL;
+}
+
+/**
+ * flow4_next_part - an iterator over flowspec components in flowspec binary stream
+ * @pos: the beginning of a previous or the first component in flowspec binary
+ * stream
+ * @end: the last valid byte in scanned flowspec binary stream
+ *
+ * This function returns a position to the beginning of the next component
+ * (to a component type byte) in flowspec binary stream or %NULL for the end.
+ */
+inline const byte *
+flow4_next_part(const byte *pos, const byte *end)
+{
+ return flow_next_part(pos, end, 0);
+}
+
+/**
+ * flow6_next_part - an iterator over flowspec components in flowspec binary stream
+ * @pos: the beginning of a previous or the first component in flowspec binary
+ * stream
+ * @end: the last valid byte in scanned flowspec binary stream
+ *
+ * This function returns a position to the beginning of the next component
+ * (to a component type byte) in flowspec binary stream or %NULL for the end.
+ */
+inline const byte *
+flow6_next_part(const byte *pos, const byte *end)
+{
+ return flow_next_part(pos, end, 1);
+}
+
+
+/*
+ * Flowspec validation
+ */
+
+static const char* flow_validated_state_str_[] = {
+ [FLOW_ST_UNKNOWN_COMPONENT] = "Unknown component",
+ [FLOW_ST_VALID] = "Valid",
+ [FLOW_ST_NOT_COMPLETE] = "Not complete",
+ [FLOW_ST_EXCEED_MAX_PREFIX_LENGTH] = "Exceed maximal prefix length",
+ [FLOW_ST_EXCEED_MAX_PREFIX_OFFSET] = "Exceed maximal prefix offset",
+ [FLOW_ST_EXCEED_MAX_VALUE_LENGTH] = "Exceed maximal value length",
+ [FLOW_ST_BAD_TYPE_ORDER] = "Bad component order",
+ [FLOW_ST_AND_BIT_SHOULD_BE_UNSET] = "The AND-bit should be unset",
+ [FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED] = "The Zero-bit should be unset",
+ [FLOW_ST_DEST_PREFIX_REQUIRED] = "Destination prefix is missing",
+ [FLOW_ST_INVALID_TCP_FLAGS] = "TCP flags exceeding 0xfff",
+ [FLOW_ST_CANNOT_USE_DONT_FRAGMENT] = "Cannot use Don't fragment flag in IPv6 flow"
+};
+
+/**
+ * flow_validated_state_str - return a textual description of validation process
+ * @code: validation result
+ *
+ * This function return well described validation state in string.
+ */
+const char *
+flow_validated_state_str(enum flow_validated_state code)
+{
+ return flow_validated_state_str_[code];
+}
+
+static const u8 flow4_max_value_length[] = {
+ [FLOW_TYPE_DST_PREFIX] = 0,
+ [FLOW_TYPE_SRC_PREFIX] = 0,
+ [FLOW_TYPE_IP_PROTOCOL] = 1,
+ [FLOW_TYPE_PORT] = 2,
+ [FLOW_TYPE_DST_PORT] = 2,
+ [FLOW_TYPE_SRC_PORT] = 2,
+ [FLOW_TYPE_ICMP_TYPE] = 1,
+ [FLOW_TYPE_ICMP_CODE] = 1,
+ [FLOW_TYPE_TCP_FLAGS] = 2,
+ [FLOW_TYPE_PACKET_LENGTH] = 2,
+ [FLOW_TYPE_DSCP] = 1,
+ [FLOW_TYPE_FRAGMENT] = 1 /* XXX */
+};
+
+static const u8 flow6_max_value_length[] = {
+ [FLOW_TYPE_DST_PREFIX] = 0,
+ [FLOW_TYPE_SRC_PREFIX] = 0,
+ [FLOW_TYPE_NEXT_HEADER] = 1,
+ [FLOW_TYPE_PORT] = 2,
+ [FLOW_TYPE_DST_PORT] = 2,
+ [FLOW_TYPE_SRC_PORT] = 2,
+ [FLOW_TYPE_ICMP_TYPE] = 1,
+ [FLOW_TYPE_ICMP_CODE] = 1,
+ [FLOW_TYPE_TCP_FLAGS] = 2,
+ [FLOW_TYPE_PACKET_LENGTH] = 2,
+ [FLOW_TYPE_DSCP] = 1,
+ [FLOW_TYPE_FRAGMENT] = 1, /* XXX */
+ [FLOW_TYPE_LABEL] = 4
+};
+
+static u8
+flow_max_value_length(enum flow_type type, int ipv6)
+{
+ return ipv6 ? flow6_max_value_length[type] : flow4_max_value_length[type];
+}
+
+/**
+ * flow_check_cf_bmk_values - check value/bitmask part of flowspec component
+ * @fb: flow builder instance
+ * @neg: negation operand
+ * @val: value from value/mask pair
+ * @mask: bitmap mask from value/mask pair
+ *
+ * This function checks value/bitmask pair. If some problem will appear, the
+ * function calls cf_error() function with a textual description of reason
+ * to failing of validation.
+ */
+void
+flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mask)
+{
+ flow_check_cf_value_length(fb, val);
+ flow_check_cf_value_length(fb, mask);
+
+ if (neg && !(val == 0 || val == mask))
+ cf_error("For negation, value must be zero or bitmask");
+
+ if ((fb->this_type == FLOW_TYPE_TCP_FLAGS) && (mask & 0xf000))
+ cf_error("Invalid mask 0x%x, must not exceed 0xfff", mask);
+
+ if ((fb->this_type == FLOW_TYPE_FRAGMENT) && fb->ipv6 && (mask & 0x01))
+ cf_error("Invalid mask 0x%x, bit 0 must be 0", mask);
+
+ if (val & ~mask)
+ cf_error("Value 0x%x outside bitmask 0x%x", val, mask);
+}
+
+/**
+ * flow_check_cf_value_length - check value by flowspec component type
+ * @fb: flow builder instance
+ * @val: value
+ *
+ * This function checks if the value is in range of component's type support.
+ * If some problem will appear, the function calls cf_error() function with
+ * a textual description of reason to failing of validation.
+ */
+void
+flow_check_cf_value_length(struct flow_builder *fb, u32 val)
+{
+ enum flow_type t = fb->this_type;
+ u8 max = flow_max_value_length(t, fb->ipv6);
+
+ if (t == FLOW_TYPE_DSCP && val > 0x3f)
+ cf_error("%s value %u out of range (0-63)", flow_type_str(t, fb->ipv6), val);
+
+ if (max == 1 && (val > 0xff))
+ cf_error("%s value %u out of range (0-255)", flow_type_str(t, fb->ipv6), val);
+
+ if (max == 2 && (val > 0xffff))
+ cf_error("%s value %u out of range (0-65535)", flow_type_str(t, fb->ipv6), val);
+}
+
+static enum flow_validated_state
+flow_validate(const byte *nlri, uint len, int ipv6)
+{
+ enum flow_type type = 0;
+ const byte *pos = nlri;
+ const byte *end = nlri + len;
+ int met_dst_pfx = 0;
+
+ while (pos < end)
+ {
+ /* Check increasing type ordering */
+ if (*pos <= type)
+ return FLOW_ST_BAD_TYPE_ORDER;
+ type = *pos++;
+
+ switch (type)
+ {
+ case FLOW_TYPE_DST_PREFIX:
+ met_dst_pfx = 1;
+ /* Fall through */
+ case FLOW_TYPE_SRC_PREFIX:
+ {
+ uint pxlen = *pos++;
+ if (pxlen > (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH))
+ return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+
+ uint bytes = BYTES(pxlen);
+ if (ipv6)
+ {
+ uint pxoffset = *pos++;
+ if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
+ return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
+ bytes -= pxoffset / 8;
+ }
+ pos += bytes;
+
+ break;
+ }
+
+ case FLOW_TYPE_LABEL:
+ if (!ipv6)
+ return FLOW_ST_UNKNOWN_COMPONENT;
+ /* fall through */
+ case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
+ case FLOW_TYPE_PORT:
+ case FLOW_TYPE_DST_PORT:
+ case FLOW_TYPE_SRC_PORT:
+ case FLOW_TYPE_ICMP_TYPE:
+ case FLOW_TYPE_ICMP_CODE:
+ case FLOW_TYPE_TCP_FLAGS:
+ case FLOW_TYPE_PACKET_LENGTH:
+ case FLOW_TYPE_DSCP:
+ case FLOW_TYPE_FRAGMENT:
+ {
+ uint last = 0;
+ uint first = 1;
+
+ while (!last)
+ {
+ /*
+ * 0 1 2 3 4 5 6 7
+ * +---+---+---+---+---+---+---+---+
+ * | e | a | len | 0 |lt |gt |eq |
+ * +---+---+---+---+---+---+---+---+
+ *
+ * Numeric operator
+ */
+
+ last = isset_end(pos);
+
+ /* The AND bit should in the first operator byte of a sequence */
+ if (first && isset_and(pos))
+ return FLOW_ST_AND_BIT_SHOULD_BE_UNSET;
+
+ /* This bit should be zero */
+ if (*pos & 0x08)
+ return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
+
+ if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT)
+ {
+ /*
+ * 0 1 2 3 4 5 6 7
+ * +---+---+---+---+---+---+---+---+
+ * | e | a | len | 0 | 0 |not| m |
+ * +---+---+---+---+---+---+---+---+
+ *
+ * Bitmask operand
+ */
+ if (*pos & 0x04)
+ return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
+ }
+
+ /* Value length of operator */
+ uint len = get_value_length(pos);
+ if (len > flow_max_value_length(type, ipv6))
+ return FLOW_ST_EXCEED_MAX_VALUE_LENGTH;
+
+ /* TCP Flags component must not check highest nibble (just 12 valid bits) */
+ if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2) && (pos[1] & 0xf0))
+ return FLOW_ST_INVALID_TCP_FLAGS;
+
+ /* Bit-7 must be 0 [draft-ietf-idr-flow-spec-v6] */
+ if ((type == FLOW_TYPE_FRAGMENT) && ipv6 && (pos[1] & 0x01))
+ return FLOW_ST_CANNOT_USE_DONT_FRAGMENT;
+ /* XXX: Could be a fragment component encoded in 2-bytes? */
+
+ pos += 1+len;
+
+ if (pos > end && !last)
+ return FLOW_ST_NOT_COMPLETE;
+
+ if (pos > (end+1))
+ return FLOW_ST_NOT_COMPLETE;
+
+ first = 0;
+ }
+ break;
+ }
+ default:
+ return FLOW_ST_UNKNOWN_COMPONENT;
+ }
+ }
+
+ if (pos != end)
+ return FLOW_ST_NOT_COMPLETE;
+
+ if (!ipv6 && !met_dst_pfx)
+ return FLOW_ST_DEST_PREFIX_REQUIRED;
+
+ return FLOW_ST_VALID;
+}
+
+/**
+ * flow4_validate - check untrustworthy IPv4 flowspec data stream
+ * @nlri: flowspec data stream without compressed encoded length value
+ * @len: length of @nlri
+ *
+ * This function checks meaningfulness of binary flowspec. It should return
+ * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it
+ * returns some other %FLOW_ST_xxx state.
+ */
+inline enum flow_validated_state
+flow4_validate(const byte *nlri, uint len)
+{
+ return flow_validate(nlri, len, 0);
+}
+
+/**
+ * flow6_validate - check untrustworthy IPv6 flowspec data stream
+ * @nlri: flowspec binary stream without encoded length value
+ * @len: length of @nlri
+ *
+ * This function checks meaningfulness of binary flowspec. It should return
+ * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it
+ * returns some other %FLOW_ST_xxx state.
+ */
+inline enum flow_validated_state
+flow6_validate(const byte *nlri, uint len)
+{
+ return flow_validate(nlri, len, 1);
+}
+
+/**
+ * flow4_validate_cf - validate flowspec data structure &net_addr_flow4 in parsing time
+ * @f: flowspec data structure &net_addr_flow4
+ *
+ * Check if @f is valid flowspec data structure. Can call cf_error() function
+ * with a textual description of reason to failing of validation.
+ */
+void
+flow4_validate_cf(net_addr_flow4 *f)
+{
+ enum flow_validated_state r = flow4_validate(flow4_first_part(f), flow_read_length(f->data));
+
+ if (r != FLOW_ST_VALID)
+ cf_error("Invalid flow route: %s", flow_validated_state_str(r));
+}
+
+/**
+ * flow6_validate_cf - validate flowspec data structure &net_addr_flow6 in parsing time
+ * @f: flowspec data structure &net_addr_flow6
+ *
+ * Check if @f is valid flowspec data structure. Can call cf_error() function
+ * with a textual description of reason to failing of validation.
+ */
+void
+flow6_validate_cf(net_addr_flow6 *f)
+{
+ enum flow_validated_state r = flow6_validate(flow6_first_part(f), flow_read_length(f->data));
+
+ if (r != FLOW_ST_VALID)
+ cf_error("Invalid flow route: %s", flow_validated_state_str(r));
+}
+
+
+/*
+ * Flowspec Builder
+ */
+
+/**
+ * flow_builder_init - constructor for flowspec builder instance
+ * @pool: memory pool
+ *
+ * This function prepares flowspec builder instance using memory pool @pool.
+ */
+struct flow_builder *
+flow_builder_init(pool *pool)
+{
+ struct flow_builder *fb = mb_allocz(pool, sizeof(struct flow_builder));
+ BUFFER_INIT(fb->data, pool, 4);
+ return fb;
+}
+
+static int
+is_stackable_type(enum flow_type type)
+{
+ switch (type)
+ {
+ case FLOW_TYPE_IP_PROTOCOL:
+ case FLOW_TYPE_PORT:
+ case FLOW_TYPE_DST_PORT:
+ case FLOW_TYPE_SRC_PORT:
+ case FLOW_TYPE_ICMP_TYPE:
+ case FLOW_TYPE_ICMP_CODE:
+ case FLOW_TYPE_TCP_FLAGS:
+ case FLOW_TYPE_PACKET_LENGTH:
+ case FLOW_TYPE_DSCP:
+ case FLOW_TYPE_FRAGMENT:
+ case FLOW_TYPE_LABEL:
+ return 1;
+
+ default:
+ /* The unknown components are not stack-able in default */
+ return 0;
+ }
+}
+
+static int
+builder_add_prepare(struct flow_builder *fb)
+{
+ if (fb->parts[fb->this_type].length)
+ {
+ if (fb->last_type != fb->this_type)
+ return 0;
+
+ if (!is_stackable_type(fb->this_type))
+ return 0;
+ }
+ else
+ {
+ fb->parts[fb->this_type].offset = fb->data.used;
+ }
+
+ return 1;
+}
+
+static void
+builder_add_finish(struct flow_builder *fb)
+{
+ fb->parts[fb->this_type].length = fb->data.used - fb->parts[fb->this_type].offset;
+ flow_builder_set_type(fb, fb->this_type);
+}
+
+static void
+push_pfx_to_buffer(struct flow_builder *fb, u8 pxlen_bytes, byte *ip)
+{
+ for (int i = 0; i < pxlen_bytes; i++)
+ BUFFER_PUSH(fb->data) = *ip++;
+}
+
+/**
+ * flow_builder4_add_pfx - add IPv4 prefix
+ * @fb: flowspec builder instance
+ * @n4: net address of type IPv4
+ *
+ * This function add IPv4 prefix into flowspec builder instance.
+ */
+int
+flow_builder4_add_pfx(struct flow_builder *fb, const net_addr_ip4 *n4)
+{
+ if (!builder_add_prepare(fb))
+ return 0;
+
+ ip4_addr ip4 = ip4_hton(n4->prefix);
+
+ BUFFER_PUSH(fb->data) = fb->this_type;
+ BUFFER_PUSH(fb->data) = n4->pxlen;
+ push_pfx_to_buffer(fb, BYTES(n4->pxlen), (byte *) &ip4);
+
+ builder_add_finish(fb);
+ return 1;
+}
+
+/**
+ * flow_builder6_add_pfx - add IPv6 prefix
+ * @fb: flowspec builder instance
+ * @n6: net address of type IPv4
+ * @pxoffset: prefix offset for @n6
+ *
+ * This function add IPv4 prefix into flowspec builder instance. This function
+ * should return 1 for successful adding, otherwise returns %0.
+ */
+int
+flow_builder6_add_pfx(struct flow_builder *fb, const net_addr_ip6 *n6, u32 pxoffset)
+{
+ if (!builder_add_prepare(fb))
+ return 0;
+
+ ip6_addr ip6 = ip6_hton(n6->prefix);
+
+ BUFFER_PUSH(fb->data) = fb->this_type;
+ BUFFER_PUSH(fb->data) = n6->pxlen;
+ BUFFER_PUSH(fb->data) = pxoffset;
+ push_pfx_to_buffer(fb, BYTES(n6->pxlen) - (pxoffset / 8), ((byte *) &ip6) + (pxoffset / 8));
+
+ builder_add_finish(fb);
+ return 1;
+}
+
+/**
+ * flow_builder_add_op_val - add operator/value pair
+ * @fb: flowspec builder instance
+ * @op: operator
+ * @value: value
+ *
+ * This function add operator/value pair as a part of a flowspec component. It
+ * is required to set appropriate flowspec component type using function
+ * flow_builder_set_type(). This function should return 1 for successful
+ * adding, otherwise returns 0.
+ */
+int
+flow_builder_add_op_val(struct flow_builder *fb, byte op, u32 value)
+{
+ if (!builder_add_prepare(fb))
+ return 0;
+
+ if (fb->this_type == fb->last_type)
+ {
+ /* Remove the end-bit from last operand-value pair of the component */
+ fb->data.data[fb->last_op_offset] &= 0x7f;
+ }
+ else
+ {
+ BUFFER_PUSH(fb->data) = fb->this_type;
+ }
+
+ fb->last_op_offset = fb->data.used;
+
+ /* Set the end-bit for operand-value pair of the component */
+ op |= 0x80;
+
+ if (value & 0xff00)
+ {
+ BUFFER_PUSH(fb->data) = op | 0x10;
+ put_u16(BUFFER_INC(fb->data, 2), value);
+ }
+ else
+ {
+ BUFFER_PUSH(fb->data) = op;
+ BUFFER_PUSH(fb->data) = (u8) value;
+ }
+
+ builder_add_finish(fb);
+ return 1;
+}
+
+/**
+ * flow_builder_add_val_mask - add value/bitmask pair
+ * @fb: flowspec builder instance
+ * @op: operator
+ * @value: value
+ * @mask: bitmask
+ *
+ * It is required to set appropriate flowspec component type using function
+ * flow_builder_set_type(). This function should return 1 for successful adding,
+ * otherwise returns 0.
+ */
+int
+flow_builder_add_val_mask(struct flow_builder *fb, byte op, u32 value, u32 mask)
+{
+ u32 a = value & mask;
+ u32 b = ~value & mask;
+
+ if (a)
+ {
+ flow_builder_add_op_val(fb, op ^ 0x01, a);
+ op |= FLOW_OP_AND;
+ }
+
+ if (b)
+ flow_builder_add_op_val(fb, op ^ 0x02, b);
+
+ return 1;
+}
+
+
+/**
+ * flow_builder_set_type - set type of next flowspec component
+ * @fb: flowspec builder instance
+ * @type: flowspec component type
+ *
+ * This function sets type of next flowspec component. It is necessary to call
+ * this function before each changing of adding flowspec component.
+ */
+void
+flow_builder_set_type(struct flow_builder *fb, enum flow_type type)
+{
+ fb->last_type = fb->this_type;
+ fb->this_type = type;
+}
+
+static ip4_addr
+flow_read_ip4(const byte *px, uint pxlen)
+{
+ ip4_addr ip = IP4_NONE;
+ memcpy(&ip, px, BYTES(pxlen));
+ return ip4_ntoh(ip);
+}
+
+static ip6_addr
+flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
+{
+ uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
+ uint ceil_len = BYTES(pxlen);
+ ip6_addr ip = IP6_NONE;
+
+ memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
+
+ return ip6_ntoh(ip);
+}
+
+static void
+builder_write_parts(struct flow_builder *fb, byte *buf)
+{
+ for (int i = 1; i < FLOW_TYPE_MAX; i++)
+ {
+ if (fb->parts[i].length)
+ {
+ memcpy(buf, fb->data.data + fb->parts[i].offset, fb->parts[i].length);
+ buf += fb->parts[i].length;
+ }
+ }
+}
+
+/**
+ * flow_builder4_finalize - assemble final flowspec data structure &net_addr_flow4
+ * @fb: flowspec builder instance
+ * @lpool: linear memory pool
+ *
+ * This function returns final flowspec data structure &net_addr_flow4 allocated
+ * onto @lpool linear memory pool.
+ */
+net_addr_flow4 *
+flow_builder4_finalize(struct flow_builder *fb, linpool *lpool)
+{
+ uint data_len = fb->data.used + (fb->data.used < 0xf0 ? 1 : 2);
+ net_addr_flow4 *f = lp_alloc(lpool, sizeof(struct net_addr_flow4) + data_len);
+
+ ip4_addr prefix = IP4_NONE;
+ uint pxlen = 0;
+
+ if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
+ {
+ byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
+ pxlen = *p++;
+ prefix = flow_read_ip4(p, pxlen);
+ }
+ *f = NET_ADDR_FLOW4(prefix, pxlen, data_len);
+
+ builder_write_parts(fb, f->data + flow_write_length(f->data, fb->data.used));
+
+ return f;
+}
+
+/**
+ * flow_builder6_finalize - assemble final flowspec data structure &net_addr_flow6
+ * @fb: flowspec builder instance
+ * @lpool: linear memory pool for allocation of
+ *
+ * This function returns final flowspec data structure &net_addr_flow6 allocated
+ * onto @lpool linear memory pool.
+ */
+net_addr_flow6 *
+flow_builder6_finalize(struct flow_builder *fb, linpool *lpool)
+{
+ uint data_len = fb->data.used + (fb->data.used < 0xf0 ? 1 : 2);
+ net_addr_flow6 *n = lp_alloc(lpool, sizeof(net_addr_flow6) + data_len);
+
+ ip6_addr prefix = IP6_NONE;
+ uint pxlen = 0;
+
+ if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
+ {
+ byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
+ pxlen = *p++;
+ uint pxoffset = *p++;
+ prefix = flow_read_ip6(p, pxlen, pxoffset);
+ }
+ *n = NET_ADDR_FLOW6(prefix, pxlen, data_len);
+
+ builder_write_parts(fb, n->data + flow_write_length(n->data, fb->data.used));
+
+ return n;
+}
+
+/**
+ * flow_builder_clear - flush flowspec builder instance for another flowspec creation
+ * @fb: flowspec builder instance
+ *
+ * This function flushes all data from builder but it maintains pre-allocated
+ * buffer space.
+ */
+void
+flow_builder_clear(struct flow_builder *fb)
+{
+ BUFFER(byte) data;
+ BUFFER_FLUSH(fb->data);
+
+ BUFFER_SHALLOW_COPY(data, fb->data);
+ memset(fb, 0, sizeof(struct flow_builder));
+ BUFFER_SHALLOW_COPY(fb->data, data);
+}
+
+
+/*
+ * Net Formatting
+ */
+
+/* Flowspec operators for [op, value]+ pairs */
+
+static const char *
+num_op_str(const byte *op)
+{
+ switch (*op & 0x07)
+ {
+ case FLOW_OP_TRUE: return "true";
+ case FLOW_OP_EQ: return "=";
+ case FLOW_OP_GT: return ">";
+ case FLOW_OP_GEQ: return ">=";
+ case FLOW_OP_LT: return "<";
+ case FLOW_OP_LEQ: return "<=";
+ case FLOW_OP_NEQ: return "!=";
+ case FLOW_OP_FALSE: return "false";
+ }
+
+ return NULL;
+}
+
+static uint
+get_value(const byte *val, u8 len)
+{
+ switch (len)
+ {
+ case 1: return *val;
+ case 2: return get_u16(val);
+ case 4: return get_u32(val);
+ // No component may have length 8
+ // case 8: return get_u64(val);
+ }
+
+ return 0;
+}
+
+static const char *
+fragment_val_str(u8 val)
+{
+ switch (val)
+ {
+ case 1: return "dont_fragment";
+ case 2: return "is_fragment";
+ case 4: return "first_fragment";
+ case 8: return "last_fragment";
+ }
+ return "???";
+}
+
+static void
+net_format_flow_ip(buffer *b, const byte *part, int ipv6)
+{
+ uint pxlen = *(part+1);
+ if (ipv6)
+ {
+ uint pxoffset = *(part+2);
+ if (pxoffset)
+ buffer_print(b, "%I6/%u offset %u; ", flow_read_ip6(part+3,pxlen,pxoffset), pxlen, pxoffset);
+ else
+ buffer_print(b, "%I6/%u; ", flow_read_ip6(part+3,pxlen,0), pxlen);
+ }
+ else
+ {
+ buffer_print(b, "%I4/%u; ", flow_read_ip4(part+2,pxlen), pxlen);
+ }
+}
+
+static void
+net_format_flow_num(buffer *b, const byte *part)
+{
+ const byte *last_op = NULL;
+ const byte *op = part+1;
+ uint val;
+ uint len;
+ uint first = 1;
+
+ while (1)
+ {
+ if (!first)
+ {
+ /* XXX: I don't like this so complicated if-tree */
+ if (!isset_and(op) &&
+ ((num_op( op) == FLOW_OP_EQ) || (num_op( op) == FLOW_OP_GEQ)) &&
+ ((num_op(last_op) == FLOW_OP_EQ) || (num_op(last_op) == FLOW_OP_LEQ)))
+ {
+ b->pos--; /* Remove last char (it is a space) */
+ buffer_puts(b, ",");
+ }
+ else
+ {
+ buffer_puts(b, isset_and(op) ? "&& " : "|| ");
+ }
+ }
+ first = 0;
+
+ len = get_value_length(op);
+ val = get_value(op+1, len);
+
+ if (!isset_end(op) && !isset_and(op) && isset_and(op+1+len) &&
+ (num_op(op) == FLOW_OP_GEQ) && (num_op(op+1+len) == FLOW_OP_LEQ))
+ {
+ /* Display interval */
+ buffer_print(b, "%u..", val);
+ op += 1 + len;
+ len = get_value_length(op);
+ val = get_value(op+1, len);
+ buffer_print(b, "%u", val);
+ }
+ else if (num_op(op) == FLOW_OP_EQ)
+ {
+ buffer_print(b, "%u", val);
+ }
+ else
+ {
+ buffer_print(b, "%s %u", num_op_str(op), val);
+ }
+
+ if (isset_end(op))
+ {
+ buffer_puts(b, "; ");
+ break;
+ }
+ else
+ {
+ buffer_puts(b, " ");
+ }
+
+ last_op = op;
+ op += 1 + len;
+ }
+}
+
+static void
+net_format_flow_bitmask(buffer *b, const byte *part)
+{
+ const byte *op = part+1;
+ uint val;
+ uint len;
+ uint first = 1;
+
+ while (1)
+ {
+ if (!first)
+ {
+ if (isset_and(op))
+ {
+ b->pos--; /* Remove last char (it is a space) */
+ buffer_puts(b, ",");
+ }
+ else
+ {
+ buffer_puts(b, "|| ");
+ }
+ }
+ first = 0;
+
+ len = get_value_length(op);
+ val = get_value(op+1, len);
+
+ /*
+ * Not Match Show
+ * ------------------
+ * 0 0 !0/B
+ * 0 1 B/B
+ * 1 0 0/B
+ * 1 1 !B/B
+ */
+
+ if ((*op & 0x3) == 0x3 || (*op & 0x3) == 0)
+ buffer_puts(b, "!");
+
+ if (*part == FLOW_TYPE_FRAGMENT && (val == 1 || val == 2 || val == 4 || val == 8))
+ buffer_print(b, "%s%s", ((*op & 0x1) ? "" : "!"), fragment_val_str(val));
+ else
+ buffer_print(b, "0x%x/0x%x", ((*op & 0x1) ? val : 0), val);
+
+ if (isset_end(op))
+ {
+ buffer_puts(b, "; ");
+ break;
+ }
+ else
+ {
+ buffer_puts(b, " ");
+ }
+
+ op += 1 + len;
+ }
+}
+
+static uint
+net_format_flow(char *buf, uint blen, const byte *data, uint dlen, int ipv6)
+{
+ buffer b = {
+ .start = buf,
+ .pos = buf,
+ .end = buf + blen,
+ };
+
+ const byte *part = flow_first_part(data);
+ *buf = 0;
+
+ if (ipv6)
+ buffer_puts(&b, "flow6 { ");
+ else
+ buffer_puts(&b, "flow4 { ");
+
+ while (part)
+ {
+ buffer_print(&b, "%s ", flow_type_str(*part, ipv6));
+
+ switch (*part)
+ {
+ case FLOW_TYPE_DST_PREFIX:
+ case FLOW_TYPE_SRC_PREFIX:
+ net_format_flow_ip(&b, part, ipv6);
+ break;
+ case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
+ case FLOW_TYPE_PORT:
+ case FLOW_TYPE_DST_PORT:
+ case FLOW_TYPE_SRC_PORT:
+ case FLOW_TYPE_ICMP_TYPE:
+ case FLOW_TYPE_ICMP_CODE:
+ case FLOW_TYPE_PACKET_LENGTH:
+ case FLOW_TYPE_DSCP:
+ net_format_flow_num(&b, part);
+ break;
+ case FLOW_TYPE_TCP_FLAGS:
+ case FLOW_TYPE_FRAGMENT:
+ case FLOW_TYPE_LABEL:
+ net_format_flow_bitmask(&b, part);
+ break;
+ }
+
+ part = flow_next_part(part, data+dlen, ipv6);
+ }
+
+ buffer_puts(&b, "}");
+
+ if (b.pos == b.end)
+ {
+ b.pos = b.start + MIN(blen - 6, strlen(b.start));
+ buffer_puts(&b, " ...}");
+ }
+
+ return b.pos - b.start;
+}
+
+/**
+ * flow4_net_format - stringify flowspec data structure &net_addr_flow4
+ * @buf: pre-allocated buffer for writing a stringify net address flowspec
+ * @blen: free allocated space in @buf
+ * @f: flowspec data structure &net_addr_flow4 for stringify
+ *
+ * This function writes stringified @f into @buf. The function returns number
+ * of written chars. If final string is too large, the string will ends the with
+ * ' ...}' sequence and zero-terminator.
+ */
+uint
+flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f)
+{
+ return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow4), 0);
+}
+
+/**
+ * flow6_net_format - stringify flowspec data structure &net_addr_flow6
+ * @buf: pre-allocated buffer for writing a stringify net address flowspec
+ * @blen: free allocated space in @buf
+ * @f: flowspec data structure &net_addr_flow4 for stringify
+ *
+ * This function writes stringified @f into @buf. The function returns number
+ * of written chars. If final string is too large, the string will ends the with
+ * ' ...}' sequence and zero-terminator.
+ */
+uint
+flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f)
+{
+ return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow6), 1);
+}
diff --git a/lib/flowspec.h b/lib/flowspec.h
new file mode 100644
index 00000000..fa90c70d
--- /dev/null
+++ b/lib/flowspec.h
@@ -0,0 +1,152 @@
+/*
+ * BIRD Library -- Flow specification (RFC 5575)
+ *
+ * (c) 2016 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_FLOWSPEC_H_
+#define _BIRD_FLOWSPEC_H_
+
+#include "nest/bird.h"
+#include "lib/buffer.h"
+#include "lib/net.h"
+
+
+/* Flow component operators */
+#define FLOW_OP_TRUE 0x00 /* 0b000 */
+#define FLOW_OP_EQ 0x01 /* 0b001 */
+#define FLOW_OP_GT 0x02 /* 0b010 */
+#define FLOW_OP_GEQ 0x03 /* 0b011 */
+#define FLOW_OP_LT 0x04 /* 0b100 */
+#define FLOW_OP_LEQ 0x05 /* 0b101 */
+#define FLOW_OP_NEQ 0x06 /* 0b110 */
+#define FLOW_OP_FALSE 0x07 /* 0b111 */
+
+#define FLOW_OP_OR 0x00
+#define FLOW_OP_AND 0x40
+
+
+/* Types of components in flowspec */
+enum flow_type {
+ FLOW_TYPE_DST_PREFIX = 1,
+ FLOW_TYPE_SRC_PREFIX = 2,
+ FLOW_TYPE_IP_PROTOCOL = 3,
+ FLOW_TYPE_NEXT_HEADER = 3, /* IPv6 */
+ FLOW_TYPE_PORT = 4,
+ FLOW_TYPE_DST_PORT = 5,
+ FLOW_TYPE_SRC_PORT = 6,
+ FLOW_TYPE_ICMP_TYPE = 7,
+ FLOW_TYPE_ICMP_CODE = 8,
+ FLOW_TYPE_TCP_FLAGS = 9,
+ FLOW_TYPE_PACKET_LENGTH = 10,
+ FLOW_TYPE_DSCP = 11, /* DiffServ Code Point */
+ FLOW_TYPE_FRAGMENT = 12,
+ FLOW_TYPE_LABEL = 13, /* IPv6 */
+ FLOW_TYPE_MAX
+};
+
+const char *flow_type_str(enum flow_type type, int ipv6);
+
+
+/*
+ * Length
+ */
+
+uint flow_write_length(byte *data, u16 len);
+
+static inline u16 flow_hdr_length(const byte *data)
+{ return ((*data & 0xf0) == 0xf0) ? 2 : 1; }
+
+static inline u16 flow_read_length(const byte *data)
+{ return ((*data & 0xf0) == 0xf0) ? get_u16(data) & 0x0fff : *data; }
+
+static inline u16 flow4_get_length(const net_addr_flow4 *f)
+{ return f->length - sizeof(net_addr_flow4); }
+
+static inline u16 flow6_get_length(const net_addr_flow6 *f)
+{ return f->length - sizeof(net_addr_flow6); }
+
+static inline void flow4_set_length(net_addr_flow4 *f, u16 len)
+{ f->length = sizeof(net_addr_flow4) + flow_write_length(f->data, len) + len; }
+
+static inline void flow6_set_length(net_addr_flow6 *f, u16 len)
+{ f->length = sizeof(net_addr_flow6) + flow_write_length(f->data, len) + len; }
+
+
+/*
+ * Iterators
+ */
+
+const byte *flow4_first_part(const net_addr_flow4 *f);
+const byte *flow6_first_part(const net_addr_flow6 *f);
+const byte *flow4_next_part(const byte *pos, const byte *end);
+const byte *flow6_next_part(const byte *pos, const byte *end);
+
+
+/*
+ * Flowspec Builder
+ */
+
+/* A data structure for keep a state of flow builder */
+struct flow_builder {
+ BUFFER_(byte) data;
+ enum flow_type this_type;
+ enum flow_type last_type;
+ u16 last_op_offset; /* Position of last operator in data.data */
+ int ipv6;
+ struct {
+ u16 offset; /* Beginning of a component */
+ u16 length; /* Length of a component */
+ } parts[FLOW_TYPE_MAX]; /* Indexing all components */
+};
+
+struct flow_builder *flow_builder_init(pool *pool);
+void flow_builder_clear(struct flow_builder *fb);
+void flow_builder_set_type(struct flow_builder *fb, enum flow_type p);
+int flow_builder4_add_pfx(struct flow_builder *fb, const net_addr_ip4 *n4);
+int flow_builder6_add_pfx(struct flow_builder *fb, const net_addr_ip6 *n6, u32 offset);
+int flow_builder_add_op_val(struct flow_builder *fb, byte op, u32 value);
+int flow_builder_add_val_mask(struct flow_builder *fb, byte op, u32 value, u32 mask);
+net_addr_flow4 *flow_builder4_finalize(struct flow_builder *fb, linpool *lpool);
+net_addr_flow6 *flow_builder6_finalize(struct flow_builder *fb, linpool *lpool);
+
+
+/*
+ * Validation
+ */
+
+/* Results of validation Flow specification */
+enum flow_validated_state {
+ FLOW_ST_UNKNOWN_COMPONENT,
+ FLOW_ST_VALID,
+ FLOW_ST_NOT_COMPLETE,
+ FLOW_ST_EXCEED_MAX_PREFIX_LENGTH,
+ FLOW_ST_EXCEED_MAX_PREFIX_OFFSET,
+ FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+ FLOW_ST_BAD_TYPE_ORDER,
+ FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
+ FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
+ FLOW_ST_DEST_PREFIX_REQUIRED,
+ FLOW_ST_INVALID_TCP_FLAGS,
+ FLOW_ST_CANNOT_USE_DONT_FRAGMENT
+};
+
+const char *flow_validated_state_str(enum flow_validated_state code);
+enum flow_validated_state flow4_validate(const byte *nlri, uint len);
+enum flow_validated_state flow6_validate(const byte *nlri, uint len);
+void flow_check_cf_value_length(struct flow_builder *fb, u32 expr);
+void flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mask);
+void flow4_validate_cf(net_addr_flow4 *f);
+void flow6_validate_cf(net_addr_flow6 *f);
+
+
+/*
+ * Net Formatting
+ */
+
+uint flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f);
+uint flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f);
+
+#endif /* _BIRD_FLOWSPEC_H_ */
diff --git a/lib/flowspec_test.c b/lib/flowspec_test.c
new file mode 100644
index 00000000..dd71dc7b
--- /dev/null
+++ b/lib/flowspec_test.c
@@ -0,0 +1,639 @@
+/*
+ * BIRD Library -- Flow specification (RFC 5575) Tests
+ *
+ * (c) 2016 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+#include "lib/flowspec.h"
+
+#define NET_ADDR_FLOW4_(what,prefix,pxlen,data_) \
+ do \
+ { \
+ what = alloca(sizeof(net_addr_flow4) + 128); \
+ *what = NET_ADDR_FLOW4(prefix, pxlen, sizeof(data_)); \
+ memcpy(what->data, &(data_), sizeof(data_)); \
+ } while(0)
+
+#define NET_ADDR_FLOW6_(what,prefix,pxlen,data_) \
+ do \
+ { \
+ what = alloca(sizeof(net_addr_flow6) + 128); \
+ *what = NET_ADDR_FLOW6(prefix, pxlen, sizeof(data_)); \
+ memcpy(what->data, &(data_), sizeof(data_)); \
+ } while(0)
+
+static int
+t_read_length(void)
+{
+ byte data[] = { 0xcc, 0xcc, 0xcc };
+
+ for (uint expect = 0; expect < 0xf0; expect++)
+ {
+ *data = expect;
+ uint get = flow_read_length(data);
+ bt_assert_msg(get == expect, "Testing get length 0x%02x (get 0x%02x)", expect, get);
+ }
+
+ for (uint expect = 0; expect <= 0xfff; expect++)
+ {
+ put_u16(data, expect | 0xf000);
+ uint get = flow_read_length(data);
+ bt_assert_msg(get == expect, "Testing get length 0x%03x (get 0x%03x)", expect, get);
+ }
+
+ return 1;
+}
+
+static int
+t_write_length(void)
+{
+ byte data[] = { 0xcc, 0xcc, 0xcc };
+
+ for (uint expect = 0; expect <= 0xfff; expect++)
+ {
+ uint offset = flow_write_length(data, expect);
+
+ uint set = (expect < 0xf0) ? *data : (get_u16(data) & 0x0fff);
+ bt_assert_msg(set == expect, "Testing set length 0x%03x (set 0x%03x)", expect, set);
+ bt_assert(offset == (expect < 0xf0 ? 1 : 2));
+ }
+
+ return 1;
+}
+
+static int
+t_first_part(void)
+{
+ net_addr_flow4 *f;
+ NET_ADDR_FLOW4_(f, ip4_build(10,0,0,1), 24, ((byte[]) { 0x00, 0x00, 0xab }));
+
+ const byte *under240 = &f->data[1];
+ const byte *above240 = &f->data[2];
+
+ /* Case 0x00 0x00 */
+ bt_assert(flow4_first_part(f) == NULL);
+
+ /* Case 0x01 0x00 */
+ f->data[0] = 0x01;
+ bt_assert(flow4_first_part(f) == under240);
+
+ /* Case 0xef 0x00 */
+ f->data[0] = 0xef;
+ bt_assert(flow4_first_part(f) == under240);
+
+ /* Case 0xf0 0x00 */
+ f->data[0] = 0xf0;
+ bt_assert(flow4_first_part(f) == NULL);
+
+ /* Case 0xf0 0x01 */
+ f->data[1] = 0x01;
+ bt_assert(flow4_first_part(f) == above240);
+
+ /* Case 0xff 0xff */
+ f->data[0] = 0xff;
+ f->data[1] = 0xff;
+ bt_assert(flow4_first_part(f) == above240);
+
+ return 1;
+}
+
+static int
+t_iterators4(void)
+{
+ net_addr_flow4 *f;
+ NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
+ 25, /* Length */
+ FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+ FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+ FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
+ FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+ FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+ }));
+
+ const byte *start = f->data;
+ const byte *p1_dst_pfx = &f->data[1];
+ const byte *p2_src_pfx = &f->data[6];
+ const byte *p3_ip_proto = &f->data[12];
+ const byte *p4_port = &f->data[15];
+ const byte *p5_tcp_flags = &f->data[23];
+ const byte *end = &f->data[25];
+
+ bt_assert(flow_read_length(f->data) == (end-start));
+ bt_assert(flow4_first_part(f) == p1_dst_pfx);
+
+ bt_assert(flow4_next_part(p1_dst_pfx, end) == p2_src_pfx);
+ bt_assert(flow4_next_part(p2_src_pfx, end) == p3_ip_proto);
+ bt_assert(flow4_next_part(p3_ip_proto, end) == p4_port);
+ bt_assert(flow4_next_part(p4_port, end) == p5_tcp_flags);
+ bt_assert(flow4_next_part(p5_tcp_flags, end) == NULL);
+
+ return 1;
+}
+
+static int
+t_iterators6(void)
+{
+ net_addr_flow6 *f;
+ NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 64, ((byte[]) {
+ 26, /* Length */
+ FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
+ FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
+ FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+ FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+ FLOW_TYPE_LABEL, 0x80, 0x55,
+ }));
+
+ const byte *start = f->data;
+ const byte *p1_dst_pfx = &f->data[1];
+ const byte *p2_src_pfx = &f->data[9];
+ const byte *p3_next_header = &f->data[13];
+ const byte *p4_port = &f->data[16];
+ const byte *p5_label = &f->data[24];
+ const byte *end = &f->data[26];
+
+ bt_assert(flow_read_length(f->data) == (end-start));
+ bt_assert(flow6_first_part(f) == p1_dst_pfx);
+
+ bt_assert(flow6_next_part(p1_dst_pfx, end) == p2_src_pfx);
+ bt_assert(flow6_next_part(p2_src_pfx, end) == p3_next_header);
+ bt_assert(flow6_next_part(p3_next_header, end) == p4_port);
+ bt_assert(flow6_next_part(p4_port, end) == p5_label);
+ bt_assert(flow6_next_part(p5_label, end) == NULL);
+
+ return 1;
+}
+
+static int
+t_validation4(void)
+{
+ enum flow_validated_state res;
+
+ byte nlri1[] = {
+ FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+ FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+ FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
+ FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+ FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+ };
+
+ /* Isn't included destination prefix */
+ res = flow4_validate(nlri1, 0);
+ bt_assert(res == FLOW_ST_DEST_PREFIX_REQUIRED);
+ res = flow4_validate(&nlri1[5], sizeof(nlri1)-5);
+ bt_assert(res == FLOW_ST_DEST_PREFIX_REQUIRED);
+
+ /* Valid / Not Complete testing */
+ uint valid_sizes[] = {5, 11, 14, 22, 25, 0};
+ uint valid_idx = 0;
+ for (uint size = 1; size <= sizeof(nlri1); size++)
+ {
+ res = flow4_validate(nlri1, size);
+ bt_debug("size %u, result: %s\n", size, flow_validated_state_str(res));
+ if (size == valid_sizes[valid_idx])
+ {
+ valid_idx++;
+ bt_assert(res == FLOW_ST_VALID);
+ }
+ else
+ {
+ bt_assert(res == FLOW_ST_NOT_COMPLETE);
+ }
+ }
+
+ /* Misc err tests */
+
+ struct tset {
+ enum flow_validated_state expect;
+ char *description;
+ u16 size;
+ byte *nlri;
+ };
+
+#define TS(type, msg, data) ((struct tset) {type, msg, sizeof(data), (data)})
+ struct tset tset[] = {
+ TS(
+ FLOW_ST_EXCEED_MAX_PREFIX_LENGTH,
+ "33-length IPv4 prefix",
+ ((byte []) {
+ FLOW_TYPE_DST_PREFIX, 33, 5, 6, 7, 8, 9
+ })
+ ),
+ TS(
+ FLOW_ST_BAD_TYPE_ORDER,
+ "Bad flowspec component type order",
+ ((byte []) {
+ FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+ FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+ })
+ ),
+ TS(
+ FLOW_ST_BAD_TYPE_ORDER,
+ "Doubled destination prefix component",
+ ((byte []) {
+ FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+ FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+ })
+ ),
+ TS(
+ FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
+ "The first numeric operator has set the AND bit",
+ ((byte []) {
+ FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+ })
+ ),
+ TS(
+ FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
+ "Set zero bit in operand to one",
+ ((byte []) {
+ FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06,
+ })
+ ),
+ TS(
+ FLOW_ST_UNKNOWN_COMPONENT,
+ "Unknown component of type number 13",
+ ((byte []) {
+ FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+ FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+ 13 /*something new*/, 0x80, 0x55,
+ })
+ ),
+ };
+#undef TS
+
+ for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++)
+ {
+ res = flow4_validate(tset[tcase].nlri, tset[tcase].size);
+ bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description);
+ }
+
+ return 1;
+}
+
+static int
+t_validation6(void)
+{
+ enum flow_validated_state res;
+
+ byte nlri1[] = {
+ FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+ FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+ FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+ FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+ FLOW_TYPE_LABEL, 0x80, 0x55,
+ };
+
+ /* Isn't included destination prefix */
+ res = flow6_validate(nlri1, 0);
+ bt_assert(res == FLOW_ST_VALID);
+
+ /* Valid / Not Complete testing */
+ uint valid_sizes[] = {0, 9, 13, 16, 24, 27, 0};
+ uint valid_idx = 0;
+ for (uint size = 0; size <= sizeof(nlri1); size++)
+ {
+ res = flow6_validate(nlri1, size);
+ bt_debug("size %u, result: %s\n", size, flow_validated_state_str(res));
+ if (size == valid_sizes[valid_idx])
+ {
+ valid_idx++;
+ bt_assert(res == FLOW_ST_VALID);
+ }
+ else
+ {
+ bt_assert(res == FLOW_ST_NOT_COMPLETE);
+ }
+ }
+
+ /* Misc err tests */
+
+ struct tset {
+ enum flow_validated_state expect;
+ char *description;
+ u16 size;
+ byte *nlri;
+ };
+
+#define TS(type, msg, data) ((struct tset) {type, msg, sizeof(data), (data)})
+ struct tset tset[] = {
+ TS(
+ FLOW_ST_EXCEED_MAX_PREFIX_LENGTH,
+ "129-length IPv6 prefix",
+ ((byte []) {
+ FLOW_TYPE_DST_PREFIX, 129, 64, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12
+ })
+ ),
+ TS(
+ FLOW_ST_EXCEED_MAX_PREFIX_OFFSET,
+ "Prefix offset is higher than prefix length",
+ ((byte []) {
+ FLOW_TYPE_DST_PREFIX, 48, 64, 0x40, 0x12, 0x34
+ })
+ ),
+ TS(
+ FLOW_ST_BAD_TYPE_ORDER,
+ "Bad flowspec component type order",
+ ((byte []) {
+ FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+ FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+ })
+ ),
+ TS(
+ FLOW_ST_BAD_TYPE_ORDER,
+ "Doubled destination prefix component",
+ ((byte []) {
+ FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+ FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+ })
+ ),
+ TS(
+ FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
+ "The first numeric operator has set the AND bit",
+ ((byte []) {
+ FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90
+ })
+ ),
+ TS(
+ FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
+ "Set zero bit in operand to one",
+ ((byte []) {
+ FLOW_TYPE_NEXT_HEADER, 0x89, 0x06
+ })
+ ),
+ TS(
+ FLOW_ST_VALID,
+ "Component of type number 13 (Label) is well-known in IPv6",
+ ((byte []) {
+ FLOW_TYPE_LABEL, 0x80, 0x55
+ })
+ ),
+ TS(
+ FLOW_ST_UNKNOWN_COMPONENT,
+ "Unknown component of type number 14",
+ ((byte []) {
+ FLOW_TYPE_LABEL, 0x80, 0x55,
+ 14 /*something new*/, 0x80, 0x55,
+ })
+ )
+ };
+#undef TS
+
+ for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++)
+ {
+ res = flow6_validate(tset[tcase].nlri, tset[tcase].size);
+ bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description);
+ }
+
+ return 1;
+}
+
+
+
+/*
+ * Builder tests
+ */
+
+static int
+t_builder4(void)
+{
+ resource_init();
+
+ struct flow_builder *fb = flow_builder_init(&root_pool);
+ linpool *lp = lp_new_default(&root_pool);
+
+ /* Expectation */
+
+ static byte nlri[] = {
+ 25,
+ FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+ FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+ FLOW_TYPE_IP_PROTOCOL, 0x80, 0x06,
+ FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+ FLOW_TYPE_TCP_FLAGS, 0x80, 0x55
+ };
+
+ net_addr_flow4 *expect;
+ NET_ADDR_FLOW4_(expect, ip4_build(5, 6, 7, 0), 24, nlri);
+
+ /* Normal order */
+
+ net_addr_ip4 n1;
+ net_fill_ip4((net_addr *) &n1, ip4_build(5,6,7,0), 24);
+ flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+ flow_builder4_add_pfx(fb, &n1);
+
+ net_addr_ip4 n2;
+ net_fill_ip4((net_addr *) &n2, ip4_build(10,11,12,13), 32);
+ flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+ flow_builder4_add_pfx(fb, &n2);
+
+ flow_builder_set_type(fb, FLOW_TYPE_IP_PROTOCOL);
+ flow_builder_add_op_val(fb, 0, 0x06);
+
+ flow_builder_set_type(fb, FLOW_TYPE_PORT);
+ flow_builder_add_op_val(fb, 0x03, 0x89);
+ flow_builder_add_op_val(fb, 0x45, 0x8b);
+ flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+ /* Try put a component twice time */
+ flow_builder_set_type(fb, FLOW_TYPE_IP_PROTOCOL);
+ flow_builder_add_op_val(fb, 0, 0x06);
+
+ flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
+ flow_builder_add_op_val(fb, 0, 0x55);
+
+ net_addr_flow4 *res = flow_builder4_finalize(fb, lp);
+
+ bt_assert(memcmp(res, expect, expect->length) == 0);
+
+ /* Reverse order */
+
+ flow_builder_clear(fb);
+
+ flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
+ flow_builder_add_op_val(fb, 0, 0x55);
+
+ flow_builder_set_type(fb, FLOW_TYPE_PORT);
+ flow_builder_add_op_val(fb, 0x03, 0x89);
+ flow_builder_add_op_val(fb, 0x45, 0x8b);
+ flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+ flow_builder_set_type(fb, FLOW_TYPE_IP_PROTOCOL);
+ flow_builder_add_op_val(fb, 0, 0x06);
+
+ net_fill_ip4((net_addr *) &n2, ip4_build(10,11,12,13), 32);
+ flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+ flow_builder4_add_pfx(fb, &n2);
+
+ net_fill_ip4((net_addr *) &n1, ip4_build(5,6,7,0), 24);
+ flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+ flow_builder4_add_pfx(fb, &n1);
+
+ bt_assert(memcmp(res, expect, expect->length) == 0);
+
+ return 1;
+}
+
+static int
+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;
+
+ /* Expectation */
+
+ byte nlri[] = {
+ 27,
+ FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+ FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+ FLOW_TYPE_NEXT_HEADER, 0x80, 0x06,
+ FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+ FLOW_TYPE_LABEL, 0x80, 0x55,
+ };
+
+ net_addr_flow6 *expect;
+ NET_ADDR_FLOW6_(expect, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
+
+ /* Normal order */
+
+ net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+ flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+ flow_builder6_add_pfx(fb, &ip, 61);
+
+ /* Try put a component twice time */
+ net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+ flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+ bt_assert(flow_builder6_add_pfx(fb, &ip, 61) == 0);
+
+ net_fill_ip6((net_addr *) &ip, ip6_build(0xc0000000,0,0,0), 8);
+ flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+ flow_builder6_add_pfx(fb, &ip, 0);
+
+ flow_builder_set_type(fb, FLOW_TYPE_NEXT_HEADER);
+ flow_builder_add_op_val(fb, 0, 0x06);
+
+ flow_builder_set_type(fb, FLOW_TYPE_PORT);
+ flow_builder_add_op_val(fb, 0x03, 0x89);
+ flow_builder_add_op_val(fb, 0x45, 0x8b);
+ flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+ flow_builder_set_type(fb, FLOW_TYPE_LABEL);
+ flow_builder_add_op_val(fb, 0, 0x55);
+
+ net_addr_flow6 *res = flow_builder6_finalize(fb, lp);
+ bt_assert(memcmp(res, expect, expect->length) == 0);
+
+ /* Reverse order */
+
+ flow_builder_clear(fb);
+ fb->ipv6 = 1;
+
+ flow_builder_set_type(fb, FLOW_TYPE_LABEL);
+ flow_builder_add_op_val(fb, 0, 0x55);
+
+ flow_builder_set_type(fb, FLOW_TYPE_PORT);
+ flow_builder_add_op_val(fb, 0x03, 0x89);
+ flow_builder_add_op_val(fb, 0x45, 0x8b);
+ flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+ flow_builder_set_type(fb, FLOW_TYPE_NEXT_HEADER);
+ flow_builder_add_op_val(fb, 0, 0x06);
+
+ net_fill_ip6((net_addr *) &ip, ip6_build(0xc0000000,0,0,0), 8);
+ flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+ flow_builder6_add_pfx(fb, &ip, 0);
+
+ net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+ flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+ flow_builder6_add_pfx(fb, &ip, 61);
+
+ res = flow_builder6_finalize(fb, lp);
+ bt_assert(memcmp(res, expect, expect->length) == 0);
+
+ return 1;
+}
+
+static int
+t_formatting4(void)
+{
+ char b[1024];
+
+ byte nlri[] = {
+ 0,
+ FLOW_TYPE_DST_PREFIX, 0x08, 10,
+ FLOW_TYPE_IP_PROTOCOL, 0x81, 23,
+ FLOW_TYPE_DST_PORT, 0x02, 24, 0x44, 30, 0x03, 40, 0x45, 50, 0x03, 60, 0x45, 70, 0x01, 80, 0xc3, 90,
+ FLOW_TYPE_SRC_PORT, 0x02, 24, 0x44, 0x1e, 0x01, 0x28, 0x01, 0x32, 0x03, 0x3c, 0x45, 0x46, 0x81, 0x50,
+ FLOW_TYPE_ICMP_TYPE, 0x81, 0x50,
+ FLOW_TYPE_ICMP_CODE, 0x81, 0x5a,
+ FLOW_TYPE_TCP_FLAGS, 0x01, 0x03, 0xc2, 0x0c,
+ FLOW_TYPE_PACKET_LENGTH, 0x03, 0, 0xd5, 0xff, 0xff,
+ FLOW_TYPE_DSCP, 0x81, 63,
+ FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02
+ };
+ *nlri = (u8) sizeof(nlri);
+
+ net_addr_flow4 *input;
+ NET_ADDR_FLOW4_(input, ip4_build(5, 6, 7, 0), 24, nlri);
+
+ const char *expect = "flow4 { dst 10.0.0.0/8; proto 23; dport > 24 && < 30 || 40..50,60..70,80 && >= 90; sport > 24 && < 30 || 40,50,60..70,80; icmp type 80; icmp code 90; tcp flags 0x3/0x3,0x0/0xc; length 0..65535; dscp 63; fragment dont_fragment || !is_fragment; }";
+
+ bt_assert(flow4_net_format(b, sizeof(b), input) == strlen(expect));
+ bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
+ bt_assert(strcmp(b, expect) == 0);
+
+ return 1;
+}
+
+static int
+t_formatting6(void)
+{
+ char b[1024];
+
+ byte nlri[] = {
+ 0,
+ FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+ FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+ FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+ FLOW_TYPE_PORT, 0x03, 20, 0x45, 40, 0x91, 0x01, 0x11,
+ FLOW_TYPE_LABEL, 0xa0, 0x12, 0x34, 0x56, 0x78,
+ };
+ *nlri = (u8) sizeof(nlri);
+
+ net_addr_flow6 *input;
+ NET_ADDR_FLOW6_(input, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
+
+ const char *expect = "flow6 { dst ::1:1234:5678:9800:0/103 offset 61; src c000::/8; next header 6; port 20..40,273; label !0x0/0x12345678; }";
+
+ bt_assert(flow6_net_format(b, sizeof(b), input) == strlen(expect));
+ bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
+ bt_assert(strcmp(b, expect) == 0);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_read_length, "Testing get NLRI length");
+ bt_test_suite(t_write_length, "Testing set NLRI length");
+ bt_test_suite(t_first_part, "Searching first part in net_addr_flow");
+ bt_test_suite(t_iterators4, "Testing iterators (IPv4)");
+ bt_test_suite(t_iterators6, "Testing iterators (IPv6)");
+ bt_test_suite(t_validation4, "Testing validation (IPv4)");
+ bt_test_suite(t_validation6, "Testing validation (IPv6)");
+ bt_test_suite(t_builder4, "Inserting components into existing Flow Specification (IPv4)");
+ bt_test_suite(t_builder6, "Inserting components into existing Flow Specification (IPv6)");
+ bt_test_suite(t_formatting4, "Formatting Flow Specification (IPv4) into text representation");
+ bt_test_suite(t_formatting6, "Formatting Flow Specification (IPv6) into text representation");
+
+ return bt_exit_value();
+}
diff --git a/lib/hash.h b/lib/hash.h
index 6995bbc8..97d8f69c 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -194,4 +194,40 @@
#define HASH_WALK_FILTER_END } while (0)
+
+static inline void
+mem_hash_init(u64 *h)
+{
+ *h = 0x001047d54778bcafULL;
+}
+
+static inline void
+mem_hash_mix(u64 *h, void *p, uint s)
+{
+ const u64 multiplier = 0xb38bc09a61202731ULL;
+ const char *pp = p;
+ uint i;
+
+ for (i=0; i<s/4; i++)
+ *h = *h * multiplier + ((const u32 *)pp)[i];
+
+ for (i=s & ~0x3; i<s; i++)
+ *h = *h * multiplier + pp[i];
+}
+
+static inline uint
+mem_hash_value(u64 *h)
+{
+ return ((*h >> 32) ^ (*h & 0xffffffff));
+}
+
+static inline uint
+mem_hash(void *p, uint s)
+{
+ static u64 h;
+ mem_hash_init(&h);
+ mem_hash_mix(&h, p, s);
+ return mem_hash_value(&h);
+}
+
#endif
diff --git a/lib/hash_test.c b/lib/hash_test.c
new file mode 100644
index 00000000..59beb7c0
--- /dev/null
+++ b/lib/hash_test.c
@@ -0,0 +1,305 @@
+/*
+ * BIRD Library -- Hash Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#undef LOCAL_DEBUG
+
+#include "test/birdtest.h"
+
+#include "lib/hash.h"
+
+struct test_node {
+ struct test_node *next; /* Hash chain */
+ u32 key;
+};
+
+#define TEST_KEY(n) n->key
+#define TEST_NEXT(n) n->next
+#define TEST_EQ(n1,n2) n1 == n2
+#define TEST_FN(n) (n) ^ u32_hash((n))
+#define TEST_ORDER 13
+#define TEST_PARAMS /TEST_ORDER, *2, 2, 2, TEST_ORDER, 20
+#define TEST_REHASH test_rehash
+
+HASH_DEFINE_REHASH_FN(TEST, struct test_node);
+
+HASH(struct test_node) hash;
+struct pool *my_pool;
+
+#define MAX_NUM (1 << TEST_ORDER)
+
+struct test_node nodes[MAX_NUM];
+
+static void
+print_rate_of_fulfilment(void)
+{
+ int i;
+ int num_stacked_items = 0;
+
+ for (i = 0; i < MAX_NUM; i++)
+ if (!hash.data[i])
+ num_stacked_items++;
+
+ double percent_stacked_items = ((double)num_stacked_items/(double)MAX_NUM)*100.;
+ bt_debug("%d (%.2f %%) chained of %d hashes \n", num_stacked_items, percent_stacked_items, MAX_NUM);
+}
+
+#ifdef LOCAL_DEBUG
+static void
+dump_nodes(void)
+{
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ bt_debug("nodes[%3d] is at address %14p has .key %3d, .next %14p \n", i, &nodes[i], nodes[i].key, nodes[i].next);
+}
+#endif
+
+static void
+init_hash_(uint order)
+{
+ resource_init();
+ my_pool = rp_new(&root_pool, "Test pool");
+
+ HASH_INIT(hash, my_pool, order);
+
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ nodes[i].key = i;
+ nodes[i].next = NULL;
+ }
+
+ bt_debug("MAX_NUM %d \n", MAX_NUM);
+}
+
+static void
+init_hash(void)
+{
+ init_hash_(TEST_ORDER);
+}
+
+static void
+validate_filled_hash(void)
+{
+ int i;
+ struct test_node *node;
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ node = HASH_FIND(hash, TEST, nodes[i].key);
+ bt_assert_msg(node->key == nodes[i].key, "Hash should be filled, to find (%p) the node[%d] (%p) with .key = %u, .next %p", node, i, &nodes[i], nodes[i].key, nodes[i].next);
+ }
+
+ print_rate_of_fulfilment();
+}
+
+static void
+validate_empty_hash(void)
+{
+ int i;
+ struct test_node *node;
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ node = HASH_FIND(hash, TEST, nodes[i].key);
+ bt_assert_msg(node == NULL, "Hash should be empty, to find (%p) the node[%d] (%p) with .key %u, .next %p", node, i, &nodes[i], nodes[i].key, nodes[i].next);
+ }
+}
+
+static void
+fill_hash(void)
+{
+ int i;
+ struct test_node *node;
+
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ nodes[i].key = i;
+ node = &nodes[i];
+ HASH_INSERT(hash, TEST, node);
+ }
+}
+
+static int
+t_insert_find(void)
+{
+ init_hash();
+ fill_hash();
+ validate_filled_hash();
+
+ return 1;
+}
+
+static int
+t_insert_find_random(void)
+{
+ init_hash();
+
+ int i;
+ struct test_node *node;
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ nodes[i].key = bt_random();
+ node = &nodes[i];
+ HASH_INSERT(hash, TEST, node);
+ }
+
+ validate_filled_hash();
+
+ return 1;
+}
+
+static int
+t_insert2_find(void)
+{
+ init_hash_(1);
+
+ int i;
+ struct test_node *node;
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ nodes[i].key = i;
+ node = &nodes[i];
+ HASH_INSERT2(hash, TEST, my_pool, node);
+ }
+ bt_assert_msg(hash.order != 1, "The hash should auto-resize from order 2^1. The order of the hash is 2^%u.", hash.order);
+
+ validate_filled_hash();
+
+ return 1;
+}
+
+static int
+t_walk(void)
+{
+ init_hash();
+ fill_hash();
+
+ uint i;
+ uint check[MAX_NUM];
+ for (i = 0; i < MAX_NUM; i++)
+ check[i] = 0;
+
+ HASH_WALK(hash, next, n)
+ {
+ check[n->key]++;
+ }
+ HASH_WALK_END;
+
+ for (i = 0; i < MAX_NUM; i++)
+ bt_assert(check[i] == 1);
+
+ return 1;
+}
+
+static int
+t_walk_delsafe_delete(void)
+{
+ init_hash();
+ fill_hash();
+
+ HASH_WALK_DELSAFE(hash, next, n)
+ {
+ HASH_DELETE(hash, TEST, n->key);
+ }
+ HASH_WALK_DELSAFE_END;
+
+ validate_empty_hash();
+
+ return 1;
+}
+
+static int
+t_walk_delsafe_remove(void)
+{
+ init_hash();
+ fill_hash();
+
+ HASH_WALK_DELSAFE(hash, next, n)
+ {
+ HASH_REMOVE(hash, TEST, n);
+ }
+ HASH_WALK_DELSAFE_END;
+
+ validate_empty_hash();
+
+ return 1;
+}
+
+static int
+t_walk_delsafe_delete2(void)
+{
+ init_hash();
+ fill_hash();
+
+ HASH_WALK_DELSAFE(hash, next, n)
+ {
+ HASH_DELETE2(hash, TEST, my_pool, n->key);
+ }
+ HASH_WALK_DELSAFE_END;
+
+ validate_empty_hash();
+
+ return 1;
+}
+
+static int
+t_walk_delsafe_remove2(void)
+{
+ init_hash();
+ fill_hash();
+
+ HASH_WALK_DELSAFE(hash, next, n)
+ {
+ HASH_REMOVE2(hash, TEST, my_pool, n);
+ }
+ HASH_WALK_DELSAFE_END;
+
+ validate_empty_hash();
+
+ return 1;
+}
+
+static int
+t_walk_filter(void)
+{
+ init_hash();
+ fill_hash();
+
+ uint i;
+ uint check[MAX_NUM];
+ for (i = 0; i < MAX_NUM; i++)
+ check[i] = 0;
+
+ HASH_WALK_FILTER(hash, next, n, m)
+ {
+ bt_assert(n == *m);
+ check[n->key]++;
+ }
+ HASH_WALK_FILTER_END;
+
+ for (i = 0; i < MAX_NUM; i++)
+ bt_assert(check[i] == 1);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ 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");
+ bt_test_suite(t_insert2_find, "HASH_INSERT2 and HASH_FIND. HASH_INSERT2 is HASH_INSERT and a smart auto-resize function");
+ bt_test_suite(t_walk, "HASH_WALK");
+ bt_test_suite(t_walk_delsafe_delete, "HASH_WALK_DELSAFE and HASH_DELETE");
+ bt_test_suite(t_walk_delsafe_delete2, "HASH_WALK_DELSAFE and HASH_DELETE2. HASH_DELETE2 is HASH_DELETE and smart auto-resize function");
+ bt_test_suite(t_walk_delsafe_remove, "HASH_WALK_DELSAFE and HASH_REMOVE");
+ bt_test_suite(t_walk_delsafe_remove2, "HASH_WALK_DELSAFE and HASH_REMOVE2. HASH_REMOVE2 is HASH_REMOVE and smart auto-resize function");
+ bt_test_suite(t_walk_filter, "HASH_WALK_FILTER");
+
+ return bt_exit_value();
+}
diff --git a/lib/heap_test.c b/lib/heap_test.c
new file mode 100644
index 00000000..c04a0450
--- /dev/null
+++ b/lib/heap_test.c
@@ -0,0 +1,186 @@
+/*
+ * BIRD Library -- Universal Heap Macros Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+#include "sysdep/config.h"
+#include "lib/heap.h"
+
+#define MAX_NUM 1000
+#define SPECIAL_KEY -3213
+
+#define MY_CMP(x, y) ((x) < (y))
+
+#define MY_HEAP_SWAP(heap,a,b,t) \
+ do { \
+ bt_debug("swap(%u %u) ", a, b); \
+ HEAP_SWAP(heap,a,b,t); \
+ } while(0)
+
+static int heap[MAX_NUM+1];
+static uint num;
+
+/*
+ * A valid heap must follow these rules:
+ * - `num >= 0`
+ * - `heap[i] >= heap[i / 2]` for each `i` in `[2, num]`
+ */
+static int
+is_heap_valid(int heap[], uint num)
+{
+ uint i;
+
+ if (num > MAX_NUM)
+ return 0;
+
+ for (i = 2; i <= num; i++)
+ if (heap[i] < heap[i / 2])
+ return 0;
+
+ return 1;
+}
+
+static void
+show_heap(void)
+{
+ uint i;
+ bt_debug("\n");
+ bt_debug("numbers %u; ", num);
+ for (i = 0; i <= num; i++)
+ bt_debug("%d ", heap[i]);
+ bt_debug(is_heap_valid(heap, num) ? "OK" : "NON-VALID HEAP!");
+ bt_debug("\n");
+}
+
+static void
+init_heap(void)
+{
+ uint i;
+ num = 0;
+ heap[0] = SPECIAL_KEY; /* heap[0] should be unused */
+ for (i = 1; i <= MAX_NUM; i++)
+ heap[i] = 0;
+}
+
+static int
+t_heap_insert(void)
+{
+ uint i;
+
+ init_heap();
+
+ for (i = MAX_NUM; i >= 1; i--)
+ {
+ bt_debug("ins %u at pos %u ", i, MAX_NUM - i);
+ heap[MAX_NUM - i + 1] = i;
+ HEAP_INSERT(heap, ++num, int, MY_CMP, MY_HEAP_SWAP);
+ show_heap();
+ bt_assert(is_heap_valid(heap, num));
+ }
+
+ return 1;
+}
+
+static int
+t_heap_increase_decrease(void)
+{
+ uint i;
+
+ t_heap_insert();
+
+ for (i = 1; i <= MAX_NUM; i++)
+ {
+ if ((int)i > heap[i])
+ {
+ bt_debug("inc %u ", i);
+ heap[i] = i;
+ HEAP_INCREASE(heap, num, int, MY_CMP, MY_HEAP_SWAP, i);
+ }
+ else if ((int)i < heap[i])
+ {
+ bt_debug("dec %u ", i);
+ heap[i] = i;
+ HEAP_INCREASE(heap, num, int, MY_CMP, MY_HEAP_SWAP, i);
+ }
+ show_heap();
+ bt_assert(is_heap_valid(heap, num));
+ }
+
+ return 1;
+}
+
+static int
+t_heap_delete(void)
+{
+ uint i;
+
+ t_heap_insert();
+
+ for (i = 1; i <= num; i++)
+ {
+ bt_debug("del at pos %u ", i);
+ HEAP_DELETE(heap, num, int, MY_CMP, MY_HEAP_SWAP, i);
+ show_heap();
+ bt_assert(is_heap_valid(heap, num));
+ }
+
+ return 1;
+}
+
+static int
+t_heap_0(void)
+{
+ init_heap();
+ t_heap_insert();
+ t_heap_increase_decrease();
+ t_heap_delete();
+
+ return heap[0] == SPECIAL_KEY;
+}
+
+static int
+t_heap_insert_random(void)
+{
+ int i, j;
+ int expected[MAX_NUM+1];
+
+ init_heap();
+
+ for (i = 1; i <= MAX_NUM; i++)
+ {
+ heap[i] = expected[i] = bt_random();
+ HEAP_INSERT(heap, ++num, int, MY_CMP, MY_HEAP_SWAP);
+ show_heap();
+ bt_assert(is_heap_valid(heap, num));
+ }
+
+ for (i = 1; i <= MAX_NUM; i++)
+ for (j = 1; j <= MAX_NUM; j++)
+ if(expected[i] == heap[j])
+ break;
+ else if (j == MAX_NUM)
+ {
+ show_heap();
+ bt_abort_msg("Did not find a number %d in heap.", expected[i]);
+ }
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_heap_insert, "Inserting a descending sequence of numbers (the worst case)");
+ bt_test_suite(t_heap_insert_random, "Inserting pseudo-random numbers");
+ bt_test_suite(t_heap_increase_decrease, "Increasing/Decreasing");
+ bt_test_suite(t_heap_delete, "Deleting");
+ bt_test_suite(t_heap_0, "Is a heap[0] really unused?");
+
+ return bt_exit_value();
+}
diff --git a/lib/idm.c b/lib/idm.c
new file mode 100644
index 00000000..66e311c6
--- /dev/null
+++ b/lib/idm.c
@@ -0,0 +1,76 @@
+/*
+ * BIRD Library -- ID Map
+ *
+ * (c) 2013--2015 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2013--2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdlib.h>
+
+#include "nest/bird.h"
+#include "lib/idm.h"
+#include "lib/resource.h"
+#include "lib/string.h"
+
+
+void
+idm_init(struct idm *m, pool *p, uint size)
+{
+ m->pos = 0;
+ m->used = 1;
+ m->size = size;
+ m->data = mb_allocz(p, m->size * sizeof(u32));
+
+ /* ID 0 is reserved */
+ m->data[0] = 1;
+}
+
+static inline int u32_cto(uint x) { return ffs(~x) - 1; }
+
+u32
+idm_alloc(struct idm *m)
+{
+ uint i, j;
+
+ for (i = m->pos; i < m->size; i++)
+ if (m->data[i] != 0xffffffff)
+ goto found;
+
+ /* If we are at least 7/8 full, expand */
+ if (m->used > (m->size * 28))
+ {
+ m->size *= 2;
+ m->data = mb_realloc(m->data, m->size * sizeof(u32));
+ memset(m->data + i, 0, (m->size - i) * sizeof(u32));
+ goto found;
+ }
+
+ for (i = 0; i < m->pos; i++)
+ if (m->data[i] != 0xffffffff)
+ goto found;
+
+ ASSERT(0);
+
+found:
+ ASSERT(i < 0x8000000);
+
+ m->pos = i;
+ j = u32_cto(m->data[i]);
+
+ m->data[i] |= (1 << j);
+ m->used++;
+ return 32 * i + j;
+}
+
+void
+idm_free(struct idm *m, u32 id)
+{
+ uint i = id / 32;
+ uint j = id % 32;
+
+ ASSERT((i < m->size) && (m->data[i] & (1 << j)));
+ m->data[i] &= ~(1 << j);
+ m->used--;
+}
diff --git a/lib/idm.h b/lib/idm.h
new file mode 100644
index 00000000..e3380cce
--- /dev/null
+++ b/lib/idm.h
@@ -0,0 +1,25 @@
+/*
+ * BIRD Library -- ID Map
+ *
+ * (c) 2013--2015 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2013--2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_IDM_H_
+#define _BIRD_IDM_H_
+
+struct idm
+{
+ u32 *data;
+ u32 pos;
+ u32 used;
+ u32 size;
+};
+
+void idm_init(struct idm *m, pool *p, uint size);
+u32 idm_alloc(struct idm *m);
+void idm_free(struct idm *m, u32 id);
+
+#endif
diff --git a/lib/ip.c b/lib/ip.c
index 2050d19e..9497248c 100644
--- a/lib/ip.c
+++ b/lib/ip.c
@@ -58,7 +58,7 @@ ip6_mkmask(uint n)
return a;
}
-int
+uint
ip6_masklen(ip6_addr *a)
{
int i, j, n;
@@ -67,12 +67,12 @@ ip6_masklen(ip6_addr *a)
if (a->addr[i] != ~0U)
{
j = u32_masklen(a->addr[i]);
- if (j < 0)
+ if (j == 255)
return j;
n += j;
while (++i < 4)
if (a->addr[i])
- return -1;
+ return 255;
break;
}
@@ -306,7 +306,7 @@ ip6_pton(const char *a, ip6_addr *o)
if (*a == ':' && a[1])
a++;
- else if (*a == '.' && (i == 6 || i < 6 && hfil >= 0))
+ else if (*a == '.' && (i == 6 || (i < 6 && hfil >= 0)))
{ /* Embedded IPv4 address */
ip4_addr x;
if (!ip4_pton(start, &x))
diff --git a/lib/ip.h b/lib/ip.h
index 298b72a7..5cfce1f1 100644
--- a/lib/ip.h
+++ b/lib/ip.h
@@ -9,7 +9,7 @@
#ifndef _BIRD_IP_H_
#define _BIRD_IP_H_
-#include "lib/endian.h"
+#include "sysdep/unix/endian.h"
#include "lib/string.h"
#include "lib/bitops.h"
#include "lib/unaligned.h"
@@ -31,6 +31,13 @@
#define IP4_NONE _MI4(0)
#define IP6_NONE _MI6(0,0,0,0)
+#define IP4_MAX_PREFIX_LENGTH 32
+#define IP6_MAX_PREFIX_LENGTH 128
+
+#define IP4_MAX_TEXT_LENGTH 15 /* "255.255.255.255" */
+#define IP6_MAX_TEXT_LENGTH 39 /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" */
+#define IPA_MAX_TEXT_LENGTH 39
+
#define IP4_MIN_MTU 576
#define IP6_MIN_MTU 1280
@@ -41,19 +48,6 @@
#define UDP_HEADER_LENGTH 8
-#ifdef IPV6
-#define MAX_PREFIX_LENGTH 128
-#define BITS_PER_IP_ADDRESS 128
-#define STD_ADDRESS_P_LENGTH 39
-#define SIZE_OF_IP_HEADER 40
-#else
-#define MAX_PREFIX_LENGTH 32
-#define BITS_PER_IP_ADDRESS 32
-#define STD_ADDRESS_P_LENGTH 15
-#define SIZE_OF_IP_HEADER 24
-#endif
-
-
#ifdef DEBUGGING
typedef struct ip4_addr {
@@ -84,8 +78,6 @@ typedef struct ip6_addr {
#define _I3(a) ((a).addr[3])
-#ifdef IPV6
-
/* Structure ip_addr may contain both IPv4 and IPv6 addresses */
typedef ip6_addr ip_addr;
#define IPA_NONE IP6_NONE
@@ -99,24 +91,10 @@ typedef ip6_addr ip_addr;
#define ipa_to_u32(x) ip4_to_u32(ipa_to_ip4(x))
#define ipa_is_ip4(a) ip6_is_v4mapped(a)
+#define ipa_is_ip6(a) (! ip6_is_v4mapped(a))
-#else
-
-/* Provisionary ip_addr definition same as ip4_addr */
-typedef ip4_addr ip_addr;
-#define IPA_NONE IP4_NONE
-
-#define ipa_from_ip4(x) x
-#define ipa_from_ip6(x) IPA_NONE
-#define ipa_from_u32(x) ipa_from_ip4(ip4_from_u32(x))
-
-#define ipa_to_ip4(x) x
-#define ipa_to_ip6(x) IP6_NONE
-#define ipa_to_u32(x) ip4_to_u32(ipa_to_ip4(x))
-
-#define ipa_is_ip4(a) 1
-
-#endif
+#define IPA_NONE4 ipa_from_ip4(IP4_NONE)
+#define IPA_NONE6 ipa_from_ip6(IP6_NONE)
/*
@@ -181,7 +159,6 @@ static inline ip6_addr ip6_not(ip6_addr a)
{ return _MI6(~_I0(a), ~_I1(a), ~_I2(a), ~_I3(a)); }
-#ifdef IPV6
#define ipa_equal(x,y) ip6_equal(x,y)
#define ipa_zero(x) ip6_zero(x)
#define ipa_nonzero(x) ip6_nonzero(x)
@@ -189,19 +166,8 @@ static inline ip6_addr ip6_not(ip6_addr a)
#define ipa_or(x,y) ip6_or(x,y)
#define ipa_xor(x,y) ip6_xor(x,y)
#define ipa_not(x) ip6_not(x)
-#else
-#define ipa_equal(x,y) ip4_equal(x,y)
-#define ipa_zero(x) ip4_zero(x)
-#define ipa_nonzero(x) ip4_nonzero(x)
-#define ipa_and(x,y) ip4_and(x,y)
-#define ipa_or(x,y) ip4_or(x,y)
-#define ipa_xor(x,y) ip4_xor(x,y)
-#define ipa_not(x) ip4_not(x)
-#endif
-
-#ifdef IPV6
/*
* A zero address is either a token for invalid/unused, or the prefix of default
* routes. These functions should be used in the second case, where both IPv4
@@ -214,26 +180,12 @@ static inline int ipa_zero2(ip_addr a)
static inline int ipa_nonzero2(ip_addr a)
{ return _I0(a) || _I1(a) || ((_I2(a) != 0) && (_I2(a) != 0xffff)) || _I3(a); }
-#else
-#define ipa_zero2(x) ip4_zero(x)
-#define ipa_nonzero2(x) ip4_nonzero(x)
-#endif
-
/*
* Hash and compare functions
*/
-static inline uint ip4_hash(ip4_addr a)
-{
- /* Returns a 16-bit value */
- u32 x = _I(a);
- x ^= x >> 16;
- x ^= x << 10;
- return x & 0xffff;
-}
-
-static inline u32 ip4_hash32(ip4_addr a)
+static inline u32 ip4_hash(ip4_addr a)
{
/* Returns a 32-bit value, although low-order bits are not mixed */
u32 x = _I(a);
@@ -242,14 +194,7 @@ static inline u32 ip4_hash32(ip4_addr a)
return x;
}
-static inline uint ip6_hash(ip6_addr a)
-{
- /* Returns a 16-bit hash key */
- u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a);
- return (x ^ (x >> 16) ^ (x >> 8)) & 0xffff;
-}
-
-static inline u32 ip6_hash32(ip6_addr a)
+static inline u32 ip6_hash(ip6_addr a)
{
/* Returns a 32-bit hash key, although low-order bits are not mixed */
u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a);
@@ -261,16 +206,8 @@ static inline int ip4_compare(ip4_addr a, ip4_addr b)
int ip6_compare(ip6_addr a, ip6_addr b);
-
-#ifdef IPV6
#define ipa_hash(x) ip6_hash(x)
-#define ipa_hash32(x) ip6_hash32(x)
#define ipa_compare(x,y) ip6_compare(x,y)
-#else
-#define ipa_hash(x) ip4_hash(x)
-#define ipa_hash32(x) ip4_hash32(x)
-#define ipa_compare(x,y) ip4_compare(x,y)
-#endif
/*
@@ -301,14 +238,10 @@ static inline int ip6_is_link_local(ip6_addr a)
static inline int ip6_is_v4mapped(ip6_addr a)
{ return _I0(a) == 0 && _I1(a) == 0 && _I2(a) == 0xffff; }
-#ifdef IPV6
#define ipa_classify(x) ip6_classify(&(x))
#define ipa_is_link_local(x) ip6_is_link_local(x)
-#else
-#define ipa_classify(x) ip4_classify(x)
-#define ipa_is_link_local(x) 0
-#endif
+/* XXXX remove */
static inline int ipa_classify_net(ip_addr a)
{ return ipa_zero2(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); }
@@ -320,11 +253,11 @@ static inline int ipa_classify_net(ip_addr a)
static inline ip4_addr ip4_mkmask(uint n)
{ return _MI4(u32_mkmask(n)); }
-static inline int ip4_masklen(ip4_addr a)
+static inline uint ip4_masklen(ip4_addr a)
{ return u32_masklen(_I(a)); }
ip6_addr ip6_mkmask(uint n);
-int ip6_masklen(ip6_addr *a);
+uint ip6_masklen(ip6_addr *a);
/* ipX_pxlen() requires that x != y */
static inline uint ip4_pxlen(ip4_addr a, ip4_addr b)
@@ -346,6 +279,18 @@ static inline u32 ip4_getbit(ip4_addr a, uint pos)
static inline u32 ip6_getbit(ip6_addr a, uint pos)
{ return a.addr[pos / 32] & (0x80000000 >> (pos % 32)); }
+static inline u32 ip4_setbit(ip4_addr *a, uint pos)
+{ return _I(*a) |= (0x80000000 >> pos); }
+
+static inline u32 ip6_setbit(ip6_addr *a, uint pos)
+{ return a->addr[pos / 32] |= (0x80000000 >> (pos % 32)); }
+
+static inline u32 ip4_clrbit(ip4_addr *a, uint pos)
+{ return _I(*a) &= ~(0x80000000 >> pos); }
+
+static inline u32 ip6_clrbit(ip6_addr *a, uint pos)
+{ return a->addr[pos / 32] &= ~(0x80000000 >> (pos % 32)); }
+
static inline ip4_addr ip4_opposite_m1(ip4_addr a)
{ return _MI4(_I(a) ^ 1); }
@@ -360,21 +305,8 @@ static inline ip6_addr ip6_opposite_m2(ip6_addr a)
ip4_addr ip4_class_mask(ip4_addr ad);
-#ifdef IPV6
-#define ipa_mkmask(x) ip6_mkmask(x)
-#define ipa_masklen(x) ip6_masklen(&x)
-#define ipa_pxlen(x,y) ip6_pxlen(x,y)
-#define ipa_getbit(x,n) ip6_getbit(x,n)
#define ipa_opposite_m1(x) ip6_opposite_m1(x)
#define ipa_opposite_m2(x) ip6_opposite_m2(x)
-#else
-#define ipa_mkmask(x) ip4_mkmask(x)
-#define ipa_masklen(x) ip4_masklen(x)
-#define ipa_pxlen(x,y) ip4_pxlen(x,y)
-#define ipa_getbit(x,n) ip4_getbit(x,n)
-#define ipa_opposite_m1(x) ip4_opposite_m1(x)
-#define ipa_opposite_m2(x) ip4_opposite_m2(x)
-#endif
/*
@@ -393,14 +325,33 @@ static inline ip6_addr ip6_hton(ip6_addr a)
static inline ip6_addr ip6_ntoh(ip6_addr a)
{ return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); }
-#ifdef IPV6
-#define ipa_hton(x) x = ip6_hton(x)
-#define ipa_ntoh(x) x = ip6_ntoh(x)
-#else
-#define ipa_hton(x) x = ip4_hton(x)
-#define ipa_ntoh(x) x = ip4_ntoh(x)
-#endif
+#define MPLS_MAX_LABEL_STACK 8
+typedef struct mpls_label_stack {
+ uint len;
+ u32 stack[MPLS_MAX_LABEL_STACK];
+} mpls_label_stack;
+
+static inline int
+mpls_get(const char *buf, int buflen, u32 *stack)
+{
+ for (int i=0; (i<MPLS_MAX_LABEL_STACK) && (i*4+3 < buflen); i++)
+ {
+ u32 s = get_u32(buf + i*4);
+ stack[i] = s >> 12;
+ if (s & 0x100)
+ return i+1;
+ }
+ return -1;
+}
+
+static inline int
+mpls_put(char *buf, int len, u32 *stack)
+{
+ for (int i=0; i<len; i++)
+ put_u32(buf + i*4, stack[i] << 12 | (i+1 == len ? 0x100 : 0));
+ return len*4;
+}
/*
* Unaligned data access (in network order)
@@ -431,15 +382,6 @@ static inline void * put_ip6(void *buf, ip6_addr a)
return buf+16;
}
-// XXXX these functions must be redesigned or removed
-#ifdef IPV6
-#define get_ipa(x) get_ip6(x)
-#define put_ipa(x,y) put_ip6(x,y)
-#else
-#define get_ipa(x) get_ip4(x)
-#define put_ipa(x,y) put_ip4(x,y)
-#endif
-
/*
* Binary/text form conversions
@@ -457,34 +399,11 @@ static inline char * ip6_ntox(ip6_addr a, char *b)
int ip4_pton(const char *a, ip4_addr *o);
int ip6_pton(const char *a, ip6_addr *o);
-// XXXX these functions must be redesigned or removed
-#ifdef IPV6
-#define ipa_ntop(x,y) ip6_ntop(x,y)
-#define ipa_ntox(x,y) ip6_ntox(x,y)
-#define ipa_pton(x,y) ip6_pton(x,y)
-#else
-#define ipa_ntop(x,y) ip4_ntop(x,y)
-#define ipa_ntox(x,y) ip4_ntox(x,y)
-#define ipa_pton(x,y) ip4_pton(x,y)
-#endif
-
/*
* Miscellaneous
*/
-// XXXX review this
-
-#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l)))))
-#define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p))))
-#define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2)))))
-
char *ip_scope_text(uint);
-struct prefix {
- ip_addr addr;
- uint len;
-};
-
-
#endif
diff --git a/lib/ip_test.c b/lib/ip_test.c
new file mode 100644
index 00000000..fd70c957
--- /dev/null
+++ b/lib/ip_test.c
@@ -0,0 +1,161 @@
+/*
+ * BIRD Library -- IP address functions Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+
+#include "lib/ip.h"
+
+#define IP4_MAX_LEN 16
+
+static int
+test_ipa_pton(void *out_, const void *in_, const void *expected_out_)
+{
+ ip_addr *out = out_;
+ const char *in = in_;
+ const ip_addr *expected_out = expected_out_;
+
+ if (ipa_is_ip4(*expected_out))
+ {
+ ip4_addr ip4;
+ bt_assert(ip4_pton(in, &ip4));
+ *out = ipa_from_ip4(ip4);
+ }
+ else
+ {
+ bt_assert(ip6_pton(in, out));
+ /* ip_addr == ip6_addr */
+ }
+
+ return ipa_equal(*out, *expected_out);
+}
+
+static int
+t_ip4_pton(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "192.168.1.128",
+ .out = & ipa_build4(192, 168, 1, 128),
+ },
+ {
+ .in = "255.255.255.255",
+ .out = & ipa_build4(255, 255, 255, 255),
+ },
+ {
+ .in = "0.0.0.0",
+ .out = & ipa_build4(0, 0, 0, 0),
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_ipa_pton, bt_fmt_str, bt_fmt_ipa);
+}
+
+static int
+t_ip6_pton(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "2001:0db8:0000:0000:0000:0000:1428:57ab",
+ .out = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
+ },
+ {
+ .in = "2001:0db8:0000:0000:0000::1428:57ab",
+ .out = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
+ },
+ {
+ .in = "2001:0db8::1428:57ab",
+ .out = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
+ },
+ {
+ .in = "2001:db8::1428:57ab",
+ .out = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
+ },
+ {
+ .in = "::1",
+ .out = & ipa_build6(0x00000000, 0x00000000, 0x00000000, 0x00000001),
+ },
+ {
+ .in = "::",
+ .out = & ipa_build6(0x00000000, 0x00000000, 0x00000000, 0x00000000),
+ },
+ {
+ .in = "2605:2700:0:3::4713:93e3",
+ .out = & ipa_build6(0x26052700, 0x00000003, 0x00000000, 0x471393E3),
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_ipa_pton, bt_fmt_str, bt_fmt_ipa);
+}
+
+static int
+test_ipa_ntop(void *out_, const void *in_, const void *expected_out_)
+{
+ char *out = out_;
+ const ip_addr *in = in_;
+ const char *expected_out = expected_out_;
+
+ if (ipa_is_ip4(*in))
+ ip4_ntop(ipa_to_ip4(*in), out);
+ else
+ ip6_ntop(ipa_to_ip6(*in), out);
+
+ int result = strncmp(out, expected_out, ipa_is_ip4(*in) ? IP4_MAX_TEXT_LENGTH : IP6_MAX_TEXT_LENGTH) == 0;
+ return result;
+}
+
+static int
+t_ip4_ntop(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & ipa_build4(192, 168, 1, 128),
+ .out = "192.168.1.128",
+ },
+ {
+ .in = & ipa_build4(255, 255, 255, 255),
+ .out = "255.255.255.255",
+ },
+ {
+ .in = & ipa_build4(0, 0, 0, 1),
+ .out = "0.0.0.1",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_ipa_ntop, bt_fmt_ipa, bt_fmt_str);
+}
+
+static int
+t_ip6_ntop(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
+ .out = "2001:db8::1428:57ab",
+ },
+ {
+ .in = & ipa_build6(0x26052700, 0x00000003, 0x00000000, 0x471393E3),
+ .out = "2605:2700:0:3::4713:93e3",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_ipa_ntop, bt_fmt_ipa, bt_fmt_str);
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_ip4_pton, "Converting IPv4 string to ip4_addr struct");
+ bt_test_suite(t_ip6_pton, "Converting IPv6 string to ip6_addr struct");
+ bt_test_suite(t_ip4_ntop, "Converting ip4_addr struct to IPv4 string");
+ bt_test_suite(t_ip6_ntop, "Converting ip6_addr struct to IPv6 string");
+
+ return bt_exit_value();
+}
+
diff --git a/lib/lists.c b/lib/lists.c
index 12ef3cc6..4a48d3b7 100644
--- a/lib/lists.c
+++ b/lib/lists.c
@@ -158,3 +158,15 @@ add_tail_list(list *to, list *l)
q->next = &to->tail_node;
to->tail = q;
}
+
+LIST_INLINE uint
+list_length(list *l)
+{
+ uint len = 0;
+ node *n;
+
+ WALK_LIST(n, *l)
+ len++;
+
+ return len;
+}
diff --git a/lib/lists.h b/lib/lists.h
index 51856b05..066eafbb 100644
--- a/lib/lists.h
+++ b/lib/lists.h
@@ -52,7 +52,10 @@ typedef union list { /* In fact two overlayed nodes */
#define WALK_LIST2(n,nn,list,pos) \
for(nn=(list).head; NODE_VALID(nn) && (n=SKIP_BACK(typeof(*n),pos,nn)); nn=nn->next)
#define WALK_LIST_DELSAFE(n,nxt,list) \
- for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt)
+ for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt)
+#define WALK_LIST2_DELSAFE(n,nn,nxt,list,pos) \
+ for(nn=HEAD(list); (nxt=nn->next) && (n=SKIP_BACK(typeof(*n),pos,nn)); nn=nxt)
+
/* WALK_LIST_FIRST supposes that called code removes each processed node */
#define WALK_LIST_FIRST(n,list) \
while(n=HEAD(list), (NODE (n))->next)
@@ -77,6 +80,7 @@ void rem_node(node *);
void add_tail_list(list *, list *);
void init_list(list *);
void insert_node(node *, node *);
+uint list_length(list *);
#endif
#endif
diff --git a/lib/lists_test.c b/lib/lists_test.c
new file mode 100644
index 00000000..f26a88e2
--- /dev/null
+++ b/lib/lists_test.c
@@ -0,0 +1,287 @@
+/*
+ * BIRD Library -- Linked Lists Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+#include "lib/lists.h"
+
+#define MAX_NUM 1000
+
+static node nodes[MAX_NUM];
+static list l;
+
+static void
+show_list(void)
+{
+ bt_debug("\n");
+ bt_debug("list.null is at %p and point to %p\n", &l.null, l.null);
+ bt_debug("list.head is at %p and point to %p\n", &l.head, l.head);
+ bt_debug("list.tail is at %p and point to %p\n", &l.tail, l.tail);
+
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ bt_debug("n[%3i] is at %p\n", i, &nodes[i]);
+ bt_debug(" prev is at %p and point to %p\n", &(nodes[i].prev), nodes[i].prev);
+ bt_debug(" next is at %p and point to %p\n", &(nodes[i].next), nodes[i].next);
+ }
+}
+
+static int
+is_filled_list_well_linked(void)
+{
+ int i;
+ bt_assert(l.head == &nodes[0]);
+ bt_assert(l.tail == &nodes[MAX_NUM-1]);
+ bt_assert((void *) nodes[0].prev == (void *) &l.head);
+ bt_assert((void *) nodes[MAX_NUM-1].next == (void *) &l.null);
+
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ if (i < (MAX_NUM-1))
+ bt_assert(nodes[i].next == &nodes[i+1]);
+
+ if (i > 0)
+ bt_assert(nodes[i].prev == &nodes[i-1]);
+ }
+
+ return 1;
+}
+
+static int
+is_empty_list_well_unlinked(void)
+{
+ int i;
+
+ bt_assert(l.head == NODE &l.null);
+ bt_assert(l.tail == NODE &l.head);
+ bt_assert(EMPTY_LIST(l));
+
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ bt_assert(nodes[i].next == NULL);
+ bt_assert(nodes[i].prev == NULL);
+ }
+
+ return 1;
+}
+
+static void
+init_list__(list *l, struct node nodes[])
+{
+ init_list(l);
+
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ nodes[i].next = NULL;
+ nodes[i].prev = NULL;
+ }
+}
+
+static void
+init_list_(void)
+{
+ init_list__(&l, (node *) nodes);
+}
+
+static int
+t_add_tail(void)
+{
+ int i;
+
+ init_list_();
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ add_tail(&l, &nodes[i]);
+ bt_debug(".");
+ bt_assert(l.tail == &nodes[i]);
+ bt_assert(l.head == &nodes[0]);
+ bt_assert((void *) nodes[i].next == (void *) &l.null);
+ if (i > 0)
+ {
+ bt_assert(nodes[i-1].next == &nodes[i]);
+ bt_assert(nodes[i].prev == &nodes[i-1]);
+ }
+ }
+ show_list();
+ bt_assert(is_filled_list_well_linked());
+
+ return 1;
+}
+
+static int
+t_add_head(void)
+{
+ int i;
+
+ init_list_();
+ for (i = MAX_NUM-1; i >= 0; i--)
+ {
+ add_head(&l, &nodes[i]);
+ bt_debug(".");
+ bt_assert(l.head == &nodes[i]);
+ bt_assert(l.tail == &nodes[MAX_NUM-1]);
+ if (i < MAX_NUM-1)
+ {
+ bt_assert(nodes[i+1].prev == &nodes[i]);
+ bt_assert(nodes[i].next == &nodes[i+1]);
+ }
+ }
+ show_list();
+ bt_assert(is_filled_list_well_linked());
+
+ return 1;
+}
+
+static void
+insert_node_(node *n, node *after)
+{
+ insert_node(n, after);
+ bt_debug(".");
+}
+
+static int
+t_insert_node(void)
+{
+ int i;
+
+ init_list_();
+
+ // add first node
+ insert_node_(&nodes[0], NODE &l.head);
+
+ // add odd nodes
+ for (i = 2; i < MAX_NUM; i+=2)
+ insert_node_(&nodes[i], &nodes[i-2]);
+
+ // add even nodes
+ for (i = 1; i < MAX_NUM; i+=2)
+ insert_node_(&nodes[i], &nodes[i-1]);
+
+ bt_debug("\n");
+ bt_assert(is_filled_list_well_linked());
+
+ return 1;
+}
+
+static void
+fill_list2(list *l, node nodes[])
+{
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ add_tail(l, &nodes[i]);
+}
+
+static void
+fill_list(void)
+{
+ fill_list2(&l, (node *) nodes);
+}
+
+static int
+t_remove_node(void)
+{
+ int i;
+
+ init_list_();
+
+ /* Fill & Remove & Check */
+ fill_list();
+ for (i = 0; i < MAX_NUM; i++)
+ rem_node(&nodes[i]);
+ bt_assert(is_empty_list_well_unlinked());
+
+ /* Fill & Remove the half of nodes & Check & Remove the rest nodes & Check */
+ fill_list();
+ for (i = 0; i < MAX_NUM; i+=2)
+ rem_node(&nodes[i]);
+
+ int tail_node_index = (MAX_NUM % 2) ? MAX_NUM - 2 : MAX_NUM - 1;
+ bt_assert(l.head == &nodes[1]);
+ bt_assert(l.tail == &nodes[tail_node_index]);
+ bt_assert(nodes[tail_node_index].next == NODE &l.null);
+
+ for (i = 1; i < MAX_NUM; i+=2)
+ {
+ if (i > 1)
+ bt_assert(nodes[i].prev == &nodes[i-2]);
+ if (i < tail_node_index)
+ bt_assert(nodes[i].next == &nodes[i+2]);
+ }
+
+ for (i = 1; i < MAX_NUM; i+=2)
+ rem_node(&nodes[i]);
+ bt_assert(is_empty_list_well_unlinked());
+
+ return 1;
+}
+
+static int
+t_replace_node(void)
+{
+ node head, inside, tail;
+
+ init_list_();
+ fill_list();
+
+ replace_node(&nodes[0], &head);
+ bt_assert(l.head == &head);
+ bt_assert(head.prev == NODE &l.head);
+ bt_assert(head.next == &nodes[1]);
+ bt_assert(nodes[1].prev == &head);
+
+ replace_node(&nodes[MAX_NUM/2], &inside);
+ bt_assert(nodes[MAX_NUM/2-1].next == &inside);
+ bt_assert(nodes[MAX_NUM/2+1].prev == &inside);
+ bt_assert(inside.prev == &nodes[MAX_NUM/2-1]);
+ bt_assert(inside.next == &nodes[MAX_NUM/2+1]);
+
+ replace_node(&nodes[MAX_NUM-1], &tail);
+ bt_assert(l.tail == &tail);
+ bt_assert(tail.prev == &nodes[MAX_NUM-2]);
+ bt_assert(tail.next == NODE &l.null);
+ bt_assert(nodes[MAX_NUM-2].next == &tail);
+
+ return 1;
+}
+
+static int
+t_add_tail_list(void)
+{
+ node nodes2[MAX_NUM];
+ list l2;
+
+ init_list__(&l, (node *) nodes);
+ fill_list2(&l, (node *) nodes);
+
+ init_list__(&l2, (node *) nodes2);
+ fill_list2(&l2, (node *) nodes2);
+
+ add_tail_list(&l, &l2);
+
+ bt_assert(nodes[MAX_NUM-1].next == &nodes2[0]);
+ bt_assert(nodes2[0].prev == &nodes[MAX_NUM-1]);
+ bt_assert(l.tail == &nodes2[MAX_NUM-1]);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_add_tail, "Adding nodes to tail of list");
+ bt_test_suite(t_add_head, "Adding nodes to head of list");
+ bt_test_suite(t_insert_node, "Inserting nodes to list");
+ bt_test_suite(t_remove_node, "Removing nodes from list");
+ bt_test_suite(t_replace_node, "Replacing nodes in list");
+ bt_test_suite(t_add_tail_list, "At the tail of a list adding the another list");
+
+ return bt_exit_value();
+}
diff --git a/lib/mac_test.c b/lib/mac_test.c
new file mode 100644
index 00000000..806fe3e4
--- /dev/null
+++ b/lib/mac_test.c
@@ -0,0 +1,1159 @@
+/*
+ * BIRD Library -- SHA and HMAC-SHA functions tests
+ *
+ * (c) 2015 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"
+
+#include "lib/mac.h"
+
+
+#define define_test_hash_fn(name,id) \
+static int \
+test_##name(void *out_, const void *in_, const void *expected_out_) \
+{ \
+ char *out = out_; \
+ const char *in = in_; \
+ const char *expected_out = expected_out_; \
+ \
+ struct mac_context ctx; \
+ mac_init(&ctx, id, NULL, 0); \
+ mac_update(&ctx, in, strlen(in)); \
+ byte *out_bin = mac_final(&ctx); \
+ \
+ uint len = mac_type_length(id); \
+ bt_bytes_to_hex(out, out_bin, len); \
+ \
+ return strncmp(out, expected_out, 2*len+1) == 0; \
+}
+
+define_test_hash_fn(md5, ALG_MD5)
+define_test_hash_fn(sha1, ALG_SHA1)
+define_test_hash_fn(sha224, ALG_SHA224)
+define_test_hash_fn(sha256, ALG_SHA256)
+define_test_hash_fn(sha384, ALG_SHA384)
+define_test_hash_fn(sha512, ALG_SHA512)
+
+
+static int
+t_md5(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "",
+ .out = "d41d8cd98f00b204e9800998ecf8427e",
+ },
+ {
+ .in = "a",
+ .out = "0cc175b9c0f1b6a831c399e269772661",
+ },
+ {
+ .in = "abc",
+ .out = "900150983cd24fb0d6963f7d28e17f72",
+ },
+ {
+ .in = "message digest",
+ .out = "f96b697d7cb7938d525a2f31aaf161d0",
+ },
+ {
+ .in = "abcdefghijklmnopqrstuvwxyz",
+ .out = "c3fcd3d76192e4007dfb496cca67e13b",
+ },
+ {
+ .in = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ .out = "d174ab98d277d9f5a5611c2c9f419d9f",
+ },
+ {
+ .in = "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ .out = "57edf4a22be3c955ac49da2e2107b67a",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_md5, bt_fmt_str, bt_fmt_str);
+}
+
+
+/*
+ * Testing SHAxxx functions
+ */
+
+
+static int
+t_sha1(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "",
+ .out = "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ },
+ {
+ .in = "a",
+ .out = "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
+ },
+ {
+ .in = "abc",
+ .out = "a9993e364706816aba3e25717850c26c9cd0d89d",
+ },
+ {
+ .in = "message digest",
+ .out = "c12252ceda8be8994d5fa0290a47231c1d16aae3",
+ },
+ {
+ .in = "abcdefghijklmnopqrstuvwxyz",
+ .out = "32d10c7b8cf96570ca04ce37f2a19d84240d3a89",
+ },
+ {
+ .in = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ .out = "761c457bf73b14d27e9e9265c46f4b4dda11f940",
+ },
+ {
+ .in = "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ .out = "50abf5706a150990a08b2c5ea40fa0e585554732",
+ },
+ {
+ .in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .out = "6a64fcc1fb970f7339ce886601775d2efea5cd4b",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha1, bt_fmt_str, bt_fmt_str);
+}
+
+static int
+t_sha224(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "",
+ .out = "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
+ },
+ {
+ .in = "a",
+ .out = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5",
+ },
+ {
+ .in = "abc",
+ .out = "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7",
+ },
+ {
+ .in = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ .out = "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525",
+ },
+ {
+ .in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .out = "cca7dd1a332a17775d8b0429bdb45055c2d4368ebaab0c7cf385586e",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha224, bt_fmt_str, bt_fmt_str);
+}
+
+static int
+t_sha256(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "",
+ .out = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ },
+ {
+ .in = "a",
+ .out = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
+ },
+ {
+ .in = "abc",
+ .out = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+ },
+ {
+ .in = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ .out = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1",
+ },
+ {
+ .in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .out = "bf18b43b61652b5d73f41ebf3d72e5e43aebf5076f497dde31ea3de9de4998ef",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha256, bt_fmt_str, bt_fmt_str);
+}
+
+static int
+t_sha384(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "",
+ .out = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
+ },
+ {
+ .in = "a",
+ .out = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31",
+ },
+ {
+ .in = "abc",
+ .out = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7",
+ },
+ {
+ .in = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ .out = "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b",
+ },
+ {
+ .in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .out = "6452928a62ca915a60f2d16ea22cc832d8ecb35443d78a3ff6986e7def9174a1dc16ce2ff65d3ed1666db98357f3c05e",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha384, bt_fmt_str, bt_fmt_str);
+}
+
+static int
+t_sha512(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = "",
+ .out = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
+ },
+ {
+ .in = "a",
+ .out = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75",
+ },
+ {
+ .in = "abc",
+ .out = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+ },
+ {
+ .in = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ .out = "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445",
+ },
+ {
+ .in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ .out = "415509a1c345371acb3e27a88b3835e3b6dfebcbbab5134850596f4db64d7bb22ac42c3cd179446a80c92b8be955460eb536eac01389a7e1fdf09d1dca83922f",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha512, bt_fmt_str, bt_fmt_str);
+}
+
+
+/*
+ * Testing SHAxxx HMAC functions
+ */
+
+#define HMAC_BUFFER_SIZE 160
+struct hmac_data_in {
+ byte key[HMAC_BUFFER_SIZE];
+ uint key_len;
+ byte data[HMAC_BUFFER_SIZE];
+ uint data_len;
+};
+
+static void
+hmac_in_fmt(char *buf, size_t size, const void *data_)
+{
+ uint i;
+ const struct hmac_data_in *data = data_;
+
+ snprintf(buf, size, "data: '");
+ for (i = 0; i < data->data_len; i++)
+ snprintf(buf+strlen(buf), size-strlen(buf), bt_is_char(data->data[i]) ? "%c" : " 0x%02x", data->data[i]);
+
+ snprintf(buf+strlen(buf), size-strlen(buf), "', key: '");
+ for (i = 0; i < data->key_len; i++)
+ snprintf(buf+strlen(buf), size-strlen(buf), bt_is_char(data->key[i]) ? "%c" : " 0x%02x", data->key[i]);
+ snprintf(buf+strlen(buf), size-strlen(buf), "'");
+}
+
+#define define_test_hmac_fn(name,id) \
+static int \
+test_##name##_hmac(void *out_, const void *in_, const void *expected_out_) \
+{ \
+ char *out = out_; \
+ const struct hmac_data_in *in = in_; \
+ const char *expected_out = expected_out_; \
+ \
+ struct mac_context ctx; \
+ mac_init(&ctx, id, in->key, in->key_len); \
+ mac_update(&ctx, in->data, in->data_len); \
+ byte *out_bin = mac_final(&ctx); \
+ \
+ uint len = mac_type_length(id); \
+ bt_bytes_to_hex(out, out_bin, len); \
+ \
+ return strncmp(out, expected_out, 2*len+1) == 0; \
+}
+
+define_test_hmac_fn(md5, ALG_HMAC_MD5)
+define_test_hmac_fn(sha1, ALG_HMAC_SHA1)
+define_test_hmac_fn(sha224, ALG_HMAC_SHA224)
+define_test_hmac_fn(sha256, ALG_HMAC_SHA256)
+define_test_hmac_fn(sha384, ALG_HMAC_SHA384)
+define_test_hmac_fn(sha512, ALG_HMAC_SHA512)
+
+
+static int
+t_md5_hmac(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ },
+ .key_len = 16,
+ .data = "Hi There",
+ .data_len = 8,
+ },
+ .out = "9294727a3638bb1c13f48ef8158bfc9d",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = "Jefe",
+ .key_len = 4,
+ .data = "what do ya want for nothing?",
+ .data_len = 28,
+ },
+ .out = "750c783e6ab0b503eaa86e310a5db738",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 16,
+ .data = {
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ },
+ .data_len = 50,
+ },
+ .out = "56be34521d144c88dbb8c733f0e8b3f6",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ },
+ .key_len = 25,
+ .data = {
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ },
+ .data_len = 50,
+ },
+ .out = "697eaf0aca3a3aea3a75164746ffaa79",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ },
+ .key_len = 16,
+ .data = "Test With Truncation",
+ .data_len = 20,
+ },
+ .out = "56461ef2342edc00f9bab995690efd4c",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 80,
+ .data = "Test Using Larger Than Block-Size Key - Hash Key First",
+ .data_len = 54,
+ },
+ .out = "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 80,
+ .data = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ .data_len = 73,
+ },
+ .out = "6f630fad67cda0ee1fb1f562db3aa53e",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_md5_hmac, hmac_in_fmt, bt_fmt_str);
+}
+
+static int
+t_sha1_hmac(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ },
+ .key_len = 20,
+ .data = "Hi There",
+ .data_len = 8,
+ },
+ .out = "b617318655057264e28bc0b6fb378c8ef146be00",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = "Jefe",
+ .key_len = 4,
+ .data = "what do ya want for nothing?",
+ .data_len = 28,
+ },
+ .out = "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 20,
+ .data = {
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ },
+ .data_len = 50,
+ },
+ .out = "125d7342b9ac11cd91a39af48aa17b4f63f175d3",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ },
+ .key_len = 25,
+ .data = {
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ },
+ .data_len = 50,
+ },
+ .out = "4c9007f4026250c6bc8414f9bf50c86c2d7235da",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ },
+ .key_len = 20,
+ .data = "Test With Truncation",
+ .data_len = 20,
+ },
+ .out = "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 80,
+ .data = "Test Using Larger Than Block-Size Key - Hash Key First",
+ .data_len = 54,
+ },
+ .out = "aa4ae5e15272d00e95705637ce8a3b55ed402112",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 80,
+ .data = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ .data_len = 73,
+ },
+ .out = "e8e99d0f45237d786d6bbaa7965c7808bbff1a91",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61,
+ },
+ .key_len = 64,
+ .data = "Test Using key 64 bytes sized",
+ .data_len = 29,
+ },
+ .out = "a55d4fb80962a6b3d2e720705314bee417d68cf6",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha1_hmac, hmac_in_fmt, bt_fmt_str);
+}
+
+static int
+t_sha224_hmac(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ },
+ .key_len = 20,
+ .data = "Hi There",
+ .data_len = 8,
+ },
+ .out = "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = "Jefe",
+ .key_len = 4,
+ .data = "what do ya want for nothing?",
+ .data_len = 28,
+ },
+ .out = "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 20,
+ .data = {
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ },
+ .data_len = 50,
+ },
+ .out = "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ },
+ .key_len = 25,
+ .data = {
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ },
+ .data_len = 50,
+ },
+ .out = "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ },
+ .key_len = 20,
+ .data = "Test With Truncation",
+ .data_len = 20,
+ },
+ .out = "0e2aea68a90c8d37c988bcdb9fca6fa8099cd857c7ec4a1815cac54c",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa,
+ },
+ .key_len = 131,
+ .data = "Test Using Larger Than Block-Size Key - Hash Key First",
+ .data_len = 54,
+ },
+ .out = "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa,
+ },
+ .key_len = 131,
+ .data = "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+ .data_len = 152,
+ },
+ .out = "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha224_hmac, hmac_in_fmt, bt_fmt_str);
+}
+
+static int
+t_sha256_hmac(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ },
+ .key_len = 20,
+ .data = "Hi There",
+ .data_len = 8,
+ },
+ .out = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = "Jefe",
+ .key_len = 4,
+ .data = "what do ya want for nothing?",
+ .data_len = 28,
+ },
+ .out = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 20,
+ .data = {
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ },
+ .data_len = 50,
+ },
+ .out = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ },
+ .key_len = 25,
+ .data = {
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ },
+ .data_len = 50,
+ },
+ .out = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ },
+ .key_len = 20,
+ .data = "Test With Truncation",
+ .data_len = 20,
+ },
+ .out = "a3b6167473100ee06e0c796c2955552bfa6f7c0a6a8aef8b93f860aab0cd20c5",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa,
+ },
+ .key_len = 131,
+ .data = "Test Using Larger Than Block-Size Key - Hash Key First",
+ .data_len = 54,
+ },
+ .out = "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa,
+ },
+ .key_len = 131,
+ .data = "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+ .data_len = 152,
+ },
+ .out = "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha256_hmac, hmac_in_fmt, bt_fmt_str);
+}
+
+static int
+t_sha384_hmac(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ },
+ .key_len = 20,
+ .data = "Hi There",
+ .data_len = 8,
+ },
+ .out = "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = "Jefe",
+ .key_len = 4,
+ .data = "what do ya want for nothing?",
+ .data_len = 28,
+ },
+ .out = "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 20,
+ .data = {
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ },
+ .data_len = 50,
+ },
+ .out = "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ },
+ .key_len = 25,
+ .data = {
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ },
+ .data_len = 50,
+ },
+ .out = "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ },
+ .key_len = 20,
+ .data = "Test With Truncation",
+ .data_len = 20,
+ },
+ .out = "3abf34c3503b2a23a46efc619baef897f4c8e42c934ce55ccbae9740fcbc1af4ca62269e2a37cd88ba926341efe4aeea",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa,
+ },
+ .key_len = 131,
+ .data = "Test Using Larger Than Block-Size Key - Hash Key First",
+ .data_len = 54,
+ },
+ .out = "4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa,
+ },
+ .key_len = 131,
+ .data = "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+ .data_len = 152,
+ },
+ .out = "6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha384_hmac, hmac_in_fmt, bt_fmt_str);
+}
+
+static int
+t_sha512_hmac(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ },
+ .key_len = 20,
+ .data = "Hi There",
+ .data_len = 8,
+ },
+ .out = "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = "Jefe",
+ .key_len = 4,
+ .data = "what do ya want for nothing?",
+ .data_len = 28,
+ },
+ .out = "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ },
+ .key_len = 20,
+ .data = {
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ },
+ .data_len = 50,
+ },
+ .out = "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19,
+ },
+ .key_len = 25,
+ .data = {
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ },
+ .data_len = 50,
+ },
+ .out = "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ },
+ .key_len = 20,
+ .data = "Test With Truncation",
+ .data_len = 20,
+ },
+ .out = "415fad6271580a531d4179bc891d87a650188707922a4fbb36663a1eb16da008711c5b50ddd0fc235084eb9d3364a1454fb2ef67cd1d29fe6773068ea266e96b",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa,
+ },
+ .key_len = 131,
+ .data = "Test Using Larger Than Block-Size Key - Hash Key First",
+ .data_len = 54,
+ },
+ .out = "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598",
+ },
+ {
+ .in = & (struct hmac_data_in) {
+ .key = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa,
+ },
+ .key_len = 131,
+ .data = "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+ .data_len = 152,
+ },
+ .out = "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58",
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_sha512_hmac, hmac_in_fmt, bt_fmt_str);
+}
+
+
+/*
+ * Testing SHAxxx concating independence
+ */
+
+#include "lib/sha256.h"
+#include "lib/sha512.h"
+
+static int
+t_sha256_concating(void)
+{
+ char hash_a[SHA256_HEX_SIZE];
+ char hash_b[SHA256_HEX_SIZE];
+
+ char *str_a = "a" "bb" "ccc" "dddd" "eeeee" "ffffff";
+ char *str_b1 = "a" ;
+ char *str_b2 = "bb" ;
+ char *str_b3 = "ccc" ;
+ char *str_b4 = "dddd" ;
+ char *str_b5 = "eeeee" ;
+ char *str_b6 = "ffffff";
+
+ struct hash_context ctx_a;
+ sha256_init(&ctx_a);
+ sha256_update(&ctx_a, str_a, strlen(str_a));
+ byte *hash_a_ = sha256_final(&ctx_a);
+ bt_bytes_to_hex(hash_a, hash_a_, SHA256_SIZE);
+
+ struct hash_context ctx_b;
+ sha256_init(&ctx_b);
+ sha256_update(&ctx_b, str_b1, strlen(str_b1));
+ sha256_update(&ctx_b, str_b2, strlen(str_b2));
+ sha256_update(&ctx_b, str_b3, strlen(str_b3));
+ sha256_update(&ctx_b, str_b4, strlen(str_b4));
+ sha256_update(&ctx_b, str_b5, strlen(str_b5));
+ sha256_update(&ctx_b, str_b6, strlen(str_b6));
+ byte *hash_b_ = sha256_final(&ctx_b);
+ bt_bytes_to_hex(hash_b, hash_b_, SHA256_SIZE);
+
+ int are_hash_a_b_equal = (strncmp(hash_a, hash_b, sizeof(hash_a)) == 0);
+ bt_assert_msg(are_hash_a_b_equal, "Hashes A: %s, B: %s should be same", hash_a, hash_b);
+
+ return 1;
+}
+
+
+static int
+t_sha512_concating(void)
+{
+ char hash_a[SHA512_HEX_SIZE];
+ char hash_b[SHA512_HEX_SIZE];
+
+ char *str_a = "a" "bb" "ccc" "dddd" "eeeee" "ffffff";
+ char *str_b1 = "a" ;
+ char *str_b2 = "bb" ;
+ char *str_b3 = "ccc" ;
+ char *str_b4 = "dddd" ;
+ char *str_b5 = "eeeee" ;
+ char *str_b6 = "ffffff";
+
+ struct hash_context ctx_a;
+ sha512_init(&ctx_a);
+ sha512_update(&ctx_a, str_a, strlen(str_a));
+ byte *hash_a_ = sha512_final(&ctx_a);
+ bt_bytes_to_hex(hash_a, hash_a_, SHA512_SIZE);
+
+ struct hash_context ctx_b;
+ sha512_init(&ctx_b);
+ sha512_update(&ctx_b, str_b1, strlen(str_b1));
+ sha512_update(&ctx_b, str_b2, strlen(str_b2));
+ sha512_update(&ctx_b, str_b3, strlen(str_b3));
+ sha512_update(&ctx_b, str_b4, strlen(str_b4));
+ sha512_update(&ctx_b, str_b5, strlen(str_b5));
+ sha512_update(&ctx_b, str_b6, strlen(str_b6));
+ byte *hash_b_ = sha512_final(&ctx_b);
+ bt_bytes_to_hex(hash_b, hash_b_, SHA512_SIZE);
+
+ int are_hash_a_b_equal = (strncmp(hash_a, hash_b, sizeof(hash_a)) == 0);
+ bt_assert_msg(are_hash_a_b_equal, "Hashes A: %s, B: %s should be same", hash_a, hash_b);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_md5, "Testing MD5 by RFC 1321");
+ bt_test_suite(t_sha1, "Testing SHA-1");
+ bt_test_suite(t_sha224, "Testing SHA-224");
+ bt_test_suite(t_sha256, "Testing SHA-256");
+ bt_test_suite(t_sha384, "Testing SHA-384");
+ bt_test_suite(t_sha512, "Testing SHA-512");
+
+ bt_test_suite(t_md5_hmac, "Testing HMAC-MD5 by RFC 2202");
+ bt_test_suite(t_sha1_hmac, "Testing HMAC-SHA-1 by RFC 2202");
+ bt_test_suite(t_sha224_hmac, "Testing HMAC-SHA-224 by RFC 4231");
+ bt_test_suite(t_sha256_hmac, "Testing HMAC-SHA-256 by RFC 4231");
+ bt_test_suite(t_sha384_hmac, "Testing HMAC-SHA-384 by RFC 4231");
+ bt_test_suite(t_sha512_hmac, "Testing HMAC-SHA-512 by RFC 4231");
+
+ bt_test_suite(t_sha256_concating, "Testing concatenation input string to hash using sha256_update");
+ bt_test_suite(t_sha512_concating, "Testing concatenation input string to hash using sha512_update");
+
+ return bt_exit_value();
+}
diff --git a/lib/mempool.c b/lib/mempool.c
index a8281041..758882ce 100644
--- a/lib/mempool.c
+++ b/lib/mempool.c
@@ -11,7 +11,7 @@
*
* Linear memory pools are collections of memory blocks which
* support very fast allocation of new blocks, but are able to free only
- * the whole collection at once.
+ * the whole collection at once (or in stack order).
*
* Example: Each configuration is described by a complex system of structures,
* linked lists and function trees which are all allocated from a single linear
@@ -32,10 +32,12 @@ struct lp_chunk {
byte data[0];
};
+const int lp_chunk_size = sizeof(struct lp_chunk);
+
struct linpool {
resource r;
byte *ptr, *end;
- struct lp_chunk *first, *current, **plast; /* Normal (reusable) chunks */
+ struct lp_chunk *first, *current; /* Normal (reusable) chunks */
struct lp_chunk *first_large; /* Large chunks */
uint chunk_size, threshold, total, total_large;
};
@@ -67,7 +69,6 @@ linpool
*lp_new(pool *p, uint blk)
{
linpool *m = ralloc(p, &lp_class);
- m->plast = &m->first;
m->chunk_size = blk;
m->threshold = 3*blk/4;
return m;
@@ -112,22 +113,25 @@ lp_alloc(linpool *m, uint size)
}
else
{
- if (m->current)
+ if (m->current && m->current->next)
{
/* Still have free chunks from previous incarnation (before lp_flush()) */
- c = m->current;
- m->current = c->next;
+ c = m->current->next;
}
else
{
/* Need to allocate a new chunk */
c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
m->total += m->chunk_size;
- *m->plast = c;
- m->plast = &c->next;
c->next = NULL;
c->size = m->chunk_size;
+
+ if (m->current)
+ m->current->next = c;
+ else
+ m->first = c;
}
+ m->current = c;
m->ptr = c->data + size;
m->end = c->data + m->chunk_size;
}
@@ -188,9 +192,11 @@ lp_flush(linpool *m)
{
struct lp_chunk *c;
- /* Relink all normal chunks to free list and free all large chunks */
- m->ptr = m->end = NULL;
- m->current = m->first;
+ /* Move ptr to the first chunk and free all large chunks */
+ m->current = c = m->first;
+ m->ptr = c ? c->data : NULL;
+ m->end = c ? c->data + m->chunk_size : NULL;
+
while (c = m->first_large)
{
m->first_large = c->next;
@@ -199,6 +205,50 @@ lp_flush(linpool *m)
m->total_large = 0;
}
+/**
+ * lp_save - save the state of a linear memory pool
+ * @m: linear memory pool
+ * @p: state buffer
+ *
+ * This function saves the state of a linear memory pool. Saved state can be
+ * used later to restore the pool (to free memory allocated since).
+ */
+void
+lp_save(linpool *m, lp_state *p)
+{
+ p->current = m->current;
+ p->large = m->first_large;
+ p->ptr = m->ptr;
+}
+
+/**
+ * lp_restore - restore the state of a linear memory pool
+ * @m: linear memory pool
+ * @p: saved state
+ *
+ * This function restores the state of a linear memory pool, freeing all memory
+ * allocated since the state was saved. Note that the function cannot un-free
+ * the memory, therefore the function also invalidates other states that were
+ * saved between (on the same pool).
+ */
+void
+lp_restore(linpool *m, lp_state *p)
+{
+ struct lp_chunk *c;
+
+ /* Move ptr to the saved pos and free all newer large chunks */
+ m->current = c = p->current;
+ m->ptr = p->ptr;
+ m->end = c ? c->data + m->chunk_size : NULL;
+
+ while ((c = m->first_large) && (c != p->large))
+ {
+ m->first_large = c->next;
+ m->total_large -= c->size;
+ xfree(c);
+ }
+}
+
static void
lp_free(resource *r)
{
diff --git a/lib/net.c b/lib/net.c
new file mode 100644
index 00000000..9335b78f
--- /dev/null
+++ b/lib/net.c
@@ -0,0 +1,308 @@
+
+#include "nest/bird.h"
+#include "lib/ip.h"
+#include "lib/net.h"
+#include "lib/flowspec.h"
+
+
+const char * const net_label[] = {
+ [NET_IP4] = "ipv4",
+ [NET_IP6] = "ipv6",
+ [NET_VPN4] = "vpn4",
+ [NET_VPN6] = "vpn6",
+ [NET_ROA4] = "roa4",
+ [NET_ROA6] = "roa6",
+ [NET_FLOW4] = "flow4",
+ [NET_FLOW6] = "flow6",
+ [NET_MPLS] = "mpls",
+};
+
+const u16 net_addr_length[] = {
+ [NET_IP4] = sizeof(net_addr_ip4),
+ [NET_IP6] = sizeof(net_addr_ip6),
+ [NET_VPN4] = sizeof(net_addr_vpn4),
+ [NET_VPN6] = sizeof(net_addr_vpn6),
+ [NET_ROA4] = sizeof(net_addr_roa4),
+ [NET_ROA6] = sizeof(net_addr_roa6),
+ [NET_FLOW4] = 0,
+ [NET_FLOW6] = 0,
+ [NET_MPLS] = sizeof(net_addr_mpls),
+};
+
+const u8 net_max_prefix_length[] = {
+ [NET_IP4] = IP4_MAX_PREFIX_LENGTH,
+ [NET_IP6] = IP6_MAX_PREFIX_LENGTH,
+ [NET_VPN4] = IP4_MAX_PREFIX_LENGTH,
+ [NET_VPN6] = IP6_MAX_PREFIX_LENGTH,
+ [NET_ROA4] = IP4_MAX_PREFIX_LENGTH,
+ [NET_ROA6] = IP6_MAX_PREFIX_LENGTH,
+ [NET_FLOW4] = IP4_MAX_PREFIX_LENGTH,
+ [NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
+ [NET_MPLS] = 0,
+};
+
+const u16 net_max_text_length[] = {
+ [NET_IP4] = 18, /* "255.255.255.255/32" */
+ [NET_IP6] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
+ [NET_VPN4] = 40, /* "4294967296:4294967296 255.255.255.255/32" */
+ [NET_VPN6] = 65, /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
+ [NET_ROA4] = 34, /* "255.255.255.255/32-32 AS4294967295" */
+ [NET_ROA6] = 60, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128-128 AS4294967295" */
+ [NET_FLOW4] = 0, /* "flow4 { ... }" */
+ [NET_FLOW6] = 0, /* "flow6 { ... }" */
+ [NET_MPLS] = 7, /* "1048575" */
+};
+
+
+int
+rd_format(const u64 rd, char *buf, int buflen)
+{
+ switch (rd >> 48)
+ {
+ case 0: return bsnprintf(buf, buflen, "%u:%u", (u32) (rd >> 32), (u32) rd);
+ case 1: return bsnprintf(buf, buflen, "%I4:%u", ip4_from_u32(rd >> 16), (u32) (rd & 0xffff));
+ case 2: if (((u32) (rd >> 16)) >> 16)
+ return bsnprintf(buf, buflen, "%u:%u", (u32) (rd >> 16), (u32) (rd & 0xffff));
+ else
+ return bsnprintf(buf, buflen, "2:%u:%u", (u32) (rd >> 16), (u32) (rd & 0xffff));
+ default: return bsnprintf(buf, buflen, "X:%08x:%08x", (u32) (rd >> 32), (u32) rd);
+ }
+}
+
+int
+net_format(const net_addr *N, char *buf, int buflen)
+{
+ net_addr_union *n = (void *) N;
+ buf[0] = 0;
+
+ switch (n->n.type)
+ {
+ case NET_IP4:
+ return bsnprintf(buf, buflen, "%I4/%d", n->ip4.prefix, n->ip4.pxlen);
+ case NET_IP6:
+ return bsnprintf(buf, buflen, "%I6/%d", n->ip6.prefix, n->ip6.pxlen);
+ case NET_VPN4:
+ {
+ int c = rd_format(n->vpn4.rd, buf, buflen);
+ ADVANCE(buf, buflen, c);
+ return bsnprintf(buf, buflen, " %I4/%d", n->vpn4.prefix, n->vpn4.pxlen);
+ }
+ case NET_VPN6:
+ {
+ /* XXX: RD format is specified for VPN4; not found any for VPN6, reusing the same as for VPN4 */
+ int c = rd_format(n->vpn6.rd, buf, buflen);
+ ADVANCE(buf, buflen, c);
+ return bsnprintf(buf, buflen, " %I6/%d", n->vpn6.prefix, n->vpn6.pxlen);
+ }
+ case NET_ROA4:
+ return bsnprintf(buf, buflen, "%I4/%u-%u AS%u", n->roa4.prefix, n->roa4.pxlen, n->roa4.max_pxlen, n->roa4.asn);
+ case NET_ROA6:
+ return bsnprintf(buf, buflen, "%I6/%u-%u AS%u", n->roa6.prefix, n->roa6.pxlen, n->roa6.max_pxlen, n->roa6.asn);
+ case NET_FLOW4:
+ return flow4_net_format(buf, buflen, &n->flow4);
+ case NET_FLOW6:
+ return flow6_net_format(buf, buflen, &n->flow6);
+ case NET_MPLS:
+ return bsnprintf(buf, buflen, "%u", n->mpls.label);
+ }
+
+ bug("unknown network type");
+}
+
+ip_addr
+net_pxmask(const net_addr *a)
+{
+ switch (a->type)
+ {
+ case NET_IP4:
+ case NET_VPN4:
+ case NET_ROA4:
+ case NET_FLOW4:
+ return ipa_from_ip4(ip4_mkmask(net4_pxlen(a)));
+
+ case NET_IP6:
+ case NET_VPN6:
+ case NET_ROA6:
+ case NET_FLOW6:
+ return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
+
+ case NET_MPLS:
+ default:
+ return IPA_NONE;
+ }
+}
+
+int
+net_compare(const net_addr *a, const net_addr *b)
+{
+ if (a->type != b->type)
+ return uint_cmp(a->type, b->type);
+
+ switch (a->type)
+ {
+ case NET_IP4:
+ return net_compare_ip4((const net_addr_ip4 *) a, (const net_addr_ip4 *) b);
+ case NET_IP6:
+ return net_compare_ip6((const net_addr_ip6 *) a, (const net_addr_ip6 *) b);
+ case NET_VPN4:
+ return net_compare_vpn4((const net_addr_vpn4 *) a, (const net_addr_vpn4 *) b);
+ case NET_VPN6:
+ return net_compare_vpn6((const net_addr_vpn6 *) a, (const net_addr_vpn6 *) b);
+ case NET_ROA4:
+ return net_compare_roa4((const net_addr_roa4 *) a, (const net_addr_roa4 *) b);
+ case NET_ROA6:
+ return net_compare_roa6((const net_addr_roa6 *) a, (const net_addr_roa6 *) b);
+ case NET_FLOW4:
+ return net_compare_flow4((const net_addr_flow4 *) a, (const net_addr_flow4 *) b);
+ case NET_FLOW6:
+ return net_compare_flow6((const net_addr_flow6 *) a, (const net_addr_flow6 *) b);
+ case NET_MPLS:
+ return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
+ }
+ return 0;
+}
+
+#define NET_HASH(a,t) net_hash_##t((const net_addr_##t *) a)
+
+u32
+net_hash(const net_addr *n)
+{
+ switch (n->type)
+ {
+ case NET_IP4: return NET_HASH(n, ip4);
+ case NET_IP6: return NET_HASH(n, ip6);
+ case NET_VPN4: return NET_HASH(n, vpn4);
+ case NET_VPN6: return NET_HASH(n, vpn6);
+ case NET_ROA4: return NET_HASH(n, roa4);
+ case NET_ROA6: return NET_HASH(n, roa6);
+ case NET_FLOW4: return NET_HASH(n, flow4);
+ case NET_FLOW6: return NET_HASH(n, flow6);
+ case NET_MPLS: return NET_HASH(n, mpls);
+ default: bug("invalid type");
+ }
+}
+
+
+#define NET_VALIDATE(a,t) net_validate_##t((const net_addr_##t *) a)
+
+int
+net_validate(const net_addr *n)
+{
+ switch (n->type)
+ {
+ case NET_IP4: return NET_VALIDATE(n, ip4);
+ case NET_IP6: return NET_VALIDATE(n, ip6);
+ case NET_VPN4: return NET_VALIDATE(n, vpn4);
+ case NET_VPN6: return NET_VALIDATE(n, vpn6);
+ case NET_ROA4: return NET_VALIDATE(n, roa4);
+ case NET_ROA6: return NET_VALIDATE(n, roa6);
+ case NET_FLOW4: return NET_VALIDATE(n, flow4);
+ case NET_FLOW6: return NET_VALIDATE(n, flow6);
+ case NET_MPLS: return NET_VALIDATE(n, mpls);
+ default: return 0;
+ }
+}
+
+void
+net_normalize(net_addr *N)
+{
+ net_addr_union *n = (void *) N;
+
+ switch (n->n.type)
+ {
+ case NET_IP4:
+ case NET_VPN4:
+ case NET_ROA4:
+ case NET_FLOW4:
+ return net_normalize_ip4(&n->ip4);
+
+ case NET_IP6:
+ case NET_VPN6:
+ case NET_ROA6:
+ case NET_FLOW6:
+ return net_normalize_ip6(&n->ip6);
+
+ case NET_MPLS:
+ return;
+ }
+}
+
+int
+net_classify(const net_addr *N)
+{
+ net_addr_union *n = (void *) N;
+
+ switch (n->n.type)
+ {
+ case NET_IP4:
+ case NET_VPN4:
+ case NET_ROA4:
+ case NET_FLOW4:
+ return ip4_zero(n->ip4.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip4_classify(n->ip4.prefix);
+
+ case NET_IP6:
+ case NET_VPN6:
+ case NET_ROA6:
+ case NET_FLOW6:
+ return ip6_zero(n->ip6.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6.prefix);
+
+ case NET_MPLS:
+ return IADDR_HOST | SCOPE_UNIVERSE;
+ }
+
+ return IADDR_INVALID;
+}
+
+int
+ipa_in_netX(const ip_addr a, const net_addr *n)
+{
+ switch (n->type)
+ {
+ case NET_IP4:
+ case NET_VPN4:
+ case NET_ROA4:
+ case NET_FLOW4:
+ if (!ipa_is_ip4(a)) return 0;
+ return ip4_zero(ip4_and(ip4_xor(ipa_to_ip4(a), net4_prefix(n)),
+ ip4_mkmask(net4_pxlen(n))));
+
+ case NET_IP6:
+ case NET_VPN6:
+ case NET_ROA6:
+ case NET_FLOW6:
+ if (ipa_is_ip4(a)) return 0;
+ return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)),
+ ip6_mkmask(net6_pxlen(n))));
+
+ case NET_MPLS:
+ default:
+ return 0;
+ }
+}
+
+int
+net_in_netX(const net_addr *a, const net_addr *n)
+{
+ if (a->type != n->type)
+ return 0;
+
+ return (net_pxlen(n) <= net_pxlen(a)) && ipa_in_netX(net_prefix(a), n);
+}
+
+#define CHECK_NET(T,S) \
+ ({ if (sizeof(T) != S) die("sizeof %s is %d/%d", #T, (int) sizeof(T), S); })
+
+void
+net_init(void)
+{
+ CHECK_NET(net_addr, 24);
+ CHECK_NET(net_addr_ip4, 8);
+ CHECK_NET(net_addr_ip6, 20);
+ CHECK_NET(net_addr_vpn4, 16);
+ CHECK_NET(net_addr_vpn6, 32);
+ CHECK_NET(net_addr_roa4, 16);
+ CHECK_NET(net_addr_roa6, 28);
+ CHECK_NET(net_addr_flow4, 8);
+ CHECK_NET(net_addr_flow6, 20);
+ CHECK_NET(net_addr_mpls, 8);
+}
diff --git a/lib/net.h b/lib/net.h
new file mode 100644
index 00000000..69f00641
--- /dev/null
+++ b/lib/net.h
@@ -0,0 +1,550 @@
+/*
+ * BIRD Internet Routing Daemon -- Network addresses
+ *
+ * (c) 2015 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_NET_H_
+#define _BIRD_NET_H_
+
+#include "lib/ip.h"
+
+
+#define NET_IP4 1
+#define NET_IP6 2
+#define NET_VPN4 3
+#define NET_VPN6 4
+#define NET_ROA4 5
+#define NET_ROA6 6
+#define NET_FLOW4 7
+#define NET_FLOW6 8
+#define NET_MPLS 9
+#define NET_MAX 10
+
+#define NB_IP4 (1 << NET_IP4)
+#define NB_IP6 (1 << NET_IP6)
+#define NB_VPN4 (1 << NET_VPN4)
+#define NB_VPN6 (1 << NET_VPN6)
+#define NB_ROA4 (1 << NET_ROA4)
+#define NB_ROA6 (1 << NET_ROA6)
+#define NB_FLOW4 (1 << NET_FLOW4)
+#define NB_FLOW6 (1 << NET_FLOW6)
+#define NB_MPLS (1 << NET_MPLS)
+
+#define NB_IP (NB_IP4 | NB_IP6)
+#define NB_VPN (NB_VPN4 | NB_VPN6)
+#define NB_FLOW (NB_FLOW4 | NB_FLOW6)
+#define NB_DEST (NB_IP | NB_VPN | NB_MPLS)
+#define NB_ANY 0xffffffff
+
+
+typedef struct net_addr {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ u8 data[20];
+ u64 align[0];
+} net_addr;
+
+typedef struct net_addr_ip4 {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip4_addr prefix;
+} net_addr_ip4;
+
+typedef struct net_addr_ip6 {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip6_addr prefix;
+} net_addr_ip6;
+
+typedef struct net_addr_vpn4 {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip4_addr prefix;
+ u64 rd;
+} net_addr_vpn4;
+
+typedef struct net_addr_vpn6 {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip6_addr prefix;
+ u32 padding;
+ u64 rd;
+} net_addr_vpn6;
+
+typedef struct net_addr_roa4 {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip4_addr prefix;
+ u32 max_pxlen;
+ u32 asn;
+} net_addr_roa4;
+
+typedef struct net_addr_roa6 {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip6_addr prefix;
+ u32 max_pxlen;
+ u32 asn;
+} net_addr_roa6;
+
+typedef struct net_addr_flow4 {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip4_addr prefix;
+ byte data[0];
+} net_addr_flow4;
+
+typedef struct net_addr_flow6 {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ ip6_addr prefix;
+ byte data[0];
+} net_addr_flow6;
+
+typedef struct net_addr_mpls {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ u32 label;
+} net_addr_mpls;
+
+typedef union net_addr_union {
+ net_addr n;
+ net_addr_ip4 ip4;
+ net_addr_ip6 ip6;
+ net_addr_vpn4 vpn4;
+ net_addr_vpn6 vpn6;
+ net_addr_roa4 roa4;
+ net_addr_roa6 roa6;
+ net_addr_flow4 flow4;
+ net_addr_flow6 flow6;
+ net_addr_mpls mpls;
+} net_addr_union;
+
+
+extern const char * const net_label[];
+extern const u16 net_addr_length[];
+extern const u8 net_max_prefix_length[];
+extern const u16 net_max_text_length[];
+
+#define NET_MAX_TEXT_LENGTH 256
+
+
+#define NET_ADDR_IP4(prefix,pxlen) \
+ ((net_addr_ip4) { NET_IP4, pxlen, sizeof(net_addr_ip4), prefix })
+
+#define NET_ADDR_IP6(prefix,pxlen) \
+ ((net_addr_ip6) { NET_IP6, pxlen, sizeof(net_addr_ip6), prefix })
+
+#define NET_ADDR_VPN4(prefix,pxlen,rd) \
+ ((net_addr_vpn4) { NET_VPN4, pxlen, sizeof(net_addr_vpn4), prefix, rd })
+
+#define NET_ADDR_VPN6(prefix,pxlen,rd) \
+ ((net_addr_vpn6) { NET_VPN6, pxlen, sizeof(net_addr_vpn6), prefix, 0, rd })
+
+#define NET_ADDR_ROA4(prefix,pxlen,max_pxlen,asn) \
+ ((net_addr_roa4) { NET_ROA4, pxlen, sizeof(net_addr_roa4), prefix, max_pxlen, asn })
+
+#define NET_ADDR_ROA6(prefix,pxlen,max_pxlen,asn) \
+ ((net_addr_roa6) { NET_ROA6, pxlen, sizeof(net_addr_roa6), prefix, max_pxlen, asn })
+
+#define NET_ADDR_FLOW4(prefix,pxlen,dlen) \
+ ((net_addr_flow4) { NET_FLOW4, pxlen, sizeof(net_addr_ip4) + dlen, prefix })
+
+#define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
+ ((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix })
+
+#define NET_ADDR_MPLS(label) \
+ ((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
+
+
+static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
+{ *(net_addr_ip4 *)a = NET_ADDR_IP4(prefix, pxlen); }
+
+static inline void net_fill_ip6(net_addr *a, ip6_addr prefix, uint pxlen)
+{ *(net_addr_ip6 *)a = NET_ADDR_IP6(prefix, pxlen); }
+
+static inline void net_fill_vpn4(net_addr *a, ip4_addr prefix, uint pxlen, u64 rd)
+{ *(net_addr_vpn4 *)a = NET_ADDR_VPN4(prefix, pxlen, rd); }
+
+static inline void net_fill_vpn6(net_addr *a, ip6_addr prefix, uint pxlen, u64 rd)
+{ *(net_addr_vpn6 *)a = NET_ADDR_VPN6(prefix, pxlen, rd); }
+
+static inline void net_fill_roa4(net_addr *a, ip4_addr prefix, uint pxlen, uint max_pxlen, u32 asn)
+{ *(net_addr_roa4 *)a = NET_ADDR_ROA4(prefix, pxlen, max_pxlen, asn); }
+
+static inline void net_fill_roa6(net_addr *a, ip6_addr prefix, uint pxlen, uint max_pxlen, u32 asn)
+{ *(net_addr_roa6 *)a = NET_ADDR_ROA6(prefix, pxlen, max_pxlen, asn); }
+
+static inline void net_fill_mpls(net_addr *a, u32 label)
+{ *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
+
+static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
+{
+ if (ipa_is_ip4(prefix))
+ net_fill_ip4(a, ipa_to_ip4(prefix), pxlen);
+ else
+ net_fill_ip6(a, ipa_to_ip6(prefix), pxlen);
+}
+
+static inline void net_fill_ip_host(net_addr *a, ip_addr prefix)
+{
+ if (ipa_is_ip4(prefix))
+ net_fill_ip4(a, ipa_to_ip4(prefix), IP4_MAX_PREFIX_LENGTH);
+ else
+ net_fill_ip6(a, ipa_to_ip6(prefix), IP6_MAX_PREFIX_LENGTH);
+}
+
+static inline void net_fill_flow4(net_addr *a, ip4_addr prefix, uint pxlen, byte *data, uint dlen)
+{
+ net_addr_flow4 *f = (void *) a;
+ *f = NET_ADDR_FLOW4(prefix, pxlen, dlen);
+ memcpy(f->data, data, dlen);
+}
+
+static inline void net_fill_flow6(net_addr *a, ip6_addr prefix, uint pxlen, byte *data, uint dlen)
+{
+ net_addr_flow6 *f = (void *) a;
+ *f = NET_ADDR_FLOW6(prefix, pxlen, dlen);
+ memcpy(f->data, data, dlen);
+}
+
+static inline int net_val_match(u8 type, u32 mask)
+{ return !!((1 << type) & mask); }
+
+static inline int net_type_match(const net_addr *a, u32 mask)
+{ return net_val_match(a->type, mask); }
+
+static inline int net_is_ip(const net_addr *a)
+{ return (a->type == NET_IP4) || (a->type == NET_IP6); }
+
+static inline int net_is_vpn(const net_addr *a)
+{ return (a->type == NET_VPN4) || (a->type == NET_VPN6); }
+
+static inline int net_is_roa(const net_addr *a)
+{ return (a->type == NET_ROA4) || (a->type == NET_ROA6); }
+
+static inline int net_is_flow(const net_addr *a)
+{ return (a->type == NET_FLOW4) || (a->type == NET_FLOW6); }
+
+
+static inline ip4_addr net4_prefix(const net_addr *a)
+{ return ((net_addr_ip4 *) a)->prefix; }
+
+static inline ip6_addr net6_prefix(const net_addr *a)
+{ return ((net_addr_ip6 *) a)->prefix; }
+
+static inline ip_addr net_prefix(const net_addr *a)
+{
+ switch (a->type)
+ {
+ case NET_IP4:
+ case NET_VPN4:
+ case NET_ROA4:
+ case NET_FLOW4:
+ return ipa_from_ip4(net4_prefix(a));
+
+ case NET_IP6:
+ case NET_VPN6:
+ case NET_ROA6:
+ case NET_FLOW6:
+ return ipa_from_ip6(net6_prefix(a));
+
+ case NET_MPLS:
+ default:
+ return IPA_NONE;
+ }
+}
+
+static inline u32 net_mpls(const net_addr *a)
+{
+ if (a->type == NET_MPLS)
+ return ((net_addr_mpls *) a)->label;
+
+ bug("Can't call net_mpls on non-mpls net_addr");
+}
+
+static inline uint net4_pxlen(const net_addr *a)
+{ return a->pxlen; }
+
+static inline uint net6_pxlen(const net_addr *a)
+{ return a->pxlen; }
+
+static inline uint net_pxlen(const net_addr *a)
+{ return a->pxlen; }
+
+ip_addr net_pxmask(const net_addr *a);
+
+static inline u64 net_rd(const net_addr *a)
+{
+ switch (a->type)
+ {
+ case NET_VPN4:
+ return ((net_addr_vpn4 *)a)->rd;
+ case NET_VPN6:
+ return ((net_addr_vpn6 *)a)->rd;
+ }
+ return 0;
+}
+
+
+static inline int net_equal(const net_addr *a, const net_addr *b)
+{ return (a->length == b->length) && !memcmp(a, b, a->length); }
+
+static inline int net_equal_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
+{ return !memcmp(a, b, sizeof(net_addr_ip4)); }
+
+static inline int net_equal_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
+{ return !memcmp(a, b, sizeof(net_addr_ip6)); }
+
+static inline int net_equal_vpn4(const net_addr_vpn4 *a, const net_addr_vpn4 *b)
+{ return !memcmp(a, b, sizeof(net_addr_vpn4)); }
+
+static inline int net_equal_vpn6(const net_addr_vpn6 *a, const net_addr_vpn6 *b)
+{ return !memcmp(a, b, sizeof(net_addr_vpn6)); }
+
+static inline int net_equal_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
+{ return !memcmp(a, b, sizeof(net_addr_roa4)); }
+
+static inline int net_equal_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
+{ return !memcmp(a, b, sizeof(net_addr_roa6)); }
+
+static inline int net_equal_flow4(const net_addr_flow4 *a, const net_addr_flow4 *b)
+{ return net_equal((const net_addr *) a, (const net_addr *) b); }
+
+static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
+{ return net_equal((const net_addr *) a, (const net_addr *) b); }
+
+static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
+{ return !memcmp(a, b, sizeof(net_addr_mpls)); }
+
+
+static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
+{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
+
+static inline int net_equal_prefix_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
+{ return ip6_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
+
+
+static inline int net_zero_ip4(const net_addr_ip4 *a)
+{ return !a->pxlen && ip4_zero(a->prefix); }
+
+static inline int net_zero_ip6(const net_addr_ip6 *a)
+{ return !a->pxlen && ip6_zero(a->prefix); }
+
+static inline int net_zero_vpn4(const net_addr_vpn4 *a)
+{ return !a->pxlen && ip4_zero(a->prefix) && !a->rd; }
+
+static inline int net_zero_vpn6(const net_addr_vpn6 *a)
+{ return !a->pxlen && ip6_zero(a->prefix) && !a->rd; }
+
+static inline int net_zero_roa4(const net_addr_roa4 *a)
+{ return !a->pxlen && ip4_zero(a->prefix) && !a->max_pxlen && !a->asn; }
+
+static inline int net_zero_roa6(const net_addr_roa6 *a)
+{ return !a->pxlen && ip6_zero(a->prefix) && !a->max_pxlen && !a->asn; }
+
+static inline int net_zero_flow4(const net_addr_flow4 *a)
+{ return !a->pxlen && ip4_zero(a->prefix) && (a->length == sizeof(net_addr_flow4)); }
+
+static inline int net_zero_flow6(const net_addr_flow6 *a)
+{ return !a->pxlen && ip6_zero(a->prefix) && (a->length == sizeof(net_addr_flow6)); }
+
+static inline int net_zero_mpls(const net_addr_mpls *a)
+{ return !a->label; }
+
+
+static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
+{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
+
+static inline int net_compare_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
+{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
+
+static inline int net_compare_vpn4(const net_addr_vpn4 *a, const net_addr_vpn4 *b)
+{ return u64_cmp(a->rd, b->rd) ?: ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
+
+static inline int net_compare_vpn6(const net_addr_vpn6 *a, const net_addr_vpn6 *b)
+{ return u64_cmp(a->rd, b->rd) ?: ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
+
+static inline int net_compare_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
+{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); }
+
+static inline int net_compare_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
+{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); }
+
+static inline int net_compare_flow4(const net_addr_flow4 *a, const net_addr_flow4 *b)
+{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow4)); }
+
+static inline int net_compare_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
+{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow6)); }
+
+static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
+{ return uint_cmp(a->label, b->label); }
+
+int net_compare(const net_addr *a, const net_addr *b);
+
+
+static inline void net_copy(net_addr *dst, const net_addr *src)
+{ memcpy(dst, src, src->length); }
+
+static inline void net_copy_ip4(net_addr_ip4 *dst, const net_addr_ip4 *src)
+{ memcpy(dst, src, sizeof(net_addr_ip4)); }
+
+static inline void net_copy_ip6(net_addr_ip6 *dst, const net_addr_ip6 *src)
+{ memcpy(dst, src, sizeof(net_addr_ip6)); }
+
+static inline void net_copy_vpn4(net_addr_vpn4 *dst, const net_addr_vpn4 *src)
+{ memcpy(dst, src, sizeof(net_addr_vpn4)); }
+
+static inline void net_copy_vpn6(net_addr_vpn6 *dst, const net_addr_vpn6 *src)
+{ memcpy(dst, src, sizeof(net_addr_vpn6)); }
+
+static inline void net_copy_roa4(net_addr_roa4 *dst, const net_addr_roa4 *src)
+{ memcpy(dst, src, sizeof(net_addr_roa4)); }
+
+static inline void net_copy_roa6(net_addr_roa6 *dst, const net_addr_roa6 *src)
+{ memcpy(dst, src, sizeof(net_addr_roa6)); }
+
+static inline void net_copy_flow4(net_addr_flow4 *dst, const net_addr_flow4 *src)
+{ memcpy(dst, src, src->length); }
+
+static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src)
+{ memcpy(dst, src, src->length); }
+
+static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
+{ memcpy(dst, src, sizeof(net_addr_mpls)); }
+
+
+/* XXXX */
+static inline u32 u64_hash(u64 a)
+{ return u32_hash(a); }
+
+static inline u32 net_hash_ip4(const net_addr_ip4 *n)
+{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+
+static inline u32 net_hash_ip6(const net_addr_ip6 *n)
+{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+
+static inline u32 net_hash_vpn4(const net_addr_vpn4 *n)
+{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
+
+static inline u32 net_hash_vpn6(const net_addr_vpn6 *n)
+{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
+
+static inline u32 net_hash_roa4(const net_addr_roa4 *n)
+{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+
+static inline u32 net_hash_roa6(const net_addr_roa6 *n)
+{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+
+static inline u32 net_hash_flow4(const net_addr_flow4 *n)
+{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+
+static inline u32 net_hash_flow6(const net_addr_flow6 *n)
+{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+
+static inline u32 net_hash_mpls(const net_addr_mpls *n)
+{ return n->label; }
+
+u32 net_hash(const net_addr *a);
+
+
+static inline int net_validate_px4(const ip4_addr prefix, uint pxlen)
+{
+ return (pxlen <= IP4_MAX_PREFIX_LENGTH) &&
+ ip4_zero(ip4_and(prefix, ip4_not(ip4_mkmask(pxlen))));
+}
+
+static inline int net_validate_px6(const ip6_addr prefix, uint pxlen)
+{
+ return (pxlen <= IP6_MAX_PREFIX_LENGTH) &&
+ ip6_zero(ip6_and(prefix, ip6_not(ip6_mkmask(pxlen))));
+}
+
+static inline int net_validate_ip4(const net_addr_ip4 *n)
+{ return net_validate_px4(n->prefix, n->pxlen); }
+
+static inline int net_validate_ip6(const net_addr_ip6 *n)
+{ return net_validate_px6(n->prefix, n->pxlen); }
+
+static inline int net_validate_vpn4(const net_addr_vpn4 *n)
+{ return net_validate_px4(n->prefix, n->pxlen); }
+
+static inline int net_validate_vpn6(const net_addr_vpn6 *n)
+{ return net_validate_px6(n->prefix, n->pxlen); }
+
+static inline int net_validate_roa4(const net_addr_roa4 *n)
+{
+ return net_validate_px4(n->prefix, n->pxlen) &&
+ (n->pxlen <= n->max_pxlen) && (n->max_pxlen <= IP4_MAX_PREFIX_LENGTH);
+}
+
+static inline int net_validate_roa6(const net_addr_roa6 *n)
+{
+ return net_validate_px6(n->prefix, n->pxlen) &&
+ (n->pxlen <= n->max_pxlen) && (n->max_pxlen <= IP6_MAX_PREFIX_LENGTH);
+}
+
+// FIXME: Better check, call flow_validate?
+static inline int net_validate_flow4(const net_addr_flow4 *n)
+{ return net_validate_px4(n->prefix, n->pxlen); }
+
+static inline int net_validate_flow6(const net_addr_flow6 *n)
+{ return net_validate_px6(n->prefix, n->pxlen); }
+
+static inline int net_validate_mpls(const net_addr_mpls *n)
+{ return n->label < (1 << 20); }
+
+int net_validate(const net_addr *N);
+
+
+static inline void net_normalize_ip4(net_addr_ip4 *n)
+{ n->prefix = ip4_and(n->prefix, ip4_mkmask(n->pxlen)); }
+
+static inline void net_normalize_ip6(net_addr_ip6 *n)
+{ n->prefix = ip6_and(n->prefix, ip6_mkmask(n->pxlen)); }
+
+static inline void net_normalize_vpn4(net_addr_vpn4 *n)
+{ net_normalize_ip4((net_addr_ip4 *) n); }
+
+static inline void net_normalize_vpn6(net_addr_vpn6 *n)
+{ net_normalize_ip6((net_addr_ip6 *) n); }
+
+void net_normalize(net_addr *N);
+
+
+int net_classify(const net_addr *N);
+int net_format(const net_addr *N, char *buf, int buflen);
+int rd_format(const u64 rd, char *buf, int buflen);
+
+static inline int ipa_in_net_ip4(ip4_addr a, const net_addr_ip4 *n)
+{ return ip4_zero(ip4_and(ip4_xor(a, n->prefix), ip4_mkmask(n->pxlen))); }
+
+static inline int net_in_net_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
+{ return (a->pxlen >= b->pxlen) && ipa_in_net_ip4(a->prefix, b); }
+
+static inline int ipa_in_net_ip6(ip6_addr a, const net_addr_ip6 *n)
+{ return ip6_zero(ip6_and(ip6_xor(a, n->prefix), ip6_mkmask(n->pxlen))); }
+
+static inline int net_in_net_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
+{ return (a->pxlen >= b->pxlen) && ipa_in_net_ip6(a->prefix, b); }
+
+int ipa_in_netX(const ip_addr A, const net_addr *N);
+int net_in_netX(const net_addr *A, const net_addr *N);
+
+void net_init(void);
+
+#endif
diff --git a/lib/patmatch_test.c b/lib/patmatch_test.c
new file mode 100644
index 00000000..d65f316f
--- /dev/null
+++ b/lib/patmatch_test.c
@@ -0,0 +1,149 @@
+/*
+ * BIRD Library -- Pattern Matching Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+
+#include "nest/bird.h"
+#include "lib/string.h"
+
+#define MATCH (int) { 1 }
+#define NOMATCH (int) { 0 }
+
+struct match_pair {
+ byte *pattern;
+ byte *data;
+};
+
+static int
+test_matching(void *out_, const void *in_, const void *expected_out_)
+{
+ int *out = out_;
+ const struct match_pair *in = in_;
+ const int *expected_out = expected_out_;
+
+ *out = patmatch(in->pattern, in->data);
+
+ return *out == *expected_out;
+}
+
+static void
+fmt_match_pair(char *buf, size_t size, const void *data)
+{
+ const struct match_pair *mp = data;
+ snprintf(buf, size, "pattern: '%s', subject: '%s'", mp->pattern, mp->data);
+}
+
+static void
+fmt_match_result(char *buf, size_t size, const void *data)
+{
+ const int *result = data;
+ snprintf(buf, size, *result ? "match" : "no-match");
+}
+
+static int
+t_matching(void)
+{
+ struct bt_pair test_vectors[] = {
+ {
+ .in = & (struct match_pair) {
+ .pattern = "",
+ .data = "",
+ },
+ .out = & MATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "*",
+ .data = "",
+ },
+ .out = & MATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "\\*",
+ .data = "*",
+ },
+ .out = & MATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "\\*",
+ .data = "a",
+ },
+ .out = & NOMATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "?",
+ .data = "",
+ },
+ .out = & NOMATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "abcdefghijklmnopqrstuvwxyz",
+ .data = "abcdefghijklmnopqrstuvwxyz",
+ },
+ .out = & MATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "??????????????????????????",
+ .data = "abcdefghijklmnopqrstuvwxyz",
+ },
+ .out = & MATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "*abcdefghijklmnopqrstuvwxyz*",
+ .data = "abcdefghijklmnopqrstuvwxyz",
+ },
+ .out = & MATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "ab?defg*jklmnop*stu*wxy*z",
+ .data = "abcdefghijklmnopqrstuvwxyz",
+ },
+ .out = & MATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "abcdefghijklmnopqrstuvwxyz",
+ .data = "abcdefghijklmnopqrtuvwxyz",
+ },
+ .out = & NOMATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "abcdefghijklmnopqr?uvwxyz",
+ .data = "abcdefghijklmnopqrstuvwxyz",
+ },
+ .out = & NOMATCH,
+ },
+ {
+ .in = & (struct match_pair) {
+ .pattern = "aa*aaaaa?aaaaaaaaaaaaaaaaaaa",
+ .data = "aaaaaaaaaaaaaaaaaaaaaaaaaa",
+ },
+ .out = & NOMATCH,
+ },
+ };
+
+ return bt_assert_batch(test_vectors, test_matching, fmt_match_pair, fmt_match_result);
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_matching, "Pattern matching");
+
+ return bt_exit_value();
+}
diff --git a/lib/printf.c b/lib/printf.c
index 4fd75c9b..533a1300 100644
--- a/lib/printf.c
+++ b/lib/printf.c
@@ -118,16 +118,17 @@ static char * number(char * str, long num, int base, int size, int precision,
* @fmt: format string
* @args: a list of arguments to be formatted
*
- * This functions acts like ordinary sprintf() except that it checks
- * available space to avoid buffer overflows and it allows some more
- * format specifiers: |%I| for formatting of IP addresses (any non-zero
- * width is automatically replaced by standard IP address width which
- * depends on whether we use IPv4 or IPv6; |%#I| gives hexadecimal format),
- * |%R| for Router / Network ID (u32 value printed as IPv4 address)
- * |%lR| for 64bit Router / Network ID (u64 value printed as eight :-separated octets)
- * and |%m| resp. |%M| for error messages (uses strerror() to translate @errno code to
- * message text). On the other hand, it doesn't support floating
- * point numbers.
+ * This functions acts like ordinary sprintf() except that it checks available
+ * space to avoid buffer overflows and it allows some more format specifiers:
+ * |%I| for formatting of IP addresses (width of 1 is automatically replaced by
+ * standard IP address width which depends on whether we use IPv4 or IPv6; |%I4|
+ * or |%I6| can be used for explicit ip4_addr / ip6_addr arguments, |%N| for
+ * generic network addresses (net_addr *), |%R| for Router / Network ID (u32
+ * value printed as IPv4 address), |%lR| for 64bit Router / Network ID (u64
+ * value printed as eight :-separated octets), |%t| for time values (btime) with
+ * specified subsecond precision, and |%m| resp. |%M| for error messages (uses
+ * strerror() to translate @errno code to message text). On the other hand, it
+ * doesn't support floating point numbers.
*
* Result: number of characters of the output string or -1 if
* the buffer space was insufficient.
@@ -139,9 +140,11 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
int i, base;
u32 x;
u64 X;
+ btime t;
+ s64 t1, t2;
char *str, *start;
const char *s;
- char ipbuf[MAX(STD_ADDRESS_P_LENGTH,ROUTER_ID_64_LENGTH)+1];
+ char ipbuf[NET_MAX_TEXT_LENGTH+1];
struct iface *iface;
int flags; /* flags to number() */
@@ -158,7 +161,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
*str++ = *fmt;
continue;
}
-
+
/* process flags */
flags = 0;
repeat:
@@ -170,7 +173,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}
-
+
/* get field width */
field_width = -1;
if (is_digit(*fmt))
@@ -188,7 +191,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* get the precision */
precision = -1;
if (*fmt == '.') {
- ++fmt;
+ ++fmt;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
@@ -238,6 +241,14 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
case 'M':
s = strerror(va_arg(args, int));
goto str;
+ case 'N': {
+ net_addr *n = va_arg(args, net_addr *);
+ if (field_width == 1)
+ field_width = net_max_text_length[n->type];
+ net_format(n, ipbuf, sizeof(ipbuf));
+ s = ipbuf;
+ goto str;
+ }
case 's':
s = va_arg(args, char *);
if (!s)
@@ -271,7 +282,6 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
return -1;
continue;
-
case 'n':
if (qualifier == 'l') {
long * ip = va_arg(args, long *);
@@ -284,14 +294,35 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* IP address */
case 'I':
- if (flags & SPECIAL)
- ipa_ntox(va_arg(args, ip_addr), ipbuf);
- else {
- ipa_ntop(va_arg(args, ip_addr), ipbuf);
- if (field_width == 1)
- field_width = STD_ADDRESS_P_LENGTH;
+ if (fmt[1] == '4') {
+ /* Explicit IPv4 address */
+ ip4_addr a = va_arg(args, ip4_addr);
+ ip4_ntop(a, ipbuf);
+ i = IP4_MAX_TEXT_LENGTH;
+ fmt++;
+ } else if (fmt[1] == '6') {
+ /* Explicit IPv6 address */
+ ip6_addr a = va_arg(args, ip6_addr);
+ ip6_ntop(a, ipbuf);
+ i = IP6_MAX_TEXT_LENGTH;
+ fmt++;
+ } else {
+ /* Just IP address */
+ ip_addr a = va_arg(args, ip_addr);
+
+ if (ipa_is_ip4(a)) {
+ ip4_ntop(ipa_to_ip4(a), ipbuf);
+ i = IP4_MAX_TEXT_LENGTH;
+ } else {
+ ip6_ntop(ipa_to_ip6(a), ipbuf);
+ i = IP6_MAX_TEXT_LENGTH;
+ }
}
+
s = ipbuf;
+ if (field_width == 1)
+ field_width = i;
+
goto str;
/* Interface scope after link-local IP address */
@@ -311,7 +342,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
/* Router/Network ID - essentially IPv4 address in u32 value */
case 'R':
- if(qualifier == 'l') {
+ if (qualifier == 'l') {
X = va_arg(args, u64);
bsprintf(ipbuf, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
((X >> 56) & 0xff),
@@ -326,15 +357,55 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
else
{
x = va_arg(args, u32);
- bsprintf(ipbuf, "%d.%d.%d.%d",
- ((x >> 24) & 0xff),
- ((x >> 16) & 0xff),
- ((x >> 8) & 0xff),
- (x & 0xff));
+ ip4_ntop(ip4_from_u32(x), ipbuf);
}
s = ipbuf;
goto str;
+ case 't':
+ t = va_arg(args, btime);
+ t1 = t TO_S;
+ t2 = t - t1 S;
+
+ if (precision < 0)
+ precision = 3;
+
+ if (precision > 6)
+ precision = 6;
+
+ /* Compute field_width for second part */
+ if ((precision > 0) && (field_width > 0))
+ field_width -= (1 + precision);
+
+ if (field_width < 0)
+ field_width = 0;
+
+ /* Print seconds */
+ flags |= SIGN;
+ str = number(str, t1, 10, field_width, 0, flags, size);
+ if (!str)
+ return -1;
+
+ if (precision > 0)
+ {
+ size -= (str-start);
+ start = str;
+
+ if ((1 + precision) > size)
+ return -1;
+
+ /* Convert microseconds to requested precision */
+ for (i = precision; i < 6; i++)
+ t2 /= 10;
+
+ /* Print sub-seconds */
+ *str++ = '.';
+ str = number(str, t2, 10, precision, 0, ZEROPAD, size - 1);
+ if (!str)
+ return -1;
+ }
+ goto done;
+
/* integer number formats - set up the flags and "break" */
case 'o':
base = 8;
@@ -376,6 +447,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
str = number(str, num, base, field_width, precision, flags, size);
if (!str)
return -1;
+ done: ;
}
if (!size)
return -1;
@@ -442,6 +514,10 @@ int
buffer_vprint(buffer *buf, const char *fmt, va_list args)
{
int i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
+
+ if ((i < 0) && (buf->pos < buf->end))
+ *buf->pos = 0;
+
buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
return i;
}
@@ -453,9 +529,12 @@ buffer_print(buffer *buf, const char *fmt, ...)
int i;
va_start(args, fmt);
- i=bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
+ i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
va_end(args);
+ if ((i < 0) && (buf->pos < buf->end))
+ *buf->pos = 0;
+
buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
return i;
}
@@ -464,13 +543,13 @@ void
buffer_puts(buffer *buf, const char *str)
{
byte *bp = buf->pos;
- byte *be = buf->end;
+ byte *be = buf->end - 1;
while (bp < be && *str)
*bp++ = *str++;
- if (bp < be)
+ if (bp <= be)
*bp = 0;
- buf->pos = bp;
+ buf->pos = (bp < be) ? bp : buf->end;
}
diff --git a/lib/printf_test.c b/lib/printf_test.c
new file mode 100644
index 00000000..a2683d93
--- /dev/null
+++ b/lib/printf_test.c
@@ -0,0 +1,79 @@
+/*
+ * BIRD Library -- String Functions Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+
+#include "lib/string.h"
+
+#define BSPRINTF(nw, res, buf, fmt, ...) \
+ do { \
+ int n = bsprintf(buf, fmt, ##__VA_ARGS__); \
+ bt_assert_msg(n == nw, "fmt=\"%s\" returns n=%d, want %d", fmt, n, nw); \
+ bt_assert_msg(buf[n] == 0, "fmt=\"%s\" buf[%d] should be \'\\0\', found 0x%02x", fmt, n, buf[n]); \
+ bt_assert_msg(memcmp(buf, res, nw) == 0, "fmt=\"%s\" writes \"%*s\", want \"%*s\"", fmt, (n < nw ? n : nw), buf, nw, res); \
+ } while (0)
+
+static int
+t_simple(void)
+{
+ char buf[256];
+ memset(buf, 0xa5, 256);
+
+ BSPRINTF(0, "", buf, "", NULL);
+ BSPRINTF(1, "%", buf, "%%", NULL);
+ BSPRINTF(2, "%%", buf, "%%%%", NULL);
+
+ BSPRINTF(1, "\x00", buf, "%c", 0);
+ BSPRINTF(1, "@", buf, "@", 64);
+ BSPRINTF(1, "\xff", buf, "%c", 0xff);
+
+ errno = 5;
+ BSPRINTF(18, "Input/output error", buf, "%m");
+ errno = 0;
+
+ BSPRINTF(18, "Input/output error", buf, "%M", 5);
+
+ BSPRINTF(11, "TeSt%StRiNg", buf, "%s", "TeSt%StRiNg");
+
+ if (sizeof(void *) == 4)
+ BSPRINTF(8, "1a15600d", buf, "%p", (void *) 0x1a15600d);
+ else
+ BSPRINTF(16, "00000fee1a15600d", buf, "%p", (void *) 0xfee1a15600d);
+
+ long ln = 0;
+ BSPRINTF(10, "TeStStRiNg", buf, "TeStS%lntRiNg", &ln);
+ bt_assert_msg(ln == 5, "fmt=\"TeStS%%lntRiNg\", &ln makes ln=%ld, want 5", ln);
+
+ BSPRINTF(2, "%d", buf, "%%d", 1);
+ BSPRINTF(1, "1", buf, "%d", 1);
+ BSPRINTF(2, "+1", buf, "%+d", 1);
+ BSPRINTF(2, " 1", buf, "% d", 1);
+ BSPRINTF(2, "-1", buf, "%d", -1);
+ BSPRINTF(11, "-2147483648", buf, "%d", -2147483648);
+
+ BSPRINTF(7, "123.456", buf, "%t", (btime) 123456789);
+ BSPRINTF(7, "123.456", buf, "%2t", (btime) 123456789);
+ BSPRINTF(8, " 123.456", buf, "%8t", (btime) 123456789);
+ BSPRINTF(4, " 123", buf, "%4.0t", (btime) 123456789);
+ BSPRINTF(8, "123.4567", buf, "%8.4t", (btime) 123456789);
+ BSPRINTF(9, "0123.4567", buf, "%09.4t", (btime) 123456789);
+ BSPRINTF(12, " 123.456789", buf, "%12.10t", (btime) 123456789);
+ BSPRINTF(8, " 123.004", buf, "%8t", (btime) 123004 MS);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_simple, "printf without varargs");
+
+ return bt_exit_value();
+}
diff --git a/lib/resource.c b/lib/resource.c
index 68718dfb..ab8c800f 100644
--- a/lib/resource.c
+++ b/lib/resource.c
@@ -31,7 +31,7 @@
struct pool {
resource r;
list inside;
- char *name;
+ const char *name;
};
static void pool_dump(resource *);
@@ -61,7 +61,7 @@ static int indent;
* parent pool.
*/
pool *
-rp_new(pool *p, char *name)
+rp_new(pool *p, const char *name)
{
pool *z = ralloc(p, &pool_class);
z->name = name;
diff --git a/lib/resource.h b/lib/resource.h
index 1a62d389..d9d4bb8f 100644
--- a/lib/resource.h
+++ b/lib/resource.h
@@ -37,7 +37,7 @@ struct resclass {
typedef struct pool pool;
void resource_init(void);
-pool *rp_new(pool *, char *); /* Create new pool */
+pool *rp_new(pool *, const char *); /* Create new pool */
void rfree(void *); /* Free single resource */
void rdump(void *); /* Dump to debug output */
size_t rmemsize(void *res); /* Return size of memory used by the resource */
@@ -59,11 +59,23 @@ void mb_free(void *);
typedef struct linpool linpool;
+typedef struct lp_state {
+ void *current, *large;
+ byte *ptr;
+} lp_state;
+
linpool *lp_new(pool *, unsigned blk);
void *lp_alloc(linpool *, unsigned size); /* Aligned */
void *lp_allocu(linpool *, unsigned size); /* Unaligned */
void *lp_allocz(linpool *, unsigned size); /* With clear */
void lp_flush(linpool *); /* Free everything, but leave linpool */
+void lp_save(linpool *m, lp_state *p); /* Save state */
+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))
/* Slabs */
diff --git a/lib/slist_test.c b/lib/slist_test.c
new file mode 100644
index 00000000..069e361c
--- /dev/null
+++ b/lib/slist_test.c
@@ -0,0 +1,384 @@
+/*
+ * BIRD Library -- Safe Linked Lists Tests
+ *
+ * (c) 2015 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+
+#include "lib/slists.h"
+
+#define MAX_NUM 1000
+
+static snode nodes[MAX_NUM];
+static slist lst;
+
+static void
+show_list(void)
+{
+ bt_debug("\n");
+ bt_debug("list.null is at %p and point to %p \n", &lst.null, lst.null);
+ bt_debug("list.head is at %p and point to %p \n", &lst.head, lst.head);
+ bt_debug("list.tail is at %p and point to %p \n", &lst.tail, lst.tail);
+ bt_debug("list.tail_readers is at %p and point to %p \n", &lst.tail_readers, lst.tail_readers);
+
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ bt_debug("n[%3i] is at %p, .prev (%p) points to %p, .next (%p) points to %p, .readers (%p) points to %p \n",
+ i, &nodes[i], &(nodes[i].prev), nodes[i].prev, &(nodes[i].next), nodes[i].next, &(nodes[i].readers), nodes[i].readers);
+}
+
+static int
+is_filled_list_well_linked(void)
+{
+ int i;
+ bt_assert(lst.head == &nodes[0]);
+ bt_assert(lst.tail == &nodes[MAX_NUM-1]);
+ bt_assert((void *) nodes[0].prev == (void *) &lst.head);
+ bt_assert((void *) nodes[MAX_NUM-1].next == (void *) &lst.null);
+
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ if (i < (MAX_NUM-1))
+ bt_assert(nodes[i].next == &nodes[i+1]);
+
+ if (i > 0)
+ bt_assert(nodes[i].prev == &nodes[i-1]);
+ }
+
+ return 1;
+}
+
+static int
+is_empty_list_well_unlinked(void)
+{
+ bt_assert(lst.head == SNODE &lst.null);
+ bt_assert(lst.tail == SNODE &lst.head);
+
+ bt_assert(EMPTY_SLIST(lst));
+
+ return 1;
+}
+
+static void
+init_list__(slist *l, struct snode nodes[])
+{
+ s_init_list(l);
+
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ nodes[i].next = NULL;
+ nodes[i].prev = NULL;
+ }
+}
+
+static void
+init_list_(void)
+{
+ init_list__(&lst, nodes);
+}
+
+static int
+t_add_tail(void)
+{
+ int i;
+
+ init_list_();
+ for (i = 0; i < MAX_NUM; i++)
+ {
+ s_add_tail(&lst, &nodes[i]);
+ bt_debug(".");
+ bt_assert(lst.tail == &nodes[i]);
+ bt_assert(lst.head == &nodes[0]);
+ bt_assert((void *) nodes[i].next == (void *) &lst.null);
+ if (i > 0)
+ {
+ bt_assert(nodes[i-1].next == &nodes[i]);
+ bt_assert(nodes[i].prev == &nodes[i-1]);
+ }
+ }
+
+ bt_assert(is_filled_list_well_linked());
+
+ return 1;
+}
+
+static int
+t_add_head(void)
+{
+ int i;
+
+ init_list_();
+ for (i = MAX_NUM-1; i >= 0; i--)
+ {
+ s_add_head(&lst, &nodes[i]);
+ bt_debug(".");
+ bt_assert(lst.head == &nodes[i]);
+ bt_assert(lst.tail == &nodes[MAX_NUM-1]);
+ if (i < MAX_NUM-1)
+ {
+ bt_assert(nodes[i+1].prev == &nodes[i]);
+ bt_assert(nodes[i].next == &nodes[i+1]);
+ }
+ }
+
+ bt_assert(is_filled_list_well_linked());
+
+ return 1;
+}
+
+static void
+insert_node_(snode *n, snode *after)
+{
+ s_insert_node(n, after);
+ bt_debug(".");
+}
+
+static int
+t_insert_node(void)
+{
+ int i;
+
+ init_list_();
+
+ // add first node
+ insert_node_(&nodes[0], SNODE &lst.head);
+
+ // add odd nodes
+ for (i = 2; i < MAX_NUM; i+=2)
+ insert_node_(&nodes[i], &nodes[i-2]);
+
+ // add even nodes
+ for (i = 1; i < MAX_NUM; i+=2)
+ insert_node_(&nodes[i], &nodes[i-1]);
+
+ bt_debug("\n");
+ bt_assert(is_filled_list_well_linked());
+
+ return 1;
+}
+
+static void
+fill_list2(slist *l, snode nodes[])
+{
+ int i;
+ for (i = 0; i < MAX_NUM; i++)
+ s_add_tail(l, &nodes[i]);
+}
+
+static void
+fill_list(void)
+{
+ fill_list2(&lst, SNODE nodes);
+}
+
+
+static int
+t_remove_node(void)
+{
+ int i;
+
+ init_list_();
+
+ /* Fill & Remove & Check */
+ fill_list();
+ for (i = 0; i < MAX_NUM; i++)
+ s_rem_node(&nodes[i]);
+ bt_assert(is_empty_list_well_unlinked());
+
+ /* Fill & Remove the half of nodes & Check & Remove the rest nodes & Check */
+ fill_list();
+ for (i = 0; i < MAX_NUM; i+=2)
+ s_rem_node(&nodes[i]);
+
+ int tail_node_index = (MAX_NUM % 2) ? MAX_NUM - 2 : MAX_NUM - 1;
+ bt_assert(lst.head == &nodes[1]);
+ bt_assert(lst.tail == &nodes[tail_node_index]);
+ bt_assert(nodes[tail_node_index].next == SNODE &lst.null);
+
+ for (i = 1; i < MAX_NUM; i+=2)
+ {
+ if (i > 1)
+ bt_assert(nodes[i].prev == &nodes[i-2]);
+ if (i < tail_node_index)
+ bt_assert(nodes[i].next == &nodes[i+2]);
+ }
+
+ for (i = 1; i < MAX_NUM; i+=2)
+ s_rem_node(&nodes[i]);
+ bt_assert(is_empty_list_well_unlinked());
+
+ return 1;
+}
+
+static int
+t_add_tail_list(void)
+{
+ snode nodes2[MAX_NUM];
+ slist l2;
+
+ init_list__(&lst, SNODE &nodes);
+ fill_list2(&lst, SNODE &nodes);
+
+ init_list__(&l2, SNODE &nodes2);
+ fill_list2(&l2, SNODE &nodes2);
+
+ s_add_tail_list(&lst, &l2);
+
+ bt_assert(nodes[MAX_NUM-1].next == &nodes2[0]);
+ bt_assert(nodes2[0].prev == &nodes[MAX_NUM-1]);
+ bt_assert(lst.tail == &nodes2[MAX_NUM-1]);
+
+ return 1;
+}
+
+void
+dump(const char *str, slist *a)
+{
+ snode *x;
+
+ bt_debug("%s \n", str);
+ for (x = SHEAD(*a); x; x = x->next)
+ {
+ siterator *i, *j;
+ bt_debug("%p", x);
+ j = (siterator *) x;
+ for (i = x->readers; i; i = i->next)
+ {
+ if (i->prev != j)
+ bt_debug(" ???");
+ j = i;
+ bt_debug(" [%p:%p]", i, i->node);
+ }
+ bt_debug("\n");
+ }
+ bt_debug("---\n");
+}
+
+static int
+t_iterator_walk(void)
+{
+ snode *node;
+ siterator iter;
+
+ init_list_();
+ fill_list();
+
+ int k;
+ int i = 0;
+
+ show_list();
+
+ s_init(&iter, &lst);
+ WALK_SLIST(node, lst)
+ {
+ s_get(&iter);
+ s_put(&iter, node);
+ bt_debug("node->readers: %p, iter: %p, nodes[%d].readers: %p, node: %p, nodes[i]: %p, node->next: %p \n",
+ node->readers, &iter, i, nodes[i].readers, node, &(nodes[i]), node->next);
+ bt_assert(node->readers == &iter);
+ bt_assert(node->readers == nodes[i].readers);
+ bt_assert(node == &(nodes[i]));
+ for (k = 0; k < MAX_NUM; k++)
+ if (k != i)
+ bt_assert(nodes[k].readers == NULL);
+
+ dump("",&lst);
+ i++;
+ }
+
+ return 1;
+}
+
+static int
+t_original(void)
+{
+ slist a, b;
+ snode *x, *y;
+ siterator i, j;
+
+ s_init_list(&a);
+ s_init_list(&b);
+ x = xmalloc(sizeof(*x));
+ s_add_tail(&a, x);
+ x = xmalloc(sizeof(*x));
+ s_add_tail(&a, x);
+ x = xmalloc(sizeof(*x));
+ s_add_tail(&a, x);
+ dump("1", &a);
+
+ s_init(&i, &a);
+ s_init(&j, &a);
+ dump("2", &a);
+
+ x = s_get(&i);
+ bt_debug("Got %p\n", x);
+ dump("3", &a);
+
+ s_put(&i, x->next);
+ dump("4", &a);
+
+ y = s_get(&j);
+ while (y)
+ {
+ s_put(&j, y);
+ dump("5*", &a);
+ y = s_get(&j)->next;
+ }
+
+ dump("5 done", &a);
+
+ s_rem_node(a.head->next);
+ dump("6 (deletion)", &a);
+
+ s_put(&i, s_get(&i)->next);
+ dump("6 (relink)", &a);
+
+ x = xmalloc(sizeof(*x));
+ s_add_tail(&b, x);
+ dump("7 (second list)", &b);
+
+ s_add_tail_list(&b, &a);
+ dump("8 (after merge)", &b);
+
+ return 1;
+}
+
+static int
+t_safe_del_walk(void)
+{
+ init_list_();
+ fill_list();
+
+ show_list();
+
+ snode *node, *node_next;
+ WALK_SLIST_DELSAFE(node,node_next, lst)
+ {
+ bt_debug("Will remove node %p \n", node);
+ s_rem_node(SNODE node);
+ }
+ bt_assert(is_empty_list_well_unlinked());
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_add_tail, "Adding nodes to tail of list");
+ bt_test_suite(t_add_head, "Adding nodes to head of list");
+ bt_test_suite(t_insert_node, "Inserting nodes to list");
+ bt_test_suite(t_remove_node, "Removing nodes from list");
+ bt_test_suite(t_add_tail_list, "At the tail of a list adding the another list");
+ bt_test_suite(t_iterator_walk, "Iterator walk");
+ bt_test_suite(t_safe_del_walk, "WALK_SLIST_DELSAFE and s_rem_node all nodes");
+ bt_test_suite(t_original, "The original BIRD test suit for SLIST");
+
+ return bt_exit_value();
+}
diff --git a/lib/slists.c b/lib/slists.c
index 6e0df39e..00f3c84f 100644
--- a/lib/slists.c
+++ b/lib/slists.c
@@ -150,85 +150,3 @@ s_add_tail_list(slist *to, slist *l)
to->tail = q;
s_merge((snode *) &l->null, (snode *) &to->null);
}
-
-#ifdef TEST
-
-#include "lib/resource.h"
-#include <stdio.h>
-
-void dump(char *c, slist *a)
-{
- snode *x;
-
- puts(c);
- for(x=SHEAD(*a); x; x=x->next)
- {
- siterator *i, *j;
- printf("%p", x);
- j = (siterator *) x;
- for(i=x->readers; i; i=i->next)
- {
- if (i->prev != j)
- printf(" ???");
- j = i;
- printf(" [%p:%p]", i, i->node);
- }
- putchar('\n');
- }
- puts("---");
-}
-
-int main(void)
-{
- slist a, b;
- snode *x, *y;
- siterator i, j;
-
- s_init_list(&a);
- s_init_list(&b);
- x = xmalloc(sizeof(*x));
- s_add_tail(&a, x);
- x = xmalloc(sizeof(*x));
- s_add_tail(&a, x);
- x = xmalloc(sizeof(*x));
- s_add_tail(&a, x);
- dump("1", &a);
-
- s_init(&i, &a);
- s_init(&j, &a);
- dump("2", &a);
-
- x = s_get(&i);
- printf("Got %p\n", x);
- dump("3", &a);
-
- s_put(&i, x->next);
- dump("4", &a);
-
- y = s_get(&j);
- while (y)
- {
- s_put(&j, y);
- dump("5*", &a);
- y = s_get(&j)->next;
- }
-
- dump("5 done", &a);
-
- s_rem_node(a.head->next);
- dump("6 (deletion)", &a);
-
- s_put(&i, s_get(&i)->next);
- dump("6 (relink)", &a);
-
- x = xmalloc(sizeof(*x));
- s_add_tail(&b, x);
- dump("7 (second list)", &b);
-
- s_add_tail_list(&b, &a);
- dump("8 (after merge)", &b);
-
- return 0;
-}
-
-#endif
diff --git a/lib/socket.h b/lib/socket.h
index 0769489b..e53ec5ba 100644
--- a/lib/socket.h
+++ b/lib/socket.h
@@ -10,16 +10,40 @@
#define _BIRD_SOCKET_H_
#include <errno.h>
-// #include <sys/socket.h>
#include "lib/resource.h"
+#ifdef HAVE_LIBSSH
+#define LIBSSH_LEGACY_0_4
+#include <libssh/libssh.h>
+#endif
+
+#ifdef HAVE_LIBSSH
+struct ssh_sock {
+ const char *username; /* (Required) SSH user name */
+ const char *server_hostkey_path; /* (Optional) Filepath to the SSH public key of remote side, can be knownhost file */
+ const char *client_privkey_path; /* (Optional) Filepath to the SSH private key of BIRD */
+ const char *subsystem; /* (Optional) Name of SSH subsytem */
+ ssh_session session; /* Internal */
+ ssh_channel channel; /* Internal */
+ int state; /* Internal */
+#define SK_SSH_CONNECT 0 /* Start state */
+#define SK_SSH_SERVER_KNOWN 1 /* Internal */
+#define SK_SSH_USERAUTH 2 /* Internal */
+#define SK_SSH_CHANNEL 3 /* Internal */
+#define SK_SSH_SESSION 4 /* Internal */
+#define SK_SSH_SUBSYSTEM 5 /* Internal */
+#define SK_SSH_ESTABLISHED 6 /* Final state */
+};
+#endif
typedef struct birdsock {
resource r;
pool *pool; /* Pool where incoming connections should be allocated (for SK_xxx_PASSIVE) */
int type; /* Socket type */
+ int subtype; /* Socket subtype */
void *data; /* User data */
ip_addr saddr, daddr; /* IPA_NONE = unspecified */
+ const char *host; /* Alternative to daddr, NULL = unspecified */
uint sport, dport; /* 0 = unspecified (for IP: protocol type) */
int tos; /* TOS / traffic class, -1 = default */
int priority; /* Local socket priority, -1 = default */
@@ -46,14 +70,15 @@ typedef struct birdsock {
uint lifindex; /* local interface that received the datagram */
/* laddr and lifindex are valid only if SKF_LADDR_RX flag is set to request it */
- int af; /* Address family (AF_INET, AF_INET6 or 0 for non-IP) of fd */
+ int af; /* System-dependend adress family (e.g. AF_INET) */
int fd; /* System-dependent data */
int index; /* Index in poll buffer */
int rcv_ttl; /* TTL of last received datagram */
node n;
void *rbuf_alloc, *tbuf_alloc;
char *password; /* Password for MD5 authentication */
- char *err; /* Error message */
+ const char *err; /* Error message */
+ struct ssh_sock *ssh; /* Used in SK_SSH */
} sock;
sock *sock_new(pool *); /* Allocate new socket */
@@ -69,19 +94,12 @@ void sk_set_tbsize(sock *s, uint val); /* Resize TX buffer, keeping content */
void sk_set_tbuf(sock *s, void *tbuf); /* Switch TX buffer, NULL-> return to internal */
void sk_dump_all(void);
+int sk_is_ipv4(sock *s); /* True if socket is IPv4 */
+int sk_is_ipv6(sock *s); /* True if socket is IPv6 */
+
static inline int sk_send_buffer_empty(sock *sk)
{ return sk->tbuf == sk->tpos; }
-
-#ifdef IPV6
-#define sk_is_ipv4(X) 0
-#define sk_is_ipv6(X) 1
-#else
-#define sk_is_ipv4(X) 1
-#define sk_is_ipv6(X) 0
-#endif
-
-
int sk_setup_multicast(sock *s); /* Prepare UDP or IP socket for multicasting */
int sk_join_group(sock *s, ip_addr maddr); /* Join multicast group on sk iface */
int sk_leave_group(sock *s, ip_addr maddr); /* Leave multicast group on sk iface */
@@ -100,7 +118,6 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
/* Socket flags */
-#define SKF_V4ONLY 0x01 /* Use IPv4 for IP sockets */
#define SKF_V6ONLY 0x02 /* Use IPV6_V6ONLY socket option */
#define SKF_LADDR_RX 0x04 /* Report local address for RX packets */
#define SKF_TTL_RX 0x08 /* Report TTL / Hop Limit for RX packets */
@@ -124,26 +141,38 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
#define SK_MAGIC 7 /* Internal use by sysdep code */
#define SK_UNIX_PASSIVE 8
#define SK_UNIX 9
+#define SK_SSH_ACTIVE 10 /* - - * * - ? - DA = host */
+#define SK_SSH 11
+
+/*
+ * Socket subtypes
+ */
+
+#define SK_IPV4 1
+#define SK_IPV6 2
/*
- * For SK_UDP or SK_IP sockets setting DA/DP allows to use sk_send(),
- * otherwise sk_send_to() must be used.
+ * For TCP/IP sockets, Address family (IPv4 or IPv6) can be specified either
+ * explicitly (SK_IPV4 or SK_IPV6) or implicitly (based on saddr, daddr). But
+ * these specifications must be consistent.
+ *
+ * For SK_UDP or SK_IP sockets setting DA/DP allows to use sk_send(), otherwise
+ * sk_send_to() must be used.
*
- * For SK_IP sockets setting DP specifies protocol number, which is used
- * for both receiving and sending.
+ * For SK_IP sockets setting DP specifies protocol number, which is used for
+ * both receiving and sending.
*
- * For multicast on SK_UDP or SK_IP sockets set IF and TTL,
- * call sk_setup_multicast() to enable multicast on that socket,
- * and then use sk_join_group() and sk_leave_group() to manage
- * a set of received multicast groups.
+ * For multicast on SK_UDP or SK_IP sockets set IF and TTL, call
+ * sk_setup_multicast() to enable multicast on that socket, and then use
+ * sk_join_group() and sk_leave_group() to manage a set of received multicast
+ * groups.
*
- * For datagram (SK_UDP, SK_IP) sockets, there are two ways to handle
- * source address. The socket could be bound to it using bind()
- * syscall, but that also forbids the reception of multicast packets,
- * or the address could be set on per-packet basis using platform
- * dependent options (but these are not available in some corner
- * cases). The first way is used when SKF_BIND is specified, the
- * second way is used otherwise.
+ * For datagram (SK_UDP, SK_IP) sockets, there are two ways to handle source
+ * address. The socket could be bound to it using bind() syscall, but that also
+ * forbids the reception of multicast packets, or the address could be set on
+ * per-packet basis using platform dependent options (but these are not
+ * available in some corner cases). The first way is used when SKF_BIND is
+ * specified, the second way is used otherwise.
*/
#endif
diff --git a/lib/string.h b/lib/string.h
index 75cb88dd..0d34f9c5 100644
--- a/lib/string.h
+++ b/lib/string.h
@@ -13,6 +13,8 @@
#include <string.h>
#include <strings.h>
+#include "lib/resource.h"
+
int bsprintf(char *str, const char *fmt, ...);
int bvsprintf(char *str, const char *fmt, va_list args);
int bsnprintf(char *str, int size, const char *fmt, ...);
@@ -39,6 +41,15 @@ xstrdup(const char *c)
return z;
}
+static inline char *
+lp_strdup(linpool *lp, const char *c)
+{
+ size_t l = strlen(c) + 1;
+ char *z = lp_allocu(lp, l);
+ memcpy(z, c, l);
+ return z;
+}
+
static inline void
memset32(void *D, u32 val, uint n)
{
diff --git a/lib/tbf.c b/lib/tbf.c
index 39e18e57..e6e84b4f 100644
--- a/lib/tbf.c
+++ b/lib/tbf.c
@@ -8,22 +8,30 @@
*/
#include "nest/bird.h"
+#include "lib/timer.h"
-void
-tbf_update(struct tbf *f)
+int
+tbf_limit(struct tbf *f)
{
- bird_clock_t delta = now - f->timestamp;
+ btime delta = current_time() - f->timestamp;
- if (delta == 0)
- return;
-
- f->timestamp = now;
+ if (delta > 0)
+ {
+ u64 next = f->count + delta * f->rate;
+ u64 burst = (u64) f->burst << 20;
+ f->count = MIN(next, burst);
+ f->timestamp += delta;
+ }
- if ((0 < delta) && (delta < f->burst))
+ if (f->count < 1000000)
{
- u32 next = f->count + delta * f->rate;
- f->count = MIN(next, f->burst);
+ f->drop++;
+ return 1;
}
else
- f->count = f->burst;
+ {
+ f->count -= 1000000;
+ f->drop = 0;
+ return 0;
+ }
}
diff --git a/lib/timer.c b/lib/timer.c
new file mode 100644
index 00000000..ed731d26
--- /dev/null
+++ b/lib/timer.c
@@ -0,0 +1,378 @@
+/*
+ * BIRD -- Timers
+ *
+ * (c) 2013--2017 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2013--2017 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: Timers
+ *
+ * Timers are resources which represent a wish of a module to call a function at
+ * the specified time. The timer code does not guarantee exact timing, only that
+ * a timer function will not be called before the requested time.
+ *
+ * In BIRD, time is represented by values of the &btime type which is signed
+ * 64-bit integer interpreted as a relative number of microseconds since some
+ * fixed time point in past. The current time can be obtained by current_time()
+ * function with reasonable accuracy and is monotonic. There is also a current
+ * 'wall-clock' real time obtainable by current_real_time() reported by OS.
+ *
+ * Each timer is described by a &timer structure containing a pointer to the
+ * handler function (@hook), data private to this function (@data), time the
+ * function should be called at (@expires, 0 for inactive timers), for the other
+ * fields see |timer.h|.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "nest/bird.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);
+}
+
+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, \
+ heap[a]->index = (a), heap[b]->index = (b))
+
+
+static void
+tm_free(resource *r)
+{
+ timer *t = (void *) r;
+
+ tm_stop(t);
+}
+
+static void
+tm_dump(resource *r)
+{
+ timer *t = (void *) r;
+
+ debug("(code %p, data %p, ", t->hook, t->data);
+ if (t->randomize)
+ debug("rand %d, ", t->randomize);
+ if (t->recurrent)
+ debug("recur %d, ", t->recurrent);
+ if (t->expires)
+ debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
+ else
+ debug("inactive)\n");
+}
+
+
+static struct resclass tm_class = {
+ "Timer",
+ sizeof(timer),
+ tm_free,
+ tm_dump,
+ NULL,
+ NULL
+};
+
+timer *
+tm_new(pool *p)
+{
+ timer *t = ralloc(p, &tm_class);
+ t->index = -1;
+ return t;
+}
+
+void
+tm_set(timer *t, btime when)
+{
+ struct timeloop *loop = timeloop_current();
+ uint tc = timers_count(loop);
+
+ 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);
+ }
+ else if (t->expires < when)
+ {
+ t->expires = when;
+ HEAP_INCREASE(loop->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);
+ }
+
+#ifdef CONFIG_BFD
+ /* Hack to notify BFD loops */
+ if ((loop != &main_timeloop) && (t->index == 1))
+ wakeup_kick_current();
+#endif
+}
+
+void
+tm_start(timer *t, btime after)
+{
+ tm_set(t, current_time() + MAX(after, 0));
+}
+
+void
+tm_stop(timer *t)
+{
+ if (!t->expires)
+ return;
+
+ struct timeloop *loop = timeloop_current();
+ uint tc = timers_count(loop);
+
+ HEAP_DELETE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
+ BUFFER_POP(loop->timers);
+
+ t->index = -1;
+ t->expires = 0;
+}
+
+void
+timers_init(struct timeloop *loop, pool *p)
+{
+ times_init(loop);
+
+ BUFFER_INIT(loop->timers, p, 4);
+ BUFFER_PUSH(loop->timers) = NULL;
+}
+
+void io_log_event(void *hook, void *data);
+
+void
+timers_fire(struct timeloop *loop)
+{
+ btime base_time;
+ timer *t;
+
+ times_update(loop);
+ base_time = loop->last_time;
+
+ while (t = timers_first(loop))
+ {
+ if (t->expires > base_time)
+ return;
+
+ if (t->recurrent)
+ {
+ btime when = t->expires + t->recurrent;
+
+ if (when <= loop->last_time)
+ when = loop->last_time + t->recurrent;
+
+ if (t->randomize)
+ when += random() % (t->randomize + 1);
+
+ tm_set(t, when);
+ }
+ 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)
+ 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
+ * @x: time string
+ *
+ * tm_parse_time() takes a textual representation of a date and time
+ * (yyyy-mm-dd[ hh:mm:ss[.sss]]) and converts it to the corresponding value of
+ * type &btime.
+ */
+btime
+tm_parse_time(char *x)
+{
+ struct tm tm;
+ int usec, n1, n2, n3, r;
+
+ r = sscanf(x, "%d-%d-%d%n %d:%d:%d%n.%d%n",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &n1,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n2,
+ &usec, &n3);
+
+ if ((r == 3) && !x[n1])
+ tm.tm_hour = tm.tm_min = tm.tm_sec = usec = 0;
+ else if ((r == 6) && !x[n2])
+ usec = 0;
+ else if ((r == 7) && !x[n3])
+ {
+ /* Convert subsecond digits to proper precision */
+ int digits = n3 - n2 - 1;
+ if ((usec < 0) || (usec > 999999) || (digits < 1) || (digits > 6))
+ return 0;
+
+ while (digits++ < 6)
+ usec *= 10;
+ }
+ else
+ return 0;
+
+ tm.tm_mon--;
+ tm.tm_year -= 1900;
+ s64 ts = mktime(&tm);
+ if ((ts == (s64) (time_t) -1) || (ts < 0) || (ts > ((s64) 1 << 40)))
+ return 0;
+
+ return ts S + usec;
+}
+
+/**
+ * tm_format_time - convert date and time to textual representation
+ * @x: destination buffer of size %TM_DATETIME_BUFFER_SIZE
+ * @fmt: specification of resulting textual representation of the time
+ * @t: time
+ *
+ * This function formats the given relative time value @t to a textual
+ * date/time representation (dd-mm-yyyy hh:mm:ss) in real time.
+ */
+void
+tm_format_time(char *x, struct timeformat *fmt, btime t)
+{
+ btime dt = current_time() - t;
+ btime rt = current_real_time() - dt;
+ int v1 = !fmt->limit || (dt < fmt->limit);
+
+ tm_format_real_time(x, v1 ? fmt->fmt1 : fmt->fmt2, rt);
+}
+
+/* Replace %f in format string with usec scaled to requested precision */
+static int
+strfusec(char *buf, int size, const char *fmt, uint usec)
+{
+ char *str = buf;
+ int parity = 0;
+
+ while (*fmt)
+ {
+ if (!size)
+ return 0;
+
+ if ((fmt[0] == '%') && (!parity) &&
+ ((fmt[1] == 'f') || (fmt[1] >= '1') && (fmt[1] <= '6') && (fmt[2] == 'f')))
+ {
+ int digits = (fmt[1] == 'f') ? 6 : (fmt[1] - '0');
+ uint d = digits, u = usec;
+
+ /* Convert microseconds to requested precision */
+ while (d++ < 6)
+ u /= 10;
+
+ int num = bsnprintf(str, size, "%0*u", digits, u);
+ if (num < 0)
+ return 0;
+
+ fmt += (fmt[1] == 'f') ? 2 : 3;
+ ADVANCE(str, size, num);
+ }
+ else
+ {
+ /* Handle '%%' expression */
+ parity = (*fmt == '%') ? !parity : 0;
+ *str++ = *fmt++;
+ size--;
+ }
+ }
+
+ if (!size)
+ return 0;
+
+ *str = 0;
+ return str - buf;
+}
+
+void
+tm_format_real_time(char *x, const char *fmt, btime t)
+{
+ s64 t1 = t TO_S;
+ s64 t2 = t - t1 S;
+
+ time_t ts = t1;
+ struct tm tm;
+ if (!localtime_r(&ts, &tm))
+ goto err;
+
+ byte tbuf[TM_DATETIME_BUFFER_SIZE];
+ if (!strfusec(tbuf, TM_DATETIME_BUFFER_SIZE, fmt, t2))
+ goto err;
+
+ if (!strftime(x, TM_DATETIME_BUFFER_SIZE, tbuf, &tm))
+ goto err;
+
+ return;
+
+err:
+ strcpy(x, "<error>");
+}
diff --git a/lib/timer.h b/lib/timer.h
new file mode 100644
index 00000000..ed8f0d02
--- /dev/null
+++ b/lib/timer.h
@@ -0,0 +1,127 @@
+/*
+ * BIRD -- Timers
+ *
+ * (c) 2013--2017 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2013--2017 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_TIMER_H_
+#define _BIRD_TIMER_H_
+
+#include "nest/bird.h"
+#include "lib/buffer.h"
+#include "lib/resource.h"
+
+
+typedef struct timer
+{
+ resource r;
+ void (*hook)(struct timer *);
+ void *data;
+
+ btime expires; /* 0=inactive */
+ uint randomize; /* Amount of randomization */
+ uint recurrent; /* Timer recurrence */
+
+ int index;
+} timer;
+
+struct timeloop
+{
+ BUFFER_(timer *) timers;
+ btime last_time;
+ btime real_time;
+};
+
+static inline uint timers_count(struct timeloop *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;
+
+btime current_time(void);
+btime current_real_time(void);
+
+//#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);
+void tm_stop(timer *t);
+
+static inline int
+tm_active(timer *t)
+{
+ return t->expires != 0;
+}
+
+static inline btime
+tm_remains(timer *t)
+{
+ btime now_ = current_time();
+ return (t->expires > now_) ? (t->expires - now_) : 0;
+}
+
+static inline timer *
+tm_new_init(pool *p, void (*hook)(struct timer *), void *data, uint rec, uint rand)
+{
+ timer *t = tm_new(p);
+ t->hook = hook;
+ t->data = data;
+ t->recurrent = rec;
+ t->randomize = rand;
+ return t;
+}
+
+static inline void
+tm_set_max(timer *t, btime when)
+{
+ if (when > t->expires)
+ tm_set(t, when);
+}
+
+static inline void
+tm_start_max(timer *t, btime after)
+{
+ btime rem = tm_remains(t);
+ tm_start(t, MAX_(rem, after));
+}
+
+/* In sysdep code */
+void times_init(struct timeloop *loop);
+void times_update(struct timeloop *loop);
+void times_update_real_time(struct timeloop *loop);
+
+/* For I/O loop */
+void timers_init(struct timeloop *loop, pool *p);
+void timers_fire(struct timeloop *loop);
+
+void timer_init(void);
+
+
+struct timeformat {
+ char *fmt1, *fmt2;
+ btime limit;
+};
+
+#define TM_ISO_SHORT_S (struct timeformat){"%T", "%F", (s64) (20*3600) S_}
+#define TM_ISO_SHORT_MS (struct timeformat){"%T.%3f", "%F", (s64) (20*3600) S_}
+#define TM_ISO_SHORT_US (struct timeformat){"%T.%6f", "%F", (s64) (20*3600) S_}
+
+#define TM_ISO_LONG_S (struct timeformat){"%F %T", NULL, 0}
+#define TM_ISO_LONG_MS (struct timeformat){"%F %T.%3f", NULL, 0}
+#define TM_ISO_LONG_US (struct timeformat){"%F %T.%6f", NULL, 0}
+
+#define TM_DATETIME_BUFFER_SIZE 32 /* Buffer size required by tm_format_time() */
+
+btime tm_parse_time(char *x);
+void tm_format_time(char *x, struct timeformat *fmt, btime t);
+void tm_format_real_time(char *x, const char *fmt, btime t);
+
+#endif
diff --git a/lib/unaligned.h b/lib/unaligned.h
index dc777fbf..0da1fdb4 100644
--- a/lib/unaligned.h
+++ b/lib/unaligned.h
@@ -17,6 +17,7 @@
* if possible.
*/
+#include "sysdep/unix/endian.h"
#include "lib/string.h"
static inline u16
@@ -28,6 +29,13 @@ get_u16(const void *p)
}
static inline u32
+get_u24(const void *P)
+{
+ const byte *p = P;
+ return (p[0] << 16) + (p[1] << 8) + p[2];
+}
+
+static inline u32
get_u32(const void *p)
{
u32 x;
@@ -52,6 +60,13 @@ put_u16(void *p, u16 x)
}
static inline void
+put_u24(void *p, u32 x)
+{
+ x = htonl(x);
+ memcpy(p, ((char *) &x) + 1, 3);
+}
+
+static inline void
put_u32(void *p, u32 x)
{
x = htonl(x);
@@ -68,4 +83,22 @@ put_u64(void *p, u64 x)
memcpy(p+4, &xl, 4);
}
+static inline void
+get_u32s(const void *p, u32 *x, int n)
+{
+ int i;
+ memcpy(x, p, 4*n);
+ for (i = 0; i < n; i++)
+ x[i] = ntohl(x[i]);
+}
+
+static inline void
+put_u32s(void *p, const u32 *x, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ put_u32((byte *) p + 4*i, x[i]);
+}
+
+
#endif