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 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 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 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 ways how to name symbols (like protocol names, filter names, constants etc.).
can either use a simple string starting with a letter followed by any 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 combination of letters and numbers (e.g. <cf/R123/, <cf/myfilter/, <cf/bgp5/) or
enclose the name into apostrophes (<cf/'/) and than you can use any combination you can enclose the name into apostrophes (<cf/'/) and than you can use any
of numbers, letters. hyphens, dots and colons (e.g. <cf/'1:strange-name'/, combination of numbers, letters. hyphens, dots and colons (e.g.
<cf/'-NAME-'/, <cf/'cool::name'/). <cf/'1:strange-name'/, <cf/'-NAME-'/, <cf/'cool::name'/).
<p>Here is an example of a simple config file. It enables synchronization of <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 routing tables with OS kernel, learns network interfaces and runs RIP on all
and runs RIP on all network interfaces found. network interfaces found.
<code> <code>
protocol kernel { protocol kernel {
persist; # Don't remove routes on BIRD shutdown ipv4 {
scan time 20; # Scan kernel routing table every 20 seconds
export all; # Default is export none export all; # Default is export none
};
persist; # Don't remove routes on BIRD shutdown
} }
protocol device { protocol device {
scan time 10; # Scan interfaces every 10 seconds
} }
protocol rip { protocol rip {
export all; ipv4 {
import all; import all;
export all;
};
interface "*"; interface "*";
} }
</code> </code>
@ -775,8 +777,10 @@ agreement").
<label id="channel-opts"> <label id="channel-opts">
<p>Every channel belongs to a protocol and is configured inside its block. The <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 minimal channel config is empty, then it uses default values. The name of the
the channel implies its nettype. 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> <descrip>
<tag><label id="proto-table">table <m/name/</tag> <tag><label id="proto-table">table <m/name/</tag>
@ -841,7 +845,7 @@ protocol rip ng {
} }
</code> </code>
<p>And this is a non-trivial example. <p>This is a non-trivial example.
<code> <code>
protocol rip ng { protocol rip ng {
ipv6 { ipv6 {
@ -854,6 +858,33 @@ protocol rip ng {
} }
</code> </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 <chapt>Remote control
<label id="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. Evaluate given expression.
</descrip> </descrip>
<chapt>Filters <chapt>Filters
<label id="filters"> <label id="filters">
@ -2427,7 +2459,7 @@ together with their appropriate channels follows.
</tabular> </tabular>
</table> </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> <descrip>
<tag><label id="bgp-next-hop-keep">next hop keep</tag> <tag><label id="bgp-next-hop-keep">next hop keep</tag>

View file

@ -214,7 +214,7 @@ proto_item:
channel_start: net_type 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: channel_item:

View file

@ -455,11 +455,10 @@ const struct channel_class channel_basic = {
}; };
void * 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 channel_config *cf = NULL;
struct rtable_config *tab = NULL; struct rtable_config *tab = NULL;
const char *name = NULL;
if (net_type) 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"); cf_error("Different channel type");
tab = new_config->def_tables[net_type]; tab = new_config->def_tables[net_type];
name = net_label[net_type];
} }
if (!cc) 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 = cfg_allocz(cc->config_size);
cf->name = name; cf->name = name;
cf->channel = cc; cf->channel = cc;
cf->parent = proto;
cf->table = tab; cf->table = tab;
cf->out_filter = FILTER_REJECT; 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; 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 * struct channel_config *
channel_copy_config(struct channel_config *src, struct proto_config *proto) channel_copy_config(struct channel_config *src, struct proto_config *proto)
{ {

View file

@ -461,6 +461,7 @@ struct channel_config {
const char *name; const char *name;
const struct channel_class *channel; 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 rtable_config *table; /* Table we're attached to */
struct filter *in_filter, *out_filter; /* Attached filters */ struct filter *in_filter, *out_filter; /* Attached filters */
struct channel_limit rx_limit; /* Limit for receiving routes from protocol 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); } static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
void channel_request_feeding(struct channel *c); 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); int channel_reconfigure(struct channel *c, struct channel_config *cf);

View file

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

View file

@ -84,8 +84,8 @@ ospf_proto_finish(void)
/* Define default channel */ /* Define default channel */
if (EMPTY_LIST(this_proto->channels)) if (EMPTY_LIST(this_proto->channels))
{ {
this_proto->net_type = ospf_cfg_is_v2() ? NET_IP4 : NET_IP6; uint net_type = this_proto->net_type = ospf_cfg_is_v2() ? NET_IP4 : NET_IP6;
channel_config_new(NULL, this_proto->net_type, this_proto); channel_config_new(NULL, net_label[net_type], net_type, this_proto);
} }
/* Propagate global instance ID to interfaces */ /* Propagate global instance ID to interfaces */
@ -238,7 +238,8 @@ ospf_af_mc:
/* We redefine proto_channel to add multicast flag */ /* We redefine proto_channel to add multicast flag */
ospf_channel_start: net_type ospf_af_mc 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 */ /* Save the multicast flag */
if (this_channel == proto_cf_main_channel(this_proto)) 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 pipe_proto_start: proto_start PIPE
{ {
this_proto = proto_config_new(&proto_pipe, $1); 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->in_filter = FILTER_ACCEPT;
this_channel->out_filter = FILTER_ACCEPT; this_channel->out_filter = FILTER_ACCEPT;
}; };

View file

@ -569,7 +569,7 @@ radv_postconfig(struct proto_config *CF)
/* Define default channel */ /* Define default channel */
if (EMPTY_LIST(CF->channels)) 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 * static struct proto *

View file

@ -1078,7 +1078,7 @@ rip_postconfig(struct proto_config *CF)
/* Define default channel */ /* Define default channel */
if (EMPTY_LIST(CF->channels)) 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 * static struct proto *

View file

@ -901,7 +901,7 @@ rpki_postconfig(struct proto_config *CF)
{ {
/* Define default channel */ /* Define default channel */
if (EMPTY_LIST(CF->channels)) 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 static void