Hello everyone.
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:
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/6] 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/6] 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/6] 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/6] 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/6] * 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 c407778be84b67fc1fee8589a99dced2af7c2ec5 Mon Sep 17 00:00:00 2001 From: "Alexander V. Chernikov" <[email protected]> Date: Thu, 12 Feb 2015 03:19:34 +0300 Subject: [PATCH 6/6] Implement dynamic BGP templates permitting to specify IP ranges inside templates instead of per-neighbor record. --- conf/cf-lex.l | 2 + conf/conf.h | 4 + nest/config.Y | 4 +- nest/proto.c | 141 ++++++++++++++++++++++++- nest/protocol.h | 7 +- proto/bgp/bgp.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++ proto/bgp/bgp.h | 9 ++ proto/bgp/config.Y | 15 ++- 8 files changed, 475 insertions(+), 4 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 eb8acb5..7827280 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. */ @@ -124,6 +127,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..eaeeaed 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -398,6 +398,141 @@ proto_init(struct proto_config *c) return q; } +/** + * 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; + struct symbol *sym; + + q = tc->protocol; + + if (!q->copy_dynconfig) + return NULL; + + config_start_edit(config); + + sym = cf_find_symbol(config, 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(config, q, SYM_PROTO); + proto_copy_config(c, tc); + c->name = sym->name; + c->dyn_parent = tc; + q->copy_dynconfig(c, NULL, pi); + + p = proto_init(c); + config_stop_edit(); + + if (!c) + { + log(L_ERR "Creating dynamic %s config for %s failed", q->name, pname); + return NULL; + } + + 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, + */ +struct proto_config * +proto_reconfigure_dynamic(struct config *new, struct proto *p) +{ + struct proto_config *oc, *nc, *c; + 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(%p)\n", oc->name, sym ? sym->class : 0, sym); + 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); + config_start_edit(new); + /* + * Check if address condition still holds + * and create config if true + */ + sym = cf_find_symbol(new, p->name); + if (!sym || sym->class != SYM_VOID) + { + config_stop_edit(); + log(L_ERR "Unable to create symbol %s for dynamic %s protocol", + p->name, nc->protocol->name); + return NULL; + } + sym->class = SYM_PROTO; + + /* Create new config based on template */ + c = proto_create_config(new, nc->protocol, SYM_PROTO); + proto_copy_config(c, nc); + c->name = sym->name; + c->dyn_parent = nc; + /* Do necessary customizations */ + nc->protocol->copy_dynconfig(c, p, NULL); + + config_stop_edit(); + return c; +} + int proto_reconfig_type; /* Hack to propagate type info to pipe reconfigure hook */ static int @@ -521,13 +656,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..b2c88e5 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,8 @@ 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 +93,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 +104,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 */ diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 0fd2b68..5ead5ad 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,39 @@ 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 */ +}; + +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 @@ -712,6 +746,223 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c return; } +static int +bgp_print_template(char *buf, int size, const char *fmt, ip_addr addr) +{ + 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 = 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(addr); + a = (a >> off) & 0xFF; + len = bsnprintf(ipbuf, sizeof(ipbuf), "%u", a); + buf += len; + size -= len; +#endif + break; + + /* IP address */ + case 'I': + ip_ntop(addr, ipbuf); + 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; + char pname[SYM_MAX_LEN]; + + if (bgp_print_template(pname, sizeof(pname), tc->dyn_nameformat, sk->daddr) == -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); + + pi.addr = 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; + + bgp_cfg = bgp_get_protocol_config(new); + + /* Handle link-local addresses differently */ + if (ipa_has_link_scope(addr)) + { + an = (struct bgp_addr_node *)cfl_allocz(new, sizeof(struct bgp_addr_node)); + an->tn.addr = addr; + an->tn.plen = plen; + an->c = c; + add_tail(&bgp_cfg->lladdr_list, &an->addr_node); + add_tail(&c->dyn_allow_list, &an->allow_node); + return; + } + + 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); + add_tail(&c->dyn_allow_list, &an->allow_node); + an->c = c; +} + +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; + } + + /* 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,15 +972,49 @@ 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; WALK_LIST(pc, config->protos) if (pc->protocol == &proto_bgp && pc->proto) { struct bgp_proto *p = (struct bgp_proto *) pc->proto; + DBG("Checking proto %s [%I%%%s] vs %I%%%s\n", + p->p.name, p->cf->remote_ip, p->cf->iface? p->cf->iface->name : "NULL", + sk->daddr, sk->iface ? sk->iface->name : "NULL"); if (ipa_equal(p->cf->remote_ip, sk->daddr) && (!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 +1489,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 @@ -1239,6 +1532,8 @@ bgp_copy_config(struct proto_config *dest, struct proto_config *src) { /* Just a shallow copy */ proto_copy_rest(dest, src, sizeof(struct bgp_config)); + /* Do not copy dynamic allow lists */ + init_list(&((struct bgp_config *)dest)->dyn_allow_list); } @@ -1437,6 +1732,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
