4 * Copyright 1998--2000 Pavel Machek
6 * Can be freely distributed and used under the terms of the GNU GPL.
8 FIXME: priority of ! should be lower
13 #include "filter/f-inst.h"
14 #include "filter/data.h"
18 static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
19 static inline u32 pair_a(u32 p) { return p >> 16; }
20 static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
22 #define f_generate_complex(fi_code, da, arg) \
23 f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
26 f_new_var(struct sym_scope *s)
29 * - A variable is an offset on vstack from vbase.
30 * - Vbase is set on filter start / function call.
31 * - Scopes contain (non-frame) block scopes inside filter/function scope
32 * - Each scope knows number of vars in that scope
33 * - Offset is therefore a sum of 'slots' up to filter/function scope
34 * - New variables are added on top of vstk, so intermediate values cannot
35 * be there during FI_VAR_INIT. I.e. no 'var' inside 'term'.
36 * - Also, each f_line must always have its scope, otherwise a variable may
37 * be defined but not initialized if relevant f_line is not executed.
40 int offset = s->slots++;
50 cf_error("Too many variables, at most 255 allowed");
56 * Sets and their items are during parsing handled as lists, linked
57 * through left ptr. The first item in a list also contains a pointer
58 * to the last item in a list (right ptr). For convenience, even items
59 * are handled as one-item lists. Lists are merged by f_merge_items().
62 f_valid_set_type(int type)
81 static inline struct f_tree *
82 f_new_item(struct f_val from, struct f_val to)
84 struct f_tree *t = f_new_tree();
91 static inline struct f_tree *
92 f_merge_items(struct f_tree *a, struct f_tree *b)
101 static inline struct f_tree *
102 f_new_pair_item(int fa, int ta, int fb, int tb)
109 if ((ta < fa) || (tb < fb))
110 cf_error( "From value cannot be higher that To value in pair sets");
112 struct f_tree *t = f_new_tree();
114 t->from.type = t->to.type = T_PAIR;
115 t->from.val.i = pair(fa, fb);
116 t->to.val.i = pair(ta, tb);
120 static inline struct f_tree *
121 f_new_pair_set(int fa, int ta, int fb, int tb)
128 if ((ta < fa) || (tb < fb))
129 cf_error( "From value cannot be higher that To value in pair sets");
131 struct f_tree *lst = NULL;
134 for (i = fa; i <= ta; i++)
135 lst = f_merge_items(lst, f_new_pair_item(i, i, fb, tb));
140 #define CC_ALL 0xFFFF
141 #define EC_ALL 0xFFFFFFFF
142 #define LC_ALL 0xFFFFFFFF
144 static struct f_tree *
145 f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
149 if ((kind != EC_GENERIC) && (ipv4_used || (key >= 0x10000))) {
157 if (kind == EC_GENERIC) {
158 fm = ec_generic(key, vf);
159 to = ec_generic(key, vt);
161 else if (ipv4_used) {
162 fm = ec_ip4(kind, key, vf);
163 to = ec_ip4(kind, key, vt);
165 else if (key < 0x10000) {
166 fm = ec_as2(kind, key, vf);
167 to = ec_as2(kind, key, vt);
170 fm = ec_as4(kind, key, vf);
171 to = ec_as4(kind, key, vt);
174 struct f_tree *t = f_new_tree();
176 t->from.type = t->to.type = T_EC;
182 static struct f_tree *
183 f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
185 struct f_tree *t = f_new_tree();
187 t->from.type = t->to.type = T_LC;
188 t->from.val.lc = (lcomm) {f1, f2, f3};
189 t->to.val.lc = (lcomm) {t1, t2, t3};
193 static inline struct f_inst *
194 f_const_empty(struct f_dynamic_attr dyn)
199 case EAF_TYPE_AS_PATH:
200 case EAF_TYPE_INT_SET:
201 case EAF_TYPE_EC_SET:
202 case EAF_TYPE_LC_SET:
203 empty = (struct f_val) {
205 .val.ad = &null_adata,
209 cf_error("Can't empty that attribute");
212 return f_new_inst(FI_CONSTANT, empty);
215 #define f_dummy_dynattr(_type, _f_type) ((struct f_dynamic_attr) { .type = _type, .f_type = _f_type, })
217 #define f_const_empty_path f_const_empty(f_dummy_dynattr(EAF_TYPE_AS_PATH, T_PATH))
218 #define f_const_empty_clist f_const_empty(f_dummy_dynattr(EAF_TYPE_INT_SET, T_CLIST))
219 #define f_const_empty_eclist f_const_empty(f_dummy_dynattr(EAF_TYPE_EC_SET, T_ECLIST))
220 #define f_const_empty_lclist f_const_empty(f_dummy_dynattr(EAF_TYPE_LC_SET, T_LCLIST))
223 * Remove all new lines and doubled whitespaces
224 * and convert all tabulators to spaces
225 * and return a copy of string
228 assert_copy_expr(const char *start, size_t len)
230 /* XXX: Allocates maybe a little more memory than we really finally need */
231 char *str = cfg_alloc(len + 1);
234 const char *src = start - 1;
235 const char *end = start + len;
241 /* Skip doubled whitespaces */
244 const char *prev = src - 1;
245 if ((*src == ' ' || *src == '\t') && (*prev == ' ' || *prev == '\t'))
262 * assert_done - create f_instruction of bt_assert
263 * @expr: expression in bt_assert()
264 * @start: pointer to first char of test expression
265 * @end: pointer to the last char of test expression
267 static struct f_inst *
268 assert_done(struct f_inst *expr, const char *start, const char *end)
270 return f_new_inst(FI_ASSERT, expr,
272 assert_copy_expr(start, end - start + 1)
276 static struct f_inst *
277 assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const char *end)
279 struct f_inst *setter, *getter, *checker;
280 switch (lval->type) {
281 case F_LVAL_VARIABLE:
282 setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
283 getter = f_new_inst(FI_VAR_GET, lval->sym);
286 setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
287 getter = f_new_inst(FI_RTA_GET, lval->sa);
290 setter = f_new_inst(FI_EA_SET, expr, lval->da);
291 getter = f_new_inst(FI_EA_GET, lval->da);
294 bug("Unknown lval type");
297 checker = f_new_inst(FI_EQ, expr, getter);
298 setter->next = checker;
300 return assert_done(setter, start, end);
305 CF_KEYWORDS_EXCLUSIVE(IN)
306 CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
307 ACCEPT, REJECT, ERROR,
308 INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
309 SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
310 IF, THEN, ELSE, CASE,
312 TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
313 FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS, ONLINK,
315 ROA_CHECK, ASN, SRC, DST,
320 ADD, DELETE, CONTAINS, RESET,
321 PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
324 FILTER, WHERE, EVAL, ATTRIBUTE,
326 BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT)
331 %type <xp> cmds_int cmd_prep
332 %type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor print_list var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
333 %type <fda> dynamic_attr
334 %type <fsa> static_attr
335 %type <f> filter where_filter
336 %type <fl> filter_body function_body
338 %type <i> type function_vars
339 %type <fa> function_argsn function_args
341 %type <fret> break_command
343 %type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
344 %type <trie> fprefix_set
345 %type <v> set_atom switch_atom fipa
347 %type <t> get_cf_position
354 FILTER symbol { $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL); cf_push_scope( $2 ); }
356 struct filter *f = cfg_alloc(sizeof(struct filter));
357 *f = (struct filter) { .sym = $2, .root = $4 };
366 EVAL term { cf_eval_int($2); }
370 custom_attr: ATTRIBUTE type symbol ';' {
371 cf_define_symbol($3, SYM_ATTRIBUTE, attribute, ca_lookup(new_config->pool, $3->name, $2)->fda);
374 conf: bt_test_suite ;
376 BT_TEST_SUITE '(' CF_SYM_KNOWN ',' text ')' {
377 cf_assert_symbol($3, SYM_FUNCTION);
378 struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
379 t->fn = $3->function;
380 t->fn_name = $3->name;
383 add_tail(&new_config->tests, &t->n);
389 BT_TEST_SAME '(' CF_SYM_KNOWN ',' CF_SYM_KNOWN ',' NUM ')' {
390 cf_assert_symbol($3, SYM_FUNCTION);
391 cf_assert_symbol($5, SYM_FUNCTION);
392 struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
393 t->fn = $3->function;
394 t->cmp = $5->function;
396 t->fn_name = $3->name;
398 add_tail(&new_config->tests, &t->n);
404 | BOOL { $$ = T_BOOL; }
407 | PREFIX { $$ = T_NET; }
408 | PAIR { $$ = T_PAIR; }
409 | QUAD { $$ = T_QUAD; }
412 | STRING { $$ = T_STRING; }
413 | BYTESTRING { $$ = T_BYTESTRING; }
414 | BGPMASK { $$ = T_PATH_MASK; }
415 | BGPPATH { $$ = T_PATH; }
416 | CLIST { $$ = T_CLIST; }
417 | ECLIST { $$ = T_ECLIST; }
418 | LCLIST { $$ = T_LCLIST; }
436 cf_error( "You can't create sets of this type." );
442 /* EMPTY */ { $$ = NULL; }
443 | function_argsn type symbol ';' {
444 if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
445 $$ = cfg_alloc(sizeof(struct f_arg));
446 $$->arg = cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
452 '(' ')' { $$ = NULL; }
453 | '(' function_argsn type symbol ')' {
454 $$ = cfg_alloc(sizeof(struct f_arg));
455 $$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
461 /* EMPTY */ { $$ = 0; }
462 | function_vars type symbol ';' {
463 cf_define_symbol($3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
468 filter_body: function_body ;
472 cf_assert_symbol($1, SYM_FILTER);
475 | { cf_push_scope(NULL); } filter_body {
476 struct filter *f = cfg_alloc(sizeof(struct filter));
477 *f = (struct filter) { .root = $2 };
486 /* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
487 $$ = f_new_where($2);
492 function_vars '{' cmds '}' {
493 $$ = f_linearize($3, 0);
501 DBG( "Beginning of function %s\n", $2->name );
502 $2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
505 /* Make dummy f_line for storing function prototype */
506 struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
507 $2->function = dummy;
509 /* Revert the args */
511 struct f_arg *tmp = $4;
514 tmp->next = dummy->arg_list;
515 dummy->arg_list = tmp;
519 $6->args = $2->function->args;
520 $6->arg_list = $2->function->arg_list;
528 cmds: /* EMPTY */ { $$ = NULL; }
529 | cmds_int { $$ = $1.begin; }
532 cmds_scoped: { cf_push_soft_scope(); } cmds { cf_pop_soft_scope(); $$ = $2; } ;
537 $$.begin = $$.end = $1;
540 $$.end = $$.end->next;
545 | cmds_int cmd_prep {
553 $1.end->next = $2.begin;
559 * Complex types, their bison value is struct f_val
562 IP4 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); }
563 | IP6 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); }
569 * Set constants. They are also used in switch cases. We use separate
570 * nonterminals for switch (set_atom/switch_atom, set_item/switch_item ...)
571 * to elude a collision between symbol (in expr) in set_atom and symbol
572 * as a function call in switch case cmds.
576 NUM { $$.type = T_INT; $$.val.i = $1; }
578 | VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
579 | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
581 $$ = cf_eval($2, T_VOID);
582 if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
585 cf_assert_symbol($1, SYM_CONSTANT);
586 if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
592 NUM { $$.type = T_INT; $$.val.i = $1; }
593 | '(' term ')' { $$ = cf_eval($2, T_INT); }
595 | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
599 term { $$ = cf_eval_int($1); }
602 '(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
603 | '(' cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_item($2, $2, $4, $6); }
604 | '(' cnum ',' '*' ')' { $$ = f_new_pair_item($2, $2, 0, CC_ALL); }
605 | '(' cnum DDOT cnum ',' cnum ')' { $$ = f_new_pair_set($2, $4, $6, $6); }
606 | '(' cnum DDOT cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_set($2, $4, $6, $8); }
607 | '(' cnum DDOT cnum ',' '*' ')' { $$ = f_new_pair_item($2, $4, 0, CC_ALL); }
608 | '(' '*' ',' cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $4); }
609 | '(' '*' ',' cnum DDOT cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $6); }
610 | '(' '*' ',' '*' ')' { $$ = f_new_pair_item(0, CC_ALL, 0, CC_ALL); }
611 | '(' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ')'
612 { $$ = f_new_pair_item($2, $8, $4, $10); }
618 | UNKNOWN NUM { $$ = $2; }
619 | GENERIC { $$ = EC_GENERIC; }
623 '(' ec_kind ',' cnum ',' cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $6); }
624 | '(' ec_kind ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $8); }
625 | '(' ec_kind ',' cnum ',' '*' ')' { $$ = f_new_ec_item($2, 0, $4, 0, EC_ALL); }
629 '(' cnum ',' cnum ',' cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $6); }
630 | '(' cnum ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $8); }
631 | '(' cnum ',' cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $4, 0, LC_ALL); }
632 | '(' cnum ',' cnum DDOT cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $6, 0, LC_ALL); }
633 | '(' cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $2, 0, LC_ALL, 0, LC_ALL); }
634 | '(' cnum DDOT cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $4, 0, LC_ALL, 0, LC_ALL); }
635 | '(' '*' ',' '*' ',' '*' ')' { $$ = f_new_lc_item(0, LC_ALL, 0, LC_ALL, 0, LC_ALL); }
636 | '(' cnum ',' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ',' cnum ')'
637 { $$ = f_new_lc_item($2, $10, $4, $12, $6, $14); }
644 | set_atom { $$ = f_new_item($1, $1); }
645 | set_atom DDOT set_atom { $$ = f_new_item($1, $3); }
652 | switch_atom { $$ = f_new_item($1, $1); }
653 | switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); }
658 | set_items ',' set_item { $$ = f_merge_items($1, $3); }
663 | switch_items ',' switch_item { $$ = f_merge_items($1, $3); }
667 net_ip_ { $$.net = $1; $$.lo = $1.pxlen; $$.hi = $1.pxlen; }
668 | net_ip_ '+' { $$.net = $1; $$.lo = $1.pxlen; $$.hi = net_max_prefix_length[$1.type]; }
669 | net_ip_ '-' { $$.net = $1; $$.lo = 0; $$.hi = $1.pxlen; }
670 | net_ip_ '{' NUM ',' NUM '}' {
671 $$.net = $1; $$.lo = $3; $$.hi = $5;
672 if (($3 > $5) || ($5 > net_max_prefix_length[$1.type]))
673 cf_error("Invalid prefix pattern range: {%u, %u}", $3, $5);
678 fprefix { $$ = f_new_trie(cfg_mem, 0); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); }
679 | fprefix_set ',' fprefix { $$ = $1; if (!trie_add_prefix($$, &($3.net), $3.lo, $3.hi)) cf_error("Mixed IPv4/IPv6 prefixes in prefix set"); }
682 switch_body: /* EMPTY */ { $$ = NULL; }
683 | switch_body switch_items ':' cmds_scoped {
684 /* Fill data fields */
686 for (t = $2; t; t = t->left)
688 $$ = f_merge_items($1, $2);
690 | switch_body ELSECOL cmds_scoped {
691 struct f_tree *t = f_new_tree();
692 t->from.type = t->to.type = T_VOID;
695 $$ = f_merge_items($1, t);
700 symbol_value { $$ = $1; }
701 | '(' term ')' { $$ = $2; }
705 PO bgp_path_tail PC { $$ = $2; }
709 NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; }
710 | NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; }
711 | '[' ']' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = NULL, .kind = PM_ASN_SET }, }); $$->next = $3; }
712 | '[' set_items ']' bgp_path_tail {
713 if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
714 $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = build_tree($2), .kind = PM_ASN_SET }, }); $$->next = $4;
716 | '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); $$->next = $2; }
717 | '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); $$->next = $2; }
718 | '+' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_LOOP }, }); $$->next = $2; }
719 | bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; }
724 NUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
725 | TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
726 | FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
727 | TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
728 | BYTETEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BYTESTRING, .val.bs = $1, }); }
729 | fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
730 | VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
731 | net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
732 | '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
733 | '[' set_items ']' {
734 DBG( "We've got a set here..." );
735 $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
738 | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
739 | ENUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
743 '(' term ',' term ')' { $$ = f_new_inst(FI_PAIR_CONSTRUCT, $2, $4); }
744 | '(' ec_kind ',' term ',' term ')' { $$ = f_new_inst(FI_EC_CONSTRUCT, $4, $6, $2); }
745 | '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_LC_CONSTRUCT, $2, $4, $6); }
746 | bgp_path { $$ = f_new_inst(FI_PATHMASK_CONSTRUCT, $1); }
750 /* This generates the function_call variable list backwards. */
751 var_list: /* EMPTY */ { $$ = NULL; }
753 | var_list ',' term { $$ = $3; $$->next = $1; }
756 CF_SYM_KNOWN '(' var_list ')'
758 if ($1->class != SYM_FUNCTION)
759 cf_error("You can't call something which is not a function. Really.");
761 /* Revert the var_list */
762 struct f_inst *args = NULL;
764 struct f_inst *tmp = $3;
771 $$ = f_new_inst(FI_CALL, args, $1);
775 symbol_value: CF_SYM_KNOWN
778 case SYM_CONSTANT_RANGE:
779 $$ = f_new_inst(FI_CONSTANT, *($1->val));
781 case SYM_VARIABLE_RANGE:
782 $$ = f_new_inst(FI_VAR_GET, $1);
785 $$ = f_new_inst(FI_EA_GET, *$1->attribute);
788 cf_error("Can't get value of symbol %s", $1->name);
794 FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 0); }
795 | GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); }
796 | NET { $$ = f_new_static_attr(T_NET, SA_NET, 1); }
797 | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 1); }
798 | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 1); }
799 | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 0); }
800 | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); }
801 | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
802 | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
803 | WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
804 | PREFERENCE { $$ = f_new_static_attr(T_INT, SA_PREF, 0); }
805 | GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
806 | ONLINK { $$ = f_new_static_attr(T_BOOL, SA_ONLINK, 0); }
810 '(' term ')' { $$ = $2; }
811 | term '+' term { $$ = f_new_inst(FI_ADD, $1, $3); }
812 | term '-' term { $$ = f_new_inst(FI_SUBTRACT, $1, $3); }
813 | term '*' term { $$ = f_new_inst(FI_MULTIPLY, $1, $3); }
814 | term '/' term { $$ = f_new_inst(FI_DIVIDE, $1, $3); }
815 | term AND term { $$ = f_new_inst(FI_AND, $1, $3); }
816 | term OR term { $$ = f_new_inst(FI_OR, $1, $3); }
817 | term '=' term { $$ = f_new_inst(FI_EQ, $1, $3); }
818 | term NEQ term { $$ = f_new_inst(FI_NEQ, $1, $3); }
819 | term '<' term { $$ = f_new_inst(FI_LT, $1, $3); }
820 | term LEQ term { $$ = f_new_inst(FI_LTE, $1, $3); }
821 | term '>' term { $$ = f_new_inst(FI_LT, $3, $1); }
822 | term GEQ term { $$ = f_new_inst(FI_LTE, $3, $1); }
823 | term '~' term { $$ = f_new_inst(FI_MATCH, $1, $3); }
824 | term NMA term { $$ = f_new_inst(FI_NOT_MATCH, $1, $3); }
825 | '!' term { $$ = f_new_inst(FI_NOT, $2); }
826 | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
828 | symbol_value { $$ = $1; }
829 | constant { $$ = $1; }
830 | constructor { $$ = $1; }
832 | static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
834 | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
836 | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
837 | term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
838 | term '.' IP { $$ = f_new_inst(FI_IP, $1); }
839 | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); }
840 | term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
841 | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
842 | term '.' ASN { $$ = f_new_inst(FI_ASN, $1); }
843 | term '.' SRC { $$ = f_new_inst(FI_NET_SRC, $1); }
844 | term '.' DST { $$ = f_new_inst(FI_NET_DST, $1); }
845 | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
846 | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
847 | term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
848 | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); }
849 | term '.' DATA { $$ = f_new_inst(FI_PAIR_DATA, $1); }
850 | term '.' DATA1 { $$ = f_new_inst(FI_LC_DATA1, $1); }
851 | term '.' DATA2 { $$ = f_new_inst(FI_LC_DATA2, $1); }
852 | term '.' MIN { $$ = f_new_inst(FI_MIN, $1); }
853 | term '.' MAX { $$ = f_new_inst(FI_MAX, $1); }
856 /* This causes one shift/reduce conflict
857 | dynamic_attr '.' ADD '(' term ')' { }
858 | dynamic_attr '.' DELETE '(' term ')' { }
859 | dynamic_attr '.' CONTAINS '(' term ')' { }
860 | dynamic_attr '.' RESET{ }
863 | '+' EMPTY '+' { $$ = f_const_empty_path; }
864 | '-' EMPTY '-' { $$ = f_const_empty_clist; }
865 | '-' '-' EMPTY '-' '-' { $$ = f_const_empty_eclist; }
866 | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_const_empty_lclist; }
867 | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
868 | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
869 | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
870 | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); }
872 | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
873 | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
875 | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
877 /* | term '.' LEN { $$->code = P('P','l'); } */
884 FROM_HEX '(' term ')' { $$ = f_new_inst(FI_FROM_HEX, $3); }
888 ACCEPT { $$ = F_ACCEPT; }
889 | REJECT { $$ = F_REJECT; }
890 | ERROR { $$ = F_ERROR; }
893 print_list: /* EMPTY */ { $$ = NULL; }
895 | term ',' print_list {
897 ASSERT($1->next == NULL);
904 type symbol '=' term ';' {
905 struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
906 $$ = f_new_inst(FI_VAR_INIT, $4, sym);
909 struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
910 $$ = f_new_inst(FI_VAR_INIT0, sym);
915 type symbol { $$ = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
916 | CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
920 '{' cmds_scoped '}' {
924 $$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
926 | IF term THEN cmd ELSE cmd {
927 $$ = f_new_inst(FI_CONDITION, $2, $4, $6);
930 /* Reserve space for walk data on stack */
931 cf_push_block_scope();
932 conf_this_scope->slots += 2;
934 /* Parse term in the parent scope */
935 { conf_this_scope->active = 0; } term { conf_this_scope->active = 1; }
937 cf_pop_block_scope();
938 $$ = f_new_inst(FI_FOR_INIT, $6, $3);
939 $$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
941 | CF_SYM_KNOWN '=' term ';' {
943 case SYM_VARIABLE_RANGE:
944 $$ = f_new_inst(FI_VAR_SET, $3, $1);
947 $$ = f_new_inst(FI_EA_SET, $3, *$1->attribute);
950 cf_error("Can't assign to symbol %s", $1->name);
954 DBG( "Ook, we'll return the value\n" );
955 $$ = f_new_inst(FI_RETURN, $2);
957 | dynamic_attr '=' term ';' {
958 $$ = f_new_inst(FI_EA_SET, $3, $1);
960 | static_attr '=' term ';' {
962 cf_error( "This static attribute is read-only.");
963 $$ = f_new_inst(FI_RTA_SET, $3, $1);
965 | UNSET '(' dynamic_attr ')' ';' {
966 $$ = f_new_inst(FI_EA_UNSET, $3);
968 | break_command print_list ';' {
969 struct f_inst *breaker = f_new_inst(FI_DIE, $1);
971 struct f_inst *printer = f_new_inst(FI_PRINT, $2);
972 struct f_inst *flusher = f_new_inst(FI_FLUSH);
973 printer->next = flusher;
974 flusher->next = breaker;
979 | PRINT print_list ';' {
980 $$ = f_new_inst(FI_PRINT, $2);
981 $$->next = f_new_inst(FI_FLUSH);
983 | PRINTN print_list ';' {
984 $$ = f_new_inst(FI_PRINT, $2);
986 | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
987 | CASE term '{' switch_body '}' {
988 $$ = f_new_inst(FI_SWITCH, $2, $4);
991 | dynamic_attr '.' EMPTY ';' { $$ = f_new_inst(FI_EA_SET, f_const_empty($1), $1); }
992 | dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, $1, $5 ); }
993 | dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD, $1, $5 ); }
994 | dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_DEL, $1, $5 ); }
995 | dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_FILTER, $1, $5 ); }
996 | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
997 | BT_CHECK_ASSIGN '(' get_cf_position lvalue get_cf_position ',' term ')' ';' { $$ = assert_assign(&$4, $7, $3 + 1, $5 - 1); }
1006 CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
1007 | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
1008 | dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };