summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Filip <feela@network.cz>2018-02-27 06:08:03 +0100
committerOndrej Filip <feela@network.cz>2018-02-27 06:08:03 +0100
commit44062812600bd29f8edf30ebc871ff218069c5a2 (patch)
tree656c5cfcfe340cc8a12e7a88d930cfe839f7b97e
parent6f46465af1b3d21ca67e3b4379640c008fc9d1a1 (diff)
parent1561ee799cfe79d208ce9588e487da4b62a88dad (diff)
Merge branch 'int-new' of ssh://gitlab.labs.nic.cz/labs/bird into int-new
-rw-r--r--.gitignore1
-rw-r--r--Makefile.in5
-rw-r--r--bird.conf7
-rw-r--r--conf/confbase.Y24
-rw-r--r--doc/bird.conf.example211
-rw-r--r--filter/f-util.c4
-rw-r--r--filter/filter.c18
-rw-r--r--filter/filter.h1
-rw-r--r--lib/net.c87
-rw-r--r--lib/net.h90
-rw-r--r--nest/a-path.c2
-rw-r--r--nest/a-path_test.c6
-rw-r--r--nest/cli.c4
-rw-r--r--nest/config.Y21
-rw-r--r--nest/iface.c2
-rw-r--r--nest/mrtdump.h1
-rw-r--r--nest/neighbor.c2
-rw-r--r--nest/proto-hooks.c2
-rw-r--r--nest/protocol.h2
-rw-r--r--nest/route.h8
-rw-r--r--nest/rt-attr.c2
-rw-r--r--nest/rt-dev.c10
-rw-r--r--nest/rt-fib.c26
-rw-r--r--nest/rt-show.c8
-rw-r--r--nest/rt-table.c89
-rw-r--r--proto/babel/babel.c60
-rw-r--r--proto/babel/babel.h25
-rw-r--r--proto/babel/packets.c169
-rw-r--r--proto/bgp/bgp.h1
-rw-r--r--proto/bgp/config.Y30
-rw-r--r--proto/bgp/packets.c10
-rw-r--r--proto/pipe/config.Y14
-rw-r--r--proto/pipe/pipe.c4
-rw-r--r--sysdep/cf/linux.h1
-rw-r--r--sysdep/linux/netlink.c43
-rw-r--r--sysdep/unix/io.c9
-rw-r--r--sysdep/unix/krt.Y13
-rw-r--r--sysdep/unix/krt.c46
-rw-r--r--sysdep/unix/krt.h2
39 files changed, 649 insertions, 211 deletions
diff --git a/.gitignore b/.gitignore
index 0dcebfd1..3b734f49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@
/configure
/sysdep/autoconf.h.in
/sysdep/autoconf.h.in~
+/cscope.*
diff --git a/Makefile.in b/Makefile.in
index fdd5e6c7..c8168bbe 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -58,7 +58,7 @@ endif
docgoals := docs userdocs progdocs
testgoals := check test tests tests_run
cleangoals := clean distclean testsclean
-.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags
+.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags cscope
all: daemon cli
daemon: $(daemon)
@@ -162,6 +162,9 @@ endif
tags:
cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]`
+cscope:
+ cd $(srcdir) ; find $(dirs) -name *.[chY] > cscope.files ; cscope -b
+
# Install
install: all
diff --git a/bird.conf b/bird.conf
index 410f190c..e383c934 100644
--- a/bird.conf
+++ b/bird.conf
@@ -22,13 +22,14 @@ protocol direct {
# Feed routes to kernel FIB
protocol kernel {
- ipv4 { export all; };
-# learn; # Learn all routes from the kernel
+ ipv4 { export all; import all; };
+ learn; # Learn all routes from the kernel
# scan time 10; # Scan kernel tables every 10 seconds
}
protocol kernel {
- ipv6;
+ ipv6 { import all; };
+ learn;
}
# Static route feed
diff --git a/conf/confbase.Y b/conf/confbase.Y
index 7e0537c5..c2d647eb 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -83,7 +83,7 @@ CF_DECLS
%type <time> expr_us time
%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_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_mpls_
+%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
%type <mls> label_stack_start label_stack
%type <t> text opttext
@@ -96,7 +96,7 @@ CF_DECLS
%left '!'
%nonassoc '.'
-CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS)
+CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM)
CF_GRAMMAR
@@ -206,6 +206,25 @@ net_ip6_: IP6 '/' NUM
n->prefix, n->pxlen, ip6_and(n->prefix, ip6_mkmask(n->pxlen)), n->pxlen);
};
+net_ip6_sadr_: IP6 '/' NUM FROM IP6 '/' NUM
+{
+ if ($3 > IP6_MAX_PREFIX_LENGTH)
+ cf_error("Invalid prefix length %u", $3);
+
+ if ($7 > IP6_MAX_PREFIX_LENGTH)
+ cf_error("Invalid prefix length %u", $7);
+
+ $$ = cfg_alloc(sizeof(net_addr_ip6_sadr));
+ net_fill_ip6_sadr($$, $1, $3, $5, $7);
+
+ net_addr_ip6_sadr *n = (void *) $$;
+ if (!net_validate_ip6_sadr(n))
+ cf_error("Invalid SADR IPv6 prefix %I6/%d from %I6/%d, maybe you wanted %I6/%d from %I6/%d",
+ n->dst_prefix, n->dst_pxlen, n->src_prefix, n->src_pxlen,
+ ip6_and(n->dst_prefix, ip6_mkmask(n->dst_pxlen)), n->dst_pxlen,
+ ip6_and(n->src_prefix, ip6_mkmask(n->src_pxlen)), n->src_pxlen);
+};
+
net_vpn4_: VPN_RD net_ip4_
{
$$ = cfg_alloc(sizeof(net_addr_vpn4));
@@ -249,6 +268,7 @@ net_:
| net_vpn_
| net_roa_
| net_flow_
+ | net_ip6_sadr_
| net_mpls_
;
diff --git a/doc/bird.conf.example2 b/doc/bird.conf.example2
index d7048b64..815651c7 100644
--- a/doc/bird.conf.example2
+++ b/doc/bird.conf.example2
@@ -28,20 +28,15 @@ flow6 table flowtab6;
protocol device {
- scan time 10;
}
protocol kernel kernel4 {
- scan time 20;
-
ipv4 {
export all;
};
}
protocol kernel kernel6 {
- scan time 20;
-
ipv6 {
export all;
};
@@ -169,8 +164,6 @@ protocol pipe {
}
protocol ospf v2 ospf4 {
-# ecmp;
-
ipv4 {
import all;
# export where source = RTS_STATIC;
@@ -186,8 +179,6 @@ protocol ospf v2 ospf4 {
protocol ospf v3 ospf6 {
-# ecmp;
-
ipv6 {
import all;
# export where source = RTS_STATIC;
@@ -251,7 +242,7 @@ protocol bgp {
};
# IPv6 with MPLS labels (2/4)
- ipv6 multicast {
+ ipv6 mpls {
# explicit IPv6 table
table mtab6;
import all;
diff --git a/filter/f-util.c b/filter/f-util.c
index 661941ec..52c13223 100644
--- a/filter/f-util.c
+++ b/filter/f-util.c
@@ -24,11 +24,11 @@ f_new_inst(void)
}
struct f_inst *
-f_new_dynamic_attr(int type, int f_type UNUSED, int code)
+f_new_dynamic_attr(int type, int f_type, int code)
{
/* FIXME: Remove the f_type parameter? */
struct f_inst *f = f_new_inst();
- f->aux = type;
+ f->aux = (f_type << 8) | type;
f->a2.i = code;
return f;
}
diff --git a/filter/filter.c b/filter/filter.c
index 4e17f974..8cf90b53 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -587,7 +587,8 @@ val_format_str(struct f_val v) {
static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
#define runtime(fmt, ...) do { \
- log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \
+ if (!(f_flags & FF_SILENT)) \
+ log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \
res.type = T_RETURN; \
res.val.i = F_ERROR; \
return res; \
@@ -903,7 +904,8 @@ interpret(struct f_inst *what)
break;
case P('p',','):
ONEARG;
- if (what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p))
+ if ((what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) &&
+ !(f_flags & FF_SILENT))
log_commit(*L_INFO, &f_buf);
switch (what->a2.i) {
@@ -1003,6 +1005,7 @@ interpret(struct f_inst *what)
{
eattr *e = NULL;
u16 code = what->a2.i;
+ int f_type = what->aux >> 8;
if (!(f_flags & FF_FORCE_TMPATTR))
e = ea_find((*f_rte)->attrs->eattrs, code);
@@ -1047,7 +1050,7 @@ interpret(struct f_inst *what)
switch (what->aux & EAF_TYPE_MASK) {
case EAF_TYPE_INT:
- res.type = T_INT;
+ res.type = f_type;
res.val.i = e->u.data;
break;
case EAF_TYPE_ROUTER_ID:
@@ -1097,18 +1100,18 @@ interpret(struct f_inst *what)
{
struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr));
u16 code = what->a2.i;
+ int f_type = what->aux >> 8;
l->next = NULL;
l->flags = EALF_SORTED;
l->count = 1;
l->attrs[0].id = code;
l->attrs[0].flags = 0;
- l->attrs[0].type = what->aux | EAF_ORIGINATED | EAF_FRESH;
+ l->attrs[0].type = (what->aux & 0xff) | EAF_ORIGINATED | EAF_FRESH;
switch (what->aux & EAF_TYPE_MASK) {
case EAF_TYPE_INT:
- // Enums are also ints, so allow them in.
- if (v1.type != T_INT && (v1.type < T_ENUM_LO || v1.type > T_ENUM_HI))
+ if (v1.type != f_type)
runtime( "Setting int attribute to non-int value" );
l->attrs[0].u.data = v1.val.i;
break;
@@ -1793,7 +1796,8 @@ f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struc
if (res.type != T_RETURN) {
- log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name);
+ if (!(f_flags & FF_SILENT))
+ log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name);
return F_ERROR;
}
DBG( "done (%u)\n", res.val.i );
diff --git a/filter/filter.h b/filter/filter.h
index 89cd80e6..49004c33 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -208,6 +208,7 @@ struct f_trie
#define NEW_F_VAL struct f_val * val; val = cfg_alloc(sizeof(struct f_val));
#define FF_FORCE_TMPATTR 1 /* Force all attributes to be temporary */
+#define FF_SILENT 2 /* Silent filter execution */
/* Bird Tests */
struct f_bt_test_suite {
diff --git a/lib/net.c b/lib/net.c
index 9335b78f..976ddbcc 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -6,50 +6,54 @@
const char * const net_label[] = {
- [NET_IP4] = "ipv4",
- [NET_IP6] = "ipv6",
- [NET_VPN4] = "vpn4",
- [NET_VPN6] = "vpn6",
- [NET_ROA4] = "roa4",
- [NET_ROA6] = "roa6",
- [NET_FLOW4] = "flow4",
- [NET_FLOW6] = "flow6",
+ [NET_IP4] = "ipv4",
+ [NET_IP6] = "ipv6",
+ [NET_VPN4] = "vpn4",
+ [NET_VPN6] = "vpn6",
+ [NET_ROA4] = "roa4",
+ [NET_ROA6] = "roa6",
+ [NET_FLOW4] = "flow4",
+ [NET_FLOW6] = "flow6",
+ [NET_IP6_SADR]= "ipv6-sadr",
[NET_MPLS] = "mpls",
};
const u16 net_addr_length[] = {
- [NET_IP4] = sizeof(net_addr_ip4),
- [NET_IP6] = sizeof(net_addr_ip6),
- [NET_VPN4] = sizeof(net_addr_vpn4),
- [NET_VPN6] = sizeof(net_addr_vpn6),
- [NET_ROA4] = sizeof(net_addr_roa4),
- [NET_ROA6] = sizeof(net_addr_roa6),
- [NET_FLOW4] = 0,
- [NET_FLOW6] = 0,
+ [NET_IP4] = sizeof(net_addr_ip4),
+ [NET_IP6] = sizeof(net_addr_ip6),
+ [NET_VPN4] = sizeof(net_addr_vpn4),
+ [NET_VPN6] = sizeof(net_addr_vpn6),
+ [NET_ROA4] = sizeof(net_addr_roa4),
+ [NET_ROA6] = sizeof(net_addr_roa6),
+ [NET_FLOW4] = 0,
+ [NET_FLOW6] = 0,
+ [NET_IP6_SADR]= sizeof(net_addr_ip6_sadr),
[NET_MPLS] = sizeof(net_addr_mpls),
};
const u8 net_max_prefix_length[] = {
- [NET_IP4] = IP4_MAX_PREFIX_LENGTH,
- [NET_IP6] = IP6_MAX_PREFIX_LENGTH,
- [NET_VPN4] = IP4_MAX_PREFIX_LENGTH,
- [NET_VPN6] = IP6_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_IP4] = IP4_MAX_PREFIX_LENGTH,
+ [NET_IP6] = IP6_MAX_PREFIX_LENGTH,
+ [NET_VPN4] = IP4_MAX_PREFIX_LENGTH,
+ [NET_VPN6] = IP6_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_IP6_SADR]= IP6_MAX_PREFIX_LENGTH,
[NET_MPLS] = 0,
};
const u16 net_max_text_length[] = {
- [NET_IP4] = 18, /* "255.255.255.255/32" */
- [NET_IP6] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
- [NET_VPN4] = 40, /* "4294967296:4294967296 255.255.255.255/32" */
- [NET_VPN6] = 65, /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
- [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_IP4] = 18, /* "255.255.255.255/32" */
+ [NET_IP6] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
+ [NET_VPN4] = 40, /* "4294967296:4294967296 255.255.255.255/32" */
+ [NET_VPN6] = 65, /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
+ [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_IP6_SADR]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
[NET_MPLS] = 7, /* "1048575" */
};
@@ -102,6 +106,8 @@ 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_IP6_SADR:
+ return bsnprintf(buf, buflen, "%I6/%d from %I6/%d", n->ip6_sadr.dst_prefix, n->ip6_sadr.dst_pxlen, n->ip6_sadr.src_prefix, n->ip6_sadr.src_pxlen);
case NET_MPLS:
return bsnprintf(buf, buflen, "%u", n->mpls.label);
}
@@ -124,6 +130,7 @@ net_pxmask(const net_addr *a)
case NET_VPN6:
case NET_ROA6:
case NET_FLOW6:
+ case NET_IP6_SADR:
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
case NET_MPLS:
@@ -156,6 +163,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_IP6_SADR:
+ return net_compare_ip6_sadr((const net_addr_ip6_sadr *) a, (const net_addr_ip6_sadr *) b);
case NET_MPLS:
return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b);
}
@@ -177,6 +186,7 @@ net_hash(const net_addr *n)
case NET_ROA6: return NET_HASH(n, roa6);
case NET_FLOW4: return NET_HASH(n, flow4);
case NET_FLOW6: return NET_HASH(n, flow6);
+ case NET_IP6_SADR: return NET_HASH(n, ip6_sadr);
case NET_MPLS: return NET_HASH(n, mpls);
default: bug("invalid type");
}
@@ -198,6 +208,7 @@ net_validate(const net_addr *n)
case NET_ROA6: return NET_VALIDATE(n, roa6);
case NET_FLOW4: return NET_VALIDATE(n, flow4);
case NET_FLOW6: return NET_VALIDATE(n, flow6);
+ case NET_IP6_SADR: return NET_VALIDATE(n, ip6_sadr);
case NET_MPLS: return NET_VALIDATE(n, mpls);
default: return 0;
}
@@ -222,6 +233,9 @@ net_normalize(net_addr *N)
case NET_FLOW6:
return net_normalize_ip6(&n->ip6);
+ case NET_IP6_SADR:
+ return net_normalize_ip6_sadr(&n->ip6_sadr);
+
case NET_MPLS:
return;
}
@@ -246,6 +260,9 @@ net_classify(const net_addr *N)
case NET_FLOW6:
return ip6_zero(n->ip6.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6.prefix);
+ case NET_IP6_SADR:
+ return ip6_zero(n->ip6_sadr.dst_prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6_sadr.dst_prefix);
+
case NET_MPLS:
return IADDR_HOST | SCOPE_UNIVERSE;
}
@@ -274,6 +291,11 @@ 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_IP6_SADR:
+ if (ipa_is_ip4(a)) return 0;
+ 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;
@@ -304,5 +326,6 @@ net_init(void)
CHECK_NET(net_addr_roa6, 28);
CHECK_NET(net_addr_flow4, 8);
CHECK_NET(net_addr_flow6, 20);
+ CHECK_NET(net_addr_ip6_sadr, 40);
CHECK_NET(net_addr_mpls, 8);
}
diff --git a/lib/net.h b/lib/net.h
index 69f00641..ad4000fd 100644
--- a/lib/net.h
+++ b/lib/net.h
@@ -21,8 +21,9 @@
#define NET_ROA6 6
#define NET_FLOW4 7
#define NET_FLOW6 8
-#define NET_MPLS 9
-#define NET_MAX 10
+#define NET_IP6_SADR 9
+#define NET_MPLS 10
+#define NET_MAX 11
#define NB_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6)
@@ -32,12 +33,13 @@
#define NB_ROA6 (1 << NET_ROA6)
#define NB_FLOW4 (1 << NET_FLOW4)
#define NB_FLOW6 (1 << NET_FLOW6)
+#define NB_IP6_SADR (1 << NET_IP6_SADR)
#define NB_MPLS (1 << NET_MPLS)
#define NB_IP (NB_IP4 | NB_IP6)
#define NB_VPN (NB_VPN4 | NB_VPN6)
#define NB_FLOW (NB_FLOW4 | NB_FLOW6)
-#define NB_DEST (NB_IP | NB_VPN | NB_MPLS)
+#define NB_DEST (NB_IP | NB_IP6_SADR | NB_VPN | NB_MPLS)
#define NB_ANY 0xffffffff
@@ -121,6 +123,15 @@ typedef struct net_addr_mpls {
u32 label;
} net_addr_mpls;
+typedef struct net_addr_ip6_sadr {
+ u8 type;
+ u8 dst_pxlen;
+ u16 length;
+ ip6_addr dst_prefix;
+ s32 src_pxlen; /* s32 to avoid padding */
+ ip6_addr src_prefix;
+} net_addr_ip6_sadr;
+
typedef union net_addr_union {
net_addr n;
net_addr_ip4 ip4;
@@ -131,6 +142,7 @@ typedef union net_addr_union {
net_addr_roa6 roa6;
net_addr_flow4 flow4;
net_addr_flow6 flow6;
+ net_addr_ip6_sadr ip6_sadr;
net_addr_mpls mpls;
} net_addr_union;
@@ -167,6 +179,9 @@ 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_IP6_SADR(dst_prefix,dst_pxlen,src_prefix,src_pxlen) \
+ ((net_addr_ip6_sadr) { NET_IP6_SADR, dst_pxlen, sizeof(net_addr_ip6_sadr), dst_prefix, src_pxlen, src_prefix })
+
#define NET_ADDR_MPLS(label) \
((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
@@ -189,6 +204,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_ip6_sadr(net_addr *a, ip6_addr dst_prefix, uint dst_pxlen, ip6_addr src_prefix, uint src_pxlen)
+{ *(net_addr_ip6_sadr *)a = NET_ADDR_IP6_SADR(dst_prefix, dst_pxlen, src_prefix, src_pxlen); }
+
static inline void net_fill_mpls(net_addr *a, u32 label)
{ *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
@@ -222,6 +240,16 @@ static inline void net_fill_flow6(net_addr *a, ip6_addr prefix, uint pxlen, byte
memcpy(f->data, data, dlen);
}
+/* Make NET_IP6_SADR from NET_IP6, assuming there is enough space */
+static inline void net_make_ip6_sadr(net_addr *a)
+{
+ net_addr_ip6_sadr *n = (void *) a;
+ n->type = NET_IP6_SADR;
+ n->length = sizeof(net_addr_ip6_sadr);
+ n->src_pxlen = 0;
+ n->src_prefix = IP6_NONE;
+}
+
static inline int net_val_match(u8 type, u32 mask)
{ return !!((1 << type) & mask); }
@@ -261,6 +289,7 @@ static inline ip_addr net_prefix(const net_addr *a)
case NET_VPN6:
case NET_ROA6:
case NET_FLOW6:
+ case NET_IP6_SADR:
return ipa_from_ip6(net6_prefix(a));
case NET_MPLS:
@@ -328,6 +357,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_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
+{ return !memcmp(a, b, sizeof(net_addr_ip6_sadr)); }
+
static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
{ return !memcmp(a, b, sizeof(net_addr_mpls)); }
@@ -338,6 +370,12 @@ static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_r
static inline int net_equal_prefix_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
{ return ip6_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
+static inline int net_equal_dst_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
+{ return ip6_equal(a->dst_prefix, b->dst_prefix) && (a->dst_pxlen == b->dst_pxlen); }
+
+static inline int net_equal_src_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
+{ return ip6_equal(a->src_prefix, b->src_prefix) && (a->src_pxlen == b->src_pxlen); }
+
static inline int net_zero_ip4(const net_addr_ip4 *a)
{ return !a->pxlen && ip4_zero(a->prefix); }
@@ -391,6 +429,13 @@ 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_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
+{
+ return
+ ip6_compare(a->dst_prefix, b->dst_prefix) ?: uint_cmp(a->dst_pxlen, b->dst_pxlen) ?:
+ ip6_compare(a->src_prefix, b->src_prefix) ?: uint_cmp(a->src_pxlen, b->src_pxlen);
+}
+
static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
{ return uint_cmp(a->label, b->label); }
@@ -424,6 +469,9 @@ 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_ip6_sadr(net_addr_ip6_sadr *dst, const net_addr_ip6_sadr *src)
+{ memcpy(dst, src, sizeof(net_addr_ip6_sadr)); }
+
static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
{ memcpy(dst, src, sizeof(net_addr_mpls)); }
@@ -456,6 +504,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_ip6_sadr(const net_addr_ip6_sadr *n)
+{ return net_hash_ip6((net_addr_ip6 *) n); }
+
static inline u32 net_hash_mpls(const net_addr_mpls *n)
{ return n->label; }
@@ -508,6 +559,9 @@ static inline int net_validate_flow6(const net_addr_flow6 *n)
static inline int net_validate_mpls(const net_addr_mpls *n)
{ return n->label < (1 << 20); }
+static inline int net_validate_ip6_sadr(const net_addr_ip6_sadr *n)
+{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); }
+
int net_validate(const net_addr *N);
@@ -523,6 +577,12 @@ static inline void net_normalize_vpn4(net_addr_vpn4 *n)
static inline void net_normalize_vpn6(net_addr_vpn6 *n)
{ net_normalize_ip6((net_addr_ip6 *) n); }
+static inline void net_normalize_ip6_sadr(net_addr_ip6_sadr *n)
+{
+ n->dst_prefix = ip6_and(n->dst_prefix, ip6_mkmask(n->dst_pxlen));
+ n->src_prefix = ip6_and(n->src_prefix, ip6_mkmask(n->src_pxlen));
+}
+
void net_normalize(net_addr *N);
@@ -530,17 +590,29 @@ int net_classify(const net_addr *N);
int net_format(const net_addr *N, char *buf, int buflen);
int rd_format(const u64 rd, char *buf, int buflen);
-static inline int ipa_in_net_ip4(ip4_addr a, const net_addr_ip4 *n)
-{ return ip4_zero(ip4_and(ip4_xor(a, n->prefix), ip4_mkmask(n->pxlen))); }
+static inline int ipa_in_px4(ip4_addr a, ip4_addr prefix, uint pxlen)
+{ return ip4_zero(ip4_and(ip4_xor(a, prefix), ip4_mkmask(pxlen))); }
-static inline int net_in_net_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
-{ return (a->pxlen >= b->pxlen) && ipa_in_net_ip4(a->prefix, b); }
+static inline int ipa_in_px6(ip6_addr a, ip6_addr prefix, uint pxlen)
+{ return ip6_zero(ip6_and(ip6_xor(a, prefix), ip6_mkmask(pxlen))); }
+
+static inline int ipa_in_net_ip4(ip4_addr a, const net_addr_ip4 *n)
+{ return ipa_in_px4(a, n->prefix, n->pxlen); }
static inline int ipa_in_net_ip6(ip6_addr a, const net_addr_ip6 *n)
-{ return ip6_zero(ip6_and(ip6_xor(a, n->prefix), ip6_mkmask(n->pxlen))); }
+{ return ipa_in_px6(a, n->prefix, n->pxlen); }
+
+static inline int net_in_net_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
+{ return (a->pxlen >= b->pxlen) && ipa_in_px4(a->prefix, b->prefix, b->pxlen); }
static inline int net_in_net_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
-{ return (a->pxlen >= b->pxlen) && ipa_in_net_ip6(a->prefix, b); }
+{ return (a->pxlen >= b->pxlen) && ipa_in_px6(a->prefix, b->prefix, b->pxlen); }
+
+static inline int net_in_net_dst_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
+{ return (a->dst_pxlen >= b->dst_pxlen) && ipa_in_px6(a->dst_prefix, b->dst_prefix, b->dst_pxlen); }
+
+static inline int net_in_net_src_ip6_sadr(const net_addr_ip6_sadr *a, const net_addr_ip6_sadr *b)
+{ return (a->src_pxlen >= b->src_pxlen) && ipa_in_px6(a->src_prefix, b->src_prefix, b->src_pxlen); }
int ipa_in_netX(const ip_addr A, const net_addr *N);
int net_in_netX(const net_addr *A, const net_addr *N);
diff --git a/nest/a-path.c b/nest/a-path.c
index 32ffc72c..c0d16c30 100644
--- a/nest/a-path.c
+++ b/nest/a-path.c
@@ -810,7 +810,7 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
case PM_ASN_RANGE:
val = mask->val;
val2 = mask->val2;
- goto step;
+ goto step;
case PM_QUESTION:
step:
nh = nl = -1;
diff --git a/nest/a-path_test.c b/nest/a-path_test.c
index 5e122396..a71b48ba 100644
--- a/nest/a-path_test.c
+++ b/nest/a-path_test.c
@@ -43,14 +43,14 @@ t_as_path_match(void)
bt_debug("Prepending ASN: %10u \n", val);
if (i == 0)
- first_prepended = val;
+ first_prepended = val;
if (i == AS_PATH_LENGTH-1)
- last_prepended = val;
+ last_prepended = val;
mask[i].kind = PM_ASN;
mask[i].val = val;
if (i)
- mask[i].next = &mask[i-1];
+ mask[i].next = &mask[i-1];
}
bt_assert_msg(as_path_match(as_path, &mask[AS_PATH_LENGTH-1]), "Mask should match with AS path");
diff --git a/nest/cli.c b/nest/cli.c
index ad81d384..c421cc7e 100644
--- a/nest/cli.c
+++ b/nest/cli.c
@@ -60,7 +60,7 @@
* the new one. When the consumer processes everything in the buffer
* queue, it calls cli_written(), tha frees all buffers (except the
* first one) and schedules cli.event .
- *
+ *
*/
#include "nest/bird.h"
@@ -136,7 +136,7 @@ cli_printf(cli *c, int code, char *msg, ...)
}
else if (cd == CLI_ASYNC_CODE)
{
- size = 1; buf[0] = '+';
+ size = 1; buf[0] = '+';
errcode = cd;
}
else
diff --git a/nest/config.Y b/nest/config.Y
index 044aba2b..ab09a10c 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -66,7 +66,7 @@ CF_DECLS
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, TABLE, STATES, ROUTES, FILTERS)
-CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6)
+CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
@@ -77,7 +77,7 @@ CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
/* For r_args_channel */
-CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
+CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
@@ -134,6 +134,7 @@ gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
net_type:
IPV4 { $$ = NET_IP4; }
| IPV6 { $$ = NET_IP6; }
+ | IPV6 SADR { $$ = NET_IP6_SADR; }
| VPN4 { $$ = NET_VPN4; }
| VPN6 { $$ = NET_VPN6; }
| ROA4 { $$ = NET_ROA4; }
@@ -143,7 +144,7 @@ net_type:
| MPLS { $$ = NET_MPLS; }
;
-CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6)
+CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP6_SADR)
/* Creation of routing tables */
@@ -151,7 +152,7 @@ CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6)
CF_ADDTO(conf, table)
table_sorted:
- { $$ = 0; }
+ { $$ = 0; }
| SORTED { $$ = 1; }
;
@@ -454,9 +455,9 @@ password_item:
password_item_begin:
PASSWORD text {
if (!this_p_list) {
- this_p_list = cfg_alloc(sizeof(list));
- init_list(this_p_list);
- password_id = 1;
+ this_p_list = cfg_alloc(sizeof(list));
+ init_list(this_p_list);
+ password_id = 1;
}
this_p_item = cfg_alloc(sizeof (struct password_item));
this_p_item->password = $2;
@@ -625,6 +626,7 @@ r_args_for:
}
| net_vpn4_
| net_vpn6_
+ | net_ip6_sadr_
| VPN_RD IP4 {
$$ = cfg_alloc(sizeof(net_addr_vpn4));
net_fill_vpn4($$, $2, IP4_MAX_PREFIX_LENGTH, $1);
@@ -633,6 +635,10 @@ r_args_for:
$$ = cfg_alloc(sizeof(net_addr_vpn6));
net_fill_vpn6($$, $2, IP6_MAX_PREFIX_LENGTH, $1);
}
+ | IP6 FROM IP6 {
+ $$ = cfg_alloc(sizeof(net_addr_ip6_sadr));
+ net_fill_ip6_sadr($$, $1, IP6_MAX_PREFIX_LENGTH, $3, IP6_MAX_PREFIX_LENGTH);
+ }
| SYM {
if ($1->class == (SYM_CONSTANT | T_IP))
{
@@ -666,6 +672,7 @@ r_args_channel:
| IPV6 { $$ = "ipv6"; }
| IPV6_MC { $$ = "ipv6-mc"; }
| IPV6_MPLS { $$ = "ipv6-mpls"; }
+ | IPV6_SADR { $$ = "ipv6-sadr"; }
| VPN4 { $$ = "vpn4"; }
| VPN4_MC { $$ = "vpn4-mc"; }
| VPN4_MPLS { $$ = "vpn4-mpls"; }
diff --git a/nest/iface.c b/nest/iface.c
index 54c16c58..a633f748 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -317,7 +317,7 @@ if_update(struct iface *new)
new->sysdep = i->sysdep;
memcpy(&new->addrs, &i->addrs, sizeof(i->addrs));
memcpy(i, new, sizeof(*i));
- i->flags &= ~IF_UP; /* IF_TMP_DOWN will be added later */
+ i->flags &= ~IF_UP; /* IF_TMP_DOWN will be added later */
goto newif;
}
diff --git a/nest/mrtdump.h b/nest/mrtdump.h
index 73932553..28b3bdfd 100644
--- a/nest/mrtdump.h
+++ b/nest/mrtdump.h
@@ -28,4 +28,3 @@
void mrt_dump_message(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len);
#endif
-
diff --git a/nest/neighbor.c b/nest/neighbor.c
index fb05d96c..4f93e29e 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -156,7 +156,7 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
WALK_LIST(i, iface_list)
if ((!p->vrf || p->vrf == i->master) &&
((scope = if_connected(a, i, &addr)) >= 0))
- {
+ {
ifa = i;
break;
}
diff --git a/nest/proto-hooks.c b/nest/proto-hooks.c
index 92863f8e..71cddd64 100644
--- a/nest/proto-hooks.c
+++ b/nest/proto-hooks.c
@@ -281,7 +281,7 @@ int import_control(struct proto *p, rte **e, ea_list **attrs, struct linpool *po
/**
* rte_recalculate - prepare routes for comparison
- * @table: a routing table
+ * @table: a routing table
* @net: a network entry
* @new: new route for the network
* @old: old route for the network
diff --git a/nest/protocol.h b/nest/protocol.h
index 9afd3a0a..8a22d76b 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -337,7 +337,7 @@ void proto_notify_state(struct proto *p, unsigned state);
*
* HUNGRY ----> FEEDING
* ^ |
- * | V
+ * | V
* FLUSHING <---- HAPPY
*
* States: HUNGRY Protocol either administratively down (i.e.,
diff --git a/nest/route.h b/nest/route.h
index 43d7d696..79127519 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -74,7 +74,7 @@ static inline struct fib_node * fib_user_to_node(struct fib *f, void *e)
void fib_init(struct fib *f, pool *p, uint addr_type, uint node_size, uint node_offset, uint hash_order, fib_init_fn init);
void *fib_find(struct fib *, const net_addr *); /* Find or return NULL if doesn't exist */
void *fib_get_chain(struct fib *f, const net_addr *a); /* Find first node in linked list from hash table */
-void *fib_get(struct fib *, const net_addr *); /* Find or create new if nonexistent */
+void *fib_get(struct fib *, const net_addr *); /* Find or create new if nonexistent */
void *fib_route(struct fib *, const net_addr *); /* Longest-match routing lookup */
void fib_delete(struct fib *, void *); /* Remove fib entry */
void fib_free(struct fib *); /* Destroy the fib */
@@ -104,7 +104,7 @@ void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uin
type *z; \
for(;;) { \
if (!fn_) \
- { \
+ { \
if (++hpos_ >= count_) \
break; \
fn_ = (fib)->hash_table[hpos_]; \
@@ -282,7 +282,7 @@ void rt_preconfig(struct config *);
void rt_commit(struct config *new, struct config *old);
void rt_lock_table(rtable *);
void rt_unlock_table(rtable *);
-void rt_setup(pool *, rtable *, char *, struct rtable_config *);
+void rt_setup(pool *, rtable *, struct rtable_config *);
static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
static inline net *net_find_valid(rtable *tab, const net_addr *addr)
{ net *n = net_find(tab, addr); return (n && rte_is_valid(n->routes)) ? n : NULL; }
@@ -663,7 +663,7 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX];
* Default protocol preferences
*/
-#define DEF_PREF_DIRECT 240 /* Directly connected */
+#define DEF_PREF_DIRECT 240 /* Directly connected */
#define DEF_PREF_STATIC 200 /* Static route */
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
#define DEF_PREF_BABEL 130 /* Babel */
diff --git a/nest/rt-attr.c b/nest/rt-attr.c
index 761ba9fe..881687de 100644
--- a/nest/rt-attr.c
+++ b/nest/rt-attr.c
@@ -1204,7 +1204,7 @@ rta_dump(rta *a)
static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
- "RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
+ "RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
debug("p=%s uc=%d %s %s%s h=%04x",
diff --git a/nest/rt-dev.c b/nest/rt-dev.c
index b3d5bf97..718c4578 100644
--- a/nest/rt-dev.c
+++ b/nest/rt-dev.c
@@ -159,13 +159,13 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
}
struct protocol proto_device = {
- .name = "Direct",
- .template = "direct%d",
+ .name = "Direct",
+ .template = "direct%d",
.preference = DEF_PREF_DIRECT,
.channel_mask = NB_IP,
.proto_size = sizeof(struct rt_dev_proto),
.config_size = sizeof(struct rt_dev_config),
- .init = dev_init,
- .reconfigure = dev_reconfigure,
- .copy_config = dev_copy_config
+ .init = dev_init,
+ .reconfigure = dev_reconfigure,
+ .copy_config = dev_copy_config
};
diff --git a/nest/rt-fib.c b/nest/rt-fib.c
index 24a7facc..18ccbfc3 100644
--- a/nest/rt-fib.c
+++ b/nest/rt-fib.c
@@ -92,8 +92,7 @@ fib_ht_free(struct fib_node **h)
}
-static u32
-fib_hash(struct fib *f, const net_addr *a);
+static inline u32 fib_hash(struct fib *f, const net_addr *a);
/**
* fib_init - initialize a new FIB
@@ -198,24 +197,11 @@ fib_rehash(struct fib *f, int step)
})
-static u32
+static inline u32
fib_hash(struct fib *f, const net_addr *a)
{
- ASSERT(f->addr_type == a->type);
-
- switch (f->addr_type)
- {
- case NET_IP4: return FIB_HASH(f, a, ip4);
- case NET_IP6: return FIB_HASH(f, a, ip6);
- case NET_VPN4: return FIB_HASH(f, a, vpn4);
- case NET_VPN6: return FIB_HASH(f, a, vpn6);
- case NET_ROA4: return FIB_HASH(f, a, roa4);
- case NET_ROA6: return FIB_HASH(f, a, roa6);
- case NET_FLOW4: return FIB_HASH(f, a, flow4);
- case NET_FLOW6: return FIB_HASH(f, a, flow6);
- case NET_MPLS: return FIB_HASH(f, a, mpls);
- default: bug("invalid type");
- }
+ /* Same as FIB_HASH() */
+ return net_hash(a) >> f->hash_shift;
}
void *
@@ -250,6 +236,7 @@ fib_find(struct fib *f, const net_addr *a)
case NET_ROA6: return FIB_FIND(f, a, roa6);
case NET_FLOW4: return FIB_FIND(f, a, flow4);
case NET_FLOW6: return FIB_FIND(f, a, flow6);
+ case NET_IP6_SADR: return FIB_FIND(f, a, ip6_sadr);
case NET_MPLS: return FIB_FIND(f, a, mpls);
default: bug("invalid type");
}
@@ -270,6 +257,7 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e)
case NET_ROA6: FIB_INSERT(f, a, e, roa6); return;
case NET_FLOW4: FIB_INSERT(f, a, e, flow4); return;
case NET_FLOW6: FIB_INSERT(f, a, e, flow6); return;
+ case NET_IP6_SADR: FIB_INSERT(f, a, e, ip6_sadr); return;
case NET_MPLS: FIB_INSERT(f, a, e, mpls); return;
default: bug("invalid type");
}
@@ -617,7 +605,7 @@ fib_histogram(struct fib *f)
for (e = f->hash_table[i]; e != NULL; e = e->next)
j++;
if (j > 0)
- log(L_WARN "Histogram line %d: %d", i, j);
+ log(L_WARN "Histogram line %d: %d", i, j);
}
log(L_WARN "Histogram dump end");
diff --git a/nest/rt-show.c b/nest/rt-show.c
index 41a141a2..1f1b73d2 100644
--- a/nest/rt-show.c
+++ b/nest/rt-show.c
@@ -74,7 +74,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
char weight[16] = "";
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]);
@@ -129,7 +129,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
/* Special case for merged export */
if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
- {
+ {
rte *rt_free;
e = rt_export_merged(ec, n, &rt_free, &tmpa, c->show_pool, 1);
pass = 1;
@@ -156,7 +156,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
* command may change the export filter and do not update routes.
*/
int do_export = (ic > 0) ||
- (f_run(ec->out_filter, &e, &tmpa, c->show_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+ (f_run(ec->out_filter, &e, &tmpa, c->show_pool,
+ FF_FORCE_TMPATTR | FF_SILENT) <= F_ACCEPT);
if (do_export != (d->export_mode == RSEM_EXPORT))
goto skip;
@@ -418,4 +419,3 @@ rt_show(struct rt_show_data *d)
cli_msg(8001, "Network not found");
}
}
-
diff --git a/nest/rt-table.c b/nest/rt-table.c
index b0dd6d3f..686d0e84 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -85,6 +85,45 @@ net_route_ip6(rtable *t, net_addr_ip6 *n)
return r;
}
+static inline void *
+net_route_ip6_sadr(rtable *t, net_addr_ip6_sadr *n)
+{
+ struct fib_node *fn;
+
+ while (1)
+ {
+ net *best = NULL;
+ int best_pxlen = 0;
+
+ /* We need to do dst first matching. Since sadr addresses are hashed on dst
+ prefix only, find the hash table chain and go through it to find the
+ match with the smallest matching src prefix. */
+ for (fn = fib_get_chain(&t->fib, (net_addr *) n); fn; fn = fn->next)
+ {
+ net_addr_ip6_sadr *a = (void *) fn->addr;
+
+ if (net_equal_dst_ip6_sadr(n, a) &&
+ net_in_net_src_ip6_sadr(n, a) &&
+ (a->src_pxlen >= best_pxlen))
+ {
+ best = fib_node_to_user(&t->fib, fn);
+ best_pxlen = a->src_pxlen;
+ }
+ }
+
+ if (best)
+ return best;
+
+ if (!n->dst_pxlen)
+ break;
+
+ n->dst_pxlen--;
+ ip6_clrbit(&n->dst_prefix, n->dst_pxlen);
+ }
+
+ return NULL;
+}
+
void *
net_route(rtable *tab, const net_addr *n)
{
@@ -105,6 +144,9 @@ net_route(rtable *tab, const net_addr *n)
case NET_ROA6:
return net_route_ip6(tab, (net_addr_ip6 *) n0);
+ case NET_IP6_SADR:
+ return net_route_ip6_sadr(tab, (net_addr_ip6_sadr *) n0);
+
default:
return NULL;
}
@@ -387,7 +429,8 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, linpo
}
v = filter && ((filter == FILTER_REJECT) ||
- (f_run(filter, &rt, tmpa, pool, FF_FORCE_TMPATTR) > F_ACCEPT));
+ (f_run(filter, &rt, tmpa, pool,
+ FF_FORCE_TMPATTR | (silent ? FF_SILENT : 0)) > F_ACCEPT));
if (v)
{
if (silent)
@@ -613,9 +656,9 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
old_meet = 1;
}
- /*
+ /*
* Second, handle the feed case. That means we do not care for
- * old_best. It is NULL for feed, and the new_best for refeed.
+ * old_best. It is NULL for feed, and the new_best for refeed.
* For refeed, there is a hack similar to one in rt_notify_basic()
* to ensure withdraws in case of changed filters
*/
@@ -824,7 +867,7 @@ rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed
* @new_best: the new best route for the same network
* @old_best: the previous best route for the same network
* @before_old: The previous route before @old for the same network.
- * If @before_old is NULL @old was the first.
+ * If @before_old is NULL @old was the first.
*
* This function gets a routing table update and announces it
* to all protocols that acccepts given type of route announcement
@@ -1386,7 +1429,7 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
/* Independent call to rte_announce(), used from next hop
recalculation, outside of rte_update(). new must be non-NULL */
-static inline void
+static inline void
rte_announce_i(rtable *tab, unsigned type, net *net, rte *new, rte *old,
rte *new_best, rte *old_best)
{
@@ -1419,7 +1462,8 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
ea_list *tmpa = rte_make_tmp_attrs(rt, rte_update_pool);
int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0;
if (v == RIC_PROCESS)
- v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+ v = (f_run(filter, &rt, &tmpa, rte_update_pool,
+ FF_FORCE_TMPATTR | FF_SILENT) <= F_ACCEPT);
/* Discard temporary rte */
if (rt != n->routes)
@@ -1597,22 +1641,19 @@ rt_event(void *ptr)
}
void
-rt_setup(pool *p, rtable *t, char *name, struct rtable_config *cf)
+rt_setup(pool *p, rtable *t, struct rtable_config *cf)
{
bzero(t, sizeof(*t));
- t->name = name;
+ t->name = cf->name;
t->config = cf;
- t->addr_type = cf ? cf->addr_type : NET_IP4;
+ t->addr_type = cf->addr_type;
fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL);
init_list(&t->channels);
- if (cf)
- {
- t->rt_event = ev_new(p);
- t->rt_event->hook = rt_event;
- t->rt_event->data = t;
- t->gc_time = current_time();
- }
+ t->rt_event = ev_new(p);
+ t->rt_event->hook = rt_event;
+ t->rt_event->data = t;
+ t->gc_time = current_time();
}
/**
@@ -2088,7 +2129,7 @@ rt_commit(struct config *new, struct config *old)
{
rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
DBG("\t%s: created\n", r->name);
- rt_setup(rt_table_pool, t, r->name, r);
+ rt_setup(rt_table_pool, t, r);
add_tail(&routing_tables, &t->n);
r->table = t;
}
@@ -2358,7 +2399,7 @@ if_local_addr(ip_addr a, struct iface *i)
return 0;
}
-static u32
+static u32
rt_get_igp_metric(rte *rt)
{
eattr *ea = ea_find(rt->attrs->eattrs, EA_GEN_IGP_METRIC);
@@ -2390,12 +2431,13 @@ static int
rt_update_hostentry(rtable *tab, struct hostentry *he)
{
rta *old_src = he->src;
+ int direct = 0;
int pxlen = 0;
/* Reset the hostentry */
he->src = NULL;
- he->nexthop_linkable = 0;
he->dest = RTD_UNREACHABLE;
+ he->nexthop_linkable = 0;
he->igp_metric = 0;
net_addr he_addr;
@@ -2415,9 +2457,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
goto done;
}
- he->dest = a->dest;
- he->nexthop_linkable = 1;
- if (he->dest == RTD_UNICAST)
+ if (a->dest == RTD_UNICAST)
{
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
if (ipa_zero(nh->gw))
@@ -2430,12 +2470,13 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
goto done;
}
- he->nexthop_linkable = 0;
- break;
+ direct++;
}
}
he->src = rta_clone(a);
+ he->dest = a->dest;
+ he->nexthop_linkable = !direct;
he->igp_metric = rt_get_igp_metric(e);
}
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index aa7e8b68..88c4711e 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -1935,8 +1935,10 @@ babel_show_neighbors(struct proto *P, char *iff)
}
static void
-babel_show_entries_(struct babel_proto *p UNUSED, struct fib *rtable)
+babel_show_entries_(struct babel_proto *p, struct fib *rtable)
{
+ int width = babel_sadr_enabled(p) ? -54 : -24;
+
FIB_WALK(rtable, struct babel_entry, e)
{
struct babel_route *r = NULL;
@@ -1950,13 +1952,13 @@ babel_show_entries_(struct babel_proto *p UNUSED, struct fib *rtable)
srcs++;
if (e->valid)
- cli_msg(-1025, "%-24N %-23lR %6u %5u %7u %7u",
+ cli_msg(-1025, "%-*N %-23lR %6u %5u %7u %7u", width,
e->n.addr, e->router_id, e->metric, e->seqno, rts, srcs);
else if (r = e->selected)
- cli_msg(-1025, "%-24N %-23lR %6u %5u %7u %7u",
+ cli_msg(-1025, "%-*N %-23lR %6u %5u %7u %7u", width,
e->n.addr, r->router_id, r->metric, r->seqno, rts, srcs);
else
- cli_msg(-1025, "%-24N %-23s %6s %5s %7u %7u",
+ cli_msg(-1025, "%-*N %-23s %6s %5s %7u %7u", width,
e->n.addr, "<none>", "-", "-", rts, srcs);
}
FIB_WALK_END;
@@ -1966,6 +1968,7 @@ void
babel_show_entries(struct proto *P)
{
struct babel_proto *p = (void *) P;
+ int width = babel_sadr_enabled(p) ? -54 : -24;
if (p->p.proto_state != PS_UP)
{
@@ -1975,7 +1978,7 @@ babel_show_entries(struct proto *P)
}
cli_msg(-1025, "%s:", p->p.name);
- cli_msg(-1025, "%-24s %-23s %6s %5s %7s %7s",
+ cli_msg(-1025, "%-*s %-23s %6s %5s %7s %7s", width,
"Prefix", "Router ID", "Metric", "Seqno", "Routes", "Sources");
babel_show_entries_(p, &p->ip4_rtable);
@@ -1985,8 +1988,10 @@ babel_show_entries(struct proto *P)
}
static void
-babel_show_routes_(struct babel_proto *p UNUSED, struct fib *rtable)
+babel_show_routes_(struct babel_proto *p, struct fib *rtable)
{
+ int width = babel_sadr_enabled(p) ? -54 : -24;
+
FIB_WALK(rtable, struct babel_entry, e)
{
struct babel_route *r;
@@ -1994,7 +1999,7 @@ babel_show_routes_(struct babel_proto *p UNUSED, struct fib *rtable)
{
char c = (r == e->selected) ? '*' : (r->feasible ? '+' : ' ');
btime time = r->expires ? r->expires - current_time() : 0;
- cli_msg(-1025, "%-24N %-25I %-10s %5u %c %5u %7t",
+ cli_msg(-1025, "%-*N %-25I %-10s %5u %c %5u %7t", width,
e->n.addr, r->next_hop, r->neigh->ifa->ifname,
r->metric, c, r->seqno, MAX(time, 0));
}
@@ -2006,6 +2011,7 @@ void
babel_show_routes(struct proto *P)
{
struct babel_proto *p = (void *) P;
+ int width = babel_sadr_enabled(p) ? -54 : -24;
if (p->p.proto_state != PS_UP)
{
@@ -2015,7 +2021,7 @@ babel_show_routes(struct proto *P)
}
cli_msg(-1025, "%s:", p->p.name);
- cli_msg(-1025, "%-24s %-25s %-9s %6s F %5s %7s",
+ cli_msg(-1025, "%-*s %-25s %-9s %6s F %5s %7s", width,
"Prefix", "Nexthop", "Interface", "Metric", "Seqno", "Expires");
babel_show_routes_(p, &p->ip4_rtable);
@@ -2139,7 +2145,7 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
e = babel_get_entry(p, net->n.addr);
/* Activate triggered updates */
- if ((e->valid |= BABEL_ENTRY_VALID) ||
+ if ((e->valid != BABEL_ENTRY_VALID) ||
(e->router_id != rt_router_id))
{
babel_trigger_update(p);
@@ -2182,14 +2188,32 @@ babel_rte_same(struct rte *new, struct rte *old)
}
+static void
+babel_postconfig(struct proto_config *CF)
+{
+ struct babel_config *cf = (void *) CF;
+ struct channel_config *ip4, *ip6, *ip6_sadr;
+
+ ip4 = proto_cf_find_channel(CF, NET_IP4);
+ ip6 = proto_cf_find_channel(CF, NET_IP6);
+ ip6_sadr = proto_cf_find_channel(CF, NET_IP6_SADR);
+
+ if (ip6 && ip6_sadr)
+ cf_error("Both ipv6 and ipv6-sadr channels");
+
+ cf->ip4_channel = ip4;
+ cf->ip6_channel = ip6 ?: ip6_sadr;
+}
+
static struct proto *
babel_init(struct proto_config *CF)
{
struct proto *P = proto_new(CF);
struct babel_proto *p = (void *) P;
+ struct babel_config *cf = (void *) CF;
- proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4));
- proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6));
+ proto_configure_channel(P, &p->ip4_channel, cf->ip4_channel);
+ proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel);
P->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify;
@@ -2207,10 +2231,11 @@ babel_start(struct proto *P)
{
struct babel_proto *p = (void *) P;
struct babel_config *cf = (void *) P->cf;
+ u8 ip6_type = cf->ip6_channel ? cf->ip6_channel->net_type : NET_IP6;
fib_init(&p->ip4_rtable, P->pool, NET_IP4, sizeof(struct babel_entry),
OFFSETOF(struct babel_entry, n), 0, babel_init_entry);
- fib_init(&p->ip6_rtable, P->pool, NET_IP6, sizeof(struct babel_entry),
+ fib_init(&p->ip6_rtable, P->pool, ip6_type, sizeof(struct babel_entry),
OFFSETOF(struct babel_entry, n), 0, babel_init_entry);
init_list(&p->interfaces);
@@ -2258,11 +2283,15 @@ babel_reconfigure(struct proto *P, struct proto_config *CF)
{
struct babel_proto *p = (void *) P;
struct babel_config *new = (void *) CF;
+ u8 ip6_type = new->ip6_channel ? new->ip6_channel->net_type : NET_IP6;
TRACE(D_EVENTS, "Reconfiguring");
- if (!proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) ||
- !proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)))
+ if (p->ip6_rtable.addr_type != ip6_type)
+ return 0;
+
+ if (!proto_configure_channel(P, &p->ip4_channel, new->ip4_channel) ||
+ !proto_configure_channel(P, &p->ip6_channel, new->ip6_channel))
return 0;
p->p.cf = CF;
@@ -2280,9 +2309,10 @@ struct protocol proto_babel = {
.template = "babel%d",
.attr_class = EAP_BABEL,
.preference = DEF_PREF_BABEL,
- .channel_mask = NB_IP,
+ .channel_mask = NB_IP | NB_IP6_SADR,
.proto_size = sizeof(struct babel_proto),
.config_size = sizeof(struct babel_config),
+ .postconfig = babel_postconfig,
.init = babel_init,
.dump = babel_dump,
.start = babel_start,
diff --git a/proto/babel/babel.h b/proto/babel/babel.h
index 1128d261..b194ce30 100644
--- a/proto/babel/babel.h
+++ b/proto/babel/babel.h
@@ -85,7 +85,10 @@ enum babel_tlv_type {
enum babel_subtlv_type {
BABEL_SUBTLV_PAD1 = 0,
- BABEL_SUBTLV_PADN = 1
+ BABEL_SUBTLV_PADN = 1,
+
+ /* Mandatory subtlvs */
+ BABEL_SUBTLV_SOURCE_PREFIX = 128,
};
enum babel_iface_type {
@@ -109,6 +112,9 @@ struct babel_config {
struct proto_config c;
list iface_list; /* List of iface configs (struct babel_iface_config) */
uint hold_time; /* Time to hold stale entries and unreachable routes */
+
+ struct channel_config *ip4_channel;
+ struct channel_config *ip6_channel;
};
struct babel_iface_config {
@@ -303,7 +309,10 @@ struct babel_msg_update {
u16 seqno;
u16 metric;
u64 router_id;
- net_addr net;
+ union {
+ net_addr net;
+ net_addr_ip6_sadr net_sadr;
+ };
ip_addr next_hop;
ip_addr sender;
};
@@ -311,7 +320,10 @@ struct babel_msg_update {
struct babel_msg_route_request {
u8 type;
u8 full;
- net_addr net;
+ union {
+ net_addr net;
+ net_addr_ip6_sadr net_sadr;
+ };
};
struct babel_msg_seqno_request {
@@ -319,7 +331,10 @@ struct babel_msg_seqno_request {
u8 hop_count;
u16 seqno;
u64 router_id;
- net_addr net;
+ union {
+ net_addr net;
+ net_addr_ip6_sadr net_sadr;
+ };
ip_addr sender;
};
@@ -339,6 +354,8 @@ struct babel_msg_node {
union babel_msg msg;
};
+static inline int babel_sadr_enabled(struct babel_proto *p)
+{ return p->ip6_rtable.addr_type == NET_IP6_SADR; }
/* babel.c */
void babel_handle_ack_req(union babel_msg *msg, struct babel_iface *ifa);
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index dd86222a..59678678 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -105,6 +105,13 @@ struct babel_tlv_seqno_request {
u8 addr[0];
} PACKED;
+struct babel_subtlv_source_prefix {
+ u8 type;
+ u8 length;
+ u8 plen;
+ u8 addr[0];
+} PACKED;
+
/* Hello flags */
#define BABEL_HF_UNICAST 0x8000
@@ -127,6 +134,7 @@ struct babel_parse_state {
u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */
u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */
u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
+ u8 sadr_enabled;
};
enum parse_result {
@@ -237,6 +245,7 @@ static int babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *msg, stru
static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
+static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
@@ -244,6 +253,7 @@ static uint babel_write_ihu(struct babel_tlv *hdr, union babel_msg *msg, struct
static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
+static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
struct babel_tlv_data {
u8 min_length;
@@ -640,6 +650,9 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
ip6_addr prefix6 = get_ip6(buf);
net_fill_ip6(&msg->net, prefix6, tlv->plen);
+ if (state->sadr_enabled)
+ net_make_ip6_sadr(&msg->net);
+
if (tlv->flags & BABEL_UF_DEF_PREFIX)
{
put_ip6(state->def_ip6_prefix, prefix6);
@@ -770,12 +783,21 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
put_u16(&tlv->seqno, msg->seqno);
put_u16(&tlv->metric, msg->metric);
+ if (msg->net.type == NET_IP6_SADR)
+ {
+ int l = babel_write_source_prefix(hdr, &msg->net, max_len - (len0 + len));
+ if (l < 0)
+ return 0;
+
+ len += l;
+ }
+
return len0 + len;
}
static int
babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
- struct babel_parse_state *state UNUSED)
+ struct babel_parse_state *state)
{
struct babel_tlv_route_request *tlv = (void *) hdr;
struct babel_msg_route_request *msg = &m->route_request;
@@ -812,6 +834,10 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
read_ip6_px(&msg->net, tlv->addr, tlv->plen);
state->current_tlv_endpos += BYTES(tlv->plen);
+
+ if (state->sadr_enabled)
+ net_make_ip6_sadr(&msg->net);
+
return PARSE_SUCCESS;
case BABEL_AE_IP6_LL:
@@ -856,6 +882,15 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m,
put_ip6_px(tlv->addr, &msg->net);
}
+ if (msg->net.type == NET_IP6_SADR)
+ {
+ int l = babel_write_source_prefix(hdr, &msg->net, max_len - len);
+ if (l < 0)
+ return 0;
+
+ len += l;
+ }
+
return len;
}
@@ -900,6 +935,10 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
read_ip6_px(&msg->net, tlv->addr, tlv->plen);
state->current_tlv_endpos += BYTES(tlv->plen);
+
+ if (state->sadr_enabled)
+ net_make_ip6_sadr(&msg->net);
+
return PARSE_SUCCESS;
case BABEL_AE_IP6_LL:
@@ -943,38 +982,147 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
tlv->hop_count = msg->hop_count;
put_u64(&tlv->router_id, msg->router_id);
+ if (msg->net.type == NET_IP6_SADR)
+ {
+ int l = babel_write_source_prefix(hdr, &msg->net, max_len - len);
+ if (l < 0)
+ return 0;
+
+ len += l;
+ }
+
return len;
}
+static int
+babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg,
+ struct babel_parse_state *state UNUSED)
+{
+ struct babel_subtlv_source_prefix *tlv = (void *) hdr;
+ net_addr_ip6_sadr *net;
+
+ /*
+ * We would like to skip the sub-TLV if SADR is not enabled, but we do not
+ * know AF of the enclosing TLV yet. We will do that later.
+ */
+
+ /* Check internal consistency */
+ if ((tlv->length < 1) ||
+ (tlv->plen > IP6_MAX_PREFIX_LENGTH) ||
+ (tlv->length < (1 + BYTES(tlv->plen))))
+ return PARSE_ERROR;
+
+ /* Plen MUST NOT be 0 */
+ if (tlv->plen == 0)
+ return PARSE_ERROR;
+
+ switch(msg->type)
+ {
+ case BABEL_TLV_UPDATE:
+ /* Wildcard updates with source prefix MUST be silently ignored */
+ if (msg->update.wildcard)
+ return PARSE_IGNORE;
+
+ net = (void *) &msg->update.net;
+ break;
+
+ case BABEL_TLV_ROUTE_REQUEST:
+ /* Wildcard requests with source addresses MUST be silently ignored */
+ if (msg->route_request.full)
+ return PARSE_IGNORE;
+
+ net = (void *) &msg->route_request.net;
+ break;
+
+ case BABEL_TLV_SEQNO_REQUEST:
+ net = (void *) &msg->seqno_request.net;
+ break;
+
+ default:
+ return PARSE_ERROR;
+ }
+
+ /* If SADR is active, the net has appropriate type */
+ if (net->type != NET_IP6_SADR)
+ return PARSE_IGNORE;
+
+ /* Duplicate Source Prefix sub-TLV; SHOULD ignore whole TLV */
+ if (net->src_pxlen > 0)
+ return PARSE_IGNORE;
+
+ net_addr_ip6 src;
+ read_ip6_px((void *) &src, tlv->addr, tlv->plen);
+ net->src_prefix = src.prefix;
+ net->src_pxlen = src.pxlen;
+
+ return PARSE_SUCCESS;
+}
+
+static int
+babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len)
+{
+ struct babel_subtlv_source_prefix *tlv = (void *) NEXT_TLV(hdr);
+ net_addr_ip6_sadr *net = (void *) n;
+
+ /* Do not use this sub-TLV for default prefix */
+ if (net->src_pxlen == 0)
+ return 0;
+
+ uint len = sizeof(*tlv) + BYTES(net->src_pxlen);
+
+ if (len > max_len)
+ return -1;
+
+ TLV_HDR(tlv, BABEL_SUBTLV_SOURCE_PREFIX, len);
+ hdr->length += len;
+
+ net_addr_ip6 src = NET_ADDR_IP6(net->src_prefix, net->src_pxlen);
+ tlv->plen = src.pxlen;
+ put_ip6_px(tlv->addr, (void *) &src);
+
+ return len;
+}
+
+
static inline int
babel_read_subtlvs(struct babel_tlv *hdr,
- union babel_msg *msg UNUSED,
+ union babel_msg *msg,
struct babel_parse_state *state)
{
struct babel_tlv *tlv;
+ byte *pos, *end = (byte *) hdr + TLV_LENGTH(hdr);
+ int res;
for (tlv = (void *) hdr + state->current_tlv_endpos;
- (void *) tlv < (void *) hdr + TLV_LENGTH(hdr);
+ (byte *) tlv < end;
tlv = NEXT_TLV(tlv))
{
+ /* Ugly special case */
+ if (tlv->type == BABEL_TLV_PAD1)
+ continue;
+
+ /* The end of the common TLV header */
+ pos = (byte *)tlv + sizeof(struct babel_tlv);
+ if ((pos > end) || (pos + tlv->length > end))
+ return PARSE_ERROR;
+
/*
* The subtlv type space is non-contiguous (due to the mandatory bit), so
* use a switch for dispatch instead of the mapping array we use for TLVs
*/
switch (tlv->type)
{
- case BABEL_SUBTLV_PAD1:
- case BABEL_SUBTLV_PADN:
- /* FIXME: Framing errors in PADN are silently ignored, see babel_process_packet() */
+ case BABEL_SUBTLV_SOURCE_PREFIX:
+ res = babel_read_source_prefix(tlv, msg, state);
+ if (res != PARSE_SUCCESS)
+ return res;
break;
+ case BABEL_SUBTLV_PADN:
default:
/* Unknown mandatory subtlv; PARSE_IGNORE ignores the whole TLV */
- if (tlv->type > 128)
- {
- DBG("Babel: Mandatory subtlv %d found; skipping TLV\n", tlv->type);
+ if (tlv->type >= 128)
return PARSE_IGNORE;
- }
break;
}
}
@@ -1197,6 +1345,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
.ifa = ifa,
.saddr = saddr,
.next_hop_ip6 = saddr,
+ .sadr_enabled = babel_sadr_enabled(p),
};
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 1310582b..30424abb 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -118,6 +118,7 @@ struct bgp_config {
unsigned error_delay_time_min; /* Time to wait after an error is detected */
unsigned error_delay_time_max;
unsigned disable_after_error; /* Disable the protocol when error is detected */
+ u32 disable_after_cease; /* Disable it when cease is received, bitfield */
char *password; /* Password used for MD5 authentication */
int check_link; /* Use iface link state for liveness detection */
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 04e6d666..41eaa729 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -32,6 +32,12 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
%type <i32> bgp_afi
+CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
+ CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
+ OUT, OF, RESOURCES)
+
+%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag
+
CF_GRAMMAR
CF_ADDTO(proto, bgp_proto '}' )
@@ -74,6 +80,29 @@ bgp_nbr_opts:
| bgp_nbr_opts AS expr { BGP_CFG->remote_as = $3; }
;
+bgp_cease_mask:
+ /* true -> all except connection collision */
+ bool { $$ = $1 ? ~(1 << 7) : 0; }
+ | '{' bgp_cease_list '}' { $$ = $2; }
+ ;
+
+bgp_cease_list:
+ bgp_cease_flag
+ | bgp_cease_list ',' bgp_cease_flag { $$ = $1 | $3; }
+ ;
+
+bgp_cease_flag:
+ CEASE { $$ = 1 << 0; }
+ | PREFIX LIMIT HIT { $$ = 1 << 1; }
+ | ADMINISTRATIVE SHUTDOWN { $$ = 1 << 2; }
+ | PEER DECONFIGURED { $$ = 1 << 3; }
+ | ADMINISTRATIVE RESET { $$ = 1 << 4; }
+ | CONNECTION REJECTED { $$ = 1 << 5; }
+ | CONFIGURATION CHANGE { $$ = 1 << 6; }
+ | CONNECTION COLLISION { $$ = 1 << 7; }
+ | OUT OF RESOURCES { $$ = 1 << 8; }
+ ;
+
bgp_proto:
bgp_proto_start proto_name '{'
| bgp_proto proto_item ';'
@@ -117,6 +146,7 @@ bgp_proto:
| bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; }
| bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; }
| bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
+ | bgp_proto DISABLE AFTER CEASE bgp_cease_mask ';' { BGP_CFG->disable_after_cease = $5; }
| bgp_proto ENABLE ROUTE REFRESH bool ';' { BGP_CFG->enable_refresh = $5; }
| bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
| bgp_proto ENABLE EXTENDED MESSAGES bool ';' { BGP_CFG->enable_extended_messages = $5; }
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 95a974eb..aa08732d 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -2772,6 +2772,16 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, uint len)
bgp_update_startup_delay(p);
bgp_stop(p, 0, NULL, 0);
}
+ else
+ {
+ uint subcode_bit = 1 << ((subcode <= 8) ? subcode : 0);
+ if (p->cf->disable_after_cease & subcode_bit)
+ {
+ log(L_INFO "%s: Disabled after Cease notification", p->p.name);
+ p->startup_delay = 0;
+ p->p.disabled = 1;
+ }
+ }
}
static void
diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y
index 4f96fdcb..b3c332be 100644
--- a/proto/pipe/config.Y
+++ b/proto/pipe/config.Y
@@ -25,13 +25,19 @@ CF_ADDTO(proto, pipe_proto '}' { this_channel = NULL; } )
pipe_proto_start: proto_start PIPE
{
this_proto = proto_config_new(&proto_pipe, $1);
- this_channel = channel_config_new(NULL, NULL, 0, this_proto);
- this_channel->in_filter = FILTER_ACCEPT;
- this_channel->out_filter = FILTER_ACCEPT;
+}
+proto_name
+{
+ this_channel = proto_cf_main_channel(this_proto);
+ if (!this_channel) {
+ this_channel = channel_config_new(NULL, NULL, 0, this_proto);
+ this_channel->in_filter = FILTER_ACCEPT;
+ this_channel->out_filter = FILTER_ACCEPT;
+ }
};
pipe_proto:
- pipe_proto_start proto_name '{'
+ pipe_proto_start '{'
| pipe_proto proto_item ';'
| pipe_proto channel_item ';'
| pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; }
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 310f3c01..49ff52e2 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -249,6 +249,8 @@ pipe_show_stats(struct pipe_proto *p)
s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
}
+static const char *pipe_feed_state[] = { [ES_DOWN] = "down", [ES_FEEDING] = "feed", [ES_READY] = "up" };
+
static void
pipe_show_proto_info(struct proto *P)
{
@@ -257,6 +259,8 @@ pipe_show_proto_info(struct proto *P)
cli_msg(-1006, " Channel %s", "main");
cli_msg(-1006, " Table: %s", p->pri->table->name);
cli_msg(-1006, " Peer table: %s", p->sec->table->name);
+ cli_msg(-1006, " Import state: %s", pipe_feed_state[p->sec->export_state]);
+ cli_msg(-1006, " Export state: %s", pipe_feed_state[p->pri->export_state]);
cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter));
cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter));
diff --git a/sysdep/cf/linux.h b/sysdep/cf/linux.h
index 3a3a15da..047d3764 100644
--- a/sysdep/cf/linux.h
+++ b/sysdep/cf/linux.h
@@ -10,6 +10,7 @@
#define CONFIG_SELF_CONSCIOUS
#define CONFIG_MULTIPLE_TABLES
#define CONFIG_ALL_TABLES_AT_ONCE
+#define CONFIG_IP6_SADR_KERNEL
#define CONFIG_MC_PROPER_SRC
#define CONFIG_UNIX_DONTROUTE
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 4cb51519..84591eb2 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -374,6 +374,7 @@ static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = {
static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = {
[RTA_DST] = { 1, 1, sizeof(ip6_addr) },
+ [RTA_SRC] = { 1, 1, sizeof(ip6_addr) },
[RTA_IIF] = { 1, 1, sizeof(u32) },
[RTA_OIF] = { 1, 1, sizeof(u32) },
[RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) },
@@ -1221,8 +1222,18 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d
}
else
#endif
+ {
nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr));
+ /* Add source address for IPv6 SADR routes */
+ if (net->n.addr->type == NET_IP6_SADR)
+ {
+ net_addr_ip6_sadr *a = (void *) &net->n.addr;
+ nl_add_attr_ip6(&r->h, rsize, RTA_SRC, a->src_prefix);
+ r->r.rtm_src_len = a->src_pxlen;
+ }
+ }
+
/*
* Strange behavior for RTM_DELROUTE:
* 1) rtm_family is ignored in IPv6, works for IPv4
@@ -1447,12 +1458,12 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
struct rtattr *a[BIRD_RTA_MAX];
int new = h->nlmsg_type == RTM_NEWROUTE;
- net_addr dst;
+ net_addr dst, src = {};
u32 oif = ~0;
u32 table_id;
u32 priority = 0;
u32 def_scope = RT_SCOPE_UNIVERSE;
- int src;
+ int krt_src;
if (!(i = nl_checkin(h, sizeof(*i))))
return;
@@ -1477,6 +1488,11 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
net_fill_ip6(&dst, rta_get_ip6(a[RTA_DST]), i->rtm_dst_len);
else
net_fill_ip6(&dst, IP6_NONE, 0);
+
+ if (a[RTA_SRC])
+ net_fill_ip6(&src, rta_get_ip6(a[RTA_SRC]), i->rtm_src_len);
+ else
+ net_fill_ip6(&src, IP6_NONE, 0);
break;
#ifdef HAVE_MPLS_KERNEL
@@ -1511,6 +1527,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
if (!p)
SKIP("unknown table %d\n", table);
+ if (a[RTA_SRC] && (p->p.net_type != NET_IP6_SADR))
+ SKIP("src prefix for non-SADR channel\n");
+
if (a[RTA_IIF])
SKIP("IIF set\n");
@@ -1533,25 +1552,33 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
SKIP("proto unspec\n");
case RTPROT_REDIRECT:
- src = KRT_SRC_REDIRECT;
+ krt_src = KRT_SRC_REDIRECT;
break;
case RTPROT_KERNEL:
- src = KRT_SRC_KERNEL;
+ krt_src = KRT_SRC_KERNEL;
return;
case RTPROT_BIRD:
if (!s->scan)
SKIP("echo\n");
- src = KRT_SRC_BIRD;
+ krt_src = KRT_SRC_BIRD;
break;
case RTPROT_BOOT:
default:
- src = KRT_SRC_ALIEN;
+ krt_src = KRT_SRC_ALIEN;
}
- net *net = net_get(p->p.main_channel->table, &dst);
+ net_addr *n = &dst;
+ if (p->p.net_type == NET_IP6_SADR)
+ {
+ n = alloca(sizeof(net_addr_ip6_sadr));
+ net_fill_ip6_sadr(n, net6_prefix(&dst), net6_pxlen(&dst),
+ net6_prefix(&src), net6_pxlen(&src));
+ }
+
+ net *net = net_get(p->p.main_channel->table, n);
if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type))
nl_announce_route(s);
@@ -1755,7 +1782,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
s->attrs = ra;
s->proto = p;
s->new = new;
- s->krt_src = src;
+ s->krt_src = krt_src;
s->krt_type = i->rtm_type;
s->krt_proto = i->rtm_protocol;
s->krt_metric = priority;
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index cd2558b2..012deaf0 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -958,10 +958,6 @@ sk_setup(sock *s)
#endif
}
- if (s->priority >= 0)
- if (sk_set_priority(s, s->priority) < 0)
- return -1;
-
if (sk_is_ipv4(s))
{
if (s->flags & SKF_LADDR_RX)
@@ -1012,6 +1008,11 @@ sk_setup(sock *s)
return -1;
}
+ /* Must be after sk_set_tos4() as setting ToS on Linux also mangles priority */
+ if (s->priority >= 0)
+ if (sk_set_priority(s, s->priority) < 0)
+ return -1;
+
return 0;
}
diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y
index 1cb28389..9aac8668 100644
--- a/sysdep/unix/krt.Y
+++ b/sysdep/unix/krt.Y
@@ -17,16 +17,6 @@ CF_DEFINES
#define KIF_IFACE ((struct kif_iface_config *) this_ipatt)
static void
-krt_set_merge_paths(struct channel_config *cc, uint merge, uint limit)
-{
- if ((limit <= 0) || (limit > 255))
- cf_error("Merge paths limit must be in range 1-255");
-
- cc->ra_mode = merge ? RA_MERGED : RA_OPTIMAL;
- cc->merge_limit = limit;
-}
-
-static void
kif_set_preferred(ip_addr ip)
{
if (ipa_is_ip4(ip))
@@ -78,10 +68,9 @@ kern_item:
cf_error("Learning of kernel routes not supported on this platform");
#endif
}
- | DEVICE ROUTES bool { THIS_KRT->devroutes = $3; }
| GRACEFUL RESTART bool { THIS_KRT->graceful_restart = $3; }
| MERGE PATHS bool kern_mp_limit {
- krt_set_merge_paths(this_channel, $3, $4);
+ THIS_KRT->merge_paths = $3 ? $4 : 0;
#ifndef KRT_ALLOW_MERGE_PATHS
if ($3)
cf_error("Path merging not supported on this platform");
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index 29d2d01e..b4fb1967 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -506,7 +506,13 @@ static void
krt_learn_init(struct krt_proto *p)
{
if (KRT_CF->learn)
- rt_setup(p->p.pool, &p->krt_table, "Inherited", NULL);
+ {
+ struct rtable_config *cf = mb_allocz(p->p.pool, sizeof(struct rtable_config));
+ cf->name = "Inherited";
+ cf->addr_type = p->p.net_type;
+
+ rt_setup(p->p.pool, &p->krt_table, cf);
+ }
}
static void
@@ -578,7 +584,7 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa)
if (filter == FILTER_ACCEPT)
goto accept;
- if (f_run(filter, &rt, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) > F_ACCEPT)
+ if (f_run(filter, &rt, tmpa, krt_filter_lp, FF_FORCE_TMPATTR | FF_SILENT) > F_ACCEPT)
goto reject;
@@ -1059,11 +1065,18 @@ krt_postconfig(struct proto_config *CF)
cf_error("All kernel syncers must use the same table scan interval");
#endif
- struct rtable_config *tab = proto_cf_main_channel(CF)->table;
+ struct channel_config *cc = proto_cf_main_channel(CF);
+ struct rtable_config *tab = cc->table;
if (tab->krt_attached)
cf_error("Kernel syncer (%s) already attached to table %s", tab->krt_attached->name, tab->name);
tab->krt_attached = CF;
+ if (cf->merge_paths)
+ {
+ cc->ra_mode = RA_MERGED;
+ cc->merge_limit = cf->merge_paths;
+ }
+
krt_sys_postconfig(cf);
}
@@ -1095,10 +1108,11 @@ krt_start(struct proto *P)
switch (p->p.net_type)
{
- case NET_IP4: p->af = AF_INET; break;
- case NET_IP6: p->af = AF_INET6; break;
+ case NET_IP4: p->af = AF_INET; break;
+ case NET_IP6: p->af = AF_INET6; break;
+ case NET_IP6_SADR: p->af = AF_INET6; break;
#ifdef AF_MPLS
- case NET_MPLS: p->af = AF_MPLS; break;
+ case NET_MPLS: p->af = AF_MPLS; break;
#endif
default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break;
}
@@ -1159,7 +1173,7 @@ krt_reconfigure(struct proto *p, struct proto_config *CF)
return 0;
/* persist, graceful restart need not be the same */
- return o->scan_time == n->scan_time && o->learn == n->learn && o->devroutes == n->devroutes;
+ return o->scan_time == n->scan_time && o->learn == n->learn;
}
struct proto_config *
@@ -1206,16 +1220,24 @@ krt_get_attr(eattr *a, byte *buf, int buflen)
}
+#ifdef CONFIG_IP6_SADR_KERNEL
+#define MAYBE_IP6_SADR NB_IP6_SADR
+#else
+#define MAYBE_IP6_SADR 0
+#endif
+
+#ifdef HAVE_MPLS_KERNEL
+#define MAYBE_MPLS NB_MPLS
+#else
+#define MAYBE_MPLS 0
+#endif
+
struct protocol proto_unix_kernel = {
.name = "Kernel",
.template = "kernel%d",
.attr_class = EAP_KRT,
.preference = DEF_PREF_INHERITED,
-#ifdef HAVE_MPLS_KERNEL
- .channel_mask = NB_IP | NB_MPLS,
-#else
- .channel_mask = NB_IP,
-#endif
+ .channel_mask = NB_IP | MAYBE_IP6_SADR | MAYBE_MPLS,
.proto_size = sizeof(struct krt_proto),
.config_size = sizeof(struct krt_config),
.preconfig = krt_preconfig,
diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h
index 3bfccfc2..b627882d 100644
--- a/sysdep/unix/krt.h
+++ b/sysdep/unix/krt.h
@@ -49,8 +49,8 @@ struct krt_config {
btime scan_time; /* How often we re-scan routes */
int persist; /* Keep routes when we exit */
int learn; /* Learn routes from other sources */
- int devroutes; /* XXX: remove */
int graceful_restart; /* Regard graceful restart recovery */
+ int merge_paths; /* Exported routes are merged for ECMP */
};
struct krt_proto {