]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - nest/config.Y
Filter: Some people can't pronounce "postfixify" correctly. Let's try "linearize...
[thirdparty/bird.git] / nest / config.Y
index 9d20dd48f954aa67b75103234696a7f83f520191..e4dedc66fa1c78e94622bddcd26c5df05c9547c6 100644 (file)
@@ -13,10 +13,12 @@ CF_HDR
 #include "nest/password.h"
 #include "nest/cmds.h"
 #include "lib/lists.h"
+#include "lib/mac.h"
 
 CF_DEFINES
 
 static struct proto_config *this_proto;
+static struct channel_config *this_channel;
 static struct iface_patt *this_ipatt;
 static struct iface_patt_node *this_ipn;
 /* static struct roa_table_config *this_roa_table; */
@@ -30,7 +32,7 @@ iface_patt_check(void)
   struct iface_patt_node *pn;
 
   WALK_LIST(pn, this_ipatt->ipn_list)
-    if (!pn->pattern || pn->prefix.pxlen) /* XXXX */
+    if (!pn->pattern || pn->prefix.type)
       cf_error("Interface name/mask expected, not IP prefix");
 }
 
@@ -49,111 +51,122 @@ get_passwords(void)
   return rv;
 }
 
+static void
+proto_postconfig(void)
+{
+  CALL(this_proto->protocol->postconfig, this_proto);
+  this_channel = NULL;
+  this_proto = NULL;
+}
+
+
 #define DIRECT_CFG ((struct rt_dev_config *) this_proto)
 
 CF_DECLS
 
 CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
-CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
-CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6)
+CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, TABLE, STATES, ROUTES, FILTERS)
+CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
 CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
 CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
-CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE) /* ,ROA */
-CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
+CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
+CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE)
+CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION, SORTED)
 CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
+CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
 CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
 
+/* For r_args_channel */
+CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
+
 CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
-       RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
+       RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
 CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
-CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST)
-CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULTIPATH)
+CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
+CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
 
 %type <i32> idval
 %type <f> imexport
 %type <r> rtable
-%type <s> optsym
+%type <s> optproto
 %type <ra> r_args
 %type <sd> sym_args
-%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action table_type table_sorted tos
+%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type table_sorted tos password_algorithm
 %type <ps> proto_patt proto_patt2
-%type <g> limit_spec
+%type <cc> channel_start proto_channel
+%type <cl> limit_spec
+%type <net> r_args_for_val
+%type <net_ptr> r_args_for
+%type <t> r_args_channel
 
 CF_GRAMMAR
 
 /* Setting of router ID */
 
-CF_ADDTO(conf, rtrid)
+conf: rtrid ;
 
-rtrid: 
+rtrid:
    ROUTER ID idval ';' { new_config->router_id = $3; }
  | ROUTER ID FROM iface_patt ';' { new_config->router_id_from = this_ipatt; }
  ;
 
 idval:
    NUM { $$ = $1; }
- | '(' term ')' { $$ = f_eval_int($2); }
+ | '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
  | IP4 { $$ = ip4_to_u32($1); }
- | SYM {
+ | CF_SYM_KNOWN {
      if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD))
        $$ = SYM_VAL($1).i;
      else if (($1->class == (SYM_CONSTANT | T_IP)) && ipa_is_ip4(SYM_VAL($1).ip))
        $$ = ipa_to_u32(SYM_VAL($1).ip);
      else
-       cf_error("Number of IPv4 address constant expected");
+       cf_error("Number or IPv4 address constant expected");
    }
  ;
 
+conf: gr_opts ;
 
-CF_ADDTO(conf, listen)
+gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
 
-listen: LISTEN BGP listen_opts ';' ;
 
-listen_opts:
-   /* Nothing */
- | listen_opts listen_opt
- ;
+/* Network types (for tables, channels) */
 
-listen_opt: 
-   ADDRESS ipa { new_config->listen_bgp_addr = $2; }
- | PORT expr { new_config->listen_bgp_port = $2; }
- | V6ONLY { new_config->listen_bgp_flags = 0; }
- | DUAL { new_config->listen_bgp_flags = 1; }
+net_type:
+   IPV4 { $$ = NET_IP4; }
+ | IPV6 { $$ = NET_IP6; }
+ | IPV6 SADR { $$ = NET_IP6_SADR; }
+ | VPN4 { $$ = NET_VPN4; }
+ | VPN6 { $$ = NET_VPN6; }
+ | ROA4 { $$ = NET_ROA4; }
+ | ROA6 { $$ = NET_ROA6; }
+ | FLOW4{ $$ = NET_FLOW4; }
+ | FLOW6{ $$ = NET_FLOW6; }
+ | MPLS { $$ = NET_MPLS; }
  ;
 
-
-CF_ADDTO(conf, gr_opts)
-
-gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
+CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR)
 
 
 /* Creation of routing tables */
 
-CF_ADDTO(conf, table)
-
-table_type:
-   /* empty */ { $$ = NET_IP4; }
- | IPV4 { $$ = NET_IP4; }
- | IPV6 { $$ = NET_IP6; }
- | VPN4 { $$ = NET_VPN4; }
- | VPN6 { $$ = NET_VPN6; }
- ;
+conf: table ;
 
 table_sorted:
-          { $$ = 0; }
+         { $$ = 0; }
  | SORTED { $$ = 1; }
  ;
 
-table: table_type TABLE SYM table_sorted {
+table: net_type TABLE CF_SYM_VOID table_sorted {
    struct rtable_config *cf;
    cf = rt_new_table($3, $1);
    cf->sorted = $4;
    }
  ;
 
+
 /* Definition of protocols */
 
-CF_ADDTO(conf, proto)
+conf: proto { proto_postconfig(); } ;
 
 proto_start:
    PROTOCOL { $$ = SYM_PROTO; }
@@ -164,51 +177,87 @@ proto_name:
    /* EMPTY */ {
      struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
      s->class = this_proto->class;
-     s->def = this_proto;
+     s->proto = this_proto;
      this_proto->name = s->name;
      }
- | SYM {
-     cf_define_symbol($1, this_proto->class, this_proto);
+ | CF_SYM_VOID {
+     cf_define_symbol($1, this_proto->class, proto, this_proto);
      this_proto->name = $1->name;
    }
- | FROM SYM {
+ | FROM CF_SYM_KNOWN {
+     if (($2->class != SYM_TEMPLATE) && ($2->class != SYM_PROTO)) cf_error("Template or protocol name expected");
+
      struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
      s->class = this_proto->class;
-     s->def = this_proto;
+     s->proto = this_proto;
      this_proto->name = s->name;
 
-     if (($2->class != SYM_TEMPLATE) && ($2->class != SYM_PROTO)) cf_error("Template or protocol name expected");
-     proto_copy_config(this_proto, $2->def);
+     proto_copy_config(this_proto, $2->proto);
    }
- | SYM FROM SYM {
-     cf_define_symbol($1, this_proto->class, this_proto);
+ | CF_SYM_VOID FROM CF_SYM_KNOWN {
+     if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
+
+     cf_define_symbol($1, this_proto->class, proto, this_proto);
      this_proto->name = $1->name;
 
-     if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
-     proto_copy_config(this_proto, $3->def);
+     proto_copy_config(this_proto, $3->proto);
    }
  ;
 
 proto_item:
    /* EMPTY */
- | PREFERENCE expr {
-     if ($2 < 0 || $2 > 0xFFFF) cf_error("Invalid preference");
-     this_proto->preference = $2;
-   }
  | DISABLED bool { this_proto->disabled = $2; }
  | DEBUG debug_mask { this_proto->debug = $2; }
  | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
- | IMPORT imexport { this_proto->in_filter = $2; }
- | EXPORT imexport { this_proto->out_filter = $2; }
- | RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; }
- | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
- | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
- | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; }
- | TABLE rtable { this_proto->table = $2; }
  | ROUTER ID idval { this_proto->router_id = $3; }
  | DESCRIPTION text { this_proto->dsc = $2; }
+ | VRF text { this_proto->vrf = if_get_by_name($2); }
  ;
 
+
+channel_start: net_type
+{
+  $$ = this_channel = channel_config_get(NULL, net_label[$1], $1, this_proto);
+};
+
+channel_item:
+   TABLE rtable {
+     if (this_channel->net_type && ($2->addr_type != this_channel->net_type))
+       cf_error("Incompatible table type");
+     this_channel->table = $2;
+   }
+ | IMPORT imexport { this_channel->in_filter = $2; }
+ | EXPORT imexport { this_channel->out_filter = $2; }
+ | RECEIVE LIMIT limit_spec { this_channel->rx_limit = $3; }
+ | IMPORT LIMIT limit_spec { this_channel->in_limit = $3; }
+ | EXPORT LIMIT limit_spec { this_channel->out_limit = $3; }
+ | PREFERENCE expr { this_channel->preference = $2; check_u16($2); }
+ | IMPORT KEEP FILTERED bool { this_channel->in_keep_filtered = $4; }
+ ;
+
+channel_opts:
+   /* empty */
+ | channel_opts channel_item ';'
+ ;
+
+channel_opt_list:
+   /* empty */
+ | '{' channel_opts '}'
+ ;
+
+channel_end:
+{
+  if (!this_channel->table)
+    cf_error("Routing table not specified");
+
+  this_channel = NULL;
+};
+
+proto_channel: channel_start channel_opt_list channel_end;
+
+
+rtable: CF_SYM_KNOWN { cf_assert_symbol($1, SYM_TABLE); $$ = $1->table; } ;
+
 imexport:
    FILTER filter { $$ = $2; }
  | where_filter
@@ -225,23 +274,12 @@ limit_action:
  ;
 
 limit_spec:
-   expr limit_action {
-     struct proto_limit *l = cfg_allocz(sizeof(struct proto_limit));
-     l->limit = $1;
-     l->action = $2;
-     $$ = l;
-   }
- | OFF { $$ = NULL; }
+   expr limit_action { $$ = (struct channel_limit){ .limit = $1, $$.action = $2 }; }
+ | OFF { $$ = (struct channel_limit){}; }
  ;
 
-rtable:
-   SYM {
-     if ($1->class != SYM_TABLE) cf_error("Table name expected");
-     $$ = $1->def;
-   }
- ;
 
-CF_ADDTO(conf, debug_default)
+conf: debug_default ;
 
 debug_default:
    DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
@@ -250,6 +288,31 @@ debug_default:
 
 /* MRTDUMP PROTOCOLS is in systep/unix/config.Y */
 
+conf: timeformat_base ;
+
+timeformat_which:
+   ROUTE { $$ = &new_config->tf_route; }
+ | PROTOCOL { $$ = &new_config->tf_proto; }
+ | BASE { $$ = &new_config->tf_base; }
+ | LOG { $$ = &new_config->tf_log; }
+ ;
+
+timeformat_spec:
+   timeformat_which TEXT { *$1 = (struct timeformat){$2, NULL, 0}; }
+ | timeformat_which TEXT expr TEXT { *$1 = (struct timeformat){$2, $4, (s64) $3 S_}; }
+ | timeformat_which ISO SHORT    { *$1 = TM_ISO_SHORT_S; }
+ | timeformat_which ISO SHORT MS { *$1 = TM_ISO_SHORT_MS; }
+ | timeformat_which ISO SHORT US { *$1 = TM_ISO_SHORT_US; }
+ | timeformat_which ISO LONG    { *$1 = TM_ISO_LONG_S; }
+ | timeformat_which ISO LONG MS { *$1 = TM_ISO_LONG_MS; }
+ | timeformat_which ISO LONG US { *$1 = TM_ISO_LONG_US; }
+ ;
+
+timeformat_base:
+   TIMEFORMAT timeformat_spec ';'
+ ;
+
+
 /* Interface patterns */
 
 iface_patt_node_init:
@@ -261,8 +324,8 @@ iface_patt_node_init:
  ;
 
 iface_patt_node_body:
-   TEXT { this_ipn->pattern = $1; net_fill_ip6(&this_ipn->prefix, IP6_NONE, 0); /* XXXX */ }
- | opttext net_or_ipa { this_ipn->pattern = $1; this_ipn->prefix = $2.n; }
+   TEXT { this_ipn->pattern = $1; /* this_ipn->prefix stays zero */ }
+ | opttext net_or_ipa { this_ipn->pattern = $1; this_ipn->prefix = $2; }
  ;
 
 iface_negate:
@@ -271,7 +334,7 @@ iface_negate:
  ;
 
 iface_patt_node:
-   iface_patt_node_init iface_negate iface_patt_node_body 
+   iface_patt_node_init iface_negate iface_patt_node_body
  ;
 
 
@@ -295,13 +358,13 @@ iface_patt:
  ;
 
 tos:
-   CLASS expr { $$ = $2 & 0xfc;        if (($2 < 0) || ($2 > 255)) cf_error("TX class must be in range 0-255"); }
- | DSCP expr  { $$ = ($2 & 0x3f) << 2; if (($2 < 0) || ($2 > 63))  cf_error("TX DSCP must be in range 0-63"); }
+   CLASS expr { $$ = $2 & 0xfc;        if ($2 > 255) cf_error("TX class must be in range 0-255"); }
+ | DSCP expr  { $$ = ($2 & 0x3f) << 2; if ($2 > 63)  cf_error("TX DSCP must be in range 0-63"); }
  ;
 
 /* Direct device route protocol */
 
-CF_ADDTO(proto, dev_proto '}')
+proto: dev_proto '}' ;
 
 dev_proto_start: proto_start DIRECT {
      this_proto = proto_config_new(&proto_device, $1);
@@ -312,7 +375,9 @@ dev_proto_start: proto_start DIRECT {
 dev_proto:
    dev_proto_start proto_name '{'
  | dev_proto proto_item ';'
+ | dev_proto proto_channel ';'
  | dev_proto dev_iface_patt ';'
+ | dev_proto CHECK LINK bool ';' { DIRECT_CFG->check_link = $4; }
  ;
 
 dev_iface_init:
@@ -374,7 +439,7 @@ password_list:
  | password_item
 ;
 
-password_items: 
+password_items:
     /* empty */
   | password_item ';' password_items
 ;
@@ -387,31 +452,47 @@ password_item:
 password_item_begin:
    PASSWORD text {
      if (!this_p_list) {
-       this_p_list = cfg_alloc(sizeof(list));
-       init_list(this_p_list);
-        password_id = 1;
+       this_p_list = cfg_alloc(sizeof(list));
+       init_list(this_p_list);
+       password_id = 1;
      }
      this_p_item = cfg_alloc(sizeof (struct password_item));
      this_p_item->password = $2;
+     this_p_item->length = strlen($2);
      this_p_item->genfrom = 0;
      this_p_item->gento = TIME_INFINITY;
      this_p_item->accfrom = 0;
      this_p_item->accto = TIME_INFINITY;
      this_p_item->id = password_id++;
+     this_p_item->alg = ALG_UNDEFINED;
      add_tail(this_p_list, &this_p_item->n);
    }
 ;
 
 password_item_params:
-   /* empty */ { } 
- | GENERATE FROM datetime ';' password_item_params { this_p_item->genfrom = $3; }
- | GENERATE TO datetime ';' password_item_params { this_p_item->gento = $3; }
- | ACCEPT FROM datetime ';' password_item_params { this_p_item->accfrom = $3; }
- | ACCEPT TO datetime ';' password_item_params { this_p_item->accto = $3; }
+   /* empty */ { }
+ | GENERATE FROM time ';' password_item_params { this_p_item->genfrom = $3; }
+ | GENERATE TO time ';' password_item_params { this_p_item->gento = $3; }
+ | ACCEPT FROM time ';' password_item_params { this_p_item->accfrom = $3; }
+ | ACCEPT TO time ';' password_item_params { this_p_item->accto = $3; }
+ | FROM time ';' password_item_params { this_p_item->genfrom = this_p_item->accfrom = $2; }
+ | TO time ';' password_item_params { this_p_item->gento = this_p_item->accto = $2; }
  | ID expr ';' password_item_params { this_p_item->id = $2; if ($2 <= 0) cf_error("Password ID has to be greated than zero."); }
+ | ALGORITHM password_algorithm ';' password_item_params { this_p_item->alg = $2; }
  ;
 
-
+password_algorithm:
+   KEYED MD5   { $$ = ALG_MD5; }
+ | KEYED SHA1  { $$ = ALG_SHA1; }
+ | KEYED SHA256        { $$ = ALG_SHA256; }
+ | KEYED SHA384        { $$ = ALG_SHA384; }
+ | KEYED SHA512        { $$ = ALG_SHA512; }
+ | HMAC MD5    { $$ = ALG_HMAC_MD5; }
+ | HMAC SHA1   { $$ = ALG_HMAC_SHA1; }
+ | HMAC SHA256 { $$ = ALG_HMAC_SHA256; }
+ | HMAC SHA384 { $$ = ALG_HMAC_SHA384; }
+ | HMAC SHA512 { $$ = ALG_HMAC_SHA512; }
+ ;
 
 /* Core commands */
 CF_CLI_HELP(SHOW, ..., [[Show status information]])
@@ -428,8 +509,8 @@ CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing
 CF_CLI(SHOW PROTOCOLS ALL, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocol details]])
 { proto_apply_cmd($4, proto_cmd_show, 0, 1); } ;
 
-optsym:
-   SYM
+optproto:
+   CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$ = $1; }
  | /* empty */ { $$ = NULL; }
  ;
 
@@ -446,23 +527,44 @@ CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filte
 r_args:
    /* empty */ {
      $$ = cfg_allocz(sizeof(struct rt_show_data));
+     init_list(&($$->tables));
      $$->filter = FILTER_ACCEPT;
+     $$->running_on_config = new_config->fallback;
    }
  | r_args net_any {
      $$ = $1;
-     if ($$->prefix) cf_error("Only one prefix expected");
-     $$->prefix = $2;
+     if ($$->addr) cf_error("Only one prefix expected");
+     $$->addr = $2;
    }
- | r_args FOR net_or_ipa {
+ | r_args FOR r_args_for {
      $$ = $1;
-     if ($$->prefix) cf_error("Only one prefix expected");
-     $$->prefix = &($3.n);
+     if ($$->addr) cf_error("Only one prefix expected");
      $$->show_for = 1;
+     $$->addr = $3;
+   }
+ | r_args TABLE CF_SYM_KNOWN {
+     cf_assert_symbol($3, SYM_TABLE);
+     $$ = $1;
+     rt_show_add_table($$, $3->table->table);
+     $$->tables_defined_by = RSD_TDB_DIRECT;
    }
- | r_args TABLE SYM {
+ | r_args TABLE ALL {
+     struct rtable_config *t;
      $$ = $1;
-     if ($3->class != SYM_TABLE) cf_error("%s is not a table", $3->name);
-     $$->table = ((struct rtable_config *)$3->def)->table;
+     WALK_LIST(t, config->tables)
+       rt_show_add_table($$, t->table);
+     $$->tables_defined_by = RSD_TDB_ALL;
+   }
+ | r_args IMPORT TABLE CF_SYM_KNOWN '.' r_args_channel {
+     cf_assert_symbol($4, SYM_PROTO);
+     $$ = $1;
+     struct proto_config *cf = $4->proto;
+     if (!cf->proto) cf_error("%s is not a protocol", $4->name);
+     struct channel *c = proto_find_channel_by_name(cf->proto, $6);
+     if (!c) cf_error("Channel %s.%s not found", $4->name, $6);
+     if (!c->in_table) cf_error("No import table in channel %s.%s", $4->name, $6);
+     rt_show_add_table($$, c->in_table);
+     $$->tables_defined_by = RSD_TDB_DIRECT;
    }
  | r_args FILTER filter {
      $$ = $1;
@@ -486,22 +588,35 @@ r_args:
      $$ = $1;
      $$->filtered = 1;
    }
- | r_args export_mode SYM {
-     struct proto_config *c = (struct proto_config *) $3->def;
+ | r_args export_mode CF_SYM_KNOWN {
+     cf_assert_symbol($3, SYM_PROTO);
+     struct proto_config *c = (struct proto_config *) $3->proto;
      $$ = $1;
-     if ($$->export_mode) cf_error("Protocol specified twice");
-     if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
+     if ($$->export_mode) cf_error("Export specified twice");
+     if (!c->proto) cf_error("%s is not a protocol", $3->name);
      $$->export_mode = $2;
      $$->export_protocol = c->proto;
-     $$->running_on_config = c->proto->cf->global;
+     $$->tables_defined_by = RSD_TDB_INDIRECT;
    }
- | r_args PROTOCOL SYM {
-     struct proto_config *c = (struct proto_config *) $3->def;
+ | r_args export_mode CF_SYM_KNOWN '.' r_args_channel {
+     cf_assert_symbol($3, SYM_PROTO);
+     struct proto_config *c = (struct proto_config *) $3->proto;
+     $$ = $1;
+     if ($$->export_mode) cf_error("Export specified twice");
+     if (!c->proto) cf_error("%s is not a protocol", $3->name);
+     $$->export_mode = $2;
+     $$->export_channel = proto_find_channel_by_name(c->proto, $5);
+     if (!$$->export_channel) cf_error("Export channel not found");
+     $$->tables_defined_by = RSD_TDB_INDIRECT;
+   }
+ | r_args PROTOCOL CF_SYM_KNOWN {
+     cf_assert_symbol($3, SYM_PROTO);
+     struct proto_config *c = (struct proto_config *) $3->proto;
      $$ = $1;
      if ($$->show_protocol) cf_error("Protocol specified twice");
-     if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
+     if (!c->proto) cf_error("%s is not a protocol", $3->name);
      $$->show_protocol = c->proto;
-     $$->running_on_config = c->proto->cf->global;
+     $$->tables_defined_by = RSD_TDB_INDIRECT;
    }
  | r_args STATS {
      $$ = $1;
@@ -513,12 +628,74 @@ r_args:
    }
  ;
 
+r_args_for:
+  r_args_for_val {
+    $$ = cfg_alloc($1.length);
+    net_copy($$, &$1);
+  }
+ | net_vpn4_
+ | net_vpn6_
+ | net_ip6_sadr_
+ | VPN_RD IP4 {
+    $$ = cfg_alloc(sizeof(net_addr_vpn4));
+    net_fill_vpn4($$, $2, IP4_MAX_PREFIX_LENGTH, $1);
+  }
+ | VPN_RD IP6 {
+    $$ = cfg_alloc(sizeof(net_addr_vpn6));
+    net_fill_vpn6($$, $2, IP6_MAX_PREFIX_LENGTH, $1);
+  }
+ | IP6 FROM IP6 {
+    $$ = cfg_alloc(sizeof(net_addr_ip6_sadr));
+    net_fill_ip6_sadr($$, $1, IP6_MAX_PREFIX_LENGTH, $3, IP6_MAX_PREFIX_LENGTH);
+  }
+ | CF_SYM_KNOWN {
+     if ($1->class == (SYM_CONSTANT | T_IP))
+     {
+       $$ = cfg_alloc(ipa_is_ip4(SYM_VAL($1).ip) ? sizeof(net_addr_ip4) : sizeof(net_addr_ip6));
+       net_fill_ip_host($$, SYM_VAL($1).ip);
+     }
+     else if (($1->class == (SYM_CONSTANT | T_NET)) && net_type_match(SYM_VAL($1).net, NB_IP | NB_VPN))
+       $$ = (net_addr *) SYM_VAL($1).net; /* Avoid const warning */
+     else
+       cf_error("IP address or network constant expected");
+   }
+ ;
+
+r_args_for_val:
+   net_ip4_
+ | net_ip6_
+ | IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); }
+ | IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); }
+
 export_mode:
    PREEXPORT   { $$ = RSEM_PREEXPORT; }
  | EXPORT      { $$ = RSEM_EXPORT; }
  | NOEXPORT    { $$ = RSEM_NOEXPORT; }
  ;
 
+/* This is ugly hack */
+r_args_channel:
+   IPV4                { $$ = "ipv4"; }
+ | IPV4_MC     { $$ = "ipv4-mc"; }
+ | IPV4_MPLS   { $$ = "ipv4-mpls"; }
+ | IPV6                { $$ = "ipv6"; }
+ | IPV6_MC     { $$ = "ipv6-mc"; }
+ | IPV6_MPLS   { $$ = "ipv6-mpls"; }
+ | IPV6_SADR   { $$ = "ipv6-sadr"; }
+ | VPN4                { $$ = "vpn4"; }
+ | VPN4_MC     { $$ = "vpn4-mc"; }
+ | VPN4_MPLS   { $$ = "vpn4-mpls"; }
+ | VPN6                { $$ = "vpn6"; }
+ | VPN6_MC     { $$ = "vpn6-mc"; }
+ | VPN6_MPLS   { $$ = "vpn6-mpls"; }
+ | ROA4                { $$ = "roa4"; }
+ | ROA6                { $$ = "roa6"; }
+ | FLOW4       { $$ = "flow4"; }
+ | FLOW6       { $$ = "flow6"; }
+ | MPLS                { $$ = "mpls"; }
+ | PRI         { $$ = "pri"; }
+ | SEC         { $$ = "sec"; }
+ ;
 
 CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]])
 CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]])
@@ -533,7 +710,7 @@ sym_args:
  | sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; }
  | sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; }
  | sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; }
- | sym_args SYM { $$ = $1; $$->sym = $2; }
+ | sym_args symbol { $$ = $1; $$->sym = $2; }
  ;
 
 
@@ -556,10 +733,10 @@ CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
 { protos_dump_all(); cli_msg(0, ""); } ;
 
 CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
-{ cmd_eval($2); } ;
+{ cmd_eval(f_linearize($2)); } ;
 
 CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])
-CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug | trace | info | remote | warning | error | auth }) [<buffer-size>], [[Control echoing of log messages]]) {
+CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug|trace|info|remote|warning|error|auth [, ...] }) [<buffer-size>], [[Control echoing of log messages]]) {
   cli_set_log_echo(this_cli, $2, $3);
   cli_msg(0, "");
 } ;
@@ -578,12 +755,12 @@ echo_size:
    }
  ;
 
-CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]])
-{ proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ;
-CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]])
-{ proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ;
-CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]])
-{ proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ;
+CF_CLI(DISABLE, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Disable protocol]])
+{ proto_apply_cmd($2, proto_cmd_disable, 1, (uintptr_t) $3); } ;
+CF_CLI(ENABLE, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Enable protocol]])
+{ proto_apply_cmd($2, proto_cmd_enable, 1, (uintptr_t) $3); } ;
+CF_CLI(RESTART, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Restart protocol]])
+{ proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ;
 CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
 { proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
 CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
@@ -592,30 +769,29 @@ CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protoc
 { proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ;
 
 CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]])
-CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | interfaces | events | packets }), [[Control protocol debugging via BIRD logs]])
+CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | \"<pattern>\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]])
 { proto_apply_cmd($2, proto_cmd_debug, 1, $3); } ;
 
 CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]])
-CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | <pattern> | all) (all | off | { states | messages }), [[Control protocol debugging via MRTdump format]])
+CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | \"<pattern>\" | all) (all | off | { states|messages [, ...] }), [[Control protocol debugging via MRTdump format]])
 { proto_apply_cmd($2, proto_cmd_mrtdump, 1, $3); } ;
 
 CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
 { this_cli->restricted = 1; cli_msg(16, "Access restricted"); } ;
 
 proto_patt:
-   SYM  { $$.ptr = $1; $$.patt = 0; }
+   CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$.ptr = $1; $$.patt = 0; }
  | ALL  { $$.ptr = NULL; $$.patt = 1; }
  | TEXT { $$.ptr = $1; $$.patt = 1; }
  ;
 
 proto_patt2:
-   SYM  { $$.ptr = $1; $$.patt = 0; }
+   CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$.ptr = $1; $$.patt = 0; }
  |      { $$.ptr = NULL; $$.patt = 1; }
  | TEXT { $$.ptr = $1; $$.patt = 1; }
  ;
 
-CF_ADDTO(dynamic_attr, IGP_METRIC
-       { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); })
+dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, 0, T_INT, EA_GEN_IGP_METRIC); } ;
 
 
 CF_CODE