mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	Filter data manipulation functions separated to their file
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
			
		||||
src := filter.c f-util.c tree.c trie.c inst-gen.c
 | 
			
		||||
src := filter.c data.c f-util.c tree.c trie.c inst-gen.c
 | 
			
		||||
obj := $(src-o-files)
 | 
			
		||||
$(all-daemon)
 | 
			
		||||
$(cf-local)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										519
									
								
								filter/data.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								filter/data.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,519 @@
 | 
			
		||||
/*
 | 
			
		||||
 *	Filters: utility functions
 | 
			
		||||
 *
 | 
			
		||||
 *	(c) 1998 Pavel Machek <pavel@ucw.cz>
 | 
			
		||||
 *	(c) 2019 Maria Matejka <mq@jmq.cz>
 | 
			
		||||
 *
 | 
			
		||||
 *	Can be freely distributed and used under the terms of the GNU GPL.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "nest/bird.h"
 | 
			
		||||
#include "lib/lists.h"
 | 
			
		||||
#include "lib/resource.h"
 | 
			
		||||
#include "lib/socket.h"
 | 
			
		||||
#include "lib/string.h"
 | 
			
		||||
#include "lib/unaligned.h"
 | 
			
		||||
#include "lib/net.h"
 | 
			
		||||
#include "lib/ip.h"
 | 
			
		||||
#include "nest/route.h"
 | 
			
		||||
#include "nest/protocol.h"
 | 
			
		||||
#include "nest/iface.h"
 | 
			
		||||
#include "nest/attrs.h"
 | 
			
		||||
#include "conf/conf.h"
 | 
			
		||||
#include "filter/filter.h"
 | 
			
		||||
#include "filter/f-inst.h"
 | 
			
		||||
#include "filter/data.h"
 | 
			
		||||
 | 
			
		||||
const struct f_val f_const_empty_path = {
 | 
			
		||||
  .type = T_PATH,
 | 
			
		||||
  .val.ad = &null_adata,
 | 
			
		||||
}, f_const_empty_clist = {
 | 
			
		||||
  .type = T_CLIST,
 | 
			
		||||
  .val.ad = &null_adata,
 | 
			
		||||
}, f_const_empty_eclist = {
 | 
			
		||||
  .type = T_ECLIST,
 | 
			
		||||
  .val.ad = &null_adata,
 | 
			
		||||
}, f_const_empty_lclist = {
 | 
			
		||||
  .type = T_LCLIST,
 | 
			
		||||
  .val.ad = &null_adata,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct adata *
 | 
			
		||||
adata_empty(struct linpool *pool, int l)
 | 
			
		||||
{
 | 
			
		||||
  struct adata *res = lp_alloc(pool, sizeof(struct adata) + l);
 | 
			
		||||
  res->length = l;
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
pm_format(const struct f_path_mask *p, buffer *buf)
 | 
			
		||||
{
 | 
			
		||||
  buffer_puts(buf, "[= ");
 | 
			
		||||
 | 
			
		||||
  for (uint i=0; i<p->len; i++)
 | 
			
		||||
  {
 | 
			
		||||
    switch(p->item[i].kind)
 | 
			
		||||
    {
 | 
			
		||||
    case PM_ASN:
 | 
			
		||||
      buffer_print(buf, "%u ", p->item[i].asn);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case PM_QUESTION:
 | 
			
		||||
      buffer_puts(buf, "? ");
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case PM_ASTERISK:
 | 
			
		||||
      buffer_puts(buf, "* ");
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case PM_ASN_RANGE:
 | 
			
		||||
      buffer_print(buf, "%u..%u ", p->item[i].from, p->item[i].to);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case PM_ASN_EXPR:
 | 
			
		||||
      ASSERT(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  buffer_puts(buf, "=]");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int
 | 
			
		||||
lcomm_cmp(lcomm v1, lcomm v2)
 | 
			
		||||
{
 | 
			
		||||
  if (v1.asn != v2.asn)
 | 
			
		||||
    return (v1.asn > v2.asn) ? 1 : -1;
 | 
			
		||||
  if (v1.ldp1 != v2.ldp1)
 | 
			
		||||
    return (v1.ldp1 > v2.ldp1) ? 1 : -1;
 | 
			
		||||
  if (v1.ldp2 != v2.ldp2)
 | 
			
		||||
    return (v1.ldp2 > v2.ldp2) ? 1 : -1;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * val_compare - compare two values
 | 
			
		||||
 * @v1: first value
 | 
			
		||||
 * @v2: second value
 | 
			
		||||
 *
 | 
			
		||||
 * Compares two values and returns -1, 0, 1 on <, =, > or F_CMP_ERROR on
 | 
			
		||||
 * error. Tree module relies on this giving consistent results so
 | 
			
		||||
 * that it can be used for building balanced trees.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
val_compare(const struct f_val *v1, const struct f_val *v2)
 | 
			
		||||
{
 | 
			
		||||
  if (v1->type != v2->type) {
 | 
			
		||||
    if (v1->type == T_VOID)	/* Hack for else */
 | 
			
		||||
      return -1;
 | 
			
		||||
    if (v2->type == T_VOID)
 | 
			
		||||
      return 1;
 | 
			
		||||
 | 
			
		||||
    /* IP->Quad implicit conversion */
 | 
			
		||||
    if ((v1->type == T_QUAD) && val_is_ip4(v2))
 | 
			
		||||
      return uint_cmp(v1->val.i, ipa_to_u32(v2->val.ip));
 | 
			
		||||
    if (val_is_ip4(v1) && (v2->type == T_QUAD))
 | 
			
		||||
      return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i);
 | 
			
		||||
 | 
			
		||||
    debug( "Types do not match in val_compare\n" );
 | 
			
		||||
    return F_CMP_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (v1->type) {
 | 
			
		||||
  case T_VOID:
 | 
			
		||||
    return 0;
 | 
			
		||||
  case T_ENUM:
 | 
			
		||||
  case T_INT:
 | 
			
		||||
  case T_BOOL:
 | 
			
		||||
  case T_PAIR:
 | 
			
		||||
  case T_QUAD:
 | 
			
		||||
    return uint_cmp(v1->val.i, v2->val.i);
 | 
			
		||||
  case T_EC:
 | 
			
		||||
  case T_RD:
 | 
			
		||||
    return u64_cmp(v1->val.ec, v2->val.ec);
 | 
			
		||||
  case T_LC:
 | 
			
		||||
    return lcomm_cmp(v1->val.lc, v2->val.lc);
 | 
			
		||||
  case T_IP:
 | 
			
		||||
    return ipa_compare(v1->val.ip, v2->val.ip);
 | 
			
		||||
  case T_NET:
 | 
			
		||||
    return net_compare(v1->val.net, v2->val.net);
 | 
			
		||||
  case T_STRING:
 | 
			
		||||
    return strcmp(v1->val.s, v2->val.s);
 | 
			
		||||
  default:
 | 
			
		||||
    return F_CMP_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
pm_same(const struct f_path_mask *m1, const struct f_path_mask *m2)
 | 
			
		||||
{
 | 
			
		||||
  if (m1->len != m2->len)
 | 
			
		||||
 | 
			
		||||
  for (uint i=0; i<m1->len; i++)
 | 
			
		||||
  {
 | 
			
		||||
    if (m1->item[i].kind != m2->item[i].kind)
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
    switch (m1->item[i].kind) {
 | 
			
		||||
      case PM_ASN:
 | 
			
		||||
	if (m1->item[i].asn != m2->item[i].asn)
 | 
			
		||||
	  return 0;
 | 
			
		||||
	break;
 | 
			
		||||
      case PM_ASN_EXPR:
 | 
			
		||||
	if (!f_same(m1->item[i].expr, m2->item[i].expr))
 | 
			
		||||
	  return 0;
 | 
			
		||||
	break;
 | 
			
		||||
      case PM_ASN_RANGE:
 | 
			
		||||
	if (m1->item[i].from != m2->item[i].from)
 | 
			
		||||
	  return 0;
 | 
			
		||||
	if (m1->item[i].to != m2->item[i].to)
 | 
			
		||||
	  return 0;
 | 
			
		||||
	break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * val_same - compare two values
 | 
			
		||||
 * @v1: first value
 | 
			
		||||
 * @v2: second value
 | 
			
		||||
 *
 | 
			
		||||
 * Compares two values and returns 1 if they are same and 0 if not.
 | 
			
		||||
 * Comparison of values of different types is valid and returns 0.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
val_same(const struct f_val *v1, const struct f_val *v2)
 | 
			
		||||
{
 | 
			
		||||
  int rc;
 | 
			
		||||
 | 
			
		||||
  rc = val_compare(v1, v2);
 | 
			
		||||
  if (rc != F_CMP_ERROR)
 | 
			
		||||
    return !rc;
 | 
			
		||||
 | 
			
		||||
  if (v1->type != v2->type)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  switch (v1->type) {
 | 
			
		||||
  case T_PATH_MASK:
 | 
			
		||||
    return pm_same(v1->val.path_mask, v2->val.path_mask);
 | 
			
		||||
  case T_PATH:
 | 
			
		||||
  case T_CLIST:
 | 
			
		||||
  case T_ECLIST:
 | 
			
		||||
  case T_LCLIST:
 | 
			
		||||
    return adata_same(v1->val.ad, v2->val.ad);
 | 
			
		||||
  case T_SET:
 | 
			
		||||
    return same_tree(v1->val.t, v2->val.t);
 | 
			
		||||
  case T_PREFIX_SET:
 | 
			
		||||
    return trie_same(v1->val.ti, v2->val.ti);
 | 
			
		||||
  default:
 | 
			
		||||
    bug("Invalid type in val_same(): %x", v1->type);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
clist_set_type(const struct f_tree *set, struct f_val *v)
 | 
			
		||||
{
 | 
			
		||||
  switch (set->from.type)
 | 
			
		||||
  {
 | 
			
		||||
  case T_PAIR:
 | 
			
		||||
    v->type = T_PAIR;
 | 
			
		||||
    return 1;
 | 
			
		||||
 | 
			
		||||
  case T_QUAD:
 | 
			
		||||
    v->type = T_QUAD;
 | 
			
		||||
    return 1;
 | 
			
		||||
 | 
			
		||||
  case T_IP:
 | 
			
		||||
    if (val_is_ip4(&(set->from)) && val_is_ip4(&(set->to)))
 | 
			
		||||
    {
 | 
			
		||||
      v->type = T_QUAD;
 | 
			
		||||
      return 1;
 | 
			
		||||
    }
 | 
			
		||||
    /* Fall through */
 | 
			
		||||
  default:
 | 
			
		||||
    v->type = T_VOID;
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
clist_match_set(const struct adata *clist, const struct f_tree *set)
 | 
			
		||||
{
 | 
			
		||||
  if (!clist)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
  if (!clist_set_type(set, &v))
 | 
			
		||||
    return F_CMP_ERROR;
 | 
			
		||||
 | 
			
		||||
  u32 *l = (u32 *) clist->data;
 | 
			
		||||
  u32 *end = l + clist->length/4;
 | 
			
		||||
 | 
			
		||||
  while (l < end) {
 | 
			
		||||
    v.val.i = *l++;
 | 
			
		||||
    if (find_tree(set, &v))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
eclist_match_set(const struct adata *list, const struct f_tree *set)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if (!eclist_set_type(set))
 | 
			
		||||
    return F_CMP_ERROR;
 | 
			
		||||
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  v.type = T_EC;
 | 
			
		||||
  for (i = 0; i < len; i += 2) {
 | 
			
		||||
    v.val.ec = ec_get(l, i);
 | 
			
		||||
    if (find_tree(set, &v))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
lclist_match_set(const struct adata *list, const struct f_tree *set)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if (!lclist_set_type(set))
 | 
			
		||||
    return F_CMP_ERROR;
 | 
			
		||||
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  v.type = T_LC;
 | 
			
		||||
  for (i = 0; i < len; i += 3) {
 | 
			
		||||
    v.val.lc = lc_get(l, i);
 | 
			
		||||
    if (find_tree(set, &v))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct adata *
 | 
			
		||||
clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  int tree = (set->type == T_SET);	/* 1 -> set is T_SET, 0 -> set is T_CLIST */
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
  if (tree)
 | 
			
		||||
    clist_set_type(set->val.t, &v);
 | 
			
		||||
  else
 | 
			
		||||
    v.type = T_PAIR;
 | 
			
		||||
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  u32 tmp[len];
 | 
			
		||||
  u32 *k = tmp;
 | 
			
		||||
  u32 *end = l + len;
 | 
			
		||||
 | 
			
		||||
  while (l < end) {
 | 
			
		||||
    v.val.i = *l++;
 | 
			
		||||
    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
 | 
			
		||||
    if ((tree ? !!find_tree(set->val.t, &v) : int_set_contains(set->val.ad, v.val.i)) == pos)
 | 
			
		||||
      *k++ = v.val.i;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint nl = (k - tmp) * sizeof(u32);
 | 
			
		||||
  if (nl == list->length)
 | 
			
		||||
    return list;
 | 
			
		||||
 | 
			
		||||
  struct adata *res = adata_empty(pool, nl);
 | 
			
		||||
  memcpy(res->data, tmp, nl);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct adata *
 | 
			
		||||
eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  int tree = (set->type == T_SET);	/* 1 -> set is T_SET, 0 -> set is T_CLIST */
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  u32 tmp[len];
 | 
			
		||||
  u32 *k = tmp;
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  v.type = T_EC;
 | 
			
		||||
  for (i = 0; i < len; i += 2) {
 | 
			
		||||
    v.val.ec = ec_get(l, i);
 | 
			
		||||
    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
 | 
			
		||||
    if ((tree ? !!find_tree(set->val.t, &v) : ec_set_contains(set->val.ad, v.val.ec)) == pos) {
 | 
			
		||||
      *k++ = l[i];
 | 
			
		||||
      *k++ = l[i+1];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint nl = (k - tmp) * sizeof(u32);
 | 
			
		||||
  if (nl == list->length)
 | 
			
		||||
    return list;
 | 
			
		||||
 | 
			
		||||
  struct adata *res = adata_empty(pool, nl);
 | 
			
		||||
  memcpy(res->data, tmp, nl);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct adata *
 | 
			
		||||
lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  int tree = (set->type == T_SET);	/* 1 -> set is T_SET, 0 -> set is T_CLIST */
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  u32 tmp[len];
 | 
			
		||||
  u32 *k = tmp;
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  v.type = T_LC;
 | 
			
		||||
  for (i = 0; i < len; i += 3) {
 | 
			
		||||
    v.val.lc = lc_get(l, i);
 | 
			
		||||
    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
 | 
			
		||||
    if ((tree ? !!find_tree(set->val.t, &v) : lc_set_contains(set->val.ad, v.val.lc)) == pos)
 | 
			
		||||
      k = lc_copy(k, l+i);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint nl = (k - tmp) * sizeof(u32);
 | 
			
		||||
  if (nl == list->length)
 | 
			
		||||
    return list;
 | 
			
		||||
 | 
			
		||||
  struct adata *res = adata_empty(pool, nl);
 | 
			
		||||
  memcpy(res->data, tmp, nl);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * val_in_range - implement |~| operator
 | 
			
		||||
 * @v1: element
 | 
			
		||||
 * @v2: set
 | 
			
		||||
 *
 | 
			
		||||
 * Checks if @v1 is element (|~| operator) of @v2.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
val_in_range(const struct f_val *v1, const struct f_val *v2)
 | 
			
		||||
{
 | 
			
		||||
  if ((v1->type == T_PATH) && (v2->type == T_PATH_MASK))
 | 
			
		||||
    return as_path_match(v1->val.ad, v2->val.path_mask);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_INT) && (v2->type == T_PATH))
 | 
			
		||||
    return as_path_contains(v2->val.ad, v1->val.i, 1);
 | 
			
		||||
 | 
			
		||||
  if (((v1->type == T_PAIR) || (v1->type == T_QUAD)) && (v2->type == T_CLIST))
 | 
			
		||||
    return int_set_contains(v2->val.ad, v1->val.i);
 | 
			
		||||
  /* IP->Quad implicit conversion */
 | 
			
		||||
  if (val_is_ip4(v1) && (v2->type == T_CLIST))
 | 
			
		||||
    return int_set_contains(v2->val.ad, ipa_to_u32(v1->val.ip));
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_EC) && (v2->type == T_ECLIST))
 | 
			
		||||
    return ec_set_contains(v2->val.ad, v1->val.ec);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_LC) && (v2->type == T_LCLIST))
 | 
			
		||||
    return lc_set_contains(v2->val.ad, v1->val.lc);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_STRING) && (v2->type == T_STRING))
 | 
			
		||||
    return patmatch(v2->val.s, v1->val.s);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_IP) && (v2->type == T_NET))
 | 
			
		||||
    return ipa_in_netX(v1->val.ip, v2->val.net);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_NET) && (v2->type == T_NET))
 | 
			
		||||
    return net_in_netX(v1->val.net, v2->val.net);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_NET) && (v2->type == T_PREFIX_SET))
 | 
			
		||||
    return trie_match_net(v2->val.ti, v1->val.net);
 | 
			
		||||
 | 
			
		||||
  if (v2->type != T_SET)
 | 
			
		||||
    return F_CMP_ERROR;
 | 
			
		||||
 | 
			
		||||
  /* With integrated Quad<->IP implicit conversion */
 | 
			
		||||
  if ((v1->type == v2->val.t->from.type) ||
 | 
			
		||||
      ((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to))))
 | 
			
		||||
    return !!find_tree(v2->val.t, v1);
 | 
			
		||||
 | 
			
		||||
  if (v1->type == T_CLIST)
 | 
			
		||||
    return clist_match_set(v1->val.ad, v2->val.t);
 | 
			
		||||
 | 
			
		||||
  if (v1->type == T_ECLIST)
 | 
			
		||||
    return eclist_match_set(v1->val.ad, v2->val.t);
 | 
			
		||||
 | 
			
		||||
  if (v1->type == T_LCLIST)
 | 
			
		||||
    return lclist_match_set(v1->val.ad, v2->val.t);
 | 
			
		||||
 | 
			
		||||
  if (v1->type == T_PATH)
 | 
			
		||||
    return as_path_match_set(v1->val.ad, v2->val.t);
 | 
			
		||||
 | 
			
		||||
  return F_CMP_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * val_format - format filter value
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
val_format(const struct f_val *v, buffer *buf)
 | 
			
		||||
{
 | 
			
		||||
  char buf2[1024];
 | 
			
		||||
  switch (v->type)
 | 
			
		||||
  {
 | 
			
		||||
  case T_VOID:	buffer_puts(buf, "(void)"); return;
 | 
			
		||||
  case T_BOOL:	buffer_puts(buf, v->val.i ? "TRUE" : "FALSE"); return;
 | 
			
		||||
  case T_INT:	buffer_print(buf, "%u", v->val.i); return;
 | 
			
		||||
  case T_STRING: buffer_print(buf, "%s", v->val.s); return;
 | 
			
		||||
  case T_IP:	buffer_print(buf, "%I", v->val.ip); return;
 | 
			
		||||
  case T_NET:   buffer_print(buf, "%N", v->val.net); return;
 | 
			
		||||
  case T_PAIR:	buffer_print(buf, "(%u,%u)", v->val.i >> 16, v->val.i & 0xffff); return;
 | 
			
		||||
  case T_QUAD:	buffer_print(buf, "%R", v->val.i); return;
 | 
			
		||||
  case T_EC:	ec_format(buf2, v->val.ec); buffer_print(buf, "%s", buf2); return;
 | 
			
		||||
  case T_LC:	lc_format(buf2, v->val.lc); buffer_print(buf, "%s", buf2); return;
 | 
			
		||||
  case T_RD:	rd_format(v->val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return;
 | 
			
		||||
  case T_PREFIX_SET: trie_format(v->val.ti, buf); return;
 | 
			
		||||
  case T_SET:	tree_format(v->val.t, buf); return;
 | 
			
		||||
  case T_ENUM:	buffer_print(buf, "(enum %x)%u", v->type, v->val.i); return;
 | 
			
		||||
  case T_PATH:	as_path_format(v->val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return;
 | 
			
		||||
  case T_CLIST:	int_set_format(v->val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return;
 | 
			
		||||
  case T_ECLIST: ec_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
 | 
			
		||||
  case T_LCLIST: lc_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return;
 | 
			
		||||
  case T_PATH_MASK: pm_format(v->val.path_mask, buf); return;
 | 
			
		||||
  default:	buffer_print(buf, "[unknown type %x]", v->type); return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char val_dump_buffer[1024];
 | 
			
		||||
const char *
 | 
			
		||||
val_dump(const struct f_val *v) {
 | 
			
		||||
  static buffer b = {
 | 
			
		||||
    .start = val_dump_buffer,
 | 
			
		||||
    .end = val_dump_buffer + 1024,
 | 
			
		||||
  };
 | 
			
		||||
  b.pos = b.start;
 | 
			
		||||
  val_format(v, &b);
 | 
			
		||||
  return val_dump_buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -164,6 +164,39 @@ int trie_match_net(const struct f_trie *t, const net_addr *n);
 | 
			
		||||
int trie_same(const struct f_trie *t1, const struct f_trie *t2);
 | 
			
		||||
void trie_format(const struct f_trie *t, buffer *buf);
 | 
			
		||||
 | 
			
		||||
#define F_CMP_ERROR 999
 | 
			
		||||
 | 
			
		||||
int val_same(const struct f_val *v1, const struct f_val *v2);
 | 
			
		||||
int val_compare(const struct f_val *v1, const struct f_val *v2);
 | 
			
		||||
void val_format(const struct f_val *v, buffer *buf);
 | 
			
		||||
const char *val_dump(const struct f_val *v);
 | 
			
		||||
 | 
			
		||||
static inline int val_is_ip4(const struct f_val *v)
 | 
			
		||||
{ return (v->type == T_IP) && ipa_is_ip4(v->val.ip); }
 | 
			
		||||
int val_in_range(const struct f_val *v1, const struct f_val *v2);
 | 
			
		||||
 | 
			
		||||
int clist_set_type(const struct f_tree *set, struct f_val *v);
 | 
			
		||||
static inline int eclist_set_type(const struct f_tree *set)
 | 
			
		||||
{ return set->from.type == T_EC; }
 | 
			
		||||
static inline int lclist_set_type(const struct f_tree *set)
 | 
			
		||||
{ return set->from.type == T_LC; }
 | 
			
		||||
 | 
			
		||||
const struct adata *clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
 | 
			
		||||
const struct adata *eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
 | 
			
		||||
const struct adata *lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Special undef value for paths and clists */
 | 
			
		||||
static inline int
 | 
			
		||||
undef_value(struct f_val v)
 | 
			
		||||
{
 | 
			
		||||
  return ((v.type == T_PATH) || (v.type == T_CLIST) ||
 | 
			
		||||
	  (v.type == T_ECLIST) || (v.type == T_LCLIST)) &&
 | 
			
		||||
    (v.val.ad == &null_adata);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist;
 | 
			
		||||
 | 
			
		||||
enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -165,7 +165,7 @@
 | 
			
		||||
    ARG_ANY(1);
 | 
			
		||||
    ARG_ANY(2);
 | 
			
		||||
    int i = val_compare(&v1, &v2);
 | 
			
		||||
    if (i == CMP_ERROR)
 | 
			
		||||
    if (i == F_CMP_ERROR)
 | 
			
		||||
      runtime( "Can't compare values of incompatible types" );
 | 
			
		||||
    RESULT(T_BOOL, i, (i == -1));
 | 
			
		||||
  }
 | 
			
		||||
@@ -174,7 +174,7 @@
 | 
			
		||||
    ARG_ANY(1);
 | 
			
		||||
    ARG_ANY(2);
 | 
			
		||||
    int i = val_compare(&v1, &v2);
 | 
			
		||||
    if (i == CMP_ERROR)
 | 
			
		||||
    if (i == F_CMP_ERROR)
 | 
			
		||||
      runtime( "Can't compare values of incompatible types" );
 | 
			
		||||
    RESULT(T_BOOL, i, (i != 1));
 | 
			
		||||
  }
 | 
			
		||||
@@ -188,7 +188,7 @@
 | 
			
		||||
    ARG_ANY(1);
 | 
			
		||||
    ARG_ANY(2);
 | 
			
		||||
    int i = val_in_range(&v1, &v2);
 | 
			
		||||
    if (i == CMP_ERROR)
 | 
			
		||||
    if (i == F_CMP_ERROR)
 | 
			
		||||
      runtime( "~ applied on unknown type pair" );
 | 
			
		||||
    RESULT(T_BOOL, i, !!i);
 | 
			
		||||
  }
 | 
			
		||||
@@ -197,7 +197,7 @@
 | 
			
		||||
    ARG_ANY(1);
 | 
			
		||||
    ARG_ANY(2);
 | 
			
		||||
    int i = val_in_range(&v1, &v2);
 | 
			
		||||
    if (res.val.i == CMP_ERROR)
 | 
			
		||||
    if (res.val.i == F_CMP_ERROR)
 | 
			
		||||
      runtime( "!~ applied on unknown type pair" );
 | 
			
		||||
    RESULT(T_BOOL, i, !i);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										642
									
								
								filter/filter.c
									
									
									
									
									
								
							
							
						
						
									
										642
									
								
								filter/filter.c
									
									
									
									
									
								
							@@ -50,8 +50,6 @@
 | 
			
		||||
#include "filter/f-inst.h"
 | 
			
		||||
#include "filter/data.h"
 | 
			
		||||
 | 
			
		||||
#define CMP_ERROR 999
 | 
			
		||||
 | 
			
		||||
/* Internal filter state, to be allocated on stack when executing filters */
 | 
			
		||||
struct filter_state {
 | 
			
		||||
  struct rte **rte;
 | 
			
		||||
@@ -133,507 +131,6 @@ f_instruction_name(enum f_instruction_code fi)
 | 
			
		||||
    bug("Got unknown instruction code: %d", fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Special undef value for paths and clists */
 | 
			
		||||
static inline int
 | 
			
		||||
undef_value(struct f_val v)
 | 
			
		||||
{
 | 
			
		||||
  return ((v.type == T_PATH) || (v.type == T_CLIST) ||
 | 
			
		||||
	  (v.type == T_ECLIST) || (v.type == T_LCLIST)) &&
 | 
			
		||||
    (v.val.ad == &null_adata);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct f_val f_const_empty_path = {
 | 
			
		||||
  .type = T_PATH,
 | 
			
		||||
  .val.ad = &null_adata,
 | 
			
		||||
}, f_const_empty_clist = {
 | 
			
		||||
  .type = T_CLIST,
 | 
			
		||||
  .val.ad = &null_adata,
 | 
			
		||||
}, f_const_empty_eclist = {
 | 
			
		||||
  .type = T_ECLIST,
 | 
			
		||||
  .val.ad = &null_adata,
 | 
			
		||||
}, f_const_empty_lclist = {
 | 
			
		||||
  .type = T_LCLIST,
 | 
			
		||||
  .val.ad = &null_adata,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct adata *
 | 
			
		||||
adata_empty(struct linpool *pool, int l)
 | 
			
		||||
{
 | 
			
		||||
  struct adata *res = lp_alloc(pool, sizeof(struct adata) + l);
 | 
			
		||||
  res->length = l;
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
pm_format(const struct f_path_mask *p, buffer *buf)
 | 
			
		||||
{
 | 
			
		||||
  buffer_puts(buf, "[= ");
 | 
			
		||||
 | 
			
		||||
  for (uint i=0; i<p->len; i++)
 | 
			
		||||
  {
 | 
			
		||||
    switch(p->item[i].kind)
 | 
			
		||||
    {
 | 
			
		||||
    case PM_ASN:
 | 
			
		||||
      buffer_print(buf, "%u ", p->item[i].asn);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case PM_QUESTION:
 | 
			
		||||
      buffer_puts(buf, "? ");
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case PM_ASTERISK:
 | 
			
		||||
      buffer_puts(buf, "* ");
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case PM_ASN_RANGE:
 | 
			
		||||
      buffer_print(buf, "%u..%u ", p->item[i].from, p->item[i].to);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case PM_ASN_EXPR:
 | 
			
		||||
      ASSERT(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  buffer_puts(buf, "=]");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int val_is_ip4(const struct f_val *v)
 | 
			
		||||
{ return (v->type == T_IP) && ipa_is_ip4(v->val.ip); }
 | 
			
		||||
 | 
			
		||||
static inline int
 | 
			
		||||
lcomm_cmp(lcomm v1, lcomm v2)
 | 
			
		||||
{
 | 
			
		||||
  if (v1.asn != v2.asn)
 | 
			
		||||
    return (v1.asn > v2.asn) ? 1 : -1;
 | 
			
		||||
  if (v1.ldp1 != v2.ldp1)
 | 
			
		||||
    return (v1.ldp1 > v2.ldp1) ? 1 : -1;
 | 
			
		||||
  if (v1.ldp2 != v2.ldp2)
 | 
			
		||||
    return (v1.ldp2 > v2.ldp2) ? 1 : -1;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * val_compare - compare two values
 | 
			
		||||
 * @v1: first value
 | 
			
		||||
 * @v2: second value
 | 
			
		||||
 *
 | 
			
		||||
 * Compares two values and returns -1, 0, 1 on <, =, > or CMP_ERROR on
 | 
			
		||||
 * error. Tree module relies on this giving consistent results so
 | 
			
		||||
 * that it can be used for building balanced trees.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
val_compare(const struct f_val *v1, const struct f_val *v2)
 | 
			
		||||
{
 | 
			
		||||
  if (v1->type != v2->type) {
 | 
			
		||||
    if (v1->type == T_VOID)	/* Hack for else */
 | 
			
		||||
      return -1;
 | 
			
		||||
    if (v2->type == T_VOID)
 | 
			
		||||
      return 1;
 | 
			
		||||
 | 
			
		||||
    /* IP->Quad implicit conversion */
 | 
			
		||||
    if ((v1->type == T_QUAD) && val_is_ip4(v2))
 | 
			
		||||
      return uint_cmp(v1->val.i, ipa_to_u32(v2->val.ip));
 | 
			
		||||
    if (val_is_ip4(v1) && (v2->type == T_QUAD))
 | 
			
		||||
      return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i);
 | 
			
		||||
 | 
			
		||||
    debug( "Types do not match in val_compare\n" );
 | 
			
		||||
    return CMP_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (v1->type) {
 | 
			
		||||
  case T_VOID:
 | 
			
		||||
    return 0;
 | 
			
		||||
  case T_ENUM:
 | 
			
		||||
  case T_INT:
 | 
			
		||||
  case T_BOOL:
 | 
			
		||||
  case T_PAIR:
 | 
			
		||||
  case T_QUAD:
 | 
			
		||||
    return uint_cmp(v1->val.i, v2->val.i);
 | 
			
		||||
  case T_EC:
 | 
			
		||||
  case T_RD:
 | 
			
		||||
    return u64_cmp(v1->val.ec, v2->val.ec);
 | 
			
		||||
  case T_LC:
 | 
			
		||||
    return lcomm_cmp(v1->val.lc, v2->val.lc);
 | 
			
		||||
  case T_IP:
 | 
			
		||||
    return ipa_compare(v1->val.ip, v2->val.ip);
 | 
			
		||||
  case T_NET:
 | 
			
		||||
    return net_compare(v1->val.net, v2->val.net);
 | 
			
		||||
  case T_STRING:
 | 
			
		||||
    return strcmp(v1->val.s, v2->val.s);
 | 
			
		||||
  default:
 | 
			
		||||
    return CMP_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
pm_same(const struct f_path_mask *m1, const struct f_path_mask *m2)
 | 
			
		||||
{
 | 
			
		||||
  if (m1->len != m2->len)
 | 
			
		||||
 | 
			
		||||
  for (uint i=0; i<m1->len; i++)
 | 
			
		||||
  {
 | 
			
		||||
    if (m1->item[i].kind != m2->item[i].kind)
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
    switch (m1->item[i].kind) {
 | 
			
		||||
      case PM_ASN:
 | 
			
		||||
	if (m1->item[i].asn != m2->item[i].asn)
 | 
			
		||||
	  return 0;
 | 
			
		||||
	break;
 | 
			
		||||
      case PM_ASN_EXPR:
 | 
			
		||||
	if (!f_same(m1->item[i].expr, m2->item[i].expr))
 | 
			
		||||
	  return 0;
 | 
			
		||||
	break;
 | 
			
		||||
      case PM_ASN_RANGE:
 | 
			
		||||
	if (m1->item[i].from != m2->item[i].from)
 | 
			
		||||
	  return 0;
 | 
			
		||||
	if (m1->item[i].to != m2->item[i].to)
 | 
			
		||||
	  return 0;
 | 
			
		||||
	break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * val_same - compare two values
 | 
			
		||||
 * @v1: first value
 | 
			
		||||
 * @v2: second value
 | 
			
		||||
 *
 | 
			
		||||
 * Compares two values and returns 1 if they are same and 0 if not.
 | 
			
		||||
 * Comparison of values of different types is valid and returns 0.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
val_same(const struct f_val *v1, const struct f_val *v2)
 | 
			
		||||
{
 | 
			
		||||
  int rc;
 | 
			
		||||
 | 
			
		||||
  rc = val_compare(v1, v2);
 | 
			
		||||
  if (rc != CMP_ERROR)
 | 
			
		||||
    return !rc;
 | 
			
		||||
 | 
			
		||||
  if (v1->type != v2->type)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  switch (v1->type) {
 | 
			
		||||
  case T_PATH_MASK:
 | 
			
		||||
    return pm_same(v1->val.path_mask, v2->val.path_mask);
 | 
			
		||||
  case T_PATH:
 | 
			
		||||
  case T_CLIST:
 | 
			
		||||
  case T_ECLIST:
 | 
			
		||||
  case T_LCLIST:
 | 
			
		||||
    return adata_same(v1->val.ad, v2->val.ad);
 | 
			
		||||
  case T_SET:
 | 
			
		||||
    return same_tree(v1->val.t, v2->val.t);
 | 
			
		||||
  case T_PREFIX_SET:
 | 
			
		||||
    return trie_same(v1->val.ti, v2->val.ti);
 | 
			
		||||
  default:
 | 
			
		||||
    bug("Invalid type in val_same(): %x", v1->type);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
clist_set_type(const struct f_tree *set, struct f_val *v)
 | 
			
		||||
{
 | 
			
		||||
  switch (set->from.type)
 | 
			
		||||
  {
 | 
			
		||||
  case T_PAIR:
 | 
			
		||||
    v->type = T_PAIR;
 | 
			
		||||
    return 1;
 | 
			
		||||
 | 
			
		||||
  case T_QUAD:
 | 
			
		||||
    v->type = T_QUAD;
 | 
			
		||||
    return 1;
 | 
			
		||||
 | 
			
		||||
  case T_IP:
 | 
			
		||||
    if (val_is_ip4(&(set->from)) && val_is_ip4(&(set->to)))
 | 
			
		||||
    {
 | 
			
		||||
      v->type = T_QUAD;
 | 
			
		||||
      return 1;
 | 
			
		||||
    }
 | 
			
		||||
    /* Fall through */
 | 
			
		||||
  default:
 | 
			
		||||
    v->type = T_VOID;
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int
 | 
			
		||||
eclist_set_type(const struct f_tree *set)
 | 
			
		||||
{ return set->from.type == T_EC; }
 | 
			
		||||
 | 
			
		||||
static inline int
 | 
			
		||||
lclist_set_type(const struct f_tree *set)
 | 
			
		||||
{ return set->from.type == T_LC; }
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
clist_match_set(const struct adata *clist, const struct f_tree *set)
 | 
			
		||||
{
 | 
			
		||||
  if (!clist)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
  if (!clist_set_type(set, &v))
 | 
			
		||||
    return CMP_ERROR;
 | 
			
		||||
 | 
			
		||||
  u32 *l = (u32 *) clist->data;
 | 
			
		||||
  u32 *end = l + clist->length/4;
 | 
			
		||||
 | 
			
		||||
  while (l < end) {
 | 
			
		||||
    v.val.i = *l++;
 | 
			
		||||
    if (find_tree(set, &v))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
eclist_match_set(const struct adata *list, const struct f_tree *set)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if (!eclist_set_type(set))
 | 
			
		||||
    return CMP_ERROR;
 | 
			
		||||
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  v.type = T_EC;
 | 
			
		||||
  for (i = 0; i < len; i += 2) {
 | 
			
		||||
    v.val.ec = ec_get(l, i);
 | 
			
		||||
    if (find_tree(set, &v))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
lclist_match_set(const struct adata *list, const struct f_tree *set)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if (!lclist_set_type(set))
 | 
			
		||||
    return CMP_ERROR;
 | 
			
		||||
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  v.type = T_LC;
 | 
			
		||||
  for (i = 0; i < len; i += 3) {
 | 
			
		||||
    v.val.lc = lc_get(l, i);
 | 
			
		||||
    if (find_tree(set, &v))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct adata *
 | 
			
		||||
clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  int tree = (set->type == T_SET);	/* 1 -> set is T_SET, 0 -> set is T_CLIST */
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
  if (tree)
 | 
			
		||||
    clist_set_type(set->val.t, &v);
 | 
			
		||||
  else
 | 
			
		||||
    v.type = T_PAIR;
 | 
			
		||||
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  u32 tmp[len];
 | 
			
		||||
  u32 *k = tmp;
 | 
			
		||||
  u32 *end = l + len;
 | 
			
		||||
 | 
			
		||||
  while (l < end) {
 | 
			
		||||
    v.val.i = *l++;
 | 
			
		||||
    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
 | 
			
		||||
    if ((tree ? !!find_tree(set->val.t, &v) : int_set_contains(set->val.ad, v.val.i)) == pos)
 | 
			
		||||
      *k++ = v.val.i;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint nl = (k - tmp) * sizeof(u32);
 | 
			
		||||
  if (nl == list->length)
 | 
			
		||||
    return list;
 | 
			
		||||
 | 
			
		||||
  struct adata *res = adata_empty(pool, nl);
 | 
			
		||||
  memcpy(res->data, tmp, nl);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct adata *
 | 
			
		||||
eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  int tree = (set->type == T_SET);	/* 1 -> set is T_SET, 0 -> set is T_CLIST */
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  u32 tmp[len];
 | 
			
		||||
  u32 *k = tmp;
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  v.type = T_EC;
 | 
			
		||||
  for (i = 0; i < len; i += 2) {
 | 
			
		||||
    v.val.ec = ec_get(l, i);
 | 
			
		||||
    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
 | 
			
		||||
    if ((tree ? !!find_tree(set->val.t, &v) : ec_set_contains(set->val.ad, v.val.ec)) == pos) {
 | 
			
		||||
      *k++ = l[i];
 | 
			
		||||
      *k++ = l[i+1];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint nl = (k - tmp) * sizeof(u32);
 | 
			
		||||
  if (nl == list->length)
 | 
			
		||||
    return list;
 | 
			
		||||
 | 
			
		||||
  struct adata *res = adata_empty(pool, nl);
 | 
			
		||||
  memcpy(res->data, tmp, nl);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct adata *
 | 
			
		||||
lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
 | 
			
		||||
{
 | 
			
		||||
  if (!list)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  int tree = (set->type == T_SET);	/* 1 -> set is T_SET, 0 -> set is T_CLIST */
 | 
			
		||||
  struct f_val v;
 | 
			
		||||
 | 
			
		||||
  int len = int_set_get_size(list);
 | 
			
		||||
  u32 *l = int_set_get_data(list);
 | 
			
		||||
  u32 tmp[len];
 | 
			
		||||
  u32 *k = tmp;
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  v.type = T_LC;
 | 
			
		||||
  for (i = 0; i < len; i += 3) {
 | 
			
		||||
    v.val.lc = lc_get(l, i);
 | 
			
		||||
    /* pos && member(val, set) || !pos && !member(val, set),  member() depends on tree */
 | 
			
		||||
    if ((tree ? !!find_tree(set->val.t, &v) : lc_set_contains(set->val.ad, v.val.lc)) == pos)
 | 
			
		||||
      k = lc_copy(k, l+i);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint nl = (k - tmp) * sizeof(u32);
 | 
			
		||||
  if (nl == list->length)
 | 
			
		||||
    return list;
 | 
			
		||||
 | 
			
		||||
  struct adata *res = adata_empty(pool, nl);
 | 
			
		||||
  memcpy(res->data, tmp, nl);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * val_in_range - implement |~| operator
 | 
			
		||||
 * @v1: element
 | 
			
		||||
 * @v2: set
 | 
			
		||||
 *
 | 
			
		||||
 * Checks if @v1 is element (|~| operator) of @v2.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
val_in_range(const struct f_val *v1, const struct f_val *v2)
 | 
			
		||||
{
 | 
			
		||||
  if ((v1->type == T_PATH) && (v2->type == T_PATH_MASK))
 | 
			
		||||
    return as_path_match(v1->val.ad, v2->val.path_mask);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_INT) && (v2->type == T_PATH))
 | 
			
		||||
    return as_path_contains(v2->val.ad, v1->val.i, 1);
 | 
			
		||||
 | 
			
		||||
  if (((v1->type == T_PAIR) || (v1->type == T_QUAD)) && (v2->type == T_CLIST))
 | 
			
		||||
    return int_set_contains(v2->val.ad, v1->val.i);
 | 
			
		||||
  /* IP->Quad implicit conversion */
 | 
			
		||||
  if (val_is_ip4(v1) && (v2->type == T_CLIST))
 | 
			
		||||
    return int_set_contains(v2->val.ad, ipa_to_u32(v1->val.ip));
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_EC) && (v2->type == T_ECLIST))
 | 
			
		||||
    return ec_set_contains(v2->val.ad, v1->val.ec);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_LC) && (v2->type == T_LCLIST))
 | 
			
		||||
    return lc_set_contains(v2->val.ad, v1->val.lc);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_STRING) && (v2->type == T_STRING))
 | 
			
		||||
    return patmatch(v2->val.s, v1->val.s);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_IP) && (v2->type == T_NET))
 | 
			
		||||
    return ipa_in_netX(v1->val.ip, v2->val.net);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_NET) && (v2->type == T_NET))
 | 
			
		||||
    return net_in_netX(v1->val.net, v2->val.net);
 | 
			
		||||
 | 
			
		||||
  if ((v1->type == T_NET) && (v2->type == T_PREFIX_SET))
 | 
			
		||||
    return trie_match_net(v2->val.ti, v1->val.net);
 | 
			
		||||
 | 
			
		||||
  if (v2->type != T_SET)
 | 
			
		||||
    return CMP_ERROR;
 | 
			
		||||
 | 
			
		||||
  /* With integrated Quad<->IP implicit conversion */
 | 
			
		||||
  if ((v1->type == v2->val.t->from.type) ||
 | 
			
		||||
      ((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to))))
 | 
			
		||||
    return !!find_tree(v2->val.t, v1);
 | 
			
		||||
 | 
			
		||||
  if (v1->type == T_CLIST)
 | 
			
		||||
    return clist_match_set(v1->val.ad, v2->val.t);
 | 
			
		||||
 | 
			
		||||
  if (v1->type == T_ECLIST)
 | 
			
		||||
    return eclist_match_set(v1->val.ad, v2->val.t);
 | 
			
		||||
 | 
			
		||||
  if (v1->type == T_LCLIST)
 | 
			
		||||
    return lclist_match_set(v1->val.ad, v2->val.t);
 | 
			
		||||
 | 
			
		||||
  if (v1->type == T_PATH)
 | 
			
		||||
    return as_path_match_set(v1->val.ad, v2->val.t);
 | 
			
		||||
 | 
			
		||||
  return CMP_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * val_format - format filter value
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
val_format(const struct f_val *v, buffer *buf)
 | 
			
		||||
{
 | 
			
		||||
  char buf2[1024];
 | 
			
		||||
  switch (v->type)
 | 
			
		||||
  {
 | 
			
		||||
  case T_VOID:	buffer_puts(buf, "(void)"); return;
 | 
			
		||||
  case T_BOOL:	buffer_puts(buf, v->val.i ? "TRUE" : "FALSE"); return;
 | 
			
		||||
  case T_INT:	buffer_print(buf, "%u", v->val.i); return;
 | 
			
		||||
  case T_STRING: buffer_print(buf, "%s", v->val.s); return;
 | 
			
		||||
  case T_IP:	buffer_print(buf, "%I", v->val.ip); return;
 | 
			
		||||
  case T_NET:   buffer_print(buf, "%N", v->val.net); return;
 | 
			
		||||
  case T_PAIR:	buffer_print(buf, "(%u,%u)", v->val.i >> 16, v->val.i & 0xffff); return;
 | 
			
		||||
  case T_QUAD:	buffer_print(buf, "%R", v->val.i); return;
 | 
			
		||||
  case T_EC:	ec_format(buf2, v->val.ec); buffer_print(buf, "%s", buf2); return;
 | 
			
		||||
  case T_LC:	lc_format(buf2, v->val.lc); buffer_print(buf, "%s", buf2); return;
 | 
			
		||||
  case T_RD:	rd_format(v->val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return;
 | 
			
		||||
  case T_PREFIX_SET: trie_format(v->val.ti, buf); return;
 | 
			
		||||
  case T_SET:	tree_format(v->val.t, buf); return;
 | 
			
		||||
  case T_ENUM:	buffer_print(buf, "(enum %x)%u", v->type, v->val.i); return;
 | 
			
		||||
  case T_PATH:	as_path_format(v->val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return;
 | 
			
		||||
  case T_CLIST:	int_set_format(v->val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return;
 | 
			
		||||
  case T_ECLIST: ec_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
 | 
			
		||||
  case T_LCLIST: lc_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return;
 | 
			
		||||
  case T_PATH_MASK: pm_format(v->val.path_mask, buf); return;
 | 
			
		||||
  default:	buffer_print(buf, "[unknown type %x]", v->type); return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static inline void f_cache_eattrs(struct filter_state *fs)
 | 
			
		||||
{
 | 
			
		||||
  fs->eattrs = &((*fs->rte)->attrs->eattrs);
 | 
			
		||||
@@ -687,19 +184,6 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
 | 
			
		||||
#define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1)
 | 
			
		||||
static const char f_dump_line_indent_str[] = "                                ";
 | 
			
		||||
 | 
			
		||||
static char val_dump_buffer[1024];
 | 
			
		||||
 | 
			
		||||
static const char *
 | 
			
		||||
val_dump(const struct f_val *v) {
 | 
			
		||||
  static buffer b = {
 | 
			
		||||
    .start = val_dump_buffer,
 | 
			
		||||
    .end = val_dump_buffer + 1024,
 | 
			
		||||
  };
 | 
			
		||||
  b.pos = b.start;
 | 
			
		||||
  val_format(v, &b);
 | 
			
		||||
  return val_dump_buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void f_dump_line(const struct f_line *dest, int indent);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -891,122 +375,6 @@ f_same(const struct f_line *fl1, const struct f_line *fl2)
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
  case FI_ADD: /* fall through */
 | 
			
		||||
  case FI_SUBTRACT:
 | 
			
		||||
  case FI_MULTIPLY:
 | 
			
		||||
  case FI_DIVIDE:
 | 
			
		||||
  case FI_OR:
 | 
			
		||||
  case FI_AND:
 | 
			
		||||
  case FI_PAIR_CONSTRUCT:
 | 
			
		||||
  case FI_EC_CONSTRUCT:
 | 
			
		||||
  case FI_NEQ:
 | 
			
		||||
  case FI_EQ:
 | 
			
		||||
  case FI_LT:
 | 
			
		||||
  case FI_LTE: TWOARGS; break;
 | 
			
		||||
 | 
			
		||||
  case FI_PATHMASK_CONSTRUCT: if (!pm_same(f1->a[0].p, f2->a[0].p)) return 0; break;
 | 
			
		||||
 | 
			
		||||
  case FI_NOT: ONEARG; break;
 | 
			
		||||
  case FI_NOT_MATCH:
 | 
			
		||||
  case FI_MATCH: TWOARGS; break;
 | 
			
		||||
  case FI_DEFINED: ONEARG; break;
 | 
			
		||||
  case FI_TYPE: ONEARG; break;
 | 
			
		||||
 | 
			
		||||
  case FI_LC_CONSTRUCT:
 | 
			
		||||
    THREEARGS;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case FI_SET:
 | 
			
		||||
    ARG(2);
 | 
			
		||||
    {
 | 
			
		||||
      struct symbol *s1, *s2;
 | 
			
		||||
      s1 = f1->a[0].p;
 | 
			
		||||
      s2 = f2->a[0].p;
 | 
			
		||||
      if (strcmp(s1->name, s2->name))
 | 
			
		||||
	return 0;
 | 
			
		||||
      if (s1->class != s2->class)
 | 
			
		||||
	return 0;
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case FI_CONSTANT:
 | 
			
		||||
    switch (f1->aux) {
 | 
			
		||||
 | 
			
		||||
    case T_PREFIX_SET:
 | 
			
		||||
      if (!trie_same(f1->a[1].p, f2->a[1].p))
 | 
			
		||||
	return 0;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case T_SET:
 | 
			
		||||
      if (!same_tree(f1->a[1].p, f2->a[1].p))
 | 
			
		||||
	return 0;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case T_STRING:
 | 
			
		||||
      if (strcmp(f1->a[1].p, f2->a[1].p))
 | 
			
		||||
	return 0;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      A2_SAME;
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case FI_CONSTANT_INDIRECT:
 | 
			
		||||
    if (!val_same(* (struct f_val *) f1->a[0].p, * (struct f_val *) f2->a[0].p))
 | 
			
		||||
      return 0;
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case FI_VARIABLE:
 | 
			
		||||
    if (strcmp((char *) f1->a[1].p, (char *) f2->a[1].p))
 | 
			
		||||
      return 0;
 | 
			
		||||
    break;
 | 
			
		||||
  case FI_PRINT: case FI_LENGTH: ONEARG; break;
 | 
			
		||||
  case FI_CONDITION: THREEARGS; break;
 | 
			
		||||
  case FI_NOP: case FI_EMPTY: break;
 | 
			
		||||
  case FI_PRINT_AND_DIE: ONEARG; A2_SAME; break;
 | 
			
		||||
  case FI_PREF_GET:
 | 
			
		||||
  case FI_RTA_GET: A2_SAME; break;
 | 
			
		||||
  case FI_EA_GET: A2_SAME; break;
 | 
			
		||||
  case FI_PREF_SET:
 | 
			
		||||
  case FI_RTA_SET:
 | 
			
		||||
  case FI_EA_SET: ONEARG; A2_SAME; break;
 | 
			
		||||
 | 
			
		||||
  case FI_RETURN: ONEARG; break;
 | 
			
		||||
  case FI_ROA_MAXLEN: ONEARG; break;
 | 
			
		||||
  case FI_ROA_ASN: ONEARG; break;
 | 
			
		||||
  case FI_SADR_SRC: ONEARG; break;
 | 
			
		||||
  case FI_IP: ONEARG; break;
 | 
			
		||||
  case FI_IS_V4: ONEARG; break;
 | 
			
		||||
  case FI_ROUTE_DISTINGUISHER: ONEARG; break;
 | 
			
		||||
  case FI_CALL: /* Call rewriting trickery to avoid exponential behaviour */
 | 
			
		||||
             ONEARG;
 | 
			
		||||
	     if (!i_same(f1->a[1].p, f2->a[1].p))
 | 
			
		||||
	       return 0;
 | 
			
		||||
	     f2->a[1].p = f1->a[1].p;
 | 
			
		||||
	     break;
 | 
			
		||||
  case FI_CLEAR_LOCAL_VARS: break; /* internal instruction */
 | 
			
		||||
  case FI_SWITCH: ONEARG; if (!same_tree(f1->a[1].p, f2->a[1].p)) return 0; break;
 | 
			
		||||
  case FI_IP_MASK: TWOARGS; break;
 | 
			
		||||
  case FI_PATH_PREPEND: TWOARGS; break;
 | 
			
		||||
  case FI_CLIST_ADD_DEL: TWOARGS; break;
 | 
			
		||||
  case FI_AS_PATH_FIRST:
 | 
			
		||||
  case FI_AS_PATH_LAST:
 | 
			
		||||
  case FI_AS_PATH_LAST_NAG: ONEARG; break;
 | 
			
		||||
  case FI_ROA_CHECK:
 | 
			
		||||
    TWOARGS;
 | 
			
		||||
    /* FIXME: ROA check results may change anyway */
 | 
			
		||||
    if (strcmp(f1->a[2].rtc->name,
 | 
			
		||||
	       f2->a[2].rtc->name))
 | 
			
		||||
      return 0;
 | 
			
		||||
    break;
 | 
			
		||||
  case FI_FORMAT: ONEARG; break;
 | 
			
		||||
  case FI_ASSERT: ONEARG; break;
 | 
			
		||||
  default:
 | 
			
		||||
    bug( "Unknown instruction %d in same (%c)", f1->fi_code, f1->fi_code & 0xff);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * f_run - run a filter for a route
 | 
			
		||||
 * @filter: filter to run
 | 
			
		||||
@@ -1133,6 +501,16 @@ f_eval_int(const struct f_line *expr)
 | 
			
		||||
  return val.val.i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum filter_return
 | 
			
		||||
f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf)
 | 
			
		||||
{
 | 
			
		||||
  struct f_val val;
 | 
			
		||||
  enum filter_return fret = f_eval(expr, tmp_pool, &val);
 | 
			
		||||
  if (fret > F_RETURN)
 | 
			
		||||
    val_format(&val, buf);
 | 
			
		||||
  return fret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * filter_same - compare two filters
 | 
			
		||||
 * @new: first filter to be compared
 | 
			
		||||
 
 | 
			
		||||
@@ -40,17 +40,13 @@ struct rte;
 | 
			
		||||
 | 
			
		||||
enum filter_return f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags);
 | 
			
		||||
enum filter_return f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool);
 | 
			
		||||
enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres);
 | 
			
		||||
uint f_eval_int(const struct f_line *expr);
 | 
			
		||||
enum filter_return f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf);
 | 
			
		||||
 | 
			
		||||
char *filter_name(struct filter *filter);
 | 
			
		||||
int filter_same(struct filter *new, struct filter *old);
 | 
			
		||||
int f_same(const struct f_line *f1, const struct f_line *f2);
 | 
			
		||||
 | 
			
		||||
int val_compare(const struct f_val *v1, const struct f_val *v2);
 | 
			
		||||
 | 
			
		||||
void val_format(const struct f_val *v, buffer *buf);
 | 
			
		||||
 | 
			
		||||
#define FILTER_ACCEPT NULL
 | 
			
		||||
#define FILTER_REJECT ((void *) 1)
 | 
			
		||||
#define FILTER_UNDEF  ((void *) 2)	/* Used in BGP */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user