New version.
Changelog:
* Split nest API and bgp implementation
* Simplify new nest code a bit
* Fix crash on CLI lookups (forgot to fill in sym->def) 
* Remove protocols on dynamic templates renaming
* Permit deleting dynamic instances via removal of disabled protos on 
reconfigure
* Do not create new dynamic protocols on shutdown
* Permit using table names (%T) in templates
* Allow to inherit allow list from common templates (handy in link-local setups)


12.02.2015, 03:48, "Alexander V. Chernikov" <[email protected]>:
> I'm sorry, previous posting was in html, duplicating as plain-text
>
> I'd like to share some (beta-quality) patches permitting to create BGP 
> protocol instances dynamically, based on templates.
> Main purpose is to get feedback and make them good enough to be integrated 
> into main tree.
>
> Similar feature was implemented by major vendors yeas ago.
> Juniper:
> http://www.juniper.net/techpubs/en_US/junos11.4/topics/usage-guidelines/routing-configuring-bgp-groups-and-peers.html
> Cisco IOS: 
> http://www.cisco.com/c/en/us/td/docs/ios/12_2sr/12_2srb/feature/guide/tbgp_c/brbpeer.html#wp1128937
>
> Typically it is used in peering setups.
>
> Patches still have some rough edges (see below) but should work^Wnot make 
> bird crash every 5 minutes.
>
> Example setup:
>
> # Set up template with ALL info except and IP address
> # In this case we don't have any option to specify interface, so it is 
> specified in
> # neighbor part
> template bgp bgg {
>         debug all;
>         local as 4444;
>         neighbor fe80::1%em0 as 4443;
>         import all;
>         export all;
> };
>
> # Set up dynamic template with ranges and neighbor mask
> # Accepted format (%) values:
> # 1..4 - octets in IPv4 address
> # 1..8 - hex groups in IPv6 address
> # I - full address
> # format values can be specified more than once in format string
> # e.g. "b_x_%4_%I_%4"
> dynamic template bgp bgp_int from bgg {
>         dynamic template name "b_s%4-%8-%8";
>         dynamic allow fe80::f000/112, fe80::f00/116;
> };
>
> # You need to have at least one "normal" bgp peer to get listen socket opened
> # That's all
>
> # Example from working system:
> #   using  several { dynamic template name "b_Vrf1_s%8"; dynamic allow 
> fe80::/64; }
> #  templates
>
> bird> show protocols
> name     proto    table    state  since       info
> direct1  Direct   master   up     22:06:05
> kernel1  Kernel   master   up     22:06:05
> device1  Device   master   up     22:06:05
> ospf3    OSPF     master   up     22:06:05    Running
> ..
> b_Vrf2_dc1_c1 BGP      Vrf2     up     22:06:06    Established
> b_Vrf1_se48 BGP      Vrf1   up     22:06:05    Established
> b_Vrf1_se10 BGP      Vrf1   up     22:06:06    Established
> b_Vrf1_se45 BGP      Vrf1   up     22:06:06    Established
> b_Vrf1_se60 BGP      Vrf1   up     22:06:07    Established
> b_Vrf2_se91 BGP      Vrf2     up     22:06:08    Established
> ..
>
> Comments on individual patches:
> 0001 is basic LPM for the f_trie  and is part of aggregator patch
> 0002 decouples get-proto-from-address function from incoming connection 
> handling. This makes hacking here much simpler (for example, implementing IP 
> hash lookup)
> 0003 just moves SYM_MAX_LEN definition to header
> 0004 moves sizeof(XXX_proto) info inside protocol structure permitting anyone 
> to call proto_config_init() without knowing details
> 0005 tries to isolate/limit global new_config/cfg_mem variables usage to 
> absolute minimum. Despite the fact that actual BGP patch does not require 
> much more than config_start_edit() / config_stop_edit() implementation, I 
> (after hours of debugging cores) decided to make things more transparent here.
> 0006 is the actual patch.
> Basically, it
> * adds SYM_TEMPLATE class
> * adds dyn_parent proto_config field to specify parent template
> * adds condition-check and copy-dynamic protocol callbacks
> * adds runtime config/protocol creation functions to nest
> * adds bgp-specific structure to struct config allocated on demand
>
> Rough edges:
> * non-optimized case for IPv6 LL prefixes
> * non-optimized peer lookup for existing sessions
> * dynamic expire not implemented (next version)
> * no docs (next version)
> * some bugs, definitely
From ddbd97b2ea919ddfb49015fc6044daa960873138 Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <[email protected]>
Date: Sat, 24 Jan 2015 21:10:01 +0300
Subject: [PATCH 1/8] Add LPM support for trie.

---
 filter/config.Y |   2 +-
 filter/filter.h |   7 +++-
 filter/trie.c   | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 nest/rt-table.c |   4 +-
 4 files changed, 113 insertions(+), 15 deletions(-)

diff --git a/filter/config.Y b/filter/config.Y
index e50e75c..7eb2c0a 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -592,7 +592,7 @@ fprefix:
  ;
 
 fprefix_set:
-   fprefix { $$ = f_new_trie(cfg_mem); trie_add_fprefix($$, &($1.val.px)); }
+   fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_fprefix($$, &($1.val.px)); }
  | fprefix_set ',' fprefix { $$ = $1; trie_add_fprefix($$, &($3.val.px)); }
  ;
 
diff --git a/filter/filter.h b/filter/filter.h
index 3a6b66d..522af9f 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -80,11 +80,13 @@ struct f_tree *find_tree(struct f_tree *t, struct f_val val);
 int same_tree(struct f_tree *t1, struct f_tree *t2);
 void tree_format(struct f_tree *t, buffer *buf);
 
-struct f_trie *f_new_trie(linpool *lp);
-void trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h);
+struct f_trie *f_new_trie(linpool *lp, size_t node_size);
+void *trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h);
 int trie_match_prefix(struct f_trie *t, ip_addr px, int plen);
+void *trie_match_longest_prefix(struct f_trie *t, ip_addr px, int plen);
 int trie_same(struct f_trie *t1, struct f_trie *t2);
 void trie_format(struct f_trie *t, buffer *buf);
+void trie_walk(struct f_trie *t, void *func, void *data);
 
 void fprefix_get_bounds(struct f_prefix *px, int *l, int *h);
 
@@ -204,6 +206,7 @@ struct f_trie
 {
   linpool *lp;
   int zero;
+  int node_size;
   struct f_trie_node root;
 };
 
diff --git a/filter/trie.c b/filter/trie.c
index 217d72c..9851877 100644
--- a/filter/trie.c
+++ b/filter/trie.c
@@ -75,23 +75,24 @@
 #include "filter/filter.h"
 
 /**
- * f_new_trie
- *
- * Allocates and returns a new empty trie.
+ * f_new_trie - Allocates and returns a new empty trie.
+ * @lp: linear pool to allocate items from
+ * @node_size: element size to allocate
  */
 struct f_trie *
-f_new_trie(linpool *lp)
+f_new_trie(linpool *lp, size_t node_size)
 {
   struct f_trie * ret;
-  ret = lp_allocz(lp, sizeof(struct f_trie));
+  ret = lp_allocz(lp, sizeof(struct f_trie) + node_size - sizeof(struct f_trie_node));
   ret->lp = lp;
+  ret->node_size = node_size;
   return ret;
 }
 
 static inline struct f_trie_node *
 new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask)
 {
-  struct f_trie_node *n = lp_allocz(t->lp, sizeof(struct f_trie_node));
+  struct f_trie_node *n = lp_allocz(t->lp, t->node_size);
   n->plen = plen;
   n->addr = paddr;
   n->mask = pmask;
@@ -116,9 +117,13 @@ attach_node(struct f_trie_node *parent, struct f_trie_node *child)
  * Adds prefix (prefix pattern) @px/@plen to trie @t.  @l and @h are lower
  * and upper bounds on accepted prefix lengths, both inclusive.
  * 0 <= l, h <= 32 (128 for IPv6).
+ *
+ * Returns pointer to allocated node. Function can return pointer to
+ * existing node if @px and @plen are the same. If px/plen == 0/0 (or ::/0)
+ * pointer to root node is returned
  */
 
-void
+void *
 trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
 {
   if (l == 0)
@@ -156,7 +161,7 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
 	  attach_node(o, b);
 	  attach_node(b, n);
 	  attach_node(b, a);
-	  return;
+	  return a;
 	}
 
       if (plen < n->plen)
@@ -166,14 +171,14 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
 	  struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
 	  attach_node(o, a);
 	  attach_node(a, n);
-	  return;
+	  return a;
 	}
 	
       if (plen == n->plen)
 	{
 	  /* We already found added node in trie. Just update accept mask */
 	  n->accept = ipa_or(n->accept, amask);
-	  return;
+	  return n;
 	}
 
       /* Update accept mask part M2 and go deeper */
@@ -187,6 +192,8 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
   /* We add new tail node 'a' after node 'o' */
   struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
   attach_node(o, a);
+
+  return a;
 }
 
 /**
@@ -234,6 +241,94 @@ trie_match_prefix(struct f_trie *t, ip_addr px, int plen)
   return 0;
 }
 
+#define NODE_IS_BRANCHING(x)	(*((u32 *)(((struct f_trie_node *)(x)) + 1)) == 0)
+/**
+ * trie_match_longest_prefix - find longest prefix match
+ * @t: trie
+ * @px: prefix address
+ * @plen: prefix length
+ *
+ * Tries to find a matching prefix pattern in the trie such that
+ * prefix @px/@plen matches that prefix pattern. Returns prefix pointer
+ * or NULL.
+ */
+void *
+trie_match_longest_prefix(struct f_trie *t, ip_addr px, int plen)
+{
+  ip_addr pmask = ipa_mkmask(plen);
+  ip_addr paddr = ipa_and(px, pmask);
+  ip_addr cmask;
+  struct f_trie_node *n = &t->root, *parent = NULL;
+
+  /* Return root node for 0/0 or :: */
+  if ((plen == 0) && (t->zero))
+	  return n;
+
+  /* Skip root node since it is cath-all node */
+  n = n->c[(ipa_getbit(paddr, 0)) ? 1 : 0];
+
+  while (n)
+    {
+      cmask = ipa_and(n->mask, pmask);
+
+      /* We are out of path */
+      if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask)))
+	break;
+
+      /* Mask is too specific */
+      if (n->plen > plen)
+	break;
+
+      /* Do not save pointer to branching nodes */
+      if (!NODE_IS_BRANCHING(n))
+	parent = n;
+
+      /* Choose children */
+      n = n->c[(ipa_getbit(paddr, n->plen)) ? 1 : 0];
+    }
+
+  /*
+   * parent is either
+   * 1) NULL (if the first non-null node does not exist oris out of path)
+   * or
+   * 2) points to the last entry that match
+   *
+   * In former case we check if catch-all prefix really exists and return
+   * either pointer to root node or NULL. In latter case we simply return parent.
+   */
+
+  return parent ? parent : (t->zero ? &t->root : NULL);
+}
+
+static void
+trie_walk_call(struct f_trie_node *n, void *func, void *data)
+{
+  void (*f)(struct f_trie_node *, void *) = func;
+
+  if (n)
+    f(n, data);
+
+  if (n->c[0])
+    trie_walk_call(n->c[0], func, data);
+
+  if (n->c[1])
+    trie_walk_call(n->c[1], func, data);
+}
+
+void
+trie_walk(struct f_trie *t, void *func, void *data)
+{
+  void (*f)(struct f_trie_node *, void *) = func;
+
+  if (t->zero)
+    f(&t->root, data);
+
+  if (t->root.c[0])
+    trie_walk_call(t->root.c[0], func, data);
+  if (t->root.c[1])
+  trie_walk_call(t->root.c[1], func, data);
+}
+
 static int
 trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2)
 {
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 125f1d1..dbe0c50 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -1988,7 +1988,7 @@ rt_init_hostcache(rtable *tab)
   hc->slab = sl_new(rt_table_pool, sizeof(struct hostentry));
 
   hc->lp = lp_new(rt_table_pool, 1008);
-  hc->trie = f_new_trie(hc->lp);
+  hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node));
 
   tab->hostcache = hc;
 }
@@ -2136,7 +2136,7 @@ rt_update_hostcache(rtable *tab)
 
   /* Reset the trie */
   lp_flush(hc->lp);
-  hc->trie = f_new_trie(hc->lp);
+  hc->trie = f_new_trie(hc->lp, sizeof(struct f_trie_node));
 
   WALK_LIST_DELSAFE(n, x, hc->hostentries)
     {
-- 
2.1.2

From 716d97caa91e90bf079ce55356193d854c63dfdd Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <[email protected]>
Date: Sat, 31 Jan 2015 20:34:38 +0300
Subject: [PATCH 2/8] Make BGP protocol instance search separate function.

---
 proto/bgp/bgp.c | 122 +++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 71 insertions(+), 51 deletions(-)

diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index e233911..99ec1ce 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -713,6 +713,28 @@ bgp_connect(struct bgp_proto *p)	/* Enter Connect state and start establishing c
 }
 
 /**
+ * bgp_find_proto - find existing proto by socket
+ * @sk: TCP socket
+ *
+ */
+static struct bgp_proto *
+bgp_find_proto(sock *sk)
+{
+  struct proto_config *pc;
+
+  WALK_LIST(pc, config->protos)
+    if (pc->protocol == &proto_bgp && pc->proto)
+      {
+	struct bgp_proto *p = (struct bgp_proto *) pc->proto;
+	if (ipa_equal(p->cf->remote_ip, sk->daddr) &&
+	    (!ipa_has_link_scope(sk->daddr) || (p->cf->iface == sk->iface)))
+		return p;
+      }
+
+  return NULL;
+}
+
+/**
  * bgp_incoming_connection - handle an incoming connection
  * @sk: TCP socket
  * @dummy: unused
@@ -727,60 +749,58 @@ bgp_connect(struct bgp_proto *p)	/* Enter Connect state and start establishing c
 static int
 bgp_incoming_connection(sock *sk, int dummy UNUSED)
 {
-  struct proto_config *pc;
+  struct bgp_proto *p;
+  int acc, hops;
 
   DBG("BGP: Incoming connection from %I port %d\n", sk->daddr, sk->dport);
-  WALK_LIST(pc, config->protos)
-    if (pc->protocol == &proto_bgp && pc->proto)
-      {
-	struct bgp_proto *p = (struct bgp_proto *) pc->proto;
-	if (ipa_equal(p->cf->remote_ip, sk->daddr) &&
-	    (!ipa_has_link_scope(sk->daddr) || (p->cf->iface == sk->iface)))
-	  {
-	    /* We are in proper state and there is no other incoming connection */
-	    int acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) &&
-	      (p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk);
-
-	    if (p->conn && (p->conn->state == BS_ESTABLISHED) && p->gr_ready)
-	    {
-	      bgp_store_error(p, NULL, BE_MISC, BEM_GRACEFUL_RESTART);
-	      bgp_handle_graceful_restart(p);
-	      bgp_conn_enter_idle_state(p->conn);
-	      acc = 1;
-	    }
-
-	    BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s",
-		      sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL,
-		      sk->dport, acc ? "accepted" : "rejected");
-
-	    if (!acc)
-	      goto reject;
-
-	    int hops = p->cf->multihop ? : 1;
-
-	    if (sk_set_ttl(sk, p->cf->ttl_security ? 255 : hops) < 0)
-	      goto err;
-
-	    if (p->cf->ttl_security)
-	      if (sk_set_min_ttl(sk, 256 - hops) < 0)
-		goto err;
-
-	    bgp_setup_conn(p, &p->incoming_conn);
-	    bgp_setup_sk(&p->incoming_conn, sk);
-	    bgp_send_open(&p->incoming_conn);
-	    return 0;
-
-	  err:
-	    sk_log_error(sk, p->p.name);
-	    log(L_ERR "%s: Incoming connection aborted", p->p.name);
-	    rfree(sk);
-	    return 0;
-	  }
-      }
 
-  log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)",
-      sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, sk->dport);
- reject:
+  p = bgp_find_proto(sk);
+  if (!p)
+    {
+        log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)",
+	    sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, sk->dport);
+	rfree(sk);
+	return 0;
+    }
+
+  /* We are in proper state and there is no other incoming connection */
+  acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) &&
+    (p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk);
+
+  if (p->conn && (p->conn->state == BS_ESTABLISHED) && p->gr_ready)
+    {
+      bgp_store_error(p, NULL, BE_MISC, BEM_GRACEFUL_RESTART);
+      bgp_handle_graceful_restart(p);
+      bgp_conn_enter_idle_state(p->conn);
+      acc = 1;
+    }
+
+  BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s",
+      sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL,
+      sk->dport, acc ? "accepted" : "rejected");
+
+  if (!acc)
+    {
+      rfree(sk);
+      return 0;
+    }
+
+  hops = p->cf->multihop ? : 1;
+
+  if (sk_set_ttl(sk, p->cf->ttl_security ? 255 : hops) < 0)
+    goto err;
+
+  if (p->cf->ttl_security && ((sk_set_min_ttl(sk, 256 - hops) < 0)))
+    goto err;
+
+  bgp_setup_conn(p, &p->incoming_conn);
+  bgp_setup_sk(&p->incoming_conn, sk);
+  bgp_send_open(&p->incoming_conn);
+  return 0;
+
+err:
+  sk_log_error(sk, p->p.name);
+  log(L_ERR "%s: Incoming connection aborted", p->p.name);
   rfree(sk);
   return 0;
 }
-- 
2.1.2

From 070a4735e626c9a92edf64139c9acd022cd915ea Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <[email protected]>
Date: Sun, 8 Feb 2015 17:39:24 +0300
Subject: [PATCH 3/8] Move SYM_MAX_LEN definition to conf.h

---
 conf/cf-lex.l | 1 -
 conf/conf.h   | 2 ++
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 35b590b..57e0bfa 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -61,7 +61,6 @@ static struct keyword *kw_hash[KW_HASH_SIZE];
 static int kw_hash_inited;
 
 #define SYM_HASH_SIZE 128
-#define SYM_MAX_LEN 32
 
 struct sym_scope {
   struct sym_scope *next;		/* Next on scope stack */
diff --git a/conf/conf.h b/conf/conf.h
index 799873d..3e641ce 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -108,6 +108,8 @@ struct symbol {
   char name[1];
 };
 
+#define SYM_MAX_LEN 32
+
 /* Remember to update cf_symbol_class_name() */
 #define SYM_VOID 0
 #define SYM_PROTO 1
-- 
2.1.2

From f9d08002d1c5969d7f48be92185660fd17511a05 Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <[email protected]>
Date: Mon, 9 Feb 2015 00:17:32 +0300
Subject: [PATCH 4/8] Store protocol instance size inside protocol structure.
 Make proto_config_new() use this info instead of supplied size.

---
 nest/config.Y         | 2 +-
 nest/proto.c          | 5 ++---
 nest/protocol.h       | 3 ++-
 nest/rt-dev.c         | 1 +
 proto/bfd/bfd.c       | 1 +
 proto/bfd/config.Y    | 2 +-
 proto/bgp/bgp.c       | 1 +
 proto/bgp/config.Y    | 2 +-
 proto/ospf/config.Y   | 2 +-
 proto/ospf/ospf.c     | 1 +
 proto/pipe/config.Y   | 2 +-
 proto/pipe/pipe.c     | 1 +
 proto/radv/config.Y   | 2 +-
 proto/radv/radv.c     | 1 +
 proto/rip/config.Y    | 2 +-
 proto/rip/rip.c       | 1 +
 proto/static/config.Y | 2 +-
 proto/static/static.c | 1 +
 sysdep/unix/krt.c     | 6 ++++--
 19 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/nest/config.Y b/nest/config.Y
index 59a776b..8b69729 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -307,7 +307,7 @@ tos:
 CF_ADDTO(proto, dev_proto '}')
 
 dev_proto_start: proto_start DIRECT {
-     this_proto = proto_config_new(&proto_device, sizeof(struct rt_dev_config), $1);
+     this_proto = proto_config_new(&proto_device, $1);
      init_list(&DIRECT_CFG->iface_list);
    }
  ;
diff --git a/nest/proto.c b/nest/proto.c
index 7a1e9bf..1166573 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -245,7 +245,6 @@ proto_free_ahooks(struct proto *p)
 /**
  * proto_config_new - create a new protocol configuration
  * @pr: protocol the configuration will belong to
- * @size: size of the structure including generic data
  * @class: SYM_PROTO or SYM_TEMPLATE
  *
  * Whenever the configuration file says that a new instance
@@ -262,9 +261,9 @@ proto_free_ahooks(struct proto *p)
  * initialized during protos_commit()).
  */
 void *
-proto_config_new(struct protocol *pr, unsigned size, int class)
+proto_config_new(struct protocol *pr, int class)
 {
-  struct proto_config *c = cfg_allocz(size);
+  struct proto_config *c = cfg_allocz(pr->proto_size);
 
   if (class == SYM_PROTO)
     add_tail(&new_config->protos, &c->n);
diff --git a/nest/protocol.h b/nest/protocol.h
index eb43418..233ffa6 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -41,6 +41,7 @@ struct protocol {
   int attr_class;			/* Attribute class known to this protocol */
   int multitable;			/* Protocol handles all announce hooks itself */
   unsigned preference;			/* Default protocol preference */
+  size_t proto_size;			/* size of protocol instance */
 
   void (*preconfig)(struct protocol *, struct config *);	/* Just before configuring */
   void (*postconfig)(struct proto_config *);			/* After configuring each instance */
@@ -239,7 +240,7 @@ struct proto_spec {
 
 
 void *proto_new(struct proto_config *, unsigned size);
-void *proto_config_new(struct protocol *, unsigned size, int class);
+void *proto_config_new(struct protocol *, int class);
 void proto_copy_config(struct proto_config *dest, struct proto_config *src);
 void proto_request_feeding(struct proto *p);
 
diff --git a/nest/rt-dev.c b/nest/rt-dev.c
index 1a859da..5d22396 100644
--- a/nest/rt-dev.c
+++ b/nest/rt-dev.c
@@ -115,6 +115,7 @@ struct protocol proto_device = {
   name:		"Direct",
   template:	"direct%d",
   preference:	DEF_PREF_DIRECT,
+  proto_size:	sizeof(struct rt_dev_config),
   init:		dev_init,
   reconfigure:	dev_reconfigure,
   copy_config:	dev_copy_config
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
index 23e04e4..bdd27b4 100644
--- a/proto/bfd/bfd.c
+++ b/proto/bfd/bfd.c
@@ -1112,6 +1112,7 @@ bfd_show_sessions(struct proto *P)
 struct protocol proto_bfd = {
   .name =		"BFD",
   .template =		"bfd%d",
+  .proto_size=		sizeof(struct bfd_config),
   .init =		bfd_init,
   .start =		bfd_start,
   .shutdown =		bfd_shutdown,
diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y
index 1b07495..4affb92 100644
--- a/proto/bfd/config.Y
+++ b/proto/bfd/config.Y
@@ -34,7 +34,7 @@ CF_ADDTO(proto, bfd_proto)
 
 bfd_proto_start: proto_start BFD
 {
-  this_proto = proto_config_new(&proto_bfd, sizeof(struct bfd_config), $1);
+  this_proto = proto_config_new(&proto_bfd, $1);
   init_list(&BFD_CFG->patt_list);
   init_list(&BFD_CFG->neigh_list);
 
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 99ec1ce..0fd2b68 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1430,6 +1430,7 @@ struct protocol proto_bgp = {
   template:		"bgp%d",
   attr_class:		EAP_BGP,
   preference:		DEF_PREF_BGP,
+  proto_size:		sizeof(struct bgp_config),
   init:			bgp_init,
   start:		bgp_start,
   shutdown:		bgp_shutdown,
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 8e0b241..d0ae064 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -33,7 +33,7 @@ CF_GRAMMAR
 CF_ADDTO(proto, bgp_proto '}' { bgp_check_config(BGP_CFG); } )
 
 bgp_proto_start: proto_start BGP {
-     this_proto = proto_config_new(&proto_bgp, sizeof(struct bgp_config), $1);
+     this_proto = proto_config_new(&proto_bgp, $1);
      BGP_CFG->multihop = -1;	/* undefined */
      BGP_CFG->hold_time = 240;
      BGP_CFG->connect_retry_time = 120;
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index 478529b..58cc52d 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -143,7 +143,7 @@ CF_GRAMMAR
 CF_ADDTO(proto, ospf_proto '}' { ospf_proto_finish(); } )
 
 ospf_proto_start: proto_start OSPF {
-     this_proto = proto_config_new(&proto_ospf, sizeof(struct ospf_config), $1);
+     this_proto = proto_config_new(&proto_ospf, $1);
      init_list(&OSPF_CFG->area_list);
      init_list(&OSPF_CFG->vlink_list);
      OSPF_CFG->rfc1583 = DEFAULT_RFC1583;
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index 6756ff4..3f5a6a4 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -1590,6 +1590,7 @@ struct protocol proto_ospf = {
   template:		"ospf%d",
   attr_class:		EAP_OSPF,
   preference:		DEF_PREF_OSPF,
+  proto_size:		sizeof(struct ospf_config),
   init:			ospf_init,
   dump:			ospf_dump,
   start:		ospf_start,
diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y
index 4063755..8daf2e7 100644
--- a/proto/pipe/config.Y
+++ b/proto/pipe/config.Y
@@ -23,7 +23,7 @@ CF_GRAMMAR
 CF_ADDTO(proto, pipe_proto '}')
 
 pipe_proto_start: proto_start PIPE {
-     this_proto = proto_config_new(&proto_pipe, sizeof(struct pipe_config), $1);
+     this_proto = proto_config_new(&proto_pipe, $1);
      PIPE_CFG->mode = PIPE_TRANSPARENT;
   }
  ;
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 22381c3..fc9f948 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -340,6 +340,7 @@ struct protocol proto_pipe = {
   template:		"pipe%d",
   multitable:		1,
   preference:		DEF_PREF_PIPE,
+  proto_size:		sizeof(struct pipe_config),
   postconfig:		pipe_postconfig,
   init:			pipe_init,
   start:		pipe_start,
diff --git a/proto/radv/config.Y b/proto/radv/config.Y
index a26ea88..da30066 100644
--- a/proto/radv/config.Y
+++ b/proto/radv/config.Y
@@ -40,7 +40,7 @@ CF_ADDTO(proto, radv_proto)
 
 radv_proto_start: proto_start RADV
 {
-  this_proto = proto_config_new(&proto_radv, sizeof(struct radv_config), $1);
+  this_proto = proto_config_new(&proto_radv, $1);
   init_list(&RADV_CFG->patt_list);
   init_list(&RADV_CFG->pref_list);
   init_list(&RADV_CFG->rdnss_list);
diff --git a/proto/radv/radv.c b/proto/radv/radv.c
index 6be7cd8..eb2a693 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -426,6 +426,7 @@ radv_get_status(struct proto *p, byte *buf)
 struct protocol proto_radv = {
   .name =		"RAdv",
   .template =		"radv%d",
+  .proto_size=		sizeof(struct radv_config),
   .init =		radv_init,
   .start =		radv_start,
   .shutdown =		radv_shutdown,
diff --git a/proto/rip/config.Y b/proto/rip/config.Y
index 7639752..b2b9909 100644
--- a/proto/rip/config.Y
+++ b/proto/rip/config.Y
@@ -43,7 +43,7 @@ CF_GRAMMAR
 CF_ADDTO(proto, rip_cfg '}' { RIP_CFG->passwords = get_passwords(); } )
 
 rip_cfg_start: proto_start RIP {
-     this_proto = proto_config_new(&proto_rip, sizeof(struct rip_proto_config), $1);
+     this_proto = proto_config_new(&proto_rip, $1);
      rip_init_config(RIP_CFG);
    }
  ;
diff --git a/proto/rip/rip.c b/proto/rip/rip.c
index bc9ffc5..85f1d93 100644
--- a/proto/rip/rip.c
+++ b/proto/rip/rip.c
@@ -1050,6 +1050,7 @@ struct protocol proto_rip = {
   template: "rip%d",
   attr_class: EAP_RIP,
   preference: DEF_PREF_RIP,
+  proto_size: sizeof(struct rip_proto_config),
   get_route_info: rip_get_route_info,
   get_attr: rip_get_attr,
 
diff --git a/proto/static/config.Y b/proto/static/config.Y
index 2d9d4b4..a8bfa36 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -26,7 +26,7 @@ CF_GRAMMAR
 CF_ADDTO(proto, static_proto '}')
 
 static_proto_start: proto_start STATIC {
-     this_proto = proto_config_new(&proto_static, sizeof(struct static_config), $1);
+     this_proto = proto_config_new(&proto_static, $1);
      static_init_config((struct static_config *) this_proto);
   }
  ;
diff --git a/proto/static/static.c b/proto/static/static.c
index d3a595d..7d571b3 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -531,6 +531,7 @@ struct protocol proto_static = {
   name:		"Static",
   template:	"static%d",
   preference:	DEF_PREF_STATIC,
+  proto_size:	sizeof(struct static_config),
   init:		static_init,
   dump:		static_dump,
   start:	static_start,
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index a2fb83d..6aa264f 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -251,7 +251,7 @@ kif_init_config(int class)
   if (kif_cf)
     cf_error("Kernel device protocol already defined");
 
-  kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, sizeof(struct kif_config), class);
+  kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, class);
   kif_cf->scan_time = 60;
   init_list(&kif_cf->primary);
 
@@ -280,6 +280,7 @@ struct protocol proto_unix_iface = {
   name:		"Device",
   template:	"device%d",
   preference:	DEF_PREF_DIRECT,
+  proto_size:	sizeof(struct kif_config),
   preconfig:	kif_preconfig,
   init:		kif_init,
   start:	kif_start,
@@ -1150,7 +1151,7 @@ krt_init_config(int class)
     cf_error("Kernel protocol already defined");
 #endif
 
-  krt_cf = (struct krt_config *) proto_config_new(&proto_unix_kernel, sizeof(struct krt_config), class);
+  krt_cf = (struct krt_config *) proto_config_new(&proto_unix_kernel, class);
   krt_cf->scan_time = 60;
 
   krt_sys_init_config(krt_cf);
@@ -1202,6 +1203,7 @@ struct protocol proto_unix_kernel = {
   template:	"kernel%d",
   attr_class:	EAP_KRT,
   preference:	DEF_PREF_INHERITED,
+  proto_size:	sizeof(struct krt_config),
   preconfig:	krt_preconfig,
   postconfig:	krt_postconfig,
   init:		krt_init,
-- 
2.1.2

From d0f8857438476b456690bc7608b472a2569b697d Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <[email protected]>
Date: Thu, 12 Feb 2015 02:13:54 +0300
Subject: [PATCH 5/8] * Limit scope of new_config and cfg_mem variables:
 new_config is not exported anymore, direct cfg_mem usage signifficantly
 reduced. * Be more accurate in global variables assignment: use
 config_start_edit() along with config_stop_edit() to set/clear new_config and
 cfg_mem.

---
 conf/cf-lex.l     | 71 ++++++++++++++++++++++++++++---------------------------
 conf/conf.c       | 65 +++++++++++++++++++++++++++++++++-----------------
 conf/conf.h       | 18 +++++++++-----
 conf/confbase.Y   |  4 ++--
 filter/config.Y   | 24 +++++++++----------
 filter/filter.c   |  4 ++--
 filter/filter.h   |  6 +++--
 filter/tree.c     |  4 ++--
 nest/config.Y     | 15 +++++++-----
 nest/proto.c      | 17 ++++++-------
 nest/protocol.h   |  2 +-
 nest/route.h      |  6 ++---
 nest/rt-roa.c     | 14 +++++------
 nest/rt-table.c   | 16 ++++++-------
 sysdep/unix/krt.Y |  4 ++--
 sysdep/unix/krt.c |  8 +++----
 sysdep/unix/krt.h |  4 ++--
 17 files changed, 158 insertions(+), 124 deletions(-)

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 57e0bfa..b995e10 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -70,9 +70,7 @@ struct sym_scope {
 static struct sym_scope *conf_this_scope;
 
 static int cf_hash(byte *c);
-static struct symbol *cf_find_sym(byte *c, unsigned int h0);
-
-linpool *cfg_mem;
+static struct symbol *cf_find_sym(struct config *c, byte *str, unsigned int h0);
 
 int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
 struct include_file_stack *ifs;
@@ -192,7 +190,7 @@ else: {
 	}
       k=k->next;
     }
-  cf_lval.s = cf_find_sym(yytext, h);
+  cf_lval.s = cf_find_sym(config_get_ptr_new(), yytext, h);
   return SYM;
 }
 
@@ -211,7 +209,7 @@ else: {
 
 ["][^"\n]*["] {
   yytext[yyleng-1] = 0;
-  cf_lval.t = cfg_strdup(yytext+1);
+  cf_lval.t = cfg_strdup(config_get_ptr_new(), yytext+1);
   return TEXT;
 }
 
@@ -277,10 +275,10 @@ cf_hash(byte *c)
  */
 
 static struct include_file_stack *
-push_ifs(struct include_file_stack *old)
+push_ifs(struct config *c, struct include_file_stack *old)
 {
   struct include_file_stack *ret;
-  ret = cfg_allocz(sizeof(struct include_file_stack));
+  ret = cfl_allocz(c, sizeof(struct include_file_stack));
   ret->lino = 1;
   ret->prev = old;
   return ret;
@@ -341,8 +339,11 @@ cf_include(char *arg, int alen)
   struct include_file_stack *base_ifs = ifs;
   int new_depth, rv, i;
   char *patt;
+  struct config *new;
   glob_t g = {};
 
+  new = config_get_ptr_new();
+
   new_depth = ifs->depth + 1;
   if (new_depth > MAX_INCLUDE_DEPTH)
     cf_error("Max include depth reached");
@@ -363,8 +364,8 @@ cf_include(char *arg, int alen)
      response when the included config file is missing */
   if (!strpbrk(arg, "?*["))
     {
-      ifs = push_ifs(ifs);
-      ifs->file_name = cfg_strdup(patt);
+      ifs = push_ifs(new, ifs);
+      ifs->file_name = cfg_strdup(new, patt);
       ifs->depth = new_depth;
       ifs->up = base_ifs;
       enter_ifs(ifs);
@@ -398,8 +399,8 @@ cf_include(char *arg, int alen)
         continue;
 
       /* Prepare new stack item */
-      ifs = push_ifs(ifs);
-      ifs->file_name = cfg_strdup(fname);
+      ifs = push_ifs(new, ifs);
+      ifs->file_name = cfg_strdup(new, fname);
       ifs->depth = new_depth;
       ifs->up = base_ifs;
     }
@@ -424,48 +425,48 @@ check_eof(void)
 }
 
 static struct symbol *
-cf_new_sym(byte *c, unsigned int h)
+cf_new_sym(struct config *c, byte *str, unsigned int h)
 {
   struct symbol *s, **ht;
   int l;
 
-  if (!new_config->sym_hash)
-    new_config->sym_hash = cfg_allocz(SYM_HASH_SIZE * sizeof(struct keyword *));
-  ht = new_config->sym_hash;
-  l = strlen(c);
+  if (!c->sym_hash)
+    c->sym_hash = cfl_allocz(c, SYM_HASH_SIZE * sizeof(struct keyword *));
+  ht = c->sym_hash;
+  l = strlen(str);
   if (l > SYM_MAX_LEN)
     cf_error("Symbol too long");
-  s = cfg_alloc(sizeof(struct symbol) + l);
+  s = cfl_alloc(c, sizeof(struct symbol) + l);
   s->next = ht[h];
   ht[h] = s;
   s->scope = conf_this_scope;
   s->class = SYM_VOID;
   s->def = NULL;
   s->aux = 0;
-  strcpy(s->name, c);
+  strcpy(s->name, str);
   return s;
 }
 
 static struct symbol *
-cf_find_sym(byte *c, unsigned int h0)
+cf_find_sym(struct config *c, byte *str, unsigned int h0)
 {
   unsigned int h = h0 & (SYM_HASH_SIZE-1);
   struct symbol *s, **ht;
 
-  if (ht = new_config->sym_hash)
+  if (ht = c->sym_hash)
     {
       for(s = ht[h]; s; s=s->next)
-	if (!strcmp(s->name, c) && s->scope->active)
+	if (!strcmp(s->name, str) && s->scope->active)
 	  return s;
     }
-  if (new_config->sym_fallback)
+  if (c->sym_fallback)
     {
       /* We know only top-level scope is active */
-      for(s = new_config->sym_fallback[h]; s; s=s->next)
-	if (!strcmp(s->name, c) && s->scope->active)
+      for(s = c->sym_fallback[h]; s; s=s->next)
+	if (!strcmp(s->name, str) && s->scope->active)
 	  return s;
     }
-  return cf_new_sym(c, h);
+  return cf_new_sym(c, str, h);
 }
 
 /**
@@ -479,13 +480,13 @@ cf_find_sym(byte *c, unsigned int h0)
  * and returns %NULL to signify no match.
  */
 struct symbol *
-cf_find_symbol(byte *c)
+cf_find_symbol(struct config *c, byte *str)
 {
-  return cf_find_sym(c, cf_hash(c));
+  return cf_find_sym(c, str, cf_hash(str));
 }
 
 struct symbol *
-cf_default_name(char *template, int *counter)
+cf_default_name(struct config *c, char *template, int *counter)
 {
   char buf[32];
   struct symbol *s;
@@ -494,7 +495,7 @@ cf_default_name(char *template, int *counter)
   for(;;)
     {
       bsprintf(buf, template, ++(*counter));
-      s = cf_find_sym(buf, cf_hash(buf));
+      s = cf_find_sym(c, buf, cf_hash(buf));
       if (!s)
 	break;
       if (s->class == SYM_VOID)
@@ -521,13 +522,13 @@ cf_default_name(char *template, int *counter)
  * scope, it's the same @sym as passed to the function.
  */
 struct symbol *
-cf_define_symbol(struct symbol *sym, int type, void *def)
+cf_define_symbol(struct config *c, struct symbol *sym, int type, void *def)
 {
   if (sym->class)
     {
       if (sym->scope == conf_this_scope)
 	cf_error("Symbol already defined");
-      sym = cf_new_sym(sym->name, cf_hash(sym->name) & (SYM_HASH_SIZE-1));
+      sym = cf_new_sym(c, sym->name, cf_hash(sym->name) & (SYM_HASH_SIZE-1));
     }
   sym->class = type;
   sym->def = def;
@@ -561,7 +562,7 @@ cf_lex_init(int is_cli, struct config *c)
   if (!kw_hash_inited)
     cf_lex_init_kh();
 
-  ifs_head = ifs = push_ifs(NULL);
+  ifs_head = ifs = push_ifs(c, NULL);
   if (!is_cli) 
     {
       ifs->file_name = c->file_name;
@@ -577,7 +578,7 @@ cf_lex_init(int is_cli, struct config *c)
   else
     BEGIN(INITIAL);
 
-  conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
+  conf_this_scope = cfl_allocz(c, sizeof(struct sym_scope));
   conf_this_scope->active = 1;
 }
 
@@ -592,9 +593,9 @@ cf_lex_init(int is_cli, struct config *c)
  * in all scopes stored on the stack.
  */
 void
-cf_push_scope(struct symbol *sym)
+cf_push_scope(struct config *c, struct symbol *sym)
 {
-  struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope));
+  struct sym_scope *s = cfl_alloc(c, sizeof(struct sym_scope));
 
   s->next = conf_this_scope;
   conf_this_scope = s;
diff --git a/conf/conf.c b/conf/conf.c
index a907402..9f93813 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -59,6 +59,7 @@
 static jmp_buf conf_jmpbuf;
 
 struct config *config, *new_config;
+linpool *cfg_mem;
 
 static struct config *old_config;	/* Old configuration */
 static struct config *future_config;	/* New config held here if recon requested during recon */
@@ -93,8 +94,8 @@ config_alloc(byte *name)
 
   c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */
   c->pool = p;
-  cfg_mem = c->mem = l;
-  c->file_name = cfg_strdup(name);
+  c->mem = l;
+  c->file_name = cfg_strdup(c, name);
   c->load_time = now;
   c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600};
   c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0};
@@ -120,10 +121,12 @@ int
 config_parse(struct config *c)
 {
   DBG("Parsing configuration file `%s'\n", c->file_name);
-  new_config = c;
-  cfg_mem = c->mem;
+  config_start_edit(c);
   if (setjmp(conf_jmpbuf))
-    return 0;
+    {
+      config_stop_edit();
+      return 0;
+    }
   cf_lex_init(0, c);
   sysdep_preconfig(c);
   protos_preconfig(c);
@@ -137,6 +140,7 @@ config_parse(struct config *c)
   if (!c->router_id)
     cf_error("Router ID must be configured manually on IPv6 routers");
 #endif
+  config_stop_edit();
   return 1;
 }
 
@@ -150,13 +154,16 @@ config_parse(struct config *c)
 int
 cli_parse(struct config *c)
 {
-  new_config = c;
+  config_start_edit(c);
   c->sym_fallback = config->sym_hash;
-  cfg_mem = c->mem;
   if (setjmp(conf_jmpbuf))
-    return 0;
+    {
+      config_stop_edit();
+      return 0;
+    }
   cf_lex_init(1, c);
   cf_parse();
+  config_stop_edit();
   return 1;
 }
 
@@ -237,10 +244,6 @@ config_do_commit(struct config *c, int type)
   if (old_config && !config->shutdown)
     log(L_INFO "Reconfiguring");
 
-  /* This should not be necessary, but it seems there are some
-     functions that access new_config instead of config */
-  new_config = config;
-
   if (old_config)
     old_config->obstacle_count++;
 
@@ -254,9 +257,6 @@ config_do_commit(struct config *c, int type)
   DBG("protos_commit\n");
   protos_commit(c, old_config, force_restart, type);
 
-  /* Just to be sure nobody uses that now */
-  new_config = NULL;
-
   int obs = 0;
   if (old_config)
     obs = --old_config->obstacle_count;
@@ -288,6 +288,26 @@ config_done(void *unused UNUSED)
     }
 }
 
+void
+config_start_edit(struct config *c)
+{
+  new_config = c;
+  cfg_mem = c->mem;
+}
+
+void
+config_stop_edit()
+{
+  new_config = NULL;
+  cfg_mem = NULL;
+}
+
+struct config *
+config_get_ptr_new(void)
+{
+  return new_config;
+}
+
 /**
  * config_commit - commit a configuration
  * @c: new configuration
@@ -499,7 +519,7 @@ cf_error(char *msg, ...)
   va_start(args, msg);
   if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
     strcpy(buf, "<bug: error message too long>");
-  new_config->err_msg = cfg_strdup(buf);
+  new_config->err_msg = cfg_strdup(new_config, buf);
   new_config->err_lino = ifs->lino;
   new_config->err_file_name = ifs->file_name;
   cf_lex_unwind();
@@ -508,19 +528,20 @@ cf_error(char *msg, ...)
 
 /**
  * cfg_strdup - copy a string to config memory
- * @c: string to copy
+ * @c: pointer to config
+ * @str: string to copy
  *
  * cfg_strdup() creates a new copy of the string in the memory
- * pool associated with the configuration being currently parsed.
+ * pool @c.
  * It's often used when a string literal occurs in the configuration
  * and we want to preserve it for further use.
  */
 char *
-cfg_strdup(char *c)
+cfg_strdup(struct config *c, char *str)
 {
-  int l = strlen(c) + 1;
-  char *z = cfg_allocu(l);
-  memcpy(z, c, l);
+  int l = strlen(str) + 1;
+  char *z = cfl_allocu(c, l);
+  memcpy(z, str, l);
   return z;
 }
 
diff --git a/conf/conf.h b/conf/conf.h
index 3e641ce..eb8acb5 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -55,7 +55,6 @@ struct config {
 
 /* Please don't use these variables in protocols. Use proto_config->global instead. */
 extern struct config *config;		/* Currently active configuration */
-extern struct config *new_config;	/* Configuration being parsed */
 
 struct config *config_alloc(byte *name);
 int config_parse(struct config *);
@@ -64,6 +63,8 @@ void config_free(struct config *);
 int config_commit(struct config *, int type, int timeout);
 int config_confirm(void);
 int config_undo(void);
+void config_start_edit(struct config *);
+void config_stop_edit(void);
 void config_init(void);
 void cf_error(char *msg, ...) NORET;
 void config_add_obstacle(struct config *);
@@ -91,8 +92,13 @@ extern linpool *cfg_mem;
 #define cfg_alloc(size) lp_alloc(cfg_mem, size)
 #define cfg_allocu(size) lp_allocu(cfg_mem, size)
 #define cfg_allocz(size) lp_allocz(cfg_mem, size)
-char *cfg_strdup(char *c);
+char *cfg_strdup(struct config *c, char *str);
 void cfg_copy_list(list *dest, list *src, unsigned node_size);
+struct config * config_get_ptr_new(void);
+
+#define cfl_alloc(c, size) lp_alloc((c)->mem, size)
+#define cfl_allocz(c, size) lp_allocz((c)->mem, size)
+#define cfl_allocu(c, size) lp_allocu((c)->mem, size)
 
 /* Lexer */
 
@@ -143,10 +149,10 @@ int cf_lex(void);
 void cf_lex_init(int is_cli, struct config *c);
 void cf_lex_unwind(void);
 
-struct symbol *cf_find_symbol(byte *c);
-struct symbol *cf_default_name(char *template, int *counter);
-struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def);
-void cf_push_scope(struct symbol *);
+struct symbol *cf_find_symbol(struct config *c, byte *str);
+struct symbol *cf_default_name(struct config *c, char *template, int *counter);
+struct symbol *cf_define_symbol(struct config *c, struct symbol *symbol, int type, void *def);
+void cf_push_scope(struct config *c, struct symbol *);
 void cf_pop_scope(void);
 struct symbol *cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos);
 char *cf_symbol_class_name(struct symbol *sym);
diff --git a/conf/confbase.Y b/conf/confbase.Y
index 49831b1..5f209a0 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -114,13 +114,13 @@ definition:
      struct f_val *val = cfg_alloc(sizeof(struct f_val));
      *val = f_eval($4, cfg_mem);
      if (val->type == T_RETURN) cf_error("Runtime error");
-     cf_define_symbol($2, SYM_CONSTANT | val->type, val);
+     cf_define_symbol(new_config, $2, SYM_CONSTANT | val->type, val);
    }
  ;
 
 expr:
    NUM
- | '(' term ')' { $$ = f_eval_int($2); }
+ | '(' term ')' { $$ = f_eval_int($2, cfg_mem); }
  | SYM {
      if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number expected");
      $$ = SYM_VAL($1).i; }
diff --git a/filter/config.Y b/filter/config.Y
index 7eb2c0a..0a338e5 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -46,7 +46,7 @@ f_valid_set_type(int type)
 static inline struct f_tree *
 f_new_item(struct f_val from, struct f_val to)
 {
-  struct f_tree *t = f_new_tree();
+  struct f_tree *t = f_new_tree(new_config);
   t->right = t;
   t->from = from;
   t->to = to;
@@ -66,7 +66,7 @@ f_merge_items(struct f_tree *a, struct f_tree *b)
 static inline struct f_tree *
 f_new_pair_item(int fa, int ta, int fb, int tb)
 {
-  struct f_tree *t = f_new_tree();
+  struct f_tree *t = f_new_tree(new_config);
   t->right = t;
   t->from.type = t->to.type = T_PAIR;
   t->from.val.i = pair(fa, fb);
@@ -124,7 +124,7 @@ f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
     to = ec_as4(kind, key, vt);
   }
 
-  struct f_tree *t = f_new_tree();
+  struct f_tree *t = f_new_tree(new_config);
   t->right = t;
   t->from.type = t->to.type = T_EC;
   t->from.val.ec = fm;
@@ -305,7 +305,7 @@ CF_GRAMMAR
 
 CF_ADDTO(conf, filter_def)
 filter_def:
-   FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); }
+   FILTER SYM { $2 = cf_define_symbol(new_config, $2, SYM_FILTER, NULL); cf_push_scope( new_config, $2 ); }
      filter_body {
      $2->def = $4;
      $4->name = $2->name;
@@ -316,7 +316,7 @@ filter_def:
 
 CF_ADDTO(conf, filter_eval)
 filter_eval:
-   EVAL term { f_eval_int($2); }
+   EVAL term { f_eval_int($2, cfg_mem); }
  ;
 
 type:
@@ -356,7 +356,7 @@ one_decl:
    type SYM {
      struct f_val * val = cfg_alloc(sizeof(struct f_val));
      val->type = T_VOID;
-     $2 = cf_define_symbol($2, SYM_VARIABLE | $1, val);
+     $2 = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, val);
      DBG( "New variable %s type %x\n", $2->name, $1 );
      $2->aux2 = NULL;
      $$=$2;
@@ -441,8 +441,8 @@ function_body:
 CF_ADDTO(conf, function_def)
 function_def:
    FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
-     $2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
-     cf_push_scope($2);
+     $2 = cf_define_symbol(new_config, $2, SYM_FUNCTION, NULL);
+     cf_push_scope(new_config, $2);
    } function_params function_body {
      $2->def = $5;
      $2->aux2 = $4;
@@ -507,14 +507,14 @@ set_atom:
 
 switch_atom:
    NUM   { $$.type = T_INT; $$.val.i = $1; }
- | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int($2); }
+ | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int($2, cfg_mem); }
  | RTRID { $$.type = T_QUAD; $$.val.i = $1; }
  | fipa  { $$ = $1; }
  | ENUM  { $$.type = pair_a($1); $$.val.i = pair_b($1); }
  ;
 
 pair_expr:
-   term { $$ = f_eval_int($1); check_u16($$); }
+   term { $$ = f_eval_int($1, cfg_mem); check_u16($$); }
 
 pair_atom:
    pair_expr { $$ = pair($1, $1); }
@@ -535,7 +535,7 @@ pair_item:
  ;
 
 ec_expr:
-   term { $$ = f_eval_int($1); }
+   term { $$ = f_eval_int($1, cfg_mem); }
 
 ec_kind:
    RT { $$ = EC_RT; }
@@ -605,7 +605,7 @@ switch_body: /* EMPTY */ { $$ = NULL; }
      $$ = f_merge_items($1, $2);
    }
  | switch_body ELSECOL cmds { 
-     struct f_tree *t = f_new_tree();
+     struct f_tree *t = f_new_tree(new_config);
      t->from.type = t->to.type = T_VOID;
      t->right = t;
      t->data = $3;
diff --git a/filter/filter.c b/filter/filter.c
index 63c2cd8..ac256f3 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -1513,10 +1513,10 @@ f_eval(struct f_inst *expr, struct linpool *tmp_pool)
 }
 
 uint
-f_eval_int(struct f_inst *expr)
+f_eval_int(struct f_inst *expr, struct linpool *tmp_pool)
 {
   /* Called independently in parse-time to eval expressions */
-  struct f_val res = f_eval(expr, cfg_mem);
+  struct f_val res = f_eval(expr, tmp_pool);
 
   if (res.type != T_INT)
     cf_error("Integer expression expected");
diff --git a/filter/filter.h b/filter/filter.h
index 522af9f..23385cf 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -68,9 +68,11 @@ struct filter {
   struct f_inst *root;
 };
 
+struct config;
+
 struct f_inst *f_new_inst(void);
 struct f_inst *f_new_dynamic_attr(int type, int f_type, int code);	/* Type as core knows it, type as filters know it, and code of dynamic attribute */
-struct f_tree *f_new_tree(void);
+struct f_tree *f_new_tree(struct config *);
 struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument);
 struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn);
 
@@ -110,7 +112,7 @@ struct rte;
 
 int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struct linpool *tmp_pool, int flags);
 struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
-uint f_eval_int(struct f_inst *expr);
+uint f_eval_int(struct f_inst *expr, struct linpool *tmp_pool);
 u32 f_eval_asn(struct f_inst *expr);
 
 char *filter_name(struct filter *filter);
diff --git a/filter/tree.c b/filter/tree.c
index ee9f448..5910398 100644
--- a/filter/tree.c
+++ b/filter/tree.c
@@ -100,10 +100,10 @@ build_tree(struct f_tree *from)
 }
 
 struct f_tree *
-f_new_tree(void)
+f_new_tree(struct config *c)
 {
   struct f_tree * ret;
-  ret = cfg_alloc(sizeof(struct f_tree));
+  ret = cfl_alloc(c, sizeof(struct f_tree));
   ret->left = ret->right = NULL;
   ret->from.type = ret->to.type = T_VOID;
   ret->from.val.i = ret->to.val.i = 0;
diff --git a/nest/config.Y b/nest/config.Y
index 8b69729..481d9f5 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -16,6 +16,9 @@ CF_HDR
 
 CF_DEFINES
 
+extern struct config *new_config; /* Configuration being parsed */
+#define proto_config_new(pr, cl)	proto_create_config(new_config, pr, cl)
+
 static struct proto_config *this_proto;
 static struct iface_patt *this_ipatt;
 static struct iface_patt_node *this_ipn;
@@ -138,7 +141,7 @@ CF_ADDTO(conf, newtab)
 
 newtab: TABLE SYM tab_sorted {
    struct rtable_config *cf;
-   cf = rt_new_table($2);
+   cf = rt_new_table(new_config, $2);
    cf->sorted = $3;
    }
  ;
@@ -146,13 +149,13 @@ newtab: TABLE SYM tab_sorted {
 CF_ADDTO(conf, roa_table)
 
 roa_table_start: ROA TABLE SYM {
-  this_roa_table = roa_new_table_config($3);
+  this_roa_table = roa_new_table_config(new_config, $3);
 };
 
 roa_table_opts:
    /* empty */
  | roa_table_opts ROA prefix MAX NUM AS NUM ';' {
-     roa_add_item_config(this_roa_table, $3.addr, $3.len, $5, $7);
+     roa_add_item_config(new_config, this_roa_table, $3.addr, $3.len, $5, $7);
    }
  ;
 
@@ -172,19 +175,19 @@ proto_start:
 
 proto_name:
    /* EMPTY */ {
-     struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
+     struct symbol *s = cf_default_name(new_config, this_proto->protocol->template, &this_proto->protocol->name_counter);
      s->class = this_proto->class;
      s->def = this_proto;
      this_proto->name = s->name;
      }
  | SYM {
-     cf_define_symbol($1, this_proto->class, this_proto);
+     cf_define_symbol(new_config, $1, this_proto->class, this_proto);
      this_proto->name = $1->name;
    }
  | SYM FROM SYM {
      if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
 
-     cf_define_symbol($1, this_proto->class, this_proto);
+     cf_define_symbol(new_config, $1, this_proto->class, this_proto);
      this_proto->name = $1->name;
 
      proto_copy_config(this_proto, $3->def);
diff --git a/nest/proto.c b/nest/proto.c
index 1166573..846d172 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -243,7 +243,8 @@ proto_free_ahooks(struct proto *p)
 
 
 /**
- * proto_config_new - create a new protocol configuration
+ * proto_create_config - create a new protocol configuration
+ * @new: new config instance
  * @pr: protocol the configuration will belong to
  * @class: SYM_PROTO or SYM_TEMPLATE
  *
@@ -261,21 +262,21 @@ proto_free_ahooks(struct proto *p)
  * initialized during protos_commit()).
  */
 void *
-proto_config_new(struct protocol *pr, int class)
+proto_create_config(struct config *new, struct protocol *pr, int class)
 {
-  struct proto_config *c = cfg_allocz(pr->proto_size);
+  struct proto_config *c = cfl_allocz(new, pr->proto_size);
 
   if (class == SYM_PROTO)
-    add_tail(&new_config->protos, &c->n);
-  c->global = new_config;
+    add_tail(&new->protos, &c->n);
+  c->global = new;
   c->protocol = pr;
   c->name = pr->name;
   c->preference = pr->preference;
   c->class = class;
   c->out_filter = FILTER_REJECT;
   c->table = c->global->master_rtc;
-  c->debug = new_config->proto_default_debug;
-  c->mrtdump = new_config->proto_default_mrtdump;
+  c->debug = new->proto_default_debug;
+  c->mrtdump = new->proto_default_mrtdump;
   return c;
 }
 
@@ -521,7 +522,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
       WALK_LIST(oc, old->protos)
 	{
 	  p = oc->proto;
-	  sym = cf_find_symbol(oc->name);
+	  sym = cf_find_symbol(new, oc->name);
 	  if (sym && sym->class == SYM_PROTO && !new->shutdown)
 	    {
 	      /* Found match, let's check if we can smoothly switch to new configuration */
diff --git a/nest/protocol.h b/nest/protocol.h
index 233ffa6..a20b053 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -240,7 +240,7 @@ struct proto_spec {
 
 
 void *proto_new(struct proto_config *, unsigned size);
-void *proto_config_new(struct protocol *, int class);
+void *proto_create_config(struct config *, struct protocol *, int class);
 void proto_copy_config(struct proto_config *dest, struct proto_config *src);
 void proto_request_feeding(struct proto *p);
 
diff --git a/nest/route.h b/nest/route.h
index 5ee04a3..c38df41 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -274,7 +274,7 @@ void rt_dump_all(void);
 int rt_feed_baby(struct proto *p);
 void rt_feed_baby_abort(struct proto *p);
 int rt_prune_loop(void);
-struct rtable_config *rt_new_table(struct symbol *s);
+struct rtable_config *rt_new_table(struct config *c, struct symbol *s);
 
 static inline void
 rt_mark_for_prune(rtable *tab)
@@ -591,8 +591,8 @@ void roa_add_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen,
 void roa_delete_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src);
 void roa_flush(struct roa_table *t, byte src);
 byte roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn);
-struct roa_table_config * roa_new_table_config(struct symbol *s);
-void roa_add_item_config(struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn);
+struct roa_table_config * roa_new_table_config(struct config *c, struct symbol *s);
+void roa_add_item_config(struct config *c, struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn);
 void roa_init(void);
 void roa_preconfig(struct config *c);
 void roa_commit(struct config *new, struct config *old);
diff --git a/nest/rt-roa.c b/nest/rt-roa.c
index aa453f1..1c28bdd 100644
--- a/nest/rt-roa.c
+++ b/nest/rt-roa.c
@@ -240,13 +240,13 @@ roa_new_table(struct roa_table_config *cf)
 }
 
 struct roa_table_config *
-roa_new_table_config(struct symbol *s)
+roa_new_table_config(struct config *c, struct symbol *s)
 {
-  struct roa_table_config *rtc = cfg_allocz(sizeof(struct roa_table_config));
+  struct roa_table_config *rtc = cfl_allocz(c, sizeof(struct roa_table_config));
 
-  cf_define_symbol(s, SYM_ROA, rtc);
+  cf_define_symbol(c, s, SYM_ROA, rtc);
   rtc->name = s->name;
-  add_tail(&new_config->roa_tables, &rtc->n);
+  add_tail(&c->roa_tables, &rtc->n);
   return rtc;
 }
 
@@ -257,9 +257,9 @@ roa_new_table_config(struct symbol *s)
  * are specifying the ROA entry.
  */
 void
-roa_add_item_config(struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn)
+roa_add_item_config(struct config *c, struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn)
 {
-  struct roa_item_config *ric = cfg_allocz(sizeof(struct roa_item_config));
+  struct roa_item_config *ric = cfl_allocz(c, sizeof(struct roa_item_config));
 
   ric->prefix = prefix;
   ric->pxlen = pxlen;
@@ -311,7 +311,7 @@ roa_commit(struct config *new, struct config *old)
   if (old)
     WALK_LIST(t, roa_table_list)
       {
-	struct symbol *sym = cf_find_symbol(t->name);
+	struct symbol *sym = cf_find_symbol(new, t->name);
 	if (sym && sym->class == SYM_ROA)
 	  {
 	    /* Found old table in new config */
diff --git a/nest/rt-table.c b/nest/rt-table.c
index dbe0c50..17ca98e 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -1491,10 +1491,10 @@ rt_prune_loop(void)
 void
 rt_preconfig(struct config *c)
 {
-  struct symbol *s = cf_find_symbol("master");
+  struct symbol *s = cf_find_symbol(c, "master");
 
   init_list(&c->tables);
-  c->master_rtc = rt_new_table(s);
+  c->master_rtc = rt_new_table(c, s);
 }
 
 
@@ -1648,17 +1648,17 @@ rt_next_hop_update(rtable *tab)
 
 
 struct rtable_config *
-rt_new_table(struct symbol *s)
+rt_new_table(struct config *new, struct symbol *s)
 {
   /* Hack that allows to 'redefine' the master table */
-  if ((s->class == SYM_TABLE) && (s->def == new_config->master_rtc))
+  if ((s->class == SYM_TABLE) && (s->def == new->master_rtc))
     return s->def;
 
-  struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config));
+  struct rtable_config *c = cfl_allocz(new, sizeof(struct rtable_config));
 
-  cf_define_symbol(s, SYM_TABLE, c);
+  cf_define_symbol(new, s, SYM_TABLE, c);
   c->name = s->name;
-  add_tail(&new_config->tables, &c->n);
+  add_tail(&new->tables, &c->n);
   c->gc_max_ops = 1000;
   c->gc_min_time = 5;
   return c;
@@ -1728,7 +1728,7 @@ rt_commit(struct config *new, struct config *old)
 	  rtable *ot = o->table;
 	  if (!ot->deleted)
 	    {
-	      struct symbol *sym = cf_find_symbol(o->name);
+	      struct symbol *sym = cf_find_symbol(new, o->name);
 	      if (sym && sym->class == SYM_TABLE && !new->shutdown)
 		{
 		  DBG("\t%s: same\n", o->name);
diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y
index 630cda3..d1bacb6 100644
--- a/sysdep/unix/krt.Y
+++ b/sysdep/unix/krt.Y
@@ -25,7 +25,7 @@ CF_GRAMMAR
 
 CF_ADDTO(proto, kern_proto '}')
 
-kern_proto_start: proto_start KERNEL { this_proto = krt_init_config($1); }
+kern_proto_start: proto_start KERNEL { this_proto = krt_init_config(new_config, $1); }
  ;
 
 CF_ADDTO(kern_proto, kern_proto_start proto_name '{')
@@ -53,7 +53,7 @@ kern_item:
 
 CF_ADDTO(proto, kif_proto '}')
 
-kif_proto_start: proto_start DEVICE { this_proto = kif_init_config($1); }
+kif_proto_start: proto_start DEVICE { this_proto = kif_init_config(new_config, $1); }
  ;
 
 CF_ADDTO(kif_proto, kif_proto_start proto_name '{')
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index 6aa264f..d342f7b 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -246,12 +246,12 @@ kif_preconfig(struct protocol *P UNUSED, struct config *c)
 }
 
 struct proto_config *
-kif_init_config(int class)
+kif_init_config(struct config *c, int class)
 {
   if (kif_cf)
     cf_error("Kernel device protocol already defined");
 
-  kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, class);
+  kif_cf = (struct kif_config *) proto_create_config(c, &proto_unix_iface, class);
   kif_cf->scan_time = 60;
   init_list(&kif_cf->primary);
 
@@ -1144,14 +1144,14 @@ krt_postconfig(struct proto_config *C)
 }
 
 struct proto_config *
-krt_init_config(int class)
+krt_init_config(struct config *c, int class)
 {
 #ifndef CONFIG_MULTIPLE_TABLES
   if (krt_cf)
     cf_error("Kernel protocol already defined");
 #endif
 
-  krt_cf = (struct krt_config *) proto_config_new(&proto_unix_kernel, class);
+  krt_cf = (struct krt_config *) proto_create_config(c, &proto_unix_kernel, class);
   krt_cf->scan_time = 60;
 
   krt_sys_init_config(krt_cf);
diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h
index 2cd2316..fa4d25a 100644
--- a/sysdep/unix/krt.h
+++ b/sysdep/unix/krt.h
@@ -77,7 +77,7 @@ extern pool *krt_pool;
   if (pr->p.debug & fl)				\
     { log(L_TRACE "%s: " msg, pr->p.name , ## args); } } while(0)
 
-struct proto_config * kif_init_config(int class);
+struct proto_config * kif_init_config(struct config *, int class);
 void kif_request_scan(void);
 void krt_got_route(struct krt_proto *p, struct rte *e);
 void krt_got_route_async(struct krt_proto *p, struct rte *e, int new);
@@ -112,7 +112,7 @@ struct kif_proto {
 
 #define KIF_CF ((struct kif_config *)p->p.cf)
 
-struct proto_config * krt_init_config(int class);
+struct proto_config * krt_init_config(struct config *, int class);
 
 
 /* krt sysdep */
-- 
2.1.2

From 071677ee93e42c3097f1c3acb5ad7fc73eaf2509 Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <[email protected]>
Date: Fri, 13 Feb 2015 13:11:07 +0300
Subject: [PATCH 6/8] * Pass struct config to cfg_copy_list to specify where to
 alloc memory * from

---
 conf/conf.c       | 4 ++--
 conf/conf.h       | 2 +-
 nest/rt-dev.c     | 2 +-
 proto/radv/radv.c | 2 +-
 sysdep/unix/krt.c | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/conf/conf.c b/conf/conf.c
index 9f93813..fd619f9 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -547,14 +547,14 @@ cfg_strdup(struct config *c, char *str)
 
 
 void
-cfg_copy_list(list *dest, list *src, unsigned node_size)
+cfg_copy_list(struct config *c, list *dest, list *src, unsigned node_size)
 {
   node *dn, *sn;
 
   init_list(dest);
   WALK_LIST(sn, *src)
   {
-    dn = cfg_alloc(node_size);
+    dn = cfl_alloc(c, node_size);
     memcpy(dn, sn, node_size);
     add_tail(dest, dn);
   }
diff --git a/conf/conf.h b/conf/conf.h
index eb8acb5..71da2a1 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -93,7 +93,7 @@ extern linpool *cfg_mem;
 #define cfg_allocu(size) lp_allocu(cfg_mem, size)
 #define cfg_allocz(size) lp_allocz(cfg_mem, size)
 char *cfg_strdup(struct config *c, char *str);
-void cfg_copy_list(list *dest, list *src, unsigned node_size);
+void cfg_copy_list(struct config *c, list *dest, list *src, unsigned node_size);
 struct config * config_get_ptr_new(void);
 
 #define cfl_alloc(c, size) lp_alloc((c)->mem, size)
diff --git a/nest/rt-dev.c b/nest/rt-dev.c
index 5d22396..c153c3f 100644
--- a/nest/rt-dev.c
+++ b/nest/rt-dev.c
@@ -108,7 +108,7 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
    * Copy suffices to be is shallow, because new nodes can be added, but
    * old nodes cannot be modified (although they contain internal lists).
    */
-  cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct iface_patt));
+  cfg_copy_list(dest->global, &d->iface_list, &s->iface_list, sizeof(struct iface_patt));
 }
 
 struct protocol proto_device = {
diff --git a/proto/radv/radv.c b/proto/radv/radv.c
index eb2a693..d1bed6e 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -411,7 +411,7 @@ radv_copy_config(struct proto_config *dest, struct proto_config *src)
   init_list(&d->patt_list);
 
   /* We copy pref_list, shallow copy suffices */
-  cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
+  cfg_copy_list(dest->global, &d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
 }
 
 static void
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index d342f7b..ded42c7 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -269,7 +269,7 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
   proto_copy_rest(dest, src, sizeof(struct kif_config));
 
   /* Copy primary addr list */
-  cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item));
+  cfg_copy_list(dest->global, &d->primary, &s->primary, sizeof(struct kif_primary_item));
 
   /* Fix sysdep parts */
   kif_sys_copy_config(d, s);
-- 
2.1.2

From 9e0ef5395bbe6d17e4836495ff66103892e4a8fe Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <[email protected]>
Date: Sun, 15 Feb 2015 14:13:55 +0300
Subject: [PATCH 7/8] Add api for dynamic protocol creation based on dynamic
 templates.

---
 conf/cf-lex.l   |   2 +
 conf/conf.h     |   1 +
 nest/config.Y   |   4 +-
 nest/proto.c    | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 nest/protocol.h |   6 ++-
 5 files changed, 156 insertions(+), 3 deletions(-)

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index b995e10..dfcee5f 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -657,6 +657,8 @@ cf_symbol_class_name(struct symbol *sym)
       return "protocol";
     case SYM_TEMPLATE:
       return "protocol template";
+    case SYM_DYNTEMPLATE:
+      return "dynamic template";
     case SYM_FUNCTION:
       return "function";
     case SYM_FILTER:
diff --git a/conf/conf.h b/conf/conf.h
index 71da2a1..9490377 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -124,6 +124,7 @@ struct symbol {
 #define SYM_FILTER 4
 #define SYM_TABLE 5
 #define SYM_ROA 6
+#define SYM_DYNTEMPLATE 7
 
 #define SYM_VARIABLE 0x100	/* 0x100-0x1ff are variable types */
 #define SYM_CONSTANT 0x200	/* 0x200-0x2ff are variable types */
diff --git a/nest/config.Y b/nest/config.Y
index 481d9f5..095a8fe 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -63,7 +63,7 @@ CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFA
 CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE, ROA)
 CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
 CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
-CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
+CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS, DYNAMIC)
 
 CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
 	RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@@ -171,6 +171,7 @@ CF_ADDTO(conf, proto)
 proto_start:
    PROTOCOL { $$ = SYM_PROTO; }
  | TEMPLATE { $$ = SYM_TEMPLATE; }
+ | DYNAMIC TEMPLATE { $$ = SYM_DYNTEMPLATE; }
  ;
 
 proto_name:
@@ -191,6 +192,7 @@ proto_name:
      this_proto->name = $1->name;
 
      proto_copy_config(this_proto, $3->def);
+     this_proto->class = $1->class;
    }
  ;
 
diff --git a/nest/proto.c b/nest/proto.c
index 846d172..dda438c 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -398,6 +398,146 @@ proto_init(struct proto_config *c)
   return q;
 }
 
+/**
+ * config_create_dynamic - Creates dynamic protocol configuration
+ * @tc = config template to use
+ * @pname - instance name
+ * @p - existing protocol instance, if reconfiguring
+ * @pi - pointer to protocol-specific data
+ *
+ * Function creates all attributes for protocol - symbol of
+ * appropriate type, protocol config and performs necessary bindings.
+ *
+ * Returns pointer to protocol config or NULL.
+ */
+static struct proto_config *
+config_create_dynamic(struct proto_config *tc, char *pname, struct proto *p, void *pi)
+{
+  struct proto_config *c;
+  struct protocol *q;
+  struct symbol *sym;
+
+  q = tc->protocol;
+
+  if (!q->copy_dynconfig)
+    return NULL;
+
+  config_start_edit(tc->global);
+
+  sym = cf_find_symbol(tc->global, pname);
+  if (!sym || sym->class != SYM_VOID)
+    {
+      config_stop_edit();
+      log(L_ERR "Unable to create symbol %s for dynamic %s protocol", pname, q->name);
+      return NULL;
+    }
+  sym->class = SYM_PROTO;
+
+  c = proto_create_config(tc->global, q, SYM_PROTO);
+  proto_copy_config(c, tc);
+  sym->def = c;
+  c->name = sym->name;
+  c->dyn_parent = tc;
+  q->copy_dynconfig(c, p, pi);
+
+  config_stop_edit();
+
+  return c;
+}
+
+/**
+ * proto_create_dynamic - Creates dynamic protocol instance
+ * @tc = config template to use
+ * @pname - instance name
+ * @pi - pointer to protocol-specific data
+ *
+ * Function creates all atrributes for protocol - symbol of
+ * appropriate type, protocol config and protocol itself.
+ * Protocol is started after this.
+ *
+ * Returns pointer to protocol instance or NULL.
+ */
+struct proto *
+proto_create_dynamic(struct proto_config *tc, char *pname, void *pi)
+{
+  struct proto_config *c;
+  struct proto *p = NULL;
+  struct protocol *q;
+
+  q = tc->protocol;
+
+  if (!q->copy_dynconfig)
+    return NULL;
+
+  c = config_create_dynamic(tc, pname, NULL, pi);
+  if (!c)
+    {
+      log(L_ERR "Creating dynamic %s config for %s failed", q->name, pname);
+      return NULL;
+    }
+
+  p = proto_init(c);
+
+  if (!p)
+    {
+      log(L_ERR "Creating dynamic %s protocol instance %s failed", q->name, pname);
+      return NULL;
+    }
+
+  /* Start protocol */
+  proto_rethink_goal(p);
+
+  return p;
+}
+
+/**
+ * proto_reconfigure_dynamic - creates dynamic protocol configuration on request
+ * @p - protocol instance
+ *
+ * Function checks is the protocol is really dynamic and it dynamic
+ * template still exists in new config. If true, it checks if the original
+ * creation conditions still holds by running @retest_dynconfig callback.
+ * At the end, new configuration is created based on new template and running
+ * protocol data.
+ *
+ * Returns pointer to proto_config or NULL,
+ */
+static struct proto_config *
+proto_reconfigure_dynamic(struct config *new, struct proto *p)
+{
+  struct proto_config *oc, *nc;
+  struct symbol *sym;
+
+  DBG("Dynamic protocol %s reconfiguration check\n", p->name);
+  /* Try to find parent dynamic template in new config */
+  oc = p->cf->dyn_parent;
+  sym = cf_find_symbol(new, oc->name);
+  if (!sym || sym->class != SYM_DYNTEMPLATE)
+    {
+      DBG("Wrong dyntemplate symbol %s class: %d\n", oc->name, sym ? sym->class : 0);
+      return NULL;
+    }
+  /* Found parent configuration */
+  nc = sym->def;
+
+  /* Check if dynamic template protocol is the same */
+  if (oc->protocol != nc->protocol)
+    {
+      DBG("Wrong dyntemplate protocol: %s vs %s\n", oc->protocol->name, nc->protocol->name);
+      return NULL;
+    }
+
+  /* Check if condition still holds */
+  if (!nc->protocol->retest_dynconfig(p, nc))
+    {
+      DBG("retest dynconfig failed\n");
+      return NULL;
+    }
+
+  DBG("Start of dynamic protocol %s reconfiguration\n", p->name);
+  return config_create_dynamic(nc, p->name, p, NULL);
+}
+
 int proto_reconfig_type;  /* Hack to propagate type info to pipe reconfigure hook */
 
 static int
@@ -521,13 +661,17 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
     {
       WALK_LIST(oc, old->protos)
 	{
+	  nc = NULL;
 	  p = oc->proto;
 	  sym = cf_find_symbol(new, oc->name);
 	  if (sym && sym->class == SYM_PROTO && !new->shutdown)
+	    nc = sym->def;
+	  if (!nc && oc->dyn_parent)
+	    nc = proto_reconfigure_dynamic(new, p);
+	  if (nc)
 	    {
 	      /* Found match, let's check if we can smoothly switch to new configuration */
 	      /* No need to check description */
-	      nc = sym->def;
 	      nc->proto = p;
 
 	      /* We will try to reconfigure protocol p */
diff --git a/nest/protocol.h b/nest/protocol.h
index a20b053..0af8757 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -57,6 +57,8 @@ struct protocol {
   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 (*copy_config)(struct proto_config *, struct proto_config *);	/* Copy config from given protocol instance */
+  void (*copy_dynconfig)(struct proto_config *, struct proto *, void *);    /* Clone config from given dynamic template */
+  int (*retest_dynconfig)(struct proto *, struct proto_config *);
 };
 
 void protos_build(void);
@@ -65,6 +67,7 @@ void protos_preconfig(struct config *);
 void protos_postconfig(struct config *);
 void protos_commit(struct config *new, struct config *old, int force_restart, int type);
 void protos_dump_all(void);
+struct proto *proto_create_dynamic(struct proto_config *tc, char *pname, void *pi);
 
 #define GA_UNKNOWN	0		/* Attribute not recognized */
 #define GA_NAME		1		/* Result = name */
@@ -89,7 +92,7 @@ struct proto_config {
   struct proto *proto;			/* Instance we've created */
   char *name;
   char *dsc;
-  int class;				/* SYM_PROTO or SYM_TEMPLATE */
+  int class;				/* SYM_PROTO or SYM_TEMPLATE or SYM_DYNTEMPLATE */
   u32 debug, mrtdump;			/* Debugging bitfields, both use D_* constants */
   unsigned preference, disabled;	/* Generic parameters */
   int in_keep_filtered;			/* Routes rejected in import filter are kept */
@@ -100,6 +103,7 @@ struct proto_config {
 					   (relevant when in_keep_filtered is active) */
   struct proto_limit *in_limit;		/* Limit for importing routes from protocol */
   struct proto_limit *out_limit;	/* Limit for exporting routes to protocol */
+  struct proto_config *dyn_parent;	/* Parent dynamic template */
 
   /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
 
-- 
2.1.2

From 7d8303ea3210156ed797f23423ed1e53ad365398 Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <[email protected]>
Date: Sun, 15 Feb 2015 14:24:02 +0300
Subject: [PATCH 8/8] Implement dynamic BGP protocols creation based on
 incoming connections.

---
 conf/conf.h        |   3 +
 proto/bgp/bgp.c    | 374 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 proto/bgp/bgp.h    |   9 ++
 proto/bgp/config.Y |  15 ++-
 4 files changed, 400 insertions(+), 1 deletion(-)

diff --git a/conf/conf.h b/conf/conf.h
index 9490377..334d31c 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -14,6 +14,7 @@
 
 
 /* Configuration structure */
+struct bgp_protocol_cfg;
 
 struct config {
   pool *pool;				/* Pool the configuration is stored in */
@@ -51,6 +52,8 @@ struct config {
   int obstacle_count;			/* Number of items blocking freeing of this config */
   int shutdown;				/* This is a pseudo-config for daemon shutdown */
   bird_clock_t load_time;		/* When we've got this configuration */
+
+  struct bgp_protocol_cfg *bgp_cfg;	/* generic storage for protocol specific data */
 };
 
 /* Please don't use these variables in protocols. Use proto_config->global instead. */
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 0fd2b68..a6bf7f4 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -71,6 +71,7 @@
 #include "nest/route.h"
 #include "nest/cli.h"
 #include "nest/locks.h"
+#include "filter/filter.h"
 #include "conf/conf.h"
 #include "lib/socket.h"
 #include "lib/resource.h"
@@ -89,6 +90,40 @@ static void bgp_active(struct bgp_proto *p);
 static sock *bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags);
 static void bgp_update_bfd(struct bgp_proto *p, int use_bfd);
 
+struct bgp_addr_node {
+  struct f_trie_node tn;		/* Information about network */
+  struct bgp_config *c;			/* Pointer to protocol config */
+  node addr_node;			/* Member of global lladdr_list */
+  node allow_node;			/* Member of proto allow_list */
+};
+
+/* Used to pass data to dynamic reconfigure callback */
+struct bgp_dtemplate_pinfo {
+  ip_addr addr;
+};
+
+/**
+ * bgp_get_protocol_config - sets up BGP-specific parameters storage in config
+ *
+ */
+static struct bgp_protocol_cfg *
+bgp_get_protocol_config(struct config *new)
+{
+  struct bgp_protocol_cfg *bgp_cfg;
+
+  bgp_cfg = (struct bgp_protocol_cfg *)new->bgp_cfg;
+  if (!bgp_cfg)
+    {
+      bgp_cfg = cfl_allocz(new, sizeof(struct bgp_protocol_cfg));
+      bgp_cfg->addr_trie = f_new_trie(new->mem, sizeof(struct bgp_addr_node));
+      init_list(&bgp_cfg->lladdr_list);
+      new->bgp_cfg = bgp_cfg;
+      DBG("Set up new bgp-specific config\n");
+    }
+
+  return bgp_cfg;
+}
+
 
 /**
  * bgp_open - open a BGP instance
@@ -713,6 +748,271 @@ bgp_connect(struct bgp_proto *p)	/* Enter Connect state and start establishing c
 }
 
 /**
+ * bgp_print_template - creates proto named based on template
+ * @buf - pointer to buffer storage
+ * @size - size of supplied buffer
+ * @fmt - format string
+ * @tname - protocol table name
+ * @pi - additional data used to create new proto
+ *
+ * Returns new protocol name length or -1
+ */
+static int
+bgp_print_template(char *buf, int size, const char *fmt, const char *tname, struct bgp_dtemplate_pinfo *pi)
+{
+  int len, off;
+  u32 a;
+  char *str;
+  char ipbuf[STD_ADDRESS_P_LENGTH+1];
+
+  for (str=buf ; *fmt ; ++fmt)
+    {
+      if (*fmt != '%')
+	{
+	  if (!size)
+	    return -1;
+	  *str++ = *fmt;
+	  size--;
+	  continue;
+	}
+      ++fmt;
+      len = 0;
+
+      switch (*fmt)
+        {
+	/* Part of IPv4/IPv6 address */
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+#ifdef IPV6
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	  off = (*fmt - '1');
+	  a = pi->addr.addr[off / 2];
+	  a = ((off % 2) ? a : (a >> 16)) & 0xffff;
+	  len = bsnprintf(ipbuf, sizeof(ipbuf), "%x", a);
+	  buf += len;
+	  size -= len;
+#else
+	  off = 32 - (*fmt - '0') * 8;
+	  a = ipa_to_u32(pi->addr);
+	  a = (a >> off) & 0xFF;
+	  len = bsnprintf(ipbuf, sizeof(ipbuf), "%u", a);
+	  buf += len;
+	  size -= len;
+#endif
+	  break;
+
+	/* IP address */
+	case 'I':
+	  ip_ntop(pi->addr, ipbuf);
+	  len = strlen(ipbuf);
+	  break;
+
+	/* Table name */
+	case 'T':
+	  len = bsnprintf(ipbuf, sizeof(ipbuf), "%s", tname);
+	  len = strlen(ipbuf);
+	  break;
+	}
+
+      if (len > size)
+	return -1;
+      if (len)
+	{
+	  strcpy(str, ipbuf);
+	  str += len;
+	}
+    }
+
+    if (!size)
+      return -1;
+    *str = '\0';
+    return str-buf;
+}
+
+/**
+ * bgp_create_proto - creates new dynamic instance
+ * @tc - pointer to template config
+ * @sk - socket
+ *
+ * Functions compiles protocol name based ot template and address
+ * and tries to create protocol instance itself.
+ *
+ * Returns pointer to instance or NULL.
+ */
+static struct bgp_proto *
+bgp_create_proto(struct bgp_config *tc, sock *sk)
+{
+  struct bgp_dtemplate_pinfo pi;
+  struct bgp_proto *p;
+  struct rtable_config *t;
+  char pname[SYM_MAX_LEN];
+
+  /* Use master table in the template by defaul. */
+  t = tc->c.table;
+  pi.addr = sk->daddr;
+
+  if (bgp_print_template(pname, sizeof(pname), tc->dyn_nameformat, t->name, &pi) == -1)
+    {
+      log(L_ERR "Unable to name connection from %I based on template %s", sk->daddr, tc->c.name);
+      return NULL;
+    }
+  log(L_ERR "Creating new BGP protocol %s from template %s and incoming connection %I",
+    pname, tc->c.name, sk->daddr);
+
+
+  p = (struct bgp_proto *)proto_create_dynamic(&tc->c, pname, &pi);
+  if (!p)
+    return NULL;
+
+  p->start_state = p->cf->capabilities ? BSS_CONNECT : BSS_CONNECT_NOCAP;
+
+  return p;
+}
+
+/**
+ * bgp_add_allow_mask - link new matching CIDR to the dynamic template
+ * @new - global config
+ * @c - bgp config
+ * @addr - prefix address
+ * @plen - mask length
+ *
+ * For addresses with link scope, add record to global linked list.
+ * For global scope, add record to global trie.
+ * In any case, link record to template allow_list.
+ */
+void
+bgp_add_allow_mask(struct config *new, struct bgp_config *c, ip_addr addr, int plen)
+{
+  struct bgp_protocol_cfg *bgp_cfg;
+  struct bgp_addr_node *an = NULL;
+
+  bgp_cfg = bgp_get_protocol_config(new);
+
+  /* Handle link-local addresses differently */
+  if (ipa_has_link_scope(addr) || c->c.class != SYM_DYNTEMPLATE)
+    {
+      /* LL or non-dynamic template. Do not attach to global trie */
+      an = (struct bgp_addr_node *)cfl_allocz(new, sizeof(struct bgp_addr_node));
+      an->tn.addr = addr;
+      an->tn.plen = plen;
+    }
+  else
+    {
+      /*
+       * Global address AND dynamic template.
+       * We should get here only in case of real reconfiguration
+       */
+      an = (struct bgp_addr_node *)trie_add_prefix(bgp_cfg->addr_trie,
+	addr, plen, plen, MAX_PREFIX_LENGTH);
+      if (an->c)
+	cf_error("Prefix %I/%d already exists from %s", addr, plen, an->c->c.name);
+    }
+
+  if (ipa_has_link_scope(addr) && c->c.class == SYM_DYNTEMPLATE)
+    add_tail(&bgp_cfg->lladdr_list, &an->addr_node);
+
+  an->c = c;
+  add_tail(&c->dyn_allow_list, &an->allow_node);
+
+}
+
+static void
+bgp_copy_dynconfig(struct proto_config *c, struct proto *p, void *data)
+{
+  struct bgp_config *new, *oc;
+  struct bgp_dtemplate_pinfo *pi;
+
+  new = (struct bgp_config *)c;
+
+  /* The only thing we have to copy is source address */
+  if (p)
+    {
+      oc = (struct bgp_config *)p->cf;
+      new->remote_ip = oc->remote_ip;
+    }
+  else
+    {
+      pi = (struct bgp_dtemplate_pinfo *)data;
+      new->remote_ip = pi->addr;
+    }
+}
+
+/**
+ * bgp_retest_dynconfig - checks if dynamic instance is still relevant
+ * @p - protocol instance in question
+ * @c - new template to check with
+ *
+ * Checks if @p remote address still matches prefix lists and
+ * interface in @c. Checks also if protocol was used recently.
+ *
+ * Returns non-zero if condition still holds/
+ */
+static int
+bgp_retest_dynconfig(struct proto *p, struct proto_config *c)
+{
+  struct bgp_config *oc, *nc;
+  struct bgp_addr_node *an;
+  node *n;
+  int found;
+
+  nc = (struct bgp_config *)c;
+  oc = (struct bgp_config *)p->cf;
+
+  if (oc->iface != nc->iface)
+    {
+      log(L_ERR "Interface changed for dynamic proto %s: %s vs %s",
+       p->name, oc->iface ? oc->iface->name : "NULL",
+       nc->iface ? nc->iface->name : "NULL");
+      return 0;
+    }
+
+  if (strcmp(oc->dyn_nameformat, nc->dyn_nameformat))
+    {
+      log(L_ERR "Name format changed for dynamic proto %s: %s vs %s",
+       p->name, oc->dyn_nameformat, nc->dyn_nameformat);
+      return 0;
+    }
+
+  if (strcmp(oc->dyn_nameformat, nc->dyn_nameformat))
+    {
+      log(L_ERR "Name format changed for dynamic proto %s: %s vs %s",
+       p->name, oc->dyn_nameformat, nc->dyn_nameformat);
+      return 0;
+    }
+
+  if (p->disabled)
+    {
+      log(L_ERR "Removing disabled dynamic proto %s", p->name);
+      return 0;
+    }
+
+  /* Check all dynamic prefix masks */
+  found = 0;
+  WALK_LIST(n, nc->dyn_allow_list)
+    {
+      an = SKIP_BACK(struct bgp_addr_node, allow_node, n);
+      if (ipa_in_net(oc->remote_ip, an->tn.addr, an->tn.plen))
+       {
+         found = 1;
+         break;
+       }
+    }
+  if (!found)
+    {
+      log(L_ERR "No more matching masks for dynamic proto %s in template %s",
+       p->name, nc->c.name);
+      return 0;
+    }
+
+  return 1;
+}
+
+/**
  * bgp_find_proto - find existing proto by socket
  * @sk: TCP socket
  *
@@ -721,6 +1021,12 @@ static struct bgp_proto *
 bgp_find_proto(sock *sk)
 {
   struct proto_config *pc;
+  struct bgp_addr_node *an;
+  struct bgp_protocol_cfg *bgp_cfg;
+  node *n;
+
+  if (config->shutdown)
+    return NULL;
 
   WALK_LIST(pc, config->protos)
     if (pc->protocol == &proto_bgp && pc->proto)
@@ -730,6 +1036,34 @@ bgp_find_proto(sock *sk)
 	    (!ipa_has_link_scope(sk->daddr) || (p->cf->iface == sk->iface)))
 		return p;
       }
+  bgp_cfg = config->bgp_cfg;
+  if (!bgp_cfg)
+    {
+      DBG("No dynamic templates\n");
+      return NULL;
+    }
+  DBG("Searching for %I in dynamic templates masks\n", sk->daddr);
+  /* Next stage: check dynamic masks */
+  if (ipa_has_link_scope(sk->daddr))
+    {
+      WALK_LIST(n, bgp_cfg->lladdr_list)
+       {
+	   an = SKIP_BACK(struct bgp_addr_node, addr_node, n);
+	   if (!ipa_in_net(sk->daddr, an->tn.addr, an->tn.plen))
+	     continue;
+	   if (an->c->iface != sk->iface)
+	     continue;
+	   return bgp_create_proto(an->c, sk);
+       }
+    }
+  else
+    {
+      an = (struct bgp_addr_node *)trie_match_longest_prefix(bgp_cfg->addr_trie,
+       sk->daddr, MAX_PREFIX_LENGTH);
+      DBG("Searching for %I in address trie\n", sk->daddr);
+      if (an)
+       return bgp_create_proto(an->c, sk);
+    }
 
   return NULL;
 }
@@ -1204,6 +1538,14 @@ bgp_check_config(struct bgp_config *c)
 
   if (c->secondary && !c->c.table->sorted)
     cf_error("BGP with secondary option requires sorted table");
+
+  if (c->c.class == SYM_DYNTEMPLATE)
+    {
+      if (!c->dyn_nameformat)
+       cf_error("Name template has to be specified");
+      if (EMPTY_LIST(c->dyn_allow_list))
+       cf_error("At least one prefix mask has to be specified");
+    }
 }
 
 static int
@@ -1237,8 +1579,38 @@ bgp_reconfigure(struct proto *P, struct proto_config *C)
 static void
 bgp_copy_config(struct proto_config *dest, struct proto_config *src)
 {
+  struct bgp_config *nc, *oc;
+  struct bgp_addr_node *an, *xn;
+  node *n;
+
   /* Just a shallow copy */
   proto_copy_rest(dest, src, sizeof(struct bgp_config));
+
+  nc = (struct bgp_config *)dest;
+  oc = (struct bgp_config *)src;
+  /* Do not blindly copy dynamic allow lists */
+  init_list(&nc->dyn_allow_list);
+  if (dest->class == SYM_TEMPLATE)
+    {
+      /* copy prefix info only to permit dynamic template inheritance */
+      WALK_LIST(n, oc->dyn_allow_list)
+	{
+	  an = SKIP_BACK(struct bgp_addr_node, allow_node, n);
+	  xn = cfl_allocz(dest->global, sizeof(*xn));
+	  xn->tn.addr = an->tn.addr;
+	  xn->tn.plen = an->tn.plen;
+	  add_tail(&nc->dyn_allow_list, &xn->allow_node);
+	}
+    }
+  else if (dest->class == SYM_DYNTEMPLATE)
+    {
+      /* copy with saving to global trie */
+      WALK_LIST(n, oc->dyn_allow_list)
+	{
+	  an = SKIP_BACK(struct bgp_addr_node, allow_node, n);
+	  bgp_add_allow_mask(dest->global, nc, an->tn.addr, an->tn.plen);
+	}
+    }
 }
 
 
@@ -1437,6 +1809,8 @@ struct protocol proto_bgp = {
   cleanup:		bgp_cleanup,
   reconfigure:		bgp_reconfigure,
   copy_config:		bgp_copy_config,
+  copy_dynconfig:	bgp_copy_dynconfig,
+  retest_dynconfig:	bgp_retest_dynconfig,
   get_status:		bgp_get_status,
   get_attr:		bgp_get_attr,
   get_route_info:	bgp_get_route_info,
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 0fd3a73..c8487b2 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -63,6 +63,14 @@ struct bgp_config {
   char *password;			/* Password used for MD5 authentication */
   struct rtable_config *igp_table;	/* Table used for recursive next hop lookups */
   int bfd;				/* Use BFD for liveness detection */
+  int dyn_expire;			/* Min time to expire inactive dynamic protocols */
+  char *dyn_nameformat;			/* Format string to name devived protocols */
+  list dyn_allow_list;			/* list of prefixes allowed to connect */
+};
+
+struct bgp_protocol_cfg {
+  struct f_trie *addr_trie;		/* trie for global prefixes */
+  list lladdr_list;			/* list to store link-local addresses */
 };
 
 #define MLL_SELF 1
@@ -197,6 +205,7 @@ void bgp_handle_graceful_restart(struct bgp_proto *p);
 void bgp_graceful_restart_done(struct bgp_proto *p);
 void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code);
 void bgp_stop(struct bgp_proto *p, unsigned subcode);
+void bgp_add_allow_mask(struct config *new, struct bgp_config *c, ip_addr addr, int plen);
 
 struct rte_source *bgp_find_source(struct bgp_proto *p, u32 path_id);
 struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index d0ae064..4c83ba1 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -26,7 +26,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
 	PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
 	INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
 	TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
-	SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE)
+	SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE,
+	EXPIRE, INACTIVE)
 
 CF_GRAMMAR
 
@@ -52,6 +53,7 @@ bgp_proto_start: proto_start BGP {
      BGP_CFG->default_local_pref = 100;
      BGP_CFG->gr_mode = BGP_GR_AWARE;
      BGP_CFG->gr_time = 120;
+     init_list(&BGP_CFG->dyn_allow_list);
  }
  ;
 
@@ -124,6 +126,17 @@ bgp_proto:
  | bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
  | bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
  | bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); }
+ | bgp_proto DYNAMIC ALLOW bgp_allow_dynamic ';'
+ | bgp_proto DYNAMIC TEMPLATE NAME text ';' { BGP_CFG->dyn_nameformat = $5; }
+ | bgp_proto DYNAMIC EXPIRE INACTIVE expr ';' { BGP_CFG->dyn_expire = $5; }
+ ;
+
+bgp_allow_dynamic:
+  bgp_allow_dynamic_entry
+ | bgp_allow_dynamic ',' bgp_allow_dynamic_entry
+ ;
+
+bgp_allow_dynamic_entry: prefix_or_ipa { bgp_add_allow_mask(new_config, BGP_CFG, $1.addr, $1.len); }
  ;
 
 CF_ADDTO(dynamic_attr, BGP_ORIGIN
-- 
2.1.2

Reply via email to