]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implements IGP metric comparison for BGP routes.
authorOndrej Zajicek <santiago@crfreenet.org>
Fri, 30 Jul 2010 23:04:32 +0000 (01:04 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Fri, 30 Jul 2010 23:04:32 +0000 (01:04 +0200)
nest/route.h
nest/rt-attr.c
nest/rt-table.c
proto/bgp/attrs.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/bgp/packets.c

index 4dd437501f8f5fb778a8ef67d65295c04a50135c..d062e60024235773e90ef132a9fdf1077e9126e4 100644 (file)
@@ -173,6 +173,7 @@ struct hostentry {
   struct iface *iface;                 /* Chosen outgoing interface */
   ip_addr gw;                          /* Chosen next hop */
   byte dest;                           /* Chosen route destination type (RTD_...) */
+  u32 igp_metric;                      /* Chosen route IGP metric */
 };
 
 typedef struct rte {
@@ -276,6 +277,7 @@ typedef struct rta {
   byte flags;                          /* Route flags (RTF_...), now unused */
   byte aflags;                         /* Attribute cache flags (RTAF_...) */
   u16 hash_key;                                /* Hash over important fields */
+  u32 igp_metric;                      /* IGP metric to next hop (for iBGP routes) */
   ip_addr gw;                          /* Next hop */
   ip_addr from;                                /* Advertising router */
   struct hostentry *hostentry;         /* Hostentry for recursive next-hops */
@@ -311,6 +313,9 @@ typedef struct rta {
 
 #define RTAF_CACHED 1                  /* This is a cached rta */
 
+#define IGP_METRIC_UNKNOWN 0x80000000  /* Default igp_metric used when no other
+                                          protocol-specific metric is availabe */
+
 /*
  *     Extended Route Attributes
  */
index 9caee8d5af4c645647adc3da641b0592cd579178..d71481889acd2a8ec2c6f334baa66bbc6fc9bdcc 100644 (file)
@@ -584,9 +584,11 @@ rta_same(rta *x, rta *y)
          x->cast == y->cast &&
          x->dest == y->dest &&
          x->flags == y->flags &&
+         x->igp_metric == y->igp_metric &&
          ipa_equal(x->gw, y->gw) &&
          ipa_equal(x->from, y->from) &&
          x->iface == y->iface &&
+         x->hostentry == y->hostentry &&
          ea_same(x->eattrs, y->eattrs));
 }
 
index 1f84e975f7add465236a085b767e4db01af40106..b73f52fa63585b6aa030b62c4aea9c08ca3d4ce8 100644 (file)
@@ -59,6 +59,24 @@ static void rt_prune(rtable *tab);
 
 static inline void rt_schedule_gc(rtable *tab);
 
+/* Like fib_route(), but skips empty net entries */
+static net *
+net_route(rtable *tab, ip_addr a, int len)
+{
+  ip_addr a0;
+  net *n;
+
+  while (len >= 0)
+    {
+      a0 = ipa_and(a, ipa_mkmask(len));
+      n = fib_find(&tab->fib, &a0, len);
+      if (n && n->routes)
+       return n;
+      len--;
+    }
+  return NULL;
+}
+
 static void
 rte_init(struct fib_node *N)
 {
@@ -945,16 +963,18 @@ rt_preconfig(struct config *c)
  */
 
 static inline int
-hostentry_diff(struct hostentry *he, struct iface *iface, ip_addr gw, byte dest)
+hostentry_diff(struct hostentry *he, struct iface *iface, ip_addr gw,
+              byte dest, u32 igp_metric)
 {
-  return (he->iface != iface) || !ipa_equal(he->gw, gw) || (he->dest != dest);
+  return (he->iface != iface) || !ipa_equal(he->gw, gw) ||
+    (he->dest != dest) || (he->igp_metric != igp_metric);
 }
 
 static inline int
 rta_next_hop_outdated(rta *a)
 {
   struct hostentry *he = a->hostentry;
-  return he && hostentry_diff(he, a->iface, a->gw, a->dest);
+  return he && hostentry_diff(he, a->iface, a->gw, a->dest, a->igp_metric);
 }
 
 static inline void
@@ -964,6 +984,7 @@ rta_apply_hostentry(rta *a, struct hostentry *he)
   a->iface = he->iface;
   a->gw = he->gw;
   a->dest = he->dest;
+  a->igp_metric = he->igp_metric;
 }
 
 static inline rte *
@@ -1449,16 +1470,36 @@ if_local_addr(ip_addr a, struct iface *i)
   return 0;
 }
 
+static u32 
+rt_get_igp_metric(rte *rt)
+{
+  rta *a = rt->attrs;
+  if ((a->source == RTS_OSPF) ||
+      (a->source == RTS_OSPF_IA) ||
+      (a->source == RTS_OSPF_EXT1))
+    return rt->u.ospf.metric1;
+
+  if (a->source == RTS_RIP)
+    return rt->u.rip.metric;
+
+  /* Device routes */
+  if (a->dest != RTD_ROUTER)
+    return 0;
+
+  return IGP_METRIC_UNKNOWN;
+}
+
 static int
 rt_update_hostentry(rtable *tab, struct hostentry *he)
 {
   struct iface *old_iface = he->iface;
   ip_addr old_gw = he->gw;
   byte old_dest = he->dest;
+  u32 old_metric = he->igp_metric;
   int pxlen = 0;
 
-  net *n = fib_route(&tab->fib, he->addr, MAX_PREFIX_LENGTH);
-  if (n && n->routes)
+  net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH);
+  if (n)
     {
       rta *a = n->routes->attrs;
       pxlen = n->n.pxlen;
@@ -1489,6 +1530,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
          he->gw = a->gw;
          he->dest = a->dest;
        }
+
+      he->igp_metric = he->iface ? rt_get_igp_metric(n->routes) : 0;
     }
   else
     {
@@ -1496,12 +1539,13 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
       he->iface = NULL;
       he->gw = IPA_NONE;
       he->dest = RTD_UNREACHABLE;
+      he->igp_metric = 0;
     }
 
   /* Add a prefix range to the trie */
   trie_add_prefix(tab->hostcache->trie, he->addr, MAX_PREFIX_LENGTH, pxlen, MAX_PREFIX_LENGTH);
 
-  return hostentry_diff(he, old_iface, old_gw, old_dest);
+  return hostentry_diff(he, old_iface, old_gw, old_dest, old_metric);
 }
 
 static void
@@ -1730,9 +1774,9 @@ rt_show(struct rt_show_data *d)
   else
     {
       if (d->show_for)
-       n = fib_route(&d->table->fib, d->prefix, d->pxlen);
+       n = net_route(d->table, d->prefix, d->pxlen);
       else
-       n = fib_find(&d->table->fib, &d->prefix, d->pxlen);
+       n = net_find(d->table, d->prefix, d->pxlen);
       if (n)
        {
          rt_show_net(this_cli, n, d);
index 3e7c94a608424a57e942459536264053289bcbe5..ef5d024ec59be8bb6dd04aa19356cadae41ad665 100644 (file)
@@ -1084,8 +1084,13 @@ bgp_rte_better(rte *new, rte *old)
   if (new_bgp->is_internal < old_bgp->is_internal)
     return 1;
 
-  /* Skipping RFC 4271 9.1.2.2. e) */
-  /* We don't have interior distances */
+  /* RFC 4271 9.1.2.2. e) Compare IGP metrics */
+  n = new_bgp->cf->igp_metric ? new->attrs->igp_metric : 0;
+  o = old_bgp->cf->igp_metric ? old->attrs->igp_metric : 0;
+  if (n < o)
+    return 1;
+  if (n > o)
+    return 0;
 
   /* RFC 4271 9.1.2.2. f) Compare BGP identifiers */
   /* RFC 4456 9. a) Use ORIGINATOR_ID instead of local neighor ID */
@@ -1494,7 +1499,18 @@ bgp_get_route_info(rte *e, byte *buf, ea_list *attrs)
   eattr *o = ea_find(attrs, EA_CODE(EAP_BGP, BA_ORIGIN));
   u32 origas;
 
-  buf += bsprintf(buf, " (%d) [", e->pref);
+  buf += bsprintf(buf, " (%d", e->pref);
+  if (e->attrs->hostentry)
+    {
+      if (!e->attrs->iface)
+       buf += bsprintf(buf, "/-");
+      else if (e->attrs->igp_metric >= IGP_METRIC_UNKNOWN)
+       buf += bsprintf(buf, "/?");
+      else
+       buf += bsprintf(buf, "/%d", e->attrs->igp_metric);
+    }
+  buf += bsprintf(buf, ") [");
+
   if (p && as_path_get_last(p->u.ptr, &origas))
     buf += bsprintf(buf, "AS%u", origas);
   if (o)
index 6bb1d6e5dc986222895b6e916b7fe9d1966c8d51..b06f20a0f5f9fd3ac7139a169b1159690feaa1b9 100644 (file)
@@ -25,6 +25,7 @@ struct bgp_config {
   int missing_lladdr;                  /* What we will do when we don' know link-local addr, see MLL_* */
   int gw_mode;                         /* How we compute route gateway from next_hop attr, see GW_* */
   int compare_path_lengths;            /* Use path lengths when selecting best route */
+  int igp_metric;                      /* Use IGP metrics when selecting best route */
   int prefer_older;                    /* Prefer older routes according to RFC 5004 */
   u32 default_local_pref;              /* Default value for LOCAL_PREF attribute */
   u32 default_med;                     /* Default value for MULTI_EXIT_DISC attribute */
index 75b93391cf482a5c9ec83abbd7524b901ac62e75..e932a7f6ef174fdafc4e383ee093557b5d25a687 100644 (file)
@@ -38,6 +38,7 @@ bgp_proto_start: proto_start BGP {
      BGP_CFG->connect_retry_time = 120;
      BGP_CFG->initial_hold_time = 240;
      BGP_CFG->compare_path_lengths = 1;
+     BGP_CFG->igp_metric = 1;
      BGP_CFG->start_delay_time = 5;
      BGP_CFG->error_amnesia_time = 300;
      BGP_CFG->error_delay_time_min = 60;
@@ -78,6 +79,7 @@ bgp_proto:
  | bgp_proto GATEWAY DIRECT ';' { BGP_CFG->gw_mode = GW_DIRECT; }
  | bgp_proto GATEWAY RECURSIVE ';' { BGP_CFG->gw_mode = GW_RECURSIVE; }
  | bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; }
+ | bgp_proto IGP METRIC bool ';' { BGP_CFG->igp_metric = $4; }
  | bgp_proto PREFER OLDER bool ';' { BGP_CFG->prefer_older = $4; }
  | bgp_proto DEFAULT BGP_MED expr ';' { BGP_CFG->default_med = $4; }
  | bgp_proto DEFAULT BGP_LOCAL_PREF expr ';' { BGP_CFG->default_local_pref = $4; }
index 632c564e1a012cfa7077a3c21fe59c3de39e16ba..29d23b994ecf453282a7d67199ccc3c56ce883e8 100644 (file)
@@ -823,6 +823,7 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
       a->gw = ng->addr;
       a->iface = ng->iface;
       a->hostentry = NULL;
+      a->igp_metric = 0;
     }
   else /* GW_RECURSIVE */
     rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);