summaryrefslogtreecommitdiff
path: root/lib/flowspec.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/flowspec.c')
-rw-r--r--lib/flowspec.c1183
1 files changed, 1183 insertions, 0 deletions
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);
+}