Merged multipath and single-path data structures.

Dropped struct mpnh and mpnh_*()
Now struct nexthop exists, nexthop_*(), and also included struct nexthop
into struct rta.

Also converted RTD_DEVICE and RTD_ROUTER to RTD_UNICAST. If it is needed
to distinguish between these two cases, RTD_DEVICE is equivalent to
IPA_ZERO(a->nh.gw), RTD_ROUTER is then IPA_NONZERO(a->nh.gw).

From now on, we also explicitely want C99 compatible compiler. We assume
that this 20-year norm should be known almost everywhere.
This commit is contained in:
Jan Moskyto Matejka 2016-05-06 15:48:35 +02:00
parent b7605d5c95
commit 4e276a8920
21 changed files with 434 additions and 499 deletions

View file

@ -57,6 +57,7 @@ if test "$ac_test_CFLAGS" != set ; then
bird_cflags_default=yes bird_cflags_default=yes
fi fi
AC_PROG_CC
AC_PROG_CC_C99 AC_PROG_CC_C99
if test -z "$GCC" ; then if test -z "$GCC" ; then
AC_MSG_ERROR([This program requires the GNU C Compiler.]) AC_MSG_ERROR([This program requires the GNU C Compiler.])

View file

@ -900,15 +900,15 @@ interpret(struct f_inst *what)
switch (what->a2.i) switch (what->a2.i)
{ {
case SA_FROM: res.val.ip = rta->from; break; case SA_FROM: res.val.ip = rta->from; break;
case SA_GW: res.val.ip = rta->gw; break; case SA_GW: res.val.ip = rta->nh.gw; break;
case SA_NET: res.val.net = (*f_rte)->net->n.addr; break; case SA_NET: res.val.net = (*f_rte)->net->n.addr; break;
case SA_PROTO: res.val.s = rta->src->proto->name; break; case SA_PROTO: res.val.s = rta->src->proto->name; break;
case SA_SOURCE: res.val.i = rta->source; break; case SA_SOURCE: res.val.i = rta->source; break;
case SA_SCOPE: res.val.i = rta->scope; break; case SA_SCOPE: res.val.i = rta->scope; break;
case SA_CAST: res.val.i = rta->cast; break; case SA_CAST: res.val.i = rta->cast; break;
case SA_DEST: res.val.i = rta->dest; break; case SA_DEST: res.val.i = rta->dest; break;
case SA_IFNAME: res.val.s = rta->iface ? rta->iface->name : ""; break; case SA_IFNAME: res.val.s = rta->nh.iface ? rta->nh.iface->name : ""; break;
case SA_IFINDEX: res.val.i = rta->iface ? rta->iface->index : 0; break; case SA_IFINDEX: res.val.i = rta->nh.iface ? rta->nh.iface->index : 0; break;
default: default:
bug("Invalid static attribute access (%x)", res.type); bug("Invalid static attribute access (%x)", res.type);
@ -938,10 +938,10 @@ interpret(struct f_inst *what)
if (!n || (n->scope == SCOPE_HOST)) if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" ); runtime( "Invalid gw address" );
rta->dest = RTD_ROUTER; rta->dest = RTD_UNICAST;
rta->gw = ip; rta->nh.gw = ip;
rta->iface = n->iface; rta->nh.iface = n->iface;
rta->nexthops = NULL; rta->nh.next = NULL;
rta->hostentry = NULL; rta->hostentry = NULL;
} }
break; break;
@ -956,9 +956,9 @@ interpret(struct f_inst *what)
runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
rta->dest = i; rta->dest = i;
rta->gw = IPA_NONE; rta->nh.gw = IPA_NONE;
rta->iface = NULL; rta->nh.iface = NULL;
rta->nexthops = NULL; rta->nh.next = NULL;
rta->hostentry = NULL; rta->hostentry = NULL;
break; break;

View file

@ -79,7 +79,7 @@ CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIREC
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL) RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST) CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST)
CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULTIPATH) CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
%type <i32> idval %type <i32> idval

View file

@ -195,7 +195,7 @@ struct hostentry {
unsigned hash_key; /* Hash key */ unsigned hash_key; /* Hash key */
unsigned uc; /* Use count */ unsigned uc; /* Use count */
struct rta *src; /* Source rta entry */ struct rta *src; /* Source rta entry */
ip_addr gw; /* Chosen next hop */ struct nexthop *nh; /* Chosen next hop */
byte dest; /* Chosen route destination type (RTD_...) */ byte dest; /* Chosen route destination type (RTD_...) */
u32 igp_metric; /* Chosen route IGP metric */ u32 igp_metric; /* Chosen route IGP metric */
}; };
@ -332,11 +332,11 @@ void rt_show(struct rt_show_data *);
* construction of BGP route attribute lists. * construction of BGP route attribute lists.
*/ */
/* Multipath next-hop */ /* Nexthop structure */
struct mpnh { struct nexthop {
ip_addr gw; /* Next hop */ ip_addr gw; /* Next hop */
struct iface *iface; /* Outgoing interface */ struct iface *iface; /* Outgoing interface */
struct mpnh *next; struct nexthop *next;
byte weight; byte weight;
}; };
@ -353,20 +353,19 @@ typedef struct rta {
struct rta *next, **pprev; /* Hash chain */ struct rta *next, **pprev; /* Hash chain */
u32 uc; /* Use count */ u32 uc; /* Use count */
u32 hash_key; /* Hash over important fields */ u32 hash_key; /* Hash over important fields */
struct mpnh *nexthops; /* Next-hops for multipath routes */
struct ea_list *eattrs; /* Extended Attribute chain */ struct ea_list *eattrs; /* Extended Attribute chain */
struct rte_src *src; /* Route source that created the route */ struct rte_src *src; /* Route source that created the route */
struct hostentry *hostentry; /* Hostentry for recursive next-hops */ struct hostentry *hostentry; /* Hostentry for recursive next-hops */
struct iface *iface; /* Outgoing interface */
ip_addr gw; /* Next hop */
ip_addr from; /* Advertising router */ ip_addr from; /* Advertising router */
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */ u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
byte source; /* Route source (RTS_...) */ u32 bf[0];
byte scope; /* Route scope (SCOPE_... -- see ip.h) */ u32 source:6; /* Route source (RTS_...) */
byte cast; /* Casting type (RTC_...) */ u32 scope:6; /* Route scope (SCOPE_... -- see ip.h) */
byte dest; /* Route destination type (RTD_...) */ u32 cast:6; /* Casting type (RTC_...) */
byte flags; /* Route flags (RTF_...), now unused */ u32 dest:6; /* Route destination type (RTD_...) */
byte aflags; /* Attribute cache flags (RTAF_...) */ // u32 eflags:8; /* Flags (RTAF_...) */
u32 aflags:8;
struct nexthop nh; /* Next hop */
} rta; } rta;
#define RTS_DUMMY 0 /* Dummy route to be removed soon */ #define RTS_DUMMY 0 /* Dummy route to be removed soon */
@ -391,12 +390,10 @@ typedef struct rta {
#define RTC_MULTICAST 2 #define RTC_MULTICAST 2
#define RTC_ANYCAST 3 /* IPv6 Anycast */ #define RTC_ANYCAST 3 /* IPv6 Anycast */
#define RTD_ROUTER 0 /* Next hop is neighbor router */ #define RTD_UNICAST 0 /* Next hop is neighbor router */
#define RTD_DEVICE 1 /* Points to device */
#define RTD_BLACKHOLE 2 /* Silently drop packets */ #define RTD_BLACKHOLE 2 /* Silently drop packets */
#define RTD_UNREACHABLE 3 /* Reject as unreachable */ #define RTD_UNREACHABLE 3 /* Reject as unreachable */
#define RTD_PROHIBIT 4 /* Administratively prohibited */ #define RTD_PROHIBIT 4 /* Administratively prohibited */
#define RTD_MULTIPATH 5 /* Multipath route (nexthops != NULL) */
#define RTD_NONE 6 /* Invalid RTD */ #define RTD_NONE 6 /* Invalid RTD */
/* Flags for net->n.flags, used by kernel syncer */ /* Flags for net->n.flags, used by kernel syncer */
@ -411,7 +408,7 @@ typedef struct rta {
/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */ /* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */
static inline int rte_is_reachable(rte *r) static inline int rte_is_reachable(rte *r)
{ uint d = r->attrs->dest; return (d == RTD_ROUTER) || (d == RTD_DEVICE) || (d == RTD_MULTIPATH); } { uint d = r->attrs->dest; return (d == RTD_UNICAST); }
/* /*
@ -516,12 +513,14 @@ uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
ea_list *ea_append(ea_list *to, ea_list *what); ea_list *ea_append(ea_list *to, ea_list *what);
void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max); void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */ int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath nexthops */
static inline int mpnh_same(struct mpnh *x, struct mpnh *y) static inline int nexthop_same(struct nexthop *x, struct nexthop *y)
{ return (x == y) || mpnh__same(x, y); } { return (x == y) || nexthop__same(x, y); }
struct mpnh *mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp); struct nexthop *nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp);
void mpnh_insert(struct mpnh **n, struct mpnh *y); static inline void nexthop_link(struct rta *a, struct nexthop *from)
int mpnh_is_sorted(struct mpnh *x); { a->nh.gw = from->gw; a->nh.iface = from->iface; a->nh.weight = from->weight; a->nh.next = from->next; }
void nexthop_insert(struct nexthop *n, struct nexthop *y);
int nexthop_is_sorted(struct nexthop *x);
void rta_init(void); void rta_init(void);
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */ rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */

View file

@ -61,7 +61,7 @@
pool *rta_pool; pool *rta_pool;
static slab *rta_slab; static slab *rta_slab;
static slab *mpnh_slab; static slab *nexthop_slab;
static slab *rte_src_slab; static slab *rte_src_slab;
static struct idm src_ids; static struct idm src_ids;
@ -144,7 +144,7 @@ rt_prune_sources(void)
*/ */
static inline u32 static inline u32
mpnh_hash(struct mpnh *x) nexthop_hash(struct nexthop *x)
{ {
u32 h = 0; u32 h = 0;
for (; x; x = x->next) for (; x; x = x->next)
@ -154,7 +154,7 @@ mpnh_hash(struct mpnh *x)
} }
int int
mpnh__same(struct mpnh *x, struct mpnh *y) nexthop__same(struct nexthop *x, struct nexthop *y)
{ {
for (; x && y; x = x->next, y = y->next) for (; x && y; x = x->next, y = y->next)
if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) || (x->weight != y->weight)) if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) || (x->weight != y->weight))
@ -164,7 +164,7 @@ mpnh__same(struct mpnh *x, struct mpnh *y)
} }
static int static int
mpnh_compare_node(struct mpnh *x, struct mpnh *y) nexthop_compare_node(struct nexthop *x, struct nexthop *y)
{ {
int r; int r;
@ -185,10 +185,10 @@ mpnh_compare_node(struct mpnh *x, struct mpnh *y)
return ((int) x->iface->index) - ((int) y->iface->index); return ((int) x->iface->index) - ((int) y->iface->index);
} }
static inline struct mpnh * static inline struct nexthop *
mpnh_copy_node(const struct mpnh *src, linpool *lp) nexthop_copy_node(const struct nexthop *src, linpool *lp)
{ {
struct mpnh *n = lp_alloc(lp, sizeof(struct mpnh)); struct nexthop *n = lp_alloc(lp, sizeof(struct nexthop));
n->gw = src->gw; n->gw = src->gw;
n->iface = src->iface; n->iface = src->iface;
n->next = NULL; n->next = NULL;
@ -197,7 +197,7 @@ mpnh_copy_node(const struct mpnh *src, linpool *lp)
} }
/** /**
* mpnh_merge - merge nexthop lists * nexthop_merge - merge nexthop lists
* @x: list 1 * @x: list 1
* @y: list 2 * @y: list 2
* @rx: reusability of list @x * @rx: reusability of list @x
@ -205,7 +205,7 @@ mpnh_copy_node(const struct mpnh *src, linpool *lp)
* @max: max number of nexthops * @max: max number of nexthops
* @lp: linpool for allocating nexthops * @lp: linpool for allocating nexthops
* *
* The mpnh_merge() function takes two nexthop lists @x and @y and merges them, * The nexthop_merge() function takes two nexthop lists @x and @y and merges them,
* eliminating possible duplicates. The input lists must be sorted and the * eliminating possible duplicates. The input lists must be sorted and the
* result is sorted too. The number of nexthops in result is limited by @max. * result is sorted too. The number of nexthops in result is limited by @max.
* New nodes are allocated from linpool @lp. * New nodes are allocated from linpool @lp.
@ -218,28 +218,28 @@ mpnh_copy_node(const struct mpnh *src, linpool *lp)
* resulting list is no longer needed. When reusability is not set, the * resulting list is no longer needed. When reusability is not set, the
* corresponding lists are not modified nor linked from the resulting list. * corresponding lists are not modified nor linked from the resulting list.
*/ */
struct mpnh * struct nexthop *
mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp) nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp)
{ {
struct mpnh *root = NULL; struct nexthop *root = NULL;
struct mpnh **n = &root; struct nexthop **n = &root;
while ((x || y) && max--) while ((x || y) && max--)
{ {
int cmp = mpnh_compare_node(x, y); int cmp = nexthop_compare_node(x, y);
if (cmp < 0) if (cmp < 0)
{ {
*n = rx ? x : mpnh_copy_node(x, lp); *n = rx ? x : nexthop_copy_node(x, lp);
x = x->next; x = x->next;
} }
else if (cmp > 0) else if (cmp > 0)
{ {
*n = ry ? y : mpnh_copy_node(y, lp); *n = ry ? y : nexthop_copy_node(y, lp);
y = y->next; y = y->next;
} }
else else
{ {
*n = rx ? x : (ry ? y : mpnh_copy_node(x, lp)); *n = rx ? x : (ry ? y : nexthop_copy_node(x, lp));
x = x->next; x = x->next;
y = y->next; y = y->next;
} }
@ -251,43 +251,55 @@ mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp)
} }
void void
mpnh_insert(struct mpnh **n, struct mpnh *x) nexthop_insert(struct nexthop *n, struct nexthop *x)
{ {
for (; *n; n = &((*n)->next)) struct nexthop tmp;
memcpy(&tmp, n, sizeof(struct nexthop));
if (nexthop_compare_node(n, x) > 0) /* Insert to the included nexthop */
{ {
int cmp = mpnh_compare_node(*n, x); memcpy(n, x, sizeof(struct nexthop));
memcpy(x, &tmp, sizeof(struct nexthop));
if (cmp < 0) n->next = x;
continue; return;
else if (cmp > 0) }
break;
else for (struct nexthop **nn = &(n->next); *nn; nn = &((*nn)->next))
{
int cmp = nexthop_compare_node(*nn, x);
if (cmp < 0)
continue;
if (cmp > 0)
{
x->next = *nn;
*nn = x;
}
return; return;
} }
x->next = *n;
*n = x;
} }
int int
mpnh_is_sorted(struct mpnh *x) nexthop_is_sorted(struct nexthop *x)
{ {
for (; x && x->next; x = x->next) for (; x && x->next; x = x->next)
if (mpnh_compare_node(x, x->next) >= 0) if (nexthop_compare_node(x, x->next) >= 0)
return 0; return 0;
return 1; return 1;
} }
static struct mpnh * static struct nexthop *
mpnh_copy(struct mpnh *o) nexthop_copy(struct nexthop *o)
{ {
struct mpnh *first = NULL; struct nexthop *first = NULL;
struct mpnh **last = &first; struct nexthop **last = &first;
for (; o; o = o->next) for (; o; o = o->next)
{ {
struct mpnh *n = sl_alloc(mpnh_slab); struct nexthop *n = sl_alloc(nexthop_slab);
n->gw = o->gw; n->gw = o->gw;
n->iface = o->iface; n->iface = o->iface;
n->next = NULL; n->next = NULL;
@ -301,14 +313,14 @@ mpnh_copy(struct mpnh *o)
} }
static void static void
mpnh_free(struct mpnh *o) nexthop_free(struct nexthop *o)
{ {
struct mpnh *n; struct nexthop *n;
while (o) while (o)
{ {
n = o->next; n = o->next;
sl_free(mpnh_slab, o); sl_free(nexthop_slab, o);
o = n; o = n;
} }
} }
@ -994,19 +1006,12 @@ rta_hash(rta *a)
#define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f)); #define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f));
MIX(src); MIX(src);
MIX(hostentry); MIX(hostentry);
MIX(iface);
MIX(gw);
MIX(from); MIX(from);
MIX(igp_metric); MIX(igp_metric);
MIX(source); mem_hash_mix(&h, a->bf, sizeof(u32));
MIX(scope);
MIX(cast);
MIX(dest);
MIX(flags);
MIX(aflags);
#undef MIX #undef MIX
return mem_hash_value(&h) ^ mpnh_hash(a->nexthops) ^ ea_hash(a->eattrs); return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs);
} }
static inline int static inline int
@ -1017,13 +1022,12 @@ rta_same(rta *x, rta *y)
x->scope == y->scope && x->scope == y->scope &&
x->cast == y->cast && x->cast == y->cast &&
x->dest == y->dest && x->dest == y->dest &&
x->flags == y->flags &&
x->igp_metric == y->igp_metric && x->igp_metric == y->igp_metric &&
ipa_equal(x->gw, y->gw) && ipa_equal(x->nh.gw, y->nh.gw) &&
ipa_equal(x->from, y->from) && ipa_equal(x->from, y->from) &&
x->iface == y->iface && x->nh.iface == y->nh.iface &&
x->hostentry == y->hostentry && x->hostentry == y->hostentry &&
mpnh_same(x->nexthops, y->nexthops) && nexthop_same(&(x->nh), &(y->nh)) &&
ea_same(x->eattrs, y->eattrs)); ea_same(x->eattrs, y->eattrs));
} }
@ -1034,7 +1038,7 @@ rta_copy(rta *o)
memcpy(r, o, sizeof(rta)); memcpy(r, o, sizeof(rta));
r->uc = 1; r->uc = 1;
r->nexthops = mpnh_copy(o->nexthops); r->nh.next = nexthop_copy(o->nh.next);
r->eattrs = ea_list_copy(o->eattrs); r->eattrs = ea_list_copy(o->eattrs);
return r; return r;
} }
@ -1130,7 +1134,8 @@ rta__free(rta *a)
a->aflags = 0; /* Poison the entry */ a->aflags = 0; /* Poison the entry */
rt_unlock_hostentry(a->hostentry); rt_unlock_hostentry(a->hostentry);
rt_unlock_source(a->src); rt_unlock_source(a->src);
mpnh_free(a->nexthops); if (a->nh.next)
nexthop_free(a->nh.next);
ea_free(a->eattrs); ea_free(a->eattrs);
sl_free(rta_slab, a); sl_free(rta_slab, a);
} }
@ -1167,10 +1172,12 @@ rta_dump(rta *a)
if (!(a->aflags & RTAF_CACHED)) if (!(a->aflags & RTAF_CACHED))
debug(" !CACHED"); debug(" !CACHED");
debug(" <-%I", a->from); debug(" <-%I", a->from);
if (a->dest == RTD_ROUTER) if (a->dest == RTD_UNICAST)
debug(" ->%I", a->gw); for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
if (a->dest == RTD_DEVICE || a->dest == RTD_ROUTER) {
debug(" [%s]", a->iface ? a->iface->name : "???" ); if (ipa_nonzero(nh->gw)) debug(" ->%I", nh->gw);
debug(" [%s]", nh->iface ? nh->iface->name : "???");
}
if (a->eattrs) if (a->eattrs)
{ {
debug(" EA: "); debug(" EA: ");
@ -1228,7 +1235,7 @@ rta_init(void)
{ {
rta_pool = rp_new(&root_pool, "Attributes"); rta_pool = rp_new(&root_pool, "Attributes");
rta_slab = sl_new(rta_pool, sizeof(rta)); rta_slab = sl_new(rta_pool, sizeof(rta));
mpnh_slab = sl_new(rta_pool, sizeof(struct mpnh)); nexthop_slab = sl_new(rta_pool, sizeof(struct nexthop));
rta_alloc_hash(); rta_alloc_hash();
rte_src_init(); rte_src_init();
} }

View file

@ -79,8 +79,10 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
.source = RTS_DEVICE, .source = RTS_DEVICE,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
.cast = RTC_UNICAST, .cast = RTC_UNICAST,
.dest = RTD_DEVICE, .dest = RTD_UNICAST,
.nh = {
.iface = ad->iface .iface = ad->iface
}
}; };
a = rta_lookup(&a0); a = rta_lookup(&a0);

View file

@ -708,19 +708,17 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
} }
static struct mpnh * static struct nexthop *
mpnh_merge_rta(struct mpnh *nhs, rta *a, linpool *pool, int max) nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
{ {
struct mpnh nh = { .gw = a->gw, .iface = a->iface }; return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
struct mpnh *nh2 = (a->dest == RTD_MULTIPATH) ? a->nexthops : &nh;
return mpnh_merge(nhs, nh2, 1, 0, max, pool);
} }
rte * rte *
rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent) rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
{ {
// struct proto *p = c->proto; // struct proto *p = c->proto;
struct mpnh *nhs = NULL; struct nexthop *nhs = NULL;
rte *best0, *best, *rt0, *rt, *tmp; rte *best0, *best, *rt0, *rt, *tmp;
best0 = net->routes; best0 = net->routes;
@ -745,7 +743,7 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
continue; continue;
if (rte_is_reachable(rt)) if (rte_is_reachable(rt))
nhs = mpnh_merge_rta(nhs, rt->attrs, pool, c->merge_limit); nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
if (tmp) if (tmp)
rte_free(tmp); rte_free(tmp);
@ -753,13 +751,12 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
if (nhs) if (nhs)
{ {
nhs = mpnh_merge_rta(nhs, best->attrs, pool, c->merge_limit); nhs = nexthop_merge_rta(nhs, best->attrs, pool, c->merge_limit);
if (nhs->next) if (nhs->next)
{ {
best = rte_cow_rta(best, pool); best = rte_cow_rta(best, pool);
best->attrs->dest = RTD_MULTIPATH; nexthop_link(best->attrs, nhs);
best->attrs->nexthops = nhs;
} }
} }
@ -922,7 +919,7 @@ rte_validate(rte *e)
return 0; return 0;
} }
if ((e->attrs->dest == RTD_MULTIPATH) && !mpnh_is_sorted(e->attrs->nexthops)) if ((e->attrs->dest == RTD_UNICAST) && !nexthop_is_sorted(&(e->attrs->nh)))
{ {
log(L_WARN "Ignoring unsorted multipath route %N received via %s", log(L_WARN "Ignoring unsorted multipath route %N received via %s",
n->n.addr, e->sender->proto->name); n->n.addr, e->sender->proto->name);
@ -1763,20 +1760,22 @@ rta_next_hop_outdated(rta *a)
if (!he->src) if (!he->src)
return a->dest != RTD_UNREACHABLE; return a->dest != RTD_UNREACHABLE;
return (a->iface != he->src->iface) || !ipa_equal(a->gw, he->gw) || return (a->dest != he->dest) || (a->igp_metric != he->igp_metric) ||
(a->dest != he->dest) || (a->igp_metric != he->igp_metric) || !nexthop_same(&(a->nh), he->nh);
!mpnh_same(a->nexthops, he->src->nexthops);
} }
static inline void static inline void
rta_apply_hostentry(rta *a, struct hostentry *he) rta_apply_hostentry(rta *a, struct hostentry *he)
{ {
a->hostentry = he; a->hostentry = he;
a->iface = he->src ? he->src->iface : NULL;
a->gw = he->gw; a->nh.gw = ipa_nonzero(he->nh->gw) ? he->nh->gw : he->link;
a->nh.iface = he->nh->iface;
a->nh.weight = he->nh->weight;
a->nh.next = he->nh->next;
a->dest = he->dest; a->dest = he->dest;
a->igp_metric = he->igp_metric; a->igp_metric = he->igp_metric;
a->nexthops = he->src ? he->src->nexthops : NULL;
} }
static inline rte * static inline rte *
@ -2310,8 +2309,7 @@ rt_get_igp_metric(rte *rt)
return rt->u.rip.metric; return rt->u.rip.metric;
#endif #endif
/* Device routes */ if (a->source == RTS_DEVICE)
if ((a->dest != RTD_ROUTER) && (a->dest != RTD_MULTIPATH))
return 0; return 0;
return IGP_METRIC_UNKNOWN; return IGP_METRIC_UNKNOWN;
@ -2325,7 +2323,6 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
/* Reset the hostentry */ /* Reset the hostentry */
he->src = NULL; he->src = NULL;
he->gw = IPA_NONE;
he->dest = RTD_UNREACHABLE; he->dest = RTD_UNREACHABLE;
he->igp_metric = 0; he->igp_metric = 0;
@ -2346,24 +2343,24 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
goto done; goto done;
} }
if (a->dest == RTD_DEVICE) if ((a->dest == RTD_UNICAST) && ipa_zero(a->nh.gw) && !a->next)
{ { /* We have singlepath device route */
if (if_local_addr(he->addr, a->iface)) if (if_local_addr(he->addr, a->nh.iface))
{ {
/* The host address is a local address, this is not valid */ /* The host address is a local address, this is not valid */
log(L_WARN "Next hop address %I is a local address of iface %s", log(L_WARN "Next hop address %I is a local address of iface %s",
he->addr, a->iface->name); he->addr, a->nh.iface->name);
goto done; goto done;
} }
/* The host is directly reachable, use link as a gateway */ /* The host is directly reachable, use link as a gateway */
he->gw = he->link; he->nh = NULL;
he->dest = RTD_ROUTER; he->dest = RTD_UNICAST;
} }
else else
{ {
/* The host is reachable through some route entry */ /* The host is reachable through some route entry */
he->gw = a->gw; he->nh = (&a->nh);
he->dest = a->dest; he->dest = a->dest;
} }
@ -2442,16 +2439,21 @@ rt_format_via(rte *e)
rta *a = e->attrs; rta *a = e->attrs;
/* Max text length w/o IP addr and interface name is 16 */ /* Max text length w/o IP addr and interface name is 16 */
static byte via[IPA_MAX_TEXT_LENGTH+sizeof(a->iface->name)+16]; static byte via[IPA_MAX_TEXT_LENGTH+sizeof(a->nh.iface->name)+16];
switch (a->dest) switch (a->dest)
{ {
case RTD_ROUTER: bsprintf(via, "via %I on %s", a->gw, a->iface->name); break; case RTD_UNICAST: if (a->nh.next)
case RTD_DEVICE: bsprintf(via, "dev %s", a->iface->name); break; bsprintf(via, "multipath");
else
{
if (ipa_nonzero(a->nh.gw)) bsprintf(via, "via %I ", a->nh.gw);
bsprintf(via, "dev %s", a->nh.iface->name);
}
break;
case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break; case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break; case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
case RTD_PROHIBIT: bsprintf(via, "prohibited"); break; case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
default: bsprintf(via, "???"); default: bsprintf(via, "???");
} }
return via; return via;
@ -2466,10 +2468,10 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
int primary = (e->net->routes == e); int primary = (e->net->routes == e);
int sync_error = (e->net->n.flags & KRF_SYNC_ERROR); int sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs);
struct mpnh *nh; struct nexthop *nh;
tm_format_datetime(tm, &config->tf_route, e->lastmod); tm_format_datetime(tm, &config->tf_route, e->lastmod);
if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->gw)) if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
bsprintf(from, " from %I", a->from); bsprintf(from, " from %I", a->from);
else else
from[0] = 0; from[0] = 0;
@ -2490,7 +2492,8 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
bsprintf(info, " (%d)", e->pref); bsprintf(info, " (%d)", e->pref);
cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rt_format_via(e), a->src->proto->name, cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rt_format_via(e), a->src->proto->name,
tm, from, primary ? (sync_error ? " !" : " *") : "", info); tm, from, primary ? (sync_error ? " !" : " *") : "", info);
for (nh = a->nexthops; nh; nh = nh->next) if (a->nh.next)
for (nh = &(a->nh); nh; nh = nh->next)
cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1); cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1);
if (d->verbose) if (d->verbose)
rta_show(c, a, tmpa); rta_show(c, a, tmpa);

View file

@ -1462,7 +1462,7 @@ static inline int
rte_resolvable(rte *rt) rte_resolvable(rte *rt)
{ {
int rd = rt->attrs->dest; int rd = rt->attrs->dest;
return (rd == RTD_ROUTER) || (rd == RTD_DEVICE) || (rd == RTD_MULTIPATH); return (rd == RTD_UNICAST);
} }
int int

View file

@ -699,9 +699,10 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
if (!nbr || (nbr->scope == SCOPE_HOST)) if (!nbr || (nbr->scope == SCOPE_HOST))
WITHDRAW(BAD_NEXT_HOP); WITHDRAW(BAD_NEXT_HOP);
a->dest = RTD_ROUTER; a->dest = RTD_UNICAST;
a->gw = nbr->addr; a->nh.gw = nbr->addr;
a->iface = nbr->iface; a->nh.iface = nbr->iface;
a->nh.next = NULL;
a->hostentry = NULL; a->hostentry = NULL;
a->igp_metric = 0; a->igp_metric = 0;
} }
@ -749,7 +750,7 @@ bgp_use_gateway(struct bgp_export_state *s)
return 0; return 0;
/* We need valid global gateway */ /* We need valid global gateway */
if ((ra->dest != RTD_ROUTER) || ipa_zero(ra->gw) || ipa_is_link_local(ra->gw)) if ((ra->dest != RTD_UNICAST) || (ra->nh.next) || ipa_zero(ra->nh.gw) || ipa_is_link_local(ra->nh.gw))
return 0; return 0;
/* Use it when exported to internal peers */ /* Use it when exported to internal peers */
@ -757,7 +758,7 @@ bgp_use_gateway(struct bgp_export_state *s)
return 1; return 1;
/* Use it when forwarded to single-hop BGP peer on on the same iface */ /* Use it when forwarded to single-hop BGP peer on on the same iface */
return p->neigh && (p->neigh->iface == ra->iface); return p->neigh && (p->neigh->iface == ra->nh.iface);
} }
static void static void
@ -767,7 +768,7 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
{ {
if (bgp_use_gateway(s)) if (bgp_use_gateway(s))
{ {
ip_addr nh[1] = { s->route->attrs->gw }; ip_addr nh[1] = { s->route->attrs->nh.gw };
bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, 16); bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, 16);
} }
else else

View file

@ -235,7 +235,7 @@ ospf_start(struct proto *P)
p->lsab_size = 256; p->lsab_size = 256;
p->lsab_used = 0; p->lsab_used = 0;
p->lsab = mb_alloc(P->pool, p->lsab_size); p->lsab = mb_alloc(P->pool, p->lsab_size);
p->nhpool = lp_new(P->pool, 12*sizeof(struct mpnh)); p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop));
init_list(&(p->iface_list)); init_list(&(p->iface_list));
init_list(&(p->area_list)); init_list(&(p->area_list));
fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6, fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6,

View file

@ -22,7 +22,7 @@ static inline void reset_ri(ort *ort)
} }
static inline int static inline int
nh_is_vlink(struct mpnh *nhs) nh_is_vlink(struct nexthop *nhs)
{ {
return !nhs->iface; return !nhs->iface;
} }
@ -33,10 +33,10 @@ unresolved_vlink(ort *ort)
return ort->n.nhs && nh_is_vlink(ort->n.nhs); return ort->n.nhs && nh_is_vlink(ort->n.nhs);
} }
static inline struct mpnh * static inline struct nexthop *
new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight) new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight)
{ {
struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh)); struct nexthop *nh = lp_alloc(p->nhpool, sizeof(struct nexthop));
nh->gw = gw; nh->gw = gw;
nh->iface = iface; nh->iface = iface;
nh->next = NULL; nh->next = NULL;
@ -46,7 +46,7 @@ new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight)
/* Returns true if there are device nexthops in n */ /* Returns true if there are device nexthops in n */
static inline int static inline int
has_device_nexthops(const struct mpnh *n) has_device_nexthops(const struct nexthop *n)
{ {
for (; n; n = n->next) for (; n; n = n->next)
if (ipa_zero(n->gw)) if (ipa_zero(n->gw))
@ -56,13 +56,13 @@ has_device_nexthops(const struct mpnh *n)
} }
/* Replace device nexthops with nexthops to gw */ /* Replace device nexthops with nexthops to gw */
static struct mpnh * static struct nexthop *
fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw) fix_device_nexthops(struct ospf_proto *p, const struct nexthop *n, ip_addr gw)
{ {
struct mpnh *root1 = NULL; struct nexthop *root1 = NULL;
struct mpnh *root2 = NULL; struct nexthop *root2 = NULL;
struct mpnh **nn1 = &root1; struct nexthop **nn1 = &root1;
struct mpnh **nn2 = &root2; struct nexthop **nn2 = &root2;
if (!p->ecmp) if (!p->ecmp)
return new_nexthop(p, gw, n->iface, n->weight); return new_nexthop(p, gw, n->iface, n->weight);
@ -73,7 +73,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
for (; n; n = n->next) for (; n; n = n->next)
{ {
struct mpnh *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight); struct nexthop *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
if (ipa_zero(n->gw)) if (ipa_zero(n->gw))
{ {
@ -87,7 +87,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
} }
} }
return mpnh_merge(root1, root2, 1, 1, p->ecmp, p->nhpool); return nexthop_merge(root1, root2, 1, 1, p->ecmp, p->nhpool);
} }
@ -283,7 +283,7 @@ ort_merge(struct ospf_proto *p, ort *o, const orta *new)
if (old->nhs != new->nhs) if (old->nhs != new->nhs)
{ {
old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse, old->nhs = nexthop_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
p->ecmp, p->nhpool); p->ecmp, p->nhpool);
old->nhs_reuse = 1; old->nhs_reuse = 1;
} }
@ -299,7 +299,7 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new)
if (old->nhs != new->nhs) if (old->nhs != new->nhs)
{ {
old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse, old->nhs = nexthop_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
p->ecmp, p->nhpool); p->ecmp, p->nhpool);
old->nhs_reuse = 1; old->nhs_reuse = 1;
} }
@ -1673,18 +1673,18 @@ ospf_rt_spf(struct ospf_proto *p)
static inline int static inline int
inherit_nexthops(struct mpnh *pn) inherit_nexthops(struct nexthop *pn)
{ {
/* Proper nexthops (with defined GW) or dummy vlink nexthops (without iface) */ /* Proper nexthops (with defined GW) or dummy vlink nexthops (without iface) */
return pn && (ipa_nonzero(pn->gw) || !pn->iface); return pn && (ipa_nonzero(pn->gw) || !pn->iface);
} }
static struct mpnh * static struct nexthop *
calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
struct top_hash_entry *par, int pos) struct top_hash_entry *par, int pos)
{ {
struct ospf_proto *p = oa->po; struct ospf_proto *p = oa->po;
struct mpnh *pn = par->nhs; struct nexthop *pn = par->nhs;
struct ospf_iface *ifa; struct ospf_iface *ifa;
u32 rid = en->lsa.rt; u32 rid = en->lsa.rt;
@ -1812,7 +1812,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
if (!link_back(oa, en, par)) if (!link_back(oa, en, par))
return; return;
struct mpnh *nhs = calc_next_hop(oa, en, par, pos); struct nexthop *nhs = calc_next_hop(oa, en, par, pos);
if (!nhs) if (!nhs)
{ {
log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)", log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
@ -1850,7 +1850,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
/* Merge old and new */ /* Merge old and new */
int new_reuse = (par->nhs != nhs); int new_reuse = (par->nhs != nhs);
en->nhs = mpnh_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool); en->nhs = nexthop_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool);
en->nhs_reuse = 1; en->nhs_reuse = 1;
return; return;
} }
@ -1906,8 +1906,8 @@ ort_changed(ort *nf, rta *nr)
(nf->n.metric1 != nf->old_metric1) || (nf->n.metric2 != nf->old_metric2) || (nf->n.metric1 != nf->old_metric1) || (nf->n.metric2 != nf->old_metric2) ||
(nf->n.tag != nf->old_tag) || (nf->n.rid != nf->old_rid) || (nf->n.tag != nf->old_tag) || (nf->n.rid != nf->old_rid) ||
(nr->source != or->source) || (nr->dest != or->dest) || (nr->source != or->source) || (nr->dest != or->dest) ||
(nr->iface != or->iface) || !ipa_equal(nr->gw, or->gw) || (nr->nh.iface != or->nh.iface) || !ipa_equal(nr->nh.gw, or->nh.gw) ||
!mpnh_same(nr->nexthops, or->nexthops); !nexthop_same(&(nr->nh), &(or->nh));
} }
static void static void
@ -1931,7 +1931,7 @@ again1:
/* Sanity check of next-hop addresses, failure should not happen */ /* Sanity check of next-hop addresses, failure should not happen */
if (nf->n.type) if (nf->n.type)
{ {
struct mpnh *nh; struct nexthop *nh;
for (nh = nf->n.nhs; nh; nh = nh->next) for (nh = nf->n.nhs; nh; nh = nh->next)
if (ipa_nonzero(nh->gw)) if (ipa_nonzero(nh->gw))
{ {
@ -1954,22 +1954,8 @@ again1:
.cast = RTC_UNICAST .cast = RTC_UNICAST
}; };
if (nf->n.nhs->next) nexthop_link(&a0, nf->n.nhs);
{ a0.dest = RTD_UNICAST;
a0.dest = RTD_MULTIPATH;
a0.nexthops = nf->n.nhs;
}
else if (ipa_nonzero(nf->n.nhs->gw))
{
a0.dest = RTD_ROUTER;
a0.iface = nf->n.nhs->iface;
a0.gw = nf->n.nhs->gw;
}
else
{
a0.dest = RTD_DEVICE;
a0.iface = nf->n.nhs->iface;
}
if (reload || ort_changed(nf, &a0)) if (reload || ort_changed(nf, &a0))
{ {

View file

@ -53,7 +53,7 @@ typedef struct orta
struct ospf_area *oa; struct ospf_area *oa;
struct ospf_area *voa; /* Used when route is replaced in ospf_rt_sum_tr(), struct ospf_area *voa; /* Used when route is replaced in ospf_rt_sum_tr(),
NULL otherwise */ NULL otherwise */
struct mpnh *nhs; /* Next hops computed during SPF */ struct nexthop *nhs; /* Next hops computed during SPF */
struct top_hash_entry *en; /* LSA responsible for this orta */ struct top_hash_entry *en; /* LSA responsible for this orta */
} }
orta; orta;

View file

@ -1288,8 +1288,8 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
ip_addr fwd = IPA_NONE; ip_addr fwd = IPA_NONE;
if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface)) if ((a->dest == RTD_UNICAST) && use_gw_for_fwaddr(p, a->nh.gw, a->nh.iface))
fwd = a->gw; fwd = a->nh.gw;
/* NSSA-LSA with P-bit set must have non-zero forwarding address */ /* NSSA-LSA with P-bit set must have non-zero forwarding address */
if (oa && ipa_zero(fwd)) if (oa && ipa_zero(fwd))

View file

@ -28,7 +28,7 @@ struct top_hash_entry
u16 next_lsa_opts; /* For postponed LSA origination */ u16 next_lsa_opts; /* For postponed LSA origination */
bird_clock_t inst_time; /* Time of installation into DB */ bird_clock_t inst_time; /* Time of installation into DB */
struct ort *nf; /* Reference fibnode for sum and ext LSAs, NULL for otherwise */ struct ort *nf; /* Reference fibnode for sum and ext LSAs, NULL for otherwise */
struct mpnh *nhs; /* Computed nexthops - valid only in ospf_rt_spf() */ struct nexthop *nhs; /* Computed nexthops - valid only in ospf_rt_spf() */
ip_addr lb; /* In OSPFv2, link back address. In OSPFv3, any global address in the area useful for vlinks */ ip_addr lb; /* In OSPFv2, link back address. In OSPFv3, any global address in the area useful for vlinks */
u32 lb_id; /* Interface ID of link back iface (for bcast or NBMA networks) */ u32 lb_id; /* Interface ID of link back iface (for bcast or NBMA networks) */
u32 dist; /* Distance from the root */ u32 dist; /* Distance from the root */

View file

@ -158,10 +158,10 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
while (rt2 && !rip_valid_rte(rt2)) while (rt2 && !rip_valid_rte(rt2))
rt2 = rt2->next; rt2 = rt2->next;
a0.dest = RTD_UNICAST;
if (p->ecmp && rt2) if (p->ecmp && rt2)
{ {
/* ECMP route */ /* ECMP route */
struct mpnh *nhs = NULL;
int num = 0; int num = 0;
for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next) for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next)
@ -169,33 +169,34 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
if (!rip_valid_rte(rt)) if (!rip_valid_rte(rt))
continue; continue;
struct mpnh *nh = alloca(sizeof(struct mpnh)); struct nexthop *nh = (a0.nh.next ? &(a0.nh) : alloca(sizeof(struct nexthop)));
nh->gw = rt->next_hop; nh->gw = rt->next_hop;
nh->iface = rt->from->nbr->iface; nh->iface = rt->from->nbr->iface;
nh->weight = rt->from->ifa->cf->ecmp_weight; nh->weight = rt->from->ifa->cf->ecmp_weight;
mpnh_insert(&nhs, nh);
if (a0.nh.next)
nexthop_insert(&(a0.nh), nh);
num++; num++;
if (rt->tag != rt_tag) if (rt->tag != rt_tag)
rt_tag = 0; rt_tag = 0;
} }
a0.dest = RTD_MULTIPATH;
a0.nexthops = nhs;
} }
else else
{ {
/* Unipath route */ /* Unipath route */
a0.dest = RTD_ROUTER; a0.nh.next = NULL;
a0.gw = rt->next_hop; a0.nh.gw = rt->next_hop;
a0.iface = rt->from->nbr->iface; a0.nh.iface = rt->from->nbr->iface;
a0.from = rt->from->nbr->addr; a0.from = rt->from->nbr->addr;
} }
rta *a = rta_lookup(&a0); rta *a = rta_lookup(&a0);
rte *e = rte_get_temp(a); rte *e = rte_get_temp(a);
e->u.rip.from = a0.iface; e->u.rip.from = a0.nh.iface;
e->u.rip.metric = rt_metric; e->u.rip.metric = rt_metric;
e->u.rip.tag = rt_tag; e->u.rip.tag = rt_tag;
@ -345,8 +346,8 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
en->metric = rt_metric; en->metric = rt_metric;
en->tag = rt_tag; en->tag = rt_tag;
en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL; en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
en->iface = new->attrs->iface; en->iface = new->attrs->nh.iface;
en->next_hop = new->attrs->gw; en->next_hop = new->attrs->nh.gw;
} }
else else
{ {

View file

@ -13,7 +13,7 @@ CF_HDR
CF_DEFINES CF_DEFINES
#define STATIC_CFG ((struct static_config *) this_proto) #define STATIC_CFG ((struct static_config *) this_proto)
static struct static_route *this_srt, *this_srt_nh, *last_srt_nh; static struct static_route *this_srt, *last_srt;
static struct f_inst **this_srt_last_cmd; static struct f_inst **this_srt_last_cmd;
static void static void
@ -22,7 +22,6 @@ static_route_finish(void)
struct static_route *r; struct static_route *r;
/* Update undefined use_bfd entries in multipath nexthops */ /* Update undefined use_bfd entries in multipath nexthops */
if (this_srt->dest == RTD_MULTIPATH)
for (r = this_srt->mp_next; r; r = r->mp_next) for (r = this_srt->mp_next; r; r = r->mp_next)
if (r->use_bfd < 0) if (r->use_bfd < 0)
r->use_bfd = this_srt->use_bfd; r->use_bfd = this_srt->use_bfd;
@ -58,48 +57,50 @@ stat_route0: ROUTE net_any {
add_tail(&STATIC_CFG->other_routes, &this_srt->n); add_tail(&STATIC_CFG->other_routes, &this_srt->n);
this_srt->net = $2; this_srt->net = $2;
this_srt_last_cmd = &(this_srt->cmds); this_srt_last_cmd = &(this_srt->cmds);
this_srt->mp_next = NULL;
last_srt = NULL;
} }
; ;
stat_multipath1: stat_multipath1:
VIA ipa ipa_scope { VIA ipa ipa_scope {
last_srt_nh = this_srt_nh; last_srt = last_srt ? last_srt->mp_next = cfg_allocz(sizeof(struct static_route)) : this_srt;
this_srt_nh = cfg_allocz(sizeof(struct static_route));
this_srt_nh->dest = RTD_NONE; last_srt->dest = RTD_UNICAST;
this_srt_nh->via = $2; last_srt->via = $2;
this_srt_nh->via_if = $3; last_srt->via_if = $3;
this_srt_nh->if_name = (void *) this_srt; /* really */ last_srt->if_name = (void *) this_srt; /* really */
this_srt_nh->use_bfd = -1; /* undefined */ last_srt->use_bfd = -1; /* undefined */
last_srt->mp_next = NULL;
} }
| stat_multipath1 WEIGHT expr { | stat_multipath1 WEIGHT expr {
this_srt_nh->weight = $3 - 1; last_srt->weight = $3 - 1;
if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256"); if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
} }
| stat_multipath1 BFD bool { | stat_multipath1 BFD bool {
this_srt_nh->use_bfd = $3; cf_check_bfd($3); last_srt->use_bfd = $3; cf_check_bfd($3);
} }
; ;
stat_multipath: stat_multipath:
stat_multipath1 { this_srt->mp_next = this_srt_nh; } stat_multipath1
| stat_multipath stat_multipath1 { last_srt_nh->mp_next = this_srt_nh; } | stat_multipath stat_multipath1
; ;
stat_route: stat_route:
stat_route0 VIA ipa ipa_scope { stat_route0 VIA ipa ipa_scope {
this_srt->dest = RTD_ROUTER; this_srt->dest = RTD_UNICAST;
this_srt->via = $3; this_srt->via = $3;
this_srt->via_if = $4; this_srt->via_if = $4;
} }
| stat_route0 VIA TEXT { | stat_route0 VIA TEXT {
this_srt->dest = RTD_DEVICE; this_srt->dest = RTD_UNICAST;
this_srt->via = IPA_NONE;
this_srt->if_name = $3; this_srt->if_name = $3;
rem_node(&this_srt->n); rem_node(&this_srt->n);
add_tail(&STATIC_CFG->iface_routes, &this_srt->n); add_tail(&STATIC_CFG->iface_routes, &this_srt->n);
} }
| stat_route0 MULTIPATH stat_multipath { | stat_route0 MULTIPATH stat_multipath
this_srt->dest = RTD_MULTIPATH;
}
| stat_route0 RECURSIVE ipa { | stat_route0 RECURSIVE ipa {
this_srt->dest = RTDX_RECURSIVE; this_srt->dest = RTDX_RECURSIVE;
this_srt->via = $3; this_srt->via = $3;

View file

@ -58,49 +58,53 @@ p_igp_table(struct proto *p)
} }
static void static void
static_install(struct proto *p, struct static_route *r, struct iface *ifa) static_install(struct proto *p, struct static_route *r)
{ {
rta a; rta a;
rte *e; rte *e;
if (r->installed > 0) if (!(r->state & STS_WANT) && r->dest != RTD_UNICAST)
return; return;
DBG("Installing static route %N, rtd=%d\n", r->net, r->dest); DBG("Installing static route %N, rtd=%d\n", r->net, r->dest);
bzero(&a, sizeof(a)); bzero(&a, sizeof(a));
a.src = p->main_source; a.src = p->main_source;
a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC; a.source = ((r->dest == RTD_UNICAST) && ipa_zero(r->via)) ? RTS_STATIC_DEVICE : RTS_STATIC;
a.scope = SCOPE_UNIVERSE; a.scope = SCOPE_UNIVERSE;
a.cast = RTC_UNICAST; a.cast = RTC_UNICAST;
a.dest = r->dest; a.dest = r->dest;
a.gw = r->via; if (r->dest == RTD_UNICAST)
a.iface = ifa;
if (r->dest == RTD_MULTIPATH)
{ {
struct static_route *r2; struct static_route *r2;
struct mpnh *nhs = NULL; int num = 0;
for (r2 = r->mp_next; r2; r2 = r2->mp_next) for (r2 = r; r2; r2 = r2->mp_next)
if (r2->installed)
{ {
struct mpnh *nh = alloca(sizeof(struct mpnh)); if ((r2->state & STS_INSTALLED) && !(r2->state & STS_FORCE))
continue;
if (r2->state & STS_WANT)
{
struct nexthop *nh = (a.nh.next) ? alloca(sizeof(struct nexthop)) : &(a.nh);
nh->gw = r2->via; nh->gw = r2->via;
nh->iface = r2->neigh->iface; nh->iface = r2->neigh->iface;
nh->weight = r2->weight; nh->weight = r2->weight;
mpnh_insert(&nhs, nh); if (a.nh.next)
} nexthop_insert(&(a.nh), nh);
r2->state |= STS_INSTALLED;
/* There is at least one nexthop */ num++;
if (!nhs->next)
{
/* Fallback to unipath route for exactly one nexthop */
a.dest = RTD_ROUTER;
a.gw = nhs->gw;
a.iface = nhs->iface;
} }
else else
a.nexthops = nhs; r2->state = 0;
}
if (!num) // No nexthop to install
{
if (r->state & STS_INSTALLED_ANY)
rte_update(p, r->net, NULL);
return;
}
} }
if (r->dest == RTDX_RECURSIVE) if (r->dest == RTDX_RECURSIVE)
@ -115,7 +119,6 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
f_eval_rte(r->cmds, &e, static_lp); f_eval_rte(r->cmds, &e, static_lp);
rte_update(p, r->net, e); rte_update(p, r->net, e);
r->installed = 1;
if (r->cmds) if (r->cmds)
lp_flush(static_lp); lp_flush(static_lp);
@ -124,12 +127,13 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
static void static void
static_remove(struct proto *p, struct static_route *r) static_remove(struct proto *p, struct static_route *r)
{ {
if (!r->installed) if (!(r->state & STS_INSTALLED_ANY))
return; return;
DBG("Removing static route %N via %I\n", r->net, r->via); DBG("Removing static route %N via %I\n", r->net, r->via);
rte_update(p, r->net, NULL); rte_update(p, r->net, NULL);
r->installed = 0;
r->state &= ~(STS_INSTALLED | STS_INSTALLED_ANY);
} }
static void static void
@ -180,38 +184,12 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
DBG("static_add(%N,%d)\n", r->net, r->dest); DBG("static_add(%N,%d)\n", r->net, r->dest);
switch (r->dest) switch (r->dest)
{ {
case RTD_ROUTER: case RTD_UNICAST:
{
struct neighbor *n = neigh_find2(p, &r->via, r->via_if, NEF_STICKY);
if (n)
{
r->chain = n->data;
n->data = r;
r->neigh = n;
static_update_bfd(p, r);
if (static_decide(cf, r))
static_install(p, r, n->iface);
else
static_remove(p, r);
}
else
{
log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
static_remove(p, r);
}
break;
}
case RTD_DEVICE:
break;
case RTD_MULTIPATH:
{ {
int count = 0; int count = 0;
struct static_route *r2; struct static_route *r2;
for (r2 = r->mp_next; r2; r2 = r2->mp_next) for (r2 = r; r2; r2 = r2->mp_next)
{ {
struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY); struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY);
if (n) if (n)
@ -221,20 +199,19 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
r2->neigh = n; r2->neigh = n;
static_update_bfd(p, r2); static_update_bfd(p, r2);
r2->installed = static_decide(cf, r2); r2->state = static_decide(cf,r2) ? STS_WANT : 0;
count += r2->installed; count++;
} }
else else
{ {
log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via); log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
r2->installed = 0; r2->state = 0;
} }
} }
if (count) if (count)
static_install(p, r, NULL); static_install(p, r, NULL);
else
static_remove(p, r);
break; break;
} }
@ -248,14 +225,7 @@ static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
{ {
struct static_route *r2; struct static_route *r2;
if (r->bfd_req) for (r2 = r; r2; r2 = r2->mp_next)
{
rfree(r->bfd_req);
r->bfd_req = NULL;
}
if (r->dest == RTD_MULTIPATH)
for (r2 = r->mp_next; r2; r2 = r2->mp_next)
if (r2->bfd_req) if (r2->bfd_req)
{ {
rfree(r2->bfd_req); rfree(r2->bfd_req);
@ -293,11 +263,11 @@ static_shutdown(struct proto *p)
/* Just reset the flag, the routes will be flushed by the nest */ /* Just reset the flag, the routes will be flushed by the nest */
WALK_LIST(r, cf->iface_routes) WALK_LIST(r, cf->iface_routes)
r->installed = 0; r->state = 0;
WALK_LIST(r, cf->other_routes) WALK_LIST(r, cf->other_routes)
{ {
static_rte_cleanup(p, r); static_rte_cleanup(p, r);
r->installed = 0; r->state = 0;
} }
/* Handle failure during channel reconfigure */ /* Handle failure during channel reconfigure */
@ -306,9 +276,9 @@ static_shutdown(struct proto *p)
if (cf) if (cf)
{ {
WALK_LIST(r, cf->iface_routes) WALK_LIST(r, cf->iface_routes)
r->installed = 0; r->state = 0;
WALK_LIST(r, cf->other_routes) WALK_LIST(r, cf->other_routes)
r->installed = 0; r->state = 0;
} }
return PS_DOWN; return PS_DOWN;
@ -326,40 +296,13 @@ static_cleanup(struct proto *p)
static void static void
static_update_rte(struct proto *p, struct static_route *r) static_update_rte(struct proto *p, struct static_route *r)
{ {
switch (r->dest) if (r->dest != RTD_UNICAST)
{ return;
case RTD_ROUTER:
if (static_decide((struct static_config *) p->cf, r)) if (static_decide((struct static_config *) p->cf, r))
static_install(p, r, r->neigh->iface); static_install(p, r, r->neigh->iface);
else else
static_remove(p, r); static_remove(p, r);
break;
case RTD_NONE: /* a part of multipath route */
{
int decision = static_decide((struct static_config *) p->cf, r);
if (decision == r->installed)
break; /* no change */
r->installed = decision;
struct static_route *r1, *r2;
int count = 0;
r1 = (void *) r->if_name; /* really */
for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
count += r2->installed;
if (count)
{
/* Set of nexthops changed - force reinstall */
r1->installed = 0;
static_install(p, r1, NULL);
}
else
static_remove(p, r1);
break;
}
}
} }
static void static void
@ -391,18 +334,13 @@ static void
static_dump_rt(struct static_route *r) static_dump_rt(struct static_route *r)
{ {
debug("%-1N: ", r->net); debug("%-1N: ", r->net);
switch (r->dest) if (r->dest == RTD_UNICAST)
{ if (ipa_zero(r->via))
case RTD_ROUTER:
debug("via %I\n", r->via);
break;
case RTD_DEVICE:
debug("dev %s\n", r->if_name); debug("dev %s\n", r->if_name);
break; else
default: debug("via %I\n", r->via);
else
debug("rtd %d\n", r->dest); debug("rtd %d\n", r->dest);
break;
}
} }
static void static void
@ -496,22 +434,27 @@ static_same_dest(struct static_route *x, struct static_route *y)
switch (x->dest) switch (x->dest)
{ {
case RTD_ROUTER: case RTD_UNICAST:
return ipa_equal(x->via, y->via) && (x->via_if == y->via_if); {
struct static_route *xc, *yc;
case RTD_DEVICE: for (xc = x, yc = y; xc && yc; xc = xc->mp_next, yc = yc->mp_next)
return !strcmp(x->if_name, y->if_name); {
if (ipa_nonzero(xc->via) && ipa_nonzero(yc->via))
case RTD_MULTIPATH: {
for (x = x->mp_next, y = y->mp_next;
x && y;
x = x->mp_next, y = y->mp_next)
if (!ipa_equal(x->via, y->via) || if (!ipa_equal(x->via, y->via) ||
(x->via_if != y->via_if) || (x->via_if != y->via_if) ||
(x->use_bfd != y->use_bfd) || (x->use_bfd != y->use_bfd) ||
(x->weight != y->weight)) (x->weight != y->weight))
return 0; return 0;
return !x && !y; }
else
if (strcmp(x->if_name, y->if_name) ||
(x->use_bfd != y->use_bfd) ||
(x->weight != y->weight))
return 0;
}
return 1;
}
case RTDX_RECURSIVE: case RTDX_RECURSIVE:
return ipa_equal(x->via, y->via); return ipa_equal(x->via, y->via);
@ -556,10 +499,10 @@ static_match(struct proto *p, struct static_route *r, struct static_config *n)
found: found:
/* If destination is different, force reinstall */ /* If destination is different, force reinstall */
if ((r->installed > 0) && !static_same_rte(r, t)) if (r->state && !static_same_rte(r, t))
t->installed = -1; t->state = r->state | STS_WANT | STS_FORCE;
else else
t->installed = r->installed; t->state = r->state;
} }
static inline rtable * static inline rtable *
@ -606,37 +549,24 @@ static_reconfigure(struct proto *p, struct proto_config *CF)
static void static void
static_copy_routes(list *dlst, list *slst) static_copy_routes(list *dlst, list *slst)
{ {
struct static_route *dr, *sr; struct static_route *sr;
init_list(dlst); init_list(dlst);
WALK_LIST(sr, *slst) WALK_LIST(sr, *slst)
{
struct static_route *srr, *drr = NULL;
for (srr = sr; srr; srr = srr->mp_next)
{ {
/* copy one route */ /* copy one route */
dr = cfg_alloc(sizeof(struct static_route)); struct static_route *dr = cfg_alloc(sizeof(struct static_route));
if (drr)
drr->mp_next = dr;
else
add_tail(dlst, &(dr->n));
memcpy(dr, sr, sizeof(struct static_route)); memcpy(dr, sr, sizeof(struct static_route));
drr = dr;
/* This fn is supposed to be called on fresh src routes, which have 'live'
fields (like .chain, .neigh or .installed) zero, so no need to zero them */
/* We need to copy multipath chain, because there are backptrs in 'if_name' */
if (dr->dest == RTD_MULTIPATH)
{
struct static_route *md, *ms, **mp_last;
mp_last = &(dr->mp_next);
for (ms = sr->mp_next; ms; ms = ms->mp_next)
{
md = cfg_alloc(sizeof(struct static_route));
memcpy(md, ms, sizeof(struct static_route));
md->if_name = (void *) dr; /* really */
*mp_last = md;
mp_last = &(md->mp_next);
} }
*mp_last = NULL;
}
add_tail(dlst, (node *) dr);
} }
} }
@ -668,30 +598,39 @@ struct protocol proto_static = {
.copy_config = static_copy_config .copy_config = static_copy_config
}; };
static void static byte *
static_show_rt(struct static_route *r) static_format_via(struct static_route *r)
{ {
byte via[IPA_MAX_TEXT_LENGTH + 25]; static byte via[IPA_MAX_TEXT_LENGTH + 25];
switch (r->dest) switch (r->dest)
{ {
case RTD_ROUTER: bsprintf(via, "via %I%J", r->via, r->via_if); break; case RTD_UNICAST: if (ipa_zero(r->via)) bsprintf(via, "dev %s", r->if_name);
case RTD_DEVICE: bsprintf(via, "dev %s", r->if_name); break; else bsprintf(via, "via %I%J", r->via, r->via_if);
break;
case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break; case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break; case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
case RTD_PROHIBIT: bsprintf(via, "prohibited"); break; case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break; case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
default: bsprintf(via, "???"); default: bsprintf(via, "???");
} }
cli_msg(-1009, "%N %s%s%s", r->net, via, return via;
r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)"); }
static void
static_show_rt(struct static_route *r)
{
if (r->mp_next)
{
cli_msg(-1009, "%N", r->net);
struct static_route *r2; struct static_route *r2;
if (r->dest == RTD_MULTIPATH) for (r2 = r; r2; r2 = r2->mp_next)
for (r2 = r->mp_next; r2; r2 = r2->mp_next) cli_msg(-1009, "\t%s weight %d%s%s", static_format_via(r2), r2->weight + 1,
cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->weight + 1, r2->bfd_req ? " (bfd)" : "", (r2->state & STS_INSTALLED) ? "" : " (dormant)");
r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)"); }
else
cli_msg(-1009, "%N %s%s%s", r->net, static_format_via(r),
r->bfd_req ? " (bfd)" : "", (r->state & STS_INSTALLED) ? "" : " (dormant)");
} }
void void

View file

@ -32,14 +32,19 @@ struct static_route {
struct iface *via_if; /* Destination iface, for link-local vias */ struct iface *via_if; /* Destination iface, for link-local vias */
struct neighbor *neigh; struct neighbor *neigh;
byte *if_name; /* Name for RTD_DEVICE routes */ byte *if_name; /* Name for RTD_DEVICE routes */
struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */ struct static_route *mp_next; /* Nexthops for multipath routes */
struct f_inst *cmds; /* List of commands for setting attributes */ struct f_inst *cmds; /* List of commands for setting attributes */
int installed; /* Installed in rt table, -1 for reinstall */ u32 state; /* Current state: STS_* */
int use_bfd; /* Configured to use BFD */ int use_bfd; /* Configured to use BFD */
int weight; /* Multipath next hop weight */ int weight; /* Multipath next hop weight */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */ struct bfd_request *bfd_req; /* BFD request, if BFD is used */
}; };
#define STS_INSTALLED 0x1
#define STS_INSTALLED_ANY 0x2
#define STS_WANT 0x4
#define STS_FORCE 0x8
/* Dummy nodes (parts of multipath route) abuses masklen field for weight /* Dummy nodes (parts of multipath route) abuses masklen field for weight
and if_name field for a ptr to the master (RTD_MULTIPATH) node. */ and if_name field for a ptr to the master (RTD_MULTIPATH) node. */

View file

@ -148,8 +148,7 @@ krt_capable(rte *e)
return return
a->cast == RTC_UNICAST && a->cast == RTC_UNICAST &&
(a->dest == RTD_ROUTER ((a->dest == RTD_UNICAST && !a->nh.next) /* No multipath support */
|| a->dest == RTD_DEVICE
#ifdef RTF_REJECT #ifdef RTF_REJECT
|| a->dest == RTD_UNREACHABLE || a->dest == RTD_UNREACHABLE
#endif #endif
@ -190,7 +189,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
net *net = e->net; net *net = e->net;
rta *a = e->attrs; rta *a = e->attrs;
static int msg_seq; static int msg_seq;
struct iface *j, *i = a->iface; struct iface *j, *i = a->nh.iface;
int l; int l;
struct ks_msg msg; struct ks_msg msg;
char *body = (char *)msg.buf; char *body = (char *)msg.buf;
@ -243,7 +242,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
} }
} }
gw = a->gw; gw = a->nh.gw;
/* Embed interface ID to link-local address */ /* Embed interface ID to link-local address */
if (ipa_is_link_local(gw)) if (ipa_is_link_local(gw))
@ -270,18 +269,9 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
switch (a->dest) switch (a->dest)
{ {
case RTD_ROUTER: case RTD_UNICAST:
msg.rtm.rtm_flags |= RTF_GATEWAY; if (ipa_zero(gw))
msg.rtm.rtm_addrs |= RTA_GATEWAY; {
break;
#ifdef RTF_REJECT
case RTD_UNREACHABLE:
#endif
#ifdef RTF_BLACKHOLE
case RTD_BLACKHOLE:
#endif
case RTD_DEVICE:
if(i) if(i)
{ {
#ifdef RTF_CLONING #ifdef RTF_CLONING
@ -297,7 +287,18 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
sockaddr_fill(&gate, ipa_is_ip4(i->addr->ip) ? AF_INET : AF_INET6, i->addr->ip, NULL, 0); sockaddr_fill(&gate, ipa_is_ip4(i->addr->ip) ? AF_INET : AF_INET6, i->addr->ip, NULL, 0);
msg.rtm.rtm_addrs |= RTA_GATEWAY; msg.rtm.rtm_addrs |= RTA_GATEWAY;
} }
} else {
msg.rtm.rtm_flags |= RTF_GATEWAY;
msg.rtm.rtm_addrs |= RTA_GATEWAY;
}
break; break;
#ifdef RTF_REJECT
case RTD_UNREACHABLE:
#endif
#ifdef RTF_BLACKHOLE
case RTD_BLACKHOLE:
#endif
default: default:
bug("krt-sock: unknown flags, but not filtered"); bug("krt-sock: unknown flags, but not filtered");
} }
@ -489,39 +490,40 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
} }
#endif #endif
a.iface = if_find_by_index(msg->rtm.rtm_index); a.nh.iface = if_find_by_index(msg->rtm.rtm_index);
if (!a.iface) if (!a.nh.iface)
{ {
log(L_ERR "KRT: Received route %N with unknown ifindex %u", log(L_ERR "KRT: Received route %N with unknown ifindex %u",
net->n.addr, msg->rtm.rtm_index); net->n.addr, msg->rtm.rtm_index);
return; return;
} }
a.dest = RTD_UNICAST;
a.nh.next = NULL;
if (flags & RTF_GATEWAY) if (flags & RTF_GATEWAY)
{ {
neighbor *ng; neighbor *ng;
a.dest = RTD_ROUTER; a.nh.gw = igate;
a.gw = igate;
/* Clean up embedded interface ID returned in link-local address */ /* Clean up embedded interface ID returned in link-local address */
if (ipa_is_link_local(a.gw)) if (ipa_is_link_local(a.nh.gw))
_I0(a.gw) = 0xfe800000; _I0(a.nh.gw) = 0xfe800000;
ng = neigh_find2(&p->p, &a.gw, a.iface, 0); ng = neigh_find2(&p->p, &a.nh.gw, a.nh.iface, 0);
if (!ng || (ng->scope == SCOPE_HOST)) if (!ng || (ng->scope == SCOPE_HOST))
{ {
/* Ignore routes with next-hop 127.0.0.1, host routes with such /* Ignore routes with next-hop 127.0.0.1, host routes with such
next-hop appear on OpenBSD for address aliases. */ next-hop appear on OpenBSD for address aliases. */
if (ipa_classify(a.gw) == (IADDR_HOST | SCOPE_HOST)) if (ipa_classify(a.nh.gw) == (IADDR_HOST | SCOPE_HOST))
return; return;
log(L_ERR "KRT: Received route %N with strange next-hop %I", log(L_ERR "KRT: Received route %N with strange next-hop %I",
net->n.addr, a.gw); net->n.addr, a.nh.gw);
return; return;
} }
} }
else else
a.dest = RTD_DEVICE; a.nh.gw = IPA_NONE;
done: done:
e = rte_get_temp(&a); e = rte_get_temp(&a);

View file

@ -20,6 +20,7 @@
#include "nest/route.h" #include "nest/route.h"
#include "nest/protocol.h" #include "nest/protocol.h"
#include "nest/iface.h" #include "nest/iface.h"
#include "lib/alloca.h"
#include "sysdep/unix/timer.h" #include "sysdep/unix/timer.h"
#include "sysdep/unix/unix.h" #include "sysdep/unix/unix.h"
#include "sysdep/unix/krt.h" #include "sysdep/unix/krt.h"
@ -303,7 +304,6 @@ static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = {
[IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) }, [IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) },
[IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) }, [IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) },
[IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) }, [IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) },
[IFA_FLAGS] = { 1, 1, sizeof(u32) },
}; };
static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = { static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
@ -315,7 +315,7 @@ static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
#define BIRD_RTA_MAX (RTA_TABLE+1) #define BIRD_RTA_MAX (RTA_TABLE+1)
static struct nl_want_attrs mpnh_attr_want4[BIRD_RTA_MAX] = { static struct nl_want_attrs nexthop_attr_want4[BIRD_RTA_MAX] = {
[RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) }, [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) },
}; };
@ -472,7 +472,7 @@ nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh)
} }
static void static void
nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct mpnh *nh) nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct nexthop *nh)
{ {
struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH); struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH);
@ -492,17 +492,17 @@ nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct mpnh *nh)
nl_close_attr(h, a); nl_close_attr(h, a);
} }
static struct mpnh * static struct nexthop *
nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
{ {
/* Temporary buffer for multicast nexthops */ /* Temporary buffer for multicast nexthops */
static struct mpnh *nh_buffer; static struct nexthop *nh_buffer;
static int nh_buf_size; /* in number of structures */ static int nh_buf_size; /* in number of structures */
static int nh_buf_used; static int nh_buf_used;
struct rtattr *a[BIRD_RTA_MAX]; struct rtattr *a[BIRD_RTA_MAX];
struct rtnexthop *nh = RTA_DATA(ra); struct rtnexthop *nh = RTA_DATA(ra);
struct mpnh *rv, *first, **last; struct nexthop *rv, *first, **last;
unsigned len = RTA_PAYLOAD(ra); unsigned len = RTA_PAYLOAD(ra);
first = NULL; first = NULL;
@ -518,7 +518,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
if (nh_buf_used == nh_buf_size) if (nh_buf_used == nh_buf_size)
{ {
nh_buf_size = nh_buf_size ? (nh_buf_size * 2) : 4; nh_buf_size = nh_buf_size ? (nh_buf_size * 2) : 4;
nh_buffer = xrealloc(nh_buffer, nh_buf_size * sizeof(struct mpnh)); nh_buffer = xrealloc(nh_buffer, nh_buf_size * sizeof(struct nexthop));
} }
*last = rv = nh_buffer + nh_buf_used++; *last = rv = nh_buffer + nh_buf_used++;
rv->next = NULL; rv->next = NULL;
@ -531,7 +531,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
/* Nonexistent RTNH_PAYLOAD ?? */ /* Nonexistent RTNH_PAYLOAD ?? */
nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0); nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0);
nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a)); nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want4, a, sizeof(a));
if (a[RTA_GATEWAY]) if (a[RTA_GATEWAY])
{ {
rv->gw = rta_get_ipa(a[RTA_GATEWAY]); rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
@ -957,14 +957,14 @@ krt_capable(rte *e)
switch (a->dest) switch (a->dest)
{ {
case RTD_ROUTER: case RTD_UNICAST:
case RTD_DEVICE: for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
if (a->iface == NULL) if (nh->iface)
return 1;
return 0; return 0;
case RTD_BLACKHOLE: case RTD_BLACKHOLE:
case RTD_UNREACHABLE: case RTD_UNREACHABLE:
case RTD_PROHIBIT: case RTD_PROHIBIT:
case RTD_MULTIPATH:
break; break;
default: default:
return 0; return 0;
@ -973,7 +973,7 @@ krt_capable(rte *e)
} }
static inline int static inline int
nh_bufsize(struct mpnh *nh) nh_bufsize(struct nexthop *nh)
{ {
int rv = 0; int rv = 0;
for (; nh != NULL; nh = nh->next) for (; nh != NULL; nh = nh->next)
@ -982,12 +982,12 @@ nh_bufsize(struct mpnh *nh)
} }
static int static int
nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int dest, ip_addr gw, struct iface *iface) nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int dest, struct nexthop *nh)
{ {
eattr *ea; eattr *ea;
net *net = e->net; net *net = e->net;
rta *a = e->attrs; rta *a = e->attrs;
int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops); int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(&(a->nh));
u32 priority = 0; u32 priority = 0;
struct { struct {
@ -1043,7 +1043,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d
if (ea = ea_find(eattrs, EA_KRT_SCOPE)) if (ea = ea_find(eattrs, EA_KRT_SCOPE))
r->r.rtm_scope = ea->u.data; r->r.rtm_scope = ea->u.data;
else else
r->r.rtm_scope = (dest == RTD_DEVICE) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; r->r.rtm_scope = (dest == RTD_UNICAST && ipa_zero(nh->gw)) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
@ -1071,14 +1071,17 @@ dest:
/* a->iface != NULL checked in krt_capable() for router and device routes */ /* a->iface != NULL checked in krt_capable() for router and device routes */
switch (dest) switch (dest)
{ {
case RTD_ROUTER: case RTD_UNICAST:
r->r.rtm_type = RTN_UNICAST; r->r.rtm_type = RTN_UNICAST;
nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index); if (nh->next && !krt_ecmp6(p))
nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, gw); nl_add_multipath(&r->h, rsize, nh);
break; else
case RTD_DEVICE: {
r->r.rtm_type = RTN_UNICAST; nl_add_attr_u32(&r->h, rsize, RTA_OIF, nh->iface->index);
nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index);
if (ipa_nonzero(nh->gw))
nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, nh->gw);
}
break; break;
case RTD_BLACKHOLE: case RTD_BLACKHOLE:
r->r.rtm_type = RTN_BLACKHOLE; r->r.rtm_type = RTN_BLACKHOLE;
@ -1089,10 +1092,6 @@ dest:
case RTD_PROHIBIT: case RTD_PROHIBIT:
r->r.rtm_type = RTN_PROHIBIT; r->r.rtm_type = RTN_PROHIBIT;
break; break;
case RTD_MULTIPATH:
r->r.rtm_type = RTN_UNICAST;
nl_add_multipath(&r->h, rsize, a->nexthops);
break;
case RTD_NONE: case RTD_NONE:
break; break;
default: default:
@ -1109,21 +1108,21 @@ nl_add_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
rta *a = e->attrs; rta *a = e->attrs;
int err = 0; int err = 0;
if (krt_ecmp6(p) && (a->dest == RTD_MULTIPATH)) if (krt_ecmp6(p) && a->nh.next)
{ {
struct mpnh *nh = a->nexthops; struct nexthop *nh = &(a->nh);
err = nl_send_route(p, e, eattrs, NL_OP_ADD, RTD_ROUTER, nh->gw, nh->iface); err = nl_send_route(p, e, eattrs, NL_OP_ADD, RTD_UNICAST, nh);
if (err < 0) if (err < 0)
return err; return err;
for (nh = nh->next; nh; nh = nh->next) for (nh = nh->next; nh; nh = nh->next)
err += nl_send_route(p, e, eattrs, NL_OP_APPEND, RTD_ROUTER, nh->gw, nh->iface); err += nl_send_route(p, e, eattrs, NL_OP_APPEND, RTD_UNICAST, nh);
return err; return err;
} }
return nl_send_route(p, e, eattrs, NL_OP_ADD, a->dest, a->gw, a->iface); return nl_send_route(p, e, eattrs, NL_OP_ADD, a->dest, &(a->nh));
} }
static inline int static inline int
@ -1133,7 +1132,7 @@ nl_delete_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
/* For IPv6, we just repeatedly request DELETE until we get error */ /* For IPv6, we just repeatedly request DELETE until we get error */
do do
err = nl_send_route(p, e, eattrs, NL_OP_DELETE, RTD_NONE, IPA_NONE, NULL); err = nl_send_route(p, e, eattrs, NL_OP_DELETE, RTD_NONE, NULL);
while (krt_ecmp6(p) && !err); while (krt_ecmp6(p) && !err);
return err; return err;
@ -1168,10 +1167,10 @@ krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list
} }
static inline struct mpnh * static inline struct nexthop *
nl_alloc_mpnh(struct nl_parse_state *s, ip_addr gw, struct iface *iface, byte weight) nl_alloc_nexthop(struct nl_parse_state *s, ip_addr gw, struct iface *iface, byte weight)
{ {
struct mpnh *nh = lp_alloc(s->pool, sizeof(struct mpnh)); struct nexthop *nh = lp_alloc(s->pool, sizeof(struct nexthop));
nh->gw = gw; nh->gw = gw;
nh->iface = iface; nh->iface = iface;
@ -1342,7 +1341,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type)) if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type))
nl_announce_route(s); nl_announce_route(s);
rta *ra = lp_allocz(s->pool, sizeof(rta)); rta *ra = lp_allocz(s->pool, sizeof(rta)); // TODO: fix alloc
ra->src = p->p.main_source; ra->src = p->p.main_source;
ra->source = RTS_INHERIT; ra->source = RTS_INHERIT;
ra->scope = SCOPE_UNIVERSE; ra->scope = SCOPE_UNIVERSE;
@ -1354,19 +1353,19 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET)) if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET))
{ {
ra->dest = RTD_MULTIPATH; struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH]);
ra->nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]); if (!nh)
if (!ra->nexthops)
{ {
log(L_ERR "KRT: Received strange multipath route %N", net->n.addr); log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
return; return;
} }
nexthop_link(ra, nh);
break; break;
} }
ra->iface = if_find_by_index(oif); ra->nh.iface = if_find_by_index(oif);
if (!ra->iface) if (!ra->nh.iface)
{ {
log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif); log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif);
return; return;
@ -1374,28 +1373,23 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if (a[RTA_GATEWAY]) if (a[RTA_GATEWAY])
{ {
ra->dest = RTD_ROUTER; ra->nh.gw = rta_get_ipa(a[RTA_GATEWAY]);
ra->gw = rta_get_ipa(a[RTA_GATEWAY]);
/* Silently skip strange 6to4 routes */ /* Silently skip strange 6to4 routes */
const net_addr_ip6 sit = NET_ADDR_IP6(IP6_NONE, 96); const net_addr_ip6 sit = NET_ADDR_IP6(IP6_NONE, 96);
if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->gw, (net_addr *) &sit)) if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->nh.gw, (net_addr *) &sit))
return; return;
neighbor *nbr; neighbor *nbr;
nbr = neigh_find2(&p->p, &ra->gw, ra->iface, nbr = neigh_find2(&p->p, &ra->nh.gw, ra->nh.iface,
(i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
if (!nbr || (nbr->scope == SCOPE_HOST)) if (!nbr || (nbr->scope == SCOPE_HOST))
{ {
log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr, ra->gw); log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr,
ra->nh.gw);
return; return;
} }
} }
else
{
ra->dest = RTD_DEVICE;
def_scope = RT_SCOPE_LINK;
}
break; break;
case RTN_BLACKHOLE: case RTN_BLACKHOLE:
@ -1510,13 +1504,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
/* Merge next hops with the stored route */ /* Merge next hops with the stored route */
rta *a = s->attrs; rta *a = s->attrs;
if (a->dest != RTD_MULTIPATH) nexthop_insert(&a->nh, &ra->nh);
{
a->dest = RTD_MULTIPATH;
a->nexthops = nl_alloc_mpnh(s, a->gw, a->iface, 0);
}
mpnh_insert(&a->nexthops, nl_alloc_mpnh(s, ra->gw, ra->iface, 0));
} }
} }

View file

@ -645,18 +645,12 @@ krt_same_dest(rte *k, rte *e)
if (ka->dest != ea->dest) if (ka->dest != ea->dest)
return 0; return 0;
switch (ka->dest)
{ if (ka->dest == RTD_UNICAST)
case RTD_ROUTER: return nexthop_same(&(ka->nh), &(ea->nh));
return ipa_equal(ka->gw, ea->gw);
case RTD_DEVICE:
return !strcmp(ka->iface->name, ea->iface->name);
case RTD_MULTIPATH:
return mpnh_same(ka->nexthops, ea->nexthops);
default:
return 1; return 1;
} }
}
/* /*
* This gets called back when the low-level scanning code discovers a route. * This gets called back when the low-level scanning code discovers a route.
@ -1011,10 +1005,16 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct li
return -1; return -1;
} }
if (!KRT_CF->devroutes && if (!KRT_CF->devroutes && (e->attrs->source != RTS_STATIC_DEVICE))
(e->attrs->dest == RTD_DEVICE) && {
(e->attrs->source != RTS_STATIC_DEVICE)) struct nexthop *nh = &(e->attrs->nh);
for (; nh; nh = nh->next)
if (ipa_nonzero(nh->gw))
break;
if (!nh) /* Gone through all the nexthops and no explicit GW found */
return -1; return -1;
}
if (!krt_capable(e)) if (!krt_capable(e))
return -1; return -1;