summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--filter/filter.c189
1 files changed, 96 insertions, 93 deletions
diff --git a/filter/filter.c b/filter/filter.c
index 37cf16a3..1e608138 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -50,6 +50,16 @@
#define CMP_ERROR 999
+/* Internal filter state, to be allocated on stack when executing filters */
+struct filter_state {
+ struct rte **rte;
+ struct rta *old_rta;
+ struct ea_list **eattrs;
+ struct linpool *pool;
+ struct buffer buf;
+ int flags;
+};
+
void (*bt_assert_hook)(int result, struct f_inst *assert);
static struct adata undef_adata; /* adata of length 0 used for undefined */
@@ -536,65 +546,59 @@ val_format(struct f_val v, buffer *buf)
}
}
-static struct rte **f_rte;
-static struct rta *f_old_rta;
-static struct ea_list **f_eattrs;
-static struct linpool *f_pool;
-static struct buffer f_buf;
-static int f_flags;
-static inline void f_cache_eattrs(void)
+static inline void f_cache_eattrs(struct filter_state *fs)
{
- f_eattrs = &((*f_rte)->attrs->eattrs);
+ fs->eattrs = &((*fs->rte)->attrs->eattrs);
}
-static inline void f_rte_cow(void)
+static inline void f_rte_cow(struct filter_state *fs)
{
- if (!((*f_rte)->flags & REF_COW))
+ if (!((*fs->rte)->flags & REF_COW))
return;
- *f_rte = rte_do_cow(*f_rte);
+ *fs->rte = rte_cow(*fs->rte);
}
/*
* rta_cow - prepare rta for modification by filter
*/
static void
-f_rta_cow(void)
+f_rta_cow(struct filter_state *fs)
{
- if (!rta_is_cached((*f_rte)->attrs))
+ if (!rta_is_cached((*fs->rte)->attrs))
return;
/* Prepare to modify rte */
- f_rte_cow();
+ f_rte_cow(fs);
/* Store old rta to free it later, it stores reference from rte_cow() */
- f_old_rta = (*f_rte)->attrs;
+ fs->old_rta = (*fs->rte)->attrs;
/*
* Get shallow copy of rta. Fields eattrs and nexthops of rta are shared
- * with f_old_rta (they will be copied when the cached rta will be obtained
+ * with fs->old_rta (they will be copied when the cached rta will be obtained
* at the end of f_run()), also the lock of hostentry is inherited (we
* suppose hostentry is not changed by filters).
*/
- (*f_rte)->attrs = rta_do_cow((*f_rte)->attrs, f_pool);
+ (*fs->rte)->attrs = rta_do_cow((*fs->rte)->attrs, fs->pool);
/* Re-cache the ea_list */
- f_cache_eattrs();
+ f_cache_eattrs(fs);
}
static char *
-val_format_str(struct f_val v) {
+val_format_str(struct filter_state *fs, struct f_val v) {
buffer b;
LOG_BUFFER_INIT(b);
val_format(v, &b);
- return lp_strdup(f_pool, b.start);
+ return lp_strdup(fs->pool, b.start);
}
static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
#define runtime(fmt, ...) do { \
- if (!(f_flags & FF_SILENT)) \
+ if (!(fs->flags & FF_SILENT)) \
log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \
res.type = T_RETURN; \
res.val.i = F_ERROR; \
@@ -609,21 +613,22 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
n, f_instruction_name(what->fi_code), t, v##n.type);
#define INTERPRET(val, what_) \
- val = interpret(what_); \
+ val = interpret(fs, what_); \
if (val.type & T_RETURN) \
return val;
#define ACCESS_RTE \
- do { if (!f_rte) runtime("No route to access"); } while (0)
+ do { if (!fs->rte) runtime("No route to access"); } while (0)
#define ACCESS_EATTRS \
- do { if (!f_eattrs) f_cache_eattrs(); } while (0)
+ do { if (!fs->eattrs) f_cache_eattrs(fs); } while (0)
#define BITFIELD_MASK(what) \
(1u << (what->a2.i >> 24))
/**
* interpret
+ * @fs: filter state
* @what: filter to interpret
*
* Interpret given tree of filter instructions. This is core function
@@ -640,7 +645,7 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
* memory managment.
*/
static struct f_val
-interpret(struct f_inst *what)
+interpret(struct filter_state *fs, struct f_inst *what)
{
struct symbol *sym;
struct f_val v1, v2, v3, res = { .type = T_VOID }, *vp;
@@ -761,7 +766,7 @@ interpret(struct f_inst *what)
struct f_path_mask *tt = what->a1.p, *vbegin, **vv = &vbegin;
while (tt) {
- *vv = lp_alloc(f_pool, sizeof(struct f_path_mask));
+ *vv = lp_alloc(fs->pool, sizeof(struct f_path_mask));
if (tt->kind == PM_ASN_EXPR) {
struct f_val res;
INTERPRET(res, (struct f_inst *) tt->val);
@@ -895,7 +900,7 @@ interpret(struct f_inst *what)
break;
case FI_PRINT:
ARG_ANY(1);
- val_format(v1, &f_buf);
+ val_format(v1, &fs->buf);
break;
case FI_CONDITION: /* ? has really strange error value, so we can implement if ... else nicely :-) */
ARG(1, T_BOOL);
@@ -912,8 +917,8 @@ interpret(struct f_inst *what)
case FI_PRINT_AND_DIE:
ARG_ANY(1);
if ((what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) &&
- !(f_flags & FF_SILENT))
- log_commit(*L_INFO, &f_buf);
+ !(fs->flags & FF_SILENT))
+ log_commit(*L_INFO, &fs->buf);
switch (what->a2.i) {
case F_QUITBIRD:
@@ -935,14 +940,14 @@ interpret(struct f_inst *what)
case FI_RTA_GET: /* rta access */
{
ACCESS_RTE;
- struct rta *rta = (*f_rte)->attrs;
+ struct rta *rta = (*fs->rte)->attrs;
res.type = what->aux;
switch (what->a2.i)
{
case SA_FROM: res.val.ip = rta->from; break;
case SA_GW: res.val.ip = rta->nh.gw; break;
- case SA_NET: res.val.net = (*f_rte)->net->n.addr; break;
+ case SA_NET: res.val.net = (*fs->rte)->net->n.addr; break;
case SA_PROTO: res.val.s = rta->src->proto->name; break;
case SA_SOURCE: res.val.i = rta->source; break;
case SA_SCOPE: res.val.i = rta->scope; break;
@@ -961,9 +966,9 @@ interpret(struct f_inst *what)
if (what->aux != v1.type)
runtime( "Attempt to set static attribute to incompatible type" );
- f_rta_cow();
+ f_rta_cow(fs);
{
- struct rta *rta = (*f_rte)->attrs;
+ struct rta *rta = (*fs->rte)->attrs;
switch (what->a2.i)
{
@@ -1027,7 +1032,7 @@ interpret(struct f_inst *what)
{
u16 code = what->a2.i;
int f_type = what->aux >> 8;
- eattr *e = ea_find(*f_eattrs, code);
+ eattr *e = ea_find(*fs->eattrs, code);
if (!e) {
/* A special case: undefined as_path looks like empty as_path */
@@ -1114,7 +1119,7 @@ interpret(struct f_inst *what)
ACCESS_EATTRS;
ARG_ANY(1);
{
- struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr));
+ struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
u16 code = what->a2.i;
int f_type = what->aux >> 8;
@@ -1151,7 +1156,7 @@ interpret(struct f_inst *what)
if (v1.type != T_IP)
runtime( "Setting ip attribute to non-ip value" );
int len = sizeof(ip_addr);
- struct adata *ad = lp_alloc(f_pool, sizeof(struct adata) + len);
+ struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len);
ad->length = len;
(* (ip_addr *) ad->data) = v1.val.ip;
l->attrs[0].u.ptr = ad;
@@ -1166,7 +1171,7 @@ interpret(struct f_inst *what)
runtime( "Setting bit in bitfield attribute to non-bool value" );
{
/* First, we have to find the old value */
- eattr *e = ea_find(*f_eattrs, code);
+ eattr *e = ea_find(*fs->eattrs, code);
u32 data = e ? e->u.data : 0;
if (v1.val.i)
@@ -1198,23 +1203,23 @@ interpret(struct f_inst *what)
default: bug("Unknown type in e,S");
}
- f_rta_cow();
- l->next = *f_eattrs;
- *f_eattrs = l;
+ f_rta_cow(fs);
+ l->next = *fs->eattrs;
+ *fs->eattrs = l;
}
break;
case FI_PREF_GET:
ACCESS_RTE;
res.type = T_INT;
- res.val.i = (*f_rte)->pref;
+ res.val.i = (*fs->rte)->pref;
break;
case FI_PREF_SET:
ACCESS_RTE;
ARG(1,T_INT);
if (v1.val.i > 0xFFFF)
runtime( "Setting preference value out of bounds" );
- f_rte_cow();
- (*f_rte)->pref = v1.val.i;
+ f_rte_cow(fs);
+ (*fs->rte)->pref = v1.val.i;
break;
case FI_LENGTH: /* Get length of */
ARG_ANY(1);
@@ -1235,7 +1240,7 @@ interpret(struct f_inst *what)
{
net_addr_ip6_sadr *net = (void *) v1.val.net;
- net_addr *src = lp_alloc(f_pool, sizeof(net_addr_ip6));
+ net_addr *src = lp_alloc(fs->pool, sizeof(net_addr_ip6));
net_fill_ip6(src, net->src_prefix, net->src_pxlen);
res.type = T_NET;
@@ -1304,7 +1309,7 @@ interpret(struct f_inst *what)
return res;
case FI_CALL: /* CALL: this is special: if T_RETURN and returning some value, mask it out */
ARG_ANY(1);
- res = interpret(what->a2.p);
+ res = interpret(fs, what->a2.p);
if (res.type == T_RETURN)
return res;
res.type &= ~T_RETURN;
@@ -1342,14 +1347,14 @@ interpret(struct f_inst *what)
case FI_EMPTY: /* Create empty attribute */
res.type = what->aux;
- res.val.ad = adata_empty(f_pool, 0);
+ res.val.ad = adata_empty(fs->pool, 0);
break;
case FI_PATH_PREPEND: /* Path prepend */
ARG(1, T_PATH);
ARG(2, T_INT);
res.type = T_PATH;
- res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i);
+ res.val.ad = as_path_prepend(fs->pool, v1.val.ad, v2.val.i);
break;
case FI_CLIST_ADD_DEL: /* (Extended) Community list add or delete */
@@ -1380,7 +1385,7 @@ interpret(struct f_inst *what)
runtime("Can't filter integer");
res.type = T_PATH;
- res.val.ad = as_path_filter(f_pool, v1.val.ad, set, key, pos);
+ res.val.ad = as_path_filter(fs->pool, v1.val.ad, set, key, pos);
}
else if (v1.type == T_CLIST)
{
@@ -1408,22 +1413,22 @@ interpret(struct f_inst *what)
if (arg_set == 1)
runtime("Can't add set");
else if (!arg_set)
- res.val.ad = int_set_add(f_pool, v1.val.ad, n);
+ res.val.ad = int_set_add(fs->pool, v1.val.ad, n);
else
- res.val.ad = int_set_union(f_pool, v1.val.ad, v2.val.ad);
+ res.val.ad = int_set_union(fs->pool, v1.val.ad, v2.val.ad);
break;
case 'd':
if (!arg_set)
- res.val.ad = int_set_del(f_pool, v1.val.ad, n);
+ res.val.ad = int_set_del(fs->pool, v1.val.ad, n);
else
- res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 0);
+ res.val.ad = clist_filter(fs->pool, v1.val.ad, v2, 0);
break;
case 'f':
if (!arg_set)
runtime("Can't filter pair");
- res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 1);
+ res.val.ad = clist_filter(fs->pool, v1.val.ad, v2, 1);
break;
default:
@@ -1450,22 +1455,22 @@ interpret(struct f_inst *what)
if (arg_set == 1)
runtime("Can't add set");
else if (!arg_set)
- res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec);
+ res.val.ad = ec_set_add(fs->pool, v1.val.ad, v2.val.ec);
else
- res.val.ad = ec_set_union(f_pool, v1.val.ad, v2.val.ad);
+ res.val.ad = ec_set_union(fs->pool, v1.val.ad, v2.val.ad);
break;
case 'd':
if (!arg_set)
- res.val.ad = ec_set_del(f_pool, v1.val.ad, v2.val.ec);
+ res.val.ad = ec_set_del(fs->pool, v1.val.ad, v2.val.ec);
else
- res.val.ad = eclist_filter(f_pool, v1.val.ad, v2, 0);
+ res.val.ad = eclist_filter(fs->pool, v1.val.ad, v2, 0);
break;
case 'f':
if (!arg_set)
runtime("Can't filter ec");
- res.val.ad = eclist_filter(f_pool, v1.val.ad, v2, 1);
+ res.val.ad = eclist_filter(fs->pool, v1.val.ad, v2, 1);
break;
default:
@@ -1492,22 +1497,22 @@ interpret(struct f_inst *what)
if (arg_set == 1)
runtime("Can't add set");
else if (!arg_set)
- res.val.ad = lc_set_add(f_pool, v1.val.ad, v2.val.lc);
+ res.val.ad = lc_set_add(fs->pool, v1.val.ad, v2.val.lc);
else
- res.val.ad = lc_set_union(f_pool, v1.val.ad, v2.val.ad);
+ res.val.ad = lc_set_union(fs->pool, v1.val.ad, v2.val.ad);
break;
case 'd':
if (!arg_set)
- res.val.ad = lc_set_del(f_pool, v1.val.ad, v2.val.lc);
+ res.val.ad = lc_set_del(fs->pool, v1.val.ad, v2.val.lc);
else
- res.val.ad = lclist_filter(f_pool, v1.val.ad, v2, 0);
+ res.val.ad = lclist_filter(fs->pool, v1.val.ad, v2, 0);
break;
case 'f':
if (!arg_set)
runtime("Can't filter lc");
- res.val.ad = lclist_filter(f_pool, v1.val.ad, v2, 1);
+ res.val.ad = lclist_filter(fs->pool, v1.val.ad, v2, 1);
break;
default:
@@ -1531,11 +1536,11 @@ interpret(struct f_inst *what)
{
ACCESS_RTE;
ACCESS_EATTRS;
- v1.val.net = (*f_rte)->net->n.addr;
+ v1.val.net = (*fs->rte)->net->n.addr;
/* We ignore temporary attributes, probably not a problem here */
/* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
- eattr *e = ea_find(*f_eattrs, EA_CODE(PROTOCOL_BGP, 0x02));
+ eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02));
if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH))
runtime("Missing AS_PATH attribute");
@@ -1563,7 +1568,7 @@ interpret(struct f_inst *what)
ARG_ANY(1);
res.type = T_STRING;
- res.val.s = val_format_str(v1);
+ res.val.s = val_format_str(fs, v1);
break;
case FI_ASSERT: /* Birdtest Assert */
@@ -1765,38 +1770,38 @@ f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int fla
int rte_cow = ((*rte)->flags & REF_COW);
DBG( "Running filter `%s'...", filter->name );
- f_rte = rte;
- f_eattrs = NULL;
- f_old_rta = NULL;
- f_pool = tmp_pool;
- f_flags = flags;
+ struct filter_state fs = {
+ .rte = rte,
+ .pool = tmp_pool,
+ .flags = flags,
+ };
- LOG_BUFFER_INIT(f_buf);
+ LOG_BUFFER_INIT(fs.buf);
- struct f_val res = interpret(filter->root);
+ struct f_val res = interpret(&fs, filter->root);
- if (f_old_rta) {
+ if (fs.old_rta) {
/*
- * Cached rta was modified and f_rte contains now an uncached one,
+ * Cached rta was modified and fs->rte contains now an uncached one,
* sharing some part with the cached one. The cached rta should
- * be freed (if rte was originally COW, f_old_rta is a clone
+ * be freed (if rte was originally COW, fs->old_rta is a clone
* obtained during rte_cow()).
*
* This also implements the exception mentioned in f_run()
* description. The reason for this is that rta reuses parts of
- * f_old_rta, and these may be freed during rta_free(f_old_rta).
+ * fs->old_rta, and these may be freed during rta_free(fs->old_rta).
* This is not the problem if rte was COW, because original rte
* also holds the same rta.
*/
if (!rte_cow)
- (*f_rte)->attrs = rta_lookup((*f_rte)->attrs);
+ (*fs.rte)->attrs = rta_lookup((*fs.rte)->attrs);
- rta_free(f_old_rta);
+ rta_free(fs.old_rta);
}
if (res.type != T_RETURN) {
- if (!(f_flags & FF_SILENT))
+ if (!(fs.flags & FF_SILENT))
log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name);
return F_ERROR;
}
@@ -1810,16 +1815,15 @@ struct f_val
f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool)
{
- f_rte = rte;
- f_eattrs = NULL;
- f_old_rta = NULL;
- f_pool = tmp_pool;
- f_flags = 0;
+ struct filter_state fs = {
+ .rte = rte,
+ .pool = tmp_pool,
+ };
- LOG_BUFFER_INIT(f_buf);
+ LOG_BUFFER_INIT(fs.buf);
/* Note that in this function we assume that rte->attrs is private / uncached */
- struct f_val res = interpret(expr);
+ struct f_val res = interpret(&fs, expr);
return res;
}
@@ -1827,14 +1831,13 @@ f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool)
struct f_val
f_eval(struct f_inst *expr, struct linpool *tmp_pool)
{
- f_flags = 0;
- f_eattrs = NULL;
- f_rte = NULL;
- f_pool = tmp_pool;
+ struct filter_state fs = {
+ .pool = tmp_pool,
+ };
- LOG_BUFFER_INIT(f_buf);
+ LOG_BUFFER_INIT(fs.buf);
- return interpret(expr);
+ return interpret(&fs, expr);
}
uint