diff --git a/filter/Makefile b/filter/Makefile index c2062534..14e8374b 100644 --- a/filter/Makefile +++ b/filter/Makefile @@ -1,4 +1,4 @@ -src := filter.c data.c f-util.c tree.c trie.c inst-gen.c +src := filter.c data.c deref.c f-util.c tree.c trie.c inst-gen.c obj := $(src-o-files) $(all-daemon) $(cf-local) diff --git a/filter/config.Y b/filter/config.Y index 160f36ed..0256dcf6 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -431,7 +431,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST, IF, THEN, ELSE, CASE, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, - FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, + FROM, GW, NET, MASK, PROTO, AUTHOR, SENDER, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, PREFERENCE, ROA_CHECK, ASN, SRC, DST, IS_V4, IS_V6, @@ -902,6 +902,8 @@ static_attr: | GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); } | NET { $$ = f_new_static_attr(T_NET, SA_NET, 1); } | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 1); } + | SENDER { $$ = f_new_static_attr(T_CHANNEL, SA_SENDER, 1); } + | AUTHOR { $$ = f_new_static_attr(T_PROTO, SA_AUTHOR, 1); } | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 1); } | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 0); } | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); } @@ -938,19 +940,24 @@ term: | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); } - | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); } - | term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); } - | term '.' IP { $$ = f_new_inst(FI_IP, $1); } - | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); } - | term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); } - | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); } - | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN, $1); } - | term '.' SRC { $$ = f_new_inst(FI_NET_SRC, $1); } - | term '.' DST { $$ = f_new_inst(FI_NET_DST, $1); } | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); } - | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); } - | term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); } - | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); } + | term '.' token { + switch ($3.value) { + case IS_V4: $$ = f_new_inst(FI_IS_V4, $1); break; + case TYPE: $$ = f_new_inst(FI_TYPE, $1); break; + case IP: $$ = f_new_inst(FI_IP, $1); break; + case RD: $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); break; + case LEN: $$ = f_new_inst(FI_LENGTH, $1); break; + case MAXLEN: $$ = f_new_inst(FI_ROA_MAXLEN, $1); break; + case ASN: $$ = f_new_inst(FI_ROA_ASN, $1); break; + case SRC: $$ = f_new_inst(FI_NET_SRC, $1); break; + case DST: $$ = f_new_inst(FI_NET_DST, $1); break; + case FIRST: $$ = f_new_inst(FI_AS_PATH_FIRST, $1); break; + case LAST: $$ = f_new_inst(FI_AS_PATH_LAST, $1); break; + case LAST_NONAGGREGATED: $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); break; + default: $$ = f_new_inst(FI_DEREF, $1, f_get_deref($1->type, &$3)); break; + } + } /* Communities */ /* This causes one shift/reduce conflict diff --git a/filter/data.h b/filter/data.h index 4ebce73b..aab3dd42 100644 --- a/filter/data.h +++ b/filter/data.h @@ -61,6 +61,9 @@ enum f_type { T_SET = 0x80, T_PREFIX_SET = 0x81, + + T_PROTO = 0xa0, /* Protocol instance */ + T_CHANNEL = 0xa1, /* Channel */ } PACKED; /* Filter value; size of this affects filter memory consumption */ @@ -78,6 +81,8 @@ struct f_val { const struct adata *ad; const struct f_path_mask *path_mask; struct f_path_mask_item pmi; + struct proto *proto; + struct channel *channel; } val; }; @@ -94,6 +99,8 @@ enum f_sa_code { SA_GW, SA_NET, SA_PROTO, + SA_SENDER, + SA_AUTHOR, SA_SOURCE, SA_SCOPE, SA_DEST, @@ -126,6 +133,16 @@ struct f_lval { }; }; +/* Member dereference (in filter/deref.c) */ +struct f_deref { + struct f_val (*deref)(struct f_val *); + const char *name; + enum f_type source_type, result_type; +}; + +struct keyword; +const struct f_deref *f_get_deref(enum f_type, const struct keyword *); + /* IP prefix range structure */ struct f_prefix { net_addr net; /* The matching prefix must match this net */ diff --git a/filter/decl.m4 b/filter/decl.m4 index efecb9a5..db8af262 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -90,7 +90,7 @@ FID_DUMP_BODY()m4_dnl debug("%s" $4 "\n", INDENT, $5); ]]) FID_INTERPRET_EXEC()m4_dnl -const $1 $2 = whati->$2 +$1 const $2 = whati->$2 FID_INTERPRET_BODY') # Instruction arguments are needed only until linearization is done. diff --git a/filter/deref.c b/filter/deref.c new file mode 100644 index 00000000..ad241dd1 --- /dev/null +++ b/filter/deref.c @@ -0,0 +1,68 @@ +/* + * Filters: dereferencing metaobjects + * + * (c) 2020 Maria Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + * + */ + +#define PARSER 1 + +#include "nest/bird.h" +#include "nest/route.h" +#include "nest/protocol.h" +#include "filter/filter.h" +#include "filter/f-inst.h" +#include "filter/data.h" +#include "conf/conf.h" +#include "conf/cf-parse.tab.h" + +static struct f_val deref_proto__name__deref(struct f_val *val) +{ return (struct f_val) { .type = T_STRING, .val.s = val->val.proto->name }; } + +static const struct f_deref deref_proto__name = { + .deref = deref_proto__name__deref, + .name = "name", + .source_type = T_PROTO, + .result_type = T_STRING, +}; + +static struct f_val deref_channel__name__deref(struct f_val *val) +{ return (struct f_val) { .type = T_STRING, .val.s = val->val.channel->name }; } + +static const struct f_deref deref_channel__name = { + .deref = deref_channel__name__deref, + .name = "name", + .source_type = T_CHANNEL, + .result_type = T_STRING, +}; + +static struct f_val deref_channel__proto__deref(struct f_val *val) +{ return (struct f_val) { .type = T_PROTO, .val.proto = val->val.channel->proto }; } + +static const struct f_deref deref_channel__proto = { + .deref = deref_channel__proto__deref, + .name = "proto", + .source_type = T_CHANNEL, + .result_type = T_PROTO, +}; + +const struct f_deref *f_get_deref(enum f_type type, const struct keyword *kw) +{ + if (!type) + cf_error("Can't dereference an object with unsure type"); + +#define CX(t, v) ((u64) (t) | ((v) << (8 * sizeof (enum f_type)))) + switch (CX(type, kw->value)) + { + case CX(T_PROTO, NAME): + return &deref_proto__name; + case CX(T_CHANNEL, NAME): + return &deref_channel__name; + case CX(T_CHANNEL, PROTO): + return &deref_channel__proto; + } + + cf_error("Can't call (%s).%s: Undefined operation", f_type_name(type), kw->name); +} diff --git a/filter/f-inst.c b/filter/f-inst.c index 4b3c627b..bde2d52f 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -404,6 +404,21 @@ RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1)); } + INST(FI_DEREF, 1, 1) { + NEVER_CONSTANT; + ARG_ANY(1); + FID_MEMBER( + const struct f_deref *, + deref, + [[ f1->deref != f2->deref ]], + "dereference %s from %s", + [[ item->deref->name, f_type_name(item->deref->source_type) ]] + ); + + RESULT_TYPE(deref->result_type); + RESULT_VAL(deref->deref(&v1)); + } + INST(FI_TYPE, 1, 1) { ARG_ANY(1); /* There may be more types supporting this operation */ switch (v1.type) @@ -503,6 +518,8 @@ case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break; case SA_NET: RESULT(sa.f_type, net, (*fs->rte)->net->n.addr); break; case SA_PROTO: RESULT(sa.f_type, s, rta->src->proto->name); break; + case SA_SENDER: RESULT(sa.f_type, channel, (*fs->rte)->sender); break; + case SA_AUTHOR: RESULT(sa.f_type, proto, rta->src->proto); break; case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break; case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break; case SA_DEST: RESULT(sa.f_type, i, rta->dest); break;