diff options
Diffstat (limited to 'lib')
41 files changed, 6550 insertions, 342 deletions
@@ -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..0c352869 --- /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 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 d21cdf1f..317b5202 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -9,8 +9,8 @@ #ifndef _BIRD_BIRDLIB_H_ #define _BIRD_BIRDLIB_H_ -#include "timer.h" -#include "alloca.h" +#include "sysdep/unix/timer.h" +#include "lib/alloca.h" /* Ugly structure offset handling macros */ @@ -37,6 +37,15 @@ struct align_probe { char x; long int y; }; #define ABS(a) ((a)>=0 ? (a) : -(a)) #define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a)) #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) +#define BYTES(n) ((((uint) (n)) + 7) / 8) +#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 */ @@ -52,27 +61,12 @@ 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; @@ -160,7 +154,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 */ 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..6fc18852 100644 --- a/lib/buffer.h +++ b/lib/buffer.h @@ -14,7 +14,7 @@ #include "sysdep/config.h" #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)) #define BUFFER_INIT(v,pool,isize) \ @@ -46,4 +46,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..92172389 --- /dev/null +++ b/lib/event_test.c @@ -0,0 +1,88 @@ +/* + * 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(); + 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..0b863ed9 --- /dev/null +++ b/lib/flowspec.c @@ -0,0 +1,1183 @@ +/* + * 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 |= 0x40; + } + + 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 */ +#define FLOW_TRUE 0b000 +#define FLOW_EQ 0b001 +#define FLOW_GT 0b010 +#define FLOW_GTE 0b011 +#define FLOW_LT 0b100 +#define FLOW_LTE 0b101 +#define FLOW_NEQ 0b110 +#define FLOW_FALSE 0b111 + +static const char * +num_op_str(const byte *op) +{ + switch (*op & 0x07) + { + case FLOW_TRUE: return "true"; + case FLOW_EQ: return "="; + case FLOW_GT: return ">"; + case FLOW_GTE: return ">="; + case FLOW_LT: return "<"; + case FLOW_LTE: return "<="; + case FLOW_NEQ: return "!="; + case FLOW_FALSE: return "false"; + } + + return NULL; +} + +static u64 +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); + 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; + u64 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_EQ) || (num_op( op) == FLOW_GTE)) && + ((num_op(last_op) == FLOW_EQ) || (num_op(last_op) == FLOW_LTE))) + { + 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_GTE) && (num_op(op+1+len) == FLOW_LTE)) + { + /* 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_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; + u64 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..185d5a1c --- /dev/null +++ b/lib/flowspec.h @@ -0,0 +1,138 @@ +/* + * 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" + + +/* 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..69bc279d --- /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 const *under240 = &f->data[1]; + const byte const *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(); +} @@ -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 @@ -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)) @@ -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..3cf9c2d3 100644 --- a/lib/mempool.c +++ b/lib/mempool.c @@ -32,6 +32,8 @@ struct lp_chunk { byte data[0]; }; +const int lp_chunk_size = sizeof(struct lp_chunk); + struct linpool { resource r; byte *ptr, *end; diff --git a/lib/net.c b/lib/net.c new file mode 100644 index 00000000..a00ff272 --- /dev/null +++ b/lib/net.c @@ -0,0 +1,290 @@ + +#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); +} diff --git a/lib/net.h b/lib/net.h new file mode 100644 index 00000000..332f4c9a --- /dev/null +++ b/lib/net.h @@ -0,0 +1,533 @@ +/* + * 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[16]; + 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; + 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, 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_roa(const net_addr *a) +{ return (a->type == NET_ROA4) || (a->type == NET_ROA6); } + +static inline int net_is_vpn(const net_addr *a) +{ return (a->type == NET_VPN4) || (a->type == NET_VPN6); } + + +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->data; } + +static inline int net_zero_flow6(const net_addr_flow6 *a) +{ return !a->pxlen && ip6_zero(a->prefix) && !a->data; } + +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); + +int ipa_in_netX(const ip_addr A, const net_addr *N); +int net_in_netX(const net_addr *A, const net_addr *N); + + +#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..8e3cbbcf 100644 --- a/lib/printf.c +++ b/lib/printf.c @@ -118,16 +118,16 @@ 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) 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. @@ -141,7 +141,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) u64 X; 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 +158,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) *str++ = *fmt; continue; } - + /* process flags */ flags = 0; repeat: @@ -170,7 +170,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 +188,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 +238,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) @@ -284,14 +292,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 +340,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,11 +355,7 @@ 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; @@ -442,6 +467,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 +482,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 +496,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..3981d3da --- /dev/null +++ b/lib/printf_test.c @@ -0,0 +1,70 @@ +/* + * 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); + + 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..761c6adc 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 */ @@ -65,6 +65,11 @@ void *lp_allocu(linpool *, unsigned size); /* Unaligned */ void *lp_allocz(linpool *, unsigned size); /* With clear */ void lp_flush(linpool *); /* Free everything, but leave linpool */ +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 */ typedef struct slab slab; 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 2da52127..d5281b83 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 */ @@ -45,14 +69,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 */ @@ -68,19 +93,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 */ @@ -99,7 +117,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 */ @@ -123,26 +140,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/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 |