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

Babel: Revamp cost computation and run route selection when cost change

Also fix several minor bugs and add 'limit' option for k-out-of-j
link sensing strategy. Change default from 8-of-16 to 12-of-16.
Change IHU expiry factor from 1.5 to 3.5 (as in RFC 6126).
parent f00221fa
Loading
Loading
Loading
Loading
+25 −13
Original line number Diff line number Diff line
@@ -1616,6 +1616,7 @@ protocol babel [<name>] {
	interface <interface pattern> {
		type <wired|wireless>;
		rxcost <number>;
		limit <number>;
		hello interval <number>;
		update interval <number>;
		port <number>;
@@ -1632,23 +1633,34 @@ protocol babel [<name>] {

<descrip>
      <tag><label id="babel-type">type wired|wireless </tag>
      This option specifies the interface type: Wired or wireless. Wired
      interfaces are considered more reliable, and so the default hello
      interval is higher, and a neighbour is considered unreachable after only
      a small number of "hello" packets are lost. On wireless interfaces,
      hello packets are sent more often, and the ETX link quality estimation
      technique is used to compute the metrics of routes discovered over this
      interface. This technique will gradually degrade the metric of routes
      when packets are lost rather than the more binary up/down mechanism of
      wired type links. Default: <cf/wired/.
      This option specifies the interface type: Wired or wireless. On wired
      interfaces a neighbor is considered unreachable after a small number of
      Hello packets are lost, as described by <cf/limit/ option. On wireless
      interfaces the ETX link quality estimation technique is used to compute
      the metrics of routes discovered over this interface. This technique will
      gradually degrade the metric of routes when packets are lost rather than
      the more binary up/down mechanism of wired type links. Default:
      <cf/wired/.

      <tag><label id="babel-rxcost">rxcost <m/num/</tag>
      This specifies the RX cost of the interface. The route metrics will be
      computed from this value with a mechanism determined by the interface
      <cf/type/. Default: 96 for wired interfaces, 256 for wireless.
      This option specifies the nominal RX cost of the interface. The effective
      neighbor costs for route metrics will be computed from this value with a
      mechanism determined by the interface <cf/type/. Note that in contrast to
      other routing protocols like RIP or OSPF, the <cf/rxcost/ specifies the
      cost of RX instead of TX, so it affects primarily neighbors' route
      selection and not local route selection. Default: 96 for wired interfaces,
      256 for wireless.

      <tag><label id="babel-limit">limit <m/num/</tag>
      BIRD keeps track of received Hello messages from each neighbor to
      establish neighbor reachability. For wired type interfaces, this option
      specifies how many of last 16 hellos have to be correctly received in
      order to neighbor is assumed to be up. The option is ignored on wireless
      type interfaces, where gradual cost degradation is used instead of sharp
      limit. Default: 12.

      <tag><label id="babel-hello">hello interval <m/num/</tag>
      Interval at which periodic "hello" messages are sent on this interface,
      Interval at which periodic Hello messages are sent on this interface,
      in seconds. Default: 4 seconds.

      <tag><label id="babel-update">update interval <m/num/</tag>
+127 −85
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ static int babel_cache_seqno_request(struct babel_proto *p, net_addr *n, u64 ro
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_entry *e);
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);

@@ -208,7 +209,7 @@ babel_expire_route(struct babel_route *r)

  if (r->metric < BABEL_INFINITY)
  {
    r->metric = BABEL_INFINITY;
    r->metric = r->advert_metric = BABEL_INFINITY;
    r->expires = current_time() + r->expiry_interval;
  }
  else
@@ -300,15 +301,20 @@ babel_find_neighbor(struct babel_iface *ifa, ip_addr addr)
static struct babel_neighbor *
babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
{
  struct babel_proto *p = ifa->proto;
  struct babel_neighbor *nbr = babel_find_neighbor(ifa, addr);

  if (nbr)
    return nbr;

  TRACE(D_EVENTS, "New neighbor %I on %s", addr, ifa->iface->name);

  nbr = mb_allocz(ifa->pool, sizeof(struct babel_neighbor));
  nbr->ifa = ifa;
  nbr->addr = addr;
  nbr->rxcost = BABEL_INFINITY;
  nbr->txcost = BABEL_INFINITY;
  nbr->cost = BABEL_INFINITY;
  init_list(&nbr->routes);
  add_tail(&ifa->neigh_list, NODE nbr);

@@ -316,12 +322,11 @@ babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
}

static void
babel_flush_neighbor(struct babel_neighbor *nbr)
babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
{
  struct babel_proto *p = nbr->ifa->proto;
  node *n;

  TRACE(D_EVENTS, "Flushing neighbor %I", nbr->addr);
  TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->ifa->iface->name);

  WALK_LIST_FIRST(n, nbr->routes)
  {
@@ -340,24 +345,33 @@ babel_flush_neighbor(struct babel_neighbor *nbr)
}

static void
babel_expire_ihu(struct babel_neighbor *nbr)
babel_expire_ihu(struct babel_proto *p, struct babel_neighbor *nbr)
{
  TRACE(D_EVENTS, "IHU from nbr %I on %s expired", nbr->addr, nbr->ifa->iface->name);

  nbr->txcost = BABEL_INFINITY;
  nbr->ihu_expiry = 0;
  babel_update_cost(nbr);
}

static void
babel_expire_hello(struct babel_neighbor *nbr)
babel_expire_hello(struct babel_proto *p, struct babel_neighbor *nbr)
{
  nbr->hello_map <<= 1;

  if (nbr->hello_cnt < 16)
    nbr->hello_cnt++;

  TRACE(D_EVENTS, "Hello from nbr %I on %s expired, %d left",
	nbr->addr, nbr->ifa->iface->name, u32_popcount(nbr->hello_map));

  if (nbr->hello_map)
  {
    nbr->hello_expiry += nbr->last_hello_int;
    babel_update_cost(nbr);
  }
  else
    babel_flush_neighbor(nbr);
    babel_flush_neighbor(p, nbr);
}

static void
@@ -372,10 +386,10 @@ babel_expire_neighbors(struct babel_proto *p)
    WALK_LIST_DELSAFE(nbr, nbx, ifa->neigh_list)
    {
      if (nbr->ihu_expiry && nbr->ihu_expiry <= now_)
        babel_expire_ihu(nbr);
        babel_expire_ihu(p, nbr);

      if (nbr->hello_expiry && nbr->hello_expiry <= now_)
        babel_expire_hello(nbr);
        babel_expire_hello(p, nbr);
    }
  }
}
@@ -409,67 +423,82 @@ babel_is_feasible(struct babel_source *s, u16 seqno, u16 metric)
    ((seqno == s->seqno) && (metric < s->metric));
}

static u16
babel_compute_rxcost(struct babel_neighbor *n)
/* Simple additive metric - Appendix 3.1 in the RFC */
static inline u16
babel_compute_metric(struct babel_neighbor *n, uint metric)
{
  struct babel_iface *ifa = n->ifa;
  u8 cnt, missed;
  u16 map=n->hello_map;
  return MIN(metric + n->cost, BABEL_INFINITY);
}

  if (!map) return BABEL_INFINITY;
  cnt = u32_popcount(map); // number of bits set
  missed = n->hello_cnt-cnt;
static void
babel_update_cost(struct babel_neighbor *nbr)
{
  struct babel_proto *p = nbr->ifa->proto;
  struct babel_iface_config *cf = nbr->ifa->cf;
  uint rcv = u32_popcount(nbr->hello_map); // number of bits set
  uint max = nbr->hello_cnt;
  uint rxcost = BABEL_INFINITY;	/* Cost to announce in IHU */
  uint txcost = BABEL_INFINITY;	/* Effective cost for route selection */

  if (ifa->cf->type == BABEL_IFACE_TYPE_WIRELESS)
  if (!rcv || !nbr->ifa->up)
    goto done;

  switch (cf->type)
  {
    /* ETX - Appendix 2.2 in the RFC.
  case BABEL_IFACE_TYPE_WIRED:
    /* k-out-of-j selection - Appendix 2.1 in the RFC. */

    /* Link is bad if less than cf->limit/16 of expected hellos were received */
    if (rcv * 16 < cf->limit * max)
      break;

       beta = prob. of successful transmission.
       rxcost = BABEL_RXCOST_WIRELESS/beta
    rxcost =  cf->rxcost;
    txcost = nbr->txcost;
    break;

       Since: beta = 1-missed/n->hello_cnt = cnt/n->hello_cnt
       Then: rxcost = BABEL_RXCOST_WIRELESS * n->hello_cnt / cnt
  case BABEL_IFACE_TYPE_WIRELESS:
    /*
     * ETX - Appendix 2.2 in the RFC.
     *
     * alpha  = prob. of successful transmission estimated by the neighbor
     * beta   = prob. of successful transmission estimated by the router
     * rxcost = nominal rxcost of the router / beta
     * txcost = nominal rxcost of the neighbor / (alpha * beta)
     *        = received txcost / beta
     *
     * Note that received txcost is just neighbor's rxcost. Beta is rcv/max,
     * we use inverse values of beta (i.e. max/rcv) to stay in integers.
     */
    if (!cnt) return BABEL_INFINITY;
    return BABEL_RXCOST_WIRELESS * n->hello_cnt / cnt;
    rxcost = MIN( cf->rxcost * max / rcv, BABEL_INFINITY);
    txcost = MIN(nbr->txcost * max / rcv, BABEL_INFINITY);
    break;
  }
  else

done:
  /* If RX cost changed, send IHU with next Hello */
  if (rxcost != nbr->rxcost)
  {
    /* k-out-of-j selection - Appendix 2.1 in the RFC. */
    DBG("Babel: Missed %d hellos from %I\n", missed, n->addr);
    /* Link is bad if more than half the expected hellos were lost */
    return (missed > n->hello_cnt/2) ? BABEL_INFINITY : ifa->cf->rxcost;
  }
    nbr->rxcost = rxcost;
    nbr->ihu_cnt = 0;
  }


static u16
babel_compute_cost(struct babel_neighbor *n)
  /* If link cost changed, run route selection */
  if (txcost != nbr->cost)
  {
  struct babel_iface *ifa = n->ifa;
  u16 rxcost = babel_compute_rxcost(n);
  if (rxcost == BABEL_INFINITY) return rxcost;
  else if (ifa->cf->type == BABEL_IFACE_TYPE_WIRELESS)
  {
    /* ETX - Appendix 2.2 in the RFC */
    return (MAX(n->txcost, BABEL_RXCOST_WIRELESS) * rxcost)/BABEL_RXCOST_WIRELESS;
  }
  else
    TRACE(D_EVENTS, "Cost of nbr %I on %s changed from %u to %u",
	  nbr->addr, nbr->ifa->iface->name, nbr->cost, txcost);

    nbr->cost = txcost;

    struct babel_route *r; node *n;
    WALK_LIST2(r, n, nbr->routes, neigh_route)
    {
    /* k-out-of-j selection - Appendix 2.1 in the RFC. */
    return n->txcost;
      r->metric = babel_compute_metric(nbr, r->advert_metric);
      babel_select_route(r->e);
    }
  }

/* Simple additive metric - Appendix 3.1 in the RFC */
static u16
babel_compute_metric(struct babel_neighbor *n, uint metric)
{
  metric += babel_compute_cost(n);
  return MIN(metric, BABEL_INFINITY);
}


/**
 * babel_announce_rte - announce selected route to the core
 * @p: Babel protocol instance
@@ -506,6 +535,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
    rte->u.babel.router_id = r->router_id;
    rte->pflags = 0;

    r->old_metric = r->metric;
    rte_update2(c, e->n.addr, rte, p->p.main_source);
  }
  else
@@ -535,7 +565,7 @@ static void
babel_select_route(struct babel_entry *e)
{
  struct babel_proto *p = e->proto;
  struct babel_route *r, *cur = e->selected_in;
  struct babel_route *r, *cur = e->selected_in, *best = e->selected_in;

  /* try to find the best feasible route */
  WALK_LIST(r, e->routes)
@@ -544,10 +574,10 @@ babel_select_route(struct babel_entry *e)
        babel_is_feasible(babel_find_source(e, r->router_id), r->seqno, r->advert_metric))
      cur = r;

  if (cur && !OUR_ROUTE(cur) &&
      ((!e->selected_in && cur->metric < BABEL_INFINITY) ||
       (e->selected_in && cur->metric < e->selected_in->metric)))
  if (cur && !OUR_ROUTE(cur) && (cur->metric < BABEL_INFINITY) &&
      (!best || (cur->metric < best->metric) || ((cur == best) && (cur->metric != cur->old_metric))))
  {
    if (cur != best)
      TRACE(D_EVENTS, "Picked new route for prefix %N: router id %lR metric %d",
	    e->n.addr, cur->router_id, cur->metric);

@@ -564,10 +594,9 @@ babel_select_route(struct babel_entry *e)
       babel_build_rte() will set the unreachable flag if the metric is BABEL_INFINITY.*/
    if (e->selected_in)
    {
      TRACE(D_EVENTS, "Lost feasible route for prefix %N",
	    e->n.addr);
      TRACE(D_EVENTS, "Lost feasible route for prefix %N", e->n.addr);

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

      babel_send_seqno_request(e);
@@ -583,7 +612,7 @@ babel_select_route(struct babel_entry *e)
      /* No route currently selected, and no new one selected; this means we
	 don't have a route to this destination anymore (and were probably
	 called from an expiry timer). Remove the route from the nest. */
      TRACE(D_EVENTS, "Flushing route for prefix %N", e->n.addr);
      // TRACE(D_EVENTS, "Flushing route for prefix %N", e->n.addr);

      e->selected_in = NULL;
      e->updated = current_time();
@@ -617,7 +646,7 @@ babel_build_ihu(union babel_msg *msg, struct babel_iface *ifa, struct babel_neig

  msg->type = BABEL_TLV_IHU;
  msg->ihu.addr = n->addr;
  msg->ihu.rxcost = babel_compute_rxcost(n);
  msg->ihu.rxcost = n->rxcost;
  msg->ihu.interval = ifa->cf->ihu_interval;

  TRACE(D_PACKETS, "Sending IHU for %I with rxcost %d interval %t",
@@ -630,6 +659,7 @@ babel_send_ihu(struct babel_iface *ifa, struct babel_neighbor *n)
  union babel_msg msg = {};
  babel_build_ihu(&msg, ifa, n);
  babel_send_unicast(&msg, ifa, n->addr);
  n->ihu_cnt = BABEL_IHU_INTERVAL_FACTOR;
}

static void
@@ -637,15 +667,19 @@ babel_send_ihus(struct babel_iface *ifa)
{
  struct babel_neighbor *n;
  WALK_LIST(n, ifa->neigh_list)
  {
    if (n->hello_cnt && (--n->ihu_cnt <= 0))
    {
      union babel_msg msg = {};
      babel_build_ihu(&msg, ifa, n);
      babel_enqueue(&msg, ifa);
      n->ihu_cnt = BABEL_IHU_INTERVAL_FACTOR;
    }
  }
}

static void
babel_send_hello(struct babel_iface *ifa, u8 send_ihu)
babel_send_hello(struct babel_iface *ifa)
{
  struct babel_proto *p = ifa->proto;
  union babel_msg msg = {};
@@ -659,7 +693,6 @@ babel_send_hello(struct babel_iface *ifa, u8 send_ihu)

  babel_enqueue(&msg, ifa);

  if (send_ihu)
  babel_send_ihus(ifa);
}

@@ -907,7 +940,7 @@ babel_update_hello_history(struct babel_neighbor *n, u16 seqno, uint interval)

  u16 delta = ((uint) seqno - (uint) n->next_hello_seqno);

  if (delta == 0)
  if ((delta == 0) || (n->hello_cnt == 0))
  {
    /* Do nothing */
  }
@@ -934,6 +967,8 @@ babel_update_hello_history(struct babel_neighbor *n, u16 seqno, uint interval)
  n->hello_map = (n->hello_map << 1) | 1;
  n->next_hello_seqno = seqno+1;
  if (n->hello_cnt < 16) n->hello_cnt++;

  /* Update expiration */
  n->hello_expiry = current_time() + BABEL_HELLO_EXPIRY_FACTOR(interval);
  n->last_hello_int = interval;
}
@@ -1041,8 +1076,13 @@ babel_handle_hello(union babel_msg *m, struct babel_iface *ifa)
	msg->seqno, (btime) msg->interval);

  struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
  int first_hello = !n->hello_cnt;

  babel_update_hello_history(n, msg->seqno, msg->interval);
  if (ifa->cf->type == BABEL_IFACE_TYPE_WIRELESS)
  babel_update_cost(n);

  /* Speed up session establishment by sending IHU immediately */
  if (first_hello)
    babel_send_ihu(ifa, n);
}

@@ -1062,6 +1102,7 @@ babel_handle_ihu(union babel_msg *m, struct babel_iface *ifa)
  struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
  n->txcost = msg->rxcost;
  n->ihu_expiry = current_time() + BABEL_IHU_EXPIRY_FACTOR(msg->interval);
  babel_update_cost(n);
}

/**
@@ -1159,7 +1200,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
      WALK_LIST(n, nbr->routes)
      {
	r = SKIP_BACK(struct babel_route, neigh_route, n);
	r->metric = BABEL_INFINITY;
	r->metric = r->advert_metric = BABEL_INFINITY;
	babel_select_route(r->e);
      }
    }
@@ -1176,7 +1217,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
      if (!r)
	return;

      r->metric = BABEL_INFINITY;
      r->metric = r->advert_metric = BABEL_INFINITY;
      babel_select_route(e);
    }

@@ -1215,7 +1256,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
      return;

    /* Treat as retraction */
    r->metric = BABEL_INFINITY;
    r->metric = r->advert_metric = BABEL_INFINITY;
  }
  else
  {
@@ -1347,8 +1388,7 @@ babel_iface_timer(timer *t)

  if (now_ >= ifa->next_hello)
  {
    babel_send_hello(ifa, (ifa->cf->type == BABEL_IFACE_TYPE_WIRELESS ||
                           ifa->hello_seqno % BABEL_IHU_INTERVAL_FACTOR == 0));
    babel_send_hello(ifa);
    ifa->next_hello += hello_period * (1 + (now_ - ifa->next_hello) / hello_period);
  }

@@ -1395,7 +1435,7 @@ babel_iface_start(struct babel_iface *ifa)
  tm2_start(ifa->timer, 100 MS);
  ifa->up = 1;

  babel_send_hello(ifa, 0);
  babel_send_hello(ifa);
  babel_send_wildcard_retraction(ifa);
  babel_send_wildcard_request(ifa);
  babel_send_update(ifa, 0);	/* Full update */
@@ -1422,8 +1462,10 @@ babel_iface_stop(struct babel_iface *ifa)
    WALK_LIST(n, nbr->routes)
    {
      r = SKIP_BACK(struct babel_route, neigh_route, n);
      r->metric = BABEL_INFINITY;
      r->metric = r->advert_metric = BABEL_INFINITY;
      r->expires = now_ + r->expiry_interval;

      if (r == r->e->selected_in)
	babel_select_route(r->e);
    }
  }
@@ -1551,7 +1593,7 @@ babel_remove_iface(struct babel_proto *p, struct babel_iface *ifa)

  struct babel_neighbor *n;
  WALK_LIST_FIRST(n, ifa->neigh_list)
    babel_flush_neighbor(n);
    babel_flush_neighbor(p, n);

  rem_node(NODE ifa);

@@ -1871,7 +1913,7 @@ babel_show_neighbors(struct proto *P, char *iff)
      uint hellos = u32_popcount(n->hello_map);
      btime timer = n->hello_expiry - current_time();
      cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t",
	      n->addr, ifa->iface->name, n->txcost, rts, hellos, MAX(timer, 0));
	      n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0));
    }
  }

+8 −2
Original line number Diff line number Diff line
@@ -34,9 +34,10 @@

#define BABEL_HELLO_INTERVAL_WIRED	(4 S_)	/* Default hello intervals in seconds */
#define BABEL_HELLO_INTERVAL_WIRELESS	(4 S_)
#define BABEL_HELLO_LIMIT		12
#define BABEL_UPDATE_INTERVAL_FACTOR	4
#define BABEL_IHU_INTERVAL_FACTOR	3
#define BABEL_IHU_EXPIRY_FACTOR(X)	((btime)(X)*3/2)	/* 1.5 */
#define BABEL_IHU_EXPIRY_FACTOR(X)	((btime)(X)*7/2)	/* 3.5 */
#define BABEL_HELLO_EXPIRY_FACTOR(X)	((btime)(X)*3/2)	/* 1.5 */
#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 */
@@ -112,6 +113,7 @@ struct babel_iface_config {

  u16 rxcost;
  u8 type;
  u8 limit;				/* Minimum number of Hellos to keep link up */
  u8 check_link;
  uint port;
  uint hello_interval;			/* Hello interval, in us */
@@ -188,7 +190,10 @@ struct babel_neighbor {
  struct babel_iface *ifa;

  ip_addr addr;
  u16 txcost;
  u16 rxcost;				/* Sent in last IHU */
  u16 txcost;				/* Received in last IHU */
  u16 cost;				/* Computed neighbor cost */
  s8 ihu_cnt;				/* IHU countdown, 0 to send it */
  u8 hello_cnt;
  u16 hello_map;
  u16 next_hello_seqno;
@@ -218,6 +223,7 @@ struct babel_route {
  u16 seqno;
  u16 advert_metric;
  u16 metric;
  u16 old_metric;
  u64 router_id;
  ip_addr next_hop;
  btime refresh_time;
+2 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ babel_iface_start:
  init_list(&this_ipatt->ipn_list);
  BABEL_IFACE->port = BABEL_PORT;
  BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED;
  BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
  BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
  BABEL_IFACE->tx_priority = sk_priority_control;
  BABEL_IFACE->check_link = 1;
@@ -89,6 +90,7 @@ babel_iface_finish:
babel_iface_item:
 | PORT expr { BABEL_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); }
 | RXCOST expr { BABEL_IFACE->rxcost = $2; if (($2<1) || ($2>65535)) cf_error("Invalid rxcost"); }
 | LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); }
 | TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
 | TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
 | HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); }