1999-01-15 16:49:17 +00:00
|
|
|
/*
|
|
|
|
* BIRD - filters
|
|
|
|
*
|
2000-05-15 10:49:38 +00:00
|
|
|
* Copyright 1998--2000 Pavel Machek
|
1999-01-15 16:49:17 +00:00
|
|
|
*
|
|
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
1999-10-07 13:38:26 +00:00
|
|
|
*
|
2000-06-01 08:34:30 +00:00
|
|
|
FIXME: priority of ! should be lower
|
1999-01-15 16:49:17 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
CF_HDR
|
|
|
|
|
2019-02-07 20:25:38 +00:00
|
|
|
#include "filter/f-inst.h"
|
2019-02-08 12:38:12 +00:00
|
|
|
#include "filter/data.h"
|
2019-02-07 20:25:38 +00:00
|
|
|
|
2000-04-28 15:11:10 +00:00
|
|
|
CF_DEFINES
|
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
|
|
|
|
static inline u32 pair_a(u32 p) { return p >> 16; }
|
|
|
|
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
|
|
|
|
2023-06-15 11:25:40 +00:00
|
|
|
static struct symbol *this_function;
|
2023-10-05 12:26:22 +00:00
|
|
|
static struct sym_scope *this_for_scope;
|
2023-06-15 11:25:40 +00:00
|
|
|
|
2023-06-12 09:20:49 +00:00
|
|
|
static struct f_method_scope {
|
|
|
|
struct f_inst *object;
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
struct sym_scope *main;
|
2023-06-13 07:39:29 +00:00
|
|
|
struct sym_scope scope;
|
2023-06-12 09:20:49 +00:00
|
|
|
} f_method_scope_stack[32];
|
|
|
|
static int f_method_scope_pos = -1;
|
2019-06-19 12:09:57 +00:00
|
|
|
|
2023-06-12 09:20:49 +00:00
|
|
|
#define FM (f_method_scope_stack[f_method_scope_pos])
|
2022-06-27 09:04:57 +00:00
|
|
|
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
static inline void f_method_call_start(struct f_inst *object)
|
2023-06-12 09:20:49 +00:00
|
|
|
{
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
if (object->type == T_VOID)
|
|
|
|
cf_error("Can't infer type to properly call a method, please assign the value to a variable");
|
2023-06-12 09:20:49 +00:00
|
|
|
if (++f_method_scope_pos >= (int) ARRAY_SIZE(f_method_scope_stack))
|
|
|
|
cf_error("Too many nested method calls");
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
|
|
|
|
struct sym_scope *scope = f_type_method_scope(object->type);
|
2023-11-01 09:57:57 +00:00
|
|
|
if (!scope->hash.count && !scope->next)
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
cf_error("No methods defined for type %s", f_type_name(object->type));
|
|
|
|
|
2023-10-29 14:42:46 +00:00
|
|
|
/* Replacing the current symbol scope with the appropriate method scope
|
|
|
|
for the given type. */
|
2023-06-12 09:20:49 +00:00
|
|
|
FM = (struct f_method_scope) {
|
|
|
|
.object = object,
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
.main = new_config->current_scope,
|
2023-06-13 07:39:29 +00:00
|
|
|
.scope = {
|
2023-10-31 08:58:42 +00:00
|
|
|
.next = scope->next,
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
.hash = scope->hash,
|
2023-06-13 07:39:29 +00:00
|
|
|
.block = 1,
|
|
|
|
.readonly = 1,
|
|
|
|
},
|
2023-06-12 09:20:49 +00:00
|
|
|
};
|
2023-06-13 07:39:29 +00:00
|
|
|
new_config->current_scope = &FM.scope;
|
2023-06-12 09:20:49 +00:00
|
|
|
}
|
|
|
|
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
static inline void f_method_call_args(void)
|
|
|
|
{
|
2023-10-29 14:42:46 +00:00
|
|
|
/* For argument parsing, we need to revert back to the standard symbol scope. */
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
new_config->current_scope = FM.main;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void f_method_call_end(void)
|
2023-06-12 09:20:49 +00:00
|
|
|
{
|
|
|
|
ASSERT_DIE(f_method_scope_pos >= 0);
|
2023-10-29 14:42:46 +00:00
|
|
|
if (&FM.scope == new_config->current_scope)
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
new_config->current_scope = FM.main;
|
2023-06-13 07:39:29 +00:00
|
|
|
|
2023-06-12 09:20:49 +00:00
|
|
|
f_method_scope_pos--;
|
|
|
|
}
|
2022-03-19 15:23:42 +00:00
|
|
|
|
2022-03-10 00:02:45 +00:00
|
|
|
static int
|
|
|
|
f_new_var(struct sym_scope *s)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* - A variable is an offset on vstack from vbase.
|
|
|
|
* - Vbase is set on filter start / function call.
|
2022-10-18 01:58:19 +00:00
|
|
|
* - Scopes contain (non-frame) block scopes inside filter/function scope
|
2022-03-10 00:02:45 +00:00
|
|
|
* - Each scope knows number of vars in that scope
|
2022-10-18 01:58:19 +00:00
|
|
|
* - Offset is therefore a sum of 'slots' up to filter/function scope
|
2022-03-10 00:02:45 +00:00
|
|
|
* - New variables are added on top of vstk, so intermediate values cannot
|
|
|
|
* be there during FI_VAR_INIT. I.e. no 'var' inside 'term'.
|
|
|
|
* - Also, each f_line must always have its scope, otherwise a variable may
|
|
|
|
* be defined but not initialized if relevant f_line is not executed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int offset = s->slots++;
|
|
|
|
|
2022-10-18 01:58:19 +00:00
|
|
|
while (s->block)
|
2022-03-10 00:02:45 +00:00
|
|
|
{
|
|
|
|
s = s->next;
|
|
|
|
ASSERT(s);
|
|
|
|
offset += s->slots;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset >= 0xff)
|
|
|
|
cf_error("Too many variables, at most 255 allowed");
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
2023-10-30 09:44:45 +00:00
|
|
|
/* Macro for top-level pre-defined variables. */
|
|
|
|
#define f_predefined_variable(conf_, name_, type_) \
|
|
|
|
cf_define_symbol(conf_, cf_get_symbol(conf_, name_), SYM_VARIABLE | type_, offset, f_new_var(conf_->current_scope))
|
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
/*
|
|
|
|
* Sets and their items are during parsing handled as lists, linked
|
|
|
|
* through left ptr. The first item in a list also contains a pointer
|
|
|
|
* to the last item in a list (right ptr). For convenience, even items
|
|
|
|
* are handled as one-item lists. Lists are merged by f_merge_items().
|
|
|
|
*/
|
2014-10-02 09:02:14 +00:00
|
|
|
static int
|
|
|
|
f_valid_set_type(int type)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case T_INT:
|
|
|
|
case T_PAIR:
|
|
|
|
case T_QUAD:
|
|
|
|
case T_ENUM:
|
|
|
|
case T_IP:
|
|
|
|
case T_EC:
|
2016-10-01 10:50:29 +00:00
|
|
|
case T_LC:
|
2018-10-25 09:26:58 +00:00
|
|
|
case T_RD:
|
2014-10-02 09:02:14 +00:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2011-05-06 20:00:54 +00:00
|
|
|
|
|
|
|
static inline struct f_tree *
|
|
|
|
f_new_item(struct f_val from, struct f_val to)
|
2009-06-01 17:32:41 +00:00
|
|
|
{
|
2011-05-06 20:00:54 +00:00
|
|
|
struct f_tree *t = f_new_tree();
|
|
|
|
t->right = t;
|
|
|
|
t->from = from;
|
|
|
|
t->to = to;
|
|
|
|
return t;
|
|
|
|
}
|
2009-06-01 17:32:41 +00:00
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
static inline struct f_tree *
|
|
|
|
f_merge_items(struct f_tree *a, struct f_tree *b)
|
|
|
|
{
|
|
|
|
if (!a) return b;
|
|
|
|
a->right->left = b;
|
|
|
|
a->right = b->right;
|
|
|
|
b->right = NULL;
|
|
|
|
return a;
|
|
|
|
}
|
2009-06-01 17:32:41 +00:00
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
static inline struct f_tree *
|
|
|
|
f_new_pair_item(int fa, int ta, int fb, int tb)
|
|
|
|
{
|
2016-10-01 20:31:01 +00:00
|
|
|
check_u16(fa);
|
|
|
|
check_u16(ta);
|
|
|
|
check_u16(fb);
|
|
|
|
check_u16(tb);
|
|
|
|
|
|
|
|
if ((ta < fa) || (tb < fb))
|
|
|
|
cf_error( "From value cannot be higher that To value in pair sets");
|
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
struct f_tree *t = f_new_tree();
|
|
|
|
t->right = t;
|
|
|
|
t->from.type = t->to.type = T_PAIR;
|
|
|
|
t->from.val.i = pair(fa, fb);
|
|
|
|
t->to.val.i = pair(ta, tb);
|
|
|
|
return t;
|
2009-06-01 17:32:41 +00:00
|
|
|
}
|
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
static inline struct f_tree *
|
|
|
|
f_new_pair_set(int fa, int ta, int fb, int tb)
|
2011-03-26 13:18:56 +00:00
|
|
|
{
|
2016-10-01 20:31:01 +00:00
|
|
|
check_u16(fa);
|
|
|
|
check_u16(ta);
|
|
|
|
check_u16(fb);
|
|
|
|
check_u16(tb);
|
2011-03-27 21:27:37 +00:00
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
if ((ta < fa) || (tb < fb))
|
|
|
|
cf_error( "From value cannot be higher that To value in pair sets");
|
2011-03-27 21:27:37 +00:00
|
|
|
|
2016-10-01 20:31:01 +00:00
|
|
|
struct f_tree *lst = NULL;
|
|
|
|
int i;
|
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
for (i = fa; i <= ta; i++)
|
|
|
|
lst = f_merge_items(lst, f_new_pair_item(i, i, fb, tb));
|
|
|
|
|
|
|
|
return lst;
|
2011-03-26 13:18:56 +00:00
|
|
|
}
|
|
|
|
|
2016-10-01 20:31:01 +00:00
|
|
|
#define CC_ALL 0xFFFF
|
2011-08-12 19:03:43 +00:00
|
|
|
#define EC_ALL 0xFFFFFFFF
|
2016-10-01 20:31:01 +00:00
|
|
|
#define LC_ALL 0xFFFFFFFF
|
2011-08-12 19:03:43 +00:00
|
|
|
|
|
|
|
static struct f_tree *
|
|
|
|
f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
|
|
|
|
{
|
|
|
|
u64 fm, to;
|
|
|
|
|
2017-06-19 10:46:40 +00:00
|
|
|
if ((kind != EC_GENERIC) && (ipv4_used || (key >= 0x10000))) {
|
2011-08-12 19:03:43 +00:00
|
|
|
check_u16(vf);
|
|
|
|
if (vt == EC_ALL)
|
|
|
|
vt = 0xFFFF;
|
|
|
|
else
|
|
|
|
check_u16(vt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kind == EC_GENERIC) {
|
|
|
|
fm = ec_generic(key, vf);
|
|
|
|
to = ec_generic(key, vt);
|
|
|
|
}
|
|
|
|
else if (ipv4_used) {
|
|
|
|
fm = ec_ip4(kind, key, vf);
|
|
|
|
to = ec_ip4(kind, key, vt);
|
|
|
|
}
|
|
|
|
else if (key < 0x10000) {
|
|
|
|
fm = ec_as2(kind, key, vf);
|
|
|
|
to = ec_as2(kind, key, vt);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fm = ec_as4(kind, key, vf);
|
|
|
|
to = ec_as4(kind, key, vt);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct f_tree *t = f_new_tree();
|
|
|
|
t->right = t;
|
|
|
|
t->from.type = t->to.type = T_EC;
|
|
|
|
t->from.val.ec = fm;
|
|
|
|
t->to.val.ec = to;
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
2016-10-01 20:31:01 +00:00
|
|
|
static struct f_tree *
|
|
|
|
f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
|
|
|
|
{
|
|
|
|
struct f_tree *t = f_new_tree();
|
|
|
|
t->right = t;
|
|
|
|
t->from.type = t->to.type = T_LC;
|
|
|
|
t->from.val.lc = (lcomm) {f1, f2, f3};
|
|
|
|
t->to.val.lc = (lcomm) {t1, t2, t3};
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
2023-10-29 18:53:09 +00:00
|
|
|
|
2016-11-09 15:36:34 +00:00
|
|
|
/*
|
|
|
|
* Remove all new lines and doubled whitespaces
|
|
|
|
* and convert all tabulators to spaces
|
|
|
|
* and return a copy of string
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
assert_copy_expr(const char *start, size_t len)
|
|
|
|
{
|
|
|
|
/* XXX: Allocates maybe a little more memory than we really finally need */
|
|
|
|
char *str = cfg_alloc(len + 1);
|
|
|
|
|
|
|
|
char *dst = str;
|
|
|
|
const char *src = start - 1;
|
|
|
|
const char *end = start + len;
|
|
|
|
while (++src < end)
|
|
|
|
{
|
|
|
|
if (*src == '\n')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Skip doubled whitespaces */
|
|
|
|
if (src != start)
|
|
|
|
{
|
|
|
|
const char *prev = src - 1;
|
|
|
|
if ((*src == ' ' || *src == '\t') && (*prev == ' ' || *prev == '\t'))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*src == '\t')
|
|
|
|
*dst = ' ';
|
|
|
|
else
|
|
|
|
*dst = *src;
|
|
|
|
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
*dst = '\0';
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* assert_done - create f_instruction of bt_assert
|
|
|
|
* @expr: expression in bt_assert()
|
|
|
|
* @start: pointer to first char of test expression
|
|
|
|
* @end: pointer to the last char of test expression
|
|
|
|
*/
|
|
|
|
static struct f_inst *
|
|
|
|
assert_done(struct f_inst *expr, const char *start, const char *end)
|
|
|
|
{
|
2019-01-21 08:17:54 +00:00
|
|
|
return f_new_inst(FI_ASSERT, expr,
|
|
|
|
(end >= start) ?
|
|
|
|
assert_copy_expr(start, end - start + 1)
|
|
|
|
: "???");
|
2016-11-09 15:36:34 +00:00
|
|
|
}
|
2011-08-12 19:03:43 +00:00
|
|
|
|
2019-01-30 13:03:47 +00:00
|
|
|
static struct f_inst *
|
2023-06-12 09:37:50 +00:00
|
|
|
f_lval_getter(struct f_lval *lval)
|
2019-01-30 13:03:47 +00:00
|
|
|
{
|
|
|
|
switch (lval->type) {
|
2023-10-29 18:53:09 +00:00
|
|
|
case F_LVAL_CONSTANT: return f_new_inst(FI_CONSTANT, *(lval->sym->val));
|
2023-06-12 09:37:50 +00:00
|
|
|
case F_LVAL_VARIABLE: return f_new_inst(FI_VAR_GET, lval->sym);
|
2023-10-29 18:53:09 +00:00
|
|
|
case F_LVAL_SA: return f_new_inst(FI_RTA_GET, lval->rte, lval->sa);
|
|
|
|
case F_LVAL_EA: return f_new_inst(FI_EA_GET, lval->rte, lval->da);
|
2023-10-27 13:56:06 +00:00
|
|
|
case F_LVAL_ATTR_BIT:
|
|
|
|
{
|
|
|
|
struct f_inst *c = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << lval->fab.bit)});
|
2023-10-31 08:58:42 +00:00
|
|
|
return f_new_inst(FI_EQ, c, f_new_inst(FI_BITAND, f_new_inst(FI_EA_GET, lval->rte, lval->fab.class), c));
|
2023-10-27 13:56:06 +00:00
|
|
|
}
|
2023-06-12 09:37:50 +00:00
|
|
|
default: bug("Unknown lval type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct f_inst *
|
|
|
|
f_lval_setter(struct f_lval *lval, struct f_inst *expr)
|
2019-01-30 13:03:47 +00:00
|
|
|
{
|
|
|
|
switch (lval->type) {
|
2023-10-29 18:53:09 +00:00
|
|
|
case F_LVAL_CONSTANT: cf_error("Constant %s is read-only", lval->sym->name);
|
2023-06-12 09:37:50 +00:00
|
|
|
case F_LVAL_VARIABLE: return f_new_inst(FI_VAR_SET, expr, lval->sym);
|
2023-10-29 18:53:09 +00:00
|
|
|
case F_LVAL_SA:
|
|
|
|
if (lval->sa.readonly)
|
|
|
|
cf_error( "This static attribute is read-only.");
|
|
|
|
return f_new_inst(FI_RTA_SET, expr, lval->sa);
|
|
|
|
|
2023-06-12 09:37:50 +00:00
|
|
|
case F_LVAL_EA: return f_new_inst(FI_EA_SET, expr, lval->da);
|
2023-10-27 13:56:06 +00:00
|
|
|
case F_LVAL_ATTR_BIT: return f_new_inst(FI_CONDITION, expr,
|
|
|
|
f_new_inst(FI_EA_SET,
|
|
|
|
f_new_inst(FI_BITOR,
|
|
|
|
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << lval->fab.bit)}),
|
2023-10-31 08:58:42 +00:00
|
|
|
f_new_inst(FI_EA_GET, lval->rte, lval->fab.class)
|
2023-10-27 13:56:06 +00:00
|
|
|
),
|
|
|
|
lval->fab.class),
|
|
|
|
f_new_inst(FI_EA_SET,
|
|
|
|
f_new_inst(FI_BITAND,
|
|
|
|
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = ~(1U << lval->fab.bit)}),
|
2023-10-31 08:58:42 +00:00
|
|
|
f_new_inst(FI_EA_GET, lval->rte, lval->fab.class)
|
2023-10-27 13:56:06 +00:00
|
|
|
),
|
|
|
|
lval->fab.class)
|
|
|
|
);
|
2023-06-12 09:37:50 +00:00
|
|
|
default: bug("Unknown lval type");
|
2019-01-30 13:03:47 +00:00
|
|
|
}
|
2023-06-12 09:37:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct f_inst *
|
|
|
|
assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const char *end)
|
|
|
|
{
|
|
|
|
struct f_inst *setter = f_lval_setter(lval, expr),
|
|
|
|
*getter = f_lval_getter(lval);
|
2019-01-30 13:03:47 +00:00
|
|
|
|
2023-06-12 09:37:50 +00:00
|
|
|
struct f_inst *checker = f_new_inst(FI_EQ, expr, getter);
|
2019-02-08 12:38:12 +00:00
|
|
|
setter->next = checker;
|
2022-03-14 19:36:20 +00:00
|
|
|
|
2019-01-30 13:03:47 +00:00
|
|
|
return assert_done(setter, start, end);
|
|
|
|
}
|
|
|
|
|
1999-01-15 16:49:17 +00:00
|
|
|
CF_DECLS
|
|
|
|
|
2000-03-09 13:21:40 +00:00
|
|
|
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
2020-04-29 13:07:33 +00:00
|
|
|
ACCEPT, REJECT, ERROR,
|
2024-10-18 14:39:42 +00:00
|
|
|
INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC, ENUM,
|
2023-08-24 02:30:42 +00:00
|
|
|
SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
1999-09-29 14:24:58 +00:00
|
|
|
IF, THEN, ELSE, CASE,
|
2022-03-14 19:36:20 +00:00
|
|
|
FOR, IN, DO,
|
2011-08-12 19:03:43 +00:00
|
|
|
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
2023-10-28 21:42:21 +00:00
|
|
|
FROM, GW, NET, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
|
2023-10-15 21:52:46 +00:00
|
|
|
ROA_CHECK, ASPA_CHECK,
|
1999-11-10 12:44:07 +00:00
|
|
|
DEFINED,
|
2022-05-04 10:24:30 +00:00
|
|
|
ADD, DELETE, RESET,
|
2023-06-13 07:39:29 +00:00
|
|
|
PREPEND,
|
2000-04-17 10:16:47 +00:00
|
|
|
EMPTY,
|
2018-11-21 19:37:11 +00:00
|
|
|
FILTER, WHERE, EVAL, ATTRIBUTE,
|
2023-08-24 02:30:42 +00:00
|
|
|
FROM_HEX,
|
2021-08-25 20:20:48 +00:00
|
|
|
BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT, STACKS)
|
1999-01-15 16:49:17 +00:00
|
|
|
|
1999-11-10 12:44:07 +00:00
|
|
|
%nonassoc THEN
|
1999-11-11 13:27:59 +00:00
|
|
|
%nonassoc ELSE
|
1999-11-10 12:44:07 +00:00
|
|
|
|
2019-08-13 14:45:27 +00:00
|
|
|
%type <xp> cmds_int cmd_prep
|
2024-11-25 14:14:27 +00:00
|
|
|
%type <x> term term_bs cmd cmd_var cmds constant constructor var var_list var_list_r function_call bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
|
2017-10-19 10:39:44 +00:00
|
|
|
%type <fsa> static_attr
|
2019-02-15 12:53:17 +00:00
|
|
|
%type <f> filter where_filter
|
2019-05-21 16:33:37 +00:00
|
|
|
%type <fl> filter_body function_body
|
2019-01-30 13:03:47 +00:00
|
|
|
%type <flv> lvalue
|
2023-07-04 17:07:30 +00:00
|
|
|
%type <i> type function_vars function_type
|
2022-03-03 02:38:12 +00:00
|
|
|
%type <fa> function_argsn function_args
|
2019-01-21 08:17:54 +00:00
|
|
|
%type <ecs> ec_kind
|
2022-03-03 02:38:12 +00:00
|
|
|
%type <fret> break_command
|
2016-10-01 20:31:01 +00:00
|
|
|
%type <i32> cnum
|
2022-10-03 18:06:13 +00:00
|
|
|
%type <e> pair_item ec_item lc_item set_item switch_item ec_items set_items switch_items switch_body
|
2009-03-31 10:55:57 +00:00
|
|
|
%type <trie> fprefix_set
|
2024-10-08 17:34:51 +00:00
|
|
|
%type <v> set_atom0 set_atom switch_atom fipa
|
2015-12-16 09:25:12 +00:00
|
|
|
%type <px> fprefix
|
2016-11-09 15:36:34 +00:00
|
|
|
%type <t> get_cf_position
|
2022-03-14 19:36:20 +00:00
|
|
|
%type <s> for_var
|
1999-01-15 16:49:17 +00:00
|
|
|
|
|
|
|
CF_GRAMMAR
|
|
|
|
|
2021-08-25 20:20:48 +00:00
|
|
|
conf: FILTER STACKS expr expr ';' {
|
2024-06-13 13:49:56 +00:00
|
|
|
if (($3 < 16) || ($4 < 16))
|
|
|
|
/* Check for self-crippling values */
|
|
|
|
cf_error("Filter stack values lesser than 16 not supported");
|
2021-08-25 20:20:48 +00:00
|
|
|
new_config->filter_vstk = $3;
|
|
|
|
new_config->filter_estk = $4;
|
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2018-06-26 12:29:03 +00:00
|
|
|
conf: filter_def ;
|
1999-03-17 14:29:39 +00:00
|
|
|
filter_def:
|
2023-10-25 12:41:11 +00:00
|
|
|
FILTER symbol {
|
2023-10-27 16:29:31 +00:00
|
|
|
$2 = cf_define_symbol(new_config, $2, SYM_FILTER, filter, NULL);
|
2023-10-25 12:41:11 +00:00
|
|
|
cf_enter_filters();
|
2023-10-27 16:29:31 +00:00
|
|
|
cf_push_scope( new_config, $2 );
|
2023-06-15 11:25:40 +00:00
|
|
|
this_function = NULL;
|
2023-10-25 12:41:11 +00:00
|
|
|
} filter_body {
|
2019-02-15 12:53:17 +00:00
|
|
|
struct filter *f = cfg_alloc(sizeof(struct filter));
|
2019-02-26 15:44:24 +00:00
|
|
|
*f = (struct filter) { .sym = $2, .root = $4 };
|
2019-02-15 12:53:17 +00:00
|
|
|
$2->filter = f;
|
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_pop_scope(new_config);
|
2023-10-25 12:41:11 +00:00
|
|
|
cf_exit_filters();
|
1999-01-15 16:49:17 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2018-06-26 12:29:03 +00:00
|
|
|
conf: filter_eval ;
|
2000-05-15 10:49:38 +00:00
|
|
|
filter_eval:
|
2023-08-24 02:45:55 +00:00
|
|
|
EVAL term { cf_eval_int($2); }
|
2000-05-15 10:49:38 +00:00
|
|
|
;
|
|
|
|
|
2018-11-21 19:37:11 +00:00
|
|
|
conf: custom_attr ;
|
2019-07-30 12:28:40 +00:00
|
|
|
custom_attr: ATTRIBUTE type symbol ';' {
|
2023-10-29 14:42:46 +00:00
|
|
|
cf_enter_filters();
|
|
|
|
struct ea_class *ac = ea_class_find_by_name($3->name);
|
|
|
|
cf_exit_filters();
|
|
|
|
if (ac && (ac->type == $2))
|
|
|
|
ea_ref_class(new_config->pool, ac);
|
|
|
|
else
|
|
|
|
ac = ea_register_alloc(new_config->pool, (struct ea_class) {
|
2022-03-19 15:23:42 +00:00
|
|
|
.name = $3->name,
|
|
|
|
.type = $2,
|
2023-10-29 14:42:46 +00:00
|
|
|
})->class;
|
|
|
|
|
|
|
|
cf_define_symbol(new_config, $3, SYM_ATTRIBUTE, attribute, ac);
|
2018-11-21 19:37:11 +00:00
|
|
|
};
|
|
|
|
|
2018-06-26 12:29:03 +00:00
|
|
|
conf: bt_test_suite ;
|
2016-11-09 15:36:34 +00:00
|
|
|
bt_test_suite:
|
2023-10-25 12:41:11 +00:00
|
|
|
BT_TEST_SUITE '(' CF_SYM_KNOWN ',' text ')' {
|
2019-05-17 20:18:49 +00:00
|
|
|
cf_assert_symbol($3, SYM_FUNCTION);
|
2019-02-13 11:25:30 +00:00
|
|
|
struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
|
2019-02-15 12:53:17 +00:00
|
|
|
t->fn = $3->function;
|
2016-11-09 15:36:34 +00:00
|
|
|
t->fn_name = $3->name;
|
|
|
|
t->dsc = $5;
|
|
|
|
|
|
|
|
add_tail(&new_config->tests, &t->n);
|
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2019-02-13 11:25:30 +00:00
|
|
|
conf: bt_test_same ;
|
|
|
|
bt_test_same:
|
2023-10-25 12:41:11 +00:00
|
|
|
BT_TEST_SAME '(' CF_SYM_KNOWN ',' CF_SYM_KNOWN ',' NUM ')' {
|
2019-05-17 20:18:49 +00:00
|
|
|
cf_assert_symbol($3, SYM_FUNCTION);
|
|
|
|
cf_assert_symbol($5, SYM_FUNCTION);
|
2019-02-13 11:25:30 +00:00
|
|
|
struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
|
2019-02-15 12:53:17 +00:00
|
|
|
t->fn = $3->function;
|
|
|
|
t->cmp = $5->function;
|
2019-02-13 11:25:30 +00:00
|
|
|
t->result = $7;
|
|
|
|
t->fn_name = $3->name;
|
|
|
|
t->dsc = $5->name;
|
|
|
|
add_tail(&new_config->tests, &t->n);
|
|
|
|
}
|
|
|
|
;
|
|
|
|
|
1999-03-29 20:21:28 +00:00
|
|
|
type:
|
|
|
|
INT { $$ = T_INT; }
|
|
|
|
| BOOL { $$ = T_BOOL; }
|
|
|
|
| IP { $$ = T_IP; }
|
2017-03-13 12:50:32 +00:00
|
|
|
| RD { $$ = T_RD; }
|
2015-12-16 09:25:12 +00:00
|
|
|
| PREFIX { $$ = T_NET; }
|
1999-03-29 20:21:28 +00:00
|
|
|
| PAIR { $$ = T_PAIR; }
|
2010-03-29 17:29:03 +00:00
|
|
|
| QUAD { $$ = T_QUAD; }
|
2011-08-12 19:03:43 +00:00
|
|
|
| EC { $$ = T_EC; }
|
2016-10-01 10:50:29 +00:00
|
|
|
| LC { $$ = T_LC; }
|
1999-03-29 20:21:28 +00:00
|
|
|
| STRING { $$ = T_STRING; }
|
2023-08-24 02:30:42 +00:00
|
|
|
| BYTESTRING { $$ = T_BYTESTRING; }
|
2000-04-12 13:31:39 +00:00
|
|
|
| BGPMASK { $$ = T_PATH_MASK; }
|
|
|
|
| BGPPATH { $$ = T_PATH; }
|
|
|
|
| CLIST { $$ = T_CLIST; }
|
2011-08-12 19:03:43 +00:00
|
|
|
| ECLIST { $$ = T_ECLIST; }
|
2016-10-01 10:50:29 +00:00
|
|
|
| LCLIST { $$ = T_LCLIST; }
|
2023-10-29 18:53:09 +00:00
|
|
|
| ROUTE { $$ = T_ROUTE; }
|
2016-11-08 16:46:29 +00:00
|
|
|
| type SET {
|
1999-03-29 20:21:28 +00:00
|
|
|
switch ($1) {
|
2009-03-31 10:55:57 +00:00
|
|
|
case T_INT:
|
|
|
|
case T_PAIR:
|
2010-03-29 17:29:03 +00:00
|
|
|
case T_QUAD:
|
2024-10-18 14:39:42 +00:00
|
|
|
case T_ENUM:
|
2011-08-12 19:03:43 +00:00
|
|
|
case T_EC:
|
2016-10-01 10:50:29 +00:00
|
|
|
case T_LC:
|
2018-10-25 09:26:58 +00:00
|
|
|
case T_RD:
|
2010-03-29 17:29:03 +00:00
|
|
|
case T_IP:
|
2009-03-31 10:55:57 +00:00
|
|
|
$$ = T_SET;
|
|
|
|
break;
|
|
|
|
|
2015-12-16 09:25:12 +00:00
|
|
|
case T_NET:
|
2009-03-31 10:55:57 +00:00
|
|
|
$$ = T_PREFIX_SET;
|
|
|
|
break;
|
|
|
|
|
1999-03-29 20:21:28 +00:00
|
|
|
default:
|
2000-06-08 10:26:19 +00:00
|
|
|
cf_error( "You can't create sets of this type." );
|
1999-03-29 20:21:28 +00:00
|
|
|
}
|
2009-03-31 10:55:57 +00:00
|
|
|
}
|
2024-11-29 08:51:38 +00:00
|
|
|
| ENUM
|
2024-11-29 10:27:30 +00:00
|
|
|
{ $<i>$ = cf_maybe_exit_filters(); }
|
2024-11-29 08:51:38 +00:00
|
|
|
enum_type
|
|
|
|
{
|
2024-11-29 10:27:30 +00:00
|
|
|
if ($<i>2) cf_enter_filters();
|
2024-11-29 08:51:38 +00:00
|
|
|
$$ = $3;
|
|
|
|
}
|
1999-03-29 20:21:28 +00:00
|
|
|
;
|
|
|
|
|
2019-07-15 13:06:52 +00:00
|
|
|
function_argsn:
|
2022-03-03 02:38:12 +00:00
|
|
|
/* EMPTY */ { $$ = NULL; }
|
2019-07-30 12:28:40 +00:00
|
|
|
| function_argsn type symbol ';' {
|
2019-07-15 13:06:52 +00:00
|
|
|
if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
|
2022-03-03 02:38:12 +00:00
|
|
|
$$ = cfg_alloc(sizeof(struct f_arg));
|
2023-06-13 08:51:03 +00:00
|
|
|
$$->arg = cf_define_symbol(new_config, $3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
|
2022-03-03 02:38:12 +00:00
|
|
|
$$->next = $1;
|
2019-07-15 13:06:52 +00:00
|
|
|
}
|
1999-11-03 22:23:01 +00:00
|
|
|
;
|
|
|
|
|
2019-07-15 13:06:52 +00:00
|
|
|
function_args:
|
2022-03-03 02:38:12 +00:00
|
|
|
'(' ')' { $$ = NULL; }
|
2019-07-30 12:28:40 +00:00
|
|
|
| '(' function_argsn type symbol ')' {
|
2022-03-03 02:38:12 +00:00
|
|
|
$$ = cfg_alloc(sizeof(struct f_arg));
|
2023-06-13 08:51:03 +00:00
|
|
|
$$->arg = cf_define_symbol(new_config, $4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
|
2022-03-03 02:38:12 +00:00
|
|
|
$$->next = $2;
|
1999-03-17 14:29:39 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2019-07-15 13:06:52 +00:00
|
|
|
function_vars:
|
|
|
|
/* EMPTY */ { $$ = 0; }
|
2019-07-30 12:28:40 +00:00
|
|
|
| function_vars type symbol ';' {
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_define_symbol(new_config, $3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
|
2019-07-15 13:06:52 +00:00
|
|
|
$$ = $1 + 1;
|
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2023-07-04 17:07:30 +00:00
|
|
|
function_type:
|
|
|
|
/* EMPTY */ { $$ = T_VOID; }
|
|
|
|
| IMP type { $$ = $2; }
|
|
|
|
;
|
|
|
|
|
2019-06-25 14:18:06 +00:00
|
|
|
filter_body: function_body ;
|
2019-05-21 16:33:37 +00:00
|
|
|
|
1999-03-17 14:29:39 +00:00
|
|
|
filter:
|
2023-10-25 12:41:11 +00:00
|
|
|
CF_SYM_KNOWN {
|
2019-05-17 20:18:49 +00:00
|
|
|
cf_assert_symbol($1, SYM_FILTER);
|
2019-02-15 12:53:17 +00:00
|
|
|
$$ = $1->filter;
|
|
|
|
}
|
2023-10-25 12:41:11 +00:00
|
|
|
| {
|
|
|
|
cf_enter_filters();
|
2023-10-27 16:29:31 +00:00
|
|
|
cf_push_scope(new_config, NULL);
|
2023-06-15 11:25:40 +00:00
|
|
|
this_function = NULL;
|
2023-10-25 12:41:11 +00:00
|
|
|
} filter_body {
|
2019-02-15 12:53:17 +00:00
|
|
|
struct filter *f = cfg_alloc(sizeof(struct filter));
|
2022-10-18 01:58:19 +00:00
|
|
|
*f = (struct filter) { .root = $2 };
|
2019-02-15 12:53:17 +00:00
|
|
|
$$ = f;
|
2022-10-18 01:58:19 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_pop_scope(new_config);
|
2023-10-25 12:41:11 +00:00
|
|
|
cf_exit_filters();
|
1999-03-17 14:29:39 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
1999-12-02 14:04:44 +00:00
|
|
|
where_filter:
|
2023-10-25 12:41:11 +00:00
|
|
|
WHERE {
|
|
|
|
cf_enter_filters();
|
|
|
|
} term {
|
2018-12-20 13:34:35 +00:00
|
|
|
/* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
|
2023-10-25 12:41:11 +00:00
|
|
|
$$ = f_new_where($3);
|
|
|
|
cf_exit_filters();
|
2018-12-27 13:26:11 +00:00
|
|
|
}
|
1999-12-02 14:04:44 +00:00
|
|
|
;
|
|
|
|
|
1999-03-29 20:21:28 +00:00
|
|
|
function_body:
|
2019-07-15 13:06:52 +00:00
|
|
|
function_vars '{' cmds '}' {
|
2022-03-09 01:32:29 +00:00
|
|
|
$$ = f_linearize($3, 0);
|
2019-06-25 14:18:06 +00:00
|
|
|
$$->vars = $1;
|
1999-03-02 19:49:28 +00:00
|
|
|
}
|
1999-03-29 20:21:28 +00:00
|
|
|
;
|
|
|
|
|
2018-06-26 12:29:03 +00:00
|
|
|
conf: function_def ;
|
2023-06-15 11:25:40 +00:00
|
|
|
|
1999-03-29 20:21:28 +00:00
|
|
|
function_def:
|
2023-07-04 17:07:30 +00:00
|
|
|
FUNCTION symbol {
|
|
|
|
DBG( "Beginning of function %s\n", $2->name );
|
|
|
|
this_function = cf_define_symbol(new_config, $2, SYM_FUNCTION, function, NULL);
|
2023-10-25 12:41:11 +00:00
|
|
|
cf_enter_filters();
|
2023-06-15 11:25:40 +00:00
|
|
|
cf_push_scope(new_config, this_function);
|
2023-07-04 17:07:30 +00:00
|
|
|
} function_args function_type {
|
2022-03-06 01:18:01 +00:00
|
|
|
/* Make dummy f_line for storing function prototype */
|
|
|
|
struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
|
2023-06-15 11:25:40 +00:00
|
|
|
this_function->function = dummy;
|
|
|
|
|
2023-07-04 17:07:30 +00:00
|
|
|
dummy->return_type = $5;
|
2022-03-03 02:38:12 +00:00
|
|
|
|
|
|
|
/* Revert the args */
|
2023-07-04 17:07:30 +00:00
|
|
|
while ($4) {
|
|
|
|
struct f_arg *tmp = $4;
|
|
|
|
$4 = $4->next;
|
2022-03-03 02:38:12 +00:00
|
|
|
|
2022-03-06 01:18:01 +00:00
|
|
|
tmp->next = dummy->arg_list;
|
|
|
|
dummy->arg_list = tmp;
|
|
|
|
dummy->args++;
|
2022-03-03 02:38:12 +00:00
|
|
|
}
|
2022-03-06 01:18:01 +00:00
|
|
|
} function_body {
|
2023-06-15 11:25:40 +00:00
|
|
|
$7->args = this_function->function->args;
|
|
|
|
$7->arg_list = this_function->function->arg_list;
|
|
|
|
$7->return_type = this_function->function->return_type;
|
2023-07-04 17:07:30 +00:00
|
|
|
$2->function = $7;
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_pop_scope(new_config);
|
2023-10-25 12:41:11 +00:00
|
|
|
cf_exit_filters();
|
1999-03-29 20:21:28 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
|
|
|
/* Programs */
|
|
|
|
|
|
|
|
cmds: /* EMPTY */ { $$ = NULL; }
|
2019-07-02 23:23:49 +00:00
|
|
|
| cmds_int { $$ = $1.begin; }
|
2010-02-01 23:03:46 +00:00
|
|
|
;
|
|
|
|
|
2022-03-10 00:02:45 +00:00
|
|
|
cmd_var: var | cmd ;
|
|
|
|
|
|
|
|
cmd_prep: cmd_var {
|
2019-07-02 23:23:49 +00:00
|
|
|
$$.begin = $$.end = $1;
|
2019-08-13 14:45:27 +00:00
|
|
|
if ($1)
|
|
|
|
while ($$.end->next)
|
|
|
|
$$.end = $$.end->next;
|
|
|
|
}
|
|
|
|
;
|
|
|
|
|
|
|
|
cmds_int: cmd_prep
|
|
|
|
| cmds_int cmd_prep {
|
|
|
|
if (!$1.begin)
|
|
|
|
$$ = $2;
|
|
|
|
else if (!$2.begin)
|
|
|
|
$$ = $1;
|
|
|
|
else {
|
|
|
|
$$.begin = $1.begin;
|
|
|
|
$$.end = $2.end;
|
|
|
|
$1.end->next = $2.begin;
|
2019-07-02 23:23:49 +00:00
|
|
|
}
|
|
|
|
}
|
1999-03-02 19:49:28 +00:00
|
|
|
;
|
|
|
|
|
1999-10-12 06:27:42 +00:00
|
|
|
/*
|
|
|
|
* Complex types, their bison value is struct f_val
|
|
|
|
*/
|
2000-05-15 11:48:23 +00:00
|
|
|
fipa:
|
2015-12-24 14:52:03 +00:00
|
|
|
IP4 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); }
|
|
|
|
| IP6 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); }
|
1999-10-12 06:27:42 +00:00
|
|
|
;
|
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set constants. They are also used in switch cases. We use separate
|
|
|
|
* nonterminals for switch (set_atom/switch_atom, set_item/switch_item ...)
|
|
|
|
* to elude a collision between symbol (in expr) in set_atom and symbol
|
|
|
|
* as a function call in switch case cmds.
|
|
|
|
*/
|
|
|
|
|
2024-10-08 17:34:51 +00:00
|
|
|
set_atom0:
|
2018-10-25 09:26:58 +00:00
|
|
|
NUM { $$.type = T_INT; $$.val.i = $1; }
|
|
|
|
| fipa { $$ = $1; }
|
|
|
|
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
|
2024-10-18 14:39:42 +00:00
|
|
|
| ENUM_TOKEN { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
2014-10-02 09:02:14 +00:00
|
|
|
| '(' term ')' {
|
2023-10-13 11:36:47 +00:00
|
|
|
$$ = cf_eval_tmp($2, T_VOID);
|
2024-10-08 17:34:51 +00:00
|
|
|
if (!f_valid_set_type($$.type))
|
|
|
|
cf_error("Set-incompatible type (%s)", f_type_name($$.type));
|
2014-10-02 09:02:14 +00:00
|
|
|
}
|
2024-10-08 17:34:51 +00:00
|
|
|
;
|
|
|
|
|
|
|
|
set_atom:
|
|
|
|
set_atom0
|
2023-10-25 12:41:11 +00:00
|
|
|
| CF_SYM_KNOWN {
|
2019-05-17 20:18:49 +00:00
|
|
|
cf_assert_symbol($1, SYM_CONSTANT);
|
2024-10-08 17:34:51 +00:00
|
|
|
if (!f_valid_set_type(SYM_TYPE($1)))
|
|
|
|
cf_error("%s: Set-incompatible type (%s)", $1->name, f_type_name(SYM_TYPE($1)));
|
2019-02-15 12:53:17 +00:00
|
|
|
$$ = *$1->val;
|
2014-10-02 09:02:14 +00:00
|
|
|
}
|
2011-05-06 20:00:54 +00:00
|
|
|
;
|
1999-04-12 19:58:18 +00:00
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
switch_atom:
|
2024-10-08 17:34:51 +00:00
|
|
|
set_atom0
|
2011-05-06 20:00:54 +00:00
|
|
|
;
|
|
|
|
|
2016-10-01 20:31:01 +00:00
|
|
|
cnum:
|
2023-08-24 02:45:55 +00:00
|
|
|
term { $$ = cf_eval_int($1); }
|
2011-05-06 20:00:54 +00:00
|
|
|
|
|
|
|
pair_item:
|
2016-10-01 20:31:01 +00:00
|
|
|
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
|
|
|
|
| '(' cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_item($2, $2, $4, $6); }
|
|
|
|
| '(' cnum ',' '*' ')' { $$ = f_new_pair_item($2, $2, 0, CC_ALL); }
|
|
|
|
| '(' cnum DDOT cnum ',' cnum ')' { $$ = f_new_pair_set($2, $4, $6, $6); }
|
|
|
|
| '(' cnum DDOT cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_set($2, $4, $6, $8); }
|
|
|
|
| '(' cnum DDOT cnum ',' '*' ')' { $$ = f_new_pair_item($2, $4, 0, CC_ALL); }
|
|
|
|
| '(' '*' ',' cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $4); }
|
|
|
|
| '(' '*' ',' cnum DDOT cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $6); }
|
|
|
|
| '(' '*' ',' '*' ')' { $$ = f_new_pair_item(0, CC_ALL, 0, CC_ALL); }
|
|
|
|
| '(' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ')'
|
|
|
|
{ $$ = f_new_pair_item($2, $8, $4, $10); }
|
1999-04-12 19:58:18 +00:00
|
|
|
;
|
|
|
|
|
2011-08-12 19:03:43 +00:00
|
|
|
ec_kind:
|
|
|
|
RT { $$ = EC_RT; }
|
|
|
|
| RO { $$ = EC_RO; }
|
|
|
|
| UNKNOWN NUM { $$ = $2; }
|
|
|
|
| GENERIC { $$ = EC_GENERIC; }
|
|
|
|
;
|
|
|
|
|
|
|
|
ec_item:
|
2016-10-01 20:31:01 +00:00
|
|
|
'(' ec_kind ',' cnum ',' cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $6); }
|
|
|
|
| '(' ec_kind ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $8); }
|
|
|
|
| '(' ec_kind ',' cnum ',' '*' ')' { $$ = f_new_ec_item($2, 0, $4, 0, EC_ALL); }
|
2011-08-12 19:03:43 +00:00
|
|
|
;
|
|
|
|
|
2016-10-01 20:31:01 +00:00
|
|
|
lc_item:
|
|
|
|
'(' cnum ',' cnum ',' cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $6); }
|
|
|
|
| '(' cnum ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $8); }
|
|
|
|
| '(' cnum ',' cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $4, 0, LC_ALL); }
|
|
|
|
| '(' cnum ',' cnum DDOT cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $6, 0, LC_ALL); }
|
|
|
|
| '(' cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $2, 0, LC_ALL, 0, LC_ALL); }
|
|
|
|
| '(' cnum DDOT cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $4, 0, LC_ALL, 0, LC_ALL); }
|
|
|
|
| '(' '*' ',' '*' ',' '*' ')' { $$ = f_new_lc_item(0, LC_ALL, 0, LC_ALL, 0, LC_ALL); }
|
|
|
|
| '(' cnum ',' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ',' cnum ')'
|
|
|
|
{ $$ = f_new_lc_item($2, $10, $4, $12, $6, $14); }
|
|
|
|
;
|
2011-08-12 19:03:43 +00:00
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
set_item:
|
|
|
|
pair_item
|
2011-08-12 19:03:43 +00:00
|
|
|
| ec_item
|
2016-10-01 20:31:01 +00:00
|
|
|
| lc_item
|
2011-05-06 20:00:54 +00:00
|
|
|
| set_atom { $$ = f_new_item($1, $1); }
|
|
|
|
| set_atom DDOT set_atom { $$ = f_new_item($1, $3); }
|
|
|
|
;
|
|
|
|
|
|
|
|
switch_item:
|
|
|
|
pair_item
|
2011-08-12 19:03:43 +00:00
|
|
|
| ec_item
|
2016-10-01 20:31:01 +00:00
|
|
|
| lc_item
|
2011-05-06 20:00:54 +00:00
|
|
|
| switch_atom { $$ = f_new_item($1, $1); }
|
|
|
|
| switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); }
|
|
|
|
;
|
|
|
|
|
2022-10-03 18:06:13 +00:00
|
|
|
ec_items:
|
|
|
|
ec_item
|
|
|
|
| ec_items ',' ec_item { $$ = f_merge_items($1, $3); }
|
|
|
|
;
|
|
|
|
|
1999-04-12 19:58:18 +00:00
|
|
|
set_items:
|
2011-05-06 20:00:54 +00:00
|
|
|
set_item
|
|
|
|
| set_items ',' set_item { $$ = f_merge_items($1, $3); }
|
|
|
|
;
|
|
|
|
|
|
|
|
switch_items:
|
|
|
|
switch_item
|
|
|
|
| switch_items ',' switch_item { $$ = f_merge_items($1, $3); }
|
1999-04-12 19:58:18 +00:00
|
|
|
;
|
|
|
|
|
2009-03-31 10:55:57 +00:00
|
|
|
fprefix:
|
2015-12-24 14:52:03 +00:00
|
|
|
net_ip_ { $$.net = $1; $$.lo = $1.pxlen; $$.hi = $1.pxlen; }
|
|
|
|
| net_ip_ '+' { $$.net = $1; $$.lo = $1.pxlen; $$.hi = net_max_prefix_length[$1.type]; }
|
|
|
|
| net_ip_ '-' { $$.net = $1; $$.lo = 0; $$.hi = $1.pxlen; }
|
|
|
|
| net_ip_ '{' NUM ',' NUM '}' {
|
2015-12-16 09:25:12 +00:00
|
|
|
$$.net = $1; $$.lo = $3; $$.hi = $5;
|
2017-05-23 15:22:53 +00:00
|
|
|
if (($3 > $5) || ($5 > net_max_prefix_length[$1.type]))
|
|
|
|
cf_error("Invalid prefix pattern range: {%u, %u}", $3, $5);
|
2009-03-31 10:55:57 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
|
|
|
fprefix_set:
|
2020-03-26 02:57:48 +00:00
|
|
|
fprefix { $$ = f_new_trie(cfg_mem, 0); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); }
|
|
|
|
| fprefix_set ',' fprefix { $$ = $1; if (!trie_add_prefix($$, &($3.net), $3.lo, $3.hi)) cf_error("Mixed IPv4/IPv6 prefixes in prefix set"); }
|
2009-03-31 10:55:57 +00:00
|
|
|
;
|
|
|
|
|
1999-10-28 21:03:36 +00:00
|
|
|
switch_body: /* EMPTY */ { $$ = NULL; }
|
2024-11-28 11:43:43 +00:00
|
|
|
| switch_body switch_items ':' cmd {
|
2011-05-06 20:00:54 +00:00
|
|
|
/* Fill data fields */
|
|
|
|
struct f_tree *t;
|
|
|
|
for (t = $2; t; t = t->left)
|
2023-01-07 19:18:44 +00:00
|
|
|
t->data = $4;
|
2011-05-06 20:00:54 +00:00
|
|
|
$$ = f_merge_items($1, $2);
|
1999-10-28 21:03:36 +00:00
|
|
|
}
|
2024-11-25 14:14:27 +00:00
|
|
|
| switch_body ELSECOL cmd {
|
2011-05-06 20:00:54 +00:00
|
|
|
struct f_tree *t = f_new_tree();
|
|
|
|
t->from.type = t->to.type = T_VOID;
|
|
|
|
t->right = t;
|
2023-01-07 19:18:44 +00:00
|
|
|
t->data = $3;
|
2011-05-06 20:00:54 +00:00
|
|
|
$$ = f_merge_items($1, t);
|
|
|
|
}
|
1999-10-28 21:03:36 +00:00
|
|
|
;
|
1999-10-12 06:27:42 +00:00
|
|
|
|
2009-06-01 17:32:41 +00:00
|
|
|
bgp_path_expr:
|
2023-10-29 18:53:09 +00:00
|
|
|
lvalue { $$ = f_lval_getter(&$1); }
|
2009-06-01 17:32:41 +00:00
|
|
|
| '(' term ')' { $$ = $2; }
|
|
|
|
;
|
|
|
|
|
2009-01-27 16:35:00 +00:00
|
|
|
bgp_path:
|
2017-09-27 14:55:09 +00:00
|
|
|
PO bgp_path_tail PC { $$ = $2; }
|
2009-01-27 16:35:00 +00:00
|
|
|
;
|
|
|
|
|
2017-09-27 14:55:09 +00:00
|
|
|
bgp_path_tail:
|
2019-02-08 12:38:12 +00:00
|
|
|
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; }
|
|
|
|
| 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; }
|
2022-03-04 13:07:58 +00:00
|
|
|
| '[' ']' 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; }
|
2019-08-06 14:58:13 +00:00
|
|
|
| '[' set_items ']' bgp_path_tail {
|
|
|
|
if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
|
|
|
|
$$ = 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;
|
|
|
|
}
|
2019-02-08 12:38:12 +00:00
|
|
|
| '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); $$->next = $2; }
|
|
|
|
| '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); $$->next = $2; }
|
2020-05-18 14:25:08 +00:00
|
|
|
| '+' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_LOOP }, }); $$->next = $2; }
|
2019-02-08 12:38:12 +00:00
|
|
|
| bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; }
|
2016-06-08 22:30:41 +00:00
|
|
|
| { $$ = NULL; }
|
2009-01-27 16:35:00 +00:00
|
|
|
;
|
2000-04-12 12:10:37 +00:00
|
|
|
|
1999-04-07 12:11:08 +00:00
|
|
|
constant:
|
2023-08-24 02:30:42 +00:00
|
|
|
NUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
|
|
|
|
| TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
|
|
|
|
| FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
|
|
|
|
| TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
|
|
|
|
| BYTETEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BYTESTRING, .val.bs = $1, }); }
|
|
|
|
| fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
|
|
|
|
| VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
|
|
|
|
| net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
|
2022-03-04 13:07:58 +00:00
|
|
|
| '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
|
2018-12-20 14:25:04 +00:00
|
|
|
| '[' set_items ']' {
|
|
|
|
DBG( "We've got a set here..." );
|
2019-01-21 08:17:54 +00:00
|
|
|
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
|
2018-12-20 14:25:04 +00:00
|
|
|
DBG( "ook\n" );
|
|
|
|
}
|
2019-01-21 08:17:54 +00:00
|
|
|
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
|
2024-10-18 14:39:42 +00:00
|
|
|
| ENUM_TOKEN { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
|
1999-04-07 12:11:08 +00:00
|
|
|
;
|
|
|
|
|
2011-08-12 19:03:43 +00:00
|
|
|
constructor:
|
2019-01-21 08:17:54 +00:00
|
|
|
'(' term ',' term ')' { $$ = f_new_inst(FI_PAIR_CONSTRUCT, $2, $4); }
|
|
|
|
| '(' ec_kind ',' term ',' term ')' { $$ = f_new_inst(FI_EC_CONSTRUCT, $4, $6, $2); }
|
|
|
|
| '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_LC_CONSTRUCT, $2, $4, $6); }
|
2019-07-15 13:12:18 +00:00
|
|
|
| bgp_path { $$ = f_new_inst(FI_PATHMASK_CONSTRUCT, $1); }
|
2011-08-12 19:03:43 +00:00
|
|
|
;
|
|
|
|
|
2009-06-01 17:32:41 +00:00
|
|
|
|
2023-07-12 18:01:03 +00:00
|
|
|
/* This generates the function_call variable list backwards */
|
|
|
|
var_list_r:
|
|
|
|
/* EMPTY */ { $$ = NULL; }
|
2019-05-21 16:33:37 +00:00
|
|
|
| term { $$ = $1; }
|
2023-07-12 18:01:03 +00:00
|
|
|
| var_list_r ',' term { $$ = $3; $$->next = $1; }
|
|
|
|
;
|
|
|
|
|
|
|
|
var_list: var_list_r
|
|
|
|
{
|
|
|
|
$$ = NULL;
|
|
|
|
|
|
|
|
/* Revert the var_list_r */
|
|
|
|
while ($1) {
|
|
|
|
struct f_inst *tmp = $1;
|
|
|
|
$1 = $1->next;
|
|
|
|
|
|
|
|
tmp->next = $$;
|
|
|
|
$$ = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
;
|
2019-05-21 16:33:37 +00:00
|
|
|
|
2000-02-25 11:15:26 +00:00
|
|
|
function_call:
|
2023-10-25 12:41:11 +00:00
|
|
|
CF_SYM_KNOWN '(' var_list ')'
|
2022-03-01 01:04:35 +00:00
|
|
|
{
|
2019-05-21 16:33:37 +00:00
|
|
|
if ($1->class != SYM_FUNCTION)
|
|
|
|
cf_error("You can't call something which is not a function. Really.");
|
|
|
|
|
2023-07-12 18:01:03 +00:00
|
|
|
$$ = f_new_inst(FI_CALL, $3, $1);
|
2000-02-25 11:15:26 +00:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2009-06-01 17:32:41 +00:00
|
|
|
|
2000-05-30 10:23:04 +00:00
|
|
|
static_attr:
|
2022-04-20 11:56:04 +00:00
|
|
|
GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); }
|
2018-12-27 13:26:11 +00:00
|
|
|
| NET { $$ = f_new_static_attr(T_NET, SA_NET, 1); }
|
|
|
|
| PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 1); }
|
|
|
|
| DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); }
|
|
|
|
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
|
|
|
|
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
|
2020-12-02 04:02:26 +00:00
|
|
|
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
|
2021-05-17 15:50:04 +00:00
|
|
|
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
|
2000-05-30 10:23:04 +00:00
|
|
|
;
|
|
|
|
|
2023-06-27 23:21:23 +00:00
|
|
|
term_dot_method: term '.' { f_method_call_start($1); } method_name_cont { f_method_call_end(); $$ = $4; };
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
method_name_cont:
|
|
|
|
CF_SYM_METHOD_BARE {
|
2023-09-12 16:44:20 +00:00
|
|
|
$$ = f_dispatch_method($1, FM.object, NULL, 1);
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
}
|
|
|
|
| CF_SYM_METHOD_ARGS {
|
|
|
|
f_method_call_args();
|
|
|
|
} '(' var_list ')' {
|
2023-09-12 16:44:20 +00:00
|
|
|
$$ = f_dispatch_method($1, FM.object, $4, 1);
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
}
|
2023-10-29 18:53:09 +00:00
|
|
|
| static_attr {
|
|
|
|
if (FM.object->type != T_ROUTE)
|
|
|
|
cf_error("Getting a route attribute from %s, need a route", f_type_name(FM.object->type));
|
|
|
|
$$ = f_new_inst(FI_RTA_GET, FM.object, $1);
|
|
|
|
}
|
2023-10-31 08:58:42 +00:00
|
|
|
| CF_SYM_KNOWN {
|
|
|
|
if ($1->class != SYM_ATTRIBUTE)
|
|
|
|
cf_error("Not a method of %s: %s", f_type_name(FM.object->type), $1->name);
|
2023-10-29 18:53:09 +00:00
|
|
|
if (FM.object->type != T_ROUTE)
|
|
|
|
cf_error("Getting a route attribute from %s, need a route", f_type_name(FM.object->type));
|
2023-10-31 08:58:42 +00:00
|
|
|
$$ = f_new_inst(FI_EA_GET, FM.object, $1->attribute);
|
2023-10-29 18:53:09 +00:00
|
|
|
}
|
2023-06-12 09:20:49 +00:00
|
|
|
;
|
|
|
|
|
1999-03-02 19:49:28 +00:00
|
|
|
term:
|
2017-10-19 10:39:44 +00:00
|
|
|
'(' term ')' { $$ = $2; }
|
2019-01-21 08:17:54 +00:00
|
|
|
| term '+' term { $$ = f_new_inst(FI_ADD, $1, $3); }
|
|
|
|
| term '-' term { $$ = f_new_inst(FI_SUBTRACT, $1, $3); }
|
|
|
|
| term '*' term { $$ = f_new_inst(FI_MULTIPLY, $1, $3); }
|
|
|
|
| term '/' term { $$ = f_new_inst(FI_DIVIDE, $1, $3); }
|
2022-03-25 18:15:11 +00:00
|
|
|
| term '&' term { $$ = f_new_inst(FI_BITAND, $1, $3); }
|
|
|
|
| term '|' term { $$ = f_new_inst(FI_BITOR, $1, $3); }
|
2019-01-21 08:17:54 +00:00
|
|
|
| term AND term { $$ = f_new_inst(FI_AND, $1, $3); }
|
|
|
|
| term OR term { $$ = f_new_inst(FI_OR, $1, $3); }
|
|
|
|
| term '=' term { $$ = f_new_inst(FI_EQ, $1, $3); }
|
|
|
|
| term NEQ term { $$ = f_new_inst(FI_NEQ, $1, $3); }
|
|
|
|
| term '<' term { $$ = f_new_inst(FI_LT, $1, $3); }
|
|
|
|
| term LEQ term { $$ = f_new_inst(FI_LTE, $1, $3); }
|
|
|
|
| term '>' term { $$ = f_new_inst(FI_LT, $3, $1); }
|
|
|
|
| term GEQ term { $$ = f_new_inst(FI_LTE, $3, $1); }
|
|
|
|
| term '~' term { $$ = f_new_inst(FI_MATCH, $1, $3); }
|
|
|
|
| term NMA term { $$ = f_new_inst(FI_NOT_MATCH, $1, $3); }
|
|
|
|
| '!' term { $$ = f_new_inst(FI_NOT, $2); }
|
|
|
|
| DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
|
1999-04-07 12:11:08 +00:00
|
|
|
|
1999-11-10 13:59:13 +00:00
|
|
|
| constant { $$ = $1; }
|
2011-08-12 19:03:43 +00:00
|
|
|
| constructor { $$ = $1; }
|
1999-11-11 13:55:39 +00:00
|
|
|
|
2023-10-29 18:53:09 +00:00
|
|
|
| lvalue { $$ = f_lval_getter(&$1); }
|
1999-04-19 18:41:56 +00:00
|
|
|
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
| term_dot_method
|
2000-04-10 15:07:43 +00:00
|
|
|
|
2023-10-24 08:39:52 +00:00
|
|
|
| '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_PATH)); }
|
|
|
|
| '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_CLIST)); }
|
|
|
|
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_ECLIST)); }
|
|
|
|
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_LCLIST)); }
|
2023-09-13 04:21:26 +00:00
|
|
|
|
2024-02-14 12:58:56 +00:00
|
|
|
| PREPEND '(' term ',' term ')' { $$ = f_dispatch_method_x("prepend", $3->type, $3, $5); }
|
2023-09-12 16:44:20 +00:00
|
|
|
| ADD '(' term ',' term ')' { $$ = f_dispatch_method_x("add", $3->type, $3, $5); }
|
|
|
|
| DELETE '(' term ',' term ')' { $$ = f_dispatch_method_x("delete", $3->type, $3, $5); }
|
|
|
|
| FILTER '(' term ',' term ')' { $$ = f_dispatch_method_x("filter", $3->type, $3, $5); }
|
2000-04-17 10:16:47 +00:00
|
|
|
|
2022-03-19 15:38:32 +00:00
|
|
|
| ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
|
|
|
|
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); }
|
2024-11-06 11:12:37 +00:00
|
|
|
| ASPA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $7, $3); }
|
2012-03-18 16:32:30 +00:00
|
|
|
|
2019-01-21 08:17:54 +00:00
|
|
|
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
|
2016-11-09 15:36:34 +00:00
|
|
|
|
2023-08-24 02:30:42 +00:00
|
|
|
| term_bs
|
2018-12-27 13:26:11 +00:00
|
|
|
| function_call
|
1999-03-29 20:21:28 +00:00
|
|
|
;
|
|
|
|
|
2023-08-24 02:30:42 +00:00
|
|
|
term_bs:
|
|
|
|
FROM_HEX '(' term ')' { $$ = f_new_inst(FI_FROM_HEX, $3); }
|
|
|
|
;
|
|
|
|
|
1999-03-29 20:21:28 +00:00
|
|
|
break_command:
|
2020-04-29 13:07:33 +00:00
|
|
|
ACCEPT { $$ = F_ACCEPT; }
|
2004-05-31 17:44:39 +00:00
|
|
|
| REJECT { $$ = F_REJECT; }
|
|
|
|
| ERROR { $$ = F_ERROR; }
|
1999-03-29 20:21:28 +00:00
|
|
|
;
|
|
|
|
|
2022-03-10 00:02:45 +00:00
|
|
|
var:
|
2023-08-25 21:14:36 +00:00
|
|
|
type symbol '=' term ';' {
|
2023-06-13 08:51:03 +00:00
|
|
|
struct symbol *sym = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
|
2023-08-25 21:14:36 +00:00
|
|
|
$$ = f_new_inst(FI_VAR_INIT, $4, sym);
|
2022-03-10 00:02:45 +00:00
|
|
|
}
|
2023-08-25 21:14:36 +00:00
|
|
|
| type symbol ';' {
|
2023-06-13 08:51:03 +00:00
|
|
|
struct symbol *sym = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
|
2023-08-25 21:14:36 +00:00
|
|
|
$$ = f_new_inst(FI_VAR_INIT0, sym);
|
|
|
|
}
|
|
|
|
;
|
2022-03-10 00:02:45 +00:00
|
|
|
|
2022-03-14 19:36:20 +00:00
|
|
|
for_var:
|
2023-06-13 08:51:03 +00:00
|
|
|
type symbol { $$ = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
|
2023-10-04 17:57:55 +00:00
|
|
|
| CF_SYM_KNOWN { cf_error("Use of a pre-defined variable in for loop is not allowed"); }
|
2022-03-14 19:36:20 +00:00
|
|
|
;
|
|
|
|
|
1999-04-07 12:11:08 +00:00
|
|
|
cmd:
|
2024-11-25 14:14:27 +00:00
|
|
|
'{' { cf_push_soft_scope(new_config); } cmds { cf_pop_soft_scope(new_config); } '}' { $$ = $3; }
|
2022-03-08 23:31:39 +00:00
|
|
|
| IF term THEN cmd {
|
2019-01-21 08:17:54 +00:00
|
|
|
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
|
1999-04-07 12:11:08 +00:00
|
|
|
}
|
2022-03-08 23:31:39 +00:00
|
|
|
| IF term THEN cmd ELSE cmd {
|
2019-01-21 08:17:54 +00:00
|
|
|
$$ = f_new_inst(FI_CONDITION, $2, $4, $6);
|
1999-04-07 12:11:08 +00:00
|
|
|
}
|
2022-03-14 19:36:20 +00:00
|
|
|
| FOR {
|
|
|
|
/* Reserve space for walk data on stack */
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_push_block_scope(new_config);
|
|
|
|
new_config->current_scope->slots += 2;
|
2022-03-14 19:36:20 +00:00
|
|
|
} for_var IN
|
|
|
|
/* Parse term in the parent scope */
|
2023-10-05 12:26:22 +00:00
|
|
|
{ this_for_scope = new_config->current_scope; new_config->current_scope = this_for_scope->next; }
|
|
|
|
term
|
|
|
|
{ new_config->current_scope = this_for_scope; this_for_scope = NULL; }
|
2022-03-14 19:36:20 +00:00
|
|
|
DO cmd {
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_pop_block_scope(new_config);
|
2023-06-18 20:50:45 +00:00
|
|
|
$$ = f_for_cycle($3, $6, $9);
|
2022-03-14 19:36:20 +00:00
|
|
|
}
|
2023-10-29 18:53:09 +00:00
|
|
|
| lvalue '=' term ';' {
|
|
|
|
$$ = f_lval_setter(&$1, $3);
|
1999-01-15 16:49:17 +00:00
|
|
|
}
|
2000-02-25 11:15:26 +00:00
|
|
|
| RETURN term ';' {
|
2000-03-02 22:23:18 +00:00
|
|
|
DBG( "Ook, we'll return the value\n" );
|
2023-06-15 11:25:40 +00:00
|
|
|
if (!this_function)
|
|
|
|
cf_error("Can't return from a non-function, use accept or reject instead.");
|
|
|
|
if (this_function->function->return_type == T_VOID)
|
|
|
|
{
|
|
|
|
if ($2->type != T_VOID)
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
cf_warn("Inferring function %s return type from its return value: %s", this_function->name, f_type_name($2->type));
|
2023-06-15 11:25:40 +00:00
|
|
|
((struct f_line *) this_function->function)->return_type = $2->type;
|
|
|
|
}
|
|
|
|
else if (this_function->function->return_type != $2->type)
|
|
|
|
cf_error("Can't return type %s from function %s, expected %s",
|
|
|
|
f_type_name($2->type), this_function->name, f_type_name(this_function->function->return_type));
|
|
|
|
|
2019-06-19 12:09:57 +00:00
|
|
|
$$ = f_new_inst(FI_RETURN, $2);
|
2000-02-25 11:15:26 +00:00
|
|
|
}
|
2023-10-25 12:41:11 +00:00
|
|
|
| UNSET '(' CF_SYM_KNOWN ')' ';' {
|
2022-03-19 15:23:42 +00:00
|
|
|
if ($3->class != SYM_ATTRIBUTE)
|
|
|
|
cf_error("Can't unset %s", $3->name);
|
|
|
|
if ($3->attribute->readonly)
|
|
|
|
cf_error("Attribute %s is read-only", $3->attribute->name);
|
|
|
|
$$ = f_new_inst(FI_EA_UNSET, $3->attribute);
|
1999-11-18 14:01:36 +00:00
|
|
|
}
|
2023-07-12 18:01:03 +00:00
|
|
|
| break_command var_list_r ';' {
|
2023-06-19 15:24:30 +00:00
|
|
|
$$ = f_print($2, !!$2, $1);
|
2019-07-02 23:23:49 +00:00
|
|
|
}
|
2023-07-12 18:01:03 +00:00
|
|
|
| PRINT var_list_r ';' {
|
2023-06-19 15:24:30 +00:00
|
|
|
$$ = f_print($2, 1, F_NOP);
|
2019-07-15 13:43:47 +00:00
|
|
|
}
|
2023-07-12 18:01:03 +00:00
|
|
|
| PRINTN var_list_r ';' {
|
2023-06-19 15:24:30 +00:00
|
|
|
$$ = f_print($2, 0, F_NOP);
|
2019-07-15 13:43:47 +00:00
|
|
|
}
|
2022-03-10 00:02:45 +00:00
|
|
|
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
|
1999-09-29 14:24:58 +00:00
|
|
|
| CASE term '{' switch_body '}' {
|
2023-01-07 19:18:44 +00:00
|
|
|
$$ = f_new_inst(FI_SWITCH, $2, $4);
|
1999-09-29 14:24:58 +00:00
|
|
|
}
|
2023-06-12 09:37:50 +00:00
|
|
|
| lvalue '.' {
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
f_method_call_start(f_lval_getter(&$1));
|
|
|
|
} method_name_cont ';' {
|
2023-06-27 23:21:23 +00:00
|
|
|
f_method_call_end();
|
2023-06-12 09:37:50 +00:00
|
|
|
$$ = f_lval_setter(&$1, $4);
|
2023-06-12 09:20:49 +00:00
|
|
|
}
|
2016-11-16 10:09:55 +00:00
|
|
|
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
|
2019-01-30 13:03:47 +00:00
|
|
|
| BT_CHECK_ASSIGN '(' get_cf_position lvalue get_cf_position ',' term ')' ';' { $$ = assert_assign(&$4, $7, $3 + 1, $5 - 1); }
|
2016-11-09 15:36:34 +00:00
|
|
|
;
|
|
|
|
|
|
|
|
get_cf_position:
|
|
|
|
{
|
|
|
|
$$ = cf_text;
|
|
|
|
};
|
|
|
|
|
2019-01-30 13:03:47 +00:00
|
|
|
lvalue:
|
2023-10-25 12:41:11 +00:00
|
|
|
CF_SYM_KNOWN {
|
2023-06-12 09:37:50 +00:00
|
|
|
switch ($1->class)
|
|
|
|
{
|
2023-10-29 18:53:09 +00:00
|
|
|
case SYM_CONSTANT_RANGE:
|
|
|
|
$$ = (struct f_lval) { .type = F_LVAL_CONSTANT, .sym = $1, };
|
|
|
|
break;
|
2023-06-12 09:37:50 +00:00
|
|
|
case SYM_VARIABLE_RANGE:
|
2023-10-29 18:53:09 +00:00
|
|
|
$$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1, };
|
2023-06-12 09:37:50 +00:00
|
|
|
break;
|
|
|
|
case SYM_ATTRIBUTE:
|
2023-10-31 08:58:42 +00:00
|
|
|
$$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1->attribute, .rte = f_new_inst(FI_CURRENT_ROUTE), };
|
2023-06-12 09:37:50 +00:00
|
|
|
break;
|
|
|
|
default:
|
2023-10-27 13:56:06 +00:00
|
|
|
cf_error("Variable name or attribute name required");
|
2023-06-12 09:37:50 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-29 18:53:09 +00:00
|
|
|
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1, .rte = f_new_inst(FI_CURRENT_ROUTE), }; }
|
2022-03-19 15:23:42 +00:00
|
|
|
;
|
2016-11-09 15:36:34 +00:00
|
|
|
|
1999-01-15 16:49:17 +00:00
|
|
|
CF_END
|