diff --git a/Makefile.in b/Makefile.in index 8f6c0c8b..fcf2a66c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -69,7 +69,7 @@ $(daemon): LIBS += $(DAEMON_LIBS) # Include directories dirs := client conf doc filter lib nest test $(addprefix proto/,$(protocols)) @sysdep_dirs@ -conf-y-targets := $(addprefix $(objdir)/conf/,cf-parse.y keywords.h commands.h) +conf-y-targets := $(addprefix $(objdir)/conf/,cf-parse.y keywords.h commands.h) $(objdir)/filter/methods.h cf-local = $(conf-y-targets): $(s)config.Y src-o-files = $(patsubst %.c,$(o)%.o,$(src)) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index ae0bb5a6..ca6b855d 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -45,6 +45,7 @@ #include "nest/route.h" #include "nest/protocol.h" #include "filter/filter.h" +#include "filter/methods.h" #include "conf/conf.h" #include "conf/cf-parse.tab.h" #include "lib/string.h" diff --git a/conf/confbase.Y b/conf/confbase.Y index 72f56f1e..03543b30 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -51,6 +51,7 @@ CF_DECLS struct f_inst *x; struct f_dynamic_attr fda; struct f_static_attr fsa; + enum f_method efm; struct filter *f; struct f_tree *e; struct f_trie *trie; diff --git a/conf/gen_parser.m4 b/conf/gen_parser.m4 index 00b55023..1c478c90 100644 --- a/conf/gen_parser.m4 +++ b/conf/gen_parser.m4 @@ -39,6 +39,10 @@ m4_define(CF_dyn_rules,) m4_define(CF_ADDTO, `m4_define([[CF_rule_$1]],m4_ifdef([[CF_rule_$1]],CF_rule_$1 | ,[[m4_define([[CF_dyn_rules]],CF_dyn_rules[[CF_RULE($1) ]])]])$2)DNL') +# Simple filter object methods +m4_define(CF_OBJM, `m4_divert(2)CF_KEYWORDS($1) +m4_divert(3)f_object_method_simple: $1 { $$ = FM_$1; };') + # CLI commands m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]])) diff --git a/filter/Makefile b/filter/Makefile index 6bada8ca..fbde7dd1 100644 --- a/filter/Makefile +++ b/filter/Makefile @@ -6,3 +6,11 @@ $(cf-local) tests_src := tree_test.c filter_test.c trie_test.c tests_targets := $(tests_targets) $(tests-target-files) tests_objs := $(tests_objs) $(src-o-files) + +$(conf-y-targets): $(s)methods.Y + +$(o)methods.h: | $(s)gen_methods.m4 +$(objdir)/conf/cf-parse.tab.o: $(o)methods.h + +$(addprefix $(o), methods.h): $(objdir)/.dir-stamp +$(call clean, methods.h) diff --git a/filter/config.Y b/filter/config.Y index 182bfa3c..25fce5cf 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -10,6 +10,8 @@ CF_HDR +#include "filter/methods.h" + CF_DEFINES static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; } @@ -413,7 +415,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, IS_V4, IS_V6, LEN, MAXLEN, DEFINED, - ADD, DELETE, CONTAINS, RESET, + ADD, DELETE, CONTAINS, PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH, EMPTY, FILTER, WHERE, EVAL, @@ -425,6 +427,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %type term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr %type dynamic_attr %type static_attr +%type f_object_method_simple %type filter filter_body where_filter %type type break_command ec_kind %type cnum @@ -889,26 +892,9 @@ term: | rtadot dynamic_attr { $$ = f_new_inst_da(FI_EA_GET, $2); } - | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a1.p = $1; } - | term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a1.p = $1; } - | term '.' IP { $$ = f_new_inst(FI_IP); $$->a1.p = $1; $$->aux = T_IP; } - | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a1.p = $1; $$->aux = T_RD; } - | term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a1.p = $1; } - | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a1.p = $1; } - | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a1.p = $1; } - | term '.' SRC { $$ = f_new_inst(FI_SADR_SRC); $$->a1.p = $1; } - | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; } - | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a1.p = $1; } - | term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST); $$->a1.p = $1; } - | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG); $$->a1.p = $1; } + | term '.' f_object_method_simple { $$ = f_new_inst_method($1, $3); } -/* Communities */ -/* This causes one shift/reduce conflict - | rtadot dynamic_attr '.' ADD '(' term ')' { } - | rtadot dynamic_attr '.' DELETE '(' term ')' { } - | rtadot dynamic_attr '.' CONTAINS '(' term ')' { } - | rtadot dynamic_attr '.' RESET{ } -*/ + | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; } | '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; } | '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; } @@ -924,8 +910,6 @@ term: | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT); $$->a1.p = $3; } -/* | term '.' LEN { $$->code = P('P','l'); } */ - /* function_call is inlined here */ | SYM '(' var_list ')' { struct symbol *sym; diff --git a/filter/f-util.c b/filter/f-util.c index 6170760b..e652a234 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -42,6 +42,16 @@ f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa) return ret; } +struct f_inst * +f_new_inst_method(struct f_inst *target, enum f_method method) +{ + struct f_inst *ret = f_new_inst(FI_METHOD); + ret->a1.p = target; + ret->a2.i = method; + + return ret; +} + /* * Generate set_dynamic( operation( get_dynamic(), argument ) ) */ diff --git a/filter/filter.c b/filter/filter.c index 3fa3d34c..4ca86de7 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -840,23 +840,101 @@ interpret(struct f_inst *what) res.type = T_BOOL; res.val.i = (v1.type != T_VOID) && !undef_value(v1); break; - case FI_TYPE: - ARG_ANY(1); /* There may be more types supporting this operation */ - switch (v1.type) + case FI_METHOD: + switch (what->a2.i) { - case T_NET: + case FM_IS_V4: + ARG(1, T_IP); + res.type = T_BOOL; + res.val.i = ipa_is_ip4(v1.val.ip); + break; + case FM_TYPE: + ARG(1, T_NET); res.type = T_ENUM_NETTYPE; res.val.i = v1.val.net->type; break; - default: - runtime( "Can't determine type of this item" ); + case FM_IP: /* Convert prefix to ... */ + ARG(1, T_NET); + res.type = T_IP; + res.val.ip = net_prefix(v1.val.net); + break; + case FM_RD: + ARG(1, T_NET); + if (!net_is_vpn(v1.val.net)) + runtime( "VPN address expected" ); + res.type = T_RD; + res.val.ec = net_rd(v1.val.net); + break; + case FM_LEN: /* Get length of */ + ARG_ANY(1); + res.type = T_INT; + switch(v1.type) { + case T_NET: res.val.i = net_pxlen(v1.val.net); break; + case T_PATH: res.val.i = as_path_getlen(v1.val.ad); break; + case T_CLIST: res.val.i = int_set_get_size(v1.val.ad); break; + case T_ECLIST: res.val.i = ec_set_get_size(v1.val.ad); break; + case T_LCLIST: res.val.i = lc_set_get_size(v1.val.ad); break; + default: runtime( "Prefix, path, clist, eclist or lclist expected" ); + } + break; + case FM_MAXLEN: /* Get ROA max prefix length */ + ARG(1, T_NET); + if (!net_is_roa(v1.val.net)) + runtime( "ROA expected" ); + + res.type = T_INT; + res.val.i = (v1.val.net->type == NET_ROA4) ? + ((net_addr_roa4 *) v1.val.net)->max_pxlen : + ((net_addr_roa6 *) v1.val.net)->max_pxlen; + break; + case FM_ASN: /* Get ROA ASN */ + ARG(1, T_NET); + if (!net_is_roa(v1.val.net)) + runtime( "ROA expected" ); + + res.type = T_INT; + res.val.i = (v1.val.net->type == NET_ROA4) ? + ((net_addr_roa4 *) v1.val.net)->asn : + ((net_addr_roa6 *) v1.val.net)->asn; + break; + case FM_SRC: /* Get SADR src prefix */ + ARG(1, T_NET); + if (!net_is_sadr(v1.val.net)) + runtime( "SADR expected" ); + + { + net_addr_ip6_sadr *net = (void *) v1.val.net; + net_addr *src = lp_alloc(f_pool, sizeof(net_addr_ip6)); + net_fill_ip6(src, net->src_prefix, net->src_pxlen); + + res.type = T_NET; + res.val.net = src; + } + break; + case FM_FIRST: /* Get first ASN from AS PATH */ + ARG(1, T_PATH); + + as = 0; + as_path_get_first(v1.val.ad, &as); + res.type = T_INT; + res.val.i = as; + break; + case FM_LAST: /* Get last ASN from AS PATH */ + ARG(1, T_PATH); + + as = 0; + as_path_get_last(v1.val.ad, &as); + res.type = T_INT; + res.val.i = as; + break; + case FM_LAST_NONAGGREGATED: /* Get last ASN from non-aggregated part of AS PATH */ + ARG(1, T_PATH); + + res.type = T_INT; + res.val.i = as_path_get_last_nonaggregated(v1.val.ad); + break; } break; - case FI_IS_V4: - ARG(1, T_IP); - res.type = T_BOOL; - res.val.i = ipa_is_ip4(v1.val.ip); - break; /* Set to indirect value, a1 = variable, a2 = value */ case FI_SET: @@ -1204,87 +1282,6 @@ interpret(struct f_inst *what) f_rte_cow(); (*f_rte)->pref = v1.val.i; break; - case FI_LENGTH: /* Get length of */ - ARG_ANY(1); - res.type = T_INT; - switch(v1.type) { - case T_NET: res.val.i = net_pxlen(v1.val.net); break; - case T_PATH: res.val.i = as_path_getlen(v1.val.ad); break; - case T_CLIST: res.val.i = int_set_get_size(v1.val.ad); break; - case T_ECLIST: res.val.i = ec_set_get_size(v1.val.ad); break; - case T_LCLIST: res.val.i = lc_set_get_size(v1.val.ad); break; - default: runtime( "Prefix, path, clist or eclist expected" ); - } - break; - case FI_SADR_SRC: /* Get SADR src prefix */ - ARG(1, T_NET); - if (!net_is_sadr(v1.val.net)) - runtime( "SADR expected" ); - - { - net_addr_ip6_sadr *net = (void *) v1.val.net; - net_addr *src = lp_alloc(f_pool, sizeof(net_addr_ip6)); - net_fill_ip6(src, net->src_prefix, net->src_pxlen); - - res.type = T_NET; - res.val.net = src; - } - break; - case FI_ROA_MAXLEN: /* Get ROA max prefix length */ - ARG(1, T_NET); - if (!net_is_roa(v1.val.net)) - runtime( "ROA expected" ); - - res.type = T_INT; - res.val.i = (v1.val.net->type == NET_ROA4) ? - ((net_addr_roa4 *) v1.val.net)->max_pxlen : - ((net_addr_roa6 *) v1.val.net)->max_pxlen; - break; - case FI_ROA_ASN: /* Get ROA ASN */ - ARG(1, T_NET); - if (!net_is_roa(v1.val.net)) - runtime( "ROA expected" ); - - res.type = T_INT; - res.val.i = (v1.val.net->type == NET_ROA4) ? - ((net_addr_roa4 *) v1.val.net)->asn : - ((net_addr_roa6 *) v1.val.net)->asn; - break; - case FI_IP: /* Convert prefix to ... */ - ARG(1, T_NET); - res.type = T_IP; - res.val.ip = net_prefix(v1.val.net); - break; - case FI_ROUTE_DISTINGUISHER: - ARG(1, T_NET); - res.type = T_IP; - if (!net_is_vpn(v1.val.net)) - runtime( "VPN address expected" ); - res.type = T_RD; - res.val.ec = net_rd(v1.val.net); - break; - case FI_AS_PATH_FIRST: /* Get first ASN from AS PATH */ - ARG(1, T_PATH); - - as = 0; - as_path_get_first(v1.val.ad, &as); - res.type = T_INT; - res.val.i = as; - break; - case FI_AS_PATH_LAST: /* Get last ASN from AS PATH */ - ARG(1, T_PATH); - - as = 0; - as_path_get_last(v1.val.ad, &as); - res.type = T_INT; - res.val.i = as; - break; - case FI_AS_PATH_LAST_NAG: /* Get last ASN from non-aggregated part of AS PATH */ - ARG(1, T_PATH); - - res.type = T_INT; - res.val.i = as_path_get_last_nonaggregated(v1.val.ad); - break; case FI_RETURN: ARG_ANY(1); res = v1; @@ -1619,7 +1616,6 @@ i_same(struct f_inst *f1, struct f_inst *f2) case FI_NOT_MATCH: case FI_MATCH: TWOARGS; break; case FI_DEFINED: ONEARG; break; - case FI_TYPE: ONEARG; break; case FI_LC_CONSTRUCT: THREEARGS; @@ -1670,7 +1666,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) if (strcmp((char *) f1->a2.p, (char *) f2->a2.p)) return 0; break; - case FI_PRINT: case FI_LENGTH: ONEARG; break; + case FI_PRINT: ONEARG; break; case FI_CONDITION: THREEARGS; break; case FI_NOP: case FI_EMPTY: break; case FI_PRINT_AND_DIE: ONEARG; A2_SAME; break; @@ -1682,12 +1678,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) 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_METHOD: ONEARG; A2_SAME; break; case FI_CALL: /* Call rewriting trickery to avoid exponential behaviour */ ONEARG; if (!i_same(f1->a2.p, f2->a2.p)) @@ -1699,9 +1690,6 @@ i_same(struct f_inst *f1, struct f_inst *f2) 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; /* Does not really make sense - ROA check results may change anyway */ diff --git a/filter/filter.h b/filter/filter.h index febfdc65..f34d8ead 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -14,6 +14,8 @@ #include "nest/route.h" #include "nest/attrs.h" +#include "filter/methods.h" + /* Filter instruction types */ #define FI__TWOCHAR(a,b) ((a<<8) | b) @@ -36,8 +38,6 @@ F(FI_MATCH, 0, '~') \ F(FI_NOT_MATCH, '!', '~') \ F(FI_DEFINED, 'd', 'e') \ - F(FI_TYPE, 0, 'T') \ - F(FI_IS_V4, 'I', 'i') \ F(FI_SET, 0, 's') \ F(FI_CONSTANT, 0, 'c') \ F(FI_VARIABLE, 0, 'V') \ @@ -52,15 +52,7 @@ F(FI_EA_SET, 'e', 'S') \ F(FI_PREF_GET, 0, 'P') \ F(FI_PREF_SET, 'P', 'S') \ - F(FI_LENGTH, 0, 'L') \ - F(FI_ROA_MAXLEN, 'R', 'M') \ - F(FI_ROA_ASN, 'R', 'A') \ - F(FI_SADR_SRC, 'n', 's') \ - F(FI_IP, 'c', 'p') \ - F(FI_ROUTE_DISTINGUISHER, 'R', 'D') \ - F(FI_AS_PATH_FIRST, 'a', 'f') \ - F(FI_AS_PATH_LAST, 'a', 'l') \ - F(FI_AS_PATH_LAST_NAG, 'a', 'L') \ + F(FI_METHOD, 'o', 'M') \ F(FI_RETURN, 0, 'r') \ F(FI_CALL, 'c', 'a') \ F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \ @@ -152,6 +144,7 @@ struct filter { struct f_inst *f_new_inst(enum f_instruction_code fi_code); struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da); struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa); +struct f_inst *f_new_inst_method(struct f_inst *target, enum f_method method); static inline struct f_dynamic_attr f_new_dynamic_attr(int type, int f_type, int code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ { return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly) diff --git a/filter/gen_methods.m4 b/filter/gen_methods.m4 new file mode 100644 index 00000000..a6cc5125 --- /dev/null +++ b/filter/gen_methods.m4 @@ -0,0 +1,33 @@ +m4_divert(-1)m4_dnl +# +# BIRD -- Generator of Object Method List +# +# (c) 2018 Maria Matejka +# +# Can be freely distributed and used under the terms of the GNU GPL. +# +# Common aliases +m4_define(DNL, `m4_dnl') + +# Diversions used: +# 1 methods + +# We don't need headers +m4_define(CF_HDR, `m4_divert(-1)') + +m4_define(CF_OBJM, `m4_divert(1)FM_$1, +m4_divert(-1)') + +m4_m4wrap(` +m4_divert(0) +#ifndef _BIRD_FILTER_METHOD_H_ +#define _BIRD_FILTER_METHOD_H_ +enum f_method { +m4_undivert(1) +}; +#endif +') + +# As we are processing C source, we must access all M4 primitives via +# m4_* and also set different quoting convention: `[[' and ']]' +m4_changequote([[,]]) diff --git a/filter/methods.Y b/filter/methods.Y new file mode 100644 index 00000000..fb50a116 --- /dev/null +++ b/filter/methods.Y @@ -0,0 +1,13 @@ +CF_OBJM(IS_V4) +CF_OBJM(TYPE) +CF_OBJM(IP) +CF_OBJM(RD) +CF_OBJM(LEN) +CF_OBJM(MAXLEN) +CF_OBJM(ASN) +CF_OBJM(SRC) +CF_OBJM(FIRST) +CF_OBJM(LAST) +CF_OBJM(LAST_NONAGGREGATED) +CF_OBJM(RESET) +