summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/cf-lex.l54
-rw-r--r--conf/confbase.Y41
-rw-r--r--configure.in1
-rw-r--r--doc/bird.sgml22
-rw-r--r--filter/config.Y3
-rw-r--r--filter/filter.c21
-rw-r--r--filter/filter.h7
-rw-r--r--lib/alloca.h2
-rw-r--r--lib/ip.h22
-rw-r--r--lib/net.c47
-rw-r--r--lib/net.h53
-rw-r--r--nest/config.Y3
-rw-r--r--nest/route.h53
-rw-r--r--nest/rt-attr.c175
-rw-r--r--nest/rt-dev.c5
-rw-r--r--nest/rt-table.c116
-rw-r--r--proto/bgp/attrs.c3
-rw-r--r--proto/bgp/packets.c18
-rw-r--r--proto/ospf/ospf.c2
-rw-r--r--proto/ospf/rt.c64
-rw-r--r--proto/ospf/rt.h2
-rw-r--r--proto/ospf/topology.c4
-rw-r--r--proto/ospf/topology.h2
-rw-r--r--proto/pipe/pipe.c15
-rw-r--r--proto/rip/rip.c31
-rw-r--r--proto/rpki/rpki.c3
-rw-r--r--proto/static/config.Y116
-rw-r--r--proto/static/static.c455
-rw-r--r--proto/static/static.h20
-rw-r--r--sysdep/bsd/krt-sock.c98
-rw-r--r--sysdep/cf/linux.h4
-rw-r--r--sysdep/linux/netlink.c357
-rw-r--r--sysdep/unix/krt.c28
33 files changed, 1167 insertions, 680 deletions
diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index bd6dfff2..fb3d59e4 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -123,6 +123,60 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
cf_include(start, end-start);
}
+[02]:{DIGIT}+:{DIGIT}+ {
+ unsigned long int l, len1, len2;
+ char *e;
+
+ if (yytext[0] == '0')
+ {
+ cf_lval.i64 = 0;
+ len1 = 16;
+ len2 = 32;
+ }
+ else
+ {
+ cf_lval.i64 = 2ULL << 48;
+ len1 = 32;
+ len2 = 16;
+ }
+
+ errno = 0;
+ l = strtoul(yytext+2, &e, 10);
+ if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
+ cf_error("ASN out of range");
+ cf_lval.i64 |= ((u64) l) << len2;
+
+ errno = 0;
+ l = strtoul(e+1, &e, 10);
+ if (e && *e || (errno == ERANGE) || (l >> len2))
+ cf_error("Number out of range");
+ cf_lval.i64 |= l;
+
+ return VPN_RD;
+}
+
+1:{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ {
+ unsigned long int l;
+ ip4_addr ip4;
+ char *e;
+
+ cf_lval.i64 = 1ULL << 48;
+
+ e = strchr(yytext+2, ':');
+ *e++ = '\0';
+ if (!ip4_pton(yytext+2, &ip4))
+ cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext+2);
+ cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
+
+ errno = 0;
+ l = strtoul(e, &e, 10);
+ if (e && *e || (errno == ERANGE) || (l >> 16))
+ cf_error("Number out of range");
+ cf_lval.i64 |= l;
+
+ return VPN_RD;
+}
+
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
if (!ip4_pton(yytext, &cf_lval.ip4))
cf_error("Invalid IPv4 address %s", yytext);
diff --git a/conf/confbase.Y b/conf/confbase.Y
index aec4aeb4..291dc6a0 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -38,6 +38,7 @@ CF_DECLS
%union {
int i;
u32 i32;
+ u64 i64;
ip_addr a;
ip4_addr ip4;
ip6_addr ip6;
@@ -64,6 +65,7 @@ CF_DECLS
struct proto_spec ps;
struct channel_limit cl;
struct timeformat *tf;
+ u32 *lbl;
}
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
@@ -72,6 +74,7 @@ CF_DECLS
%token <i> NUM ENUM
%token <ip4> IP4
%token <ip6> IP6
+%token <i64> VPN_RD
%token <s> SYM
%token <t> TEXT
%type <iface> ipa_scope
@@ -81,7 +84,8 @@ CF_DECLS
%type <time> datetime
%type <a> ipa
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
-%type <net_ptr> net_ net_any net_roa4_ net_roa6_ net_roa_
+%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_
+%type <lbl> label_stack_start label_stack
%type <t> text opttext
@@ -93,7 +97,7 @@ CF_DECLS
%left '!'
%nonassoc '.'
-CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT)
+CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN)
CF_GRAMMAR
@@ -196,6 +200,18 @@ net_ip6_: IP6 '/' NUM
cf_error("Invalid IPv6 prefix");
};
+net_vpn4_: VPN_RD net_ip4_
+{
+ $$ = cfg_alloc(sizeof(net_addr_vpn4));
+ net_fill_vpn4($$, net4_prefix(&$2), net4_pxlen(&$2), $1);
+}
+
+net_vpn6_: VPN_RD net_ip6_
+{
+ $$ = cfg_alloc(sizeof(net_addr_vpn6));
+ net_fill_vpn6($$, net6_prefix(&$2), net6_pxlen(&$2), $1);
+}
+
net_roa4_: net_ip4_ MAX NUM AS NUM
{
$$ = cfg_alloc(sizeof(net_addr_roa4));
@@ -213,10 +229,12 @@ net_roa6_: net_ip6_ MAX NUM AS NUM
};
net_ip_: net_ip4_ | net_ip6_ ;
+net_vpn_: net_vpn4_ | net_vpn6_ ;
net_roa_: net_roa4_ | net_roa6_ ;
net_:
net_ip_ { $$ = cfg_alloc($1.length); net_copy($$, &($1)); }
+ | net_vpn_
| net_roa_
| net_flow_
;
@@ -254,6 +272,8 @@ net_any:
net_or_ipa:
net_ip4_
| net_ip6_
+ | net_vpn4_ { $$ = *$1; }
+ | net_vpn6_ { $$ = *$1; }
| IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); }
| IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); }
| SYM {
@@ -266,6 +286,23 @@ net_or_ipa:
}
;
+label_stack_start: NUM
+{
+ $$ = cfg_allocz(sizeof(u32) * (MPLS_MAX_LABEL_STACK+1));
+ $$[0] = 1;
+ $$[1] = $1;
+};
+
+label_stack:
+ label_stack_start
+ | label_stack '/' NUM {
+ if ($1[0] >= MPLS_MAX_LABEL_STACK)
+ cf_error("Too many labels in stack");
+ $1[0]++;
+ $1[*$1] = $3;
+ $$ = $1;
+ }
+;
datetime:
TEXT {
diff --git a/configure.in b/configure.in
index bb779be7..af9c452d 100644
--- a/configure.in
+++ b/configure.in
@@ -57,6 +57,7 @@ if test "$ac_test_CFLAGS" != set ; then
bird_cflags_default=yes
fi
+AC_PROG_CC
AC_PROG_CC_C99
if test -z "$GCC" ; then
AC_MSG_ERROR([This program requires the GNU C Compiler.])
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 3ab9dee9..2f8f18f3 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -4157,12 +4157,12 @@ return packets as undeliverable if they are in your IP block, you don't have any
specific destination for them and you don't want to send them out through the
default route to prevent routing loops).
-<p>There are five types of static routes: `classical' routes telling to forward
-packets to a neighboring router, multipath routes specifying several (possibly
-weighted) neighboring routers, device routes specifying forwarding to hosts on a
-directly connected network, recursive routes computing their nexthops by doing
-route table lookups for a given IP, and special routes (sink, blackhole etc.)
-which specify a special action to be done instead of forwarding the packet.
+<p>There are four types of static routes: `classical' routes telling to forward
+packets to a neighboring router (single path or multipath, possibly weighted),
+device routes specifying forwarding to hosts on a directly connected network,
+recursive routes computing their nexthops by doing route table lookups for a
+given IP, and special routes (sink, blackhole etc.) which specify a special
+action to be done instead of forwarding the packet.
<p>When the particular destination is not available (the interface is down or
the next hop of the route is not a neighbor at the moment), Static just
@@ -4191,14 +4191,14 @@ definition of the protocol contains mainly a list of static routes.
<p>Route definitions (each may also contain a block of per-route options):
<descrip>
- <tag><label id="static-route-via-ip">route <m/prefix/ via <m/ip/</tag>
- Static route through a neighboring router. For link-local next hops,
+ <tag><label id="static-route-via-ip">route <m/prefix/ via <m/ip/ [mpls <m/num/[/<m/num/[/<m/num/[...]]]]</tag>
+ Static single path route through a neighboring router. For link-local next hops,
interface can be specified as a part of the address (e.g.,
- <cf/via fe80::1234%eth0/).
+ <cf/via fe80::1234%eth0/). MPLS labels should be specified in outer-first order.
- <tag><label id="static-route-via-mpath">route <m/prefix/ multipath via <m/ip/ [weight <m/num/] [bfd <m/switch/] [via <m/.../]</tag>
+ <tag><label id="static-route-via-mpath">route <m/prefix/ via <m/ip/ [mpls <m/num/[/<m/num/[/<m/num/[...]]]] [weight <m/num/] [bfd <m/switch/] [via ...]</tag>
Static multipath route. Contains several nexthops (gateways), possibly
- with their weights.
+ with their weights and MPLS labels.
<tag><label id="static-route-via-iface">route <m/prefix/ via <m/"interface"/</tag>
Static device route through an interface to hosts on a directly
diff --git a/filter/config.Y b/filter/config.Y
index 7b4178be..94a7e307 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -397,7 +397,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
- FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX,
+ FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX,
PREFERENCE,
ROA_CHECK, ASN,
LEN, MAXLEN,
@@ -854,7 +854,6 @@ static_attr:
| PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_PROTO; }
| SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = SA_SOURCE; }
| SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE; $$->a1.i = 1; }
- | CAST { $$ = f_new_inst(); $$->aux = T_ENUM_RTC; $$->a2.i = SA_CAST; }
| DEST { $$ = f_new_inst(); $$->aux = T_ENUM_RTD; $$->a2.i = SA_DEST; $$->a1.i = 1; }
| IFNAME { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_IFNAME; }
| IFINDEX { $$ = f_new_inst(); $$->aux = T_INT; $$->a2.i = SA_IFINDEX; }
diff --git a/filter/filter.c b/filter/filter.c
index a6ef1e10..79a594bf 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -900,15 +900,14 @@ interpret(struct f_inst *what)
switch (what->a2.i)
{
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_PROTO: res.val.s = rta->src->proto->name; break;
case SA_SOURCE: res.val.i = rta->source; break;
case SA_SCOPE: res.val.i = rta->scope; break;
- case SA_CAST: res.val.i = rta->cast; break;
case SA_DEST: res.val.i = rta->dest; break;
- case SA_IFNAME: res.val.s = rta->iface ? rta->iface->name : ""; break;
- case SA_IFINDEX: res.val.i = rta->iface ? rta->iface->index : 0; break;
+ case SA_IFNAME: res.val.s = rta->nh.iface ? rta->nh.iface->name : ""; break;
+ case SA_IFINDEX: res.val.i = rta->nh.iface ? rta->nh.iface->index : 0; break;
default:
bug("Invalid static attribute access (%x)", res.type);
@@ -938,10 +937,10 @@ interpret(struct f_inst *what)
if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" );
- rta->dest = RTD_ROUTER;
- rta->gw = ip;
- rta->iface = n->iface;
- rta->nexthops = NULL;
+ rta->dest = RTD_UNICAST;
+ rta->nh.gw = ip;
+ rta->nh.iface = n->iface;
+ rta->nh.next = NULL;
rta->hostentry = NULL;
}
break;
@@ -956,9 +955,9 @@ interpret(struct f_inst *what)
runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
rta->dest = i;
- rta->gw = IPA_NONE;
- rta->iface = NULL;
- rta->nexthops = NULL;
+ rta->nh.gw = IPA_NONE;
+ rta->nh.iface = NULL;
+ rta->nh.next = NULL;
rta->hostentry = NULL;
break;
diff --git a/filter/filter.h b/filter/filter.h
index a4808731..855219ec 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -174,10 +174,9 @@ void val_format(struct f_val v, buffer *buf);
#define SA_PROTO 4
#define SA_SOURCE 5
#define SA_SCOPE 6
-#define SA_CAST 7
-#define SA_DEST 8
-#define SA_IFNAME 9
-#define SA_IFINDEX 10
+#define SA_DEST 7
+#define SA_IFNAME 8
+#define SA_IFINDEX 9
struct f_tree {
diff --git a/lib/alloca.h b/lib/alloca.h
index f0d61bb4..e5557cdb 100644
--- a/lib/alloca.h
+++ b/lib/alloca.h
@@ -15,4 +15,6 @@
#include <stdlib.h>
#endif
+#define allocz(len) ({ void *_x = alloca(len); memset(_x, 0, len); _x; })
+
#endif
diff --git a/lib/ip.h b/lib/ip.h
index 86750675..ab90bee7 100644
--- a/lib/ip.h
+++ b/lib/ip.h
@@ -325,6 +325,28 @@ static inline ip6_addr ip6_hton(ip6_addr a)
static inline ip6_addr ip6_ntoh(ip6_addr a)
{ return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); }
+#define MPLS_MAX_LABEL_STACK 8
+static inline int
+mpls_get(const char *buf, int buflen, u32 *stack)
+{
+ for (int i=0; (i<MPLS_MAX_LABEL_STACK) && (i*4+3 < buflen); i++)
+ {
+ u32 s = get_u32(buf + i*4);
+ stack[i] = s >> 12;
+ if (s & 0x100)
+ return i+1;
+ }
+ return -1;
+}
+
+static inline int
+mpls_put(char *buf, int len, u32 *stack)
+{
+ for (int i=0; i<len; i++)
+ put_u32(buf + i*4, stack[i] << 12 | (i+1 == len ? 0x100 : 0));
+
+ return len*4;
+}
/*
* Unaligned data access (in network order)
diff --git a/lib/net.c b/lib/net.c
index f283e79f..74cea277 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -13,7 +13,8 @@ const char * const net_label[] = {
[NET_ROA4] = "roa4",
[NET_ROA6] = "roa6",
[NET_FLOW4] = "flow4",
- [NET_FLOW6] = "flow6"
+ [NET_FLOW6] = "flow6",
+ [NET_MPLS] = "mpls",
};
const u16 net_addr_length[] = {
@@ -24,7 +25,8 @@ const u16 net_addr_length[] = {
[NET_ROA4] = sizeof(net_addr_roa4),
[NET_ROA6] = sizeof(net_addr_roa6),
[NET_FLOW4] = 0,
- [NET_FLOW6] = 0
+ [NET_FLOW6] = 0,
+ [NET_MPLS] = sizeof(net_addr_mpls),
};
const u8 net_max_prefix_length[] = {
@@ -35,7 +37,8 @@ const u8 net_max_prefix_length[] = {
[NET_ROA4] = IP4_MAX_PREFIX_LENGTH,
[NET_ROA6] = IP6_MAX_PREFIX_LENGTH,
[NET_FLOW4] = IP4_MAX_PREFIX_LENGTH,
- [NET_FLOW6] = IP6_MAX_PREFIX_LENGTH
+ [NET_FLOW6] = IP6_MAX_PREFIX_LENGTH,
+ [NET_MPLS] = 0,
};
const u16 net_max_text_length[] = {
@@ -46,7 +49,8 @@ const u16 net_max_text_length[] = {
[NET_ROA4] = 34, /* "255.255.255.255/32-32 AS4294967295" */
[NET_ROA6] = 60, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128-128 AS4294967295" */
[NET_FLOW4] = 0, /* "flow4 { ... }" */
- [NET_FLOW6] = 0 /* "flow6 { ... }" */
+ [NET_FLOW6] = 0, /* "flow6 { ... }" */
+ [NET_MPLS] = 7, /* "1048575" */
};
@@ -54,6 +58,7 @@ int
net_format(const net_addr *N, char *buf, int buflen)
{
net_addr_union *n = (void *) N;
+ buf[0] = 0;
switch (n->n.type)
{
@@ -62,9 +67,22 @@ net_format(const net_addr *N, char *buf, int buflen)
case NET_IP6:
return bsnprintf(buf, buflen, "%I6/%d", n->ip6.prefix, n->ip6.pxlen);
case NET_VPN4:
- return bsnprintf(buf, buflen, "%u:%u %I4/%d", (u32) (n->vpn4.rd >> 32), (u32) n->vpn4.rd, n->vpn4.prefix, n->vpn4.pxlen);
+ switch (n->vpn4.rd >> 48)
+ {
+ case 0: return bsnprintf(buf, buflen, "0:%u:%u %I4/%d", (u32) (n->vpn4.rd >> 32), (u32) n->vpn4.rd, n->vpn4.prefix, n->vpn4.pxlen);
+ case 1: return bsnprintf(buf, buflen, "1:%I4:%u %I4/%d", ip4_from_u32(n->vpn4.rd >> 16), (u32) (n->vpn4.rd & 0xffff), n->vpn4.prefix, n->vpn4.pxlen);
+ case 2: return bsnprintf(buf, buflen, "2:%u:%u %I4/%d", (u32) (n->vpn4.rd >> 16), (u32) (n->vpn4.rd & 0xffff), n->vpn4.prefix, n->vpn4.pxlen);
+ default: return bsnprintf(buf, buflen, "X:%08x:%08x %I4/%d", (u32) (n->vpn4.rd >> 32), (u32) n->vpn4.rd, n->vpn4.prefix, n->vpn4.pxlen);
+ }
case NET_VPN6:
- return bsnprintf(buf, buflen, "%u:%u %I6/%d", (u32) (n->vpn6.rd >> 32), (u32) n->vpn6.rd, n->vpn6.prefix, n->vpn6.pxlen);
+ /* XXX: RD format is specified for VPN4; not found any for VPN6, reusing the same as for VPN4 */
+ switch (n->vpn6.rd >> 48)
+ {
+ case 0: return bsnprintf(buf, buflen, "0:%u:%u %I6/%d", (u32) (n->vpn6.rd >> 32), (u32) n->vpn6.rd, n->vpn6.prefix, n->vpn6.pxlen);
+ case 1: return bsnprintf(buf, buflen, "1:%I4:%u %I6/%d", ip4_from_u32(n->vpn6.rd >> 16), (u32) (n->vpn6.rd & 0xffff), n->vpn6.prefix, n->vpn6.pxlen);
+ case 2: return bsnprintf(buf, buflen, "2:%u:%u %I6/%d", (u32) (n->vpn6.rd >> 16), (u32) (n->vpn6.rd & 0xffff), n->vpn6.prefix, n->vpn6.pxlen);
+ default: return bsnprintf(buf, buflen, "X:%08x:%08x %I6/%d", (u32) (n->vpn6.rd >> 32), (u32) n->vpn6.rd, n->vpn6.prefix, n->vpn6.pxlen);
+ }
case NET_ROA4:
return bsnprintf(buf, buflen, "%I4/%u-%u AS%u", n->roa4.prefix, n->roa4.pxlen, n->roa4.max_pxlen, n->roa4.asn);
case NET_ROA6:
@@ -73,9 +91,11 @@ net_format(const net_addr *N, char *buf, int buflen)
return flow4_net_format(buf, buflen, &n->flow4);
case NET_FLOW6:
return flow6_net_format(buf, buflen, &n->flow6);
+ case NET_MPLS:
+ return bsnprintf(buf, buflen, "%u", n->mpls.label);
}
- return 0;
+ bug("unknown network type");
}
ip_addr
@@ -95,6 +115,7 @@ net_pxmask(const net_addr *a)
case NET_FLOW6:
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
+ case NET_MPLS:
default:
return IPA_NONE;
}
@@ -124,6 +145,8 @@ net_compare(const net_addr *a, const net_addr *b)
return net_compare_flow4((const net_addr_flow4 *) a, (const net_addr_flow4 *) b);
case NET_FLOW6:
return net_compare_flow6((const net_addr_flow6 *) a, (const net_addr_flow6 *) b);
+ case NET_MPLS:
+ return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
}
return 0;
}
@@ -165,6 +188,9 @@ net_validate(const net_addr *N)
case NET_FLOW6:
return net_validate_ip6((net_addr_ip6 *) N);
+ case NET_MPLS:
+ return net_validate_mpls((net_addr_mpls *) N);
+
default:
return 0;
}
@@ -188,6 +214,9 @@ net_normalize(net_addr *N)
case NET_ROA6:
case NET_FLOW6:
return net_normalize_ip6(&n->ip6);
+
+ case NET_MPLS:
+ return;
}
}
@@ -209,6 +238,9 @@ net_classify(const net_addr *N)
case NET_ROA6:
case NET_FLOW6:
return ip6_zero(n->ip6.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6.prefix);
+
+ case NET_MPLS:
+ return IADDR_HOST | SCOPE_UNIVERSE;
}
return IADDR_INVALID;
@@ -235,6 +267,7 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)),
ip6_mkmask(net6_pxlen(n))));
+ case NET_MPLS:
default:
return 0;
}
diff --git a/lib/net.h b/lib/net.h
index f07e6b24..7144bcb9 100644
--- a/lib/net.h
+++ b/lib/net.h
@@ -21,7 +21,8 @@
#define NET_ROA6 6
#define NET_FLOW4 7
#define NET_FLOW6 8
-#define NET_MAX 9
+#define NET_MPLS 9
+#define NET_MAX 10
#define NB_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6)
@@ -31,6 +32,7 @@
#define NB_ROA6 (1 << NET_ROA6)
#define NB_FLOW4 (1 << NET_FLOW4)
#define NB_FLOW6 (1 << NET_FLOW6)
+#define NB_MPLS (1 << NET_MPLS)
#define NB_IP (NB_IP4 | NB_IP6)
#define NB_ANY 0xffffffff
@@ -108,6 +110,13 @@ typedef struct net_addr_flow6 {
byte data[0];
} net_addr_flow6;
+typedef struct net_addr_mpls {
+ u8 type;
+ u8 pxlen;
+ u16 length;
+ u32 label;
+} net_addr_mpls;
+
typedef union net_addr_union {
net_addr n;
net_addr_ip4 ip4;
@@ -118,6 +127,7 @@ typedef union net_addr_union {
net_addr_roa6 roa6;
net_addr_flow4 flow4;
net_addr_flow6 flow6;
+ net_addr_mpls mpls;
} net_addr_union;
@@ -153,6 +163,8 @@ extern const u16 net_max_text_length[];
#define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix })
+#define NET_ADDR_MPLS(label) \
+ ((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
@@ -173,6 +185,9 @@ static inline void net_fill_roa4(net_addr *a, ip4_addr prefix, uint pxlen, uint
static inline void net_fill_roa6(net_addr *a, ip6_addr prefix, uint pxlen, uint max_pxlen, u32 asn)
{ *(net_addr_roa6 *)a = NET_ADDR_ROA6(prefix, pxlen, max_pxlen, asn); }
+static inline void net_fill_mpls(net_addr *a, u32 label)
+{ *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
+
static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
{
if (ipa_is_ip4(prefix))
@@ -238,11 +253,20 @@ static inline ip_addr net_prefix(const net_addr *a)
case NET_FLOW6:
return ipa_from_ip6(net6_prefix(a));
+ case NET_MPLS:
default:
return IPA_NONE;
}
}
+static inline u32 net_mpls(const net_addr *a)
+{
+ if (a->type == NET_MPLS)
+ return ((net_addr_mpls *) a)->label;
+
+ bug("Can't call net_mpls on non-mpls net_addr");
+}
+
static inline uint net4_pxlen(const net_addr *a)
{ return a->pxlen; }
@@ -282,6 +306,9 @@ static inline int net_equal_flow4(const net_addr_flow4 *a, const net_addr_flow4
static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
{ return net_equal((const net_addr *) a, (const net_addr *) b); }
+static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
+{ return !memcmp(a, b, sizeof(net_addr_mpls)); }
+
static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
@@ -314,6 +341,8 @@ static inline int net_zero_flow4(const net_addr_flow4 *a)
static inline int net_zero_flow6(const net_addr_flow6 *a)
{ return !a->pxlen && ip6_zero(a->prefix) && !a->data; }
+static inline int net_zero_mpls(const net_addr_mpls *a)
+{ return !a->label; }
static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
@@ -340,6 +369,9 @@ static inline int net_compare_flow4(const net_addr_flow4 *a, const net_addr_flow
static inline int net_compare_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow6)); }
+static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
+{ return uint_cmp(a->label, b->label); }
+
int net_compare(const net_addr *a, const net_addr *b);
@@ -370,6 +402,13 @@ static inline void net_copy_flow4(net_addr_flow4 *dst, const net_addr_flow4 *src
static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src)
{ memcpy(dst, src, src->length); }
+static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
+{ memcpy(dst, src, sizeof(net_addr_mpls)); }
+
+
+/* XXXX */
+static inline u32 u64_hash(u64 a)
+{ return u32_hash(a); }
static inline u32 net_hash_ip4(const net_addr_ip4 *n)
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
@@ -377,10 +416,6 @@ static inline u32 net_hash_ip4(const net_addr_ip4 *n)
static inline u32 net_hash_ip6(const net_addr_ip6 *n)
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
-/* XXXX */
-static inline u32 u64_hash(u64 a)
-{ return u32_hash(a); }
-
static inline u32 net_hash_vpn4(const net_addr_vpn4 *n)
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
@@ -399,6 +434,9 @@ static inline u32 net_hash_flow4(const net_addr_flow4 *n)
static inline u32 net_hash_flow6(const net_addr_flow6 *n)
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+static inline u32 net_hash_mpls(const net_addr_mpls *n)
+{ return n->label; }
+
u32 net_hash(const net_addr *a);
@@ -414,6 +452,11 @@ static inline int net_validate_ip6(const net_addr_ip6 *n)
ip6_zero(ip6_and(n->prefix, ip6_not(ip6_mkmask(n->pxlen))));
}
+static inline int net_validate_mpls(const net_addr_mpls *n)
+{
+ return n->label < (1 << 20);
+}
+
int net_validate(const net_addr *N);
diff --git a/nest/config.Y b/nest/config.Y
index 23d6a452..511936ef 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -78,8 +78,7 @@ CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
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_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)
%type <i32> idval
diff --git a/nest/route.h b/nest/route.h
index 12e67d61..c16b2643 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -195,7 +195,7 @@ struct hostentry {
unsigned hash_key; /* Hash key */
unsigned uc; /* Use count */
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_...) */
u32 igp_metric; /* Chosen route IGP metric */
};
@@ -333,12 +333,15 @@ void rt_show(struct rt_show_data *);
* construction of BGP route attribute lists.
*/
-/* Multipath next-hop */
-struct mpnh {
+/* Nexthop structure */
+struct nexthop {
ip_addr gw; /* Next hop */
struct iface *iface; /* Outgoing interface */
- struct mpnh *next;
+ struct nexthop *next;
byte weight;
+ byte labels_append; /* Number of labels before hostentry was applied */
+ byte labels; /* Number of labels prepended */
+ u32 label[0];
};
struct rte_src {
@@ -354,20 +357,16 @@ typedef struct rta {
struct rta *next, **pprev; /* Hash chain */
u32 uc; /* Use count */
u32 hash_key; /* Hash over important fields */
- struct mpnh *nexthops; /* Next-hops for multipath routes */
struct ea_list *eattrs; /* Extended Attribute chain */
struct rte_src *src; /* Route source that created the route */
struct hostentry *hostentry; /* Hostentry for recursive next-hops */
- struct iface *iface; /* Outgoing interface */
- ip_addr gw; /* Next hop */
ip_addr from; /* Advertising router */
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
- byte source; /* Route source (RTS_...) */
- byte scope; /* Route scope (SCOPE_... -- see ip.h) */
- byte cast; /* Casting type (RTC_...) */
- byte dest; /* Route destination type (RTD_...) */
- byte flags; /* Route flags (RTF_...), now unused */
- byte aflags; /* Attribute cache flags (RTAF_...) */
+ u8 source; /* Route source (RTS_...) */
+ u8 scope; /* Route scope (SCOPE_... -- see ip.h) */
+ u8 dest; /* Route destination type (RTD_...) */
+ u8 aflags;
+ struct nexthop nh; /* Next hop */
} rta;
#define RTS_DUMMY 0 /* Dummy route to be removed soon */
@@ -392,13 +391,11 @@ typedef struct rta {
#define RTC_MULTICAST 2
#define RTC_ANYCAST 3 /* IPv6 Anycast */
-#define RTD_ROUTER 0 /* Next hop is neighbor router */
-#define RTD_DEVICE 1 /* Points to device */
+#define RTD_NONE 0 /* Undefined next hop */
+#define RTD_UNICAST 1 /* Next hop is neighbor router */
#define RTD_BLACKHOLE 2 /* Silently drop packets */
#define RTD_UNREACHABLE 3 /* Reject as unreachable */
#define RTD_PROHIBIT 4 /* Administratively prohibited */
-#define RTD_MULTIPATH 5 /* Multipath route (nexthops != NULL) */
-#define RTD_NONE 6 /* Invalid RTD */
/* Flags for net->n.flags, used by kernel syncer */
#define KRF_INSTALLED 0x80 /* This route should be installed in the kernel */
@@ -412,7 +409,7 @@ typedef struct rta {
/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */
static inline int rte_is_reachable(rte *r)
-{ uint d = r->attrs->dest; return (d == RTD_ROUTER) || (d == RTD_DEVICE) || (d == RTD_MULTIPATH); }
+{ return r->attrs->dest == RTD_UNICAST; }
/*
@@ -517,14 +514,22 @@ uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
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);
-int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */
-static inline int mpnh_same(struct mpnh *x, struct mpnh *y)
-{ return (x == y) || mpnh__same(x, y); }
-struct mpnh *mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp);
-void mpnh_insert(struct mpnh **n, struct mpnh *y);
-int mpnh_is_sorted(struct mpnh *x);
+#define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
+
+static inline size_t nexthop_size(const struct nexthop *nh)
+{ return sizeof(struct nexthop) + sizeof(u32)*nh->labels; }
+int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath nexthops */
+static inline int nexthop_same(struct nexthop *x, struct nexthop *y)
+{ return (x == y) || nexthop__same(x, y); }
+struct nexthop *nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp);
+static inline void nexthop_link(struct rta *a, struct nexthop *from)
+{ memcpy(&a->nh, from, nexthop_size(from)); }
+void nexthop_insert(struct nexthop **n, struct nexthop *y);
+int nexthop_is_sorted(struct nexthop *x);
void rta_init(void);
+static inline size_t rta_size(const rta *a) { return sizeof(rta) + sizeof(u32)*a->nh.labels; }
+#define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */
static inline int rta_is_cached(rta *r) { return r->aflags & RTAF_CACHED; }
static inline rta *rta_clone(rta *r) { r->uc++; return r; }
diff --git a/nest/rt-attr.c b/nest/rt-attr.c
index 94f25de8..afc97e22 100644
--- a/nest/rt-attr.c
+++ b/nest/rt-attr.c
@@ -60,8 +60,8 @@
pool *rta_pool;
-static slab *rta_slab;
-static slab *mpnh_slab;
+static slab *rta_slab_[4];
+static slab *nexthop_slab_[4];
static slab *rte_src_slab;
static struct idm src_ids;
@@ -144,27 +144,38 @@ rt_prune_sources(void)
*/
static inline u32
-mpnh_hash(struct mpnh *x)
+nexthop_hash(struct nexthop *x)
{
u32 h = 0;
for (; x; x = x->next)
- h ^= ipa_hash(x->gw);
+ {
+ h ^= ipa_hash(x->gw) ^ (h << 5) ^ (h >> 9);
+
+ for (int i = 0; i < x->labels; i++)
+ h ^= x->label[i] ^ (h << 6) ^ (h >> 7);
+ }
return h;
}
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)
- 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) || (x->labels != y->labels))
return 0;
+ for (int i = 0; i < x->labels; i++)
+ if (x->label[i] != y->label[i])
+ return 0;
+ }
+
return x == y;
}
static int
-mpnh_compare_node(struct mpnh *x, struct mpnh *y)
+nexthop_compare_node(struct nexthop *x, struct nexthop *y)
{
int r;
@@ -182,22 +193,33 @@ mpnh_compare_node(struct mpnh *x, struct mpnh *y)
if (r)
return r;
+ r = ((int) y->labels) - ((int) x->labels);
+ if (r)
+ return r;
+
+ for (int i = 0; i < y->labels; i++)
+ {
+ r = ((int) y->label[i]) - ((int) x->label[i]);
+ if (r)
+ return r;
+ }
+
return ((int) x->iface->index) - ((int) y->iface->index);
}
-static inline struct mpnh *
-mpnh_copy_node(const struct mpnh *src, linpool *lp)
+static inline struct nexthop *
+nexthop_copy_node(const struct nexthop *src, linpool *lp)
{
- struct mpnh *n = lp_alloc(lp, sizeof(struct mpnh));
- n->gw = src->gw;
- n->iface = src->iface;
+ struct nexthop *n = lp_alloc(lp, nexthop_size(src));
+
+ memcpy(n, src, nexthop_size(src));
n->next = NULL;
- n->weight = src->weight;
+
return n;
}
/**
- * mpnh_merge - merge nexthop lists
+ * nexthop_merge - merge nexthop lists
* @x: list 1
* @y: list 2
* @rx: reusability of list @x
@@ -205,7 +227,7 @@ mpnh_copy_node(const struct mpnh *src, linpool *lp)
* @max: max number of 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
* result is sorted too. The number of nexthops in result is limited by @max.
* New nodes are allocated from linpool @lp.
@@ -218,28 +240,28 @@ mpnh_copy_node(const struct mpnh *src, linpool *lp)
* resulting list is no longer needed. When reusability is not set, the
* corresponding lists are not modified nor linked from the resulting list.
*/
-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)
{
- struct mpnh *root = NULL;
- struct mpnh **n = &root;
+ struct nexthop *root = NULL;
+ struct nexthop **n = &root;
while ((x || y) && max--)
{
- int cmp = mpnh_compare_node(x, y);
+ int cmp = nexthop_compare_node(x, y);
if (cmp < 0)
{
- *n = rx ? x : mpnh_copy_node(x, lp);
+ *n = rx ? x : nexthop_copy_node(x, lp);
x = x->next;
}
else if (cmp > 0)
{
- *n = ry ? y : mpnh_copy_node(y, lp);
+ *n = ry ? y : nexthop_copy_node(y, lp);
y = y->next;
}
else
{
- *n = rx ? x : (ry ? y : mpnh_copy_node(x, lp));
+ *n = rx ? x : (ry ? y : nexthop_copy_node(x, lp));
x = x->next;
y = y->next;
}
@@ -251,11 +273,11 @@ mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp)
}
void
-mpnh_insert(struct mpnh **n, struct mpnh *x)
+nexthop_insert(struct nexthop **n, struct nexthop *x)
{
for (; *n; n = &((*n)->next))
{
- int cmp = mpnh_compare_node(*n, x);
+ int cmp = nexthop_compare_node(*n, x);
if (cmp < 0)
continue;
@@ -270,28 +292,37 @@ mpnh_insert(struct mpnh **n, struct mpnh *x)
}
int
-mpnh_is_sorted(struct mpnh *x)
+nexthop_is_sorted(struct nexthop *x)
{
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 1;
}
-static struct mpnh *
-mpnh_copy(struct mpnh *o)
+static inline slab *
+nexthop_slab(struct nexthop *nh)
+{
+ return nexthop_slab_[MIN(nh->labels, 3)];
+}
+
+static struct nexthop *
+nexthop_copy(struct nexthop *o)
{
- struct mpnh *first = NULL;
- struct mpnh **last = &first;
+ struct nexthop *first = NULL;
+ struct nexthop **last = &first;
for (; o; o = o->next)
{
- struct mpnh *n = sl_alloc(mpnh_slab);
+ struct nexthop *n = sl_alloc(nexthop_slab(o));
n->gw = o->gw;
n->iface = o->iface;
n->next = NULL;
n->weight = o->weight;
+ n->labels = o->labels;
+ for (int i=0; i<o->labels; i++)
+ n->label[i] = o->label[i];
*last = n;
last = &(n->next);
@@ -301,14 +332,14 @@ mpnh_copy(struct mpnh *o)
}
static void
-mpnh_free(struct mpnh *o)
+nexthop_free(struct nexthop *o)
{
- struct mpnh *n;
+ struct nexthop *n;
while (o)
{
n = o->next;
- sl_free(mpnh_slab, o);
+ sl_free(nexthop_slab(o), o);
o = n;
}
}
@@ -994,19 +1025,14 @@ rta_hash(rta *a)
#define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f));
MIX(src);
MIX(hostentry);
- MIX(iface);
- MIX(gw);
MIX(from);
MIX(igp_metric);
MIX(source);
MIX(scope);
- MIX(cast);
MIX(dest);
- MIX(flags);
- MIX(aflags);
#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
@@ -1015,26 +1041,28 @@ rta_same(rta *x, rta *y)
return (x->src == y->src &&
x->source == y->source &&
x->scope == y->scope &&
- 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 &&
- mpnh_same(x->nexthops, y->nexthops) &&
+ nexthop_same(&(x->nh), &(y->nh)) &&
ea_same(x->eattrs, y->eattrs));
}
+static inline slab *
+rta_slab(rta *a)
+{
+ return rta_slab_[a->nh.labels > 2 ? 3 : a->nh.labels];
+}
+
static rta *
rta_copy(rta *o)
{
- rta *r = sl_alloc(rta_slab);
+ rta *r = sl_alloc(rta_slab(o));
- memcpy(r, o, sizeof(rta));
+ memcpy(r, o, rta_size(o));
r->uc = 1;
- r->nexthops = mpnh_copy(o->nexthops);
+ r->nh.next = nexthop_copy(o->nh.next);
r->eattrs = ea_list_copy(o->eattrs);
return r;
}
@@ -1130,16 +1158,23 @@ rta__free(rta *a)
a->aflags = 0; /* Poison the entry */
rt_unlock_hostentry(a->hostentry);
rt_unlock_source(a->src);
- mpnh_free(a->nexthops);
+ if (a->nh.next)
+ nexthop_free(a->nh.next);
ea_free(a->eattrs);
- sl_free(rta_slab, a);
+ sl_free(rta_slab(a), a);
}
rta *
rta_do_cow(rta *o, linpool *lp)
{
- rta *r = lp_alloc(lp, sizeof(rta));
- memcpy(r, o, sizeof(rta));
+ rta *r = lp_alloc(lp, rta_size(o));
+ memcpy(r, o, rta_size(o));
+ for (struct nexthop **nhn = &(r->nh.next), *nho = o->nh.next; nho; nho = nho->next)
+ {
+ *nhn = lp_alloc(lp, nexthop_size(nho));
+ memcpy(*nhn, nho, nexthop_size(nho));
+ nhn = &((*nhn)->next);
+ }
r->aflags = 0;
r->uc = 0;
return r;
@@ -1158,19 +1193,23 @@ rta_dump(rta *a)
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
- static char *rtc[] = { "", " BC", " MC", " AC" };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
- debug("p=%s uc=%d %s %s%s%s h=%04x",
- a->src->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope), rtc[a->cast],
+ debug("p=%s uc=%d %s %s%s h=%04x",
+ a->src->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope),
rtd[a->dest], a->hash_key);
if (!(a->aflags & RTAF_CACHED))
debug(" !CACHED");
debug(" <-%I", a->from);
- if (a->dest == RTD_ROUTER)
- debug(" ->%I", a->gw);
- if (a->dest == RTD_DEVICE || a->dest == RTD_ROUTER)
- debug(" [%s]", a->iface ? a->iface->name : "???" );
+ if (a->dest == RTD_UNICAST)
+ for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
+ {
+ if (ipa_nonzero(nh->gw)) debug(" ->%I", nh->gw);
+ if (nh->labels) debug(" L %d", nh->label[0]);
+ for (int i=1; i<nh->labels; i++)
+ debug("/%d", nh->label[i]);
+ debug(" [%s]", nh->iface ? nh->iface->name : "???");
+ }
if (a->eattrs)
{
debug(" EA: ");
@@ -1206,10 +1245,9 @@ rta_show(struct cli *c, rta *a, ea_list *eal)
{
static char *src_names[] = { "dummy", "static", "inherit", "device", "static-device", "redirect",
"RIP", "OSPF", "OSPF-IA", "OSPF-E1", "OSPF-E2", "BGP", "pipe" };
- static char *cast_names[] = { "unicast", "broadcast", "multicast", "anycast" };
int i;
- cli_printf(c, -1008, "\tType: %s %s %s", src_names[a->source], cast_names[a->cast], ip_scope_text(a->scope));
+ cli_printf(c, -1008, "\tType: %s %s", src_names[a->source], ip_scope_text(a->scope));
if (!eal)
eal = a->eattrs;
for(; eal; eal=eal->next)
@@ -1227,8 +1265,17 @@ void
rta_init(void)
{
rta_pool = rp_new(&root_pool, "Attributes");
- rta_slab = sl_new(rta_pool, sizeof(rta));
- mpnh_slab = sl_new(rta_pool, sizeof(struct mpnh));
+
+ rta_slab_[0] = sl_new(rta_pool, sizeof(rta));
+ rta_slab_[1] = sl_new(rta_pool, sizeof(rta) + sizeof(u32));
+ rta_slab_[2] = sl_new(rta_pool, sizeof(rta) + sizeof(u32)*2);
+ rta_slab_[3] = sl_new(rta_pool, sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK);
+
+ nexthop_slab_[0] = sl_new(rta_pool, sizeof(struct nexthop));
+ nexthop_slab_[1] = sl_new(rta_pool, sizeof(struct nexthop) + sizeof(u32));
+ nexthop_slab_[2] = sl_new(rta_pool, sizeof(struct nexthop) + sizeof(u32)*2);
+ nexthop_slab_[3] = sl_new(rta_pool, sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK);
+
rta_alloc_hash();
rte_src_init();
}
diff --git a/nest/rt-dev.c b/nest/rt-dev.c
index d98cd79f..9993da24 100644
--- a/nest/rt-dev.c
+++ b/nest/rt-dev.c
@@ -78,9 +78,8 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
.src = src,
.source = RTS_DEVICE,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST,
- .dest = RTD_DEVICE,
- .iface = ad->iface
+ .dest = RTD_UNICAST,
+ .nh.iface = ad->iface,
};
a = rta_lookup(&a0);
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 8c429874..a33b7909 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -708,19 +708,17 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
}
-static struct mpnh *
-mpnh_merge_rta(struct mpnh *nhs, rta *a, linpool *pool, int max)
+static struct nexthop *
+nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
{
- struct mpnh nh = { .gw = a->gw, .iface = a->iface };
- struct mpnh *nh2 = (a->dest == RTD_MULTIPATH) ? a->nexthops : &nh;
- return mpnh_merge(nhs, nh2, 1, 0, max, pool);
+ return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
}
rte *
rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
{
// struct proto *p = c->proto;
- struct mpnh *nhs = NULL;
+ struct nexthop *nhs = NULL;
rte *best0, *best, *rt0, *rt, *tmp;
best0 = net->routes;
@@ -745,7 +743,7 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
continue;
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)
rte_free(tmp);
@@ -753,13 +751,12 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
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)
{
best = rte_cow_rta(best, pool);
- best->attrs->dest = RTD_MULTIPATH;
- best->attrs->nexthops = nhs;
+ nexthop_link(best->attrs, nhs);
}
}
@@ -922,7 +919,7 @@ rte_validate(rte *e)
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",
n->n.addr, e->sender->proto->name);
@@ -1763,33 +1760,63 @@ rta_next_hop_outdated(rta *a)
if (!he->src)
return a->dest != RTD_UNREACHABLE;
- return (a->iface != he->src->iface) || !ipa_equal(a->gw, he->gw) ||
- (a->dest != he->dest) || (a->igp_metric != he->igp_metric) ||
- !mpnh_same(a->nexthops, he->src->nexthops);
+ return (a->dest != he->dest) || (a->igp_metric != he->igp_metric) ||
+ !nexthop_same(&(a->nh), he->nh);
}
static inline void
rta_apply_hostentry(rta *a, struct hostentry *he)
{
a->hostentry = he;
- a->iface = he->src ? he->src->iface : NULL;
- a->gw = he->gw;
a->dest = he->dest;
a->igp_metric = he->igp_metric;
- a->nexthops = he->src ? he->src->nexthops : NULL;
+
+ if (a->nh.labels_append == 0)
+ {
+ a->nh = *(he->nh);
+ a->nh.labels_append = 0;
+ return;
+ }
+
+ int labels_append = a->nh.labels_append;
+ u32 label_stack[MPLS_MAX_LABEL_STACK];
+ memcpy(label_stack, a->nh.label, labels_append * sizeof(u32));
+
+ struct nexthop *nhp = NULL;
+ for (struct nexthop *nh = he->nh; nh; nh = nh->next)
+ {
+ nhp = nhp ? (nhp->next = lp_alloc(rte_update_pool, NEXTHOP_MAX_SIZE)) : &(a->nh);
+ nhp->gw = ipa_nonzero(nh->gw) ? nh->gw : he->link;
+ nhp->iface = nh->iface; /* FIXME: This is at least strange, if not utter nonsense. */
+ nhp->weight = nh->weight;
+ nhp->labels = nh->labels + labels_append;
+ nhp->labels_append = labels_append;
+ if (nhp->labels <= MPLS_MAX_LABEL_STACK)
+ {
+ memcpy(nhp->label, nh->label, nh->labels * sizeof(u32));
+ memcpy(&(nhp->label[nh->labels]), label_stack, labels_append * sizeof(u32));
+ }
+ else
+ {
+ log(L_WARN "Sum of label stack sizes %d + %d = %d exceedes allowed maximum (%d)",
+ nh->labels, labels_append, nhp->labels, MPLS_MAX_LABEL_STACK);
+ a->dest = RTD_UNREACHABLE;
+ break;
+ }
+ }
}
static inline rte *
rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
{
- rta a;
- memcpy(&a, old->attrs, sizeof(rta));
- rta_apply_hostentry(&a, old->attrs->hostentry);
- a.aflags = 0;
+ rta *a = alloca(RTA_MAX_SIZE);
+ memcpy(a, old->attrs, rta_size(old->attrs));
+ rta_apply_hostentry(a, old->attrs->hostentry);
+ a->aflags = 0;
rte *e = sl_alloc(rte_slab);
memcpy(e, old, sizeof(rte));
- e->attrs = rta_lookup(&a);
+ e->attrs = rta_lookup(a);
return e;
}
@@ -2310,8 +2337,7 @@ rt_get_igp_metric(rte *rt)
return rt->u.rip.metric;
#endif
- /* Device routes */
- if ((a->dest != RTD_ROUTER) && (a->dest != RTD_MULTIPATH))
+ if (a->source == RTS_DEVICE)
return 0;
return IGP_METRIC_UNKNOWN;
@@ -2325,7 +2351,6 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
/* Reset the hostentry */
he->src = NULL;
- he->gw = IPA_NONE;
he->dest = RTD_UNREACHABLE;
he->igp_metric = 0;
@@ -2346,24 +2371,25 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
goto done;
}
- if (a->dest == RTD_DEVICE)
+ if ((a->dest == RTD_UNICAST) && ipa_zero(a->nh.gw) && !a->next)
{
- if (if_local_addr(he->addr, a->iface))
+ /* We have singlepath device route */
+ if (if_local_addr(he->addr, a->nh.iface))
{
/* 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",
- he->addr, a->iface->name);
+ he->addr, a->nh.iface->name);
goto done;
}
/* The host is directly reachable, use link as a gateway */
- he->gw = he->link;
- he->dest = RTD_ROUTER;
+ he->nh = NULL;
+ he->dest = RTD_UNICAST;
}
else
{
/* The host is reachable through some route entry */
- he->gw = a->gw;
+ he->nh = &(a->nh);
he->dest = a->dest;
}
@@ -2442,16 +2468,14 @@ rt_format_via(rte *e)
rta *a = e->attrs;
/* 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)
{
- case RTD_ROUTER: bsprintf(via, "via %I on %s", a->gw, a->iface->name); break;
- case RTD_DEVICE: bsprintf(via, "dev %s", a->iface->name); break;
+ case RTD_UNICAST: bsprintf(via, "unicast"); break;
case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
- case RTD_MULTIPATH: bsprintf(via, "multipath"); break;
default: bsprintf(via, "???");
}
return via;
@@ -2466,10 +2490,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 sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
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);
- 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);
else
from[0] = 0;
@@ -2490,10 +2514,24 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
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,
tm, from, primary ? (sync_error ? " !" : " *") : "", info);
- for (nh = a->nexthops; nh; nh = nh->next)
- cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1);
+ for (nh = &(a->nh); nh; nh = nh->next)
+ {
+ char ls[MPLS_MAX_LABEL_STACK*8 + 5]; char *lsp = ls;
+ if (nh->labels)
+ {
+ lsp += bsprintf(lsp, " mpls %d", nh->label[0]);
+ for (int i=1;i<nh->labels; i++)
+ lsp += bsprintf(lsp, "/%d", nh->label[i]);
+ *lsp++ = '\0';
+ }
+ if (a->nh.next)
+ cli_printf(c, -1007, "\tvia %I%s on %s weight %d", nh->gw, (nh->labels ? ls : ""), nh->iface->name, nh->weight + 1);
+ else
+ cli_printf(c, -1007, "\tvia %I%s on %s", nh->gw, (nh->labels ? ls : ""), nh->iface->name);
+ }
if (d->verbose)
rta_show(c, a, tmpa);
+
}
static void
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 73318c6a..f2a8e8b5 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -1491,8 +1491,7 @@ bgp_get_neighbor(rte *r)
static inline int
rte_resolvable(rte *rt)
{
- int rd = rt->attrs->dest;
- return (rd == RTD_ROUTER) || (rd == RTD_DEVICE) || (rd == RTD_MULTIPATH);
+ return rt->attrs->dest == RTD_UNICAST;
}
int
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 385d5a36..2106e0d1 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -743,9 +743,8 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
if (!nbr || (nbr->scope == SCOPE_HOST))
WITHDRAW(BAD_NEXT_HOP);
- a->dest = RTD_ROUTER;
- a->gw = nbr->addr;
- a->iface = nbr->iface;
+ a->dest = RTD_UNICAST;
+ a->nh = (struct nexthop){ .gw = nbr->addr, .iface = nbr->iface };
a->hostentry = NULL;
a->igp_metric = 0;
}
@@ -792,8 +791,8 @@ bgp_use_gateway(struct bgp_export_state *s)
if (s->channel->cf->next_hop_self)
return 0;
- /* We need valid global gateway */
- if ((ra->dest != RTD_ROUTER) || ipa_zero(ra->gw) || ipa_is_link_local(ra->gw))
+ /* We need one valid global gateway */
+ if ((ra->dest != RTD_UNICAST) || ra->nh.next || ipa_zero(ra->nh.gw) || ipa_is_link_local(ra->nh.gw))
return 0;
/* Use it when exported to internal peers */
@@ -801,7 +800,7 @@ bgp_use_gateway(struct bgp_export_state *s)
return 1;
/* 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
@@ -811,7 +810,7 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
{
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);
}
else
@@ -1706,13 +1705,10 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
if (ea)
{
- a = alloca(sizeof(struct rta));
- memset(a, 0, sizeof(struct rta));
+ a = allocz(sizeof(struct rta));
a->source = RTS_BGP;
a->scope = SCOPE_UNIVERSE;
- a->cast = RTC_UNICAST;
- a->dest = RTD_UNREACHABLE;
a->from = s->proto->cf->remote_ip;
a->eattrs = ea;
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index d074600a..daf76ff2 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -235,7 +235,7 @@ ospf_start(struct proto *P)
p->lsab_size = 256;
p->lsab_used = 0;
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->area_list));
fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6,
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index 49167ceb..df9eb75b 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -22,7 +22,7 @@ static inline void reset_ri(ort *ort)
}
static inline int
-nh_is_vlink(struct mpnh *nhs)
+nh_is_vlink(struct nexthop *nhs)
{
return !nhs->iface;
}
@@ -33,20 +33,19 @@ unresolved_vlink(ort *ort)
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)
{
- struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh));
+ struct nexthop *nh = lp_allocz(p->nhpool, sizeof(struct nexthop));
nh->gw = gw;
nh->iface = iface;
- nh->next = NULL;
nh->weight = weight;
return nh;
}
/* Returns true if there are device nexthops in n */
static inline int
-has_device_nexthops(const struct mpnh *n)
+has_device_nexthops(const struct nexthop *n)
{
for (; n; n = n->next)
if (ipa_zero(n->gw))
@@ -56,13 +55,13 @@ has_device_nexthops(const struct mpnh *n)
}
/* Replace device nexthops with nexthops to gw */
-static struct mpnh *
-fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
+static struct nexthop *
+fix_device_nexthops(struct ospf_proto *p, const struct nexthop *n, ip_addr gw)
{
- struct mpnh *root1 = NULL;
- struct mpnh *root2 = NULL;
- struct mpnh **nn1 = &root1;
- struct mpnh **nn2 = &root2;
+ struct nexthop *root1 = NULL;
+ struct nexthop *root2 = NULL;
+ struct nexthop **nn1 = &root1;
+ struct nexthop **nn2 = &root2;
if (!p->ecmp)
return new_nexthop(p, gw, n->iface, n->weight);
@@ -73,7 +72,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
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))
{
@@ -87,7 +86,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 +282,7 @@ ort_merge(struct ospf_proto *p, ort *o, const orta *new)
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);
old->nhs_reuse = 1;
}
@@ -299,7 +298,7 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new)
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);
old->nhs_reuse = 1;
}
@@ -1674,18 +1673,18 @@ ospf_rt_spf(struct ospf_proto *p)
static inline int
-inherit_nexthops(struct mpnh *pn)
+inherit_nexthops(struct nexthop *pn)
{
/* Proper nexthops (with defined GW) or dummy vlink nexthops (without 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,
struct top_hash_entry *par, int pos)
{
struct ospf_proto *p = oa->po;
- struct mpnh *pn = par->nhs;
+ struct nexthop *pn = par->nhs;
struct ospf_iface *ifa;
u32 rid = en->lsa.rt;
@@ -1813,7 +1812,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
if (!link_back(oa, en, par))
return;
- struct mpnh *nhs = calc_next_hop(oa, en, par, pos);
+ struct nexthop *nhs = calc_next_hop(oa, en, par, pos);
if (!nhs)
{
log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
@@ -1851,7 +1850,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
/* Merge old and new */
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;
return;
}
@@ -1907,8 +1906,7 @@ ort_changed(ort *nf, rta *nr)
(nf->n.metric1 != nf->old_metric1) || (nf->n.metric2 != nf->old_metric2) ||
(nf->n.tag != nf->old_tag) || (nf->n.rid != nf->old_rid) ||
(nr->source != or->source) || (nr->dest != or->dest) ||
- (nr->iface != or->iface) || !ipa_equal(nr->gw, or->gw) ||
- !mpnh_same(nr->nexthops, or->nexthops);
+ !nexthop_same(&(nr->nh), &(or->nh));
}
static void
@@ -1932,7 +1930,7 @@ again1:
/* Sanity check of next-hop addresses, failure should not happen */
if (nf->n.type)
{
- struct mpnh *nh;
+ struct nexthop *nh;
for (nh = nf->n.nhs; nh; nh = nh->next)
if (ipa_nonzero(nh->gw))
{
@@ -1955,26 +1953,10 @@ again1:
.src = p->p.main_source,
.source = nf->n.type,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST
+ .dest = RTD_UNICAST,
+ .nh = *(nf->n.nhs),
};
- if (nf->n.nhs->next)
- {
- 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))
{
rta *a = rta_lookup(&a0);
diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h
index 118d09b7..589d2bc5 100644
--- a/proto/ospf/rt.h
+++ b/proto/ospf/rt.h
@@ -53,7 +53,7 @@ typedef struct orta
struct ospf_area *oa;
struct ospf_area *voa; /* Used when route is replaced in ospf_rt_sum_tr(),
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 */
}
orta;
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index aaaf2e8e..ce77f57a 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -1288,8 +1288,8 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
ip_addr fwd = IPA_NONE;
- if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface))
- fwd = a->gw;
+ if ((a->dest == RTD_UNICAST) && use_gw_for_fwaddr(p, a->nh.gw, a->nh.iface))
+ fwd = a->nh.gw;
/* NSSA-LSA with P-bit set must have non-zero forwarding address */
if (oa && ipa_zero(fwd))
diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h
index 38447fdf..d1682c54 100644
--- a/proto/ospf/topology.h
+++ b/proto/ospf/topology.h
@@ -28,7 +28,7 @@ struct top_hash_entry
u16 next_lsa_opts; /* For postponed LSA origination */
bird_clock_t inst_time; /* Time of installation into DB */
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 */
u32 lb_id; /* Interface ID of link back iface (for bcast or NBMA networks) */
u32 dist; /* Distance from the root */
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 8924c200..310f3c01 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -51,7 +51,7 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
struct rte_src *src;
rte *e;
- rta a;
+ rta *a;
if (!new && !old)
return;
@@ -65,12 +65,13 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
if (new)
{
- memcpy(&a, new->attrs, sizeof(rta));
+ a = alloca(rta_size(new->attrs));
+ memcpy(a, new->attrs, rta_size(new->attrs));
- a.aflags = 0;
- a.eattrs = attrs;
- a.hostentry = NULL;
- e = rte_get_temp(&a);
+ a->aflags = 0;
+ a->eattrs = attrs;
+ a->hostentry = NULL;
+ e = rte_get_temp(a);
e->pflags = 0;
/* Copy protocol specific embedded attributes. */
@@ -78,7 +79,7 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
e->pref = new->pref;
e->pflags = new->pflags;
- src = a.src;
+ src = a->src;
}
else
{
diff --git a/proto/rip/rip.c b/proto/rip/rip.c
index d87a078c..157093aa 100644
--- a/proto/rip/rip.c
+++ b/proto/rip/rip.c
@@ -147,21 +147,16 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
.src = p->p.main_source,
.source = RTS_RIP,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST
+ .dest = RTD_UNICAST,
};
u8 rt_metric = rt->metric;
u16 rt_tag = rt->tag;
- struct rip_rte *rt2 = rt->next;
- /* Find second valid rte */
- while (rt2 && !rip_valid_rte(rt2))
- rt2 = rt2->next;
-
- if (p->ecmp && rt2)
+ if (p->ecmp)
{
/* ECMP route */
- struct mpnh *nhs = NULL;
+ struct nexthop *nhs = NULL;
int num = 0;
for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next)
@@ -169,33 +164,33 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
if (!rip_valid_rte(rt))
continue;
- struct mpnh *nh = alloca(sizeof(struct mpnh));
+ struct nexthop *nh = allocz(sizeof(struct nexthop));
+
nh->gw = rt->next_hop;
nh->iface = rt->from->nbr->iface;
nh->weight = rt->from->ifa->cf->ecmp_weight;
- mpnh_insert(&nhs, nh);
+
+ nexthop_insert(&nhs, nh);
num++;
if (rt->tag != rt_tag)
rt_tag = 0;
}
- a0.dest = RTD_MULTIPATH;
- a0.nexthops = nhs;
+ a0.nh = *nhs;
}
else
{
/* Unipath route */
- a0.dest = RTD_ROUTER;
- a0.gw = rt->next_hop;
- a0.iface = rt->from->nbr->iface;
a0.from = rt->from->nbr->addr;
+ a0.nh.gw = rt->next_hop;
+ a0.nh.iface = rt->from->nbr->iface;
}
rta *a = rta_lookup(&a0);
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.tag = rt_tag;
@@ -345,8 +340,8 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
en->metric = rt_metric;
en->tag = rt_tag;
en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
- en->iface = new->attrs->iface;
- en->next_hop = new->attrs->gw;
+ en->iface = new->attrs->nh.iface;
+ en->next_hop = new->attrs->nh.gw;
}
else
{
diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c
index 0d4b1fd3..5459d9c3 100644
--- a/proto/rpki/rpki.c
+++ b/proto/rpki/rpki.c
@@ -124,8 +124,7 @@ rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_
.src = p->p.main_source,
.source = RTS_RPKI,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST,
- .dest = RTD_BLACKHOLE,
+ .dest = RTD_NONE,
};
rta *a = rta_lookup(&a0);
diff --git a/proto/static/config.Y b/proto/static/config.Y
index 86359f0b..16c276ce 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -13,25 +13,41 @@ CF_HDR
CF_DEFINES
#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, *this_snh;
static struct f_inst **this_srt_last_cmd;
-static void
-static_route_finish(void)
+static struct static_route *
+static_nexthop_new(void)
{
- struct static_route *r;
+ struct static_route *nh;
+
+ if (!this_snh)
+ {
+ /* First next hop */
+ nh = this_srt;
+ rem_node(&this_srt->n);
+ }
+ else
+ {
+ /* Additional next hop */
+ nh = cfg_allocz(sizeof(struct static_route));
+ nh->net = this_srt->net;
+ this_snh->mp_next = nh;
+ }
+
+ nh->dest = RTD_UNICAST;
+ nh->mp_head = this_srt;
+ return nh;
+};
- /* 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)
- if (r->use_bfd < 0)
- r->use_bfd = this_srt->use_bfd;
-}
+static void
+static_route_finish(void)
+{ }
CF_DECLS
CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
-CF_KEYWORDS(MULTIPATH, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD)
+CF_KEYWORDS(WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
CF_GRAMMAR
@@ -53,58 +69,59 @@ static_proto:
| static_proto stat_route stat_route_opt_list ';' { static_route_finish(); }
;
+stat_nexthop:
+ VIA ipa ipa_scope {
+ this_snh = static_nexthop_new();
+ this_snh->via = $2;
+ this_snh->iface = $3;
+ add_tail(&STATIC_CFG->neigh_routes, &this_snh->n);
+ }
+ | VIA TEXT {
+ this_snh = static_nexthop_new();
+ this_snh->via = IPA_NONE;
+ this_snh->if_name = $2;
+ add_tail(&STATIC_CFG->iface_routes, &this_snh->n);
+ }
+ | stat_nexthop MPLS label_stack {
+ this_snh->label_count = $3[0];
+ this_snh->label_stack = &($3[1]);
+ }
+ | stat_nexthop WEIGHT expr {
+ this_snh->weight = $3 - 1;
+ if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
+ }
+ | stat_nexthop BFD bool {
+ this_snh->use_bfd = $3; cf_check_bfd($3);
+ }
+;
+
+stat_nexthops:
+ stat_nexthop
+ | stat_nexthops stat_nexthop
+;
+
stat_route0: ROUTE net_any {
this_srt = cfg_allocz(sizeof(struct static_route));
add_tail(&STATIC_CFG->other_routes, &this_srt->n);
this_srt->net = $2;
this_srt_last_cmd = &(this_srt->cmds);
+ this_srt->mp_next = NULL;
+ this_snh = NULL;
}
;
-stat_multipath1:
- VIA ipa ipa_scope {
- last_srt_nh = this_srt_nh;
- this_srt_nh = cfg_allocz(sizeof(struct static_route));
- this_srt_nh->dest = RTD_NONE;
- this_srt_nh->via = $2;
- this_srt_nh->via_if = $3;
- this_srt_nh->if_name = (void *) this_srt; /* really */
- this_srt_nh->use_bfd = -1; /* undefined */
- }
- | stat_multipath1 WEIGHT expr {
- this_srt_nh->weight = $3 - 1;
- if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256");
- }
- | stat_multipath1 BFD bool {
- this_srt_nh->use_bfd = $3; cf_check_bfd($3);
- }
- ;
-
-stat_multipath:
- stat_multipath1 { this_srt->mp_next = this_srt_nh; }
- | stat_multipath stat_multipath1 { last_srt_nh->mp_next = this_srt_nh; }
- ;
-
stat_route:
- stat_route0 VIA ipa ipa_scope {
- this_srt->dest = RTD_ROUTER;
+ stat_route0 stat_nexthops
+ | stat_route0 RECURSIVE ipa {
+ this_srt->dest = RTDX_RECURSIVE;
this_srt->via = $3;
- this_srt->via_if = $4;
- }
- | stat_route0 VIA TEXT {
- this_srt->dest = RTD_DEVICE;
- this_srt->if_name = $3;
- rem_node(&this_srt->n);
- add_tail(&STATIC_CFG->iface_routes, &this_srt->n);
}
- | stat_route0 MULTIPATH stat_multipath {
- this_srt->dest = RTD_MULTIPATH;
- }
- | stat_route0 RECURSIVE ipa {
+ | stat_route0 RECURSIVE ipa MPLS label_stack {
this_srt->dest = RTDX_RECURSIVE;
this_srt->via = $3;
+ this_srt->label_count = $5[0];
+ this_srt->label_stack = &($5[1]);
}
-
| stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; }
| stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; }
| stat_route0 BLACKHOLE { this_srt->dest = RTD_BLACKHOLE; }
@@ -114,7 +131,6 @@ stat_route:
stat_route_item:
cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); }
- | BFD bool ';' { this_srt->use_bfd = $2; cf_check_bfd($2); }
;
stat_route_opts:
diff --git a/proto/static/static.c b/proto/static/static.c
index fb547537..e5251bf6 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -58,81 +58,88 @@ p_igp_table(struct proto *p)
}
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 *ap = allocz(RTA_MAX_SIZE);
rte *e;
- if (r->installed > 0)
- return;
+ if (!(r->state & STS_WANT) && (r->state & (STS_INSTALLED | STS_FORCE)) && r->dest != RTD_UNICAST)
+ goto drop;
DBG("Installing static route %N, rtd=%d\n", r->net, r->dest);
- bzero(&a, sizeof(a));
- a.src = p->main_source;
- a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC;
- a.scope = SCOPE_UNIVERSE;
- a.cast = RTC_UNICAST;
- a.dest = r->dest;
- a.gw = r->via;
- a.iface = ifa;
-
- if (r->dest == RTD_MULTIPATH)
+ ap->src = p->main_source;
+ ap->source = RTS_STATIC;
+ ap->scope = SCOPE_UNIVERSE;
+ ap->dest = r->dest;
+
+ if (r->dest == RTD_UNICAST)
{
+ struct nexthop *nhs = NULL;
struct static_route *r2;
- struct mpnh *nhs = NULL;
+ int update = 0;
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- if (r2->installed)
- {
- struct mpnh *nh = alloca(sizeof(struct mpnh));
- nh->gw = r2->via;
- nh->iface = r2->neigh->iface;
- nh->weight = r2->weight;
- mpnh_insert(&nhs, nh);
- }
+ r = r->mp_head;
+ for (r2 = r; r2; r2 = r2->mp_next)
+ {
+ if ((r2->state & STS_FORCE) ||
+ (!!(r2->state & STS_INSTALLED) != !!(r2->state & STS_WANT)))
+ update++;
- /* There is at least one nexthop */
- if (!nhs->next)
+ if (r2->state & STS_WANT)
{
- /* Fallback to unipath route for exactly one nexthop */
- a.dest = RTD_ROUTER;
- a.gw = nhs->gw;
- a.iface = nhs->iface;
+ struct nexthop *nh = allocz(NEXTHOP_MAX_SIZE);
+
+ nh->gw = r2->via;
+ nh->iface = r2->neigh ? r2->neigh->iface : r2->iface;
+ nh->weight = r2->weight;
+ nh->labels = r2->label_count;
+ memcpy(nh->label, r2->label_stack, r2->label_count * sizeof(u32));
+
+ r2->state |= STS_INSTALLED;
+ nexthop_insert(&nhs, nh);
}
- else
- a.nexthops = nhs;
+ else
+ r2->state = 0;
+ }
+
+ if (!update) // Nothing changed
+ return;
+
+ if (!nhs) // No nexthop to install
+ {
+drop:
+ rte_update(p, r->net, NULL);
+ return;
+ }
+
+ ap->dest = RTD_UNICAST;
+ nexthop_link(ap, nhs);
}
+ else
+ r->state |= STS_INSTALLED;
if (r->dest == RTDX_RECURSIVE)
- rta_set_recursive_next_hop(p->main_channel->table, &a, p_igp_table(p), r->via, IPA_NONE);
+ {
+ ap->nh.labels_append = ap->nh.labels = r->label_count;
+ memcpy(ap->nh.label, r->label_stack, r->label_count * sizeof(u32));
+ rta_set_recursive_next_hop(p->main_channel->table, ap, p_igp_table(p), r->via, IPA_NONE);
+ }
/* We skip rta_lookup() here */
- e = rte_get_temp(&a);
+ e = rte_get_temp(ap);
e->pflags = 0;
if (r->cmds)
f_eval_rte(r->cmds, &e, static_lp);
rte_update(p, r->net, e);
- r->installed = 1;
if (r->cmds)
lp_flush(static_lp);
}
static void
-static_remove(struct proto *p, struct static_route *r)
-{
- if (!r->installed)
- return;
-
- DBG("Removing static route %N via %I\n", r->net, r->via);
- rte_update(p, r->net, NULL);
- r->installed = 0;
-}
-
-static void
static_bfd_notify(struct bfd_request *req);
static void
@@ -161,6 +168,8 @@ static_decide(struct static_config *cf, struct static_route *r)
/* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
the route also have to be valid (r->neigh != NULL) */
+ r->state &= ~STS_WANT;
+
if (r->neigh->scope < 0)
return 0;
@@ -170,6 +179,7 @@ static_decide(struct static_config *cf, struct static_route *r)
if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
return 0;
+ r->state |= STS_WANT;
return 1;
}
@@ -177,43 +187,23 @@ static_decide(struct static_config *cf, struct static_route *r)
static void
static_add(struct proto *p, struct static_config *cf, struct static_route *r)
{
+ if (r->mp_head && r != r->mp_head)
+ return;
+
DBG("static_add(%N,%d)\n", r->net, r->dest);
switch (r->dest)
{
- case RTD_ROUTER:
- {
- 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:
+ case RTD_UNICAST:
{
int count = 0;
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);
+ if (ipa_zero(r2->via)) // No struct neighbor for device routes
+ continue;
+
+ struct neighbor *n = neigh_find2(p, &r2->via, r2->iface, NEF_STICKY);
if (n)
{
r2->chain = n->data;
@@ -221,46 +211,41 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
r2->neigh = n;
static_update_bfd(p, r2);
- r2->installed = static_decide(cf, r2);
- count += r2->installed;
+ static_decide(cf,r2);
+ count++;
}
else
{
log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
- r2->installed = 0;
+ r2->state = 0;
}
}
if (count)
- static_install(p, r, NULL);
- else
- static_remove(p, r);
+ static_install(p, r);
+
break;
}
default:
- static_install(p, r, NULL);
+ static_install(p, r);
}
}
static void
static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
{
- struct static_route *r2;
-
- if (r->bfd_req)
- {
- rfree(r->bfd_req);
- r->bfd_req = NULL;
- }
+ if (r->mp_head && (r != r->mp_head))
+ return;
- if (r->dest == RTD_MULTIPATH)
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- if (r2->bfd_req)
- {
- rfree(r2->bfd_req);
- r2->bfd_req = NULL;
- }
+ struct static_route *r2;
+
+ for (r2 = r; r2; r2 = r2->mp_next)
+ if (r2->bfd_req)
+ {
+ rfree(r2->bfd_req);
+ r2->bfd_req = NULL;
+ }
}
static int
@@ -280,8 +265,15 @@ static_start(struct proto *p)
/* We have to go UP before routes could be installed */
proto_notify_state(p, PS_UP);
- WALK_LIST(r, cf->other_routes)
+ WALK_LIST(r, cf->neigh_routes)
+ static_add(p, cf, r);
+
+ WALK_LIST(r, cf->iface_routes)
static_add(p, cf, r);
+
+ WALK_LIST(r, cf->other_routes)
+ static_install(p, r);
+
return PS_UP;
}
@@ -292,12 +284,17 @@ static_shutdown(struct proto *p)
struct static_route *r;
/* Just reset the flag, the routes will be flushed by the nest */
- WALK_LIST(r, cf->iface_routes)
- r->installed = 0;
WALK_LIST(r, cf->other_routes)
{
static_rte_cleanup(p, r);
- r->installed = 0;
+ r->state = 0;
+ }
+ WALK_LIST(r, cf->iface_routes)
+ r->state = 0;
+ WALK_LIST(r, cf->neigh_routes)
+ {
+ static_rte_cleanup(p, r);
+ r->state = 0;
}
/* Handle failure during channel reconfigure */
@@ -305,10 +302,12 @@ static_shutdown(struct proto *p)
cf = (void *) p->cf_new;
if (cf)
{
- WALK_LIST(r, cf->iface_routes)
- r->installed = 0;
WALK_LIST(r, cf->other_routes)
- r->installed = 0;
+ r->state = 0;
+ WALK_LIST(r, cf->iface_routes)
+ r->state = 0;
+ WALK_LIST(r, cf->neigh_routes)
+ r->state = 0;
}
return PS_DOWN;
@@ -326,40 +325,11 @@ static_cleanup(struct proto *p)
static void
static_update_rte(struct proto *p, struct static_route *r)
{
- switch (r->dest)
- {
- case RTD_ROUTER:
- if (static_decide((struct static_config *) p->cf, r))
- static_install(p, r, r->neigh->iface);
- else
- 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);
+ if (r->dest != RTD_UNICAST)
+ return;
- break;
- }
- }
+ static_decide((struct static_config *) p->cf, r);
+ static_install(p, r);
}
static void
@@ -391,18 +361,13 @@ static void
static_dump_rt(struct static_route *r)
{
debug("%-1N: ", r->net);
- switch (r->dest)
- {
- case RTD_ROUTER:
- debug("via %I\n", r->via);
- break;
- case RTD_DEVICE:
+ if (r->dest == RTD_UNICAST)
+ if (ipa_zero(r->via))
debug("dev %s\n", r->if_name);
- break;
- default:
- debug("rtd %d\n", r->dest);
- break;
- }
+ else
+ debug("via %I\n", r->via);
+ else
+ debug("rtd %d\n", r->dest);
}
static void
@@ -411,12 +376,15 @@ static_dump(struct proto *p)
struct static_config *c = (void *) p->cf;
struct static_route *r;
- debug("Independent static routes:\n");
- WALK_LIST(r, c->other_routes)
+ debug("Independent static nexthops:\n");
+ WALK_LIST(r, c->neigh_routes)
static_dump_rt(r);
- debug("Device static routes:\n");
+ debug("Device static nexthops:\n");
WALK_LIST(r, c->iface_routes)
static_dump_rt(r);
+ debug("Other static routes:\n");
+ WALK_LIST(r, c->other_routes)
+ static_dump_rt(r);
}
static void
@@ -429,13 +397,21 @@ static_if_notify(struct proto *p, unsigned flags, struct iface *i)
{
WALK_LIST(r, c->iface_routes)
if (!strcmp(r->if_name, i->name))
- static_install(p, r, i);
+ {
+ r->state |= STS_WANT;
+ r->iface = i;
+ static_install(p, r);
+ }
}
else if (flags & IF_CHANGE_DOWN)
{
WALK_LIST(r, c->iface_routes)
if (!strcmp(r->if_name, i->name))
- static_remove(p, r);
+ {
+ r->state &= ~STS_WANT;
+ r->iface = NULL;
+ static_install(p, r);
+ }
}
}
@@ -448,6 +424,7 @@ static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
void
static_init_config(struct static_config *c)
{
+ init_list(&c->neigh_routes);
init_list(&c->iface_routes);
init_list(&c->other_routes);
}
@@ -462,8 +439,12 @@ static_postconfig(struct proto_config *CF)
cf_error("Channel not specified");
+ WALK_LIST(r, cf->neigh_routes)
+ if (r->net && (r->net->type != CF->net_type))
+ cf_error("Route %N incompatible with channel type", r->net);
+
WALK_LIST(r, cf->iface_routes)
- if (r->net->type != CF->net_type)
+ if (r->net && (r->net->type != CF->net_type))
cf_error("Route %N incompatible with channel type", r->net);
WALK_LIST(r, cf->other_routes)
@@ -496,22 +477,33 @@ static_same_dest(struct static_route *x, struct static_route *y)
switch (x->dest)
{
- case RTD_ROUTER:
- return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
-
- case RTD_DEVICE:
- return !strcmp(x->if_name, y->if_name);
-
- 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) ||
- (x->via_if != y->via_if) ||
- (x->use_bfd != y->use_bfd) ||
- (x->weight != y->weight))
- return 0;
- return !x && !y;
+ case RTD_UNICAST:
+ {
+ struct static_route *xc, *yc;
+ for (xc = x, yc = y; xc && yc; xc = xc->mp_next, yc = yc->mp_next)
+ {
+ if (ipa_nonzero(xc->via) && ipa_nonzero(yc->via))
+ {
+ if (!ipa_equal(x->via, y->via) ||
+ (x->iface != y->iface) ||
+ (x->use_bfd != y->use_bfd) ||
+ (x->weight != y->weight) ||
+ (x->label_count != y->label_count))
+ return 0;
+ for (int i=0; i<x->label_count; i++)
+ if (x->label_stack[i] != y->label_stack[i])
+ return 0;
+ }
+ else
+ if ((!x->if_name) || (!y->if_name) ||
+ strcmp(x->if_name, y->if_name) ||
+ (x->use_bfd != y->use_bfd) ||
+ (x->weight != y->weight))
+ return 0;
+
+ }
+ return 1;
+ }
case RTDX_RECURSIVE:
return ipa_equal(x->via, y->via);
@@ -533,6 +525,9 @@ static_match(struct proto *p, struct static_route *r, struct static_config *n)
{
struct static_route *t;
+ if (r->mp_head && (r->mp_head != r))
+ return;
+
/*
* For given old route *r we find whether a route to the same
* network is also in the new route list. In that case, we keep the
@@ -543,23 +538,28 @@ static_match(struct proto *p, struct static_route *r, struct static_config *n)
if (r->neigh)
r->neigh->data = NULL;
+ WALK_LIST(t, n->neigh_routes)
+ if ((!t->mp_head || (t->mp_head == t)) && net_equal(r->net, t->net))
+ goto found;
+
WALK_LIST(t, n->iface_routes)
- if (net_equal(r->net, t->net))
+ if ((!t->mp_head || (t->mp_head == t)) && net_equal(r->net, t->net))
goto found;
WALK_LIST(t, n->other_routes)
if (net_equal(r->net, t->net))
goto found;
- static_remove(p, r);
+ r->state &= ~STS_WANT;
+ static_install(p, r);
return;
found:
+ t->state = r->state;
+
/* If destination is different, force reinstall */
- if ((r->installed > 0) && !static_same_rte(r, t))
- t->installed = -1;
- else
- t->installed = r->installed;
+ if (!static_same_rte(r, t))
+ t->state |= STS_FORCE;
}
static inline rtable *
@@ -582,20 +582,34 @@ static_reconfigure(struct proto *p, struct proto_config *CF)
return 0;
/* Delete all obsolete routes and reset neighbor entries */
+ WALK_LIST(r, o->other_routes)
+ static_match(p, r, n);
WALK_LIST(r, o->iface_routes)
static_match(p, r, n);
- WALK_LIST(r, o->other_routes)
+ WALK_LIST(r, o->neigh_routes)
static_match(p, r, n);
/* Now add all new routes, those not changed will be ignored by static_install() */
+ WALK_LIST(r, n->neigh_routes)
+ static_add(p, n, r);
+ WALK_LIST(r, o->neigh_routes)
+ static_rte_cleanup(p, r);
+
WALK_LIST(r, n->iface_routes)
{
struct iface *ifa;
if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
- static_install(p, r, ifa);
+ {
+ r->iface = ifa;
+ static_install(p, r);
+ }
}
+
WALK_LIST(r, n->other_routes)
- static_add(p, n, r);
+ {
+ r->state |= STS_WANT;
+ static_install(p, r);
+ }
WALK_LIST(r, o->other_routes)
static_rte_cleanup(p, r);
@@ -606,37 +620,24 @@ static_reconfigure(struct proto *p, struct proto_config *CF)
static void
static_copy_routes(list *dlst, list *slst)
{
- struct static_route *dr, *sr;
+ struct static_route *sr;
init_list(dlst);
WALK_LIST(sr, *slst)
{
- /* copy one route */
- dr = cfg_alloc(sizeof(struct static_route));
- memcpy(dr, sr, sizeof(struct static_route));
-
- /* 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;
- }
+ struct static_route *srr, *drr = NULL;
+ for (srr = sr->mp_head; srr; srr = srr->mp_next)
+ {
+ /* copy one route */
+ struct static_route *dr = cfg_alloc(sizeof(struct static_route));
+ if (drr)
+ drr->mp_next = dr;
+ else
+ add_tail(dlst, &(dr->n));
- add_tail(dlst, (node *) dr);
+ memcpy(dr, sr, sizeof(struct static_route));
+ drr = dr;
+ }
}
}
@@ -647,6 +648,7 @@ static_copy_config(struct proto_config *dest, struct proto_config *src)
struct static_config *s = (struct static_config *) src;
/* Copy route lists */
+ static_copy_routes(&d->neigh_routes, &s->neigh_routes);
static_copy_routes(&d->iface_routes, &s->iface_routes);
static_copy_routes(&d->other_routes, &s->other_routes);
}
@@ -668,30 +670,45 @@ struct protocol proto_static = {
.copy_config = static_copy_config
};
-static void
-static_show_rt(struct static_route *r)
+static byte *
+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)
{
- case RTD_ROUTER: bsprintf(via, "via %I%J", r->via, r->via_if); break;
- case RTD_DEVICE: bsprintf(via, "dev %s", r->if_name); break;
+ case RTD_UNICAST: if (ipa_zero(r->via)) bsprintf(via, "dev %s", r->if_name);
+ else bsprintf(via, "via %I%J", r->via, r->iface);
+ break;
case RTD_BLACKHOLE: bsprintf(via, "blackhole"); break;
case RTD_UNREACHABLE: bsprintf(via, "unreachable"); 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;
default: bsprintf(via, "???");
}
- cli_msg(-1009, "%N %s%s%s", r->net, via,
- r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
+ return via;
+}
- struct static_route *r2;
- if (r->dest == RTD_MULTIPATH)
- for (r2 = r->mp_next; r2; r2 = r2->mp_next)
- cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->weight + 1,
- r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
+static void
+static_show_rt(struct static_route *r)
+{
+ if (r->mp_head && (r != r->mp_head))
+ return;
+ if (r->mp_next)
+ {
+ cli_msg(-1009, "%N", r->net);
+ struct static_route *r2;
+ for (r2 = r; r2; r2 = r2->mp_next)
+ {
+ cli_msg(-1009, "\t%s weight %d%s%s", static_format_via(r2), r2->weight + 1,
+ r2->bfd_req ? " (bfd)" : "", (r2->state & STS_INSTALLED) ? "" : " (dormant)");
+ if (r2->mp_next == r)
+ break;
+ }
+ }
+ else
+ cli_msg(-1009, "%N %s%s%s", r->net, static_format_via(r),
+ r->bfd_req ? " (bfd)" : "", (r->state & STS_INSTALLED) ? "" : " (dormant)");
}
void
@@ -700,9 +717,11 @@ static_show(struct proto *P)
struct static_config *c = (void *) P->cf;
struct static_route *r;
- WALK_LIST(r, c->other_routes)
+ WALK_LIST(r, c->neigh_routes)
static_show_rt(r);
WALK_LIST(r, c->iface_routes)
static_show_rt(r);
+ WALK_LIST(r, c->other_routes)
+ static_show_rt(r);
cli_msg(0, "");
}
diff --git a/proto/static/static.h b/proto/static/static.h
index 51486e83..aeb9660a 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -15,7 +15,8 @@
struct static_config {
struct proto_config c;
list iface_routes; /* Routes to search on interface events */
- list other_routes; /* Routes hooked to neighbor cache and reject routes */
+ list neigh_routes; /* Routes to search on neighbor events */
+ list other_routes; /* Non-nexthop routes */
int check_link; /* Whether iface link state is used */
struct rtable_config *igp_table; /* Table used for recursive next hop lookups */
};
@@ -29,17 +30,24 @@ struct static_route {
net_addr *net; /* Network we route */
int dest; /* Destination type (RTD_*) */
ip_addr via; /* Destination router */
- struct iface *via_if; /* Destination iface, for link-local vias */
+ struct iface *iface; /* Destination iface, for link-local vias or device routes */
struct neighbor *neigh;
- byte *if_name; /* Name for RTD_DEVICE routes */
- struct static_route *mp_next; /* Nexthops for RTD_MULTIPATH routes */
+ byte *if_name; /* Name for device routes */
+ struct static_route *mp_next; /* Nexthops for multipath routes */
+ struct static_route *mp_head; /* First nexthop of this route */
struct f_inst *cmds; /* List of commands for setting attributes */
- int installed; /* Installed in rt table, -1 for reinstall */
- int use_bfd; /* Configured to use BFD */
+ u32 state; /* Current state: STS_* */
int weight; /* Multipath next hop weight */
+ byte use_bfd; /* Configured to use BFD */
+ byte label_count; /* Number of labels in stack */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
+ u32 *label_stack; /* Label stack if label_count > 0 */
};
+#define STS_INSTALLED 0x1
+#define STS_WANT 0x2
+#define STS_FORCE 0x4
+
/* Dummy nodes (parts of multipath route) abuses masklen field for weight
and if_name field for a ptr to the master (RTD_MULTIPATH) node. */
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index d2372a3d..c65cba65 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -147,9 +147,7 @@ krt_capable(rte *e)
rta *a = e->attrs;
return
- a->cast == RTC_UNICAST &&
- (a->dest == RTD_ROUTER
- || a->dest == RTD_DEVICE
+ ((a->dest == RTD_UNICAST && !a->nh.next) /* No multipath support */
#ifdef RTF_REJECT
|| a->dest == RTD_UNREACHABLE
#endif
@@ -190,12 +188,11 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
net *net = e->net;
rta *a = e->attrs;
static int msg_seq;
- struct iface *j, *i = a->iface;
+ struct iface *j, *i = a->nh.iface;
int l;
struct ks_msg msg;
char *body = (char *)msg.buf;
sockaddr gate, mask, dst;
- ip_addr gw;
DBG("krt-sock: send %I/%d via %I\n", net->n.prefix, net->n.pxlen, a->gw);
@@ -225,14 +222,12 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
msg.rtm.rtm_flags |= RTF_BLACKHOLE;
#endif
- /* This is really very nasty, but I'm not able
- * to add "(reject|blackhole)" route without
- * gateway set
+ /*
+ * This is really very nasty, but I'm not able to add reject/blackhole route
+ * without gateway address.
*/
- if(!i)
+ if (!i)
{
- i = HEAD(iface_list);
-
WALK_LIST(j, iface_list)
{
if (j->flags & IF_LOOPBACK)
@@ -241,13 +236,13 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
break;
}
}
- }
-
- gw = a->gw;
- /* Embed interface ID to link-local address */
- if (ipa_is_link_local(gw))
- _I0(gw) = 0xfe800000 | (i->index & 0x0000ffff);
+ if (!i)
+ {
+ log(L_ERR "KRT: Cannot find loopback iface");
+ return -1;
+ }
+ }
int af = AF_UNSPEC;
@@ -263,43 +258,51 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
return -1;
}
-
sockaddr_fill(&dst, af, net_prefix(net->n.addr), NULL, 0);
sockaddr_fill(&mask, af, net_pxmask(net->n.addr), NULL, 0);
- sockaddr_fill(&gate, af, gw, NULL, 0);
switch (a->dest)
{
- case RTD_ROUTER:
+ case RTD_UNICAST:
+ if (ipa_nonzero(a->nh.gw))
+ {
+ ip_addr gw = a->nh.gw;
+
+ /* Embed interface ID to link-local address */
+ if (ipa_is_link_local(gw))
+ _I0(gw) = 0xfe800000 | (i->index & 0x0000ffff);
+
+ sockaddr_fill(&gate, af, gw, NULL, 0);
msg.rtm.rtm_flags |= RTF_GATEWAY;
msg.rtm.rtm_addrs |= RTA_GATEWAY;
break;
+ }
#ifdef RTF_REJECT
- case RTD_UNREACHABLE:
+ case RTD_UNREACHABLE:
#endif
#ifdef RTF_BLACKHOLE
- case RTD_BLACKHOLE:
+ case RTD_BLACKHOLE:
#endif
- case RTD_DEVICE:
- if(i)
- {
+ {
+ /* Fallback for all other valid cases */
+ if (!i->addr)
+ {
+ log(L_ERR "KRT: interface %s has no IP addess", i->name);
+ return -1;
+ }
+
#ifdef RTF_CLONING
- if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS) /* PTP */
- msg.rtm.rtm_flags |= RTF_CLONING;
+ if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS) /* PTP */
+ msg.rtm.rtm_flags |= RTF_CLONING;
#endif
- if(!i->addr) {
- log(L_ERR "KRT: interface %s has no IP addess", i->name);
- return -1;
- }
+ sockaddr_fill(&gate, ipa_is_ip4(i->addr->ip) ? AF_INET : AF_INET6, i->addr->ip, NULL, 0);
+ msg.rtm.rtm_addrs |= RTA_GATEWAY;
+ }
- sockaddr_fill(&gate, ipa_is_ip4(i->addr->ip) ? AF_INET : AF_INET6, i->addr->ip, NULL, 0);
- msg.rtm.rtm_addrs |= RTA_GATEWAY;
- }
- break;
- default:
- bug("krt-sock: unknown flags, but not filtered");
+ default:
+ bug("krt-sock: unknown flags, but not filtered");
}
msg.rtm.rtm_index = i->index;
@@ -469,7 +472,6 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
.src = p->p.main_source,
.source = RTS_INHERIT,
.scope = SCOPE_UNIVERSE,
- .cast = RTC_UNICAST
};
/* reject/blackhole routes have also set RTF_GATEWAY,
@@ -489,39 +491,37 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
}
#endif
- a.iface = if_find_by_index(msg->rtm.rtm_index);
- if (!a.iface)
+ a.nh.iface = if_find_by_index(msg->rtm.rtm_index);
+ if (!a.nh.iface)
{
log(L_ERR "KRT: Received route %N with unknown ifindex %u",
net->n.addr, msg->rtm.rtm_index);
return;
}
+ a.dest = RTD_UNICAST;
if (flags & RTF_GATEWAY)
{
neighbor *ng;
- a.dest = RTD_ROUTER;
- a.gw = igate;
+ a.nh.gw = igate;
/* Clean up embedded interface ID returned in link-local address */
- if (ipa_is_link_local(a.gw))
- _I0(a.gw) = 0xfe800000;
+ if (ipa_is_link_local(a.nh.gw))
+ _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))
{
/* Ignore routes with next-hop 127.0.0.1, host routes with such
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;
log(L_ERR "KRT: Received route %N with strange next-hop %I",
- net->n.addr, a.gw);
+ net->n.addr, a.nh.gw);
return;
}
}
- else
- a.dest = RTD_DEVICE;
done:
e = rte_get_temp(&a);
diff --git a/sysdep/cf/linux.h b/sysdep/cf/linux.h
index cec9499c..3a3a15da 100644
--- a/sysdep/cf/linux.h
+++ b/sysdep/cf/linux.h
@@ -21,6 +21,10 @@
#define CONFIG_INCLUDE_SYSPRIV_H "sysdep/linux/syspriv.h"
+#ifndef AF_MPLS
+#define AF_MPLS 28
+#endif
+
/*
Link: sysdep/linux
Link: sysdep/unix
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 554f2c97..bb85a38b 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -20,6 +20,7 @@
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
+#include "lib/alloca.h"
#include "sysdep/unix/timer.h"
#include "sysdep/unix/unix.h"
#include "sysdep/unix/krt.h"
@@ -30,6 +31,7 @@
#include <asm/types.h>
#include <linux/if.h>
+#include <linux/lwtunnel.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
@@ -50,6 +52,21 @@
#define RTA_TABLE 15
#endif
+#ifndef RTA_VIA
+#define RTA_VIA 18
+#endif
+
+#ifndef RTA_NEWDST
+#define RTA_NEWDST 19
+#endif
+
+#ifndef RTA_ENCAP_TYPE
+#define RTA_ENCAP_TYPE 21
+#endif
+
+#ifndef RTA_ENCAP
+#define RTA_ENCAP 22
+#endif
#define krt_ecmp6(p) ((p)->af == AF_INET6)
@@ -303,7 +320,7 @@ static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = {
[IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) },
[IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) },
[IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) },
- [IFA_FLAGS] = { 1, 1, sizeof(u32) },
+ [IFA_FLAGS] = { 1, 1, sizeof(u32) },
};
static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
@@ -313,10 +330,16 @@ static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
};
-#define BIRD_RTA_MAX (RTA_TABLE+1)
+#define BIRD_RTA_MAX (RTA_ENCAP+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_ENCAP_TYPE]= { 1, 1, sizeof(u16) },
+ [RTA_ENCAP] = { 1, 0, 0 },
+};
+
+static struct nl_want_attrs encap_mpls_want[BIRD_RTA_MAX] = {
+ [RTA_DST] = { 1, 0, 0 },
};
static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = {
@@ -329,6 +352,8 @@ static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = {
[RTA_MULTIPATH] = { 1, 0, 0 },
[RTA_FLOW] = { 1, 1, sizeof(u32) },
[RTA_TABLE] = { 1, 1, sizeof(u32) },
+ [RTA_ENCAP_TYPE]= { 1, 1, sizeof(u16) },
+ [RTA_ENCAP] = { 1, 0, 0 },
};
static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = {
@@ -341,6 +366,20 @@ static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = {
[RTA_METRICS] = { 1, 0, 0 },
[RTA_FLOW] = { 1, 1, sizeof(u32) },
[RTA_TABLE] = { 1, 1, sizeof(u32) },
+ [RTA_ENCAP_TYPE]= { 1, 1, sizeof(u16) },
+ [RTA_ENCAP] = { 1, 0, 0 },
+};
+
+static struct nl_want_attrs rtm_attr_want_mpls[BIRD_RTA_MAX] = {
+ [RTA_DST] = { 1, 1, sizeof(u32) },
+ [RTA_IIF] = { 1, 1, sizeof(u32) },
+ [RTA_OIF] = { 1, 1, sizeof(u32) },
+ [RTA_PRIORITY] = { 1, 1, sizeof(u32) },
+ [RTA_METRICS] = { 1, 0, 0 },
+ [RTA_FLOW] = { 1, 1, sizeof(u32) },
+ [RTA_TABLE] = { 1, 1, sizeof(u32) },
+ [RTA_VIA] = { 1, 0, 0 },
+ [RTA_NEWDST] = { 1, 0, 0 },
};
@@ -373,6 +412,9 @@ nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k,
return 1;
}
+static inline u16 rta_get_u16(struct rtattr *a)
+{ return *(u16 *) RTA_DATA(a); }
+
static inline u32 rta_get_u32(struct rtattr *a)
{ return *(u32 *) RTA_DATA(a); }
@@ -390,6 +432,25 @@ static inline ip_addr rta_get_ipa(struct rtattr *a)
return ipa_from_ip6(rta_get_ip6(a));
}
+static inline ip_addr rta_get_via(struct rtattr *a)
+{
+ struct rtvia *v = RTA_DATA(a);
+ switch(v->rtvia_family) {
+ case AF_INET: return ipa_from_ip4(ip4_ntoh(*(ip4_addr *) v->rtvia_addr));
+ case AF_INET6: return ipa_from_ip6(ip6_ntoh(*(ip6_addr *) v->rtvia_addr));
+ }
+ return IPA_NONE;
+}
+
+static u32 rta_mpls_stack[MPLS_MAX_LABEL_STACK];
+static inline int rta_get_mpls(struct rtattr *a, u32 *stack)
+{
+ if (RTA_PAYLOAD(a) % 4)
+ log(L_WARN "KRT: Strange length of received MPLS stack: %u", RTA_PAYLOAD(a));
+
+ return mpls_get(RTA_DATA(a), RTA_PAYLOAD(a) & ~0x3, stack);
+}
+
struct rtattr *
nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen)
{
@@ -410,6 +471,24 @@ nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint
return a;
}
+static inline struct rtattr *
+nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code)
+{
+ return nl_add_attr(h, bufsize, code, NULL, 0);
+}
+
+static inline void
+nl_close_attr(struct nlmsghdr *h, struct rtattr *a)
+{
+ a->rta_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)a;
+}
+
+static inline void
+nl_add_attr_u16(struct nlmsghdr *h, uint bufsize, int code, u16 data)
+{
+ nl_add_attr(h, bufsize, code, &data, 2);
+}
+
static inline void
nl_add_attr_u32(struct nlmsghdr *h, uint bufsize, int code, u32 data)
{
@@ -439,16 +518,46 @@ nl_add_attr_ipa(struct nlmsghdr *h, uint bufsize, int code, ip_addr ipa)
nl_add_attr_ip6(h, bufsize, code, ipa_to_ip6(ipa));
}
-static inline struct rtattr *
-nl_open_attr(struct nlmsghdr *h, uint bufsize, uint code)
+static inline void
+nl_add_attr_mpls(struct nlmsghdr *h, uint bufsize, int code, int len, u32 *stack)
{
- return nl_add_attr(h, bufsize, code, NULL, 0);
+ char buf[len*4];
+ mpls_put(buf, len, stack);
+ nl_add_attr(h, bufsize, code, buf, len*4);
}
static inline void
-nl_close_attr(struct nlmsghdr *h, struct rtattr *a)
+nl_add_attr_mpls_encap(struct nlmsghdr *h, uint bufsize, int len, u32 *stack)
{
- a->rta_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)a;
+ nl_add_attr_u16(h, bufsize, RTA_ENCAP_TYPE, LWTUNNEL_ENCAP_MPLS);
+
+ struct rtattr *nest = nl_open_attr(h, bufsize, RTA_ENCAP);
+ nl_add_attr_mpls(h, bufsize, RTA_DST, len, stack);
+ nl_close_attr(h, nest);
+}
+
+static inline void
+nl_add_attr_via(struct nlmsghdr *h, uint bufsize, ip_addr ipa)
+{
+ struct rtattr *nest = nl_open_attr(h, bufsize, RTA_VIA);
+ struct rtvia *via = RTA_DATA(nest);
+
+ h->nlmsg_len += sizeof(*via);
+
+ if (ipa_is_ip4(ipa))
+ {
+ via->rtvia_family = AF_INET;
+ put_ip4(via->rtvia_addr, ipa_to_ip4(ipa));
+ h->nlmsg_len += sizeof(ip4_addr);
+ }
+ else
+ {
+ via->rtvia_family = AF_INET6;
+ put_ip6(via->rtvia_addr, ipa_to_ip6(ipa));
+ h->nlmsg_len += sizeof(ip6_addr);
+ }
+
+ nl_close_attr(h, nest);
}
static inline struct rtnexthop *
@@ -471,8 +580,24 @@ nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh)
nh->rtnh_len = (void *)h + NLMSG_ALIGN(h->nlmsg_len) - (void *)nh;
}
+static inline void
+nl_add_nexthop(struct nlmsghdr *h, uint bufsize, struct nexthop *nh, int af)
+{
+ if (nh->labels > 0)
+ if (af == AF_MPLS)
+ nl_add_attr_mpls(h, bufsize, RTA_NEWDST, nh->labels, nh->label);
+ else
+ nl_add_attr_mpls_encap(h, bufsize, nh->labels, nh->label);
+
+ if (ipa_nonzero(nh->gw))
+ if (af == AF_MPLS)
+ nl_add_attr_via(h, bufsize, nh->gw);
+ else
+ nl_add_attr_ipa(h, bufsize, RTA_GATEWAY, nh->gw);
+}
+
static void
-nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct mpnh *nh)
+nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct nexthop *nh, int af)
{
struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH);
@@ -484,7 +609,7 @@ nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct mpnh *nh)
rtnh->rtnh_hops = nh->weight;
rtnh->rtnh_ifindex = nh->iface->index;
- nl_add_attr_ipa(h, bufsize, RTA_GATEWAY, nh->gw);
+ nl_add_nexthop(h, bufsize, nh, af);
nl_close_nexthop(h, rtnh);
}
@@ -492,17 +617,17 @@ nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct mpnh *nh)
nl_close_attr(h, a);
}
-static struct mpnh *
+static struct nexthop *
nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
{
/* 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_used;
struct rtattr *a[BIRD_RTA_MAX];
struct rtnexthop *nh = RTA_DATA(ra);
- struct mpnh *rv, *first, **last;
+ struct nexthop *rv, *first, **last;
unsigned len = RTA_PAYLOAD(ra);
first = NULL;
@@ -518,7 +643,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
if (nh_buf_used == nh_buf_size)
{
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 * NEXTHOP_MAX_SIZE);
}
*last = rv = nh_buffer + nh_buf_used++;
rv->next = NULL;
@@ -531,7 +656,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
/* Nonexistent RTNH_PAYLOAD ?? */
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])
{
rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
@@ -543,7 +668,22 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
return NULL;
}
else
- return NULL;
+ rv->gw = IPA_NONE;
+
+ if (a[RTA_ENCAP_TYPE])
+ {
+ if (rta_get_u16(a[RTA_ENCAP_TYPE]) != LWTUNNEL_ENCAP_MPLS) {
+ log(L_WARN "KRT: Unknown encapsulation method %d in multipath", rta_get_u16(a[RTA_ENCAP_TYPE]));
+ return NULL;
+ }
+
+ struct rtattr *enca[BIRD_RTA_MAX];
+ nl_attr_len = RTA_PAYLOAD(a[RTA_ENCAP]);
+ nl_parse_attrs(RTA_DATA(a[RTA_ENCAP]), encap_mpls_want, enca, sizeof(enca));
+ rv->labels = rta_get_mpls(enca[RTA_DST], rv->label);
+ break;
+ }
+
len -= NLMSG_ALIGN(nh->rtnh_len);
nh = RTNH_NEXT(nh);
@@ -952,28 +1092,21 @@ krt_capable(rte *e)
{
rta *a = e->attrs;
- if (a->cast != RTC_UNICAST)
- return 0;
-
switch (a->dest)
- {
- case RTD_ROUTER:
- case RTD_DEVICE:
- if (a->iface == NULL)
- return 0;
+ {
+ case RTD_UNICAST:
case RTD_BLACKHOLE:
case RTD_UNREACHABLE:
case RTD_PROHIBIT:
- case RTD_MULTIPATH:
- break;
+ return 1;
+
default:
return 0;
- }
- return 1;
+ }
}
static inline int
-nh_bufsize(struct mpnh *nh)
+nh_bufsize(struct nexthop *nh)
{
int rv = 0;
for (; nh != NULL; nh = nh->next)
@@ -982,12 +1115,12 @@ nh_bufsize(struct mpnh *nh)
}
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;
net *net = e->net;
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;
struct {
@@ -1011,7 +1144,13 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d
r->r.rtm_dst_len = net_pxlen(net->n.addr);
r->r.rtm_protocol = RTPROT_BIRD;
r->r.rtm_scope = RT_SCOPE_UNIVERSE;
- nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr));
+ if (p->af == AF_MPLS)
+ {
+ u32 label = net_mpls(net->n.addr);
+ nl_add_attr_mpls(&r->h, rsize, RTA_DST, 1, &label);
+ }
+ else
+ nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr));
/*
* Strange behavior for RTM_DELROUTE:
@@ -1043,7 +1182,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))
r->r.rtm_scope = ea->u.data;
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))
nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
@@ -1068,17 +1207,17 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d
dest:
- /* a->iface != NULL checked in krt_capable() for router and device routes */
switch (dest)
{
- case RTD_ROUTER:
- r->r.rtm_type = RTN_UNICAST;
- nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index);
- nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, gw);
- break;
- case RTD_DEVICE:
+ case RTD_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_multipath(&r->h, rsize, nh, p->af);
+ else
+ {
+ nl_add_attr_u32(&r->h, rsize, RTA_OIF, nh->iface->index);
+ nl_add_nexthop(&r->h, rsize, nh, p->af);
+ }
break;
case RTD_BLACKHOLE:
r->r.rtm_type = RTN_BLACKHOLE;
@@ -1089,10 +1228,6 @@ dest:
case RTD_PROHIBIT:
r->r.rtm_type = RTN_PROHIBIT;
break;
- case RTD_MULTIPATH:
- r->r.rtm_type = RTN_UNICAST;
- nl_add_multipath(&r->h, rsize, a->nexthops);
- break;
case RTD_NONE:
break;
default:
@@ -1109,21 +1244,21 @@ nl_add_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
rta *a = e->attrs;
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)
return err;
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 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
@@ -1133,7 +1268,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 */
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);
return err;
@@ -1168,10 +1303,10 @@ krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list
}
-static inline struct mpnh *
-nl_alloc_mpnh(struct nl_parse_state *s, ip_addr gw, struct iface *iface, byte weight)
+static inline struct nexthop *
+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->iface = iface;
@@ -1280,6 +1415,19 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
net_fill_ip6(&dst, IP6_NONE, 0);
break;
+ case AF_MPLS:
+ if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want_mpls, a, sizeof(a)))
+ return;
+
+ if (a[RTA_DST])
+ if (rta_get_mpls(a[RTA_DST], rta_mpls_stack) == 1)
+ net_fill_mpls(&dst, rta_mpls_stack[0]);
+ else
+ log(L_WARN "KRT: Got multi-label MPLS RTA_DST");
+ else
+ return; /* No support for MPLS routes without RTA_DST */
+ break;
+
default:
return;
}
@@ -1342,60 +1490,58 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type))
nl_announce_route(s);
- rta *ra = lp_allocz(s->pool, sizeof(rta));
+ rta *ra = lp_allocz(s->pool, RTA_MAX_SIZE);
ra->src = p->p.main_source;
ra->source = RTS_INHERIT;
ra->scope = SCOPE_UNIVERSE;
- ra->cast = RTC_UNICAST;
switch (i->rtm_type)
{
case RTN_UNICAST:
+ ra->dest = RTD_UNICAST;
if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET))
{
- ra->dest = RTD_MULTIPATH;
- ra->nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]);
- if (!ra->nexthops)
+ struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH]);
+ if (!nh)
{
log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
return;
}
+ ra->nh = *nh;
break;
}
- ra->iface = if_find_by_index(oif);
- if (!ra->iface)
+ ra->nh.iface = if_find_by_index(oif);
+ if (!ra->nh.iface)
{
log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif);
return;
}
- if (a[RTA_GATEWAY])
+ if ((i->rtm_family != AF_MPLS) && a[RTA_GATEWAY] || (i->rtm_family == AF_MPLS) && a[RTA_VIA])
{
- ra->dest = RTD_ROUTER;
- ra->gw = rta_get_ipa(a[RTA_GATEWAY]);
+ if (i->rtm_family == AF_MPLS)
+ ra->nh.gw = rta_get_via(a[RTA_VIA]);
+ else
+ ra->nh.gw = rta_get_ipa(a[RTA_GATEWAY]);
/* Silently skip strange 6to4 routes */
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;
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);
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;
}
}
- else
- {
- ra->dest = RTD_DEVICE;
- def_scope = RT_SCOPE_LINK;
- }
break;
case RTN_BLACKHOLE:
@@ -1413,6 +1559,44 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
return;
}
+ int labels = 0;
+ if ((i->rtm_family == AF_MPLS) && a[RTA_NEWDST] && !ra->nh.next)
+ labels = rta_get_mpls(a[RTA_NEWDST], ra->nh.label);
+
+ if (a[RTA_ENCAP] && a[RTA_ENCAP_TYPE] && !ra->nh.next)
+ {
+ switch (rta_get_u16(a[RTA_ENCAP_TYPE]))
+ {
+ case LWTUNNEL_ENCAP_MPLS:
+ {
+ struct rtattr *enca[BIRD_RTA_MAX];
+ nl_attr_len = RTA_PAYLOAD(a[RTA_ENCAP]);
+ nl_parse_attrs(RTA_DATA(a[RTA_ENCAP]), encap_mpls_want, enca, sizeof(enca));
+ labels = rta_get_mpls(enca[RTA_DST], ra->nh.label);
+ break;
+ }
+ default:
+ SKIP("unknown encapsulation method %d\n", rta_get_u16(a[RTA_ENCAP_TYPE]));
+ break;
+ }
+ }
+
+ if (labels < 0)
+ {
+ log(L_WARN "KRT: Too long MPLS stack received, ignoring.");
+ ra->nh.labels = 0;
+ }
+ else
+ ra->nh.labels = labels;
+
+ rte *e = rte_get_temp(ra);
+ e->net = net;
+ e->u.krt.src = src;
+ e->u.krt.proto = i->rtm_protocol;
+ e->u.krt.seen = 0;
+ e->u.krt.best = 0;
+ e->u.krt.metric = 0;
+
if (i->rtm_scope != def_scope)
{
ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
@@ -1426,6 +1610,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
ea->attrs[0].u.data = i->rtm_scope;
}
+ if (a[RTA_PRIORITY])
+ e->u.krt.metric = rta_get_u32(a[RTA_PRIORITY]);
+
if (a[RTA_PREFSRC])
{
ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]);
@@ -1508,15 +1695,20 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
else
{
/* Merge next hops with the stored route */
- rta *a = s->attrs;
+ rta *oa = s->attrs;
+
+ struct nexthop *nhs = &oa->nh;
+ nexthop_insert(&nhs, &ra->nh);
- if (a->dest != RTD_MULTIPATH)
+ /* Perhaps new nexthop is inserted at the first position */
+ if (nhs == &ra->nh)
{
- a->dest = RTD_MULTIPATH;
- a->nexthops = nl_alloc_mpnh(s, a->gw, a->iface, 0);
- }
+ /* Swap rtas */
+ s->attrs = ra;
- mpnh_insert(&a->nexthops, nl_alloc_mpnh(s, ra->gw, ra->iface, 0));
+ /* Keep old eattrs */
+ ra->eattrs = oa->eattrs;
+ }
}
}
@@ -1543,6 +1735,15 @@ krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NUL
else
log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
nl_parse_end(&s);
+
+ nl_parse_begin(&s, 1, 1);
+ nl_request_dump(AF_MPLS, RTM_GETROUTE);
+ while (h = nl_get_scan())
+ if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
+ nl_parse_route(&s, h);
+ else
+ log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
+ nl_parse_end(&s);
}
/*
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index e899671d..c6ff6275 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -645,17 +645,11 @@ krt_same_dest(rte *k, rte *e)
if (ka->dest != ea->dest)
return 0;
- switch (ka->dest)
- {
- case RTD_ROUTER:
- 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;
- }
+
+ if (ka->dest == RTD_UNICAST)
+ return nexthop_same(&(ka->nh), &(ea->nh));
+
+ return 1;
}
/*
@@ -990,7 +984,7 @@ krt_store_tmp_attrs(rte *rt, struct ea_list *attrs)
static int
krt_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED)
{
- struct krt_proto *p = (struct krt_proto *) P;
+ // struct krt_proto *p = (struct krt_proto *) P;
rte *e = *new;
if (e->attrs->src->proto == P)
@@ -1011,11 +1005,6 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct li
return -1;
}
- if (!KRT_CF->devroutes &&
- (e->attrs->dest == RTD_DEVICE) &&
- (e->attrs->source != RTS_STATIC_DEVICE))
- return -1;
-
if (!krt_capable(e))
return -1;
@@ -1153,7 +1142,8 @@ krt_start(struct proto *P)
{
case NET_IP4: p->af = AF_INET; break;
case NET_IP6: p->af = AF_INET6; break;
- default: ASSERT(0);
+ case NET_MPLS: p->af = AF_MPLS; break;
+ default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break;
}
add_tail(&krt_proto_list, &p->krt_node);
@@ -1264,7 +1254,7 @@ struct protocol proto_unix_kernel = {
.template = "kernel%d",
.attr_class = EAP_KRT,
.preference = DEF_PREF_INHERITED,
- .channel_mask = NB_IP,
+ .channel_mask = NB_IP | NB_MPLS,
.proto_size = sizeof(struct krt_proto),
.config_size = sizeof(struct krt_config),
.preconfig = krt_preconfig,