diff --git a/NEWS b/NEWS
index 26314d82..6281c8a3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,26 @@
+Version 2.0.0-pre0 (2016-12-07)
+ o Integrated IPv4 + IPv6 design
+ o Major BGP protocol redesign
+ o BGP multicast support (SAFI 2)
+ o BGP flowspec support (RFC 5575)
+ o New RPKI-Router protocol
+ o New build system
+ o Unit tests
+
+ Notes:
+
+ Protocols and tables are now connected using explicit channels, most related
+ protocol options (table, import, export, ...) are now channel options. See
+ doc/bird.conf.example2 for configuration examples.
+
+
+Version 1.6.3 (2016-12-21)
+ o Large BGP communities
+ o BFD authentication (MD5, SHA1)
+ o SHA1 and SHA2 authentication for RIP and OSPF
+ o Improved documentation
+ o Several bug fixes
+
Version 1.6.2 (2016-09-29)
o Fixes serious bug introduced in the previous version
diff --git a/configure.in b/configure.in
index d5ddb2a5..af9c452d 100644
--- a/configure.in
+++ b/configure.in
@@ -186,8 +186,7 @@ fi
AC_SUBST(iproutedir)
-# all_protocols="$proto_bfd babel bgp ospf pipe radv rip $proto_rpki static"
-all_protocols="$proto_bfd bgp ospf pipe radv rip $proto_rpki static "
+all_protocols="$proto_bfd babel bgp ospf pipe radv rip $proto_rpki static"
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
diff --git a/doc/bird.conf.example b/doc/bird.conf.example
index bbfe0020..62c65ce9 100644
--- a/doc/bird.conf.example
+++ b/doc/bird.conf.example
@@ -1,5 +1,6 @@
/*
- * This is an example configuration file.
+ * This is an example configuration file
+ * (for version 1.x.x, obsolete)
*/
# Yes, even shell-like comments work...
diff --git a/doc/bird.conf.example2 b/doc/bird.conf.example2
new file mode 100644
index 00000000..6e8988b9
--- /dev/null
+++ b/doc/bird.conf.example2
@@ -0,0 +1,267 @@
+/*
+ * This is an example configuration file for MB-BGP setting
+ */
+
+
+log "bird.log" all;
+# debug protocols all;
+
+router id 192.168.1.1;
+
+ipv4 table master4;
+ipv6 table master6;
+
+ipv4 table mcast4;
+ipv6 table mcast6;
+
+flow4 table flowtab4;
+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;
+ };
+}
+
+
+protocol static static4 {
+ ipv4;
+
+ route 10.10.0.0/24 via 192.168.1.2;
+ route 10.10.1.0/24 via 192.168.1.2 { bgp_large_community.add((10,20,30)); bgp_large_community.add((10,(20*3),10)); };
+}
+
+protocol static static6 {
+ ipv6;
+
+ route 2001:db8:10:10::/64 via 2001:db8:1:1::10;
+ route 2001:db8:10:11::/64 via 2001:db8:1:1::10;
+
+ route 2001:db8:1:1::/64 via fe80::ec9b:67ff:fe60:fd5d % ve1;
+}
+
+# RFC 5575 flow specification
+protocol static flowstat4 {
+ flow4;
+
+ route flow4 {
+ dst 10.0.0.0/8;
+ proto = 23;
+ dport > 24 && < 30 || 40..50,60..70,80;
+ sport > 24 && < 30 || = 40 || 50,60..70,80;
+ icmp type 80;
+ icmp code 90;
+ tcp flags 0x03/0x0f;
+ length 2048..65535;
+ dscp = 63;
+ fragment dont_fragment, is_fragment || !first_fragment;
+ } drop;
+
+ route flow4 {
+ dst 11.0.0.0/8;
+ proto = 0x12;
+ sport > 0x5678 && < 0x9abc || 0xdef0 || 0x1234,0x5678,0x9abc..0xdef0;
+ dport = 50;
+ tcp flags 0xabcd/0xbbdd;
+ } drop;
+
+ route flow4 {
+ dst 12.0.0.0/32;
+ tcp flags ! 0 / 0x9999;
+ } drop;
+
+ route flow4 {
+ dst 220.0.254.0/24;
+ tcp flags 0x99 / 0x9999;
+ } drop;
+
+ route flow4 {
+ dst 220.0.254.192/28;
+ tcp flags !0xffff / 0xFFFF;
+ } drop;
+
+ route flow4 {
+ dst 15.0.0.0/8;
+ tcp flags !0x9999/0x9999;
+ } drop;
+}
+
+protocol static flowstat6 {
+ flow6;
+
+ route flow6 {
+ dst fec0:1122:3344:5566::1/128;
+ src 0000:0000:0000:0001:1234:5678:9800:0000/101 offset 63;
+ next header = 23;
+ sport 24..30, 42 || 50,60,70..80;
+ dport = 50;
+ tcp flags 0x03/0x0f, !0/0xff || 0x33/0x33;
+ fragment !is_fragment || !first_fragment;
+ label 0xaaaa/0xaaaa && 0x33/0x33;
+ } drop;
+
+ route flow6 {
+ dst fec0:1122:3344:5566::1/128;
+ src ::1:1234:5678:9800:0/101 offset 63;
+ next header = 23;
+ dport = 50;
+ sport > 24 && < 30 || = 40 || = 50 || = 60 || >= 70 && <= 80;
+ tcp flags 0x3/0x3 && 0x0/0xc;
+ } drop;
+}
+
+
+protocol pipe {
+ table master4;
+ peer table mcast4;
+ import none;
+ export where source = RTS_OSPF;
+}
+
+protocol pipe {
+ table master6;
+ peer table mcast6;
+ import none;
+ export where source = RTS_OSPF;
+}
+
+protocol ospf2 ospf4 {
+# ecmp;
+
+ ipv4 {
+ import all;
+# export where source = RTS_STATIC;
+ };
+
+ area 0 {
+ interface "ve0" { stub; };
+ interface "ve1" { hello 5; type ptp; };
+ interface "ve2" { hello 5; type bcast; ttl security; };
+ interface "ve3" { hello 5; type bcast; ttl security; };
+ };
+}
+
+
+protocol ospf3 ospf6 {
+# ecmp;
+
+ ipv6 {
+ import all;
+# export where source = RTS_STATIC;
+ };
+
+ area 0 {
+ interface "ve0" { stub; };
+ interface "ve1" { hello 5; type ptp; };
+ interface "ve2" { hello 5; type bcast; };
+ };
+}
+
+protocol bgp {
+ local 192.168.11.1 as 1000;
+ neighbor 192.168.11.2 as 2000;
+# local 192.168.1.1 as 1000;
+# neighbor 192.168.2.1 as 2000;
+# multihop;
+# rr client;
+# strict bind;
+# debug all;
+
+ # regular IPv4 unicast (1/1)
+ ipv4 {
+ # connects to master4 table by default
+ import all;
+ export where source ~ [ RTS_STATIC, RTS_BGP ];
+ };
+
+ # regular IPv6 unicast (2/1)
+ ipv6 {
+ # connects to master6 table by default
+ import all;
+ export where source ~ [ RTS_STATIC, RTS_BGP ];
+# next hop address 2001:db8:1:1::1;
+ };
+
+ # IPv4 multicast topology (1/2)
+ ipv4 multicast {
+ # explicit IPv4 table
+ table mcast4;
+ import all;
+ export all;
+ };
+
+ # IPv6 multicast topology (2/2)
+ ipv6 multicast {
+ # explicit IPv6 table
+ table mcast6;
+ import all;
+ export all;
+# next hop address 2001:db8:1:1::1;
+ };
+
+ # IPv4 Flowspec (1/133)
+ flow4 {
+ # connects to flowtab4 table by default
+ import all;
+ export all;
+ };
+
+ # IPv6 Flowspec (2/133)
+ flow6 {
+ # connects to flowtab6 table by default
+ import all;
+ export all;
+ };
+}
+
+protocol bgp {
+ local 192.168.1.1 as 1000;
+ neighbor 192.168.3.1 as 1000;
+ multihop;
+ rr client;
+
+ ipv4 {
+ import all;
+ export where source ~ [ RTS_STATIC, RTS_BGP ];
+ };
+
+ ipv6 {
+ import all;
+ export where source ~ [ RTS_STATIC, RTS_BGP ];
+ next hop address 2001:db8:1:1::1;
+ };
+}
+
+protocol bgp {
+ local 2001:db8:1:1::1 as 1000;
+ neighbor 2001:db8:4:1::1 as 1000;
+ multihop;
+ rr client;
+
+ ipv4 {
+ import all;
+ export where source ~ [ RTS_STATIC, RTS_BGP ];
+ next hop address 192.168.4.1;
+ };
+
+ ipv6 {
+ import all;
+ export where source ~ [ RTS_STATIC, RTS_BGP ];
+ };
+}
+
diff --git a/doc/bird.sgml b/doc/bird.sgml
index c49e7e8e..2f8f18f3 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -1945,12 +1945,11 @@ avoid routing loops.
BIRD supports all requirements of the BGP4 standard as defined in
It also supports the community attributes (),
-capability negotiation (), MD5 password authentication (), extended communities (), route reflectors (), graceful restart (), multiprotocol extensions
-(), 4B AS numbers (), and 4B AS numbers in
-extended communities ().
-
+capability negotiation (), MD5 password authentication
+(), extended communities (), route reflectors
+(), AS confederations (), graceful restart
+(), multiprotocol extensions (), 4B AS numbers
+(), and 4B AS numbers in extended communities ().
For IPv6, it uses the standard multiprotocol extensions defined in
and applied to IPv6 according to .
@@ -2041,6 +2040,16 @@ using the following configuration parameters:
source address for the BGP session. Default: the address of the local
end of the interface our neighbor is connected to.
+
Avoid calculation of the Next Hop attribute and always advertise our own
source address as a next hop. This needs to be used only occasionally to
@@ -2113,7 +2122,7 @@ using the following configuration parameters:
Note that full (ICMP protection, for example) support is
provided by Linux only. Default: disabled.
- password
+ password
Use this password for MD5 authentication of BGP sessions (). When
used on BSD systems, see also confederation
+ BGP confederations () are collections of autonomous
+ systems that act as one entity to external systems, represented by one
+ confederation identifier (instead of AS numbers). This option allows to
+ enable BGP confederation behavior and to specify the local confederation
+ identifier. When BGP confederations are used, all BGP speakers that are
+ members of the BGP confederation should have the same confederation
+ identifier configured. Default: 0 (no confederation).
+
+ confederation member
+ When BGP confederations are used, this option allows to specify whether
+ the BGP neighbor is a member of the same confederation as the local BGP
+ speaker. The option is unnecessary (and ignored) for IBGP sessions, as
+ the same AS number implies the same confederation. Default: no.
+
rr client
Be a route reflector and treat the neighbor as a route reflection
client. Default: disabled.
@@ -2256,13 +2280,6 @@ using the following configuration parameters:
This option is relevant to IPv4 mode with enabled capability
advertisement only. Default: on.
- route limit
- The maximal number of routes that may be imported from the protocol. If
- the route limit is exceeded, the connection is closed with an error.
- Limit is currently implemented as import limit . This option is obsolete and it is replaced by
- [. Default: no limit.
-
disable after error
When an error is encountered (either locally or by the other side),
disable the instance automatically and wait for an administrator to fix
diff --git a/filter/filter.c b/filter/filter.c
index bc80997f..79a594bf 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -1578,6 +1578,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case P('<','='): TWOARGS; break;
case '!': ONEARG; break;
+ case P('!', '~'):
case '~': TWOARGS; break;
case P('d','e'): ONEARG; break;
diff --git a/lib/flowspec.c b/lib/flowspec.c
index b72bc7fc..ea55b736 100644
--- a/lib/flowspec.c
+++ b/lib/flowspec.c
@@ -957,7 +957,7 @@ fragment_val_str(u8 val)
return "???";
}
-static int
+static uint
net_format_flow(char *buf, uint blen, const byte *data, uint dlen, int ipv6)
{
buffer b = {
@@ -1125,7 +1125,7 @@ net_format_flow(char *buf, uint blen, const byte *data, uint dlen, int ipv6)
* of written chars. If final string is too large, the string will ends the with
* ' ...}' sequence and zero-terminator.
*/
-int
+uint
flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f)
{
return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow4), 0);
@@ -1141,7 +1141,7 @@ flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f)
* of written chars. If final string is too large, the string will ends the with
* ' ...}' sequence and zero-terminator.
*/
-int
+uint
flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f)
{
return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow6), 1);
diff --git a/lib/flowspec.h b/lib/flowspec.h
index 57809bec..9150ca91 100644
--- a/lib/flowspec.h
+++ b/lib/flowspec.h
@@ -42,6 +42,9 @@ const char *flow_type_str(enum flow_type type, int ipv6);
uint flow_write_length(byte *data, u16 len);
+static inline u16 flow_hdr_length(const byte *data)
+{ return ((*data & 0xf0) == 0xf0) ? 2 : 1; }
+
static inline u16 flow_read_length(const byte *data)
{ return ((*data & 0xf0) == 0xf0) ? get_u16(data) & 0x0fff : *data; }
@@ -128,7 +131,7 @@ void flow6_validate_cf(net_addr_flow6 *f);
* Net Formatting
*/
-int flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f);
-int flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f);
+uint flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f);
+uint flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f);
#endif /* _BIRD_FLOWSPEC_H_ */
diff --git a/lib/flowspec_test.c b/lib/flowspec_test.c
index 93364dfe..36336104 100644
--- a/lib/flowspec_test.c
+++ b/lib/flowspec_test.c
@@ -30,20 +30,17 @@ t_read_length(void)
{
byte data[] = { 0xcc, 0xcc, 0xcc };
- u16 get;
- u16 expect;
-
for (uint expect = 0; expect < 0xf0; expect++)
{
*data = expect;
- get = flow_read_length(data);
+ uint get = flow_read_length(data);
bt_assert_msg(get == expect, "Testing get length 0x%02x (get 0x%02x)", expect, get);
}
for (uint expect = 0; expect <= 0xfff; expect++)
{
put_u16(data, expect | 0xf000);
- get = flow_read_length(data);
+ uint get = flow_read_length(data);
bt_assert_msg(get == expect, "Testing get length 0x%03x (get 0x%03x)", expect, get);
}
@@ -54,12 +51,10 @@ static int
t_write_length(void)
{
byte data[] = { 0xcc, 0xcc, 0xcc };
- uint offset;
- byte *c;
for (uint expect = 0; expect <= 0xfff; expect++)
{
- offset = flow_write_length(data, expect);
+ uint offset = flow_write_length(data, expect);
uint set = (expect < 0xf0) ? *data : (get_u16(data) & 0x0fff);
bt_assert_msg(set == expect, "Testing set length 0x%03x (set 0x%03x)", expect, set);
diff --git a/lib/hash.h b/lib/hash.h
index b37d8fa5..97d8f69c 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -25,6 +25,12 @@
(v).data = mb_allocz(pool, HASH_SIZE(v) * sizeof(* (v).data)); \
})
+#define HASH_FREE(v) \
+ ({ \
+ mb_free((v).data); \
+ (v) = (typeof(v)){ }; \
+ })
+
#define HASH_FIND(v,id,key...) \
({ \
u32 _h = HASH_FN(v, id, key); \
diff --git a/misc/bird.spec b/misc/bird.spec
index ebc0c942..1da0c813 100644
--- a/misc/bird.spec
+++ b/misc/bird.spec
@@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon
Name: bird
-Version: 1.6.2
+Version: 2.0.0
Release: 1
Copyright: GPL
Group: Networking/Daemons
diff --git a/nest/a-path.c b/nest/a-path.c
index 6ced703d..32ffc72c 100644
--- a/nest/a-path.c
+++ b/nest/a-path.c
@@ -25,7 +25,7 @@
#define BAD(DSC, VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
int
-as_path_valid(byte *data, uint len, int bs, char *err, uint elen)
+as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen)
{
byte *pos = data;
char *err_dsc = NULL;
@@ -43,9 +43,21 @@ as_path_valid(byte *data, uint len, int bs, char *err, uint elen)
if (len < slen)
BAD("segment framing error", len);
- /* XXXX handle CONFED segments */
- if ((type != AS_PATH_SET) && (type != AS_PATH_SEQUENCE))
+ switch (type)
+ {
+ case AS_PATH_SET:
+ case AS_PATH_SEQUENCE:
+ break;
+
+ case AS_PATH_CONFED_SEQUENCE:
+ case AS_PATH_CONFED_SET:
+ if (!confed)
+ BAD("AS_CONFED* segment", type);
+ break;
+
+ default:
BAD("unknown segment", type);
+ }
if (pos[1] == 0)
BAD("zero-length segment", type);
@@ -157,10 +169,13 @@ as_path_contains_confed(const struct adata *path)
return 0;
}
-static void
-as_path_strip_confed_(byte *dst, const byte *src, uint len)
+struct adata *
+as_path_strip_confed(struct linpool *pool, const struct adata *path)
{
- const byte *end = src + len;
+ struct adata *res = lp_alloc_adata(pool, path->length);
+ const byte *src = path->data;
+ const byte *end = src + path->length;
+ byte *dst = res->data;
while (src < end)
{
@@ -176,18 +191,15 @@ as_path_strip_confed_(byte *dst, const byte *src, uint len)
src += slen;
}
+
+ /* Fix the result length */
+ res->length = dst - res->data;
+
+ return res;
}
struct adata *
-as_path_strip_confed(struct linpool *pool, const struct adata *op)
-{
- struct adata *np = lp_alloc_adata(pool, op->length);
- as_path_strip_confed_(np->data, op->data, op->length);
- return np;
-}
-
-struct adata *
-as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as, int strip)
+as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as)
{
struct adata *np;
const byte *pos = op->data;
@@ -218,10 +230,7 @@ as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as,
{
byte *dst = np->data + 2 + BS * np->data[1];
- if (strip)
- as_path_strip_confed_(dst, pos, len);
- else
- memcpy(dst, pos, len);
+ memcpy(dst, pos, len);
}
return np;
@@ -325,46 +334,49 @@ as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2)
}
void
-as_path_format(const struct adata *path, byte *buf, uint size)
+as_path_format(const struct adata *path, byte *bb, uint size)
{
- const byte *p = path->data;
- const byte *e = p + path->length;
- byte *end = buf + size - 16;
- int sp = 1;
- int l, isset;
+ buffer buf = { .start = bb, .pos = bb, .end = bb + size }, *b = &buf;
+ const byte *pos = path->data;
+ const byte *end = pos + path->length;
+ const char *ops, *cls;
- while (p < e)
+ b->pos[0] = 0;
+
+ while (pos < end)
+ {
+ uint type = pos[0];
+ uint len = pos[1];
+ pos += 2;
+
+ switch (type)
{
- if (buf > end)
- {
- strcpy(buf, " ...");
- return;
- }
- isset = (*p++ == AS_PATH_SET);
- l = *p++;
- if (isset)
- {
- if (!sp)
- *buf++ = ' ';
- *buf++ = '{';
- sp = 0;
- }
- while (l-- && buf <= end)
- {
- if (!sp)
- *buf++ = ' ';
- buf += bsprintf(buf, "%u", get_as(p));
- p += BS;
- sp = 0;
- }
- if (isset)
- {
- *buf++ = ' ';
- *buf++ = '}';
- sp = 0;
- }
+ case AS_PATH_SET: ops = "{"; cls = "}"; break;
+ case AS_PATH_SEQUENCE: ops = NULL; cls = NULL; break;
+ case AS_PATH_CONFED_SEQUENCE: ops = "("; cls = ")"; break;
+ case AS_PATH_CONFED_SET: ops = "({"; cls = "})"; break;
+ default: bug("Invalid path segment");
}
- *buf = 0;
+
+ if (ops)
+ buffer_puts(b, ops);
+
+ while (len--)
+ {
+ buffer_print(b, len ? "%u " : "%u", get_as(pos));
+ pos += BS;
+ }
+
+ if (cls)
+ buffer_puts(b, cls);
+
+ if (pos < end)
+ buffer_puts(b, " ");
+ }
+
+ /* Handle overflow */
+ if (b->pos == b->end)
+ strcpy(b->end - 12, "...");
}
int
@@ -399,66 +411,80 @@ as_path_getlen(const struct adata *path)
int
as_path_get_last(const struct adata *path, u32 *orig_as)
{
+ const byte *pos = path->data;
+ const byte *end = pos + path->length;
int found = 0;
- u32 res = 0;
- const u8 *p = path->data;
- const u8 *q = p+path->length;
- int len;
+ u32 val = 0;
- while (p]data;
- const u8 *q = p+path->length;
- u32 res = 0;
- int len;
+ const byte *pos = path->data;
+ const byte *end = pos + path->length;
+ u32 val = 0;
- while (plength == 0) || (p[0] != AS_PATH_SEQUENCE) || (p[1] == 0))
return 0;
- else
+
+ *last_as = get_as(p+2);
+ return 1;
+}
+
+int
+as_path_get_first_regular(const struct adata *path, u32 *last_as)
+{
+ const byte *pos = path->data;
+ const byte *end = pos + path->length;
+
+ while (pos < end)
+ {
+ uint type = pos[0];
+ uint len = pos[1];
+ pos += 2;
+
+ switch (type)
{
- *last_as = get_as(p+2);
+ case AS_PATH_SET:
+ return 0;
+
+ case AS_PATH_SEQUENCE:
+ if (len == 0)
+ return 0;
+
+ *last_as = get_as(pos);
return 1;
+
+ case AS_PATH_CONFED_SEQUENCE:
+ case AS_PATH_CONFED_SET:
+ break;
+
+ default:
+ bug("Invalid path segment");
}
+
+ pos += BS * len;
+ }
+
+ return 0;
}
int
@@ -597,43 +659,50 @@ struct pm_pos
};
static int
-parse_path(const struct adata *path, struct pm_pos *pos)
+parse_path(const struct adata *path, struct pm_pos *pp)
{
- const u8 *p = path->data;
- const u8 *q = p + path->length;
- struct pm_pos *opos = pos;
- int i, len;
+ const byte *pos = path->data;
+ const byte *end = pos + path->length;
+ struct pm_pos *op = pp;
+ uint i;
+ while (pos < end)
+ {
+ uint type = pos[0];
+ uint len = pos[1];
+ pos += 2;
- while (p < q)
- switch (*p++)
+ switch (type)
+ {
+ case AS_PATH_SET:
+ case AS_PATH_CONFED_SET:
+ pp->set = 1;
+ pp->mark = 0;
+ pp->val.sp = pos - 1;
+ pp++;
+
+ pos += BS * len;
+ break;
+
+ case AS_PATH_SEQUENCE:
+ case AS_PATH_CONFED_SEQUENCE:
+ for (i = 0; i < len; i++)
{
- case AS_PATH_SET:
- pos->set = 1;
- pos->mark = 0;
- pos->val.sp = p;
- len = *p;
- p += 1 + BS * len;
- pos++;
- break;
-
- case AS_PATH_SEQUENCE:
- len = *p++;
- for (i = 0; i < len; i++)
- {
- pos->set = 0;
- pos->mark = 0;
- pos->val.asn = get_as(p);
- p += BS;
- pos++;
- }
- break;
+ pp->set = 0;
+ pp->mark = 0;
+ pp->val.asn = get_as(pos);
+ pp++;
- default:
- bug("as_path_match: Invalid path component");
+ pos += BS;
}
+ break;
- return pos - opos;
+ default:
+ bug("Invalid path segment");
+ }
+ }
+
+ return pp - op;
}
static int
@@ -680,7 +749,7 @@ pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
}
/* AS path matching is nontrivial. Because AS path can
- * contain sets, it is not a plain wildcard matching. A set
+ * contain sets, it is not a plain wildcard matching. A set
* in an AS path is interpreted as it might represent any
* sequence of AS numbers from that set (possibly with
* repetitions). So it is also a kind of a pattern,
@@ -718,7 +787,7 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
l = h = 0;
pos[0].mark = 1;
-
+
while (mask)
{
/* We remove this mark to not step after pos[plen] */
diff --git a/nest/a-path_test.c b/nest/a-path_test.c
index 289b2df6..fbf0f892 100644
--- a/nest/a-path_test.c
+++ b/nest/a-path_test.c
@@ -85,15 +85,19 @@ t_path_format(void)
bt_debug("Prepending ASN: %10u \n", i);
}
-#define BUFFER_SIZE 26
+#define BUFFER_SIZE 120
byte buf[BUFFER_SIZE] = {};
+
+ as_path_format(&empty_as_path, buf, BUFFER_SIZE);
+ bt_assert_msg(strcmp(buf, "") == 0, "Buffer(%zu): '%s'", strlen(buf), buf);
+
as_path_format(as_path, buf, BUFFER_SIZE);
- bt_assert_msg(strcmp(buf, "4294967294 4294967293 ...") == 0, "Buffer(%zu): '%s'", strlen(buf), buf);
+ bt_assert_msg(strcmp(buf, "4294967294 4294967293 4294967292 4294967291 4294967290 4294967289 4294967288 4294967287 4294967286 4294967285") == 0, "Buffer(%zu): '%s'", strlen(buf), buf);
#define SMALL_BUFFER_SIZE 25
byte buf2[SMALL_BUFFER_SIZE] = {};
as_path_format(as_path, buf2, SMALL_BUFFER_SIZE);
- bt_assert_msg(strcmp(buf2, "4294967294 ...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
+ bt_assert_msg(strcmp(buf2, "4294967294 42...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
rfree(lp);
diff --git a/nest/a-set.c b/nest/a-set.c
index 82bf8b0d..a2fb6953 100644
--- a/nest/a-set.c
+++ b/nest/a-set.c
@@ -233,6 +233,26 @@ lc_set_contains(struct adata *list, lcomm val)
return 0;
}
+struct adata *
+int_set_prepend(struct linpool *pool, struct adata *list, u32 val)
+{
+ struct adata *res;
+ int len;
+
+ if (int_set_contains(list, val))
+ return list;
+
+ len = list ? list->length : 0;
+ res = lp_alloc(pool, sizeof(struct adata) + len + 4);
+ res->length = len + 4;
+
+ if (list)
+ memcpy(res->data + 4, list->data, list->length);
+
+ * (u32 *) res->data = val;
+
+ return res;
+}
struct adata *
int_set_add(struct linpool *pool, struct adata *list, u32 val)
@@ -250,8 +270,7 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val)
if (list)
memcpy(res->data, list->data, list->length);
- u32 *c = (u32 *) (res->data + len);
- *c = val;
+ * (u32 *) (res->data + len) = val;
return res;
}
diff --git a/nest/attrs.h b/nest/attrs.h
index 810ff583..f66d4f04 100644
--- a/nest/attrs.h
+++ b/nest/attrs.h
@@ -30,13 +30,13 @@
struct f_tree;
-int as_path_valid(byte *data, uint len, int bs, char *err, uint elen);
+int as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen);
int as_path_16to32(byte *dst, byte *src, uint len);
int as_path_32to16(byte *dst, byte *src, uint len);
int as_path_contains_as4(const struct adata *path);
int as_path_contains_confed(const struct adata *path);
struct adata *as_path_strip_confed(struct linpool *pool, const struct adata *op);
-struct adata *as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as, int strip);
+struct adata *as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as);
struct adata *as_path_to_old(struct linpool *pool, const struct adata *path);
void as_path_cut(struct adata *path, uint num);
struct adata *as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2);
@@ -44,6 +44,7 @@ void as_path_format(const struct adata *path, byte *buf, uint size);
int as_path_getlen(const struct adata *path);
int as_path_getlen_int(const struct adata *path, int bs);
int as_path_get_first(const struct adata *path, u32 *orig_as);
+int as_path_get_first_regular(const struct adata *path, u32 *last_as);
int as_path_get_last(const struct adata *path, u32 *last_as);
u32 as_path_get_last_nonaggregated(const struct adata *path);
int as_path_contains(const struct adata *path, u32 as, int min);
@@ -51,7 +52,7 @@ int as_path_match_set(const struct adata *path, struct f_tree *set);
struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos);
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
-{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as, 0); }
+{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); }
#define PM_ASN 0
@@ -180,6 +181,7 @@ int lc_set_format(struct adata *set, int from, byte *buf, uint size);
int int_set_contains(struct adata *list, u32 val);
int ec_set_contains(struct adata *list, u64 val);
int lc_set_contains(struct adata *list, lcomm val);
+struct adata *int_set_prepend(struct linpool *pool, struct adata *list, u32 val);
struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val);
struct adata *ec_set_add(struct linpool *pool, struct adata *list, u64 val);
struct adata *lc_set_add(struct linpool *pool, struct adata *list, lcomm val);
diff --git a/nest/proto.c b/nest/proto.c
index 815d0652..0a7a32a6 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -490,7 +490,7 @@ int
channel_reconfigure(struct channel *c, struct channel_config *cf)
{
/* FIXME: better handle these changes, also handle in_keep_filtered */
- if ((c->table != cf->table->table) || (c->ra_mode != cf->ra_mode))
+ if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode)))
return 0;
int import_changed = !filter_same(c->in_filter, cf->in_filter);
diff --git a/nest/route.h b/nest/route.h
index 1e0a14bc..c16b2643 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -258,6 +258,7 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED);
/* Types of route announcement, also used as flags */
+#define RA_UNDEF 0 /* Undefined RA type */
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
#define RA_ACCEPTED 2 /* Announcement of first accepted route */
#define RA_ANY 3 /* Announcement of any route change */
diff --git a/proto/babel/Makefile b/proto/babel/Makefile
index d7684705..a5b4a13b 100644
--- a/proto/babel/Makefile
+++ b/proto/babel/Makefile
@@ -2,3 +2,5 @@ src := babel.c packets.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
+
+tests_objs := $(tests_objs) $(src-o-files)
\ No newline at end of file
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index 38be6909..73cb5c3b 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -53,8 +53,7 @@ static void babel_dump_route(struct babel_route *r);
static void babel_select_route(struct babel_entry *e);
static void babel_send_route_request(struct babel_entry *e, struct babel_neighbor *n);
static void babel_send_wildcard_request(struct babel_iface *ifa);
-static int babel_cache_seqno_request(struct babel_proto *p, ip_addr prefix, u8 plen,
- u64 router_id, u16 seqno);
+static int babel_cache_seqno_request(struct babel_proto *p, net_addr *n, u64 router_id, u16 seqno);
static void babel_trigger_iface_update(struct babel_iface *ifa);
static void babel_trigger_update(struct babel_proto *p);
static void babel_send_seqno_request(struct babel_entry *e);
@@ -67,27 +66,25 @@ static inline void babel_iface_kick_timer(struct babel_iface *ifa);
*/
static void
-babel_init_entry(struct fib_node *n)
+babel_init_entry(void *E)
{
- struct babel_entry *e = (void *) n;
- e->proto = NULL;
- e->selected_in = NULL;
- e->selected_out = NULL;
+ struct babel_entry *e = E;
+
e->updated = now;
init_list(&e->sources);
init_list(&e->routes);
}
static inline struct babel_entry *
-babel_find_entry(struct babel_proto *p, ip_addr prefix, u8 plen)
+babel_find_entry(struct babel_proto *p, const net_addr *n)
{
- return fib_find(&p->rtable, &prefix, plen);
+ return fib_find(&p->rtable, n);
}
static struct babel_entry *
-babel_get_entry(struct babel_proto *p, ip_addr prefix, u8 plen)
+babel_get_entry(struct babel_proto *p, const net_addr *n)
{
- struct babel_entry *e = fib_get(&p->rtable, &prefix, plen);
+ struct babel_entry *e = fib_get(&p->rtable, n);
e->proto = p;
return e;
}
@@ -180,8 +177,8 @@ babel_flush_route(struct babel_route *r)
{
struct babel_proto *p = r->e->proto;
- DBG("Babel: Flush route %I/%d router_id %lR neigh %I\n",
- r->e->n.prefix, r->e->n.pxlen, r->router_id, r->neigh ? r->neigh->addr : IPA_NONE);
+ DBG("Babel: Flush route %N router_id %lR neigh %I\n",
+ r->e->n.addr, r->router_id, r->neigh ? r->neigh->addr : IPA_NONE);
rem_node(NODE r);
@@ -203,8 +200,8 @@ babel_expire_route(struct babel_route *r)
struct babel_proto *p = r->e->proto;
struct babel_entry *e = r->e;
- TRACE(D_EVENTS, "Route expiry timer for %I/%d router-id %lR fired",
- e->n.prefix, e->n.pxlen, r->router_id);
+ TRACE(D_EVENTS, "Route expiry timer for %N router-id %lR fired",
+ e->n.addr, r->router_id);
if (r->metric < BABEL_INFINITY)
{
@@ -229,16 +226,14 @@ babel_refresh_route(struct babel_route *r)
static void
babel_expire_routes(struct babel_proto *p)
{
- struct babel_entry *e;
struct babel_route *r, *rx;
struct fib_iterator fit;
FIB_ITERATE_INIT(&fit, &p->rtable);
loop:
- FIB_ITERATE_START(&p->rtable, &fit, n)
+ FIB_ITERATE_START(&p->rtable, &fit, struct babel_entry, e)
{
- e = (struct babel_entry *) n;
int changed = 0;
WALK_LIST_DELSAFE(r, rx, e->routes)
@@ -261,7 +256,7 @@ loop:
* babel_rt_notify() -> p->rtable change, invalidating hidden variables.
*/
- FIB_ITERATE_PUT(&fit, n);
+ FIB_ITERATE_PUT(&fit);
babel_select_route(e);
goto loop;
}
@@ -271,12 +266,12 @@ loop:
/* Remove empty entries */
if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes))
{
- FIB_ITERATE_PUT(&fit, n);
+ FIB_ITERATE_PUT(&fit);
fib_delete(&p->rtable, e);
goto loop;
}
}
- FIB_ITERATE_END(n);
+ FIB_ITERATE_END;
}
static struct babel_neighbor *
@@ -476,8 +471,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (r)
{
- net *n = net_get(p->p.table, e->n.prefix, e->n.pxlen);
- rta A = {
+ rta a0 = {
.src = p->p.main_source,
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
@@ -489,22 +483,20 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
};
if (r->metric < BABEL_INFINITY)
- A.gw = r->next_hop;
+ a0.gw = r->next_hop;
- rta *a = rta_lookup(&A);
+ rta *a = rta_lookup(&a0);
rte *rte = rte_get_temp(a);
rte->u.babel.metric = r->metric;
rte->u.babel.router_id = r->router_id;
- rte->net = n;
rte->pflags = 0;
- rte_update(&p->p, n, rte);
+ rte_update(&p->p, e->n.addr, rte);
}
else
{
/* Retraction */
- net *n = net_find(p->p.table, e->n.prefix, e->n.pxlen);
- rte_update(&p->p, n, NULL);
+ rte_update(&p->p, e->n.addr, NULL);
}
}
@@ -541,8 +533,8 @@ babel_select_route(struct babel_entry *e)
((!e->selected_in && cur->metric < BABEL_INFINITY) ||
(e->selected_in && cur->metric < e->selected_in->metric)))
{
- TRACE(D_EVENTS, "Picked new route for prefix %I/%d: router id %lR metric %d",
- e->n.prefix, e->n.pxlen, cur->router_id, cur->metric);
+ TRACE(D_EVENTS, "Picked new route for prefix %N: router id %lR metric %d",
+ e->n.addr, cur->router_id, cur->metric);
e->selected_in = cur;
e->updated = now;
@@ -557,8 +549,8 @@ babel_select_route(struct babel_entry *e)
babel_build_rte() will set the unreachable flag if the metric is BABEL_INFINITY.*/
if (e->selected_in)
{
- TRACE(D_EVENTS, "Lost feasible route for prefix %I/%d",
- e->n.prefix, e->n.pxlen);
+ TRACE(D_EVENTS, "Lost feasible route for prefix %N",
+ e->n.addr);
e->selected_in->metric = BABEL_INFINITY;
e->updated = now;
@@ -576,7 +568,7 @@ babel_select_route(struct babel_entry *e)
/* No route currently selected, and no new one selected; this means we
don't have a route to this destination anymore (and were probably
called from an expiry timer). Remove the route from the nest. */
- TRACE(D_EVENTS, "Flushing route for prefix %I/%d", e->n.prefix, e->n.pxlen);
+ TRACE(D_EVENTS, "Flushing route for prefix %N", e->n.addr);
e->selected_in = NULL;
e->updated = now;
@@ -663,12 +655,11 @@ babel_send_route_request(struct babel_entry *e, struct babel_neighbor *n)
struct babel_iface *ifa = n->ifa;
union babel_msg msg = {};
- TRACE(D_PACKETS, "Sending route request for %I/%d to %I",
- e->n.prefix, e->n.pxlen, n->addr);
+ TRACE(D_PACKETS, "Sending route request for %N to %I",
+ e->n.addr, n->addr);
msg.type = BABEL_TLV_ROUTE_REQUEST;
- msg.route_request.prefix = e->n.prefix;
- msg.route_request.plen = e->n.pxlen;
+ net_copy(&msg.route_request.net, e->n.addr);
babel_send_unicast(&msg, ifa, n->addr);
}
@@ -698,18 +689,17 @@ babel_send_seqno_request(struct babel_entry *e)
union babel_msg msg = {};
s = babel_find_source(e, r->router_id);
- if (!s || !babel_cache_seqno_request(p, e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1))
+ if (!s || !babel_cache_seqno_request(p, e->n.addr, r->router_id, s->seqno + 1))
return;
- TRACE(D_PACKETS, "Sending seqno request for %I/%d router-id %lR seqno %d",
- e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1);
+ TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
+ e->n.addr, r->router_id, s->seqno + 1);
msg.type = BABEL_TLV_SEQNO_REQUEST;
- msg.seqno_request.plen = e->n.pxlen;
- msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.hop_count = BABEL_INITIAL_HOP_COUNT;
+ msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.router_id = r->router_id;
- msg.seqno_request.prefix = e->n.prefix;
+ net_copy(&msg.seqno_request.net, e->n.addr);
WALK_LIST(ifa, p->interfaces)
babel_enqueue(&msg, ifa);
@@ -725,18 +715,17 @@ babel_unicast_seqno_request(struct babel_route *r)
union babel_msg msg = {};
s = babel_find_source(e, r->router_id);
- if (!s || !babel_cache_seqno_request(p, e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1))
+ if (!s || !babel_cache_seqno_request(p, e->n.addr, r->router_id, s->seqno + 1))
return;
- TRACE(D_PACKETS, "Sending seqno request for %I/%d router-id %lR seqno %d",
- e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1);
+ TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
+ e->n.addr, r->router_id, s->seqno + 1);
msg.type = BABEL_TLV_SEQNO_REQUEST;
- msg.seqno_request.plen = e->n.pxlen;
- msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.hop_count = BABEL_INITIAL_HOP_COUNT;
+ msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.router_id = r->router_id;
- msg.seqno_request.prefix = e->n.prefix;
+ net_copy(&msg.seqno_request.net, e->n.addr);
babel_send_unicast(&msg, ifa, r->neigh->addr);
}
@@ -756,9 +745,8 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed)
{
struct babel_proto *p = ifa->proto;
- FIB_WALK(&p->rtable, n)
+ FIB_WALK(&p->rtable, struct babel_entry, e)
{
- struct babel_entry *e = (void *) n;
struct babel_route *r = e->selected_out;
if (!r)
@@ -776,17 +764,16 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed)
if (e->updated < changed)
continue;
- TRACE(D_PACKETS, "Sending update for %I/%d router-id %lR seqno %d metric %d",
- e->n.prefix, e->n.pxlen, r->router_id, r->seqno, r->metric);
+ TRACE(D_PACKETS, "Sending update for %N router-id %lR seqno %d metric %d",
+ e->n.addr, r->router_id, r->seqno, r->metric);
union babel_msg msg = {};
msg.type = BABEL_TLV_UPDATE;
- msg.update.plen = e->n.pxlen;
msg.update.interval = ifa->cf->update_interval;
msg.update.seqno = r->seqno;
msg.update.metric = r->metric;
- msg.update.prefix = e->n.prefix;
msg.update.router_id = r->router_id;
+ net_copy(&msg.update.net, e->n.addr);
babel_enqueue(&msg, ifa);
@@ -839,20 +826,18 @@ babel_trigger_update(struct babel_proto *p)
/* A retraction is an update with an infinite metric */
static void
-babel_send_retraction(struct babel_iface *ifa, ip_addr prefix, int plen)
+babel_send_retraction(struct babel_iface *ifa, net_addr *n)
{
struct babel_proto *p = ifa->proto;
union babel_msg msg = {};
- TRACE(D_PACKETS, "Sending retraction for %I/%d seqno %d",
- prefix, plen, p->update_seqno);
+ TRACE(D_PACKETS, "Sending retraction for %N seqno %d", n, p->update_seqno);
msg.type = BABEL_TLV_UPDATE;
- msg.update.plen = plen;
msg.update.interval = ifa->cf->update_interval;
msg.update.seqno = p->update_seqno;
msg.update.metric = BABEL_INFINITY;
- msg.update.prefix = prefix;
+ msg.update.net = *n;
babel_enqueue(&msg, ifa);
}
@@ -941,22 +926,20 @@ babel_expire_seqno_requests(struct babel_proto *p)
* found. Otherwise, a new entry is stored in the cache.
*/
static int
-babel_cache_seqno_request(struct babel_proto *p, ip_addr prefix, u8 plen,
+babel_cache_seqno_request(struct babel_proto *p, net_addr *n,
u64 router_id, u16 seqno)
{
struct babel_seqno_request *r;
WALK_LIST(r, p->seqno_cache)
{
- if (ipa_equal(r->prefix, prefix) && (r->plen == plen) &&
- (r->router_id == router_id) && (r->seqno == seqno))
+ if (net_equal(&r->net, n) && (r->router_id == router_id) && (r->seqno == seqno))
return 0;
}
/* no entries found */
r = sl_alloc(p->seqno_slab);
- r->prefix = prefix;
- r->plen = plen;
+ net_copy(&r->net, n);
r->router_id = router_id;
r->seqno = seqno;
r->updated = now;
@@ -973,8 +956,8 @@ babel_forward_seqno_request(struct babel_entry *e,
struct babel_proto *p = e->proto;
struct babel_route *r;
- TRACE(D_PACKETS, "Forwarding seqno request for %I/%d router-id %lR seqno %d",
- e->n.prefix, e->n.pxlen, in->router_id, in->seqno);
+ TRACE(D_PACKETS, "Forwarding seqno request for %N router-id %lR seqno %d",
+ e->n.addr, in->router_id, in->seqno);
WALK_LIST(r, e->routes)
{
@@ -982,16 +965,15 @@ babel_forward_seqno_request(struct babel_entry *e,
!OUR_ROUTE(r) &&
!ipa_equal(r->neigh->addr, sender))
{
- if (!babel_cache_seqno_request(p, e->n.prefix, e->n.pxlen, in->router_id, in->seqno))
+ if (!babel_cache_seqno_request(p, e->n.addr, in->router_id, in->seqno))
return;
union babel_msg msg = {};
msg.type = BABEL_TLV_SEQNO_REQUEST;
- msg.seqno_request.plen = in->plen;
- msg.seqno_request.seqno = in->seqno;
msg.seqno_request.hop_count = in->hop_count-1;
+ msg.seqno_request.seqno = in->seqno;
msg.seqno_request.router_id = in->router_id;
- msg.seqno_request.prefix = e->n.prefix;
+ net_copy(&msg.seqno_request.net, e->n.addr);
babel_send_unicast(&msg, r->neigh->ifa, r->neigh->addr);
return;
@@ -1073,8 +1055,11 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
node *n;
int feasible;
- TRACE(D_PACKETS, "Handling update for %I/%d with seqno %d metric %d",
- msg->prefix, msg->plen, msg->seqno, msg->metric);
+ if (msg->wildcard)
+ TRACE(D_PACKETS, "Handling wildcard retraction", msg->seqno);
+ else
+ TRACE(D_PACKETS, "Handling update for %N with seqno %d metric %d",
+ &msg->net, msg->seqno, msg->metric);
nbr = babel_find_neighbor(ifa, msg->sender);
if (!nbr)
@@ -1140,7 +1125,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
}
else
{
- e = babel_find_entry(p, msg->prefix, msg->plen);
+ e = babel_find_entry(p, &msg->net);
if (!e)
return;
@@ -1159,7 +1144,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
return;
}
- e = babel_get_entry(p, msg->prefix, msg->plen);
+ e = babel_get_entry(p, &msg->net);
r = babel_find_route(e, nbr); /* the route entry indexed by neighbour */
s = babel_find_source(e, msg->router_id); /* for feasibility */
feasible = babel_is_feasible(s, msg->seqno, msg->metric);
@@ -1231,14 +1216,14 @@ babel_handle_route_request(union babel_msg *m, struct babel_iface *ifa)
return;
}
- TRACE(D_PACKETS, "Handling route request for %I/%d", msg->prefix, msg->plen);
+ TRACE(D_PACKETS, "Handling route request for %N", &msg->net);
/* Non-wildcard request - see if we have an entry for the route.
If not, send a retraction, otherwise send an update. */
- struct babel_entry *e = babel_find_entry(p, msg->prefix, msg->plen);
+ struct babel_entry *e = babel_find_entry(p, &msg->net);
if (!e)
{
- babel_send_retraction(ifa, msg->prefix, msg->plen);
+ babel_send_retraction(ifa, &msg->net);
}
else
{
@@ -1256,11 +1241,11 @@ babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
/* RFC 6126 3.8.1.2 */
- TRACE(D_PACKETS, "Handling seqno request for %I/%d router-id %lR seqno %d hop count %d",
- msg->prefix, msg->plen, msg->router_id, msg->seqno, msg->hop_count);
+ TRACE(D_PACKETS, "Handling seqno request for %N router-id %lR seqno %d hop count %d",
+ &msg->net, msg->router_id, msg->seqno, msg->hop_count);
/* Ignore if we have no such entry or entry has infinite metric */
- struct babel_entry *e = babel_find_entry(p, msg->prefix, msg->plen);
+ struct babel_entry *e = babel_find_entry(p, &msg->net);
if (!e || !e->selected_out || (e->selected_out->metric == BABEL_INFINITY))
return;
@@ -1668,7 +1653,7 @@ babel_dump_entry(struct babel_entry *e)
struct babel_source *s;
struct babel_route *r;
- debug("Babel: Entry %I/%d:\n", e->n.prefix, e->n.pxlen);
+ debug("Babel: Entry %N:\n", e->n.addr);
WALK_LIST(s,e->sources)
{ debug(" "); babel_dump_source(s); }
@@ -1715,9 +1700,9 @@ babel_dump(struct proto *P)
WALK_LIST(ifa, p->interfaces)
babel_dump_iface(ifa);
- FIB_WALK(&p->rtable, n)
+ FIB_WALK(&p->rtable, struct babel_entry, e)
{
- babel_dump_entry((struct babel_entry *) n);
+ babel_dump_entry(e);
}
FIB_WALK_END;
}
@@ -1828,11 +1813,9 @@ void
babel_show_entries(struct proto *P)
{
struct babel_proto *p = (void *) P;
- struct babel_entry *e = NULL;
struct babel_source *s = NULL;
struct babel_route *r = NULL;
- char ipbuf[STD_ADDRESS_P_LENGTH+5];
char ridbuf[ROUTER_ID_64_LENGTH+1];
if (p->p.proto_state != PS_UP)
@@ -1846,17 +1829,14 @@ babel_show_entries(struct proto *P)
cli_msg(-1025, "%-29s %-23s %6s %5s %7s %7s",
"Prefix", "Router ID", "Metric", "Seqno", "Expires", "Sources");
- FIB_WALK(&p->rtable, n)
+ FIB_WALK(&p->rtable, struct babel_entry, e)
{
- e = (struct babel_entry *) n;
r = e->selected_in ? e->selected_in : e->selected_out;
int srcs = 0;
WALK_LIST(s, e->sources)
srcs++;
- bsprintf(ipbuf, "%I/%u", e->n.prefix, e->n.pxlen);
-
if (r)
{
if (r->router_id == p->router_id)
@@ -1865,12 +1845,12 @@ babel_show_entries(struct proto *P)
bsprintf(ridbuf, "%lR", r->router_id);
int time = r->expires ? r->expires - now : 0;
- cli_msg(-1025, "%-29s %-23s %6u %5u %7u %7u",
- ipbuf, ridbuf, r->metric, r->seqno, MAX(time, 0), srcs);
+ cli_msg(-1025, "%-29N %-23s %6u %5u %7u %7u",
+ e->n.addr, ridbuf, r->metric, r->seqno, MAX(time, 0), srcs);
}
else
{
- cli_msg(-1025, "%-29s %-44s %7u", ipbuf, "", srcs);
+ cli_msg(-1025, "%-29N %-44s %7u", e->n.addr, "", srcs);
}
}
FIB_WALK_END;
@@ -1964,7 +1944,7 @@ babel_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
* so store it into our data structures.
*/
static void
-babel_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net,
+babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
struct rte *new, struct rte *old UNUSED, struct ea_list *attrs UNUSED)
{
struct babel_proto *p = (void *) P;
@@ -1974,7 +1954,7 @@ babel_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *ne
if (new)
{
/* Update */
- e = babel_get_entry(p, net->n.prefix, net->n.pxlen);
+ e = babel_get_entry(p, net->n.addr);
if (new->attrs->src->proto != P)
{
@@ -1996,7 +1976,7 @@ babel_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *ne
else
{
/* Withdraw */
- e = babel_find_entry(p, net->n.prefix, net->n.pxlen);
+ e = babel_find_entry(p, net->n.addr);
if (!e || !e->selected_out)
return;
@@ -2046,11 +2026,12 @@ babel_rte_same(struct rte *new, struct rte *old)
static struct proto *
-babel_init(struct proto_config *cfg)
+babel_init(struct proto_config *CF)
{
- struct proto *P = proto_new(cfg, sizeof(struct babel_proto));
+ struct proto *P = proto_new(CF);
+
+ P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
- P->accept_ra_types = RA_OPTIMAL;
P->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify;
P->import_control = babel_import_control;
@@ -2068,7 +2049,8 @@ babel_start(struct proto *P)
struct babel_proto *p = (void *) P;
struct babel_config *cf = (void *) P->cf;
- fib_init(&p->rtable, P->pool, sizeof(struct babel_entry), 0, babel_init_entry);
+ fib_init(&p->rtable, P->pool, NET_IP6, sizeof(struct babel_entry),
+ OFFSETOF(struct babel_entry, n), 0, babel_init_entry);
init_list(&p->interfaces);
p->timer = tm_new_set(P->pool, babel_timer, p, 0, 1);
tm_start(p->timer, 2);
@@ -2111,14 +2093,17 @@ babel_shutdown(struct proto *P)
}
static int
-babel_reconfigure(struct proto *P, struct proto_config *c)
+babel_reconfigure(struct proto *P, struct proto_config *CF)
{
struct babel_proto *p = (void *) P;
- struct babel_config *new = (void *) c;
+ struct babel_config *new = (void *) CF;
TRACE(D_EVENTS, "Reconfiguring");
- p->p.cf = c;
+ if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
+ return 0;
+
+ p->p.cf = CF;
babel_reconfigure_ifaces(p, new);
babel_trigger_update(p);
@@ -2133,6 +2118,8 @@ struct protocol proto_babel = {
.template = "babel%d",
.attr_class = EAP_BABEL,
.preference = DEF_PREF_BABEL,
+ .channel_mask = NB_IP6,
+ .proto_size = sizeof(struct babel_proto),
.config_size = sizeof(struct babel_config),
.init = babel_init,
.dump = babel_dump,
diff --git a/proto/babel/babel.h b/proto/babel/babel.h
index e8b6c314..792c9d60 100644
--- a/proto/babel/babel.h
+++ b/proto/babel/babel.h
@@ -52,7 +52,7 @@
/* Max interval that will not overflow when carried as 16-bit centiseconds */
#define BABEL_MAX_INTERVAL (0xFFFF/BABEL_TIME_UNITS)
-#define BABEL_OVERHEAD (SIZE_OF_IP_HEADER+UDP_HEADER_LENGTH)
+#define BABEL_OVERHEAD (IP6_HEADER_LENGTH+UDP_HEADER_LENGTH)
#define BABEL_MIN_MTU (512 + BABEL_OVERHEAD)
@@ -208,7 +208,6 @@ struct babel_route {
};
struct babel_entry {
- struct fib_node n;
struct babel_proto *proto;
struct babel_route *selected_in;
struct babel_route *selected_out;
@@ -217,13 +216,14 @@ struct babel_entry {
list sources; /* Source entries for this prefix (struct babel_source). */
list routes; /* Routes for this prefix (struct babel_route) */
+
+ struct fib_node n;
};
/* Stores forwarded seqno requests for duplicate suppression. */
struct babel_seqno_request {
node n;
- ip_addr prefix;
- u8 plen;
+ net_addr net;
u64 router_id;
u16 seqno;
bird_clock_t updated;
@@ -265,12 +265,11 @@ struct babel_msg_ihu {
struct babel_msg_update {
u8 type;
u8 wildcard;
- u8 plen;
u16 interval;
u16 seqno;
u16 metric;
- ip_addr prefix;
u64 router_id;
+ net_addr net;
ip_addr next_hop;
ip_addr sender;
};
@@ -278,17 +277,15 @@ struct babel_msg_update {
struct babel_msg_route_request {
u8 type;
u8 full;
- u8 plen;
- ip_addr prefix;
+ net_addr net;
};
struct babel_msg_seqno_request {
u8 type;
- u8 plen;
- u16 seqno;
u8 hop_count;
+ u16 seqno;
u64 router_id;
- ip_addr prefix;
+ net_addr net;
ip_addr sender;
};
diff --git a/proto/babel/config.Y b/proto/babel/config.Y
index b6170852..cf8983fa 100644
--- a/proto/babel/config.Y
+++ b/proto/babel/config.Y
@@ -30,11 +30,13 @@ CF_ADDTO(proto, babel_proto)
babel_proto_start: proto_start BABEL
{
this_proto = proto_config_new(&proto_babel, $1);
+ this_proto->net_type = NET_IP6;
init_list(&BABEL_CFG->iface_list);
};
babel_proto_item:
proto_item
+ | proto_channel
| INTERFACE babel_iface
;
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index 08054832..70cfc196 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -146,7 +146,8 @@ struct babel_write_state {
#define TLV_HDR(tlv,t,l) ({ tlv->type = t; tlv->length = l - sizeof(struct babel_tlv); })
#define TLV_HDR0(tlv,t) TLV_HDR(tlv, t, tlv_data[t].min_length)
-#define BYTES(n) ((((uint) n) + 7) / 8)
+#define NET_SIZE(n) BYTES(net_pxlen(n))
+
static inline u16
get_time16(const void *p)
@@ -161,19 +162,19 @@ put_time16(void *p, u16 v)
put_u16(p, v * BABEL_TIME_UNITS);
}
-static inline ip6_addr
-get_ip6_px(const void *p, uint plen)
+static inline void
+read_ip6_px(net_addr *n, const void *p, uint plen)
{
ip6_addr addr = IPA_NONE;
memcpy(&addr, p, BYTES(plen));
- return ip6_ntoh(addr);
+ net_fill_ip6(n, ip6_ntoh(addr), plen);
}
static inline void
-put_ip6_px(void *p, ip6_addr addr, uint plen)
+put_ip6_px(void *p, net_addr *n)
{
- addr = ip6_hton(addr);
- memcpy(p, &addr, BYTES(plen));
+ ip6_addr addr = ip6_hton(net6_prefix(n));
+ memcpy(p, &addr, NET_SIZE(n));
}
static inline ip6_addr
@@ -480,6 +481,9 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
if (tlv->plen > 0)
return PARSE_ERROR;
+ if (msg->metric != 65535)
+ return PARSE_ERROR;
+
msg->wildcard = 1;
break;
@@ -488,7 +492,7 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE;
case BABEL_AE_IP6:
- if (tlv->plen > MAX_PREFIX_LENGTH)
+ if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR;
/* Cannot omit data if there is no saved prefix */
@@ -499,18 +503,18 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
memcpy(buf, state->def_ip6_prefix, tlv->omitted);
memcpy(buf + tlv->omitted, tlv->addr, len);
- msg->plen = tlv->plen;
- msg->prefix = ipa_from_ip6(get_ip6(buf));
+ ip6_addr prefix = get_ip6(buf);
+ net_fill_ip6(&msg->net, prefix, tlv->plen);
if (tlv->flags & BABEL_FLAG_DEF_PREFIX)
{
- put_ip6(state->def_ip6_prefix, msg->prefix);
+ put_ip6(state->def_ip6_prefix, prefix);
state->def_ip6_prefix_seen = 1;
}
if (tlv->flags & BABEL_FLAG_ROUTER_ID)
{
- state->router_id = ((u64) _I2(msg->prefix)) << 32 | _I3(msg->prefix);
+ state->router_id = ((u64) _I2(prefix)) << 32 | _I3(prefix);
state->router_id_seen = 1;
}
break;
@@ -559,7 +563,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
tlv = (struct babel_tlv_update *) NEXT_TLV(tlv);
}
- uint len = sizeof(struct babel_tlv_update) + BYTES(msg->plen);
+ uint len = sizeof(struct babel_tlv_update) + NET_SIZE(&msg->net);
if (len0 + len > max_len)
return 0;
@@ -575,8 +579,8 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
else
{
tlv->ae = BABEL_AE_IP6;
- tlv->plen = msg->plen;
- put_ip6_px(tlv->addr, msg->prefix, msg->plen);
+ tlv->plen = net6_pxlen(&msg->net);
+ put_ip6_px(tlv->addr, &msg->net);
}
put_time16(&tlv->interval, msg->interval);
@@ -610,14 +614,13 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE;
case BABEL_AE_IP6:
- if (tlv->plen > MAX_PREFIX_LENGTH)
+ if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR;
if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen))
return PARSE_ERROR;
- msg->plen = tlv->plen;
- msg->prefix = get_ip6_px(tlv->addr, tlv->plen);
+ read_ip6_px(&msg->net, tlv->addr, tlv->plen);
return PARSE_SUCCESS;
case BABEL_AE_IP6_LL:
@@ -637,7 +640,7 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m,
struct babel_tlv_route_request *tlv = (void *) hdr;
struct babel_msg_route_request *msg = &m->route_request;
- uint len = sizeof(struct babel_tlv_route_request) + BYTES(msg->plen);
+ uint len = sizeof(struct babel_tlv_route_request) + NET_SIZE(&msg->net);
if (len > max_len)
return 0;
@@ -652,8 +655,8 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m,
else
{
tlv->ae = BABEL_AE_IP6;
- tlv->plen = msg->plen;
- put_ip6_px(tlv->addr, msg->prefix, msg->plen);
+ tlv->plen = net6_pxlen(&msg->net);
+ put_ip6_px(tlv->addr, &msg->net);
}
return len;
@@ -685,14 +688,13 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE;
case BABEL_AE_IP6:
- if (tlv->plen > MAX_PREFIX_LENGTH)
+ if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR;
if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen))
return PARSE_ERROR;
- msg->plen = tlv->plen;
- msg->prefix = get_ip6_px(tlv->addr, tlv->plen);
+ read_ip6_px(&msg->net, tlv->addr, tlv->plen);
return PARSE_SUCCESS;
case BABEL_AE_IP6_LL:
@@ -712,18 +714,18 @@ babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
struct babel_tlv_seqno_request *tlv = (void *) hdr;
struct babel_msg_seqno_request *msg = &m->seqno_request;
- uint len = sizeof(struct babel_tlv_seqno_request) + BYTES(msg->plen);
+ uint len = sizeof(struct babel_tlv_seqno_request) + NET_SIZE(&msg->net);
if (len > max_len)
return 0;
TLV_HDR(tlv, BABEL_TLV_SEQNO_REQUEST, len);
tlv->ae = BABEL_AE_IP6;
- tlv->plen = msg->plen;
+ tlv->plen = net6_pxlen(&msg->net);
put_u16(&tlv->seqno, msg->seqno);
tlv->hop_count = msg->hop_count;
put_u64(&tlv->router_id, msg->router_id);
- put_ip6_px(tlv->addr, msg->prefix, msg->plen);
+ put_ip6_px(tlv->addr, &msg->net);
return len;
}
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 1b124a17..f2a8e8b5 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -41,24 +41,6 @@
* specifies that such updates should be ignored, but that is generally
* a bad idea.
*
- * Error checking of optional transitive attributes is done according to
- * draft-ietf-idr-optional-transitive-03, but errors are handled always
- * as withdraws.
- *
- * Unexpected AS_CONFED_* segments in AS_PATH are logged and removed,
- * but unknown segments cause a session drop with Malformed AS_PATH
- * error (see validate_path()). The behavior in such case is not
- * explicitly specified by RFC 4271. RFC 5065 specifies that
- * inconsistent AS_CONFED_* segments should cause a session drop, but
- * implementations that pass invalid AS_CONFED_* segments are
- * widespread.
- *
- * Error handling of AS4_* attributes is done as specified by
- * draft-ietf-idr-rfc4893bis-03. There are several possible
- * inconsistencies between AGGREGATOR and AS4_AGGREGATOR that are not
- * handled by that draft, these are logged and ignored (see
- * bgp_reconstruct_4b_attrs()).
- *
* BGP attribute table has several hooks:
*
* export - Hook that validates and normalizes attribute during export phase.
@@ -281,11 +263,19 @@ bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
static void
bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
+ struct bgp_proto *p = s->proto;
+ int as_length = s->as4_session ? 4 : 2;
+ int as_confed = p->cf->confederation && p->is_interior;
char err[128];
- if (!as_path_valid(data, len, (s->as4_session ? 4 : 2), err, sizeof(err)))
+ if (!as_path_valid(data, len, as_length, as_confed, err, sizeof(err)))
WITHDRAW("Malformed AS_PATH attribute - %s", err);
+ /* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
+ if (p->is_interior && !p->is_internal &&
+ ((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
+ WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");
+
if (!s->as4_session)
{
/* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */
@@ -603,11 +593,20 @@ bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byt
if (len < 6)
DISCARD(BAD_LENGTH, "AS4_PATH", len);
- if (!as_path_valid(data, len, 4, err, sizeof(err)))
+ if (!as_path_valid(data, len, 4, 1, err, sizeof(err)))
DISCARD("Malformed AS4_PATH attribute - %s", err);
- /* XXXX remove CONFED segments */
- bgp_set_attr_data(to, s->pool, BA_AS4_PATH, flags, data, len);
+ struct adata *a = lp_alloc_adata(s->pool, len);
+ memcpy(a->data, data, len);
+
+ /* AS_CONFED* segments are invalid in AS4_PATH; RFC 6793 6 */
+ if (as_path_contains_confed(a))
+ {
+ REPORT("Discarding AS_CONFED* segment from AS4_PATH attribute");
+ a = as_path_strip_confed(s->pool, a);
+ }
+
+ bgp_set_attr_ptr(to, s->pool, BA_AS4_PATH, flags, a);
}
static void
@@ -1042,7 +1041,7 @@ bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len)
if (bgp_as_path_loopy(p, attrs, p->local_as))
goto withdraw;
- /* Reject routes with our Confederation ID in AS_PATH attribute; RFC 5065 4 */
+ /* Reject routes with our Confederation ID in AS_PATH attribute; RFC 5065 4.0 */
if ((p->public_as != p->local_as) && bgp_as_path_loopy(p, attrs, p->public_as))
goto withdraw;
@@ -1221,8 +1220,17 @@ bgp_init_prefix_table(struct bgp_channel *c)
{
HASH_INIT(c->prefix_hash, c->pool, 8);
- c->prefix_slab = sl_new(c->pool, sizeof(struct bgp_prefix) +
- net_addr_length[c->c.net_type]);
+ uint alen = net_addr_length[c->c.net_type];
+ c->prefix_slab = alen ? sl_new(c->pool, sizeof(struct bgp_prefix) + alen) : NULL;
+}
+
+void
+bgp_free_prefix_table(struct bgp_channel *c)
+{
+ HASH_FREE(c->prefix_hash);
+
+ rfree(c->prefix_slab);
+ c->prefix_slab = NULL;
}
static struct bgp_prefix *
@@ -1237,7 +1245,11 @@ bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
return px;
}
- px = sl_alloc(c->prefix_slab);
+ if (c->prefix_slab)
+ px = sl_alloc(c->prefix_slab);
+ else
+ px = mb_alloc(c->pool, sizeof(struct bgp_prefix) + net->length);
+
px->buck_node.next = NULL;
px->buck_node.prev = NULL;
px->hash = hash;
@@ -1254,7 +1266,11 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
{
rem_node(&px->buck_node);
HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px);
- sl_free(c->prefix_slab, px);
+
+ if (c->prefix_slab)
+ sl_free(c->prefix_slab, px);
+ else
+ mb_free(px);
}
@@ -1278,6 +1294,8 @@ bgp_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct li
if (src == NULL)
return 0;
+ // XXXX: Check next hop AF
+
/* IBGP route reflection, RFC 4456 */
if (p->is_internal && src->is_internal && (p->local_as == src->local_as))
{
@@ -1314,82 +1332,86 @@ bgp_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct li
return 0;
}
-static const adata null_adata; /* adata of length 0 */
-static inline void
-bgp_path_prepend(ea_list **attrs, struct linpool *pool, int seg, u32 as, int strip)
-{
- eattr *a = bgp_find_attr(*attrs, BA_AS_PATH);
- adata *d = as_path_prepend2(pool, a ? a->u.ptr : &null_adata, seg, as, strip);
- bgp_set_attr_ptr(attrs, pool, BA_AS_PATH, 0, d);
-}
-
-static inline void
-bgp_cluster_list_prepend(ea_list **attrs, struct linpool *pool, u32 id)
-{
- eattr *a = bgp_find_attr(*attrs, BA_CLUSTER_LIST);
- adata *d = int_set_add(pool, a ? a->u.ptr : NULL, id);
- bgp_set_attr_ptr(attrs, pool, BA_CLUSTER_LIST, 0, d);
-}
+static adata null_adata; /* adata of length 0 */
static ea_list *
-bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs, struct linpool *pool)
+bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool)
{
struct proto *SRC = e->attrs->src->proto;
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL;
struct bgp_export_state s = { .proto = p, .channel =c, .pool = pool, .src = src, .route = e };
+ ea_list *attrs = attrs0;
eattr *a;
+ adata *ad;
/* ORIGIN attribute - mandatory, attach if missing */
- if (! bgp_find_attr(attrs, BA_ORIGIN))
+ if (! bgp_find_attr(attrs0, BA_ORIGIN))
bgp_set_attr_u32(&attrs, pool, BA_ORIGIN, 0, src ? ORIGIN_INCOMPLETE : ORIGIN_IGP);
+ /* AS_PATH attribute - mandatory */
+ a = bgp_find_attr(attrs0, BA_AS_PATH);
+ ad = a ? a->u.ptr : &null_adata;
+
+ /* AS_PATH attribute - strip AS_CONFED* segments outside confederation */
+ if ((!p->cf->confederation || !p->is_interior) && as_path_contains_confed(ad))
+ ad = as_path_strip_confed(pool, ad);
+
/* AS_PATH attribute - keep or prepend ASN */
if (p->is_internal ||
(p->rs_client && src && src->rs_client))
{
/* IBGP or route server -> just ensure there is one */
- if (! bgp_find_attr(attrs, BA_AS_PATH))
- bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, lp_alloc_adata(pool, 0));
+ if (!a)
+ bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, &null_adata);
}
else if (p->is_interior)
{
- /* Confederation -> prepend ASN as CONFED_SEQUENCE, keep CONFED_* segments */
- bgp_path_prepend(&attrs, pool, AS_PATH_CONFED_SEQUENCE, p->public_as, 0);
+ /* Confederation -> prepend ASN as AS_CONFED_SEQUENCE */
+ ad = as_path_prepend2(pool, ad, AS_PATH_CONFED_SEQUENCE, p->public_as);
+ bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, ad);
}
else /* Regular EBGP (no RS, no confederation) */
{
- /* Regular EBGP -> prepend ASN as regular segment, strip CONFED_* segments */
- bgp_path_prepend(&attrs, pool, AS_PATH_SEQUENCE, p->public_as, 1);
+ /* Regular EBGP -> prepend ASN as regular sequence */
+ ad = as_path_prepend2(pool, ad, AS_PATH_SEQUENCE, p->public_as);
+ bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, ad);
/* MULTI_EXIT_DESC attribute - accept only if set in export filter */
- a = bgp_find_attr(attrs, BA_MULTI_EXIT_DISC);
+ a = bgp_find_attr(attrs0, BA_MULTI_EXIT_DISC);
if (a && !(a->type & EAF_FRESH))
bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC);
}
/* NEXT_HOP attribute - delegated to AF-specific hook */
- a = bgp_find_attr(attrs, BA_NEXT_HOP);
+ a = bgp_find_attr(attrs0, BA_NEXT_HOP);
bgp_update_next_hop(&s, a, &attrs);
/* LOCAL_PREF attribute - required for IBGP, attach if missing */
- if (p->is_interior && ! bgp_find_attr(attrs, BA_LOCAL_PREF))
+ if (p->is_interior && ! bgp_find_attr(attrs0, BA_LOCAL_PREF))
bgp_set_attr_u32(&attrs, pool, BA_LOCAL_PREF, 0, p->cf->default_local_pref);
/* IBGP route reflection, RFC 4456 */
if (src && src->is_internal && p->is_internal && (src->local_as == p->local_as))
{
/* ORIGINATOR_ID attribute - attach if not already set */
- if (! bgp_find_attr(attrs, BA_ORIGINATOR_ID))
+ if (! bgp_find_attr(attrs0, BA_ORIGINATOR_ID))
bgp_set_attr_u32(&attrs, pool, BA_ORIGINATOR_ID, 0, src->remote_id);
/* CLUSTER_LIST attribute - prepend cluster ID */
- if (src->rr_cluster_id)
- bgp_cluster_list_prepend(&attrs, pool, src->rr_cluster_id);
+ a = bgp_find_attr(attrs0, BA_CLUSTER_LIST);
+ ad = a ? a->u.ptr : NULL;
- /* Handle different src and dst cluster ID - prepend both ones */
+ /* Prepend src cluster ID */
+ if (src->rr_cluster_id)
+ ad = int_set_prepend(pool, ad, src->rr_cluster_id);
+
+ /* Prepend dst cluster ID if src and dst clusters are different */
if (p->rr_cluster_id && (src->rr_cluster_id != p->rr_cluster_id))
- bgp_cluster_list_prepend(&attrs, pool, p->rr_cluster_id);
+ ad = int_set_prepend(pool, ad, p->rr_cluster_id);
+
+ /* Should be at least one prepended cluster ID */
+ bgp_set_attr_ptr(&attrs, pool, BA_CLUSTER_LIST, 0, ad);
}
/* AS4_* transition attributes, RFC 6793 4.2.2 */
@@ -1410,6 +1432,12 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
}
}
+ /*
+ * Presence of mandatory attributes ORIGIN and AS_PATH is ensured by above
+ * conditions. Presence and validity of quasi-mandatory NEXT_HOP attribute
+ * should be checked in AF-specific hooks.
+ */
+
/* Apply per-attribute export hooks for validatation and normalization */
return bgp_export_attrs(&s, attrs);
}
@@ -1452,10 +1480,12 @@ bgp_get_neighbor(rte *r)
eattr *e = ea_find(r->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
u32 as;
- if (e && as_path_get_first(e->u.ptr, &as))
+ if (e && as_path_get_first_regular(e->u.ptr, &as))
return as;
- else
- return ((struct bgp_proto *) r->attrs->src->proto)->remote_as;
+
+ /* If AS_PATH is not defined, we treat rte as locally originated */
+ struct bgp_proto *p = (void *) r->attrs->src->proto;
+ return p->cf->confederation ?: p->local_as;
}
static inline int
@@ -1653,7 +1683,7 @@ bgp_rte_mergable(rte *pri, rte *sec)
}
/* RFC 4271 9.1.2.2. d) Prefer external peers */
- if (pri_bgp->is_internal != sec_bgp->is_internal)
+ if (pri_bgp->is_interior != sec_bgp->is_interior)
return 0;
/* RFC 4271 9.1.2.2. e) Compare IGP metrics */
@@ -1843,6 +1873,7 @@ bgp_process_as4_attrs(ea_list **attrs, struct linpool *pool)
/* Handle AS_PATH attribute */
if (p2 && p4)
{
+ /* Both as_path_getlen() and as_path_cut() take AS_CONFED* as zero length */
int p2_len = as_path_getlen(p2->u.ptr);
int p4_len = as_path_getlen(p4->u.ptr);
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 2ca153ab..5e95e6b4 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -521,12 +521,17 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
if (peer->gr_aware)
c->load_state = BFS_LOADING;
+ c->ext_next_hop = c->cf->ext_next_hop && (bgp_channel_is_ipv6(c) || rem->ext_next_hop);
c->add_path_rx = (loc->add_path & BGP_ADD_PATH_RX) && (rem->add_path & BGP_ADD_PATH_TX);
c->add_path_tx = (loc->add_path & BGP_ADD_PATH_TX) && (rem->add_path & BGP_ADD_PATH_RX);
- // XXXX reset back to non-ANY?
+ /* Update RA mode */
if (c->add_path_tx)
c->c.ra_mode = RA_ANY;
+ else if (c->cf->secondary)
+ c->c.ra_mode = RA_ACCEPTED;
+ else
+ c->c.ra_mode = RA_OPTIMAL;
}
p->afi_map = mb_alloc(p->p.pool, num * sizeof(u32));
@@ -554,6 +559,10 @@ bgp_conn_leave_established_state(struct bgp_proto *p)
BGP_TRACE(D_EVENTS, "BGP session closed");
p->conn = NULL;
+ // XXXX free these tables to avoid memory leak during graceful restart
+ // bgp_free_prefix_table(p);
+ // bgp_free_bucket_table(p);
+
if (p->p.proto_state == PS_UP)
bgp_stop(p, 0);
}
@@ -1411,8 +1420,6 @@ bgp_channel_init(struct channel *C, struct channel_config *CF)
struct bgp_channel *c = (void *) C;
struct bgp_channel_config *cf = (void *) CF;
- C->ra_mode = cf->secondary ? RA_ACCEPTED : RA_OPTIMAL;
-
c->cf = cf;
c->afi = cf->afi;
c->desc = bgp_get_af_desc(c->afi);
@@ -1756,11 +1763,132 @@ bgp_get_status(struct proto *P, byte *buf)
bsprintf(buf, "%-14s%s%s", bgp_state_dsc(p), err1, err2);
}
+static void
+bgp_show_afis(int code, char *s, u32 *afis, uint count)
+{
+ buffer b;
+ LOG_BUFFER_INIT(b);
+
+ buffer_puts(&b, s);
+
+ for (u32 *af = afis; af < (afis + count); af++)
+ {
+ const struct bgp_af_desc *desc = bgp_get_af_desc(*af);
+ if (desc)
+ buffer_print(&b, " %s", desc->name);
+ else
+ buffer_print(&b, " <%u/%u>", BGP_AFI(*af), BGP_SAFI(*af));
+ }
+
+ if (b.pos == b.end)
+ strcpy(b.end - 32, " ... ");
+
+ cli_msg(code, b.start);
+}
+
+static void
+bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
+{
+ struct bgp_af_caps *ac;
+ uint any_mp_bgp = 0;
+ uint any_gr_able = 0;
+ uint any_add_path = 0;
+ uint any_ext_next_hop = 0;
+ u32 *afl1 = alloca(caps->af_count * sizeof(u32));
+ u32 *afl2 = alloca(caps->af_count * sizeof(u32));
+ uint afn1, afn2;
+
+ WALK_AF_CAPS(caps, ac)
+ {
+ any_mp_bgp |= ac->ready;
+ any_gr_able |= ac->gr_able;
+ any_add_path |= ac->add_path;
+ any_ext_next_hop |= ac->ext_next_hop;
+ }
+
+ if (any_mp_bgp)
+ {
+ cli_msg(-1006, " Multiprotocol");
+
+ afn1 = 0;
+ WALK_AF_CAPS(caps, ac)
+ if (ac->ready)
+ afl1[afn1++] = ac->afi;
+
+ bgp_show_afis(-1006, " AF announced:", afl1, afn1);
+ }
+
+ if (caps->route_refresh)
+ cli_msg(-1006, " Route refresh");
+
+ if (any_ext_next_hop)
+ {
+ cli_msg(-1006, " Extended next hop");
+
+ afn1 = 0;
+ WALK_AF_CAPS(caps, ac)
+ if (ac->ext_next_hop)
+ afl1[afn1++] = ac->afi;
+
+ bgp_show_afis(-1006, " IPv6 nexthop:", afl1, afn1);
+ }
+
+ if (caps->ext_messages)
+ cli_msg(-1006, " Extended message");
+
+ if (caps->gr_aware)
+ cli_msg(-1006, " Graceful restart");
+
+ if (any_gr_able)
+ {
+ /* Continues from gr_aware */
+ cli_msg(-1006, " Restart time: %u", caps->gr_time);
+ if (caps->gr_flags & BGP_GRF_RESTART)
+ cli_msg(-1006, " Restart recovery");
+
+ afn1 = afn2 = 0;
+ WALK_AF_CAPS(caps, ac)
+ {
+ if (ac->gr_able)
+ afl1[afn1++] = ac->afi;
+
+ if (ac->gr_af_flags & BGP_GRF_FORWARDING)
+ afl2[afn2++] = ac->afi;
+ }
+
+ bgp_show_afis(-1006, " AF supported:", afl1, afn1);
+ bgp_show_afis(-1006, " AF preserved:", afl2, afn2);
+ }
+
+ if (caps->as4_support)
+ cli_msg(-1006, " 4-octet AS numbers");
+
+ if (any_add_path)
+ {
+ cli_msg(-1006, " ADD-PATH");
+
+ afn1 = afn2 = 0;
+ WALK_AF_CAPS(caps, ac)
+ {
+ if (ac->add_path & BGP_ADD_PATH_RX)
+ afl1[afn1++] = ac->afi;
+
+ if (ac->add_path & BGP_ADD_PATH_TX)
+ afl2[afn2++] = ac->afi;
+ }
+
+ bgp_show_afis(-1006, " RX:", afl1, afn1);
+ bgp_show_afis(-1006, " TX:", afl2, afn2);
+ }
+
+ if (caps->enhanced_refresh)
+ cli_msg(-1006, " Enhanced refresh");
+}
+
static void
bgp_show_proto_info(struct proto *P)
{
struct bgp_proto *p = (struct bgp_proto *) P;
- struct bgp_conn *c = p->conn;
cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p));
cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
@@ -1789,15 +1917,11 @@ bgp_show_proto_info(struct proto *P)
else if (P->proto_state == PS_UP)
{
cli_msg(-1006, " Neighbor ID: %R", p->remote_id);
+ cli_msg(-1006, " Local capabilities");
+ bgp_show_capabilities(p, p->conn->local_caps);
+ cli_msg(-1006, " Neighbor capabilities");
+ bgp_show_capabilities(p, p->conn->remote_caps);
/* XXXX
- cli_msg(-1006, " Neighbor caps: %s%s%s%s%s%s%s",
- c->peer_refresh_support ? " refresh" : "",
- c->peer_enhanced_refresh_support ? " enhanced-refresh" : "",
- c->peer_gr_able ? " restart-able" : (c->peer_gr_aware ? " restart-aware" : ""),
- c->peer_as4_support ? " AS4" : "",
- (c->peer_add_path & ADD_PATH_RX) ? " add-path-rx" : "",
- (c->peer_add_path & ADD_PATH_TX) ? " add-path-tx" : "",
- c->peer_ext_messages_support ? " ext-messages" : "");
cli_msg(-1006, " Session: %s%s%s%s%s%s%s%s",
p->is_internal ? "internal" : "external",
p->cf->multihop ? " multihop" : "",
@@ -1810,9 +1934,9 @@ bgp_show_proto_info(struct proto *P)
*/
cli_msg(-1006, " Source address: %I", p->source_addr);
cli_msg(-1006, " Hold timer: %d/%d",
- tm_remains(c->hold_timer), c->hold_time);
+ tm_remains(p->conn->hold_timer), p->conn->hold_time);
cli_msg(-1006, " Keepalive timer: %d/%d",
- tm_remains(c->keepalive_timer), c->keepalive_time);
+ tm_remains(p->conn->keepalive_timer), p->conn->keepalive_time);
}
if ((p->last_error_class != BE_NONE) &&
@@ -1846,7 +1970,7 @@ struct protocol proto_bgp = {
.template = "bgp%d",
.attr_class = EAP_BGP,
.preference = DEF_PREF_BGP,
- .channel_mask = NB_IP,
+ .channel_mask = NB_IP | NB_FLOW4 | NB_FLOW6,
.proto_size = sizeof(struct bgp_proto),
.config_size = sizeof(struct bgp_config),
.postconfig = bgp_postconfig,
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 4ecb86a0..5d2539d5 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -31,6 +31,7 @@ struct eattr;
#define BGP_SAFI_UNICAST 1
#define BGP_SAFI_MULTICAST 2
+#define BGP_SAFI_FLOW 133
/* Internal AF codes */
@@ -42,6 +43,8 @@ struct eattr;
#define BGP_AF_IPV6 BGP_AF( BGP_AFI_IPV6, BGP_SAFI_UNICAST )
#define BGP_AF_IPV4_MC BGP_AF( BGP_AFI_IPV4, BGP_SAFI_MULTICAST )
#define BGP_AF_IPV6_MC BGP_AF( BGP_AFI_IPV6, BGP_SAFI_MULTICAST )
+#define BGP_AF_FLOW4 BGP_AF( BGP_AFI_IPV4, BGP_SAFI_FLOW )
+#define BGP_AF_FLOW6 BGP_AF( BGP_AFI_IPV6, BGP_SAFI_FLOW )
struct bgp_write_state;
@@ -70,7 +73,7 @@ struct bgp_config {
u16 local_port; /* Local listening port */
u16 remote_port; /* Neighbor destination port */
int multihop; /* Number of hops if multihop */
- int strict_bind; /* Bind listening socket to local address XXXX */
+ int strict_bind; /* Bind listening socket to local address */
int ttl_security; /* Enable TTL security [RFC 5082] */
int compare_path_lengths; /* Use path lengths when selecting best route */
int med_metric; /* Compare MULTI_EXIT_DISC even between routes from differen ASes */
@@ -120,6 +123,7 @@ struct bgp_channel_config {
u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
u8 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
u8 gr_able; /* Allow full graceful restart for the channel */
+ u8 ext_next_hop; /* Allow both IPv4 and IPv6 next hops */
u8 add_path; /* Use ADD-PATH extension [RFC 7911] */
struct rtable_config *igp_table; /* Table used for recursive next hop lookups */
@@ -151,6 +155,7 @@ struct bgp_af_caps {
u8 ready; /* Multiprotocol capability, RFC 4760 */
u8 gr_able; /* Graceful restart support, RFC 4724 */
u8 gr_af_flags; /* Graceful restart per-AF flags */
+ u8 ext_next_hop; /* Extended IPv6 next hop, RFC 5549 */
u8 add_path; /* Multiple paths support, RFC 7911 */
};
@@ -171,6 +176,10 @@ struct bgp_caps {
struct bgp_af_caps af_data[0]; /* Per-AF capability data */
};
+#define WALK_AF_CAPS(caps,ac) \
+ for (ac = caps->af_data; ac < &caps->af_data[caps->af_count]; ac++)
+
+
struct bgp_socket {
node n; /* Node in global bgp_sockets */
sock *sk; /* Real listening socket */
@@ -267,6 +276,8 @@ struct bgp_channel {
u8 gr_ready; /* Neighbor could do GR on this AF */
u8 gr_active; /* Neighbor is doing GR and keeping fwd state */
+ u8 ext_next_hop; /* Session allows both IPv4 and IPv6 next hops */
+
u8 add_path_rx; /* Session expects receive of ADD-PATH extended NLRI */
u8 add_path_tx; /* Session expects transmit of ADD-PATH extended NLRI */
@@ -446,18 +457,6 @@ bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)
{ eattr *e = bgp_set_attr(to, pool, code, 0, 0); e->type = EAF_TYPE_UNDEF; }
-
-
-/* Hack: although BA_NEXT_HOP attribute has type EAF_TYPE_IP_ADDRESS, in IPv6
- * we store two addesses in it - a global address and a link local address.
- */
-#ifdef XXX
-#define NEXT_HOP_LENGTH (2*sizeof(ip_addr))
-static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = addr; ((ip_addr *) b)[1] = IPA_NONE; }
-#define NEXT_HOP_LENGTH sizeof(ip_addr)
-static inline void set_next_hop(byte *b, ip_addr addr) { ((ip_addr *) b)[0] = addr; }
-#endif
-
int bgp_encode_attrs(struct bgp_write_state *s, ea_list *attrs, byte *buf, byte *end);
ea_list * bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len);
@@ -510,26 +509,22 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
#define BAF_PARTIAL 0x20
#define BAF_EXT_LEN 0x10
-#define BA_ORIGIN 0x01 /* [RFC1771] */ /* WM */
+#define BA_ORIGIN 0x01 /* RFC 4271 */ /* WM */
#define BA_AS_PATH 0x02 /* WM */
#define BA_NEXT_HOP 0x03 /* WM */
#define BA_MULTI_EXIT_DISC 0x04 /* ON */
#define BA_LOCAL_PREF 0x05 /* WD */
#define BA_ATOMIC_AGGR 0x06 /* WD */
#define BA_AGGREGATOR 0x07 /* OT */
-#define BA_COMMUNITY 0x08 /* [RFC1997] */ /* OT */
-#define BA_ORIGINATOR_ID 0x09 /* [RFC1966] */ /* ON */
-#define BA_CLUSTER_LIST 0x0a /* ON */
-/* We don't support these: */
-#define BA_DPA 0x0b /* ??? */
-#define BA_ADVERTISER 0x0c /* [RFC1863] */
-#define BA_RCID_PATH 0x0d
-#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */
-#define BA_MP_UNREACH_NLRI 0x0f
+#define BA_COMMUNITY 0x08 /* RFC 1997 */ /* OT */
+#define BA_ORIGINATOR_ID 0x09 /* RFC 4456 */ /* ON */
+#define BA_CLUSTER_LIST 0x0a /* RFC 4456 */ /* ON */
+#define BA_MP_REACH_NLRI 0x0e /* RFC 4760 */
+#define BA_MP_UNREACH_NLRI 0x0f /* RFC 4760 */
#define BA_EXT_COMMUNITY 0x10 /* RFC 4360 */
#define BA_AS4_PATH 0x11 /* RFC 6793 */
#define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */
-#define BA_LARGE_COMMUNITY 0x20 /* [draft-ietf-idr-large-community] */
+#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
/* BGP connection states */
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 10a338d8..8c63b331 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -28,7 +28,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
BGP_CLUSTER_LIST, IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL,
SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
- STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST)
+ STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6)
%type bgp_afi
@@ -139,6 +139,8 @@ bgp_afi:
| IPV6 { $$ = BGP_AF_IPV6; }
| IPV4 MULTICAST { $$ = BGP_AF_IPV4_MC; }
| IPV6 MULTICAST { $$ = BGP_AF_IPV6_MC; }
+ | FLOW4 { $$ = BGP_AF_FLOW4; }
+ | FLOW6 { $$ = BGP_AF_FLOW6; }
;
bgp_channel_start: bgp_afi
@@ -150,6 +152,7 @@ bgp_channel_start: bgp_afi
this_channel = channel_config_new(&channel_bgp, desc->net, this_proto);
BGP_CC->c.name = desc->name;
+ BGP_CC->c.ra_mode = RA_UNDEF;
BGP_CC->afi = $1;
BGP_CC->gr_able = 0xff; /* undefined */
};
@@ -166,6 +169,7 @@ bgp_channel_item:
| GATEWAY RECURSIVE { BGP_CC->gw_mode = GW_RECURSIVE; }
| SECONDARY bool { BGP_CC->secondary = $2; }
| GRACEFUL RESTART bool { BGP_CC->gr_able = $3; }
+ | EXTENDED NEXT HOP bool { BGP_CC->ext_next_hop = $4; }
| ADD PATHS RX { BGP_CC->add_path = BGP_ADD_PATH_RX; }
| ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; }
| ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; }
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 9380f999..2106e0d1 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -20,6 +20,7 @@
#include "nest/mrtdump.h"
#include "conf/conf.h"
#include "lib/unaligned.h"
+#include "lib/flowspec.h"
#include "lib/socket.h"
#include "nest/cli.h"
@@ -184,9 +185,6 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf)
/* Capability negotiation as per RFC 5492 */
-#define WALK_AF_CAPS(caps,ac) \
- for (ac = caps->af_data; ac < &caps->af_data[caps->af_count]; ac++)
-
const struct bgp_af_caps *
bgp_find_af_caps(struct bgp_caps *caps, u32 afi)
{
@@ -230,6 +228,7 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
struct bgp_channel *c;
struct bgp_caps *caps;
struct bgp_af_caps *ac;
+ uint any_ext_next_hop = 0;
uint any_add_path = 0;
byte *data;
@@ -261,6 +260,9 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
ac->afi = c->afi;
ac->ready = 1;
+ ac->ext_next_hop = bgp_channel_is_ipv4(c) && c->cf->ext_next_hop;
+ any_ext_next_hop |= ac->ext_next_hop;
+
ac->add_path = c->cf->add_path;
any_add_path |= ac->add_path;
@@ -279,6 +281,12 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
/* Create capability list in buffer */
+ /*
+ * Note that max length is ~ 20+14*af_count. With max 6 channels that is
+ * 104. Option limit is 253 and buffer size is 4096, so we cannot overflow
+ * unless we add new capabilities or more AFs.
+ */
+
WALK_AF_CAPS(caps, ac)
if (ac->ready)
{
@@ -294,6 +302,23 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
*buf++ = 0; /* Capability data length */
}
+ if (any_ext_next_hop)
+ {
+ *buf++ = 5; /* Capability 5: Support for extended next hop */
+ *buf++ = 0; /* Capability data length, will be fixed later */
+ data = buf;
+
+ WALK_AF_CAPS(caps, ac)
+ if (ac->ext_next_hop)
+ {
+ put_af4(buf, ac->afi);
+ put_u16(buf+4, BGP_AFI_IPV6);
+ buf += 6;
+ }
+
+ data[-1] = buf - data;
+ }
+
if (caps->ext_messages)
{
*buf++ = 6; /* Capability 6: Support for extended messages */
@@ -352,8 +377,6 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
*buf++ = 0; /* Capability data length */
}
- /* FIXME: Should not XXXX 255 */
-
return buf;
}
@@ -392,6 +415,23 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i
caps->route_refresh = 1;
break;
+ case 5: /* Extended next hop encoding capability, RFC 5549 */
+ if (cl % 6)
+ goto err;
+
+ for (i = 0; i < cl; i += 6)
+ {
+ /* Specified only for IPv4 prefixes with IPv6 next hops */
+ if ((get_u16(pos+2+i+0) != BGP_AFI_IPV4) ||
+ (get_u16(pos+2+i+4) != BGP_AFI_IPV6))
+ continue;
+
+ af = get_af4(pos+2+i);
+ ac = bgp_get_af_caps(caps, af);
+ ac->ext_next_hop = 1;
+ }
+ break;
+
case 6: /* Extended message length capability, RFC draft */
if (cl != 0)
goto err;
@@ -673,9 +713,13 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
#define REPORT(msg, args...) \
({ log(L_REMOTE "%s: " msg, s->proto->p.name, ## args); })
+#define DISCARD(msg, args...) \
+ ({ REPORT(msg, ## args); return; })
+
#define WITHDRAW(msg, args...) \
({ REPORT(msg, ## args); s->err_withdraw = 1; return; })
+#define BAD_AFI "Unexpected AF <%u/%u> in UPDATE"
#define BAD_NEXT_HOP "Invalid NEXT_HOP attribute"
#define NO_NEXT_HOP "Missing NEXT_HOP attribute"
@@ -792,6 +836,32 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
WITHDRAW(BAD_NEXT_HOP);
}
+static uint
+bgp_encode_next_hop_none(struct bgp_write_state *s UNUSED, eattr *a UNUSED, byte *buf UNUSED, uint size UNUSED)
+{
+ return 0;
+}
+
+static void
+bgp_decode_next_hop_none(struct bgp_parse_state *s UNUSED, byte *data UNUSED, uint len UNUSED, rta *a UNUSED)
+{
+ /*
+ * Although we expect no next hop and RFC 7606 7.11 states that attribute
+ * MP_REACH_NLRI with unexpected next hop length is considered malformed,
+ * FlowSpec RFC 5575 4 states that next hop shall be ignored on receipt.
+ */
+
+ return;
+}
+
+static void
+bgp_update_next_hop_none(struct bgp_export_state *s, eattr *a, ea_list **to)
+{
+ /* NEXT_HOP shall not pass */
+ if (a)
+ bgp_unset_attr(to, s->pool, BA_NEXT_HOP);
+}
+
/*
* UPDATE
@@ -1065,6 +1135,190 @@ bgp_decode_next_hop_ip6(struct bgp_parse_state *s, byte *data, uint len, rta *a)
}
+static uint
+bgp_encode_nlri_flow4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
+{
+ byte *pos = buf;
+
+ while (!EMPTY_LIST(buck->prefixes) && (size >= 4))
+ {
+ struct bgp_prefix *px = HEAD(buck->prefixes);
+ struct net_addr_flow4 *net = (void *) px->net;
+ uint flen = net->length - sizeof(net_addr_flow4);
+
+ /* Encode path ID */
+ if (s->add_path)
+ {
+ put_u32(pos, px->path_id);
+ ADVANCE(pos, size, 4);
+ }
+
+ if (flen > size)
+ break;
+
+ /* Copy whole flow data including length */
+ memcpy(pos, net->data, flen);
+ ADVANCE(pos, size, flen);
+
+ bgp_free_prefix(s->channel, px);
+ }
+
+ return pos - buf;
+}
+
+static void
+bgp_decode_nlri_flow4(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
+{
+ while (len)
+ {
+ u32 path_id = 0;
+
+ /* Decode path ID */
+ if (s->add_path)
+ {
+ if (len < 4)
+ bgp_parse_error(s, 1);
+
+ path_id = get_u32(pos);
+ ADVANCE(pos, len, 4);
+ }
+
+ if (len < 2)
+ bgp_parse_error(s, 1);
+
+ /* Decode flow length */
+ uint hlen = flow_hdr_length(pos);
+ uint dlen = flow_read_length(pos);
+ uint flen = hlen + dlen;
+ byte *data = pos + hlen;
+
+ if (len < flen)
+ bgp_parse_error(s, 1);
+
+ /* Validate flow data */
+ enum flow_validated_state r = flow4_validate(data, dlen);
+ if (r != FLOW_ST_VALID)
+ {
+ log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r));
+ bgp_parse_error(s, 1);
+ }
+
+ if (data[0] != FLOW_TYPE_DST_PREFIX)
+ {
+ log(L_REMOTE "%s: No dst prefix at first pos", s->proto->p.name);
+ bgp_parse_error(s, 1);
+ }
+
+ /* Decode dst prefix */
+ ip4_addr px = IP4_NONE;
+ uint pxlen = data[1];
+
+ // FIXME: Use some generic function
+ memcpy(&px, data, BYTES(pxlen));
+ px = ip4_and(px, ip4_mkmask(pxlen));
+
+ /* Prepare the flow */
+ net_addr *n = alloca(sizeof(struct net_addr_flow4) + flen);
+ net_fill_flow4(n, px, pxlen, pos, flen);
+ ADVANCE(pos, len, flen);
+
+ bgp_rte_update(s, n, path_id, a);
+ }
+}
+
+
+static uint
+bgp_encode_nlri_flow6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
+{
+ byte *pos = buf;
+
+ while (!EMPTY_LIST(buck->prefixes) && (size >= 4))
+ {
+ struct bgp_prefix *px = HEAD(buck->prefixes);
+ struct net_addr_flow6 *net = (void *) px->net;
+ uint flen = net->length - sizeof(net_addr_flow6);
+
+ /* Encode path ID */
+ if (s->add_path)
+ {
+ put_u32(pos, px->path_id);
+ ADVANCE(pos, size, 4);
+ }
+
+ if (flen > size)
+ break;
+
+ /* Copy whole flow data including length */
+ memcpy(pos, net->data, flen);
+ ADVANCE(pos, size, flen);
+
+ bgp_free_prefix(s->channel, px);
+ }
+
+ return pos - buf;
+}
+
+static void
+bgp_decode_nlri_flow6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
+{
+ while (len)
+ {
+ u32 path_id = 0;
+
+ /* Decode path ID */
+ if (s->add_path)
+ {
+ if (len < 4)
+ bgp_parse_error(s, 1);
+
+ path_id = get_u32(pos);
+ ADVANCE(pos, len, 4);
+ }
+
+ if (len < 2)
+ bgp_parse_error(s, 1);
+
+ /* Decode flow length */
+ uint hlen = flow_hdr_length(pos);
+ uint dlen = flow_read_length(pos);
+ uint flen = hlen + dlen;
+ byte *data = pos + hlen;
+
+ if (len < flen)
+ bgp_parse_error(s, 1);
+
+ /* Validate flow data */
+ enum flow_validated_state r = flow6_validate(data, dlen);
+ if (r != FLOW_ST_VALID)
+ {
+ log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r));
+ bgp_parse_error(s, 1);
+ }
+
+ if (data[0] != FLOW_TYPE_DST_PREFIX)
+ {
+ log(L_REMOTE "%s: No dst prefix at first pos", s->proto->p.name);
+ bgp_parse_error(s, 1);
+ }
+
+ /* Decode dst prefix */
+ ip6_addr px = IP6_NONE;
+ uint pxlen = data[1];
+
+ // FIXME: Use some generic function
+ memcpy(&px, data, BYTES(pxlen));
+ px = ip6_and(px, ip6_mkmask(pxlen));
+
+ /* Prepare the flow */
+ net_addr *n = alloca(sizeof(struct net_addr_flow6) + flen);
+ net_fill_flow6(n, px, pxlen, pos, flen);
+ ADVANCE(pos, len, flen);
+
+ bgp_rte_update(s, n, path_id, a);
+ }
+}
+
+
static const struct bgp_af_desc bgp_af_table[] = {
{
.afi = BGP_AF_IPV4,
@@ -1086,6 +1340,16 @@ static const struct bgp_af_desc bgp_af_table[] = {
.decode_next_hop = bgp_decode_next_hop_ip4,
.update_next_hop = bgp_update_next_hop_ip,
},
+ {
+ .afi = BGP_AF_FLOW4,
+ .net = NET_FLOW4,
+ .name = "flow4",
+ .encode_nlri = bgp_encode_nlri_flow4,
+ .decode_nlri = bgp_decode_nlri_flow4,
+ .encode_next_hop = bgp_encode_next_hop_none,
+ .decode_next_hop = bgp_decode_next_hop_none,
+ .update_next_hop = bgp_update_next_hop_none,
+ },
{
.afi = BGP_AF_IPV6,
.net = NET_IP6,
@@ -1106,6 +1370,16 @@ static const struct bgp_af_desc bgp_af_table[] = {
.decode_next_hop = bgp_decode_next_hop_ip6,
.update_next_hop = bgp_update_next_hop_ip,
},
+ {
+ .afi = BGP_AF_FLOW6,
+ .net = NET_FLOW6,
+ .name = "flow6",
+ .encode_nlri = bgp_encode_nlri_flow6,
+ .decode_nlri = bgp_decode_nlri_flow6,
+ .encode_next_hop = bgp_encode_next_hop_none,
+ .decode_next_hop = bgp_decode_next_hop_none,
+ .update_next_hop = bgp_update_next_hop_none,
+ },
};
const struct bgp_af_desc *
@@ -1387,15 +1661,15 @@ bgp_create_end_mark(struct bgp_channel *c, byte *buf)
}
static inline void
-bgp_rx_end_mark(struct bgp_proto *p, u32 afi)
+bgp_rx_end_mark(struct bgp_parse_state *s, u32 afi)
{
+ struct bgp_proto *p = s->proto;
struct bgp_channel *c = bgp_get_channel(p, afi);
BGP_TRACE(D_PACKETS, "Got END-OF-RIB");
- /* XXXX handle unknown AF in MP_*_NLRI */
if (!c)
- return;
+ DISCARD(BAD_AFI, BGP_AFI(afi), BGP_SAFI(afi));
if (c->load_state == BFS_LOADING)
c->load_state = BFS_NONE;
@@ -1413,9 +1687,8 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
struct bgp_channel *c = bgp_get_channel(s->proto, afi);
rta *a = NULL;
- /* XXXX handle unknown AF in MP_*_NLRI */
if (!c)
- return;
+ DISCARD(BAD_AFI, BGP_AFI(afi), BGP_SAFI(afi));
s->channel = c;
s->add_path = c->add_path_rx;
@@ -1523,12 +1796,12 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
/* Check for End-of-RIB marker */
if (!s.attr_len && !s.ip_unreach_len && !s.ip_reach_len)
- { bgp_rx_end_mark(p, BGP_AF_IPV4); goto done; }
+ { bgp_rx_end_mark(&s, BGP_AF_IPV4); goto done; }
/* Check for MP End-of-RIB marker */
if ((s.attr_len < 8) && !s.ip_unreach_len && !s.ip_reach_len &&
- !s.mp_reach_len && !s.mp_unreach_len && s.mp_unreach_af) /* XXXX See RFC 7606 5.2 */
- { bgp_rx_end_mark(p, s.mp_unreach_af); goto done; }
+ !s.mp_reach_len && !s.mp_unreach_len && s.mp_unreach_af)
+ { bgp_rx_end_mark(&s, s.mp_unreach_af); goto done; }
if (s.ip_unreach_len)
bgp_decode_nlri(&s, BGP_AF_IPV4, s.ip_unreach_nlri, s.ip_unreach_len, NULL, NULL, 0);
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index 1b0ac5e9..df9eb75b 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -421,10 +421,9 @@ add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_ent
if (en == oa->rt)
{
/*
- * Local stub networks does not have proper iface in en->nhi
- * (because they all have common top_hash_entry en).
- * We have to find iface responsible for that stub network.
- * Configured stubnets does not have any iface. They will
+ * Local stub networks do not have proper iface in en->nhi (because they all
+ * have common top_hash_entry en). We have to find iface responsible for
+ * that stub network. Configured stubnets do not have any iface. They will
* be removed in rt_sync().
*/
@@ -1428,7 +1427,6 @@ ospf_ext_spf(struct ospf_proto *p)
struct top_hash_entry *en;
struct ospf_lsa_ext_local rt;
ort *nf1, *nf2;
- orta nfa = {};
u32 br_metric;
struct ospf_area *atmp;
@@ -1436,6 +1434,8 @@ ospf_ext_spf(struct ospf_proto *p)
WALK_SLIST(en, p->lsal)
{
+ orta nfa = {};
+
/* 16.4. (1) */
if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_NSSA))
continue;
@@ -1577,6 +1577,7 @@ ospf_rt_reset(struct ospf_proto *p)
FIB_WALK(&p->rtf, ort, ri)
{
ri->area_net = 0;
+ ri->keep = 0;
reset_ri(ri);
}
FIB_WALK_END;
@@ -1939,9 +1940,12 @@ again1:
}
}
- /* Remove configured stubnets */
- if (!nf->n.nhs)
+ /* Remove configured stubnets but keep the entries */
+ if (nf->n.type && !nf->n.nhs)
+ {
reset_ri(nf);
+ nf->keep = 1;
+ }
if (nf->n.type) /* Add the route */
{
@@ -1981,7 +1985,7 @@ again1:
}
/* Remove unused rt entry, some special entries are persistent */
- if (!nf->n.type && !nf->external_rte && !nf->area_net)
+ if (!nf->n.type && !nf->external_rte && !nf->area_net && !nf->keep)
{
if (nf->lsa_id)
idm_free(&p->idm, nf->lsa_id);
diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h
index 842792f0..589d2bc5 100644
--- a/proto/ospf/rt.h
+++ b/proto/ospf/rt.h
@@ -84,6 +84,7 @@ typedef struct ort
u32 lsa_id;
u8 external_rte;
u8 area_net;
+ u8 keep;
struct fib_node fn;
}
diff --git a/proto/rpki/Makefile b/proto/rpki/Makefile
index bd76145e..eb09b7df 100644
--- a/proto/rpki/Makefile
+++ b/proto/rpki/Makefile
@@ -2,3 +2,5 @@ src := rpki.c packets.c tcp_transport.c ssh_transport.c transport.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
+
+tests_objs := $(tests_objs) $(src-o-files)
\ No newline at end of file
diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c
index b9d93106..60ca3936 100644
--- a/proto/rpki/packets.c
+++ b/proto/rpki/packets.c
@@ -531,8 +531,6 @@ rpki_send_pdu(struct rpki_cache *cache, const void *pdu, const uint len)
static int
rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu)
{
- struct rpki_proto *p = cache->p;
- int error = RPKI_SUCCESS;
u32 pdu_len = ntohl(pdu->len);
/*
@@ -557,7 +555,6 @@ rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu
*/
}
else if (cache->last_update == 0
- && pdu->ver >= RPKI_MIN_VERSION
&& pdu->ver <= RPKI_MAX_VERSION
&& pdu->ver < cache->version)
{
@@ -608,7 +605,6 @@ rpki_handle_error_pdu(struct rpki_cache *cache, const struct pdu_error *pdu)
case UNSUPPORTED_PROTOCOL_VER:
CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version");
if (pdu->ver <= RPKI_MAX_VERSION &&
- pdu->ver >= RPKI_MIN_VERSION &&
pdu->ver < cache->version)
{
CACHE_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver);
diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c
index 497edd3c..5459d9c3 100644
--- a/proto/rpki/rpki.c
+++ b/proto/rpki/rpki.c
@@ -357,7 +357,7 @@ rpki_stop_retry_timer_event(struct rpki_cache *cache)
tm_stop(cache->retry_timer);
}
-static void
+static void UNUSED
rpki_stop_expire_timer_event(struct rpki_cache *cache)
{
CACHE_DBG(cache, "Stop");
@@ -636,7 +636,7 @@ rpki_shutdown(struct proto *P)
*/
static int
-rpki_try_fast_reconnect(struct rpki_cache *cache, struct rpki_config *new, struct rpki_config *old)
+rpki_try_fast_reconnect(struct rpki_cache *cache)
{
if (cache->state == RPKI_CS_ESTABLISHED)
{
@@ -660,11 +660,10 @@ rpki_try_fast_reconnect(struct rpki_cache *cache, struct rpki_config *new, struc
* protocol. Returns |NEED_TO_RESTART| or |SUCCESSFUL_RECONF|.
*/
static int
-rpki_reconfigure_cache(struct rpki_proto *p, struct rpki_cache *cache, struct rpki_config *new, struct rpki_config *old)
+rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, struct rpki_config *new, struct rpki_config *old)
{
u8 try_fast_reconnect = 0;
-
if (strcmp(old->hostname, new->hostname) != 0)
{
CACHE_TRACE(D_EVENTS, cache, "Cache server address changed to %s", new->hostname);
@@ -709,7 +708,7 @@ rpki_reconfigure_cache(struct rpki_proto *p, struct rpki_cache *cache, struct rp
#undef TEST_INTERVAL
if (try_fast_reconnect)
- return rpki_try_fast_reconnect(cache, new, old);
+ return rpki_try_fast_reconnect(cache);
return SUCCESSFUL_RECONF;
}
@@ -906,7 +905,7 @@ rpki_postconfig(struct proto_config *CF)
static void
rpki_copy_config(struct proto_config *dest, struct proto_config *src)
{
- /* Just a shallow copy */
+ /* FIXME: Should copy transport */
}
struct protocol proto_rpki = {
diff --git a/proto/rpki/rpki.h b/proto/rpki/rpki.h
index eaeed858..80ef83df 100644
--- a/proto/rpki/rpki.h
+++ b/proto/rpki/rpki.h
@@ -29,7 +29,6 @@
#define RPKI_VERSION_0 0
#define RPKI_VERSION_1 1
-#define RPKI_MIN_VERSION RPKI_VERSION_0
#define RPKI_MAX_VERSION RPKI_VERSION_1
diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h
index 0c5f8fbb..0e895e20 100644
--- a/sysdep/bsd/sysio.h
+++ b/sysdep/bsd/sysio.h
@@ -9,6 +9,7 @@
#include
#include // Workaround for some BSDs
#include
+#include
#ifdef __NetBSD__
@@ -179,8 +180,8 @@ sk_prepare_ip_header(sock *s, void *hdr, int dlen)
ip->ip_src = ipa_to_in4(s->saddr);
ip->ip_dst = ipa_to_in4(s->daddr);
-#ifdef __OpenBSD__
- /* OpenBSD expects ip_len in network order, other BSDs expect host order */
+#if (defined __OpenBSD__) || (defined __DragonFly__) || (defined __FreeBSD__ && (__FreeBSD_version >= 1100030))
+ /* Different BSDs have different expectations of ip_len endianity */
ip->ip_len = htons(ip->ip_len);
#endif
}
diff --git a/sysdep/config.h b/sysdep/config.h
index c7f63e69..eb7356cd 100644
--- a/sysdep/config.h
+++ b/sysdep/config.h
@@ -7,7 +7,7 @@
#define _BIRD_CONFIG_H_
/* BIRD version */
-#define BIRD_VERSION "1.6.2"
+#define BIRD_VERSION "2.0.0-pre0"
/* Include parameters determined by configure script */
#include "sysdep/autoconf.h"
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 80439c47..bb85a38b 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -1809,6 +1809,7 @@ nl_async_hook(sock *sk, uint size UNUSED)
* One day we might react to it by asking for route table
* scan in near future.
*/
+ log(L_WARN "Kernel dropped some netlink messages, will resync on next scan.");
return 1; /* More data are likely to be ready */
}
else if (errno != EWOULDBLOCK)
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index c8916378..6722fc69 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -2631,7 +2631,8 @@ io_loop(void)
if (pfd[s->index].revents & (POLLHUP | POLLERR))
{
sk_err(s, pfd[s->index].revents);
- goto next2;
+ if (s != current_sock)
+ goto next2;
}
current_sock = sk_next(s);
diff --git a/test/birdtest.c b/test/birdtest.c
index 73cb9901..a4312e9b 100644
--- a/test/birdtest.c
+++ b/test/birdtest.c
@@ -200,7 +200,7 @@ bt_log_result(int result, const char *fmt, va_list argptr)
vsnprintf(pos, sizeof(msg_buf) - (pos - msg_buf), fmt, argptr);
int chrs = 0;
- for (int i = 0; i < strlen(msg_buf); i += get_num_terminal_cols())
+ for (uint i = 0; i < strlen(msg_buf); i += get_num_terminal_cols())
{
if (i)
printf("\n");
diff --git a/tools/gendist b/tools/gendist
index d9b34941..09d5d055 100755
--- a/tools/gendist
+++ b/tools/gendist
@@ -8,10 +8,10 @@ set -e
AC=`if [ -x /usr/bin/autoconf2.50 ] ; then echo autoconf2.50 ; else echo autoconf ; fi`
$AC
./configure
+make docs
make distclean
$AC
rm -rf autom4te*cache
-( cd doc ; make docs ; make clean )
VERSION=`sed $T/$REL/ChangeLog
mv $T/$REL/doc/*.ps $T/$DREL/doc
+mv $T/$REL/doc/*.pdf $T/$DREL/doc
rm -f $T/$REL/bird.conf*
rm -rf $T/$REL/.git/
-rm -rf `find $T/$REL -name CVS -o -name tmp` $T/$REL/{misc,rfc,doc/slides}
+rm -rf `find $T/$REL -name CVS -o -name tmp` $T/$REL/{misc,rfc,doc/slides,doc/slt2001,doc/old,doc/*.out}
( cd $T ; tar czvvf $REL.tar.gz $REL )
( cd $T ; tar czvvf $DREL.tar.gz $DREL )
rm -rf $T/$REL $T/$DREL