Commit e0835db4 authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

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.
parent df092aa1
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ HASH_DEFINE_REHASH_FN(SYM, struct symbol)
HASH(struct keyword) kw_hash;


static struct sym_scope *conf_this_scope;
struct sym_scope *conf_this_scope;

linpool *cfg_mem;

@@ -673,7 +673,8 @@ cf_lex_init(int is_cli, struct config *c)
  else
    BEGIN(INITIAL);

  conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
  c->root_scope = cfg_allocz(sizeof(struct sym_scope));
  conf_this_scope = c->root_scope;
  conf_this_scope->active = 1;
}

+3 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ struct config {
  int file_fd;				/* File descriptor of main configuration file */
  HASH(struct symbol) sym_hash;		/* Lexer: symbol hash table */
  struct config *fallback;		/* Link to regular config for CLI parsing */
  struct sym_scope *root_scope;		/* Scope for root symbols */
  int obstacle_count;			/* Number of items blocking freeing of this config */
  int shutdown;				/* This is a pseudo-config for daemon shutdown */
  btime load_time;			/* When we've got this configuration */
@@ -152,6 +153,8 @@ struct include_file_stack {

extern struct include_file_stack *ifs;

extern struct sym_scope *conf_this_scope;

int cf_lex(void);
void cf_lex_init(int is_cli, struct config *c);
void cf_lex_unwind(void);
+56 −0
Original line number Diff line number Diff line
@@ -874,6 +874,28 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src)
  dest->protocol->copy_config(dest, src);
}

void
proto_clone_config(struct symbol *sym, struct proto_config *parent)
{
  struct proto_config *cf = proto_config_new(parent->protocol, SYM_PROTO);
  proto_copy_config(cf, parent);
  cf->name = sym->name;
  cf->proto = NULL;
  cf->parent = parent;

  sym->class = cf->class;
  sym->def = cf;
}

static void
proto_undef_clone(struct symbol *sym, struct proto_config *cf)
{
  rem_node(&cf->n);

  sym->class = SYM_VOID;
  sym->def = NULL;
}

/**
 * protos_preconfig - pre-configuration processing
 * @c: new configuration
@@ -973,6 +995,24 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
    {
      p = oc->proto;
      sym = cf_find_symbol(new, oc->name);

      /* Handle dynamic protocols */
      if (!sym && oc->parent && !new->shutdown)
      {
	struct symbol *parsym = cf_find_symbol(new, oc->parent->name);
	if (parsym && parsym->class == SYM_PROTO)
	{
	  /* This is hack, we would like to share config, but we need to copy it now */
	  new_config = new;
	  cfg_mem = new->mem;
	  conf_this_scope = new->root_scope;
	  sym = cf_get_symbol(oc->name);
	  proto_clone_config(sym, parsym->def);
	  new_config = NULL;
	  cfg_mem = NULL;
	}
      }

      if (sym && sym->class == SYM_PROTO && !new->shutdown)
      {
	/* Found match, let's check if we can smoothly switch to new configuration */
@@ -984,6 +1024,12 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
	if (! force_reconfig && proto_reconfigure(p, oc, nc, type))
	  continue;

	if (nc->parent)
	{
	  proto_undef_clone(sym, nc);
	  goto remove;
	}

	/* Unsuccessful, we will restart it */
	if (!p->disabled && !nc->disabled)
	  log(L_INFO "Restarting protocol %s", p->name);
@@ -997,6 +1043,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
      }
      else if (!new->shutdown)
      {
      remove:
	log(L_INFO "Removing protocol %s", p->name);
	p->down_code = PDC_CF_REMOVE;
	p->cf_new = NULL;
@@ -1105,6 +1152,15 @@ proto_rethink_goal(struct proto *p)
  }
}

struct proto *
proto_spawn(struct proto_config *cf, uint disabled)
{
  struct proto *p = proto_init(cf, TAIL(proto_list));
  p->disabled = disabled;
  proto_rethink_goal(p);
  return p;
}


/**
 * DOC: Graceful restart recovery
+3 −0
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ void protos_build(void);
void proto_build(struct protocol *);
void protos_preconfig(struct config *);
void protos_commit(struct config *new, struct config *old, int force_restart, int type);
struct proto * proto_spawn(struct proto_config *cf, uint disabled);
void protos_dump_all(void);

#define GA_UNKNOWN	0		/* Attribute not recognized */
@@ -113,6 +114,7 @@ struct proto_config {
  struct config *global;		/* Global configuration data */
  struct protocol *protocol;		/* Protocol */
  struct proto *proto;			/* Instance we've created */
  struct proto_config *parent;		/* Parent proto_config for dynamic protocols */
  char *name;
  char *dsc;
  int class;				/* SYM_PROTO or SYM_TEMPLATE */
@@ -263,6 +265,7 @@ struct proto_spec {
void *proto_new(struct proto_config *);
void *proto_config_new(struct protocol *, int class);
void proto_copy_config(struct proto_config *dest, struct proto_config *src);
void proto_clone_config(struct symbol *sym, struct proto_config *parent);
void proto_set_message(struct proto *p, char *msg, int len);

void graceful_restart_recovery(void);
+102 −15
Original line number Diff line number Diff line
@@ -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");
Loading