}
}
-cf_type_name(enum f_type type)
+ static inline const char *
-cf_assert_type(const struct f_val val, enum f_type type)
++cf_type_name(btype type)
+ {
+ /* There is already f_type_name(), but this uses names more suited
+ to configuration error messages */
+ switch (type)
+ {
+ case T_INT: return "Number";
+ case T_BOOL: return "Boolean";
+ case T_IP: return "IP address";
+ case T_NET: return "Network";
+ case T_STRING: return "String";
+ case T_BYTESTRING: return "Bytestring";
+ default: return "???";
+ }
+ }
+
+ static inline void
++cf_assert_type(const struct f_val val, btype type)
+ {
+ if (val.type != type)
+ cf_error("%s expected", cf_type_name(type));
+ }
+
+
CF_DECLS
%union {
%type <iface> ipa_scope
%type <i> expr bool pxlen4
+ %type <i32> idval
%type <time> expr_us time
-%type <a> ipa
+%type <settle> settle
+%type <a> ipa net_ip6_slash
%type <net> net_ip4_ net_ip4 net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_ net_aspa_
-%type <mls> label_stack_start label_stack
+%type <ad> label_stack_start label_stack
%type <t> text opttext
%type <bs> bytestring
}
;
- | '(' term ')' { $$ = cf_eval($2, T_VOID); }
+ conf_expr:
+ symbol_known {
+ /* If the symbol is not a constant, we pass empty f_val and fail later on type check */
+ $$ = (($1->class & ~0xff) == SYM_CONSTANT) ? *$1->val : (struct f_val) {};
+ }
-symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN | KEYWORD ;
++ | '(' term ')' { $$ = *cf_eval($2, T_VOID); }
+ ;
+
++symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN ;
+ symbol_known: CF_SYM_KNOWN ;
+
+
+ /* Numbers */
+
expr:
NUM
- | '(' term ')' { $$ = cf_eval_int($2); }
- | CF_SYM_KNOWN {
- if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
- $$ = SYM_VAL($1).i; }
+ | conf_expr { cf_assert_type($1, T_INT); $$ = $1.val.i; }
;
expr_us:
}
;
- net_or_ipa:
- net_ip4_
- | net_ip6_
- | IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); }
- | IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); }
- | CF_SYM_KNOWN {
- if ($1->class == (SYM_CONSTANT | T_IP))
- net_fill_ip_host(&($$), SYM_VAL($1).ip);
- else if (($1->class == (SYM_CONSTANT | T_NET)) && net_is_ip(SYM_VAL($1).net))
- $$ = * SYM_VAL($1).net;
- else
- cf_error("IP address or network constant expected");
- }
- ;
-
- label_stack_start: NUM
+ label_stack_start: expr
{
- $$ = cfg_allocz(sizeof(mpls_label_stack));
- $$->len = 1;
- $$->stack[0] = $1;
+ $$ = cfg_allocz(ADATA_SIZE(MPLS_MAX_LABEL_STACK * sizeof(u32)));
+ $$->length = sizeof(u32);
+ *((u32 *)$$->data) = $1;
};
label_stack:
label_stack_start
- | label_stack '/' NUM {
+ | label_stack '/' expr {
- if ($1->len >= MPLS_MAX_LABEL_STACK)
+ if ($1->length >= MPLS_MAX_LABEL_STACK * sizeof(u32))
cf_error("Too many labels in stack");
- $1->stack[$1->len++] = $3;
+
+ *((u32 *)($$->data + $1->length)) = $3;
+ $1->length += sizeof(u32);
$$ = $1;
}
;
- time:
- TEXT {
- $$ = tm_parse_time($1);
- if (!$$)
- cf_error("Invalid date/time");
- }
- ;
+
+ /* Strings */
+/* Settle timer configuration */
+settle: expr_us expr_us {
+ if ($1 > $2) cf_error("Minimum settle time %t is bigger than maximum settle time %t", $1, $2);
+ $$.min = $1;
+ $$.max = $2;
+};
+
text:
TEXT
- | CF_SYM_KNOWN {
- if ($1->class != (SYM_CONSTANT | T_STRING)) cf_error("String constant expected");
- $$ = SYM_VAL($1).s;
- }
+ | conf_expr { cf_assert_type($1, T_STRING); $$ = $1.val.s; }
;
opttext:
}
;
+
+ /* Bytestrings */
+
bytestring:
BYTETEXT
- | bytestring_expr { $$ = cf_eval($1, T_BYTESTRING)->val.bs; }
+ | bytestring_expr { cf_assert_type($1, T_BYTESTRING); $$ = $1.val.bs; }
;
- bytestring_text:
+ bytestring_expr:
+ conf_expr
- | term_bs { $$ = cf_eval($1, T_VOID); }
++ | term_bs { $$ = *cf_eval($1, T_VOID); }
+ ;
+
+
+ /* Mixed ones */
+
+ /* number | IPv4 -> number */
+ idval:
+ NUM { $$ = $1; }
+ | IP4 { $$ = ip4_to_u32($1); }
+ | conf_expr {
+ if (($1.type == T_INT) || ($1.type == T_QUAD))
+ $$ = $1.val.i;
+ else if (($1.type == T_IP) && ipa_is_ip4($1.val.ip))
+ $$ = ipa_to_u32($1.val.ip);
+ else
+ cf_error("Number or IPv4 address expected");
+ }
+ ;
+
+ /* net | ipa -> net */
+ net_or_ipa:
+ net_ip4_
+ | net_ip6_
+ | IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); }
+ | IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); }
+ | conf_expr {
+ if ($1.type == T_IP)
+ net_fill_ip_host(&($$), $1.val.ip);
+ else if (($1.type == T_NET) && net_is_ip($1.val.net))
+ $$ = * $1.val.net;
+ else
+ cf_error("IP address/prefix expected");
+ }
+ ;
+
+ /* text | ipa -> f_val */
+ text_or_ipa:
+ TEXT { $$.type = T_STRING; $$.val.s = $1; }
+ | IP4 { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); }
+ | IP6 { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); }
+ | conf_expr {
+ if (($1.type != T_STRING) && ($1.type != T_IP))
+ cf_error("String or IP address expected");
+ $$ = $1;
+ }
+ ;
+
+ /* bytestring | text -> f_val */
+ bytestring_or_text:
BYTETEXT { $$.type = T_BYTESTRING; $$.val.bs = $1; }
| TEXT { $$.type = T_STRING; $$.val.s = $1; }
| bytestring_expr {
};
log_udp_port:
- /* empty */ { this_log->port = 514; }
- | PORT expr { check_u16($2); this_log->port = $2; }
+ /* empty */ { this_log->udp_port = 514; }
- | PORT NUM { check_u16($2); this_log->udp_port = $2; }
++ | PORT expr { check_u16($2); this_log->udp_port = $2; }
;
log_mask:
};
cli_opts_block:
- /* EMPTY */ |
- cli_opts_block RESTRICT { this_cli_config->restricted = 1; }
+ /* EMPTY */
+ | cli_opts_block RESTRICT ';' { this_cli_config->restricted = 1; }
+ | cli_opts_block V2 ATTRIBUTES ';' { this_cli_config->v2attributes = 1; }
;
- conf: THREADS NUM {
++conf: THREADS expr {
+ if ($2 < 1) cf_error("Number of threads must be at least one.");
+ if (!new_config->thread_group_simple &&
+ !EMPTY_TLIST(thread_group_config, &new_config->thread_group))
+ cf_error("Mixing of `threads NUM` and `threads worker {}` is not allowed.");
+
+ if (new_config->thread_group_simple == -1)
+ THEAD(thread_group_config, &new_config->thread_group)->thread_count = $2;
+
+ new_config->thread_group_simple = $2;
+};
+
+conf: THREAD GROUP symbol {
+ switch (new_config->thread_group_simple) {
+ case -1: cf_error("Put `threads` block before any protocol or table definition.");
+ case 0: break;
+ default: cf_error("Mixing of `threads NUM` and `threads worker {}` is not allowed.");
+ }
+
+ this_thread_group = cfg_alloc(sizeof *this_thread_group);
+ *this_thread_group = strcmp($3->name, "express") ?
+ thread_group_config_default_worker :
+ thread_group_config_default_express;
+} '{' thread_group_opts '}' {
+ if (this_thread_group->params.max_time > 60 S_)
+ cf_error("Loop time maximum is 60 seconds, got %t", this_thread_group->params.max_time);
+ if (this_thread_group->params.max_latency < this_thread_group->params.max_time)
+ cf_error("Loop time maximum can't be higher than its latency limit");
+ if (this_thread_group->params.min_time > this_thread_group->params.max_time)
+ cf_error("Loop time minimm can't be higher than maximum");
+ if (this_thread_group->params.wakeup_time < 1 S_)
+ cf_error("Too low idle wakeup time, expected at least 1 second");
+
+ this_thread_group->symbol = cf_define_symbol(new_config, $3, SYM_THREAD_GROUP, thread_group, this_thread_group);
+ thread_group_config_add_tail(&new_config->thread_group, this_thread_group);
+ this_thread_group = NULL;
+};
+
+thread_group_opts: MAX TIME expr_us ';' { this_thread_group->params.max_time = $3; } ;
+thread_group_opts: MIN TIME expr_us ';' { this_thread_group->params.min_time = $3; } ;
+thread_group_opts: MAX LATENCY expr_us ';' { this_thread_group->params.max_latency = $3; } ;
+thread_group_opts: THREADS expr ';' { this_thread_group->thread_count = $2; } ;
+thread_group_opts: WAKEUP TIME expr_us ';' { this_thread_group->params.wakeup_time = $3; } ;
+thread_group_opts: DEFAULT bool ';' {
+ if ($2) {
+ if (new_config->default_thread_group &&
+ new_config->default_thread_group != this_thread_group)
+ cf_error("Too many default thread groups, already have %s",
+ new_config->default_thread_group->symbol->name);
+ new_config->default_thread_group = this_thread_group;
+ }
+};
+
+
conf: debug_unix ;
debug_unix: