%nonassoc PREFIX_DUMMY
%left AND OR
- %nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA PO PC
+ %nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA IMP PO PC
+%left '|' '&'
%left '+' '-'
%left '*' '/' '%'
%left '!'
%nonassoc ELSE
%type <xp> cmds_int cmd_prep
- %type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
+ %type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list var_list_r function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
-%type <fda> dynamic_attr
%type <fsa> static_attr
%type <f> filter where_filter
%type <fl> filter_body function_body
;
conf: function_def ;
- maybe_type:
- /* EMPTY */ { $$ = T_VOID; }
- | type { $$ = $1; }
- ;
function_def:
- FUNCTION maybe_type symbol {
- DBG( "Beginning of function %s\n", $3->name );
- this_function = cf_define_symbol(new_config, $3, SYM_FUNCTION, function, NULL);
- /* if ($2 == T_VOID) cf_warn("Support for functions without explicit return type will be removed soon" ); */
+ FUNCTION symbol {
+ DBG( "Beginning of function %s\n", $2->name );
+ this_function = cf_define_symbol(new_config, $2, SYM_FUNCTION, function, NULL);
+ cf_enter_filters();
cf_push_scope(new_config, this_function);
- } function_args {
+ } function_args function_type {
/* Make dummy f_line for storing function prototype */
struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
this_function->function = dummy;
$7->args = this_function->function->args;
$7->arg_list = this_function->function->arg_list;
$7->return_type = this_function->function->return_type;
- $3->function = $7;
+ $2->function = $7;
cf_pop_scope(new_config);
+ cf_exit_filters();
}
;
;
- /* This generates the function_call variable list backwards. */
- var_list: /* EMPTY */ { $$ = NULL; }
+ /* This generates the function_call variable list backwards */
+ var_list_r:
+ /* EMPTY */ { $$ = NULL; }
| term { $$ = $1; }
- | var_list ',' term { $$ = $3; $$->next = $1; }
+ | 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;
+ }
+ }
+ ;
function_call:
- symbol_known '(' var_list ')'
+ CF_SYM_KNOWN '(' var_list ')'
{
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
- | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
-
| term_dot_method
- | '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_PATH)); }
- | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_CLIST)); }
- | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_ECLIST)); }
- | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_LCLIST)); }
+ | '+' 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)); }
- | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
+
-| PREPEND '(' term ',' term ')' { $$ = f_dispatch_method_x("prepend", $3->type, $3, $5); }
- | 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); }
++ | PREPEND '(' term ',' term ')' {
++ $$ = f_dispatch_method_x("prepend", $3->type, $3, $5);
++ cf_warn("prepend(x,y) function is deprecated, please use x.prepend(y)");
++ }
+ | ADD '(' term ',' term ')' {
+ $$ = f_dispatch_method_x("add", $3->type, $3, $5);
+ cf_warn("add(x,y) function is deprecated, please use x.add(y)");
+ }
+ | DELETE '(' term ',' term ')' {
+ $$ = f_dispatch_method_x("delete", $3->type, $3, $5);
+ cf_warn("delete(x,y) function is deprecated, please use x.delete(y)");
+ }
+ | FILTER '(' term ',' term ')' {
+ $$ = f_dispatch_method_x("filter", $3->type, $3, $5);
+ cf_warn("filter(x,y) function is deprecated, please use x.filter(y)");
+ }
- | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
- | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
+ | ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
+ | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); }
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
cf_error( "This static attribute is read-only.");
$$ = f_new_inst(FI_RTA_SET, $3, $1);
}
- | UNSET '(' dynamic_attr ')' ';' {
- $$ = f_new_inst(FI_EA_UNSET, $3);
+ | UNSET '(' CF_SYM_KNOWN ')' ';' {
+ 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);
}
- | break_command var_list ';' {
+ | break_command var_list_r ';' {
$$ = f_print($2, !!$2, $1);
}
- | PRINT var_list ';' {
+ | PRINT var_list_r ';' {
$$ = f_print($2, 1, F_NOP);
}
- | PRINTN var_list ';' {
+ | PRINTN var_list_r ';' {
$$ = f_print($2, 0, F_NOP);
}
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
static const char * const f_type_str[] = {
[T_VOID] = "void",
+ [T_NONE] = "none",
+
+ [T_OPAQUE] = "opaque byte string",
+ [T_IFACE] = "interface",
+
[T_INT] = "int",
[T_BOOL] = "bool",
[T_PAIR] = "pair",
[T_LC] = "lc",
[T_LCLIST] = "lclist",
[T_RD] = "rd",
+
+ [T_SET] = "set",
+ [T_PREFIX_SET] = "prefix set",
};
++STATIC_ASSERT((1 << (8 * sizeof(btype))) == ARRAY_SIZE(f_type_str));
++
const char *
-f_type_name(enum f_type t)
+f_type_name(btype t)
{
- return (t < ARRAY_SIZE(f_type_str)) ? (f_type_str[t] ?: "?") : "?";
- if (t < ARRAY_SIZE(f_type_str))
- return f_type_str[t] ?: "?";
-
- if ((t == T_SET) || (t == T_PREFIX_SET))
- return "set";
-
- return "?";
++ return f_type_str[t] ?: "?";
}
-enum f_type
-f_type_element_type(enum f_type t)
+btype
+f_type_element_type(btype t)
{
switch(t) {
case T_PATH: return T_INT;
return what;
}
- static int
- f_const_promotion(struct f_inst *arg, btype want)
+ int
-f_const_promotion_(struct f_inst *arg, enum f_type want, int update)
++f_const_promotion_(struct f_inst *arg, btype want, int update)
{
if (arg->fi_code != FI_CONSTANT)
return 0;
static inline const char *f_instruction_name(enum f_instruction_code fi)
{ return f_instruction_name_(fi) + 3; }
-int f_const_promotion_(struct f_inst *arg, enum f_type want, int update);
+
-static inline int f_const_promotion(struct f_inst *arg, enum f_type want)
++int f_const_promotion_(struct f_inst *arg, enum btype want, int update);
+
-static inline int f_try_const_promotion(struct f_inst *arg, enum f_type want)
++static inline int f_const_promotion(struct f_inst *arg, enum btype want)
+ { return f_const_promotion_(arg, want, 1); }
+
++static inline int f_try_const_promotion(struct f_inst *arg, enum btype want)
+ { return f_const_promotion_(arg, want, 0); }
+
+
struct f_arg {
struct symbol *arg;
struct f_arg *next;
struct filter *f_new_where(struct f_inst *);
- struct f_inst *f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args);
+ struct f_inst *f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args, int skip);
-struct f_inst *f_dispatch_method_x(const char *name, enum f_type t, struct f_inst *obj, struct f_inst *args);
+struct f_inst *f_dispatch_method_x(const char *name, enum btype t, struct f_inst *obj, struct f_inst *args);
struct f_inst *f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block);
+struct f_inst *f_implicit_roa_check(struct rtable_config *tab);
struct f_inst *f_print(struct f_inst *vars, int flush, enum filter_return fret);
-static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
-{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
-static inline struct f_dynamic_attr f_new_dynamic_attr_bit(u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
-{ return (struct f_dynamic_attr) { .type = EAF_TYPE_BITFIELD, .bit = bit, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
-static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly)
-{ return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; }
+static inline struct f_static_attr f_new_static_attr(btype type, int code, int readonly)
+{ return (struct f_static_attr) { .type = type, .sa_code = code, .readonly = readonly }; }
+struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn);
/* Hook for call bt_assert() function in configuration */
extern void (*bt_assert_hook)(int result, const struct f_line_item *assert);
/* Static type check */
if (term->type == T_VOID)
- cf_error("Couldn't infer the type of FOR expression, please assign it to a variable.");
+ cf_error("Cannot infer type of FOR expression, please assign it to a variable");
- enum f_type el_type = f_type_element_type(term->type);
+ enum btype el_type = f_type_element_type(term->type);
struct sym_scope *scope = el_type ? f_type_method_scope(term->type) : NULL;
struct symbol *ms = scope ? cf_find_symbol_scope(scope, "!for_next") : NULL;
return ms->method->new_inst(term, loop_start);
}
- f_dispatch_method(ms, path_getter, NULL),
+struct f_inst *
+f_implicit_roa_check(struct rtable_config *tab)
+{
+ const struct ea_class *def = ea_class_find("bgp_path");
+ if (!def)
+ bug("Couldn't find BGP AS Path attribute definition.");
+
+ struct f_inst *path_getter = f_new_inst(FI_EA_GET, def);
+ struct sym_scope *scope = f_type_method_scope(path_getter->type);
+ struct symbol *ms = scope ? cf_find_symbol_scope(scope, "last") : NULL;
+
+ if (!ms)
+ bug("Couldn't find the \"last\" method for AS Path.");
+
+ struct f_static_attr fsa = f_new_static_attr(T_NET, SA_NET, 1);
+
+ return f_new_inst(FI_ROA_CHECK,
+ f_new_inst(FI_RTA_GET, fsa),
++ ms->method->new_inst(path_getter, NULL),
+ tab);
+}
+
struct f_inst *
f_print(struct f_inst *vars, int flush, enum filter_return fret)
{
--- /dev/null
+/*
+ * BIRD Internet Routing Daemon -- Internal Data Types
+ *
+ * (c) 2022 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_TYPE_H_
+#define _BIRD_TYPE_H_
+
+#include "lib/birdlib.h"
+#include "lib/attrs.h"
+
+union bval {
+#define BVAL_ITEMS \
+ struct { \
+ u32 data; /* Integer type inherited from eattrs */ \
+ PADDING(data, 0, 4); /* Must be padded on 64-bits */ \
+ }; \
+ struct { \
+ u32 i; /* Integer type inherited from filters */ \
+ PADDING(i, 0, 4); /* Must be padded on 64-bits */ \
+ }; \
+ const struct adata *ptr; /* Generic attribute data inherited from eattrs */ \
+ const struct adata *ad; /* Generic attribute data inherited from filters */ \
+
+ BVAL_ITEMS;
+};
+
+union bval_long {
+ union bval bval; /* For direct assignments */
+ BVAL_ITEMS; /* For item-wise access */
+
+ u64 ec;
+ lcomm lc;
+ ip_addr ip;
+ const net_addr *net;
+ const char *s;
+ const struct bytestring *bs;
+ const struct f_tree *t;
+ const struct f_trie *ti;
+ const struct f_path_mask *path_mask;
+ struct f_path_mask_item pmi;
+};
+
+
+/* Internal types */
+enum btype {
+/* Nothing. Simply nothing. */
+ T_VOID = 0,
++ T_NONE = 0xff,
+
+/* Something but inaccessible. */
+ T_OPAQUE = 0x02, /* Opaque byte string (not filterable) */
+ T_IFACE = 0x0c, /* Pointer to an interface (inside adata) */
+ T_NEXTHOP_LIST = 0x2c, /* The whole nexthop block */
+ T_HOSTENTRY = 0x2e, /* Hostentry with possible MPLS labels */
+
+/* Types shared with eattrs */
+ T_INT = 0x01, /* 32-bit unsigned integer number */
+ T_IP = 0x04, /* IP address */
+ T_QUAD = 0x05, /* Router ID (IPv4 address) */
+ T_PATH = 0x06, /* BGP AS path (encoding per RFC 1771:4.3) */
+ T_CLIST = 0x0a, /* Set of u32's (e.g., a community list) */
+ T_ECLIST = 0x0e, /* Set of pairs of u32's - ext. community list */
+ T_LCLIST = 0x08, /* Set of triplets of u32's - large community list */
+
+ T_ENUM_BGP_ORIGIN = 0x11, /* BGP Origin enum */
+ T_ENUM_RA_PREFERENCE = 0x13, /* RA Preference enum */
+ T_ENUM_FLOWSPEC_VALID = 0x15, /* Flowspec validation result */
+
+#define EAF_TYPE__MAX 0x1f
+#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */
+ /* Otherwise, attribute data is adata */
+
+/* Other user visible types which fit in int */
+ T_BOOL = 0xa0,
+ T_PAIR = 0xa4, /* Notice that pair is stored as integer: first << 16 | second */
+
+/* Put enumerational types in 0x20..0x3f range */
+ T_ENUM_LO = 0x10,
+ T_ENUM_HI = 0x3f,
+
+ T_ENUM_RTS = 0x31,
+ T_ENUM_SCOPE = 0x33,
+ T_ENUM_RTD = 0x37,
+ T_ENUM_ROA = 0x39,
+ T_ENUM_NETTYPE = 0x3b,
+ T_ENUM_AF = 0x3d,
+
+/* new enums go here */
+
+#define T_ENUM T_ENUM_LO ... T_ENUM_HI
+
+/* Bigger ones */
+ T_NET = 0xb0,
+ T_STRING = 0xb4,
+ T_PATH_MASK = 0xb8, /* mask for BGP path */
+ T_EC = 0xbc, /* Extended community value, u64 */
+ T_LC = 0xc0, /* Large community value, lcomm */
+ T_RD = 0xc4, /* Route distinguisher for VPN addresses */
+ T_PATH_MASK_ITEM = 0xc8, /* Path mask item for path mask constructors */
+ T_BYTESTRING = 0xcc,
+
+ T_SET = 0x80,
+ T_PREFIX_SET = 0x84,
+} PACKED;
+
+typedef enum btype btype;
+
+STATIC_ASSERT(sizeof(btype) == sizeof(byte));
+
+
+#endif