Nest: Allow modification of channels inherited from templates

Multiple definitions of same channels are forbidden, but inherited
channel can be redefined. In such case channel options are merged.
This commit is contained in:
Ondrej Zajicek (work) 2018-01-09 18:42:22 +01:00
parent 09c1e370b3
commit 72163bd5f3
10 changed files with 93 additions and 35 deletions

View file

@ -407,31 +407,33 @@ extensive way.
a comment, whitespace characters are treated as a single space. If there's a
variable number of options, they are grouped using the <cf/{ }/ brackets. Each
option is terminated by a <cf/;/. Configuration is case sensitive. There are two
ways how to name symbols (like protocol names, filter names, constants etc.). You
can either use a simple string starting with a letter followed by any
combination of letters and numbers (e.g. <cf/R123/, <cf/myfilter/, <cf/bgp5/) or you can
enclose the name into apostrophes (<cf/'/) and than you can use any combination
of numbers, letters. hyphens, dots and colons (e.g. <cf/'1:strange-name'/,
<cf/'-NAME-'/, <cf/'cool::name'/).
ways how to name symbols (like protocol names, filter names, constants etc.).
You can either use a simple string starting with a letter followed by any
combination of letters and numbers (e.g. <cf/R123/, <cf/myfilter/, <cf/bgp5/) or
you can enclose the name into apostrophes (<cf/'/) and than you can use any
combination of numbers, letters. hyphens, dots and colons (e.g.
<cf/'1:strange-name'/, <cf/'-NAME-'/, <cf/'cool::name'/).
<p>Here is an example of a simple config file. It enables synchronization of
routing tables with OS kernel, scans for new network interfaces every 10 seconds
and runs RIP on all network interfaces found.
routing tables with OS kernel, learns network interfaces and runs RIP on all
network interfaces found.
<code>
protocol kernel {
ipv4 {
export all; # Default is export none
};
persist; # Don't remove routes on BIRD shutdown
scan time 20; # Scan kernel routing table every 20 seconds
export all; # Default is export none
}
protocol device {
scan time 10; # Scan interfaces every 10 seconds
}
protocol rip {
export all;
import all;
ipv4 {
import all;
export all;
};
interface "*";
}
</code>
@ -775,8 +777,10 @@ agreement").
<label id="channel-opts">
<p>Every channel belongs to a protocol and is configured inside its block. The
minimal channel config is empty, then it uses the default values. The name of
the channel implies its nettype.
minimal channel config is empty, then it uses default values. The name of the
channel implies its nettype. Channel definitions can be inherited from protocol
templates. Multiple definitions of the same channel are forbidden, but channels
inherited from templates can be updated by new definitions.
<descrip>
<tag><label id="proto-table">table <m/name/</tag>
@ -841,7 +845,7 @@ protocol rip ng {
}
</code>
<p>And this is a non-trivial example.
<p>This is a non-trivial example.
<code>
protocol rip ng {
ipv6 {
@ -854,6 +858,33 @@ protocol rip ng {
}
</code>
<p>And this is even more complicated example using templates.
<code>
template bgp {
local 198.51.100.14 as 65000;
ipv4 {
table mytable4;
import filter { ... };
};
ipv6 {
table mytable6;
import filter { ... };
};
}
protocol bgp from {
neighbor 198.51.100.130 as 64496;
# IPv4 channel is inherited as-is, while IPv6
# channel is adjusted by export filter option
ipv6 {
export filter { ... };
};
}
</code>
<chapt>Remote control
<label id="remote-control">
@ -1051,6 +1082,7 @@ This argument can be omitted if there exists only a single instance.
Evaluate given expression.
</descrip>
<chapt>Filters
<label id="filters">
@ -2427,7 +2459,7 @@ together with their appropriate channels follows.
</tabular>
</table>
<p>BGP's channels have additional config options (together with the common ones):
<p>BGP channels have additional config options (together with the common ones):
<descrip>
<tag><label id="bgp-next-hop-keep">next hop keep</tag>

View file

@ -214,7 +214,7 @@ proto_item:
channel_start: net_type
{
$$ = this_channel = channel_config_new(NULL, $1, this_proto);
$$ = this_channel = channel_config_get(NULL, net_label[$1], $1, this_proto);
};
channel_item:

View file

@ -455,11 +455,10 @@ const struct channel_class channel_basic = {
};
void *
channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto)
channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto)
{
struct channel_config *cf = NULL;
struct rtable_config *tab = NULL;
const char *name = NULL;
if (net_type)
{
@ -470,7 +469,6 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c
cf_error("Different channel type");
tab = new_config->def_tables[net_type];
name = net_label[net_type];
}
if (!cc)
@ -479,6 +477,7 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c
cf = cfg_allocz(cc->config_size);
cf->name = name;
cf->channel = cc;
cf->parent = proto;
cf->table = tab;
cf->out_filter = FILTER_REJECT;
@ -491,6 +490,26 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c
return cf;
}
void *
channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto)
{
struct channel_config *cf;
/* We are using name as token, so no strcmp() */
WALK_LIST(cf, proto->channels)
if (cf->name == name)
{
/* Allow to redefine channel only if inherited from template */
if (cf->parent == proto)
cf_error("Multiple %s channels", name);
cf->parent = proto;
return cf;
}
return channel_config_new(cc, name, net_type, proto);
}
struct channel_config *
channel_copy_config(struct channel_config *src, struct proto_config *proto)
{

View file

@ -461,6 +461,7 @@ struct channel_config {
const char *name;
const struct channel_class *channel;
struct proto_config *parent; /* Where channel is defined (proto or template) */
struct rtable_config *table; /* Table we're attached to */
struct filter *in_filter, *out_filter; /* Attached filters */
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
@ -585,7 +586,8 @@ static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP)
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
void channel_request_feeding(struct channel *c);
void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto);
void *channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
void *channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
int channel_reconfigure(struct channel *c, struct channel_config *cf);

View file

@ -158,12 +158,16 @@ bgp_channel_start: bgp_afi
if (!desc)
cf_error("Unknown AFI/SAFI");
this_channel = channel_config_new(&channel_bgp, desc->net, this_proto);
BGP_CC->c.name = desc->name;
BGP_CC->c.ra_mode = RA_UNDEF;
BGP_CC->afi = $1;
BGP_CC->desc = desc;
BGP_CC->gr_able = 0xff; /* undefined */
this_channel = channel_config_get(&channel_bgp, desc->name, desc->net, this_proto);
/* New channel */
if (!BGP_CC->desc)
{
BGP_CC->c.ra_mode = RA_UNDEF;
BGP_CC->afi = $1;
BGP_CC->desc = desc;
BGP_CC->gr_able = 0xff; /* undefined */
}
};
bgp_channel_item:

View file

@ -84,8 +84,8 @@ ospf_proto_finish(void)
/* Define default channel */
if (EMPTY_LIST(this_proto->channels))
{
this_proto->net_type = ospf_cfg_is_v2() ? NET_IP4 : NET_IP6;
channel_config_new(NULL, this_proto->net_type, this_proto);
uint net_type = this_proto->net_type = ospf_cfg_is_v2() ? NET_IP4 : NET_IP6;
channel_config_new(NULL, net_label[net_type], net_type, this_proto);
}
/* Propagate global instance ID to interfaces */
@ -238,7 +238,8 @@ ospf_af_mc:
/* We redefine proto_channel to add multicast flag */
ospf_channel_start: net_type ospf_af_mc
{
$$ = this_channel = channel_config_new(NULL, $1, this_proto);
/* TODO: change name for multicast channels */
$$ = this_channel = channel_config_get(NULL, net_label[$1], $1, this_proto);
/* Save the multicast flag */
if (this_channel == proto_cf_main_channel(this_proto))

View file

@ -25,7 +25,7 @@ CF_ADDTO(proto, pipe_proto '}' { this_channel = NULL; } )
pipe_proto_start: proto_start PIPE
{
this_proto = proto_config_new(&proto_pipe, $1);
this_channel = channel_config_new(NULL, 0, this_proto);
this_channel = channel_config_new(NULL, NULL, 0, this_proto);
this_channel->in_filter = FILTER_ACCEPT;
this_channel->out_filter = FILTER_ACCEPT;
};

View file

@ -569,7 +569,7 @@ radv_postconfig(struct proto_config *CF)
/* Define default channel */
if (EMPTY_LIST(CF->channels))
channel_config_new(NULL, NET_IP6, CF);
channel_config_new(NULL, net_label[NET_IP6], NET_IP6, CF);
}
static struct proto *

View file

@ -1078,7 +1078,7 @@ rip_postconfig(struct proto_config *CF)
/* Define default channel */
if (EMPTY_LIST(CF->channels))
channel_config_new(NULL, CF->net_type, CF);
channel_config_new(NULL, net_label[CF->net_type], CF->net_type, CF);
}
static struct proto *

View file

@ -901,7 +901,7 @@ rpki_postconfig(struct proto_config *CF)
{
/* Define default channel */
if (EMPTY_LIST(CF->channels))
channel_config_new(NULL, CF->net_type, CF);
channel_config_new(NULL, net_label[CF->net_type], CF->net_type, CF);
}
static void