[PATCH 1/1] Add general protocol templates support. Permit BGP to use them

Alexander V. Chernikov melifaro at ipfw.ru
Sat Oct 29 03:32:13 CEST 2011


---
 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--



More information about the Bird-users mailing list