summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2019-04-08 17:05:07 +0200
committerOndrej Zajicek (work) <santiago@crfreenet.org>2019-04-30 13:32:39 +0200
commite0835db4f137c1686c26165053ec9c0578b94009 (patch)
treec2a1df271b3bdbb1c48231849eb4d5ed7a607a2a /proto
parentdf092aa1def0419da74f15bd20582fab4ec09207 (diff)
BGP: Dynamic BGP
Support for dynamically spawning BGP protocols for incoming connections. Use 'neighbor range' to specify range of valid neighbor addresses, then incoming connections from these addresses spawn new BGP instances.
Diffstat (limited to 'proto')
-rw-r--r--proto/bgp/bgp.c117
-rw-r--r--proto/bgp/bgp.h9
-rw-r--r--proto/bgp/config.Y19
3 files changed, 127 insertions, 18 deletions
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index c332a836..a5c06783 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -129,6 +129,9 @@ static list bgp_sockets; /* Global list of listening sockets */
static void bgp_connect(struct bgp_proto *p);
static void bgp_active(struct bgp_proto *p);
+static void bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn);
+static void bgp_setup_sk(struct bgp_conn *conn, sock *s);
+static void bgp_send_open(struct bgp_conn *conn);
static void bgp_update_bfd(struct bgp_proto *p, int use_bfd);
static int bgp_incoming_connection(sock *sk, uint dummy UNUSED);
@@ -149,7 +152,7 @@ bgp_open(struct bgp_proto *p)
struct bgp_socket *bs = NULL;
struct iface *ifa = p->cf->strict_bind ? p->cf->iface : NULL;
ip_addr addr = p->cf->strict_bind ? p->cf->local_ip :
- (ipa_is_ip4(p->cf->remote_ip) ? IPA_NONE4 : IPA_NONE6);
+ (p->ipv4 ? IPA_NONE4 : IPA_NONE6);
uint port = p->cf->local_port;
/* FIXME: Add some global init? */
@@ -272,8 +275,17 @@ bgp_startup(struct bgp_proto *p)
BGP_TRACE(D_EVENTS, "Started");
p->start_state = BSS_CONNECT;
- if (!p->cf->passive)
+ if (!p->passive)
bgp_active(p);
+
+ if (p->postponed_sk)
+ {
+ /* Apply postponed incoming connection */
+ bgp_setup_conn(p, &p->incoming_conn);
+ bgp_setup_sk(&p->incoming_conn, p->postponed_sk);
+ bgp_send_open(&p->incoming_conn);
+ p->postponed_sk = NULL;
+ }
}
static void
@@ -456,7 +468,7 @@ bgp_decision(void *vp)
if ((p->p.proto_state == PS_START) &&
(p->outgoing_conn.state == BS_IDLE) &&
(p->incoming_conn.state != BS_OPENCONFIRM) &&
- !p->cf->passive)
+ !p->passive)
bgp_active(p);
if ((p->p.proto_state == PS_STOP) &&
@@ -465,6 +477,29 @@ bgp_decision(void *vp)
bgp_down(p);
}
+static struct bgp_proto *
+bgp_spawn(struct bgp_proto *pp, ip_addr remote_ip)
+{
+ struct symbol *sym;
+ char fmt[SYM_MAX_LEN];
+
+ bsprintf(fmt, "%s%%0%dd", pp->cf->dynamic_name, pp->cf->dynamic_name_digits);
+
+ /* This is hack, we would like to share config, but we need to copy it now */
+ new_config = config;
+ cfg_mem = config->mem;
+ conf_this_scope = config->root_scope;
+ sym = cf_default_name(fmt, &(pp->dynamic_name_counter));
+ proto_clone_config(sym, pp->p.cf);
+ new_config = NULL;
+ cfg_mem = NULL;
+
+ /* Just pass remote_ip to bgp_init() */
+ ((struct bgp_config *) sym->def)->remote_ip = remote_ip;
+
+ return (void *) proto_spawn(sym->def, 0);
+}
+
void
bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len)
{
@@ -1088,6 +1123,9 @@ err:
return;
}
+static inline int bgp_is_dynamic(struct bgp_proto *p)
+{ return ipa_zero(p->remote_ip); }
+
/**
* bgp_find_proto - find existing proto for incoming connection
* @sk: TCP socket
@@ -1096,6 +1134,7 @@ err:
static struct bgp_proto *
bgp_find_proto(sock *sk)
{
+ struct bgp_proto *best = NULL;
struct bgp_proto *p;
/* sk->iface is valid only if src or dst address is link-local */
@@ -1103,13 +1142,20 @@ bgp_find_proto(sock *sk)
WALK_LIST(p, proto_list)
if ((p->p.proto == &proto_bgp) &&
- (p->sock == sk->data) &&
- ipa_equal(p->cf->remote_ip, sk->daddr) &&
+ (ipa_equal(p->remote_ip, sk->daddr) || bgp_is_dynamic(p)) &&
+ (!p->cf->remote_range || ipa_in_netX(sk->daddr, p->cf->remote_range)) &&
+ (p->p.vrf == sk->vrf) &&
+ (p->cf->local_port == sk->sport) &&
(!link || (p->cf->iface == sk->iface)) &&
(ipa_zero(p->cf->local_ip) || ipa_equal(p->cf->local_ip, sk->saddr)))
- return p;
+ {
+ best = p;
- return NULL;
+ if (!bgp_is_dynamic(p))
+ break;
+ }
+
+ return best;
}
/**
@@ -1188,6 +1234,16 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
sk_reallocate(sk);
}
+ /* For dynamic BGP, spawn new instance and postpone the socket */
+ if (bgp_is_dynamic(p))
+ {
+ p = bgp_spawn(p, sk->daddr);
+ p->postponed_sk = sk;
+ rmove(sk, p->p.pool);
+ return 0;
+ }
+
+ rmove(sk, p->p.pool);
bgp_setup_conn(p, &p->incoming_conn);
bgp_setup_sk(&p->incoming_conn, sk);
bgp_send_open(&p->incoming_conn);
@@ -1306,7 +1362,7 @@ bgp_bfd_notify(struct bfd_request *req)
static void
bgp_update_bfd(struct bgp_proto *p, int use_bfd)
{
- if (use_bfd && !p->bfd_req)
+ if (use_bfd && !p->bfd_req && !bgp_is_dynamic(p))
p->bfd_req = bfd_request_session(p->p.pool, p->remote_ip, p->local_ip,
p->cf->multihop ? NULL : p->neigh->iface,
bgp_bfd_notify, p);
@@ -1398,7 +1454,7 @@ bgp_start_locked(struct object_lock *lock)
DBG("BGP: Got lock\n");
- if (cf->multihop)
+ if (cf->multihop || bgp_is_dynamic(p))
{
/* Multi-hop sessions do not use neighbor entries */
bgp_initiate(p);
@@ -1433,20 +1489,26 @@ bgp_start(struct proto *P)
const struct bgp_config *cf = p->cf;
p->local_ip = cf->local_ip;
- p->remote_ip = cf->remote_ip;
p->local_as = cf->local_as;
p->remote_as = cf->remote_as;
p->public_as = cf->local_as;
+ /* For dynamic BGP childs, remote_ip is already set */
+ if (ipa_nonzero(cf->remote_ip))
+ p->remote_ip = cf->remote_ip;
+
/* Confederation ID is used for truly external peers */
if (p->cf->confederation && !p->is_interior)
p->public_as = cf->confederation;
+ p->passive = cf->passive || bgp_is_dynamic(p);
+
p->start_state = BSS_PREPARE;
p->outgoing_conn.state = BS_IDLE;
p->incoming_conn.state = BS_IDLE;
p->neigh = NULL;
p->bfd_req = NULL;
+ p->postponed_sk = NULL;
p->gr_ready = 0;
p->gr_active_num = 0;
@@ -1588,6 +1650,17 @@ bgp_init(struct proto_config *CF)
p->rs_client = cf->rs_client;
p->rr_client = cf->rr_client;
+ p->ipv4 = ipa_nonzero(cf->remote_ip) ?
+ ipa_is_ip4(cf->remote_ip) :
+ (cf->remote_range && (cf->remote_range->type == NET_IP4));
+
+ p->remote_ip = cf->remote_ip;
+ p->remote_as = cf->remote_as;
+
+ /* Hack: We use cf->remote_ip just to pass remote_ip from bgp_spawn() */
+ if (cf->c.parent)
+ cf->remote_ip = IPA_NONE;
+
/* Add all channels */
struct bgp_channel_config *cc;
WALK_LIST(cc, CF->channels)
@@ -1788,9 +1861,12 @@ bgp_postconfig(struct proto_config *CF)
if (!cf->local_as)
cf_error("Local AS number must be set");
- if (ipa_zero(cf->remote_ip))
+ if (ipa_zero(cf->remote_ip) && !cf->remote_range)
cf_error("Neighbor must be configured");
+ if (ipa_zero(cf->local_ip) && cf->strict_bind)
+ cf_error("Local address must be configured for strict bind");
+
if (!cf->remote_as && !cf->peer_type)
cf_error("Remote AS number (or peer type) must be set");
@@ -1921,7 +1997,10 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
// password item is last and must be checked separately
OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
&& ((!old->password && !new->password)
- || (old->password && new->password && !strcmp(old->password, new->password)));
+ || (old->password && new->password && !strcmp(old->password, new->password)))
+ && net_equal(old->remote_range, new->remote_range)
+ && !strcmp(old->dynamic_name, new->dynamic_name)
+ && (old->dynamic_name_digits == new->dynamic_name_digits);
/* FIXME: Move channel reconfiguration to generic protocol code ? */
struct channel *C, *C2;
@@ -1951,6 +2030,9 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
if (same)
p->cf = new;
+ /* Reset name counter */
+ p->dynamic_name_counter = 0;
+
return same;
}
@@ -2081,7 +2163,7 @@ bgp_state_dsc(struct bgp_proto *p)
return "Down";
int state = MAX(p->incoming_conn.state, p->outgoing_conn.state);
- if ((state == BS_IDLE) && (p->start_state >= BSS_CONNECT) && p->cf->passive)
+ if ((state == BS_IDLE) && (p->start_state >= BSS_CONNECT) && p->passive)
return "Passive";
return bgp_state_names[state];
@@ -2257,8 +2339,13 @@ bgp_show_proto_info(struct proto *P)
struct bgp_proto *p = (struct bgp_proto *) P;
cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p));
- cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
- cli_msg(-1006, " Neighbor AS: %u", p->cf->remote_as ?: p->remote_as);
+
+ if (bgp_is_dynamic(p) && p->cf->remote_range)
+ cli_msg(-1006, " Neighbor range: %N", p->cf->remote_range);
+ else
+ cli_msg(-1006, " Neighbor address: %I%J", p->remote_ip, p->cf->iface);
+
+ cli_msg(-1006, " Neighbor AS: %u", p->remote_as);
if (p->gr_active_num)
cli_msg(-1006, " Neighbor graceful restart active");
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 4d24e4fa..d8c9fe94 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -124,6 +124,9 @@ struct bgp_config {
u32 disable_after_cease; /* Disable it when cease is received, bitfield */
char *password; /* Password used for MD5 authentication */
+ net_addr *remote_range; /* Allowed neighbor range for dynamic BGP */
+ char *dynamic_name; /* Name pattern for dynamic BGP */
+ int dynamic_name_digits; /* Minimum number of digits for dynamic names */
int check_link; /* Use iface link state for liveness detection */
int bfd; /* Use BFD for liveness detection */
};
@@ -270,12 +273,14 @@ struct bgp_proto {
u32 local_id; /* BGP identifier of this router */
u32 remote_id; /* BGP identifier of the neighbor */
u32 rr_cluster_id; /* Route reflector cluster ID */
- int start_state; /* Substates that partitions BS_START */
+ u8 start_state; /* Substates that partitions BS_START */
u8 is_internal; /* Internal BGP session (local_as == remote_as) */
u8 is_interior; /* Internal or intra-confederation BGP session */
u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
u8 rr_client; /* Whether neighbor is RR client of me */
u8 rs_client; /* Whether neighbor is RS client of me */
+ u8 ipv4; /* Use IPv4 connection, i.e. remote_ip is IPv4 */
+ u8 passive; /* Do not initiate outgoing connection */
u8 route_refresh; /* Route refresh allowed to send [RFC 2918] */
u8 enhanced_refresh; /* Enhanced refresh is negotiated [RFC 7313] */
u8 gr_ready; /* Neighbor could do graceful restart */
@@ -292,10 +297,12 @@ struct bgp_proto {
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */
struct bgp_socket *sock; /* Shared listening socket */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
+ struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */
ip_addr link_addr; /* Link-local version of local_ip */
event *event; /* Event for respawning and shutting process */
timer *startup_timer; /* Timer used to delay protocol startup due to previous errors (startup_delay) */
timer *gr_timer; /* Timer waiting for reestablishment after graceful restart */
+ int dynamic_name_counter; /* Counter for dynamic BGP names */
uint startup_delay; /* Delay (in seconds) of protocol startup due to previous errors */
btime last_proto_error; /* Time of last error that leads to protocol stop */
u8 last_error_class; /* Error class of last error */
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index c9a6af96..bbc7d9a4 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -29,7 +29,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
- LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL)
+ LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL,
+ DYNAMIC, RANGE, NAME, DIGITS)
%type <i> bgp_nh
%type <i32> bgp_afi
@@ -68,6 +69,7 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->llgr_mode = -1;
BGP_CFG->llgr_time = 3600;
BGP_CFG->setkey = 1;
+ BGP_CFG->dynamic_name = "dynbgp";
BGP_CFG->check_link = -1;
}
;
@@ -120,11 +122,18 @@ bgp_proto:
}
| bgp_proto NEIGHBOR bgp_nbr_opts ';'
| bgp_proto NEIGHBOR ipa ipa_scope bgp_nbr_opts ';' {
- if (ipa_nonzero(BGP_CFG->remote_ip))
+ if (ipa_nonzero(BGP_CFG->remote_ip) || BGP_CFG->remote_range)
cf_error("Only one neighbor per BGP instance is allowed");
BGP_CFG->remote_ip = $3;
if ($4) BGP_CFG->iface = $4;
}
+ | bgp_proto NEIGHBOR RANGE net_ip bgp_nbr_opts ';' {
+ if (ipa_nonzero(BGP_CFG->remote_ip) || BGP_CFG->remote_range)
+ cf_error("Only one neighbor per BGP instance is allowed");
+ net_addr *n = cfg_alloc($4.length);
+ net_copy(n, &($4));
+ BGP_CFG->remote_range = n;
+ }
| bgp_proto INTERFACE TEXT ';' { BGP_CFG->iface = if_get_by_name($3); }
| bgp_proto RR CLUSTER ID idval ';' { BGP_CFG->rr_cluster_id = $5; }
| bgp_proto RR CLIENT bool ';' { BGP_CFG->rr_client = $4; }
@@ -136,6 +145,12 @@ bgp_proto:
| bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
| bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); }
+ | bgp_proto DYNAMIC NAME text ';' {
+ if (strchr($4, '%')) cf_error("Forbidden character '%%' in dynamic name");
+ if (strlen($4) > (SYM_MAX_LEN - 16)) cf_error("Dynamic name too long");
+ BGP_CFG->dynamic_name = $4;
+ }
+ | bgp_proto DYNAMIC NAME DIGITS expr ';' { BGP_CFG->dynamic_name_digits = $5; if ($5>10) cf_error("Dynamic name digits must be at most 10"); }
| bgp_proto STRICT BIND bool ';' { BGP_CFG->strict_bind = $4; }
| bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; }
| bgp_proto MED METRIC bool ';' { BGP_CFG->med_metric = $4; }