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

Babel: Fix handling of seqno requests

Old behavior has several deficiencies compared to standard behavior
(no triggered updates for replies, no retransmissions, ...).
parent 672fb78e
Loading
Loading
Loading
Loading
+173 −132
Original line number Diff line number Diff line
@@ -48,19 +48,20 @@
static inline int ge_mod64k(uint a, uint b)
{ return (u16)(a - b) < 0x8000; }

static void babel_dump_entry(struct babel_entry *e);
static void babel_dump_route(struct babel_route *r);
static void babel_expire_requests(struct babel_proto *p, struct babel_entry *e);
static void babel_select_route(struct babel_proto *p, struct babel_entry *e);
static void babel_send_route_request(struct babel_proto *p, struct babel_entry *e, struct babel_neighbor *n);
static void babel_send_wildcard_request(struct babel_iface *ifa);
static int  babel_cache_seqno_request(struct babel_proto *p, net_addr *n, u64 router_id, u16 seqno);
static void babel_trigger_iface_update(struct babel_iface *ifa);
static void babel_trigger_update(struct babel_proto *p);
static void babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e);
static void babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e, struct babel_seqno_request *sr);
static void babel_update_cost(struct babel_neighbor *n);
static inline void babel_kick_timer(struct babel_proto *p);
static inline void babel_iface_kick_timer(struct babel_iface *ifa);

static inline void babel_lock_neighbor(struct babel_neighbor *nbr)
{ if (nbr) nbr->uc++; }

static inline void babel_unlock_neighbor(struct babel_neighbor *nbr)
{ if (nbr && !--nbr->uc) mb_free(nbr); }


/*
 *	Functions to maintain data structures
@@ -72,6 +73,7 @@ babel_init_entry(void *E)
  struct babel_entry *e = E;

  e->updated = current_time();
  init_list(&e->requests);
  init_list(&e->sources);
  init_list(&e->routes);
}
@@ -260,9 +262,10 @@ loop:
    }

    babel_expire_sources(p, e);
    babel_expire_requests(p, e);

    /* Remove empty entries */
    if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes))
    if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes) && EMPTY_LIST(e->requests))
    {
      FIB_ITERATE_PUT(&fit);
      fib_delete(rtable, e);
@@ -279,6 +282,110 @@ babel_expire_routes(struct babel_proto *p)
  babel_expire_routes_(p, &p->ip6_rtable);
}

static inline int seqno_request_valid(struct babel_seqno_request *sr)
{ return !sr->nbr || sr->nbr->ifa; }

/*
 * Add seqno request to the table of pending requests (RFC 6216 3.2.6) and send
 * it to network. Do nothing if it is already in the table.
 */

static void
babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
			u64 router_id, u16 seqno, u8 hop_count,
			struct babel_neighbor *nbr)
{
  struct babel_seqno_request *sr;

  WALK_LIST(sr, e->requests)
    if (sr->router_id == router_id)
    {
      /* Found matching or newer */
      if (ge_mod64k(sr->seqno, seqno) && seqno_request_valid(sr))
	return;

      /* Found older */
      babel_unlock_neighbor(sr->nbr);
      rem_node(NODE sr);
      goto found;
    }

  /* No entries found */
  sr = sl_alloc(p->seqno_slab);

found:
  sr->router_id = router_id;
  sr->seqno = seqno;
  sr->hop_count = hop_count;
  sr->count = 0;
  sr->expires = current_time() + BABEL_SEQNO_REQUEST_EXPIRY;
  babel_lock_neighbor(sr->nbr = nbr);
  add_tail(&e->requests, NODE sr);

  babel_send_seqno_request(p, e, sr);
}

static void
babel_remove_seqno_request(struct babel_proto *p, struct babel_seqno_request *sr)
{
  babel_unlock_neighbor(sr->nbr);
  rem_node(NODE sr);
  sl_free(p->seqno_slab, sr);
}

static int
babel_satisfy_seqno_request(struct babel_proto *p, struct babel_entry *e,
			   u64 router_id, u16 seqno)
{
  struct babel_seqno_request *sr;

  WALK_LIST(sr, e->requests)
    if ((sr->router_id == router_id) && ge_mod64k(seqno, sr->seqno))
    {
      /* Found the request, remove it */
      babel_remove_seqno_request(p, sr);
      return 1;
    }

  return 0;
}

static void
babel_expire_requests(struct babel_proto *p, struct babel_entry *e)
{
  struct babel_seqno_request *sr, *srx;
  btime now_ = current_time();

  WALK_LIST_DELSAFE(sr, srx, e->requests)
  {
    /* Remove seqno requests sent to dead neighbors */
    if (!seqno_request_valid(sr))
    {
      babel_remove_seqno_request(p, sr);
      continue;
    }

    /* Handle expired requests - resend or remove */
    if (sr->expires && sr->expires <= now_)
    {
      if (sr->count < BABEL_SEQNO_REQUEST_RETRY)
      {
	sr->count++;
	sr->expires += (BABEL_SEQNO_REQUEST_EXPIRY << sr->count);
	babel_send_seqno_request(p, e, sr);
      }
      else
      {
	TRACE(D_EVENTS, "Seqno request for %N router-id %lR expired",
	      e->n.addr, sr->router_id);

	babel_remove_seqno_request(p, sr);
	continue;
      }
    }
  }
}

static struct babel_neighbor *
babel_find_neighbor(struct babel_iface *ifa, ip_addr addr)
{
@@ -309,6 +416,7 @@ babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
  nbr->txcost = BABEL_INFINITY;
  nbr->cost = BABEL_INFINITY;
  init_list(&nbr->routes);
  babel_lock_neighbor(nbr);
  add_tail(&ifa->neigh_list, NODE nbr);

  return nbr;
@@ -333,8 +441,9 @@ babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
      babel_select_route(p, e);
  }

  nbr->ifa = NULL;
  rem_node(NODE nbr);
  mb_free(nbr);
  babel_unlock_neighbor(nbr);
}

static void
@@ -588,14 +697,14 @@ babel_select_route(struct babel_proto *p, struct babel_entry *e)
       (as unreachable), then send a seqno request.

       babel_build_rte() will set the unreachable flag if the metric is BABEL_INFINITY.*/
    if (e->selected_in)
    if (best)
    {
      TRACE(D_EVENTS, "Lost feasible route for prefix %N", e->n.addr);

      e->selected_in->metric = e->selected_in->advert_metric = BABEL_INFINITY;
      best->metric = best->advert_metric = BABEL_INFINITY;
      e->updated = current_time();

      babel_send_seqno_request(p, e);
      babel_add_seqno_request(p, e, best->router_id, best->seqno + 1, 0, NULL);
      babel_announce_rte(p, e);

      /* Section 3.6 of the RFC forbids an infeasible from being selected. This
@@ -695,7 +804,6 @@ babel_send_hello(struct babel_iface *ifa)
static void
babel_send_route_request(struct babel_proto *p, struct babel_entry *e, struct babel_neighbor *n)
{
  struct babel_iface *ifa = n->ifa;
  union babel_msg msg = {};

  TRACE(D_PACKETS, "Sending route request for %N to %I",
@@ -704,7 +812,7 @@ babel_send_route_request(struct babel_proto *p, struct babel_entry *e, struct ba
  msg.type = BABEL_TLV_ROUTE_REQUEST;
  net_copy(&msg.route_request.net, e->n.addr);

  babel_send_unicast(&msg, ifa, n->addr);
  babel_send_unicast(&msg, n->ifa, n->addr);
}

static void
@@ -723,48 +831,32 @@ babel_send_wildcard_request(struct babel_iface *ifa)
}

static void
babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e)
babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e, struct babel_seqno_request *sr)
{
  struct babel_route *r = e->selected_in;
  struct babel_iface *ifa = NULL;
  struct babel_source *s = NULL;
  union babel_msg msg = {};

  s = babel_find_source(e, r->router_id);
  if (!s || !babel_cache_seqno_request(p, e->n.addr, r->router_id, s->seqno + 1))
    return;

  TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
	e->n.addr, r->router_id, s->seqno + 1);

  msg.type = BABEL_TLV_SEQNO_REQUEST;
  msg.seqno_request.hop_count = BABEL_INITIAL_HOP_COUNT;
  msg.seqno_request.seqno = s->seqno + 1;
  msg.seqno_request.router_id = r->router_id;
  msg.seqno_request.hop_count = sr->hop_count ?: BABEL_INITIAL_HOP_COUNT;
  msg.seqno_request.seqno = sr->seqno;
  msg.seqno_request.router_id = sr->router_id;
  net_copy(&msg.seqno_request.net, e->n.addr);

  WALK_LIST(ifa, p->interfaces)
    babel_enqueue(&msg, ifa);
}

static void
babel_unicast_seqno_request(struct babel_proto *p, struct babel_entry *e, struct babel_source *s, struct babel_neighbor *nbr)
  if (sr->nbr)
  {
  union babel_msg msg = {};

  if (!s || !babel_cache_seqno_request(p, e->n.addr, s->router_id, s->seqno + 1))
    return;

  TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
	e->n.addr, s->router_id, s->seqno + 1);
    TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d to %I on %s",
	  e->n.addr, sr->router_id, sr->seqno, sr->nbr->addr, sr->nbr->ifa->ifname);

  msg.type = BABEL_TLV_SEQNO_REQUEST;
  msg.seqno_request.hop_count = BABEL_INITIAL_HOP_COUNT;
  msg.seqno_request.seqno = s->seqno + 1;
  msg.seqno_request.router_id = s->router_id;
  net_copy(&msg.seqno_request.net, e->n.addr);
    babel_send_unicast(&msg, sr->nbr->ifa, sr->nbr->addr);
  }
  else
  {
    TRACE(D_PACKETS, "Sending broadcast seqno request for %N router-id %lR seqno %d",
	  e->n.addr, sr->router_id, sr->seqno);

  babel_send_unicast(&msg, nbr->ifa, nbr->addr);
    struct babel_iface *ifa;
    WALK_LIST(ifa, p->interfaces)
      babel_enqueue(&msg, ifa);
  }
}

/**
@@ -966,81 +1058,6 @@ babel_update_hello_history(struct babel_neighbor *n, u16 seqno, uint interval)
  n->last_hello_int = interval;
}

static void
babel_expire_seqno_requests(struct babel_proto *p)
{
  btime now_ = current_time();

  struct babel_seqno_request *n, *nx;
  WALK_LIST_DELSAFE(n, nx, p->seqno_cache)
  {
    if ((n->updated + BABEL_SEQNO_REQUEST_EXPIRY) <= now_)
    {
      rem_node(NODE n);
      sl_free(p->seqno_slab, n);
    }
  }
}

/*
 * Checks the seqno request cache for a matching request and returns failure if
 * found. Otherwise, a new entry is stored in the cache.
 */
static int
babel_cache_seqno_request(struct babel_proto *p, net_addr *n,
                          u64 router_id, u16 seqno)
{
  struct babel_seqno_request *r;

  WALK_LIST(r, p->seqno_cache)
  {
    if (net_equal(&r->net, n) && (r->router_id == router_id) && (r->seqno == seqno))
      return 0;
  }

  /* no entries found */
  r = sl_alloc(p->seqno_slab);
  net_copy(&r->net, n);
  r->router_id = router_id;
  r->seqno = seqno;
  r->updated = current_time();
  add_tail(&p->seqno_cache, NODE r);

  return 1;
}

static void
babel_forward_seqno_request(struct babel_proto *p, struct babel_entry *e,
                            struct babel_msg_seqno_request *in,
                            ip_addr sender)
{
  struct babel_route *r;

  TRACE(D_PACKETS, "Forwarding seqno request for %N router-id %lR seqno %d",
	e->n.addr, in->router_id, in->seqno);

  WALK_LIST(r, e->routes)
  {
    if ((r->router_id == in->router_id) &&
	!OUR_ROUTE(r) &&
	!ipa_equal(r->neigh->addr, sender))
    {
      if (!babel_cache_seqno_request(p, e->n.addr, in->router_id, in->seqno))
	return;

      union babel_msg msg = {};
      msg.type = BABEL_TLV_SEQNO_REQUEST;
      msg.seqno_request.hop_count = in->hop_count-1;
      msg.seqno_request.seqno = in->seqno;
      msg.seqno_request.router_id = in->router_id;
      net_copy(&msg.seqno_request.net, e->n.addr);

      babel_send_unicast(&msg, r->neigh->ifa, r->neigh->addr);
      return;
    }
  }
}


/*
 *	TLV handlers
@@ -1227,7 +1244,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
  /* RFC section 3.8.2.2 - Dealing with unfeasible updates */
  if (!feasible && (metric != BABEL_INFINITY) &&
      (!best || (r == best) || (metric < best->metric)))
    babel_unicast_seqno_request(p, e, s, nbr);
    babel_add_seqno_request(p, e, s->router_id, s->seqno + 1, 0, nbr);

  if (!r)
  {
@@ -1264,6 +1281,13 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
    r->expires = current_time() + r->expiry_interval;
    if (r->expiry_interval > BABEL_ROUTE_REFRESH_INTERVAL)
      r->refresh_time = current_time() + r->expiry_interval - BABEL_ROUTE_REFRESH_INTERVAL;

    /* If received update satisfies seqno request, we send triggered updates */
    if (babel_satisfy_seqno_request(p, e, msg->router_id, msg->seqno))
    {
      babel_trigger_update(p);
      e->updated = current_time();
    }
  }

  babel_select_route(p, e);
@@ -1301,7 +1325,6 @@ babel_handle_route_request(union babel_msg *m, struct babel_iface *ifa)
  }
}


void
babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
{
@@ -1335,11 +1358,31 @@ babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
    p->update_seqno_inc = 1;
    babel_trigger_update(p);
  }
  else
  else if (msg->hop_count > 1)
  {
    /* Not ours; forward if TTL allows it */
    if (msg->hop_count > 1)
      babel_forward_seqno_request(p, e, msg, msg->sender);

    /* Find best admissible route */
    struct babel_route *r, *best1 = NULL, *best2 = NULL;
    WALK_LIST(r, e->routes)
      if ((r->router_id == msg->router_id) && r->neigh && !ipa_equal(r->neigh->addr, msg->sender))
      {
	/* Find best feasible route */
	if (babel_is_feasible(babel_find_source(e, r->router_id), r->seqno, r->advert_metric) &&
	    (!best1 || r->metric < best1->metric))
	  best1 = r;

	/* Find best not necessary feasible route */
	if (!best2 || r->metric < best2->metric)
	  best2 = r;
      }

    /* If no route is found, do nothing */
    r = best1 ?: best2;
    if (!r)
      return;

    babel_add_seqno_request(p, e, msg->router_id, msg->seqno, msg->hop_count-1, r->neigh);
  }
}

@@ -1396,7 +1439,7 @@ babel_iface_timer(timer *t)
  {
    TRACE(D_EVENTS, "Sending triggered updates on %s", ifa->ifname);
    babel_send_update(ifa, ifa->want_triggered);
    ifa->next_triggered = now_ + MIN(5 S, update_period / 2);
    ifa->next_triggered = now_ + MIN(1 S, update_period / 2);
    ifa->want_triggered = 0;
    p->triggered = 0;
  }
@@ -1422,7 +1465,7 @@ babel_iface_start(struct babel_iface *ifa)

  ifa->next_hello = current_time() + (random() % ifa->cf->hello_interval);
  ifa->next_regular = current_time() + (random() % ifa->cf->update_interval);
  ifa->next_triggered = current_time() + MIN(5 S, ifa->cf->update_interval / 2);
  ifa->next_triggered = current_time() + MIN(1 S, ifa->cf->update_interval / 2);
  ifa->want_triggered = 0;	/* We send an immediate update (below) */
  tm2_start(ifa->timer, 100 MS);
  ifa->up = 1;
@@ -1988,7 +2031,6 @@ babel_timer(timer *t)
  struct babel_proto *p = t->data;

  babel_expire_routes(p);
  babel_expire_seqno_requests(p);
  babel_expire_neighbors(p);
}

@@ -2177,7 +2219,6 @@ babel_start(struct proto *P)
  p->source_slab = sl_new(P->pool, sizeof(struct babel_source));
  p->msg_slab = sl_new(P->pool, sizeof(struct babel_msg_node));
  p->seqno_slab = sl_new(P->pool, sizeof(struct babel_seqno_request));
  init_list(&p->seqno_cache);

  p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };

+14 −10
Original line number Diff line number Diff line
@@ -42,7 +42,8 @@
#define BABEL_ROUTE_EXPIRY_FACTOR(X)	((btime)(X)*7/2)	/* 3.5 */
#define BABEL_ROUTE_REFRESH_INTERVAL	(2 S_)	/* Time before route expiry to send route request */
#define BABEL_HOLD_TIME			(10 S_)	/* Expiry time for our own routes */
#define BABEL_SEQNO_REQUEST_EXPIRY	(60 S_)
#define BABEL_SEQNO_REQUEST_RETRY	4
#define BABEL_SEQNO_REQUEST_EXPIRY	(2 S_)
#define BABEL_GARBAGE_INTERVAL		(300 S_)
#define BABEL_RXCOST_WIRED		96
#define BABEL_RXCOST_WIRELESS		256
@@ -147,9 +148,7 @@ struct babel_proto {
  slab *route_slab;
  slab *source_slab;
  slab *msg_slab;

  slab *seqno_slab;
  list seqno_cache;			/* Seqno requests in the cache (struct babel_seqno_request) */

  struct tbf log_pkt_tbf;		/* TBF for packet messages */
};
@@ -190,6 +189,7 @@ struct babel_neighbor {
  struct babel_iface *ifa;

  ip_addr addr;
  uint uc;				/* Reference counter for seqno requests */
  u16 rxcost;				/* Sent in last IHU */
  u16 txcost;				/* Received in last IHU */
  u16 cost;				/* Computed neighbor cost */
@@ -231,12 +231,23 @@ struct babel_route {
  btime expiry_interval;
};

struct babel_seqno_request {
  node n;
  u64 router_id;
  u16 seqno;
  u8 hop_count;
  u8 count;
  btime expires;
  struct babel_neighbor *nbr;
};

struct babel_entry {
  struct babel_route *selected_in;
  struct babel_route *selected_out;

  btime updated;

  list requests;
  list sources;				/* Source entries for this prefix (struct babel_source). */
  list routes;				/* Routes for this prefix (struct babel_route) */

@@ -244,13 +255,6 @@ struct babel_entry {
};

/* Stores forwarded seqno requests for duplicate suppression. */
struct babel_seqno_request {
  node n;
  net_addr net;
  u64 router_id;
  u16 seqno;
  btime updated;
};


/*