--- conf/cf-lex.l | 2 ++ conf/conf.c | 32 ++++++++++++++++++++++++++++++++ conf/conf.h | 4 ++++ doc/bird.conf.example | 14 ++++++++++++++ doc/bird.sgml | 8 ++++++++ nest/config.Y | 16 +++++++++++----- nest/proto.c | 18 +++++++++++++++--- nest/protocol.h | 5 +++++ proto/bgp/bgp.c | 16 +++++++++++++++- proto/bgp/config.Y | 1 + 10 files changed, 107 insertions(+), 9 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 02ba4b3..ddfe8c7 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -533,6 +533,8 @@ cf_symbol_class_name(struct symbol *sym) return "routing table"; case SYM_IPA: return "network address"; + case SYM_TEMPLATE: + return "protocol template"; default: return "unknown type"; } diff --git a/conf/conf.c b/conf/conf.c index 5bdeece..fc789bf 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -143,6 +143,38 @@ cli_parse(struct config *c) return 1; } +void +config_inherit_cfg(struct proto_config *target, struct proto_config *source) +{ + int target_is_template = 0; + node target_n; + char *target_name; + + if (target->protocol != source->protocol) + cf_error("Can't inherit configuration between different protocol types"); + + if (target->protocol->inherit_config == NULL) + cf_error("Inheriting configuration for %s is not supported"); + + DBG("Copying configuration from protocol %s template %s to %s\n", source->protocol->name, source->name, target->name); + /* + * Copy struct proto_config here. + * protocol-specific config inheritance is handled by protocol inherit_config() hook + */ + /* save dst-specific data */ + target_is_template = target->is_template; + target_n = target->n; + target_name = target->name; + /* copy entire proto_config structure */ + memcpy(target, source, sizeof(struct proto_config)); + /* restore dst-specific data */ + target->is_template = target_is_template; + target->n = target_n; + target->name = target_name; + + target->protocol->inherit_config(target, source); +} + /** * config_free - free a configuration * @c: configuration to be freed diff --git a/conf/conf.h b/conf/conf.h index 142c6ad..6c2e017 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -59,9 +59,12 @@ extern struct config *future_config; /* New config held here if recon requested extern int shutting_down; extern bird_clock_t boot_time; +struct proto_config; + struct config *config_alloc(byte *name); int config_parse(struct config *); int cli_parse(struct config *); +void config_inherit_cfg(struct proto_config *target, struct proto_config *source); void config_free(struct config *); int config_commit(struct config *, int type); #define RECONFIG_HARD 0 @@ -108,6 +111,7 @@ struct symbol { #define SYM_FILTER 4 #define SYM_TABLE 5 #define SYM_IPA 6 +#define SYM_TEMPLATE 7 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ diff --git a/doc/bird.conf.example b/doc/bird.conf.example index 339898f..736afaf 100644 --- a/doc/bird.conf.example +++ b/doc/bird.conf.example @@ -202,3 +202,17 @@ protocol static { # reject; # }; #} +# +# Template usage example +#template bgp rr_client { +# disabled; +# local as 65000; +# multihop; +# rr client; +# rr cluster id 1.0.0.1; +#} +# +#protocol bgp { +# inherit rr_client; +# neighbor 10.1.4.7 as 65000; +#} diff --git a/doc/bird.sgml b/doc/bird.sgml index d454629..9419a90 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -301,6 +301,11 @@ protocol rip { about configuring protocols in their own chapters. You can run more than one instance of most protocols (like RIP or BGP). By default, no instances are configured. + <tag>template bgp|... <m/[name]/ { <m>protocol options</m> }</tag> Define a protocol template + instance called <cf><m/name/</cf> (or with a name like "bgp1" generated automatically if you don't specify any <cf><m/name/</cf>). + Protocol instance can refer to template by its name. Templates can inherit each other. At the moment templates are supported for + BGP only. See <cf/interit/ general protocol option for more information. + <tag>define <m/constant/ = (<m/expression/)|<m/number/|<m/IP address/</tag> Define a constant. You can use it later in every place you could use a simple integer or an IP address. Besides, there are some predefined numeric constants based on /etc/iproute2/rt_* files. @@ -398,6 +403,9 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/ to override global router id for a given protocol. Default: uses global router id. + <tag>inherit <m/name/</tag> Inherits all configuration from template + <m/name/. + <tag>import all | none | filter <m/name/ | filter { <m/filter commands/ } | where <m/filter expression/</tag> Specify a filter to be used for filtering routes coming from the protocol to the routing table. <cf/all/ is shorthand for <cf/where true/ and <cf/none/ is shorthand for <cf/where false/. Default: <cf/all/. diff --git a/nest/config.Y b/nest/config.Y index dd4a9e0..1b15ffe 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -21,7 +21,7 @@ static struct iface_patt *this_ipatt; static struct iface_patt_node *this_ipn; static list *this_p_list; static struct password_item *this_p_item; -static int password_id; +static int password_id, is_template; static inline void reset_passwords(void) @@ -40,7 +40,7 @@ get_passwords(void) CF_DECLS -CF_KEYWORDS(ROUTER, ID, PROTOCOL, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) +CF_KEYWORDS(ROUTER, ID, PROTOCOL, INHERIT, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE) @@ -115,18 +115,20 @@ newtab: TABLE SYM { CF_ADDTO(conf, proto) -proto_start: PROTOCOL +proto_start: + PROTOCOL { is_template = 0; } + | TEMPLATE { is_template = 1; } ; proto_name: /* EMPTY */ { struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter); - s->class = SYM_PROTO; + s->class = is_template ? SYM_TEMPLATE : SYM_PROTO; s->def = this_proto; this_proto->name = s->name; } | SYM { - cf_define_symbol($1, SYM_PROTO, this_proto); + cf_define_symbol($1, is_template ? SYM_TEMPLATE : SYM_PROTO, this_proto); this_proto->name = $1->name; } ; @@ -145,6 +147,10 @@ proto_item: | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } | DESCRIPTION TEXT { this_proto->dsc = $2; } + | INHERIT SYM { + if (($2->class != SYM_TEMPLATE) && ($2->class != SYM_PROTO)) cf_error("template/protocol name expected"); + config_inherit_cfg(this_proto, $2->def); + } ; imexport: diff --git a/nest/proto.c b/nest/proto.c index 4a154d5..00e8cea 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -366,14 +366,26 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty { struct proto_config *oc, *nc; struct proto *p, *n; + struct symbol *sym; DBG("protos_commit:\n"); if (old) { WALK_LIST(oc, old->protos) { - struct proto *p = oc->proto; - struct symbol *sym = cf_find_symbol(oc->name); + if (oc->is_template) + { + /* + * If template exists in new configuration we don't need to do anything: + * sym->def is already pointing to the right config. + * + * If template does not exist - old config gets removed automatically, symbol + * is already deleted. + */ + continue; + } + p = oc->proto; + sym = cf_find_symbol(oc->name); if (sym && sym->class == SYM_PROTO && !new->shutdown) { /* Found match, let's check if we can smoothly switch to new configuration */ @@ -410,7 +422,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty } WALK_LIST(nc, new->protos) - if (!nc->proto) + if ((!nc->proto) && (!nc->is_template)) { if (old_config) /* Not a first-time configuration */ log(L_INFO "Adding protocol %s", nc->name); diff --git a/nest/protocol.h b/nest/protocol.h index f95905a..60f5f9d 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -53,6 +53,7 @@ struct protocol { void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */ int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */ void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */ + void (*inherit_config)(struct proto_config *, struct proto_config *); /* Inherits config from given protocol instance */ }; void protos_build(void); @@ -78,6 +79,9 @@ extern struct protocol * Routing Protocol Instance */ +/* + * Check config_inherit_cfg() after changing + */ struct proto_config { node n; struct config *global; /* Global configuration data */ @@ -87,6 +91,7 @@ struct proto_config { char *dsc; u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */ unsigned preference, disabled; /* Generic parameters */ + unsigned is_template; /* 1 if this is template config */ u32 router_id; /* Protocol specific router ID */ struct rtable_config *table; /* Table we're attached to */ struct filter *in_filter, *out_filter; /* Attached filters */ diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 4e4ca9f..c410d66 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -988,6 +988,10 @@ bgp_check(struct bgp_config *c) { int internal = (c->local_as == c->remote_as); + /* Do not check templates at all */ + if (c->c.is_template) + return; + if (!c->local_as) cf_error("Local AS number must be set"); @@ -1015,6 +1019,15 @@ bgp_check(struct bgp_config *c) c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT; } +void +bgp_inherit_config(struct proto_config *_target, struct proto_config *_source) +{ + DBG("BGP INHERIT: %s <- %s\n", _target->name, _source->name); + + /* We need to copy our procol-specific data only */ + memcpy(_target + 1, _source + 1, sizeof(struct bgp_config) - sizeof(struct proto_config)); +} + static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" }; static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", ""}; static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket" }; @@ -1158,5 +1171,6 @@ struct protocol proto_bgp = { get_status: bgp_get_status, get_attr: bgp_get_attr, get_route_info: bgp_get_route_info, - show_proto_info: bgp_show_proto_info + show_proto_info: bgp_show_proto_info, + inherit_config: bgp_inherit_config }; diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 19d757a..a5fa26b 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -34,6 +34,7 @@ CF_ADDTO(proto, bgp_proto '}' { bgp_check(BGP_CFG); } ) bgp_proto_start: proto_start BGP { this_proto = proto_config_new(&proto_bgp, sizeof(struct bgp_config)); this_proto->preference = DEF_PREF_BGP; + this_proto->is_template = is_template; BGP_CFG->hold_time = 240; BGP_CFG->connect_retry_time = 120; BGP_CFG->initial_hold_time = 240; -- 1.7.3.2 --------------020809060206090704030007--