mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1177 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1177 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *	BIRD Library -- Flow specification (RFC 5575)
 | 
						|
 *
 | 
						|
 *	(c) 2016 CZ.NIC z.s.p.o.
 | 
						|
 *
 | 
						|
 *	Can be freely distributed and used under the terms of the GNU GPL.
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * DOC: Flow specification (flowspec)
 | 
						|
 *
 | 
						|
 * Flowspec are rules (RFC 5575) for firewalls disseminated using BGP protocol.
 | 
						|
 * The |flowspec.c| is a library for handling flowspec binary streams and
 | 
						|
 * flowspec data structures. You will find there functions for validation
 | 
						|
 * incoming flowspec binary streams, iterators for jumping over components,
 | 
						|
 * functions for handling a length and functions for formatting flowspec data
 | 
						|
 * structure into user-friendly text representation.
 | 
						|
 *
 | 
						|
 * In this library, you will find also flowspec builder. In |confbase.Y|, there
 | 
						|
 * are grammar's rules for parsing and building new flowspec data structure
 | 
						|
 * from BIRD's configuration files and from BIRD's command line interface.
 | 
						|
 * Finalize function will assemble final &net_addr_flow4 or &net_addr_flow6
 | 
						|
 * data structure.
 | 
						|
 *
 | 
						|
 * The data structures &net_addr_flow4 and &net_addr_flow6 are defined in
 | 
						|
 * |net.h| file. The attribute length is size of whole data structure plus
 | 
						|
 * binary stream representation of flowspec including a compressed encoded
 | 
						|
 * length of flowspec.
 | 
						|
 *
 | 
						|
 * Sometimes in code, it is used expression flowspec type, it should mean
 | 
						|
 * flowspec component type.
 | 
						|
 */
 | 
						|
 | 
						|
#include "nest/bird.h"
 | 
						|
#include "lib/flowspec.h"
 | 
						|
#include "conf/conf.h"
 | 
						|
 | 
						|
 | 
						|
static const char* flow4_type_str[] = {
 | 
						|
  [FLOW_TYPE_DST_PREFIX]	= "dst",
 | 
						|
  [FLOW_TYPE_SRC_PREFIX]	= "src",
 | 
						|
  [FLOW_TYPE_IP_PROTOCOL]	= "proto",
 | 
						|
  [FLOW_TYPE_PORT]		= "port",
 | 
						|
  [FLOW_TYPE_DST_PORT]		= "dport",
 | 
						|
  [FLOW_TYPE_SRC_PORT]		= "sport",
 | 
						|
  [FLOW_TYPE_ICMP_TYPE]		= "icmp type",
 | 
						|
  [FLOW_TYPE_ICMP_CODE]		= "icmp code",
 | 
						|
  [FLOW_TYPE_TCP_FLAGS]		= "tcp flags",
 | 
						|
  [FLOW_TYPE_PACKET_LENGTH]	= "length",
 | 
						|
  [FLOW_TYPE_DSCP]		= "dscp",
 | 
						|
  [FLOW_TYPE_FRAGMENT]		= "fragment"
 | 
						|
};
 | 
						|
 | 
						|
static const char* flow6_type_str[] = {
 | 
						|
  [FLOW_TYPE_DST_PREFIX]	= "dst",
 | 
						|
  [FLOW_TYPE_SRC_PREFIX]	= "src",
 | 
						|
  [FLOW_TYPE_NEXT_HEADER]	= "next header",
 | 
						|
  [FLOW_TYPE_PORT]		= "port",
 | 
						|
  [FLOW_TYPE_DST_PORT]		= "dport",
 | 
						|
  [FLOW_TYPE_SRC_PORT]		= "sport",
 | 
						|
  [FLOW_TYPE_ICMP_TYPE]		= "icmp type",
 | 
						|
  [FLOW_TYPE_ICMP_CODE]		= "icmp code",
 | 
						|
  [FLOW_TYPE_TCP_FLAGS]		= "tcp flags",
 | 
						|
  [FLOW_TYPE_PACKET_LENGTH]	= "length",
 | 
						|
  [FLOW_TYPE_DSCP]		= "dscp",
 | 
						|
  [FLOW_TYPE_FRAGMENT]		= "fragment",
 | 
						|
  [FLOW_TYPE_LABEL]		= "label"
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_type_str - get stringified flowspec name of component
 | 
						|
 * @type: flowspec component type
 | 
						|
 * @ipv6: IPv4/IPv6 decide flag, use zero for IPv4 and one for IPv6
 | 
						|
 *
 | 
						|
 * This function returns flowspec name of component @type in string.
 | 
						|
 */
 | 
						|
const char *
 | 
						|
flow_type_str(enum flow_type type, int ipv6)
 | 
						|
{
 | 
						|
  return ipv6 ? flow6_type_str[type] : flow4_type_str[type];
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 	Length
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_write_length - write compressed length value
 | 
						|
 * @data: destination buffer to write
 | 
						|
 * @len: the value of the length (0 to 0xfff) for writing
 | 
						|
 *
 | 
						|
 * This function writes appropriate as (1- or 2-bytes) the value of @len into
 | 
						|
 * buffer @data. The function returns number of written bytes, thus 1 or 2 bytes.
 | 
						|
 */
 | 
						|
uint
 | 
						|
flow_write_length(byte *data, u16 len)
 | 
						|
{
 | 
						|
  if (len >= 0xf0)
 | 
						|
  {
 | 
						|
    put_u16(data, len | 0xf000);
 | 
						|
    return 2;
 | 
						|
  }
 | 
						|
 | 
						|
  *data = len;
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
inline static uint
 | 
						|
get_value_length(const byte *op)
 | 
						|
{
 | 
						|
  return (1 << ((*op & 0x30) >> 4));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 *	Flowspec iterators
 | 
						|
 */
 | 
						|
 | 
						|
static inline u8  num_op(const byte *op)    { return  (*op & 0x07); }
 | 
						|
static inline int isset_and(const byte *op) { return ((*op & 0x40) == 0x40); }
 | 
						|
static inline int isset_end(const byte *op) { return ((*op & 0x80) == 0x80); }
 | 
						|
 | 
						|
static const byte *
 | 
						|
flow_first_part(const byte *data)
 | 
						|
{
 | 
						|
  if (!data || flow_read_length(data) == 0)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  /* It is allowed to encode the value of length less then 240 into 2-bytes too */
 | 
						|
  if ((data[0] & 0xf0) == 0xf0)
 | 
						|
    return data + 2;
 | 
						|
 | 
						|
  return data + 1;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow4_first_part - get position of the first flowspec component
 | 
						|
 * @f: flowspec data structure &net_addr_flow4
 | 
						|
 *
 | 
						|
 * This function return a position to the beginning of the first flowspec
 | 
						|
 * component in IPv4 flowspec @f.
 | 
						|
 */
 | 
						|
inline const byte *
 | 
						|
flow4_first_part(const net_addr_flow4 *f)
 | 
						|
{
 | 
						|
  return f ? flow_first_part(f->data) : NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow6_first_part - get position of the first flowspec component
 | 
						|
 * @f: flowspec data structure &net_addr_flow6
 | 
						|
 *
 | 
						|
 * This function return a position to the beginning of the first flowspec
 | 
						|
 * component in IPv6 flowspec @f.
 | 
						|
 */
 | 
						|
inline const byte *
 | 
						|
flow6_first_part(const net_addr_flow6 *f)
 | 
						|
{
 | 
						|
  return f ? flow_first_part(f->data) : NULL;
 | 
						|
}
 | 
						|
 | 
						|
static const byte *
 | 
						|
flow_next_part(const byte *pos, const byte *end, int ipv6)
 | 
						|
{
 | 
						|
  switch (*pos++)
 | 
						|
  {
 | 
						|
  case FLOW_TYPE_DST_PREFIX:
 | 
						|
  case FLOW_TYPE_SRC_PREFIX:
 | 
						|
  {
 | 
						|
    uint pxlen = *pos++;
 | 
						|
    uint bytes = BYTES(pxlen);
 | 
						|
    if (ipv6)
 | 
						|
    {
 | 
						|
      uint offset = *pos++ / 8;
 | 
						|
      pos += bytes - offset;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      pos += bytes;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
 | 
						|
  case FLOW_TYPE_PORT:
 | 
						|
  case FLOW_TYPE_DST_PORT:
 | 
						|
  case FLOW_TYPE_SRC_PORT:
 | 
						|
  case FLOW_TYPE_ICMP_TYPE:
 | 
						|
  case FLOW_TYPE_ICMP_CODE:
 | 
						|
  case FLOW_TYPE_TCP_FLAGS:
 | 
						|
  case FLOW_TYPE_PACKET_LENGTH:
 | 
						|
  case FLOW_TYPE_DSCP:
 | 
						|
  case FLOW_TYPE_FRAGMENT:
 | 
						|
  case FLOW_TYPE_LABEL:
 | 
						|
  {
 | 
						|
    /* Is this the end of list operator-value pair? */
 | 
						|
    uint last = 0;
 | 
						|
 | 
						|
    while (!last)
 | 
						|
    {
 | 
						|
      last = isset_end(pos);
 | 
						|
 | 
						|
      /* Value length of operator */
 | 
						|
      uint len = get_value_length(pos);
 | 
						|
      pos += 1+len;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  default:
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  return (pos < end) ? pos : NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow4_next_part - an iterator over flowspec components in flowspec binary stream
 | 
						|
 * @pos: the beginning of a previous or the first component in flowspec binary
 | 
						|
 *       stream
 | 
						|
 * @end: the last valid byte in scanned flowspec binary stream
 | 
						|
 *
 | 
						|
 * This function returns a position to the beginning of the next component
 | 
						|
 * (to a component type byte) in flowspec binary stream or %NULL for the end.
 | 
						|
 */
 | 
						|
inline const byte *
 | 
						|
flow4_next_part(const byte *pos, const byte *end)
 | 
						|
{
 | 
						|
  return flow_next_part(pos, end, 0);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow6_next_part - an iterator over flowspec components in flowspec binary stream
 | 
						|
 * @pos: the beginning of a previous or the first component in flowspec binary
 | 
						|
 *       stream
 | 
						|
 * @end: the last valid byte in scanned flowspec binary stream
 | 
						|
 *
 | 
						|
 * This function returns a position to the beginning of the next component
 | 
						|
 * (to a component type byte) in flowspec binary stream or %NULL for the end.
 | 
						|
 */
 | 
						|
inline const byte *
 | 
						|
flow6_next_part(const byte *pos, const byte *end)
 | 
						|
{
 | 
						|
  return flow_next_part(pos, end, 1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * 	Flowspec validation
 | 
						|
 */
 | 
						|
 | 
						|
static const char* flow_validated_state_str_[] = {
 | 
						|
  [FLOW_ST_UNKNOWN_COMPONENT] 		= "Unknown component",
 | 
						|
  [FLOW_ST_VALID] 			= "Valid",
 | 
						|
  [FLOW_ST_NOT_COMPLETE] 		= "Not complete",
 | 
						|
  [FLOW_ST_EXCEED_MAX_PREFIX_LENGTH] 	= "Exceed maximal prefix length",
 | 
						|
  [FLOW_ST_EXCEED_MAX_PREFIX_OFFSET]	= "Exceed maximal prefix offset",
 | 
						|
  [FLOW_ST_EXCEED_MAX_VALUE_LENGTH]	= "Exceed maximal value length",
 | 
						|
  [FLOW_ST_BAD_TYPE_ORDER] 		= "Bad component order",
 | 
						|
  [FLOW_ST_AND_BIT_SHOULD_BE_UNSET] 	= "The AND-bit should be unset",
 | 
						|
  [FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED] 	= "The Zero-bit should be unset",
 | 
						|
  [FLOW_ST_DEST_PREFIX_REQUIRED] 	= "Destination prefix is missing",
 | 
						|
  [FLOW_ST_INVALID_TCP_FLAGS]		= "TCP flags exceeding 0xfff",
 | 
						|
  [FLOW_ST_CANNOT_USE_DONT_FRAGMENT]    = "Cannot use Don't fragment flag in IPv6 flow"
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_validated_state_str - return a textual description of validation process
 | 
						|
 * @code: validation result
 | 
						|
 *
 | 
						|
 * This function return well described validation state in string.
 | 
						|
 */
 | 
						|
const char *
 | 
						|
flow_validated_state_str(enum flow_validated_state code)
 | 
						|
{
 | 
						|
  return flow_validated_state_str_[code];
 | 
						|
}
 | 
						|
 | 
						|
static const u8 flow4_max_value_length[] = {
 | 
						|
  [FLOW_TYPE_DST_PREFIX]	= 0,
 | 
						|
  [FLOW_TYPE_SRC_PREFIX]	= 0,
 | 
						|
  [FLOW_TYPE_IP_PROTOCOL]	= 1,
 | 
						|
  [FLOW_TYPE_PORT]		= 2,
 | 
						|
  [FLOW_TYPE_DST_PORT]		= 2,
 | 
						|
  [FLOW_TYPE_SRC_PORT]		= 2,
 | 
						|
  [FLOW_TYPE_ICMP_TYPE]		= 1,
 | 
						|
  [FLOW_TYPE_ICMP_CODE]		= 1,
 | 
						|
  [FLOW_TYPE_TCP_FLAGS]		= 2,
 | 
						|
  [FLOW_TYPE_PACKET_LENGTH]	= 2,
 | 
						|
  [FLOW_TYPE_DSCP]		= 1,
 | 
						|
  [FLOW_TYPE_FRAGMENT]		= 1	/* XXX */
 | 
						|
};
 | 
						|
 | 
						|
static const u8 flow6_max_value_length[] = {
 | 
						|
  [FLOW_TYPE_DST_PREFIX]	= 0,
 | 
						|
  [FLOW_TYPE_SRC_PREFIX]	= 0,
 | 
						|
  [FLOW_TYPE_NEXT_HEADER]	= 1,
 | 
						|
  [FLOW_TYPE_PORT]		= 2,
 | 
						|
  [FLOW_TYPE_DST_PORT]		= 2,
 | 
						|
  [FLOW_TYPE_SRC_PORT]		= 2,
 | 
						|
  [FLOW_TYPE_ICMP_TYPE]		= 1,
 | 
						|
  [FLOW_TYPE_ICMP_CODE]		= 1,
 | 
						|
  [FLOW_TYPE_TCP_FLAGS]		= 2,
 | 
						|
  [FLOW_TYPE_PACKET_LENGTH]	= 2,
 | 
						|
  [FLOW_TYPE_DSCP]		= 1,
 | 
						|
  [FLOW_TYPE_FRAGMENT]		= 1,	/* XXX */
 | 
						|
  [FLOW_TYPE_LABEL]		= 4
 | 
						|
};
 | 
						|
 | 
						|
static u8
 | 
						|
flow_max_value_length(enum flow_type type, int ipv6)
 | 
						|
{
 | 
						|
  return ipv6 ? flow6_max_value_length[type] : flow4_max_value_length[type];
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_check_cf_bmk_values - check value/bitmask part of flowspec component
 | 
						|
 * @fb: flow builder instance
 | 
						|
 * @neg: negation operand
 | 
						|
 * @val: value from value/mask pair
 | 
						|
 * @mask: bitmap mask from value/mask pair
 | 
						|
 *
 | 
						|
 * This function checks value/bitmask pair. If some problem will appear, the
 | 
						|
 * function calls cf_error() function with a textual description of reason
 | 
						|
 * to failing of validation.
 | 
						|
 */
 | 
						|
void
 | 
						|
flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mask)
 | 
						|
{
 | 
						|
  flow_check_cf_value_length(fb, val);
 | 
						|
  flow_check_cf_value_length(fb, mask);
 | 
						|
 | 
						|
  if (neg && !(val == 0 || val == mask))
 | 
						|
    cf_error("For negation, value must be zero or bitmask");
 | 
						|
 | 
						|
  if ((fb->this_type == FLOW_TYPE_TCP_FLAGS) && (mask & 0xf000))
 | 
						|
    cf_error("Invalid mask 0x%x, must not exceed 0xfff", mask);
 | 
						|
 | 
						|
  if ((fb->this_type == FLOW_TYPE_FRAGMENT) && fb->ipv6 && (mask & 0x01))
 | 
						|
    cf_error("Invalid mask 0x%x, bit 0 must be 0", mask);
 | 
						|
 | 
						|
  if (val & ~mask)
 | 
						|
    cf_error("Value 0x%x outside bitmask 0x%x", val, mask);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_check_cf_value_length - check value by flowspec component type
 | 
						|
 * @fb: flow builder instance
 | 
						|
 * @val: value
 | 
						|
 *
 | 
						|
 * This function checks if the value is in range of component's type support.
 | 
						|
 * If some problem will appear, the function calls cf_error() function with
 | 
						|
 * a textual description of reason to failing of validation.
 | 
						|
 */
 | 
						|
void
 | 
						|
flow_check_cf_value_length(struct flow_builder *fb, u32 val)
 | 
						|
{
 | 
						|
  enum flow_type t = fb->this_type;
 | 
						|
  u8 max = flow_max_value_length(t, fb->ipv6);
 | 
						|
 | 
						|
  if (t == FLOW_TYPE_DSCP && val > 0x3f)
 | 
						|
    cf_error("%s value %u out of range (0-63)", flow_type_str(t, fb->ipv6), val);
 | 
						|
 | 
						|
  if (max == 1 && (val > 0xff))
 | 
						|
    cf_error("%s value %u out of range (0-255)", flow_type_str(t, fb->ipv6), val);
 | 
						|
 | 
						|
  if (max == 2 && (val > 0xffff))
 | 
						|
    cf_error("%s value %u out of range (0-65535)", flow_type_str(t, fb->ipv6), val);
 | 
						|
}
 | 
						|
 | 
						|
static enum flow_validated_state
 | 
						|
flow_validate(const byte *nlri, uint len, int ipv6)
 | 
						|
{
 | 
						|
  enum flow_type type = 0;
 | 
						|
  const byte *pos = nlri;
 | 
						|
  const byte *end = nlri + len;
 | 
						|
  int met_dst_pfx = 0;
 | 
						|
 | 
						|
  while (pos < end)
 | 
						|
  {
 | 
						|
    /* Check increasing type ordering */
 | 
						|
    if (*pos <= type)
 | 
						|
      return FLOW_ST_BAD_TYPE_ORDER;
 | 
						|
    type = *pos++;
 | 
						|
 | 
						|
    switch (type)
 | 
						|
    {
 | 
						|
    case FLOW_TYPE_DST_PREFIX:
 | 
						|
      met_dst_pfx = 1;
 | 
						|
      /* Fall through */
 | 
						|
    case FLOW_TYPE_SRC_PREFIX:
 | 
						|
    {
 | 
						|
      uint pxlen = *pos++;
 | 
						|
      if (pxlen > (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH))
 | 
						|
	return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
 | 
						|
 | 
						|
      uint bytes = BYTES(pxlen);
 | 
						|
      if (ipv6)
 | 
						|
      {
 | 
						|
        uint pxoffset = *pos++;
 | 
						|
        if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
 | 
						|
          return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
 | 
						|
        bytes -= pxoffset / 8;
 | 
						|
      }
 | 
						|
      pos += bytes;
 | 
						|
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    case FLOW_TYPE_LABEL:
 | 
						|
      if (!ipv6)
 | 
						|
	return FLOW_ST_UNKNOWN_COMPONENT;
 | 
						|
      /* fall through */
 | 
						|
    case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
 | 
						|
    case FLOW_TYPE_PORT:
 | 
						|
    case FLOW_TYPE_DST_PORT:
 | 
						|
    case FLOW_TYPE_SRC_PORT:
 | 
						|
    case FLOW_TYPE_ICMP_TYPE:
 | 
						|
    case FLOW_TYPE_ICMP_CODE:
 | 
						|
    case FLOW_TYPE_TCP_FLAGS:
 | 
						|
    case FLOW_TYPE_PACKET_LENGTH:
 | 
						|
    case FLOW_TYPE_DSCP:
 | 
						|
    case FLOW_TYPE_FRAGMENT:
 | 
						|
    {
 | 
						|
      uint last = 0;
 | 
						|
      uint first = 1;
 | 
						|
 | 
						|
      while (!last)
 | 
						|
      {
 | 
						|
	/*
 | 
						|
	 *    0   1   2   3   4   5   6   7
 | 
						|
	 *  +---+---+---+---+---+---+---+---+
 | 
						|
	 *  | e | a |  len  | 0 |lt |gt |eq |
 | 
						|
	 *  +---+---+---+---+---+---+---+---+
 | 
						|
	 *
 | 
						|
	 *           Numeric operator
 | 
						|
	 */
 | 
						|
 | 
						|
	last = isset_end(pos);
 | 
						|
 | 
						|
	/* The AND bit should in the first operator byte of a sequence */
 | 
						|
	if (first && isset_and(pos))
 | 
						|
	  return FLOW_ST_AND_BIT_SHOULD_BE_UNSET;
 | 
						|
 | 
						|
	/* This bit should be zero */
 | 
						|
	if (*pos & 0x08)
 | 
						|
	  return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
 | 
						|
 | 
						|
	if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT)
 | 
						|
	{
 | 
						|
	  /*
 | 
						|
	   *    0   1   2   3   4   5   6   7
 | 
						|
	   *  +---+---+---+---+---+---+---+---+
 | 
						|
	   *  | e | a |  len  | 0 | 0 |not| m |
 | 
						|
	   *  +---+---+---+---+---+---+---+---+
 | 
						|
	   *
 | 
						|
	   *           Bitmask operand
 | 
						|
	   */
 | 
						|
	  if (*pos & 0x04)
 | 
						|
	    return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Value length of operator */
 | 
						|
	uint len = get_value_length(pos);
 | 
						|
	if (len > flow_max_value_length(type, ipv6))
 | 
						|
	  return FLOW_ST_EXCEED_MAX_VALUE_LENGTH;
 | 
						|
 | 
						|
	/* TCP Flags component must not check highest nibble (just 12 valid bits) */
 | 
						|
	if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2) && (pos[1] & 0xf0))
 | 
						|
	  return FLOW_ST_INVALID_TCP_FLAGS;
 | 
						|
 | 
						|
	/* Bit-7 must be 0 [draft-ietf-idr-flow-spec-v6] */
 | 
						|
	if ((type == FLOW_TYPE_FRAGMENT) && ipv6 && (pos[1] & 0x01))
 | 
						|
	  return FLOW_ST_CANNOT_USE_DONT_FRAGMENT;
 | 
						|
	/* XXX: Could be a fragment component encoded in 2-bytes? */
 | 
						|
 | 
						|
	pos += 1+len;
 | 
						|
 | 
						|
	if (pos > end && !last)
 | 
						|
	  return FLOW_ST_NOT_COMPLETE;
 | 
						|
 | 
						|
	if (pos > (end+1))
 | 
						|
	  return FLOW_ST_NOT_COMPLETE;
 | 
						|
 | 
						|
	first = 0;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default:
 | 
						|
      return FLOW_ST_UNKNOWN_COMPONENT;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (pos != end)
 | 
						|
    return FLOW_ST_NOT_COMPLETE;
 | 
						|
 | 
						|
  if (!ipv6 && !met_dst_pfx)
 | 
						|
    return FLOW_ST_DEST_PREFIX_REQUIRED;
 | 
						|
 | 
						|
  return FLOW_ST_VALID;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow4_validate - check untrustworthy IPv4 flowspec data stream
 | 
						|
 * @nlri: flowspec data stream without compressed encoded length value
 | 
						|
 * @len: length of @nlri
 | 
						|
 *
 | 
						|
 * This function checks meaningfulness of binary flowspec. It should return
 | 
						|
 * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it
 | 
						|
 * returns some other %FLOW_ST_xxx state.
 | 
						|
 */
 | 
						|
inline enum flow_validated_state
 | 
						|
flow4_validate(const byte *nlri, uint len)
 | 
						|
{
 | 
						|
  return flow_validate(nlri, len, 0);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow6_validate - check untrustworthy IPv6 flowspec data stream
 | 
						|
 * @nlri: flowspec binary stream without encoded length value
 | 
						|
 * @len: length of @nlri
 | 
						|
 *
 | 
						|
 * This function checks meaningfulness of binary flowspec. It should return
 | 
						|
 * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it
 | 
						|
 * returns some other %FLOW_ST_xxx state.
 | 
						|
 */
 | 
						|
inline enum flow_validated_state
 | 
						|
flow6_validate(const byte *nlri, uint len)
 | 
						|
{
 | 
						|
  return flow_validate(nlri, len, 1);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow4_validate_cf - validate flowspec data structure &net_addr_flow4 in parsing time
 | 
						|
 * @f: flowspec data structure &net_addr_flow4
 | 
						|
 *
 | 
						|
 * Check if @f is valid flowspec data structure. Can call cf_error() function
 | 
						|
 * with a textual description of reason to failing of validation.
 | 
						|
 */
 | 
						|
void
 | 
						|
flow4_validate_cf(net_addr_flow4 *f)
 | 
						|
{
 | 
						|
  enum flow_validated_state r = flow4_validate(flow4_first_part(f), flow_read_length(f->data));
 | 
						|
 | 
						|
  if (r != FLOW_ST_VALID)
 | 
						|
    cf_error("Invalid flow route: %s", flow_validated_state_str(r));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow6_validate_cf - validate flowspec data structure &net_addr_flow6 in parsing time
 | 
						|
 * @f: flowspec data structure &net_addr_flow6
 | 
						|
 *
 | 
						|
 * Check if @f is valid flowspec data structure. Can call cf_error() function
 | 
						|
 * with a textual description of reason to failing of validation.
 | 
						|
 */
 | 
						|
void
 | 
						|
flow6_validate_cf(net_addr_flow6 *f)
 | 
						|
{
 | 
						|
  enum flow_validated_state r = flow6_validate(flow6_first_part(f), flow_read_length(f->data));
 | 
						|
 | 
						|
  if (r != FLOW_ST_VALID)
 | 
						|
    cf_error("Invalid flow route: %s", flow_validated_state_str(r));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * 	Flowspec Builder
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder_init - constructor for flowspec builder instance
 | 
						|
 * @pool: memory pool
 | 
						|
 *
 | 
						|
 * This function prepares flowspec builder instance using memory pool @pool.
 | 
						|
 */
 | 
						|
struct flow_builder *
 | 
						|
flow_builder_init(pool *pool)
 | 
						|
{
 | 
						|
  struct flow_builder *fb = mb_allocz(pool, sizeof(struct flow_builder));
 | 
						|
  BUFFER_INIT(fb->data, pool, 4);
 | 
						|
  return fb;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
is_stackable_type(enum flow_type type)
 | 
						|
{
 | 
						|
  switch (type)
 | 
						|
  {
 | 
						|
  case FLOW_TYPE_IP_PROTOCOL:
 | 
						|
  case FLOW_TYPE_PORT:
 | 
						|
  case FLOW_TYPE_DST_PORT:
 | 
						|
  case FLOW_TYPE_SRC_PORT:
 | 
						|
  case FLOW_TYPE_ICMP_TYPE:
 | 
						|
  case FLOW_TYPE_ICMP_CODE:
 | 
						|
  case FLOW_TYPE_TCP_FLAGS:
 | 
						|
  case FLOW_TYPE_PACKET_LENGTH:
 | 
						|
  case FLOW_TYPE_DSCP:
 | 
						|
  case FLOW_TYPE_FRAGMENT:
 | 
						|
  case FLOW_TYPE_LABEL:
 | 
						|
    return 1;
 | 
						|
 | 
						|
  default:
 | 
						|
    /* The unknown components are not stack-able in default */
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
builder_add_prepare(struct flow_builder *fb)
 | 
						|
{
 | 
						|
  if (fb->parts[fb->this_type].length)
 | 
						|
  {
 | 
						|
    if (fb->last_type != fb->this_type)
 | 
						|
      return 0;
 | 
						|
 | 
						|
    if (!is_stackable_type(fb->this_type))
 | 
						|
      return 0;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    fb->parts[fb->this_type].offset = fb->data.used;
 | 
						|
  }
 | 
						|
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
builder_add_finish(struct flow_builder *fb)
 | 
						|
{
 | 
						|
  fb->parts[fb->this_type].length = fb->data.used - fb->parts[fb->this_type].offset;
 | 
						|
  flow_builder_set_type(fb, fb->this_type);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
push_pfx_to_buffer(struct flow_builder *fb, u8 pxlen_bytes, byte *ip)
 | 
						|
{
 | 
						|
  for (int i = 0; i < pxlen_bytes; i++)
 | 
						|
    BUFFER_PUSH(fb->data) = *ip++;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder4_add_pfx - add IPv4 prefix
 | 
						|
 * @fb: flowspec builder instance
 | 
						|
 * @n4: net address of type IPv4
 | 
						|
 *
 | 
						|
 * This function add IPv4 prefix into flowspec builder instance.
 | 
						|
 */
 | 
						|
int
 | 
						|
flow_builder4_add_pfx(struct flow_builder *fb, const net_addr_ip4 *n4)
 | 
						|
{
 | 
						|
  if (!builder_add_prepare(fb))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  ip4_addr ip4 = ip4_hton(n4->prefix);
 | 
						|
 | 
						|
  BUFFER_PUSH(fb->data) = fb->this_type;
 | 
						|
  BUFFER_PUSH(fb->data) = n4->pxlen;
 | 
						|
  push_pfx_to_buffer(fb, BYTES(n4->pxlen), (byte *) &ip4);
 | 
						|
 | 
						|
  builder_add_finish(fb);
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder6_add_pfx - add IPv6 prefix
 | 
						|
 * @fb: flowspec builder instance
 | 
						|
 * @n6: net address of type IPv4
 | 
						|
 * @pxoffset: prefix offset for @n6
 | 
						|
 *
 | 
						|
 * This function add IPv4 prefix into flowspec builder instance. This function
 | 
						|
 * should return 1 for successful adding, otherwise returns %0.
 | 
						|
 */
 | 
						|
int
 | 
						|
flow_builder6_add_pfx(struct flow_builder *fb, const net_addr_ip6 *n6, u32 pxoffset)
 | 
						|
{
 | 
						|
  if (!builder_add_prepare(fb))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  ip6_addr ip6 = ip6_hton(n6->prefix);
 | 
						|
 | 
						|
  BUFFER_PUSH(fb->data) = fb->this_type;
 | 
						|
  BUFFER_PUSH(fb->data) = n6->pxlen;
 | 
						|
  BUFFER_PUSH(fb->data) = pxoffset;
 | 
						|
  push_pfx_to_buffer(fb, BYTES(n6->pxlen) - (pxoffset / 8), ((byte *) &ip6) + (pxoffset / 8));
 | 
						|
 | 
						|
  builder_add_finish(fb);
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder_add_op_val - add operator/value pair
 | 
						|
 * @fb: flowspec builder instance
 | 
						|
 * @op: operator
 | 
						|
 * @value: value
 | 
						|
 *
 | 
						|
 * This function add operator/value pair as a part of a flowspec component. It
 | 
						|
 * is required to set appropriate flowspec component type using function
 | 
						|
 * flow_builder_set_type(). This function should return 1 for successful
 | 
						|
 * adding, otherwise returns 0.
 | 
						|
 */
 | 
						|
int
 | 
						|
flow_builder_add_op_val(struct flow_builder *fb, byte op, u32 value)
 | 
						|
{
 | 
						|
  if (!builder_add_prepare(fb))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  if (fb->this_type == fb->last_type)
 | 
						|
  {
 | 
						|
    /* Remove the end-bit from last operand-value pair of the component */
 | 
						|
    fb->data.data[fb->last_op_offset] &= 0x7f;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    BUFFER_PUSH(fb->data) = fb->this_type;
 | 
						|
  }
 | 
						|
 | 
						|
  fb->last_op_offset = fb->data.used;
 | 
						|
 | 
						|
  /* Set the end-bit for operand-value pair of the component */
 | 
						|
  op |= 0x80;
 | 
						|
 | 
						|
  if (value & 0xff00)
 | 
						|
  {
 | 
						|
    BUFFER_PUSH(fb->data) = op | 0x10;
 | 
						|
    put_u16(BUFFER_INC(fb->data, 2), value);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    BUFFER_PUSH(fb->data) = op;
 | 
						|
    BUFFER_PUSH(fb->data) = (u8) value;
 | 
						|
  }
 | 
						|
 | 
						|
  builder_add_finish(fb);
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder_add_val_mask - add value/bitmask pair
 | 
						|
 * @fb: flowspec builder instance
 | 
						|
 * @op: operator
 | 
						|
 * @value: value
 | 
						|
 * @mask: bitmask
 | 
						|
 *
 | 
						|
 * It is required to set appropriate flowspec component type using function
 | 
						|
 * flow_builder_set_type(). This function should return 1 for successful adding,
 | 
						|
 * otherwise returns 0.
 | 
						|
 */
 | 
						|
int
 | 
						|
flow_builder_add_val_mask(struct flow_builder *fb, byte op, u32 value, u32 mask)
 | 
						|
{
 | 
						|
  u32 a =  value & mask;
 | 
						|
  u32 b = ~value & mask;
 | 
						|
 | 
						|
  if (a)
 | 
						|
  {
 | 
						|
    flow_builder_add_op_val(fb, op ^ 0x01, a);
 | 
						|
    op |= FLOW_OP_AND;
 | 
						|
  }
 | 
						|
 | 
						|
  if (b)
 | 
						|
    flow_builder_add_op_val(fb, op ^ 0x02, b);
 | 
						|
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder_set_type - set type of next flowspec component
 | 
						|
 * @fb: flowspec builder instance
 | 
						|
 * @type: flowspec component type
 | 
						|
 *
 | 
						|
 * This function sets type of next flowspec component. It is necessary to call
 | 
						|
 * this function before each changing of adding flowspec component.
 | 
						|
 */
 | 
						|
void
 | 
						|
flow_builder_set_type(struct flow_builder *fb, enum flow_type type)
 | 
						|
{
 | 
						|
  fb->last_type = fb->this_type;
 | 
						|
  fb->this_type = type;
 | 
						|
}
 | 
						|
 | 
						|
static ip4_addr
 | 
						|
flow_read_ip4(const byte *px, uint pxlen)
 | 
						|
{
 | 
						|
  ip4_addr ip = IP4_NONE;
 | 
						|
  memcpy(&ip, px, BYTES(pxlen));
 | 
						|
  return ip4_ntoh(ip);
 | 
						|
}
 | 
						|
 | 
						|
static ip6_addr
 | 
						|
flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
 | 
						|
{
 | 
						|
  uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
 | 
						|
  uint ceil_len = BYTES(pxlen);
 | 
						|
  ip6_addr ip = IP6_NONE;
 | 
						|
 | 
						|
  memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
 | 
						|
 | 
						|
  return ip6_ntoh(ip);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
builder_write_parts(struct flow_builder *fb, byte *buf)
 | 
						|
{
 | 
						|
  for (int i = 1; i < FLOW_TYPE_MAX; i++)
 | 
						|
  {
 | 
						|
    if (fb->parts[i].length)
 | 
						|
    {
 | 
						|
      memcpy(buf, fb->data.data + fb->parts[i].offset, fb->parts[i].length);
 | 
						|
      buf += fb->parts[i].length;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder4_finalize - assemble final flowspec data structure &net_addr_flow4
 | 
						|
 * @fb: flowspec builder instance
 | 
						|
 * @lpool: linear memory pool
 | 
						|
 *
 | 
						|
 * This function returns final flowspec data structure &net_addr_flow4 allocated
 | 
						|
 * onto @lpool linear memory pool.
 | 
						|
 */
 | 
						|
net_addr_flow4 *
 | 
						|
flow_builder4_finalize(struct flow_builder *fb, linpool *lpool)
 | 
						|
{
 | 
						|
  uint data_len = fb->data.used + (fb->data.used < 0xf0 ? 1 : 2);
 | 
						|
  net_addr_flow4 *f = lp_alloc(lpool, sizeof(struct net_addr_flow4) + data_len);
 | 
						|
 | 
						|
  ip4_addr prefix = IP4_NONE;
 | 
						|
  uint pxlen = 0;
 | 
						|
 | 
						|
  if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
 | 
						|
  {
 | 
						|
    byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
 | 
						|
    pxlen = *p++;
 | 
						|
    prefix = flow_read_ip4(p, pxlen);
 | 
						|
  }
 | 
						|
  *f = NET_ADDR_FLOW4(prefix, pxlen, data_len);
 | 
						|
 | 
						|
  builder_write_parts(fb, f->data + flow_write_length(f->data, fb->data.used));
 | 
						|
 | 
						|
  return f;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder6_finalize - assemble final flowspec data structure &net_addr_flow6
 | 
						|
 * @fb: flowspec builder instance
 | 
						|
 * @lpool: linear memory pool for allocation of
 | 
						|
 *
 | 
						|
 * This function returns final flowspec data structure &net_addr_flow6 allocated
 | 
						|
 * onto @lpool linear memory pool.
 | 
						|
 */
 | 
						|
net_addr_flow6 *
 | 
						|
flow_builder6_finalize(struct flow_builder *fb, linpool *lpool)
 | 
						|
{
 | 
						|
  uint data_len =  fb->data.used + (fb->data.used < 0xf0 ? 1 : 2);
 | 
						|
  net_addr_flow6 *n = lp_alloc(lpool, sizeof(net_addr_flow6) + data_len);
 | 
						|
 | 
						|
  ip6_addr prefix = IP6_NONE;
 | 
						|
  uint pxlen = 0;
 | 
						|
 | 
						|
  if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
 | 
						|
  {
 | 
						|
    byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
 | 
						|
    pxlen = *p++;
 | 
						|
    uint pxoffset = *p++;
 | 
						|
    prefix = flow_read_ip6(p, pxlen, pxoffset);
 | 
						|
  }
 | 
						|
  *n = NET_ADDR_FLOW6(prefix, pxlen, data_len);
 | 
						|
 | 
						|
  builder_write_parts(fb, n->data + flow_write_length(n->data, fb->data.used));
 | 
						|
 | 
						|
  return n;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow_builder_clear - flush flowspec builder instance for another flowspec creation
 | 
						|
 * @fb: flowspec builder instance
 | 
						|
 *
 | 
						|
 * This function flushes all data from builder but it maintains pre-allocated
 | 
						|
 * buffer space.
 | 
						|
 */
 | 
						|
void
 | 
						|
flow_builder_clear(struct flow_builder *fb)
 | 
						|
{
 | 
						|
  BUFFER(byte) data;
 | 
						|
  BUFFER_FLUSH(fb->data);
 | 
						|
 | 
						|
  BUFFER_SHALLOW_COPY(data, fb->data);
 | 
						|
  memset(fb, 0, sizeof(struct flow_builder));
 | 
						|
  BUFFER_SHALLOW_COPY(fb->data, data);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * 	Net Formatting
 | 
						|
 */
 | 
						|
 | 
						|
/* Flowspec operators for [op, value]+ pairs */
 | 
						|
 | 
						|
static const char *
 | 
						|
num_op_str(const byte *op)
 | 
						|
{
 | 
						|
  switch (*op & 0x07)
 | 
						|
  {
 | 
						|
  case FLOW_OP_TRUE:	return "true";
 | 
						|
  case FLOW_OP_EQ:	return "=";
 | 
						|
  case FLOW_OP_GT:	return ">";
 | 
						|
  case FLOW_OP_GEQ:	return ">=";
 | 
						|
  case FLOW_OP_LT:	return "<";
 | 
						|
  case FLOW_OP_LEQ:	return "<=";
 | 
						|
  case FLOW_OP_NEQ:	return "!=";
 | 
						|
  case FLOW_OP_FALSE:	return "false";
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static uint
 | 
						|
get_value(const byte *val, u8 len)
 | 
						|
{
 | 
						|
  switch (len)
 | 
						|
  {
 | 
						|
  case 1: return *val;
 | 
						|
  case 2: return get_u16(val);
 | 
						|
  case 4: return get_u32(val);
 | 
						|
  // No component may have length 8
 | 
						|
  // case 8: return get_u64(val);
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const char *
 | 
						|
fragment_val_str(u8 val)
 | 
						|
{
 | 
						|
  switch (val)
 | 
						|
  {
 | 
						|
  case 1: return "dont_fragment";
 | 
						|
  case 2: return "is_fragment";
 | 
						|
  case 4: return "first_fragment";
 | 
						|
  case 8: return "last_fragment";
 | 
						|
  }
 | 
						|
  return "???";
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
net_format_flow_ip(buffer *b, const byte *part, int ipv6)
 | 
						|
{
 | 
						|
  uint pxlen = *(part+1);
 | 
						|
  if (ipv6)
 | 
						|
  {
 | 
						|
    uint pxoffset = *(part+2);
 | 
						|
    if (pxoffset)
 | 
						|
      buffer_print(b, "%I6/%u offset %u; ", flow_read_ip6(part+3,pxlen,pxoffset), pxlen, pxoffset);
 | 
						|
    else
 | 
						|
      buffer_print(b, "%I6/%u; ", flow_read_ip6(part+3,pxlen,0), pxlen);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    buffer_print(b, "%I4/%u; ", flow_read_ip4(part+2,pxlen), pxlen);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
net_format_flow_num(buffer *b, const byte *part)
 | 
						|
{
 | 
						|
  const byte *last_op = NULL;
 | 
						|
  const byte *op = part+1;
 | 
						|
  uint val;
 | 
						|
  uint len;
 | 
						|
  uint first = 1;
 | 
						|
 | 
						|
  while (1)
 | 
						|
  {
 | 
						|
    if (!first)
 | 
						|
    {
 | 
						|
      /* XXX: I don't like this so complicated if-tree */
 | 
						|
      if (!isset_and(op) &&
 | 
						|
	  ((num_op(     op) == FLOW_OP_EQ) || (num_op(     op) == FLOW_OP_GEQ)) &&
 | 
						|
	  ((num_op(last_op) == FLOW_OP_EQ) || (num_op(last_op) == FLOW_OP_LEQ)))
 | 
						|
      {
 | 
						|
	b->pos--; /* Remove last char (it is a space) */
 | 
						|
	buffer_puts(b, ",");
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
	buffer_puts(b, isset_and(op) ? "&& " : "|| ");
 | 
						|
      }
 | 
						|
    }
 | 
						|
    first = 0;
 | 
						|
 | 
						|
    len = get_value_length(op);
 | 
						|
    val = get_value(op+1, len);
 | 
						|
 | 
						|
    if (!isset_end(op) && !isset_and(op) && isset_and(op+1+len) &&
 | 
						|
	(num_op(op) == FLOW_OP_GEQ) && (num_op(op+1+len) == FLOW_OP_LEQ))
 | 
						|
    {
 | 
						|
      /* Display interval */
 | 
						|
      buffer_print(b, "%u..", val);
 | 
						|
      op += 1 + len;
 | 
						|
      len = get_value_length(op);
 | 
						|
      val = get_value(op+1, len);
 | 
						|
      buffer_print(b, "%u", val);
 | 
						|
    }
 | 
						|
    else if (num_op(op) == FLOW_OP_EQ)
 | 
						|
    {
 | 
						|
      buffer_print(b, "%u", val);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      buffer_print(b, "%s %u", num_op_str(op), val);
 | 
						|
    }
 | 
						|
 | 
						|
    if (isset_end(op))
 | 
						|
    {
 | 
						|
      buffer_puts(b, "; ");
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      buffer_puts(b, " ");
 | 
						|
    }
 | 
						|
 | 
						|
    last_op = op;
 | 
						|
    op += 1 + len;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
net_format_flow_bitmask(buffer *b, const byte *part)
 | 
						|
{
 | 
						|
  const byte *op = part+1;
 | 
						|
  uint val;
 | 
						|
  uint len;
 | 
						|
  uint first = 1;
 | 
						|
 | 
						|
  while (1)
 | 
						|
  {
 | 
						|
    if (!first)
 | 
						|
    {
 | 
						|
      if (isset_and(op))
 | 
						|
      {
 | 
						|
	b->pos--; /* Remove last char (it is a space) */
 | 
						|
	buffer_puts(b, ",");
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
	buffer_puts(b, "|| ");
 | 
						|
      }
 | 
						|
    }
 | 
						|
    first = 0;
 | 
						|
 | 
						|
    len = get_value_length(op);
 | 
						|
    val = get_value(op+1, len);
 | 
						|
 | 
						|
    /*
 | 
						|
     *   Not Match  Show
 | 
						|
     *  ------------------
 | 
						|
     *    0    0    !0/B
 | 
						|
     *    0    1     B/B
 | 
						|
     *    1    0     0/B
 | 
						|
     *    1    1    !B/B
 | 
						|
     */
 | 
						|
 | 
						|
    if ((*op & 0x3) == 0x3 || (*op & 0x3) == 0)
 | 
						|
      buffer_puts(b, "!");
 | 
						|
 | 
						|
    if (*part == FLOW_TYPE_FRAGMENT && (val == 1 || val == 2 || val == 4 || val == 8))
 | 
						|
      buffer_print(b, "%s%s", ((*op & 0x1) ? "" : "!"), fragment_val_str(val));
 | 
						|
    else
 | 
						|
      buffer_print(b, "0x%x/0x%x", ((*op & 0x1) ? val : 0), val);
 | 
						|
 | 
						|
    if (isset_end(op))
 | 
						|
    {
 | 
						|
      buffer_puts(b, "; ");
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      buffer_puts(b, " ");
 | 
						|
    }
 | 
						|
 | 
						|
    op += 1 + len;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static uint
 | 
						|
net_format_flow(char *buf, uint blen, const byte *data, uint dlen, int ipv6)
 | 
						|
{
 | 
						|
  buffer b = {
 | 
						|
    .start = buf,
 | 
						|
    .pos = buf,
 | 
						|
    .end = buf + blen,
 | 
						|
  };
 | 
						|
 | 
						|
  const byte *part = flow_first_part(data);
 | 
						|
  *buf = 0;
 | 
						|
 | 
						|
  if (ipv6)
 | 
						|
    buffer_puts(&b, "flow6 { ");
 | 
						|
  else
 | 
						|
    buffer_puts(&b, "flow4 { ");
 | 
						|
 | 
						|
  while (part)
 | 
						|
  {
 | 
						|
    buffer_print(&b, "%s ", flow_type_str(*part, ipv6));
 | 
						|
 | 
						|
    switch (*part)
 | 
						|
    {
 | 
						|
    case FLOW_TYPE_DST_PREFIX:
 | 
						|
    case FLOW_TYPE_SRC_PREFIX:
 | 
						|
      net_format_flow_ip(&b, part, ipv6);
 | 
						|
      break;
 | 
						|
    case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
 | 
						|
    case FLOW_TYPE_PORT:
 | 
						|
    case FLOW_TYPE_DST_PORT:
 | 
						|
    case FLOW_TYPE_SRC_PORT:
 | 
						|
    case FLOW_TYPE_ICMP_TYPE:
 | 
						|
    case FLOW_TYPE_ICMP_CODE:
 | 
						|
    case FLOW_TYPE_PACKET_LENGTH:
 | 
						|
    case FLOW_TYPE_DSCP:
 | 
						|
      net_format_flow_num(&b, part);
 | 
						|
      break;
 | 
						|
    case FLOW_TYPE_TCP_FLAGS:
 | 
						|
    case FLOW_TYPE_FRAGMENT:
 | 
						|
    case FLOW_TYPE_LABEL:
 | 
						|
      net_format_flow_bitmask(&b, part);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    part = flow_next_part(part, data+dlen, ipv6);
 | 
						|
  }
 | 
						|
 | 
						|
  buffer_puts(&b, "}");
 | 
						|
 | 
						|
  if (b.pos == b.end)
 | 
						|
  {
 | 
						|
    b.pos = b.start + MIN(blen - 6, strlen(b.start));
 | 
						|
    buffer_puts(&b, " ...}");
 | 
						|
  }
 | 
						|
 | 
						|
  return b.pos - b.start;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow4_net_format - stringify flowspec data structure &net_addr_flow4
 | 
						|
 * @buf: pre-allocated buffer for writing a stringify net address flowspec
 | 
						|
 * @blen: free allocated space in @buf
 | 
						|
 * @f: flowspec data structure &net_addr_flow4 for stringify
 | 
						|
 *
 | 
						|
 * This function writes stringified @f into @buf. The function returns number
 | 
						|
 * of written chars. If final string is too large, the string will ends the with
 | 
						|
 * ' ...}' sequence and zero-terminator.
 | 
						|
 */
 | 
						|
uint
 | 
						|
flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f)
 | 
						|
{
 | 
						|
  return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow4), 0);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * flow6_net_format - stringify flowspec data structure &net_addr_flow6
 | 
						|
 * @buf: pre-allocated buffer for writing a stringify net address flowspec
 | 
						|
 * @blen: free allocated space in @buf
 | 
						|
 * @f: flowspec data structure &net_addr_flow4 for stringify
 | 
						|
 *
 | 
						|
 * This function writes stringified @f into @buf. The function returns number
 | 
						|
 * of written chars. If final string is too large, the string will ends the with
 | 
						|
 * ' ...}' sequence and zero-terminator.
 | 
						|
 */
 | 
						|
uint
 | 
						|
flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f)
 | 
						|
{
 | 
						|
  return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow6), 1);
 | 
						|
}
 |