summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2016-12-07 15:36:15 +0100
committerOndrej Zajicek (work) <santiago@crfreenet.org>2016-12-07 15:54:19 +0100
commit77234bbbde6bc328871af695e4450e6773adbafa (patch)
tree0ed60508b521eba6af6c4b852df09fdf8c659154 /lib
parentb94e5e58dbd33f4d2b9d721c51a9c8c4d8f77bea (diff)
Basic flow specification support (RFC 5575)
Add flow4/flow6 network and rt-table type and operations, config grammar and static protocol support. Squashed flowspec branch from Pavel Tvrdik.
Diffstat (limited to 'lib')
-rw-r--r--lib/Doc1
-rw-r--r--lib/Makefile4
-rw-r--r--lib/birdlib.h1
-rw-r--r--lib/buffer.h7
-rw-r--r--lib/flowspec.c1148
-rw-r--r--lib/flowspec.h134
-rw-r--r--lib/flowspec_test.c644
-rw-r--r--lib/net.c77
-rw-r--r--lib/net.h79
-rw-r--r--lib/printf.c15
10 files changed, 2078 insertions, 32 deletions
diff --git a/lib/Doc b/lib/Doc
index 632bf1f6..3877f3a3 100644
--- a/lib/Doc
+++ b/lib/Doc
@@ -3,6 +3,7 @@ S ip.c
S lists.c
S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c
S mac.c
+S flowspec.c
D resource.sgml
S resource.c
S mempool.c
diff --git a/lib/Makefile b/lib/Makefile
index a7da9802..acfe81ac 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -2,10 +2,10 @@ src := bitops.c checksum.c ip.c lists.c md5.c net.c patmatch.c printf.c sha1.c s
obj := $(src-o-files)
$(all-client)
-src := bitops.c checksum.c event.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
+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 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_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/birdlib.h b/lib/birdlib.h
index bb19df54..c82082c1 100644
--- a/lib/birdlib.h
+++ b/lib/birdlib.h
@@ -34,6 +34,7 @@
#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; })
diff --git a/lib/buffer.h b/lib/buffer.h
index 2a53f211..a8b11951 100644
--- a/lib/buffer.h
+++ b/lib/buffer.h
@@ -46,4 +46,11 @@
#define BUFFER_FLUSH(v) ({ (v).used = 0; })
+#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/flowspec.c b/lib/flowspec.c
new file mode 100644
index 00000000..b72bc7fc
--- /dev/null
+++ b/lib/flowspec.c
@@ -0,0 +1,1148 @@
+/*
+ * 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 required to define",
+ [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_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;
+ }
+
+ /* Bit-7 must be 0 [draft-ietf-idr-flow-spec-v6] */
+ if (ipv6 && type == FLOW_TYPE_FRAGMENT && (*(pos+1) & 0x01))
+ return FLOW_ST_CANNOT_USE_DONT_FRAGMENT;
+ /* XXX: Could be a fragment component encoded in 2-bytes? */
+
+ /* 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;
+ 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 int
+is_bitmask(enum flow_type type)
+{
+ switch (type)
+ {
+ case FLOW_TYPE_TCP_FLAGS:
+ case FLOW_TYPE_FRAGMENT:
+ case FLOW_TYPE_LABEL:
+ return 1;
+
+ default:
+ 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 int
+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:
+ {
+ 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);
+ }
+ 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:
+ {
+ 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) && !is_bitmask(*part) &&
+ ((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 if (isset_and(op) && is_bitmask(*part))
+ {
+ 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 (is_bitmask(*part))
+ {
+ /*
+ * 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);
+ }
+ else
+ {
+ 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;
+ }
+ }
+ }
+
+ 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.
+ */
+int
+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.
+ */
+int
+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..57809bec
--- /dev/null
+++ b/lib/flowspec.h
@@ -0,0 +1,134 @@
+/*
+ * 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_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_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
+ */
+
+int flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f);
+int 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..93364dfe
--- /dev/null
+++ b/lib/flowspec_test.c
@@ -0,0 +1,644 @@
+/*
+ * 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 };
+
+ u16 get;
+ u16 expect;
+
+ for (uint expect = 0; expect < 0xf0; expect++)
+ {
+ *data = expect;
+ 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);
+ 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 };
+ uint offset;
+ byte *c;
+
+ for (uint expect = 0; expect <= 0xfff; expect++)
+ {
+ 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(&root_pool, 4096);
+
+ /* 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(&root_pool, 4096);
+ struct flow_builder *fb = flow_builder_init(&root_pool);
+ fb->ipv6 = 1;
+
+ /* Expectation */
+
+ byte nlri[] = {
+ 27,
+ FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+ FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+ FLOW_TYPE_NEXT_HEADER, 0x80, 0x06,
+ FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+ FLOW_TYPE_LABEL, 0x80, 0x55,
+ };
+
+ net_addr_flow6 *expect;
+ NET_ADDR_FLOW6_(expect, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
+
+ /* Normal order */
+
+ net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+ flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+ flow_builder6_add_pfx(fb, &ip, 61);
+
+ /* Try put a component twice time */
+ net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+ flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+ bt_assert(flow_builder6_add_pfx(fb, &ip, 61) == 0);
+
+ net_fill_ip6((net_addr *) &ip, ip6_build(0xc0000000,0,0,0), 8);
+ flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+ flow_builder6_add_pfx(fb, &ip, 0);
+
+ flow_builder_set_type(fb, FLOW_TYPE_NEXT_HEADER);
+ flow_builder_add_op_val(fb, 0, 0x06);
+
+ flow_builder_set_type(fb, FLOW_TYPE_PORT);
+ flow_builder_add_op_val(fb, 0x03, 0x89);
+ flow_builder_add_op_val(fb, 0x45, 0x8b);
+ flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+ flow_builder_set_type(fb, FLOW_TYPE_LABEL);
+ flow_builder_add_op_val(fb, 0, 0x55);
+
+ net_addr_flow6 *res = flow_builder6_finalize(fb, lp);
+ bt_assert(memcmp(res, expect, expect->length) == 0);
+
+ /* Reverse order */
+
+ flow_builder_clear(fb);
+ fb->ipv6 = 1;
+
+ flow_builder_set_type(fb, FLOW_TYPE_LABEL);
+ flow_builder_add_op_val(fb, 0, 0x55);
+
+ flow_builder_set_type(fb, FLOW_TYPE_PORT);
+ flow_builder_add_op_val(fb, 0x03, 0x89);
+ flow_builder_add_op_val(fb, 0x45, 0x8b);
+ flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+ flow_builder_set_type(fb, FLOW_TYPE_NEXT_HEADER);
+ flow_builder_add_op_val(fb, 0, 0x06);
+
+ net_fill_ip6((net_addr *) &ip, ip6_build(0xc0000000,0,0,0), 8);
+ flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+ flow_builder6_add_pfx(fb, &ip, 0);
+
+ net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+ flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+ flow_builder6_add_pfx(fb, &ip, 61);
+
+ res = flow_builder6_finalize(fb, lp);
+ bt_assert(memcmp(res, expect, expect->length) == 0);
+
+ return 1;
+}
+
+static int
+t_formatting4(void)
+{
+ char b[1024];
+
+ byte nlri[] = {
+ 0,
+ FLOW_TYPE_DST_PREFIX, 0x08, 10,
+ FLOW_TYPE_IP_PROTOCOL, 0x81, 23,
+ FLOW_TYPE_DST_PORT, 0x02, 24, 0x44, 30, 0x03, 40, 0x45, 50, 0x03, 60, 0x45, 70, 0x01, 80, 0xc3, 90,
+ FLOW_TYPE_SRC_PORT, 0x02, 24, 0x44, 0x1e, 0x01, 0x28, 0x01, 0x32, 0x03, 0x3c, 0x45, 0x46, 0x81, 0x50,
+ FLOW_TYPE_ICMP_TYPE, 0x81, 0x50,
+ FLOW_TYPE_ICMP_CODE, 0x81, 0x5a,
+ FLOW_TYPE_TCP_FLAGS, 0x01, 0x03, 0xc2, 0x0c,
+ FLOW_TYPE_PACKET_LENGTH, 0x03, 0, 0xd5, 0xff, 0xff,
+ FLOW_TYPE_DSCP, 0x81, 63,
+ FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02
+ };
+ *nlri = (u8) sizeof(nlri);
+
+ net_addr_flow4 *input;
+ NET_ADDR_FLOW4_(input, ip4_build(5, 6, 7, 0), 24, nlri);
+
+ const char *expect = "flow4 { dst 10.0.0.0/8; proto 23; dport > 24 && < 30 || 40..50,60..70,80 && >= 90; sport > 24 && < 30 || 40,50,60..70,80; icmp type 80; icmp code 90; tcp flags 0x3/0x3,0x0/0xc; length 0..65535; dscp 63; fragment dont_fragment || !is_fragment; }";
+
+ bt_assert(flow4_net_format(b, sizeof(b), input) == strlen(expect));
+ bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
+ bt_assert(strcmp(b, expect) == 0);
+
+ return 1;
+}
+
+static int
+t_formatting6(void)
+{
+ char b[1024];
+
+ byte nlri[] = {
+ 0,
+ FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+ FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+ FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+ FLOW_TYPE_PORT, 0x03, 20, 0x45, 40, 0x91, 0x01, 0x11,
+ FLOW_TYPE_LABEL, 0xa0, 0x12, 0x34, 0x56, 0x78,
+ };
+ *nlri = (u8) sizeof(nlri);
+
+ net_addr_flow6 *input;
+ NET_ADDR_FLOW6_(input, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
+
+ const char *expect = "flow6 { dst ::1:1234:5678:9800:0/103 offset 61; src c000::/8; next header 6; port 20..40,273; label !0x0/0x12345678; }";
+
+ bt_assert(flow6_net_format(b, sizeof(b), input) == strlen(expect));
+ bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
+ bt_assert(strcmp(b, expect) == 0);
+
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ bt_init(argc, argv);
+
+ bt_test_suite(t_read_length, "Testing get NLRI length");
+ bt_test_suite(t_write_length, "Testing set NLRI length");
+ bt_test_suite(t_first_part, "Searching first part in net_addr_flow");
+ bt_test_suite(t_iterators4, "Testing iterators (IPv4)");
+ bt_test_suite(t_iterators6, "Testing iterators (IPv6)");
+ bt_test_suite(t_validation4, "Testing validation (IPv4)");
+ bt_test_suite(t_validation6, "Testing validation (IPv6)");
+ bt_test_suite(t_builder4, "Inserting components into existing Flow Specification (IPv4)");
+ bt_test_suite(t_builder6, "Inserting components into existing Flow Specification (IPv6)");
+ bt_test_suite(t_formatting4, "Formatting Flow Specification (IPv4) into text representation");
+ bt_test_suite(t_formatting6, "Formatting Flow Specification (IPv6) into text representation");
+
+ return bt_exit_value();
+}
diff --git a/lib/net.c b/lib/net.c
index 53991b4c..f283e79f 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -2,42 +2,51 @@
#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_IP4] = "ipv4",
+ [NET_IP6] = "ipv6",
+ [NET_VPN4] = "vpn4",
+ [NET_VPN6] = "vpn6",
+ [NET_ROA4] = "roa4",
+ [NET_ROA6] = "roa6",
+ [NET_FLOW4] = "flow4",
+ [NET_FLOW6] = "flow6"
};
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_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
};
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_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
};
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_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 { ... }" */
};
@@ -60,6 +69,10 @@ net_format(const net_addr *N, char *buf, int buflen)
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);
}
return 0;
@@ -73,11 +86,13 @@ net_pxmask(const net_addr *a)
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)));
default:
@@ -105,6 +120,10 @@ net_compare(const net_addr *a, const net_addr *b)
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);
}
return 0;
}
@@ -122,6 +141,8 @@ net_hash(const net_addr *n)
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);
default: bug("invalid type");
}
}
@@ -135,11 +156,13 @@ net_validate(const net_addr *N)
case NET_IP4:
case NET_VPN4:
case NET_ROA4:
+ case NET_FLOW4:
return net_validate_ip4((net_addr_ip4 *) N);
case NET_IP6:
case NET_VPN6:
case NET_ROA6:
+ case NET_FLOW6:
return net_validate_ip6((net_addr_ip6 *) N);
default:
@@ -157,11 +180,13 @@ net_normalize(net_addr *N)
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);
}
}
@@ -176,11 +201,13 @@ net_classify(const net_addr *N)
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);
}
@@ -195,6 +222,7 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
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))));
@@ -202,6 +230,7 @@ ipa_in_netX(const ip_addr a, const net_addr *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))));
diff --git a/lib/net.h b/lib/net.h
index e8310894..f07e6b24 100644
--- a/lib/net.h
+++ b/lib/net.h
@@ -19,7 +19,9 @@
#define NET_VPN6 4
#define NET_ROA4 5
#define NET_ROA6 6
-#define NET_MAX 7
+#define NET_FLOW4 7
+#define NET_FLOW6 8
+#define NET_MAX 9
#define NB_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6)
@@ -27,6 +29,8 @@
#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_IP (NB_IP4 | NB_IP6)
#define NB_ANY 0xffffffff
@@ -88,6 +92,22 @@ typedef struct net_addr_roa6 {
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 union net_addr_union {
net_addr n;
net_addr_ip4 ip4;
@@ -96,6 +116,8 @@ typedef union net_addr_union {
net_addr_vpn6 vpn6;
net_addr_roa4 roa4;
net_addr_roa6 roa6;
+ net_addr_flow4 flow4;
+ net_addr_flow6 flow6;
} net_addr_union;
@@ -104,7 +126,7 @@ 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 65
+#define NET_MAX_TEXT_LENGTH 256
#define NET_ADDR_IP4(prefix,pxlen) \
@@ -125,6 +147,12 @@ extern const u16 net_max_text_length[];
#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 })
+
static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
@@ -161,6 +189,19 @@ static inline void net_fill_ip_host(net_addr *a, ip_addr prefix)
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); }
@@ -188,11 +229,13 @@ static inline ip_addr net_prefix(const net_addr *a)
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));
default:
@@ -233,6 +276,13 @@ static inline int net_equal_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
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_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
@@ -258,6 +308,13 @@ static inline int net_zero_roa4(const net_addr_roa4 *a)
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_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); }
@@ -277,6 +334,12 @@ static inline int net_compare_roa4(const net_addr_roa4 *a, const net_addr_roa4 *
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)); }
+
int net_compare(const net_addr *a, const net_addr *b);
@@ -301,6 +364,12 @@ static inline void net_copy_roa4(net_addr_roa4 *dst, const net_addr_roa4 *src)
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 u32 net_hash_ip4(const net_addr_ip4 *n)
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
@@ -324,6 +393,12 @@ static inline u32 net_hash_roa4(const net_addr_roa4 *n)
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); }
+
u32 net_hash(const net_addr *a);
diff --git a/lib/printf.c b/lib/printf.c
index 1632b5f3..8e3cbbcf 100644
--- a/lib/printf.c
+++ b/lib/printf.c
@@ -467,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;
}
@@ -478,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;
}
@@ -489,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;
}