From 7d8303ea3210156ed797f23423ed1e53ad365398 Mon Sep 17 00:00:00 2001 From: "Alexander V. Chernikov" 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