diff --git a/filter/Makefile b/filter/Makefile index 78d39638..df7ff3f9 100644 --- a/filter/Makefile +++ b/filter/Makefile @@ -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) diff --git a/filter/data.c b/filter/data.c new file mode 100644 index 00000000..b389b8e8 --- /dev/null +++ b/filter/data.c @@ -0,0 +1,519 @@ +/* + * Filters: utility functions + * + * (c) 1998 Pavel Machek + * (c) 2019 Maria Matejka + * + * 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; ilen; 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; ilen; 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; +} + diff --git a/filter/data.h b/filter/data.h index 58db3e44..6ef2024f 100644 --- a/filter/data.h +++ b/filter/data.h @@ -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 diff --git a/filter/f-inst.c b/filter/f-inst.c index afb895c5..96d6108c 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -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); } diff --git a/filter/filter.c b/filter/filter.c index 0cb56fe4..0bcf9836 100644 --- a/filter/filter.c +++ b/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; ilen; 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; ilen; 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 diff --git a/filter/filter.h b/filter/filter.h index 9b3886fb..9f1a8e50 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -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 */ diff --git a/nest/cmds.c b/nest/cmds.c index 2b83033f..da4015cf 100644 --- a/nest/cmds.c +++ b/nest/cmds.c @@ -15,7 +15,6 @@ #include "lib/string.h" #include "lib/resource.h" #include "filter/filter.h" -#include "filter/data.h" extern int shutting_down; extern int configuring; @@ -98,16 +97,14 @@ cmd_show_memory(void) void cmd_eval(const struct f_line *expr) { - /* TODO: Return directly the string from eval */ - struct f_val v; - if (f_eval(expr, this_cli->parser_pool, &v) > F_RETURN) + buffer buf; + LOG_BUFFER_INIT(buf); + + if (f_eval_buf(expr, this_cli->parser_pool, &buf) > F_RETURN) { cli_msg(8008, "runtime error"); return; } - buffer buf; - LOG_BUFFER_INIT(buf); - val_format(&v, &buf); cli_msg(23, "%s", buf.start); }