diff --git a/nest/route.h b/nest/route.h index 4dd43750..d062e600 100644 --- a/nest/route.h +++ b/nest/route.h @@ -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 */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 9caee8d5..d7148188 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -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)); } diff --git a/nest/rt-table.c b/nest/rt-table.c index 1f84e975..b73f52fa 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -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); diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 3e7c94a6..ef5d024e 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -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) diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 6bb1d6e5..b06f20a0 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -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 */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 75b93391..e932a7f6 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -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; } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 632c564e..29d23b99 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -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);