Commit 95ea723b authored by Maria Matejka's avatar Maria Matejka
Browse files

Nest: Allow batch route import

parent 1675ecbb
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -325,6 +325,78 @@ void rte_update(struct channel *c, net_addr *net, struct rte *rte);
 */
void rte_withdraw(struct channel *c, net_addr *net, struct rte_src *src);

/* Single route update order */
struct rte_update {
  struct rte_update *next;		/* Internal single-linked list */
  struct rte_src *src;			/* Key: rte_src */
  rte* rte;				/* Value: the route itself */
  enum rte_update_flags {
    RUF_IGNORE = 1,			/* Ignore this update */
  } flags;
  net_addr n[0];			/* Key: net */
};

struct rte_update_batch {
  struct linpool *lp;			/* Linpool to allocate the batch from */
  struct rte_update *first, **last;	/* Single route update order list */
};

/**
 * rte_update_init - prepare a route update batch
 *
 * If you want to import / withdraw more routes than one, you should pack them
 * into one batch and then execute them all at once. This function prepares
 * the batch.
 */
struct rte_update_batch * rte_update_init(void);

/**
 * rte_update_get - prepare a route import
 *
 * @batch: the batch to put this import in
 * @n: network address
 * @src: the route source identifier (NULL for default)
 *
 * This function returns a structure for route import.
 * You shall fill in only the @rte member of the returned structure
 * (the pointer is already set) and set the flags.
 * The route attributes must exist until rte_update_commit()
 * is called which you can do either by calling rta_lookup()
 * or by allocating from the batch->lp linpool.
 */
struct rte_update * rte_update_get(struct rte_update_batch *batch, net_addr *n, struct rte_src *src);

/**
 * rte_withdraw_get - prepare a route withdraw
 *
 * @batch: the batch to put this import in
 * @n: network address
 * @src: the route source identifier (NULL for default)
 *
 * This function registers a withdraw. You may only set flags in the returned structure.
 */
struct rte_update * rte_withdraw_get(struct rte_update_batch *batch, net_addr *n, struct rte_src *src);

/**
 * rte_update_commit - do all the prepared updates
 *
 * @batch: batch to commit
 * @c: channel to send the updates to
 *
 * This function does all the prepared updates.
 */
void rte_update_commit(struct rte_update_batch *batch, struct channel *c);

/**
 * rte_update_cancel - cancel the prepared updates
 *
 * @batch: batch to cancel
 *
 * In case of error, you may want to send no update.
 * This frees all the memory allocated to the batch together with the batch itself.
 */
void rte_update_cancel(struct rte_update_batch *batch);

extern list routing_tables;
struct config;

+95 −6
Original line number Diff line number Diff line
@@ -1373,6 +1373,17 @@ rte_unhide_dummy_routes(net *net, rte **dummy)
  }
}

static struct rte_src *
rte_fix_src(struct channel *c, rte *new)
{
  if (new->attrs->src)
    return new->attrs->src;
  else if (rta_is_cached(new->attrs))
    bug("src is NULL in cached RTA");
  else
    return new->attrs->src = c->proto->main_source;
}

static void
rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
{
@@ -1398,6 +1409,8 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
      if (!new->pref)
	new->pref = c->preference;

      src = rte_fix_src(c, new);

      stats->imp_updates_received++;
      if (!rte_validate(new))
	{
@@ -1449,9 +1462,12 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
    }
  else
    {
      if (!src)
	src = c->proto->main_source;

      stats->imp_withdraws_received++;

      if (!(nn = net_find(c->table, n)) || !src)
      if (!(nn = net_find(c->table, n)))
	{
	  stats->imp_withdraws_ignored++;
	  rte_update_unlock();
@@ -1477,18 +1493,89 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
  rte_update_unlock();
}

struct rte_update_batch *
rte_update_init(void)
{
  rte_update_lock();
  struct rte_update_batch *batch = lp_alloc(rte_update_pool, sizeof(struct rte_update_batch));
  batch->lp = rte_update_pool;
  batch->first = NULL;
  batch->last = &(batch->first);
  return batch;
}

static int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);

void
rte_withdraw(struct channel *c, net_addr *n, struct rte_src *src)
rte_update_commit(struct rte_update_batch *batch, struct channel *c)
{
  if (!src)
    src = c->proto->main_source;
  ASSERT(batch->lp);

  _Bool have_in_table = c->in_table;

  for (struct rte_update *ru = batch->first; ru; ru = ru->next)
    if ((ru->flags & RUF_IGNORE) ||
	have_in_table && !rte_update_in(c, ru->n, ru->rte, ru->src))
      rte_free_quick(ru->rte);
    else
      rte_update2(c, ru->n, ru->rte, ru->src);

  batch->lp = NULL;
  rte_update_unlock();
}

void
rte_update_cancel(struct rte_update_batch *batch)
{
  ASSERT(batch->lp);

  if (!c->in_table || rte_update_in(c, n, NULL, src))
  for (struct rte_update *ru = batch->first; ru; ru = ru->next)
    if (ru->rte)
      rte_free_quick(ru->rte);

  batch->lp = NULL;
  rte_update_unlock();
}

struct rte_update *
rte_withdraw_get(struct rte_update_batch *batch, net_addr *n, struct rte_src *src)
{
  struct rte_update *ru = lp_alloc(batch->lp, sizeof(struct rte_update) + n->length);

  *(batch->last) = ru;
  batch->last = &(ru->next);
  ru->next = NULL;

  net_copy(ru->n, n);
  ru->src = src;
  ru->rte = NULL;
  ru->flags = 0;
  return ru;
}

void
rte_withdraw(struct channel *c, net_addr *n, struct rte_src *src)
{
  if (c->in_table && !rte_update_in(c, n, NULL, src ?: c->proto->main_source))
    rte_update2(c, n, NULL, src ?: c->proto->main_source);
}

struct rte_update *
rte_update_get(struct rte_update_batch *batch, net_addr *n, struct rte_src *src)
{
  struct rte_update *ru = rte_withdraw_get(batch, n, src);

  rte *e = sl_alloc(rte_slab);

  e->id = 0;
  e->flags = 0;
  e->pref = 0;

  ru->rte = e;

  return ru;
}

void
rte_update(struct channel *c, net_addr *n, struct rte *new)
{
@@ -1504,7 +1591,9 @@ rte_update(struct channel *c, net_addr *n, struct rte *new)
    e->attrs->src = c->proto->main_source;
  }

  if (!c->in_table || rte_update_in(c, n, e, e->attrs->src))
  if (c->in_table && !rte_update_in(c, n, e, e->attrs->src))
    rte_free_quick(e);
  else
    rte_update2(c, n, e, e->attrs->src);
}

+4 −0
Original line number Diff line number Diff line
@@ -453,6 +453,7 @@ struct bgp_parse_state {
  u32 last_id;
  struct rte_src *last_src;
  rta *cached_rta;
  struct rte_update_batch *update_batch;
};

#define BGP_PORT		179
@@ -483,6 +484,9 @@ static inline uint bgp_max_packet_length(struct bgp_conn *conn)
static inline void
bgp_parse_error(struct bgp_parse_state *s, uint subcode)
{
  if (s->update_batch)
    rte_update_cancel(s->update_batch);

  s->err_subcode = subcode;
  longjmp(s->err_jmpbuf, 1);
}
+13 −8
Original line number Diff line number Diff line
@@ -1304,7 +1304,7 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
  if (!a0)
  {
    /* Route withdraw */
    rte_withdraw(&(s->channel->c), n, s->last_src);
    rte_withdraw_get(s->update_batch, n, s->last_src);
    return;
  }

@@ -1319,12 +1319,12 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
    a0->eattrs = ea;
  }

  rte e0 = {
    .attrs = rta_clone(s->cached_rta),
    .u.bgp.stale = -1,
  };

  rte_update(&(s->channel->c), n, &e0);
  struct rte_update *ru = rte_update_get(s->update_batch, n, s->last_src);
  ru->rte->pflags = 0;
  ru->rte->attrs = rta_clone(s->cached_rta);
  ru->rte->u.bgp.suppressed = 0;
  ru->rte->u.bgp.stale = -1;
  return;
}

static void
@@ -2388,7 +2388,7 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis

  if (ea)
  {
    a = allocz(RTA_MAX_SIZE);
    a = lp_allocz(s->update_batch->lp, RTA_MAX_SIZE);

    a->source = RTS_BGP;
    a->scope = SCOPE_UNIVERSE;
@@ -2491,6 +2491,8 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
      !s.mp_reach_len && !s.mp_unreach_len && s.mp_unreach_af)
  { bgp_rx_end_mark(&s, s.mp_unreach_af); goto done; }

  s.update_batch = rte_update_init();

  if (s.ip_unreach_len)
    bgp_decode_nlri(&s, BGP_AF_IPV4, s.ip_unreach_nlri, s.ip_unreach_len, NULL, NULL, 0);

@@ -2505,6 +2507,9 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
    bgp_decode_nlri(&s, s.mp_reach_af, s.mp_reach_nlri, s.mp_reach_len,
		    ea, s.mp_next_hop_data, s.mp_next_hop_len);

  rte_update_commit(s.update_batch, &s.channel->c);
  s.update_batch = NULL;

done:
  rta_free(s.cached_rta);
  lp_flush(s.pool);
+16 −14
Original line number Diff line number Diff line
@@ -2015,6 +2015,8 @@ rt_sync(struct ospf_proto *p)

  OSPF_TRACE(D_EVENTS, "Starting routing table synchronization");

  struct rte_update_batch *rub = rte_update_init();

  DBG("Now syncing my rt table with nest's\n");
  FIB_ITERATE_INIT(&fit, fib);
again1:
@@ -2052,29 +2054,27 @@ again1:

      if (reload || ort_changed(nf, &a0))
      {
	rte e0 = {
	  .attrs = rta_lookup(&a0),
	  .u.ospf.metric1 = nf->old_metric1 = nf->n.metric1,
	  .u.ospf.metric2 = nf->old_metric2 = nf->n.metric2,
	  .u.ospf.tag = nf->old_tag = nf->n.tag,
	  .u.ospf.router_id = nf->old_rid = nf->n.rid,
	  .pflags = EA_ID_FLAG(EA_OSPF_METRIC1) | EA_ID_FLAG(EA_OSPF_ROUTER_ID),
	};
	struct rte_update *ru = rte_update_get(rub, nf->fn.addr, NULL);
	ru->rte->attrs = rta_lookup(&a0);

	rta_free(nf->old_rta);
	nf->old_rta = rta_clone(e0.attrs);
	nf->old_rta = rta_clone(ru->rte->attrs);

	ru->rte->u.ospf.metric1 = nf->old_metric1 = nf->n.metric1;
	ru->rte->u.ospf.metric2 = nf->old_metric2 = nf->n.metric2;
	ru->rte->u.ospf.tag = nf->old_tag = nf->n.tag;
	ru->rte->u.ospf.router_id = nf->old_rid = nf->n.rid;
	ru->rte->pflags = EA_ID_FLAG(EA_OSPF_METRIC1) | EA_ID_FLAG(EA_OSPF_ROUTER_ID);

	if (nf->n.type == RTS_OSPF_EXT2)
	  e0.pflags |= EA_ID_FLAG(EA_OSPF_METRIC2);
	  ru->rte->pflags |= EA_ID_FLAG(EA_OSPF_METRIC2);

	/* Perhaps onfly if tag is non-zero? */
	if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
	  e0.pflags |= EA_ID_FLAG(EA_OSPF_TAG);
	  ru->rte->pflags |= EA_ID_FLAG(EA_OSPF_TAG);

	DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
	    a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);

	rte_update(p->p.main_channel, nf->fn.addr, &e0);
      }
    }
    else if (nf->old_rta)
@@ -2083,7 +2083,7 @@ again1:
      rta_free(nf->old_rta);
      nf->old_rta = NULL;

      rte_withdraw(p->p.main_channel, nf->fn.addr, NULL);
      rte_withdraw_get(rub, nf->fn.addr, NULL);
    }

    /* Remove unused rt entry, some special entries are persistent */
@@ -2099,6 +2099,8 @@ again1:
  }
  FIB_ITERATE_END;

  rte_update_commit(rub, p->p.main_channel);

  WALK_LIST(oa, p->area_list)
  {
    /* Cleanup ASBR hash tables */
Loading