Merge branch 'int-new' into nexthop-merged

This commit is contained in:
Jan Moskyto Matejka 2017-02-22 11:58:04 +01:00
commit c609d03986
39 changed files with 1284 additions and 455 deletions

23
NEWS
View file

@ -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) Version 1.6.2 (2016-09-29)
o Fixes serious bug introduced in the previous version o Fixes serious bug introduced in the previous version

View file

@ -186,8 +186,7 @@ fi
AC_SUBST(iproutedir) AC_SUBST(iproutedir)
# all_protocols="$proto_bfd babel bgp ospf pipe radv rip $proto_rpki static" 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=`echo $all_protocols | sed 's/ /,/g'` all_protocols=`echo $all_protocols | sed 's/ /,/g'`

View file

@ -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... # Yes, even shell-like comments work...

267
doc/bird.conf.example2 Normal file
View file

@ -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 ];
};
}

View file

@ -1945,12 +1945,11 @@ avoid routing loops.
<p>BIRD supports all requirements of the BGP4 standard as defined in <p>BIRD supports all requirements of the BGP4 standard as defined in
<rfc id="4271"> It also supports the community attributes (<rfc id="1997">), <rfc id="4271"> It also supports the community attributes (<rfc id="1997">),
capability negotiation (<rfc id="5492">), MD5 password authentication (<rfc capability negotiation (<rfc id="5492">), MD5 password authentication
id="2385">), extended communities (<rfc id="4360">), route reflectors (<rfc (<rfc id="2385">), extended communities (<rfc id="4360">), route reflectors
id="4456">), graceful restart (<rfc id="4724">), multiprotocol extensions (<rfc id="4456">), AS confederations (<rfc id="5065">), graceful restart
(<rfc id="4760">), 4B AS numbers (<rfc id="4893">), and 4B AS numbers in (<rfc id="4724">), multiprotocol extensions (<rfc id="4760">), 4B AS numbers
extended communities (<rfc id="5668">). (<rfc id="4893">), and 4B AS numbers in extended communities (<rfc id="5668">).
For IPv6, it uses the standard multiprotocol extensions defined in For IPv6, it uses the standard multiprotocol extensions defined in
<rfc id="4760"> and applied to IPv6 according to <rfc id="2545">. <rfc id="4760"> and applied to IPv6 according to <rfc id="2545">.
@ -2041,6 +2040,16 @@ using the following configuration parameters:
source address for the BGP session. Default: the address of the local source address for the BGP session. Default: the address of the local
end of the interface our neighbor is connected to. end of the interface our neighbor is connected to.
<tag><label id="bgp-strict-bind">strict bind <m/switch/</tag>
Specify whether BGP listening socket should be bound to a specific local
address (the same as the <cf/source address/) and associated interface,
or to all addresses. Binding to a specific address could be useful in
cases like running multiple BIRD instances on a machine, each using its
IP address. Note that listening sockets bound to a specific address and
to all addresses collide, therefore either all BGP protocols (of the
same address family and using the same local port) should have set
<cf/strict bind/, or none of them. Default: disabled.
<tag><label id="bgp-next-hop-self">next hop self</tag> <tag><label id="bgp-next-hop-self">next hop self</tag>
Avoid calculation of the Next Hop attribute and always advertise our own 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 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) <rfc id="5082"> support is Note that full (ICMP protection, for example) <rfc id="5082"> support is
provided by Linux only. Default: disabled. provided by Linux only. Default: disabled.
<tag><label id="bgp-pass">password <m/string/</tag> <tag><label id="bgp-password">password <m/string/</tag>
Use this password for MD5 authentication of BGP sessions (<rfc id="2385">). When Use this password for MD5 authentication of BGP sessions (<rfc id="2385">). When
used on BSD systems, see also <cf/setkey/ option below. Default: no used on BSD systems, see also <cf/setkey/ option below. Default: no
authentication. authentication.
@ -2134,6 +2143,21 @@ using the following configuration parameters:
accepting incoming connections. In passive mode, outgoing connections accepting incoming connections. In passive mode, outgoing connections
are not initiated. Default: off. are not initiated. Default: off.
<tag><label id="bgp-confederation">confederation <m/number/</tag>
BGP confederations (<rfc id="5065">) 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).
<tag><label id="bgp-confederation-member">confederation member <m/switch/</tag>
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.
<tag><label id="bgp-rr-client">rr client</tag> <tag><label id="bgp-rr-client">rr client</tag>
Be a route reflector and treat the neighbor as a route reflection Be a route reflector and treat the neighbor as a route reflection
client. Default: disabled. client. Default: disabled.
@ -2256,13 +2280,6 @@ using the following configuration parameters:
This option is relevant to IPv4 mode with enabled capability This option is relevant to IPv4 mode with enabled capability
advertisement only. Default: on. advertisement only. Default: on.
<tag><label id="bgp-route-limit">route limit <m/number/</tag>
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 <cf>import limit <m/number/ action
restart</cf>. This option is obsolete and it is replaced by
<ref id="proto-import-limit" name="import limit option">. Default: no limit.
<tag><label id="bgp-disable-after-error">disable after error <m/switch/</tag> <tag><label id="bgp-disable-after-error">disable after error <m/switch/</tag>
When an error is encountered (either locally or by the other side), When an error is encountered (either locally or by the other side),
disable the instance automatically and wait for an administrator to fix disable the instance automatically and wait for an administrator to fix

View file

@ -1578,6 +1578,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case P('<','='): TWOARGS; break; case P('<','='): TWOARGS; break;
case '!': ONEARG; break; case '!': ONEARG; break;
case P('!', '~'):
case '~': TWOARGS; break; case '~': TWOARGS; break;
case P('d','e'): ONEARG; break; case P('d','e'): ONEARG; break;

View file

@ -957,7 +957,7 @@ fragment_val_str(u8 val)
return "???"; return "???";
} }
static int static uint
net_format_flow(char *buf, uint blen, const byte *data, uint dlen, int ipv6) net_format_flow(char *buf, uint blen, const byte *data, uint dlen, int ipv6)
{ {
buffer b = { 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 * of written chars. If final string is too large, the string will ends the with
* ' ...}' sequence and zero-terminator. * ' ...}' sequence and zero-terminator.
*/ */
int uint
flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f) 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); 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 * of written chars. If final string is too large, the string will ends the with
* ' ...}' sequence and zero-terminator. * ' ...}' sequence and zero-terminator.
*/ */
int uint
flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f) 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); return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow6), 1);

View file

@ -42,6 +42,9 @@ const char *flow_type_str(enum flow_type type, int ipv6);
uint flow_write_length(byte *data, u16 len); 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) static inline u16 flow_read_length(const byte *data)
{ return ((*data & 0xf0) == 0xf0) ? get_u16(data) & 0x0fff : *data; } { return ((*data & 0xf0) == 0xf0) ? get_u16(data) & 0x0fff : *data; }
@ -128,7 +131,7 @@ void flow6_validate_cf(net_addr_flow6 *f);
* Net Formatting * Net Formatting
*/ */
int flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f); uint 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 flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f);
#endif /* _BIRD_FLOWSPEC_H_ */ #endif /* _BIRD_FLOWSPEC_H_ */

View file

@ -30,20 +30,17 @@ t_read_length(void)
{ {
byte data[] = { 0xcc, 0xcc, 0xcc }; byte data[] = { 0xcc, 0xcc, 0xcc };
u16 get;
u16 expect;
for (uint expect = 0; expect < 0xf0; expect++) for (uint expect = 0; expect < 0xf0; expect++)
{ {
*data = 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); bt_assert_msg(get == expect, "Testing get length 0x%02x (get 0x%02x)", expect, get);
} }
for (uint expect = 0; expect <= 0xfff; expect++) for (uint expect = 0; expect <= 0xfff; expect++)
{ {
put_u16(data, expect | 0xf000); 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); 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) t_write_length(void)
{ {
byte data[] = { 0xcc, 0xcc, 0xcc }; byte data[] = { 0xcc, 0xcc, 0xcc };
uint offset;
byte *c;
for (uint expect = 0; expect <= 0xfff; expect++) 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); uint set = (expect < 0xf0) ? *data : (get_u16(data) & 0x0fff);
bt_assert_msg(set == expect, "Testing set length 0x%03x (set 0x%03x)", expect, set); bt_assert_msg(set == expect, "Testing set length 0x%03x (set 0x%03x)", expect, set);

View file

@ -25,6 +25,12 @@
(v).data = mb_allocz(pool, HASH_SIZE(v) * sizeof(* (v).data)); \ (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...) \ #define HASH_FIND(v,id,key...) \
({ \ ({ \
u32 _h = HASH_FN(v, id, key); \ u32 _h = HASH_FN(v, id, key); \

View file

@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon Summary: BIRD Internet Routing Daemon
Name: bird Name: bird
Version: 1.6.2 Version: 2.0.0
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
Group: Networking/Daemons Group: Networking/Daemons

View file

@ -25,7 +25,7 @@
#define BAD(DSC, VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; }) #define BAD(DSC, VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
int 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; byte *pos = data;
char *err_dsc = NULL; char *err_dsc = NULL;
@ -43,9 +43,21 @@ as_path_valid(byte *data, uint len, int bs, char *err, uint elen)
if (len < slen) if (len < slen)
BAD("segment framing error", len); BAD("segment framing error", len);
/* XXXX handle CONFED segments */ switch (type)
if ((type != AS_PATH_SET) && (type != AS_PATH_SEQUENCE)) {
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); BAD("unknown segment", type);
}
if (pos[1] == 0) if (pos[1] == 0)
BAD("zero-length segment", type); BAD("zero-length segment", type);
@ -157,10 +169,13 @@ as_path_contains_confed(const struct adata *path)
return 0; return 0;
} }
static void struct adata *
as_path_strip_confed_(byte *dst, const byte *src, uint len) 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) while (src < end)
{ {
@ -176,18 +191,15 @@ as_path_strip_confed_(byte *dst, const byte *src, uint len)
src += slen; src += slen;
} }
/* Fix the result length */
res->length = dst - res->data;
return res;
} }
struct adata * struct adata *
as_path_strip_confed(struct linpool *pool, const struct adata *op) as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as)
{
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)
{ {
struct adata *np; struct adata *np;
const byte *pos = op->data; 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]; byte *dst = np->data + 2 + BS * np->data[1];
if (strip) memcpy(dst, pos, len);
as_path_strip_confed_(dst, pos, len);
else
memcpy(dst, pos, len);
} }
return np; return np;
@ -325,46 +334,49 @@ as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2)
} }
void 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; buffer buf = { .start = bb, .pos = bb, .end = bb + size }, *b = &buf;
const byte *e = p + path->length; const byte *pos = path->data;
byte *end = buf + size - 16; const byte *end = pos + path->length;
int sp = 1; const char *ops, *cls;
int l, isset;
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) case AS_PATH_SET: ops = "{"; cls = "}"; break;
{ case AS_PATH_SEQUENCE: ops = NULL; cls = NULL; break;
strcpy(buf, " ..."); case AS_PATH_CONFED_SEQUENCE: ops = "("; cls = ")"; break;
return; case AS_PATH_CONFED_SET: ops = "({"; cls = "})"; break;
} default: bug("Invalid path segment");
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;
}
} }
*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 int
@ -399,66 +411,80 @@ as_path_getlen(const struct adata *path)
int int
as_path_get_last(const struct adata *path, u32 *orig_as) 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; int found = 0;
u32 res = 0; u32 val = 0;
const u8 *p = path->data;
const u8 *q = p+path->length;
int len;
while (p<q) while (pos < end)
{
uint type = pos[0];
uint len = pos[1];
pos += 2;
if (!len)
continue;
switch (type)
{ {
switch (*p++) case AS_PATH_SET:
{ case AS_PATH_CONFED_SET:
case AS_PATH_SET: found = 0;
if (len = *p++) break;
{
found = 0; case AS_PATH_SEQUENCE:
p += BS * len; case AS_PATH_CONFED_SEQUENCE:
} val = get_as(pos + BS * (len - 1));
break; found = 1;
case AS_PATH_SEQUENCE: break;
if (len = *p++)
{ default:
found = 1; bug("Invalid path segment");
res = get_as(p + BS * (len - 1));
p += BS * len;
}
break;
default: bug("Invalid path segment");
}
} }
pos += BS * len;
}
if (found) if (found)
*orig_as = res; *orig_as = val;
return found; return found;
} }
u32 u32
as_path_get_last_nonaggregated(const struct adata *path) as_path_get_last_nonaggregated(const struct adata *path)
{ {
const u8 *p = path->data; const byte *pos = path->data;
const u8 *q = p+path->length; const byte *end = pos + path->length;
u32 res = 0; u32 val = 0;
int len;
while (p<q) while (pos < end)
{
uint type = pos[0];
uint len = pos[1];
pos += 2;
if (!len)
continue;
switch (type)
{ {
switch (*p++) case AS_PATH_SET:
{ case AS_PATH_CONFED_SET:
case AS_PATH_SET: return val;
return res;
case AS_PATH_SEQUENCE: case AS_PATH_SEQUENCE:
if (len = *p++) case AS_PATH_CONFED_SEQUENCE:
res = get_as(p + BS * (len - 1)); val = get_as(pos + BS * (len - 1));
p += BS * len; break;
break;
default: bug("Invalid path segment"); default:
} bug("Invalid path segment");
} }
return res; pos += BS * len;
}
return val;
} }
int int
@ -468,11 +494,47 @@ as_path_get_first(const struct adata *path, u32 *last_as)
if ((path->length == 0) || (p[0] != AS_PATH_SEQUENCE) || (p[1] == 0)) if ((path->length == 0) || (p[0] != AS_PATH_SEQUENCE) || (p[1] == 0))
return 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; return 1;
case AS_PATH_CONFED_SEQUENCE:
case AS_PATH_CONFED_SET:
break;
default:
bug("Invalid path segment");
} }
pos += BS * len;
}
return 0;
} }
int int
@ -597,43 +659,50 @@ struct pm_pos
}; };
static int 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 byte *pos = path->data;
const u8 *q = p + path->length; const byte *end = pos + path->length;
struct pm_pos *opos = pos; struct pm_pos *op = pp;
int i, len; uint i;
while (pos < end)
{
uint type = pos[0];
uint len = pos[1];
pos += 2;
while (p < q) switch (type)
switch (*p++) {
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: pp->set = 0;
pos->set = 1; pp->mark = 0;
pos->mark = 0; pp->val.asn = get_as(pos);
pos->val.sp = p; pp++;
len = *p;
p += 1 + BS * len;
pos++;
break;
case AS_PATH_SEQUENCE: pos += BS;
len = *p++;
for (i = 0; i < len; i++)
{
pos->set = 0;
pos->mark = 0;
pos->val.asn = get_as(p);
p += BS;
pos++;
}
break;
default:
bug("as_path_match: Invalid path component");
} }
break;
return pos - opos; default:
bug("Invalid path segment");
}
}
return pp - op;
} }
static int static int

View file

@ -85,15 +85,19 @@ t_path_format(void)
bt_debug("Prepending ASN: %10u \n", i); bt_debug("Prepending ASN: %10u \n", i);
} }
#define BUFFER_SIZE 26 #define BUFFER_SIZE 120
byte buf[BUFFER_SIZE] = {}; 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); 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 #define SMALL_BUFFER_SIZE 25
byte buf2[SMALL_BUFFER_SIZE] = {}; byte buf2[SMALL_BUFFER_SIZE] = {};
as_path_format(as_path, 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); rfree(lp);

View file

@ -233,6 +233,26 @@ lc_set_contains(struct adata *list, lcomm val)
return 0; 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 * struct adata *
int_set_add(struct linpool *pool, struct adata *list, u32 val) 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) if (list)
memcpy(res->data, list->data, list->length); memcpy(res->data, list->data, list->length);
u32 *c = (u32 *) (res->data + len); * (u32 *) (res->data + len) = val;
*c = val;
return res; return res;
} }

View file

@ -30,13 +30,13 @@
struct f_tree; 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_16to32(byte *dst, byte *src, uint len);
int as_path_32to16(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_as4(const struct adata *path);
int as_path_contains_confed(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_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); struct adata *as_path_to_old(struct linpool *pool, const struct adata *path);
void as_path_cut(struct adata *path, uint num); void as_path_cut(struct adata *path, uint num);
struct adata *as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2); 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(const struct adata *path);
int as_path_getlen_int(const struct adata *path, int bs); 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(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); int as_path_get_last(const struct adata *path, u32 *last_as);
u32 as_path_get_last_nonaggregated(const struct adata *path); u32 as_path_get_last_nonaggregated(const struct adata *path);
int as_path_contains(const struct adata *path, u32 as, int min); 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); 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) 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 #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 int_set_contains(struct adata *list, u32 val);
int ec_set_contains(struct adata *list, u64 val); int ec_set_contains(struct adata *list, u64 val);
int lc_set_contains(struct adata *list, lcomm 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 *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 *ec_set_add(struct linpool *pool, struct adata *list, u64 val);
struct adata *lc_set_add(struct linpool *pool, struct adata *list, lcomm val); struct adata *lc_set_add(struct linpool *pool, struct adata *list, lcomm val);

View file

@ -490,7 +490,7 @@ int
channel_reconfigure(struct channel *c, struct channel_config *cf) channel_reconfigure(struct channel *c, struct channel_config *cf)
{ {
/* FIXME: better handle these changes, also handle in_keep_filtered */ /* 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; return 0;
int import_changed = !filter_same(c->in_filter, cf->in_filter); int import_changed = !filter_same(c->in_filter, cf->in_filter);

View file

@ -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 */ /* 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_OPTIMAL 1 /* Announcement of optimal route change */
#define RA_ACCEPTED 2 /* Announcement of first accepted route */ #define RA_ACCEPTED 2 /* Announcement of first accepted route */
#define RA_ANY 3 /* Announcement of any route change */ #define RA_ANY 3 /* Announcement of any route change */

View file

@ -2,3 +2,5 @@ src := babel.c packets.c
obj := $(src-o-files) obj := $(src-o-files)
$(all-daemon) $(all-daemon)
$(cf-local) $(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -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_select_route(struct babel_entry *e);
static void babel_send_route_request(struct babel_entry *e, struct babel_neighbor *n); 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 void babel_send_wildcard_request(struct babel_iface *ifa);
static int babel_cache_seqno_request(struct babel_proto *p, ip_addr prefix, u8 plen, static int babel_cache_seqno_request(struct babel_proto *p, net_addr *n, u64 router_id, u16 seqno);
u64 router_id, u16 seqno);
static void babel_trigger_iface_update(struct babel_iface *ifa); static void babel_trigger_iface_update(struct babel_iface *ifa);
static void babel_trigger_update(struct babel_proto *p); static void babel_trigger_update(struct babel_proto *p);
static void babel_send_seqno_request(struct babel_entry *e); 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 static void
babel_init_entry(struct fib_node *n) babel_init_entry(void *E)
{ {
struct babel_entry *e = (void *) n; struct babel_entry *e = E;
e->proto = NULL;
e->selected_in = NULL;
e->selected_out = NULL;
e->updated = now; e->updated = now;
init_list(&e->sources); init_list(&e->sources);
init_list(&e->routes); init_list(&e->routes);
} }
static inline struct babel_entry * 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 * 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; e->proto = p;
return e; return e;
} }
@ -180,8 +177,8 @@ babel_flush_route(struct babel_route *r)
{ {
struct babel_proto *p = r->e->proto; struct babel_proto *p = r->e->proto;
DBG("Babel: Flush route %I/%d router_id %lR neigh %I\n", DBG("Babel: Flush route %N router_id %lR neigh %I\n",
r->e->n.prefix, r->e->n.pxlen, r->router_id, r->neigh ? r->neigh->addr : IPA_NONE); r->e->n.addr, r->router_id, r->neigh ? r->neigh->addr : IPA_NONE);
rem_node(NODE r); rem_node(NODE r);
@ -203,8 +200,8 @@ babel_expire_route(struct babel_route *r)
struct babel_proto *p = r->e->proto; struct babel_proto *p = r->e->proto;
struct babel_entry *e = r->e; struct babel_entry *e = r->e;
TRACE(D_EVENTS, "Route expiry timer for %I/%d router-id %lR fired", TRACE(D_EVENTS, "Route expiry timer for %N router-id %lR fired",
e->n.prefix, e->n.pxlen, r->router_id); e->n.addr, r->router_id);
if (r->metric < BABEL_INFINITY) if (r->metric < BABEL_INFINITY)
{ {
@ -229,16 +226,14 @@ babel_refresh_route(struct babel_route *r)
static void static void
babel_expire_routes(struct babel_proto *p) babel_expire_routes(struct babel_proto *p)
{ {
struct babel_entry *e;
struct babel_route *r, *rx; struct babel_route *r, *rx;
struct fib_iterator fit; struct fib_iterator fit;
FIB_ITERATE_INIT(&fit, &p->rtable); FIB_ITERATE_INIT(&fit, &p->rtable);
loop: 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; int changed = 0;
WALK_LIST_DELSAFE(r, rx, e->routes) WALK_LIST_DELSAFE(r, rx, e->routes)
@ -261,7 +256,7 @@ loop:
* babel_rt_notify() -> p->rtable change, invalidating hidden variables. * babel_rt_notify() -> p->rtable change, invalidating hidden variables.
*/ */
FIB_ITERATE_PUT(&fit, n); FIB_ITERATE_PUT(&fit);
babel_select_route(e); babel_select_route(e);
goto loop; goto loop;
} }
@ -271,12 +266,12 @@ loop:
/* Remove empty entries */ /* Remove empty entries */
if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes)) if (EMPTY_LIST(e->sources) && EMPTY_LIST(e->routes))
{ {
FIB_ITERATE_PUT(&fit, n); FIB_ITERATE_PUT(&fit);
fib_delete(&p->rtable, e); fib_delete(&p->rtable, e);
goto loop; goto loop;
} }
} }
FIB_ITERATE_END(n); FIB_ITERATE_END;
} }
static struct babel_neighbor * static struct babel_neighbor *
@ -476,8 +471,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (r) if (r)
{ {
net *n = net_get(p->p.table, e->n.prefix, e->n.pxlen); rta a0 = {
rta A = {
.src = p->p.main_source, .src = p->p.main_source,
.source = RTS_BABEL, .source = RTS_BABEL,
.scope = SCOPE_UNIVERSE, .scope = SCOPE_UNIVERSE,
@ -489,22 +483,20 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
}; };
if (r->metric < BABEL_INFINITY) 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 *rte = rte_get_temp(a);
rte->u.babel.metric = r->metric; rte->u.babel.metric = r->metric;
rte->u.babel.router_id = r->router_id; rte->u.babel.router_id = r->router_id;
rte->net = n;
rte->pflags = 0; rte->pflags = 0;
rte_update(&p->p, n, rte); rte_update(&p->p, e->n.addr, rte);
} }
else else
{ {
/* Retraction */ /* Retraction */
net *n = net_find(p->p.table, e->n.prefix, e->n.pxlen); rte_update(&p->p, e->n.addr, NULL);
rte_update(&p->p, n, NULL);
} }
} }
@ -541,8 +533,8 @@ babel_select_route(struct babel_entry *e)
((!e->selected_in && cur->metric < BABEL_INFINITY) || ((!e->selected_in && cur->metric < BABEL_INFINITY) ||
(e->selected_in && cur->metric < e->selected_in->metric))) (e->selected_in && cur->metric < e->selected_in->metric)))
{ {
TRACE(D_EVENTS, "Picked new route for prefix %I/%d: router id %lR metric %d", TRACE(D_EVENTS, "Picked new route for prefix %N: router id %lR metric %d",
e->n.prefix, e->n.pxlen, cur->router_id, cur->metric); e->n.addr, cur->router_id, cur->metric);
e->selected_in = cur; e->selected_in = cur;
e->updated = now; 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.*/ babel_build_rte() will set the unreachable flag if the metric is BABEL_INFINITY.*/
if (e->selected_in) if (e->selected_in)
{ {
TRACE(D_EVENTS, "Lost feasible route for prefix %I/%d", TRACE(D_EVENTS, "Lost feasible route for prefix %N",
e->n.prefix, e->n.pxlen); e->n.addr);
e->selected_in->metric = BABEL_INFINITY; e->selected_in->metric = BABEL_INFINITY;
e->updated = now; 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 /* No route currently selected, and no new one selected; this means we
don't have a route to this destination anymore (and were probably don't have a route to this destination anymore (and were probably
called from an expiry timer). Remove the route from the nest. */ 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->selected_in = NULL;
e->updated = now; 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; struct babel_iface *ifa = n->ifa;
union babel_msg msg = {}; union babel_msg msg = {};
TRACE(D_PACKETS, "Sending route request for %I/%d to %I", TRACE(D_PACKETS, "Sending route request for %N to %I",
e->n.prefix, e->n.pxlen, n->addr); e->n.addr, n->addr);
msg.type = BABEL_TLV_ROUTE_REQUEST; msg.type = BABEL_TLV_ROUTE_REQUEST;
msg.route_request.prefix = e->n.prefix; net_copy(&msg.route_request.net, e->n.addr);
msg.route_request.plen = e->n.pxlen;
babel_send_unicast(&msg, ifa, 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 = {}; union babel_msg msg = {};
s = babel_find_source(e, r->router_id); 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; return;
TRACE(D_PACKETS, "Sending seqno request for %I/%d router-id %lR seqno %d", TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1); e->n.addr, r->router_id, s->seqno + 1);
msg.type = BABEL_TLV_SEQNO_REQUEST; 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.hop_count = BABEL_INITIAL_HOP_COUNT;
msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.router_id = r->router_id; 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) WALK_LIST(ifa, p->interfaces)
babel_enqueue(&msg, ifa); babel_enqueue(&msg, ifa);
@ -725,18 +715,17 @@ babel_unicast_seqno_request(struct babel_route *r)
union babel_msg msg = {}; union babel_msg msg = {};
s = babel_find_source(e, r->router_id); 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; return;
TRACE(D_PACKETS, "Sending seqno request for %I/%d router-id %lR seqno %d", TRACE(D_PACKETS, "Sending seqno request for %N router-id %lR seqno %d",
e->n.prefix, e->n.pxlen, r->router_id, s->seqno + 1); e->n.addr, r->router_id, s->seqno + 1);
msg.type = BABEL_TLV_SEQNO_REQUEST; 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.hop_count = BABEL_INITIAL_HOP_COUNT;
msg.seqno_request.seqno = s->seqno + 1;
msg.seqno_request.router_id = r->router_id; 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); 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; 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; struct babel_route *r = e->selected_out;
if (!r) if (!r)
@ -776,17 +764,16 @@ babel_send_update(struct babel_iface *ifa, bird_clock_t changed)
if (e->updated < changed) if (e->updated < changed)
continue; continue;
TRACE(D_PACKETS, "Sending update for %I/%d router-id %lR seqno %d metric %d", TRACE(D_PACKETS, "Sending update for %N router-id %lR seqno %d metric %d",
e->n.prefix, e->n.pxlen, r->router_id, r->seqno, r->metric); e->n.addr, r->router_id, r->seqno, r->metric);
union babel_msg msg = {}; union babel_msg msg = {};
msg.type = BABEL_TLV_UPDATE; msg.type = BABEL_TLV_UPDATE;
msg.update.plen = e->n.pxlen;
msg.update.interval = ifa->cf->update_interval; msg.update.interval = ifa->cf->update_interval;
msg.update.seqno = r->seqno; msg.update.seqno = r->seqno;
msg.update.metric = r->metric; msg.update.metric = r->metric;
msg.update.prefix = e->n.prefix;
msg.update.router_id = r->router_id; msg.update.router_id = r->router_id;
net_copy(&msg.update.net, e->n.addr);
babel_enqueue(&msg, ifa); babel_enqueue(&msg, ifa);
@ -839,20 +826,18 @@ babel_trigger_update(struct babel_proto *p)
/* A retraction is an update with an infinite metric */ /* A retraction is an update with an infinite metric */
static void 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; struct babel_proto *p = ifa->proto;
union babel_msg msg = {}; union babel_msg msg = {};
TRACE(D_PACKETS, "Sending retraction for %I/%d seqno %d", TRACE(D_PACKETS, "Sending retraction for %N seqno %d", n, p->update_seqno);
prefix, plen, p->update_seqno);
msg.type = BABEL_TLV_UPDATE; msg.type = BABEL_TLV_UPDATE;
msg.update.plen = plen;
msg.update.interval = ifa->cf->update_interval; msg.update.interval = ifa->cf->update_interval;
msg.update.seqno = p->update_seqno; msg.update.seqno = p->update_seqno;
msg.update.metric = BABEL_INFINITY; msg.update.metric = BABEL_INFINITY;
msg.update.prefix = prefix; msg.update.net = *n;
babel_enqueue(&msg, ifa); 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. * found. Otherwise, a new entry is stored in the cache.
*/ */
static int 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) u64 router_id, u16 seqno)
{ {
struct babel_seqno_request *r; struct babel_seqno_request *r;
WALK_LIST(r, p->seqno_cache) WALK_LIST(r, p->seqno_cache)
{ {
if (ipa_equal(r->prefix, prefix) && (r->plen == plen) && if (net_equal(&r->net, n) && (r->router_id == router_id) && (r->seqno == seqno))
(r->router_id == router_id) && (r->seqno == seqno))
return 0; return 0;
} }
/* no entries found */ /* no entries found */
r = sl_alloc(p->seqno_slab); r = sl_alloc(p->seqno_slab);
r->prefix = prefix; net_copy(&r->net, n);
r->plen = plen;
r->router_id = router_id; r->router_id = router_id;
r->seqno = seqno; r->seqno = seqno;
r->updated = now; r->updated = now;
@ -973,8 +956,8 @@ babel_forward_seqno_request(struct babel_entry *e,
struct babel_proto *p = e->proto; struct babel_proto *p = e->proto;
struct babel_route *r; struct babel_route *r;
TRACE(D_PACKETS, "Forwarding seqno request for %I/%d router-id %lR seqno %d", TRACE(D_PACKETS, "Forwarding seqno request for %N router-id %lR seqno %d",
e->n.prefix, e->n.pxlen, in->router_id, in->seqno); e->n.addr, in->router_id, in->seqno);
WALK_LIST(r, e->routes) WALK_LIST(r, e->routes)
{ {
@ -982,16 +965,15 @@ babel_forward_seqno_request(struct babel_entry *e,
!OUR_ROUTE(r) && !OUR_ROUTE(r) &&
!ipa_equal(r->neigh->addr, sender)) !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; return;
union babel_msg msg = {}; union babel_msg msg = {};
msg.type = BABEL_TLV_SEQNO_REQUEST; 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.hop_count = in->hop_count-1;
msg.seqno_request.seqno = in->seqno;
msg.seqno_request.router_id = in->router_id; 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); babel_send_unicast(&msg, r->neigh->ifa, r->neigh->addr);
return; return;
@ -1073,8 +1055,11 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
node *n; node *n;
int feasible; int feasible;
TRACE(D_PACKETS, "Handling update for %I/%d with seqno %d metric %d", if (msg->wildcard)
msg->prefix, msg->plen, msg->seqno, msg->metric); 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); nbr = babel_find_neighbor(ifa, msg->sender);
if (!nbr) if (!nbr)
@ -1140,7 +1125,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
} }
else else
{ {
e = babel_find_entry(p, msg->prefix, msg->plen); e = babel_find_entry(p, &msg->net);
if (!e) if (!e)
return; return;
@ -1159,7 +1144,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
return; 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 */ r = babel_find_route(e, nbr); /* the route entry indexed by neighbour */
s = babel_find_source(e, msg->router_id); /* for feasibility */ s = babel_find_source(e, msg->router_id); /* for feasibility */
feasible = babel_is_feasible(s, msg->seqno, msg->metric); 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; 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. /* Non-wildcard request - see if we have an entry for the route.
If not, send a retraction, otherwise send an update. */ 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) if (!e)
{ {
babel_send_retraction(ifa, msg->prefix, msg->plen); babel_send_retraction(ifa, &msg->net);
} }
else else
{ {
@ -1256,11 +1241,11 @@ babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
/* RFC 6126 3.8.1.2 */ /* RFC 6126 3.8.1.2 */
TRACE(D_PACKETS, "Handling seqno request for %I/%d router-id %lR seqno %d hop count %d", TRACE(D_PACKETS, "Handling seqno request for %N router-id %lR seqno %d hop count %d",
msg->prefix, msg->plen, msg->router_id, msg->seqno, msg->hop_count); &msg->net, msg->router_id, msg->seqno, msg->hop_count);
/* Ignore if we have no such entry or entry has infinite metric */ /* 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)) if (!e || !e->selected_out || (e->selected_out->metric == BABEL_INFINITY))
return; return;
@ -1668,7 +1653,7 @@ babel_dump_entry(struct babel_entry *e)
struct babel_source *s; struct babel_source *s;
struct babel_route *r; 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) WALK_LIST(s,e->sources)
{ debug(" "); babel_dump_source(s); } { debug(" "); babel_dump_source(s); }
@ -1715,9 +1700,9 @@ babel_dump(struct proto *P)
WALK_LIST(ifa, p->interfaces) WALK_LIST(ifa, p->interfaces)
babel_dump_iface(ifa); 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; FIB_WALK_END;
} }
@ -1828,11 +1813,9 @@ void
babel_show_entries(struct proto *P) babel_show_entries(struct proto *P)
{ {
struct babel_proto *p = (void *) P; struct babel_proto *p = (void *) P;
struct babel_entry *e = NULL;
struct babel_source *s = NULL; struct babel_source *s = NULL;
struct babel_route *r = NULL; struct babel_route *r = NULL;
char ipbuf[STD_ADDRESS_P_LENGTH+5];
char ridbuf[ROUTER_ID_64_LENGTH+1]; char ridbuf[ROUTER_ID_64_LENGTH+1];
if (p->p.proto_state != PS_UP) 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", cli_msg(-1025, "%-29s %-23s %6s %5s %7s %7s",
"Prefix", "Router ID", "Metric", "Seqno", "Expires", "Sources"); "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; r = e->selected_in ? e->selected_in : e->selected_out;
int srcs = 0; int srcs = 0;
WALK_LIST(s, e->sources) WALK_LIST(s, e->sources)
srcs++; srcs++;
bsprintf(ipbuf, "%I/%u", e->n.prefix, e->n.pxlen);
if (r) if (r)
{ {
if (r->router_id == p->router_id) if (r->router_id == p->router_id)
@ -1865,12 +1845,12 @@ babel_show_entries(struct proto *P)
bsprintf(ridbuf, "%lR", r->router_id); bsprintf(ridbuf, "%lR", r->router_id);
int time = r->expires ? r->expires - now : 0; int time = r->expires ? r->expires - now : 0;
cli_msg(-1025, "%-29s %-23s %6u %5u %7u %7u", cli_msg(-1025, "%-29N %-23s %6u %5u %7u %7u",
ipbuf, ridbuf, r->metric, r->seqno, MAX(time, 0), srcs); e->n.addr, ridbuf, r->metric, r->seqno, MAX(time, 0), srcs);
} }
else else
{ {
cli_msg(-1025, "%-29s %-44s %7u", ipbuf, "<pending>", srcs); cli_msg(-1025, "%-29N %-44s %7u", e->n.addr, "<pending>", srcs);
} }
} }
FIB_WALK_END; 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. * so store it into our data structures.
*/ */
static void 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 rte *new, struct rte *old UNUSED, struct ea_list *attrs UNUSED)
{ {
struct babel_proto *p = (void *) P; 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) if (new)
{ {
/* Update */ /* 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) if (new->attrs->src->proto != P)
{ {
@ -1996,7 +1976,7 @@ babel_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *ne
else else
{ {
/* Withdraw */ /* 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) if (!e || !e->selected_out)
return; return;
@ -2046,11 +2026,12 @@ babel_rte_same(struct rte *new, struct rte *old)
static struct proto * 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->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify; P->rt_notify = babel_rt_notify;
P->import_control = babel_import_control; P->import_control = babel_import_control;
@ -2068,7 +2049,8 @@ babel_start(struct proto *P)
struct babel_proto *p = (void *) P; struct babel_proto *p = (void *) P;
struct babel_config *cf = (void *) P->cf; 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); init_list(&p->interfaces);
p->timer = tm_new_set(P->pool, babel_timer, p, 0, 1); p->timer = tm_new_set(P->pool, babel_timer, p, 0, 1);
tm_start(p->timer, 2); tm_start(p->timer, 2);
@ -2111,14 +2093,17 @@ babel_shutdown(struct proto *P)
} }
static int 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_proto *p = (void *) P;
struct babel_config *new = (void *) c; struct babel_config *new = (void *) CF;
TRACE(D_EVENTS, "Reconfiguring"); 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_reconfigure_ifaces(p, new);
babel_trigger_update(p); babel_trigger_update(p);
@ -2133,6 +2118,8 @@ struct protocol proto_babel = {
.template = "babel%d", .template = "babel%d",
.attr_class = EAP_BABEL, .attr_class = EAP_BABEL,
.preference = DEF_PREF_BABEL, .preference = DEF_PREF_BABEL,
.channel_mask = NB_IP6,
.proto_size = sizeof(struct babel_proto),
.config_size = sizeof(struct babel_config), .config_size = sizeof(struct babel_config),
.init = babel_init, .init = babel_init,
.dump = babel_dump, .dump = babel_dump,

View file

@ -52,7 +52,7 @@
/* Max interval that will not overflow when carried as 16-bit centiseconds */ /* Max interval that will not overflow when carried as 16-bit centiseconds */
#define BABEL_MAX_INTERVAL (0xFFFF/BABEL_TIME_UNITS) #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) #define BABEL_MIN_MTU (512 + BABEL_OVERHEAD)
@ -208,7 +208,6 @@ struct babel_route {
}; };
struct babel_entry { struct babel_entry {
struct fib_node n;
struct babel_proto *proto; struct babel_proto *proto;
struct babel_route *selected_in; struct babel_route *selected_in;
struct babel_route *selected_out; struct babel_route *selected_out;
@ -217,13 +216,14 @@ struct babel_entry {
list sources; /* Source entries for this prefix (struct babel_source). */ list sources; /* Source entries for this prefix (struct babel_source). */
list routes; /* Routes for this prefix (struct babel_route) */ list routes; /* Routes for this prefix (struct babel_route) */
struct fib_node n;
}; };
/* Stores forwarded seqno requests for duplicate suppression. */ /* Stores forwarded seqno requests for duplicate suppression. */
struct babel_seqno_request { struct babel_seqno_request {
node n; node n;
ip_addr prefix; net_addr net;
u8 plen;
u64 router_id; u64 router_id;
u16 seqno; u16 seqno;
bird_clock_t updated; bird_clock_t updated;
@ -265,12 +265,11 @@ struct babel_msg_ihu {
struct babel_msg_update { struct babel_msg_update {
u8 type; u8 type;
u8 wildcard; u8 wildcard;
u8 plen;
u16 interval; u16 interval;
u16 seqno; u16 seqno;
u16 metric; u16 metric;
ip_addr prefix;
u64 router_id; u64 router_id;
net_addr net;
ip_addr next_hop; ip_addr next_hop;
ip_addr sender; ip_addr sender;
}; };
@ -278,17 +277,15 @@ struct babel_msg_update {
struct babel_msg_route_request { struct babel_msg_route_request {
u8 type; u8 type;
u8 full; u8 full;
u8 plen; net_addr net;
ip_addr prefix;
}; };
struct babel_msg_seqno_request { struct babel_msg_seqno_request {
u8 type; u8 type;
u8 plen;
u16 seqno;
u8 hop_count; u8 hop_count;
u16 seqno;
u64 router_id; u64 router_id;
ip_addr prefix; net_addr net;
ip_addr sender; ip_addr sender;
}; };

View file

@ -30,11 +30,13 @@ CF_ADDTO(proto, babel_proto)
babel_proto_start: proto_start BABEL babel_proto_start: proto_start BABEL
{ {
this_proto = proto_config_new(&proto_babel, $1); this_proto = proto_config_new(&proto_babel, $1);
this_proto->net_type = NET_IP6;
init_list(&BABEL_CFG->iface_list); init_list(&BABEL_CFG->iface_list);
}; };
babel_proto_item: babel_proto_item:
proto_item proto_item
| proto_channel
| INTERFACE babel_iface | INTERFACE babel_iface
; ;

View file

@ -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_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 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 static inline u16
get_time16(const void *p) get_time16(const void *p)
@ -161,19 +162,19 @@ put_time16(void *p, u16 v)
put_u16(p, v * BABEL_TIME_UNITS); put_u16(p, v * BABEL_TIME_UNITS);
} }
static inline ip6_addr static inline void
get_ip6_px(const void *p, uint plen) read_ip6_px(net_addr *n, const void *p, uint plen)
{ {
ip6_addr addr = IPA_NONE; ip6_addr addr = IPA_NONE;
memcpy(&addr, p, BYTES(plen)); memcpy(&addr, p, BYTES(plen));
return ip6_ntoh(addr); net_fill_ip6(n, ip6_ntoh(addr), plen);
} }
static inline void 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); ip6_addr addr = ip6_hton(net6_prefix(n));
memcpy(p, &addr, BYTES(plen)); memcpy(p, &addr, NET_SIZE(n));
} }
static inline ip6_addr static inline ip6_addr
@ -480,6 +481,9 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
if (tlv->plen > 0) if (tlv->plen > 0)
return PARSE_ERROR; return PARSE_ERROR;
if (msg->metric != 65535)
return PARSE_ERROR;
msg->wildcard = 1; msg->wildcard = 1;
break; break;
@ -488,7 +492,7 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE; return PARSE_IGNORE;
case BABEL_AE_IP6: case BABEL_AE_IP6:
if (tlv->plen > MAX_PREFIX_LENGTH) if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR; return PARSE_ERROR;
/* Cannot omit data if there is no saved prefix */ /* 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, state->def_ip6_prefix, tlv->omitted);
memcpy(buf + tlv->omitted, tlv->addr, len); memcpy(buf + tlv->omitted, tlv->addr, len);
msg->plen = tlv->plen; ip6_addr prefix = get_ip6(buf);
msg->prefix = ipa_from_ip6(get_ip6(buf)); net_fill_ip6(&msg->net, prefix, tlv->plen);
if (tlv->flags & BABEL_FLAG_DEF_PREFIX) 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; state->def_ip6_prefix_seen = 1;
} }
if (tlv->flags & BABEL_FLAG_ROUTER_ID) 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; state->router_id_seen = 1;
} }
break; break;
@ -559,7 +563,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
tlv = (struct babel_tlv_update *) NEXT_TLV(tlv); 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) if (len0 + len > max_len)
return 0; return 0;
@ -575,8 +579,8 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m,
else else
{ {
tlv->ae = BABEL_AE_IP6; tlv->ae = BABEL_AE_IP6;
tlv->plen = msg->plen; tlv->plen = net6_pxlen(&msg->net);
put_ip6_px(tlv->addr, msg->prefix, msg->plen); put_ip6_px(tlv->addr, &msg->net);
} }
put_time16(&tlv->interval, msg->interval); 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; return PARSE_IGNORE;
case BABEL_AE_IP6: case BABEL_AE_IP6:
if (tlv->plen > MAX_PREFIX_LENGTH) if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR; return PARSE_ERROR;
if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen)) if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen))
return PARSE_ERROR; return PARSE_ERROR;
msg->plen = tlv->plen; read_ip6_px(&msg->net, tlv->addr, tlv->plen);
msg->prefix = get_ip6_px(tlv->addr, tlv->plen);
return PARSE_SUCCESS; return PARSE_SUCCESS;
case BABEL_AE_IP6_LL: 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_tlv_route_request *tlv = (void *) hdr;
struct babel_msg_route_request *msg = &m->route_request; 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) if (len > max_len)
return 0; return 0;
@ -652,8 +655,8 @@ babel_write_route_request(struct babel_tlv *hdr, union babel_msg *m,
else else
{ {
tlv->ae = BABEL_AE_IP6; tlv->ae = BABEL_AE_IP6;
tlv->plen = msg->plen; tlv->plen = net6_pxlen(&msg->net);
put_ip6_px(tlv->addr, msg->prefix, msg->plen); put_ip6_px(tlv->addr, &msg->net);
} }
return len; return len;
@ -685,14 +688,13 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m,
return PARSE_IGNORE; return PARSE_IGNORE;
case BABEL_AE_IP6: case BABEL_AE_IP6:
if (tlv->plen > MAX_PREFIX_LENGTH) if (tlv->plen > IP6_MAX_PREFIX_LENGTH)
return PARSE_ERROR; return PARSE_ERROR;
if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen)) if (TLV_OPT_LENGTH(tlv) < BYTES(tlv->plen))
return PARSE_ERROR; return PARSE_ERROR;
msg->plen = tlv->plen; read_ip6_px(&msg->net, tlv->addr, tlv->plen);
msg->prefix = get_ip6_px(tlv->addr, tlv->plen);
return PARSE_SUCCESS; return PARSE_SUCCESS;
case BABEL_AE_IP6_LL: 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_tlv_seqno_request *tlv = (void *) hdr;
struct babel_msg_seqno_request *msg = &m->seqno_request; 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) if (len > max_len)
return 0; return 0;
TLV_HDR(tlv, BABEL_TLV_SEQNO_REQUEST, len); TLV_HDR(tlv, BABEL_TLV_SEQNO_REQUEST, len);
tlv->ae = BABEL_AE_IP6; tlv->ae = BABEL_AE_IP6;
tlv->plen = msg->plen; tlv->plen = net6_pxlen(&msg->net);
put_u16(&tlv->seqno, msg->seqno); put_u16(&tlv->seqno, msg->seqno);
tlv->hop_count = msg->hop_count; tlv->hop_count = msg->hop_count;
put_u64(&tlv->router_id, msg->router_id); 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; return len;
} }

View file

@ -41,24 +41,6 @@
* specifies that such updates should be ignored, but that is generally * specifies that such updates should be ignored, but that is generally
* a bad idea. * 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: * BGP attribute table has several hooks:
* *
* export - Hook that validates and normalizes attribute during export phase. * 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 static void
bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to) 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]; 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); 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) if (!s->as4_session)
{ {
/* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */ /* 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) if (len < 6)
DISCARD(BAD_LENGTH, "AS4_PATH", len); 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); DISCARD("Malformed AS4_PATH attribute - %s", err);
/* XXXX remove CONFED segments */ struct adata *a = lp_alloc_adata(s->pool, len);
bgp_set_attr_data(to, s->pool, BA_AS4_PATH, flags, data, 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 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)) if (bgp_as_path_loopy(p, attrs, p->local_as))
goto withdraw; 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)) if ((p->public_as != p->local_as) && bgp_as_path_loopy(p, attrs, p->public_as))
goto withdraw; goto withdraw;
@ -1221,8 +1220,17 @@ bgp_init_prefix_table(struct bgp_channel *c)
{ {
HASH_INIT(c->prefix_hash, c->pool, 8); HASH_INIT(c->prefix_hash, c->pool, 8);
c->prefix_slab = sl_new(c->pool, sizeof(struct bgp_prefix) + uint alen = net_addr_length[c->c.net_type];
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 * static struct bgp_prefix *
@ -1237,7 +1245,11 @@ bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
return px; 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.next = NULL;
px->buck_node.prev = NULL; px->buck_node.prev = NULL;
px->hash = hash; px->hash = hash;
@ -1254,7 +1266,11 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
{ {
rem_node(&px->buck_node); rem_node(&px->buck_node);
HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px); 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) if (src == NULL)
return 0; return 0;
// XXXX: Check next hop AF
/* IBGP route reflection, RFC 4456 */ /* IBGP route reflection, RFC 4456 */
if (p->is_internal && src->is_internal && (p->local_as == src->local_as)) 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; return 0;
} }
static const adata null_adata; /* adata of length 0 */
static inline void static adata null_adata; /* adata of length 0 */
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 ea_list * 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 proto *SRC = e->attrs->src->proto;
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL; 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 }; struct bgp_export_state s = { .proto = p, .channel =c, .pool = pool, .src = src, .route = e };
ea_list *attrs = attrs0;
eattr *a; eattr *a;
adata *ad;
/* ORIGIN attribute - mandatory, attach if missing */ /* 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); 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 */ /* AS_PATH attribute - keep or prepend ASN */
if (p->is_internal || if (p->is_internal ||
(p->rs_client && src && src->rs_client)) (p->rs_client && src && src->rs_client))
{ {
/* IBGP or route server -> just ensure there is one */ /* IBGP or route server -> just ensure there is one */
if (! bgp_find_attr(attrs, BA_AS_PATH)) if (!a)
bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, lp_alloc_adata(pool, 0)); bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, &null_adata);
} }
else if (p->is_interior) else if (p->is_interior)
{ {
/* Confederation -> prepend ASN as CONFED_SEQUENCE, keep CONFED_* segments */ /* Confederation -> prepend ASN as AS_CONFED_SEQUENCE */
bgp_path_prepend(&attrs, pool, AS_PATH_CONFED_SEQUENCE, p->public_as, 0); 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) */ else /* Regular EBGP (no RS, no confederation) */
{ {
/* Regular EBGP -> prepend ASN as regular segment, strip CONFED_* segments */ /* Regular EBGP -> prepend ASN as regular sequence */
bgp_path_prepend(&attrs, pool, AS_PATH_SEQUENCE, p->public_as, 1); 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 */ /* 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)) if (a && !(a->type & EAF_FRESH))
bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC); bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC);
} }
/* NEXT_HOP attribute - delegated to AF-specific hook */ /* 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); bgp_update_next_hop(&s, a, &attrs);
/* LOCAL_PREF attribute - required for IBGP, attach if missing */ /* 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); bgp_set_attr_u32(&attrs, pool, BA_LOCAL_PREF, 0, p->cf->default_local_pref);
/* IBGP route reflection, RFC 4456 */ /* IBGP route reflection, RFC 4456 */
if (src && src->is_internal && p->is_internal && (src->local_as == p->local_as)) if (src && src->is_internal && p->is_internal && (src->local_as == p->local_as))
{ {
/* ORIGINATOR_ID attribute - attach if not already set */ /* 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); bgp_set_attr_u32(&attrs, pool, BA_ORIGINATOR_ID, 0, src->remote_id);
/* CLUSTER_LIST attribute - prepend cluster ID */ /* CLUSTER_LIST attribute - prepend cluster ID */
if (src->rr_cluster_id) a = bgp_find_attr(attrs0, BA_CLUSTER_LIST);
bgp_cluster_list_prepend(&attrs, pool, src->rr_cluster_id); 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)) 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 */ /* 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 */ /* Apply per-attribute export hooks for validatation and normalization */
return bgp_export_attrs(&s, attrs); 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)); eattr *e = ea_find(r->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
u32 as; 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; 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 static inline int
@ -1653,7 +1683,7 @@ bgp_rte_mergable(rte *pri, rte *sec)
} }
/* RFC 4271 9.1.2.2. d) Prefer external peers */ /* 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; return 0;
/* RFC 4271 9.1.2.2. e) Compare IGP metrics */ /* 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 */ /* Handle AS_PATH attribute */
if (p2 && p4) 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 p2_len = as_path_getlen(p2->u.ptr);
int p4_len = as_path_getlen(p4->u.ptr); int p4_len = as_path_getlen(p4->u.ptr);

View file

@ -521,12 +521,17 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
if (peer->gr_aware) if (peer->gr_aware)
c->load_state = BFS_LOADING; 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_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); 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) if (c->add_path_tx)
c->c.ra_mode = RA_ANY; 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)); 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"); BGP_TRACE(D_EVENTS, "BGP session closed");
p->conn = NULL; 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) if (p->p.proto_state == PS_UP)
bgp_stop(p, 0); 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 *c = (void *) C;
struct bgp_channel_config *cf = (void *) CF; struct bgp_channel_config *cf = (void *) CF;
C->ra_mode = cf->secondary ? RA_ACCEPTED : RA_OPTIMAL;
c->cf = cf; c->cf = cf;
c->afi = cf->afi; c->afi = cf->afi;
c->desc = bgp_get_af_desc(c->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); 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, " ... <too long>");
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 static void
bgp_show_proto_info(struct proto *P) bgp_show_proto_info(struct proto *P)
{ {
struct bgp_proto *p = (struct bgp_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, " BGP state: %s", bgp_state_dsc(p));
cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface); 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) else if (P->proto_state == PS_UP)
{ {
cli_msg(-1006, " Neighbor ID: %R", p->remote_id); 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 /* 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", cli_msg(-1006, " Session: %s%s%s%s%s%s%s%s",
p->is_internal ? "internal" : "external", p->is_internal ? "internal" : "external",
p->cf->multihop ? " multihop" : "", 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, " Source address: %I", p->source_addr);
cli_msg(-1006, " Hold timer: %d/%d", 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", 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) && if ((p->last_error_class != BE_NONE) &&
@ -1846,7 +1970,7 @@ struct protocol proto_bgp = {
.template = "bgp%d", .template = "bgp%d",
.attr_class = EAP_BGP, .attr_class = EAP_BGP,
.preference = DEF_PREF_BGP, .preference = DEF_PREF_BGP,
.channel_mask = NB_IP, .channel_mask = NB_IP | NB_FLOW4 | NB_FLOW6,
.proto_size = sizeof(struct bgp_proto), .proto_size = sizeof(struct bgp_proto),
.config_size = sizeof(struct bgp_config), .config_size = sizeof(struct bgp_config),
.postconfig = bgp_postconfig, .postconfig = bgp_postconfig,

View file

@ -31,6 +31,7 @@ struct eattr;
#define BGP_SAFI_UNICAST 1 #define BGP_SAFI_UNICAST 1
#define BGP_SAFI_MULTICAST 2 #define BGP_SAFI_MULTICAST 2
#define BGP_SAFI_FLOW 133
/* Internal AF codes */ /* Internal AF codes */
@ -42,6 +43,8 @@ struct eattr;
#define BGP_AF_IPV6 BGP_AF( BGP_AFI_IPV6, BGP_SAFI_UNICAST ) #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_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_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; struct bgp_write_state;
@ -70,7 +73,7 @@ struct bgp_config {
u16 local_port; /* Local listening port */ u16 local_port; /* Local listening port */
u16 remote_port; /* Neighbor destination port */ u16 remote_port; /* Neighbor destination port */
int multihop; /* Number of hops if multihop */ 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 ttl_security; /* Enable TTL security [RFC 5082] */
int compare_path_lengths; /* Use path lengths when selecting best route */ int compare_path_lengths; /* Use path lengths when selecting best route */
int med_metric; /* Compare MULTI_EXIT_DISC even between routes from differen ASes */ 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 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 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
u8 gr_able; /* Allow full graceful restart for the channel */ 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] */ u8 add_path; /* Use ADD-PATH extension [RFC 7911] */
struct rtable_config *igp_table; /* Table used for recursive next hop lookups */ 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 ready; /* Multiprotocol capability, RFC 4760 */
u8 gr_able; /* Graceful restart support, RFC 4724 */ u8 gr_able; /* Graceful restart support, RFC 4724 */
u8 gr_af_flags; /* Graceful restart per-AF flags */ 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 */ 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 */ 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 { struct bgp_socket {
node n; /* Node in global bgp_sockets */ node n; /* Node in global bgp_sockets */
sock *sk; /* Real listening socket */ sock *sk; /* Real listening socket */
@ -267,6 +276,8 @@ struct bgp_channel {
u8 gr_ready; /* Neighbor could do GR on this AF */ u8 gr_ready; /* Neighbor could do GR on this AF */
u8 gr_active; /* Neighbor is doing GR and keeping fwd state */ 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_rx; /* Session expects receive of ADD-PATH extended NLRI */
u8 add_path_tx; /* Session expects transmit 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; } { 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); 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); 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_PARTIAL 0x20
#define BAF_EXT_LEN 0x10 #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_AS_PATH 0x02 /* WM */
#define BA_NEXT_HOP 0x03 /* WM */ #define BA_NEXT_HOP 0x03 /* WM */
#define BA_MULTI_EXIT_DISC 0x04 /* ON */ #define BA_MULTI_EXIT_DISC 0x04 /* ON */
#define BA_LOCAL_PREF 0x05 /* WD */ #define BA_LOCAL_PREF 0x05 /* WD */
#define BA_ATOMIC_AGGR 0x06 /* WD */ #define BA_ATOMIC_AGGR 0x06 /* WD */
#define BA_AGGREGATOR 0x07 /* OT */ #define BA_AGGREGATOR 0x07 /* OT */
#define BA_COMMUNITY 0x08 /* [RFC1997] */ /* OT */ #define BA_COMMUNITY 0x08 /* RFC 1997 */ /* OT */
#define BA_ORIGINATOR_ID 0x09 /* [RFC1966] */ /* ON */ #define BA_ORIGINATOR_ID 0x09 /* RFC 4456 */ /* ON */
#define BA_CLUSTER_LIST 0x0a /* ON */ #define BA_CLUSTER_LIST 0x0a /* RFC 4456 */ /* ON */
/* We don't support these: */ #define BA_MP_REACH_NLRI 0x0e /* RFC 4760 */
#define BA_DPA 0x0b /* ??? */ #define BA_MP_UNREACH_NLRI 0x0f /* RFC 4760 */
#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_EXT_COMMUNITY 0x10 /* RFC 4360 */ #define BA_EXT_COMMUNITY 0x10 /* RFC 4360 */
#define BA_AS4_PATH 0x11 /* RFC 6793 */ #define BA_AS4_PATH 0x11 /* RFC 6793 */
#define BA_AS4_AGGREGATOR 0x12 /* 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 */ /* BGP connection states */

View file

@ -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, BGP_CLUSTER_LIST, IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL,
SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY, GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST) STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6)
%type <i32> bgp_afi %type <i32> bgp_afi
@ -139,6 +139,8 @@ bgp_afi:
| IPV6 { $$ = BGP_AF_IPV6; } | IPV6 { $$ = BGP_AF_IPV6; }
| IPV4 MULTICAST { $$ = BGP_AF_IPV4_MC; } | IPV4 MULTICAST { $$ = BGP_AF_IPV4_MC; }
| IPV6 MULTICAST { $$ = BGP_AF_IPV6_MC; } | IPV6 MULTICAST { $$ = BGP_AF_IPV6_MC; }
| FLOW4 { $$ = BGP_AF_FLOW4; }
| FLOW6 { $$ = BGP_AF_FLOW6; }
; ;
bgp_channel_start: bgp_afi 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); this_channel = channel_config_new(&channel_bgp, desc->net, this_proto);
BGP_CC->c.name = desc->name; BGP_CC->c.name = desc->name;
BGP_CC->c.ra_mode = RA_UNDEF;
BGP_CC->afi = $1; BGP_CC->afi = $1;
BGP_CC->gr_able = 0xff; /* undefined */ BGP_CC->gr_able = 0xff; /* undefined */
}; };
@ -166,6 +169,7 @@ bgp_channel_item:
| GATEWAY RECURSIVE { BGP_CC->gw_mode = GW_RECURSIVE; } | GATEWAY RECURSIVE { BGP_CC->gw_mode = GW_RECURSIVE; }
| SECONDARY bool { BGP_CC->secondary = $2; } | SECONDARY bool { BGP_CC->secondary = $2; }
| GRACEFUL RESTART bool { BGP_CC->gr_able = $3; } | 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 RX { BGP_CC->add_path = BGP_ADD_PATH_RX; }
| ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; } | ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; }
| ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; } | ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; }

View file

@ -20,6 +20,7 @@
#include "nest/mrtdump.h" #include "nest/mrtdump.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "lib/unaligned.h" #include "lib/unaligned.h"
#include "lib/flowspec.h"
#include "lib/socket.h" #include "lib/socket.h"
#include "nest/cli.h" #include "nest/cli.h"
@ -184,9 +185,6 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf)
/* Capability negotiation as per RFC 5492 */ /* 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 * const struct bgp_af_caps *
bgp_find_af_caps(struct bgp_caps *caps, u32 afi) 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_channel *c;
struct bgp_caps *caps; struct bgp_caps *caps;
struct bgp_af_caps *ac; struct bgp_af_caps *ac;
uint any_ext_next_hop = 0;
uint any_add_path = 0; uint any_add_path = 0;
byte *data; byte *data;
@ -261,6 +260,9 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
ac->afi = c->afi; ac->afi = c->afi;
ac->ready = 1; 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; ac->add_path = c->cf->add_path;
any_add_path |= ac->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 */ /* 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) WALK_AF_CAPS(caps, ac)
if (ac->ready) if (ac->ready)
{ {
@ -294,6 +302,23 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
*buf++ = 0; /* Capability data length */ *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) if (caps->ext_messages)
{ {
*buf++ = 6; /* Capability 6: Support for extended 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 */ *buf++ = 0; /* Capability data length */
} }
/* FIXME: Should not XXXX 255 */
return buf; return buf;
} }
@ -392,6 +415,23 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i
caps->route_refresh = 1; caps->route_refresh = 1;
break; 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 */ case 6: /* Extended message length capability, RFC draft */
if (cl != 0) if (cl != 0)
goto err; goto err;
@ -673,9 +713,13 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
#define REPORT(msg, args...) \ #define REPORT(msg, args...) \
({ log(L_REMOTE "%s: " msg, s->proto->p.name, ## args); }) ({ log(L_REMOTE "%s: " msg, s->proto->p.name, ## args); })
#define DISCARD(msg, args...) \
({ REPORT(msg, ## args); return; })
#define WITHDRAW(msg, args...) \ #define WITHDRAW(msg, args...) \
({ REPORT(msg, ## args); s->err_withdraw = 1; return; }) ({ 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 BAD_NEXT_HOP "Invalid NEXT_HOP attribute"
#define NO_NEXT_HOP "Missing 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); 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 * 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[] = { static const struct bgp_af_desc bgp_af_table[] = {
{ {
.afi = BGP_AF_IPV4, .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, .decode_next_hop = bgp_decode_next_hop_ip4,
.update_next_hop = bgp_update_next_hop_ip, .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, .afi = BGP_AF_IPV6,
.net = NET_IP6, .net = NET_IP6,
@ -1106,6 +1370,16 @@ static const struct bgp_af_desc bgp_af_table[] = {
.decode_next_hop = bgp_decode_next_hop_ip6, .decode_next_hop = bgp_decode_next_hop_ip6,
.update_next_hop = bgp_update_next_hop_ip, .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 * const struct bgp_af_desc *
@ -1387,15 +1661,15 @@ bgp_create_end_mark(struct bgp_channel *c, byte *buf)
} }
static inline void 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); struct bgp_channel *c = bgp_get_channel(p, afi);
BGP_TRACE(D_PACKETS, "Got END-OF-RIB"); BGP_TRACE(D_PACKETS, "Got END-OF-RIB");
/* XXXX handle unknown AF in MP_*_NLRI */
if (!c) if (!c)
return; DISCARD(BAD_AFI, BGP_AFI(afi), BGP_SAFI(afi));
if (c->load_state == BFS_LOADING) if (c->load_state == BFS_LOADING)
c->load_state = BFS_NONE; 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); struct bgp_channel *c = bgp_get_channel(s->proto, afi);
rta *a = NULL; rta *a = NULL;
/* XXXX handle unknown AF in MP_*_NLRI */
if (!c) if (!c)
return; DISCARD(BAD_AFI, BGP_AFI(afi), BGP_SAFI(afi));
s->channel = c; s->channel = c;
s->add_path = c->add_path_rx; 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 */ /* Check for End-of-RIB marker */
if (!s.attr_len && !s.ip_unreach_len && !s.ip_reach_len) 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 */ /* Check for MP End-of-RIB marker */
if ((s.attr_len < 8) && !s.ip_unreach_len && !s.ip_reach_len && 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 */ !s.mp_reach_len && !s.mp_unreach_len && s.mp_unreach_af)
{ bgp_rx_end_mark(p, s.mp_unreach_af); goto done; } { bgp_rx_end_mark(&s, s.mp_unreach_af); goto done; }
if (s.ip_unreach_len) if (s.ip_unreach_len)
bgp_decode_nlri(&s, BGP_AF_IPV4, s.ip_unreach_nlri, s.ip_unreach_len, NULL, NULL, 0); bgp_decode_nlri(&s, BGP_AF_IPV4, s.ip_unreach_nlri, s.ip_unreach_len, NULL, NULL, 0);

View file

@ -421,10 +421,9 @@ add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_ent
if (en == oa->rt) if (en == oa->rt)
{ {
/* /*
* Local stub networks does not have proper iface in en->nhi * Local stub networks do not have proper iface in en->nhi (because they all
* (because they all have common top_hash_entry en). * have common top_hash_entry en). We have to find iface responsible for
* We have to find iface responsible for that stub network. * that stub network. Configured stubnets do not have any iface. They will
* Configured stubnets does not have any iface. They will
* be removed in rt_sync(). * be removed in rt_sync().
*/ */
@ -1428,7 +1427,6 @@ ospf_ext_spf(struct ospf_proto *p)
struct top_hash_entry *en; struct top_hash_entry *en;
struct ospf_lsa_ext_local rt; struct ospf_lsa_ext_local rt;
ort *nf1, *nf2; ort *nf1, *nf2;
orta nfa = {};
u32 br_metric; u32 br_metric;
struct ospf_area *atmp; struct ospf_area *atmp;
@ -1436,6 +1434,8 @@ ospf_ext_spf(struct ospf_proto *p)
WALK_SLIST(en, p->lsal) WALK_SLIST(en, p->lsal)
{ {
orta nfa = {};
/* 16.4. (1) */ /* 16.4. (1) */
if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_NSSA)) if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_NSSA))
continue; continue;
@ -1577,6 +1577,7 @@ ospf_rt_reset(struct ospf_proto *p)
FIB_WALK(&p->rtf, ort, ri) FIB_WALK(&p->rtf, ort, ri)
{ {
ri->area_net = 0; ri->area_net = 0;
ri->keep = 0;
reset_ri(ri); reset_ri(ri);
} }
FIB_WALK_END; FIB_WALK_END;
@ -1939,9 +1940,12 @@ again1:
} }
} }
/* Remove configured stubnets */ /* Remove configured stubnets but keep the entries */
if (!nf->n.nhs) if (nf->n.type && !nf->n.nhs)
{
reset_ri(nf); reset_ri(nf);
nf->keep = 1;
}
if (nf->n.type) /* Add the route */ if (nf->n.type) /* Add the route */
{ {
@ -1981,7 +1985,7 @@ again1:
} }
/* Remove unused rt entry, some special entries are persistent */ /* 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) if (nf->lsa_id)
idm_free(&p->idm, nf->lsa_id); idm_free(&p->idm, nf->lsa_id);

View file

@ -84,6 +84,7 @@ typedef struct ort
u32 lsa_id; u32 lsa_id;
u8 external_rte; u8 external_rte;
u8 area_net; u8 area_net;
u8 keep;
struct fib_node fn; struct fib_node fn;
} }

View file

@ -2,3 +2,5 @@ src := rpki.c packets.c tcp_transport.c ssh_transport.c transport.c
obj := $(src-o-files) obj := $(src-o-files)
$(all-daemon) $(all-daemon)
$(cf-local) $(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -531,8 +531,6 @@ rpki_send_pdu(struct rpki_cache *cache, const void *pdu, const uint len)
static int static int
rpki_check_receive_packet(struct rpki_cache *cache, const struct pdu_header *pdu) 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); 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 else if (cache->last_update == 0
&& pdu->ver >= RPKI_MIN_VERSION
&& pdu->ver <= RPKI_MAX_VERSION && pdu->ver <= RPKI_MAX_VERSION
&& pdu->ver < cache->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: case UNSUPPORTED_PROTOCOL_VER:
CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version"); CACHE_TRACE(D_PACKETS, cache, "Client uses unsupported protocol version");
if (pdu->ver <= RPKI_MAX_VERSION && if (pdu->ver <= RPKI_MAX_VERSION &&
pdu->ver >= RPKI_MIN_VERSION &&
pdu->ver < cache->version) pdu->ver < cache->version)
{ {
CACHE_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver); CACHE_TRACE(D_EVENTS, cache, "Downgrading from protocol version %d to version %d", cache->version, pdu->ver);

View file

@ -357,7 +357,7 @@ rpki_stop_retry_timer_event(struct rpki_cache *cache)
tm_stop(cache->retry_timer); tm_stop(cache->retry_timer);
} }
static void static void UNUSED
rpki_stop_expire_timer_event(struct rpki_cache *cache) rpki_stop_expire_timer_event(struct rpki_cache *cache)
{ {
CACHE_DBG(cache, "Stop"); CACHE_DBG(cache, "Stop");
@ -636,7 +636,7 @@ rpki_shutdown(struct proto *P)
*/ */
static int 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) 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|. * protocol. Returns |NEED_TO_RESTART| or |SUCCESSFUL_RECONF|.
*/ */
static int 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; u8 try_fast_reconnect = 0;
if (strcmp(old->hostname, new->hostname) != 0) if (strcmp(old->hostname, new->hostname) != 0)
{ {
CACHE_TRACE(D_EVENTS, cache, "Cache server address changed to %s", new->hostname); 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 #undef TEST_INTERVAL
if (try_fast_reconnect) if (try_fast_reconnect)
return rpki_try_fast_reconnect(cache, new, old); return rpki_try_fast_reconnect(cache);
return SUCCESSFUL_RECONF; return SUCCESSFUL_RECONF;
} }
@ -906,7 +905,7 @@ rpki_postconfig(struct proto_config *CF)
static void static void
rpki_copy_config(struct proto_config *dest, struct proto_config *src) rpki_copy_config(struct proto_config *dest, struct proto_config *src)
{ {
/* Just a shallow copy */ /* FIXME: Should copy transport */
} }
struct protocol proto_rpki = { struct protocol proto_rpki = {

View file

@ -29,7 +29,6 @@
#define RPKI_VERSION_0 0 #define RPKI_VERSION_0 0
#define RPKI_VERSION_1 1 #define RPKI_VERSION_1 1
#define RPKI_MIN_VERSION RPKI_VERSION_0
#define RPKI_MAX_VERSION RPKI_VERSION_1 #define RPKI_MAX_VERSION RPKI_VERSION_1

View file

@ -9,6 +9,7 @@
#include <net/if_dl.h> #include <net/if_dl.h>
#include <netinet/in_systm.h> // Workaround for some BSDs #include <netinet/in_systm.h> // Workaround for some BSDs
#include <netinet/ip.h> #include <netinet/ip.h>
#include <sys/param.h>
#ifdef __NetBSD__ #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_src = ipa_to_in4(s->saddr);
ip->ip_dst = ipa_to_in4(s->daddr); ip->ip_dst = ipa_to_in4(s->daddr);
#ifdef __OpenBSD__ #if (defined __OpenBSD__) || (defined __DragonFly__) || (defined __FreeBSD__ && (__FreeBSD_version >= 1100030))
/* OpenBSD expects ip_len in network order, other BSDs expect host order */ /* Different BSDs have different expectations of ip_len endianity */
ip->ip_len = htons(ip->ip_len); ip->ip_len = htons(ip->ip_len);
#endif #endif
} }

View file

@ -7,7 +7,7 @@
#define _BIRD_CONFIG_H_ #define _BIRD_CONFIG_H_
/* BIRD version */ /* BIRD version */
#define BIRD_VERSION "1.6.2" #define BIRD_VERSION "2.0.0-pre0"
/* Include parameters determined by configure script */ /* Include parameters determined by configure script */
#include "sysdep/autoconf.h" #include "sysdep/autoconf.h"

View file

@ -1809,6 +1809,7 @@ nl_async_hook(sock *sk, uint size UNUSED)
* One day we might react to it by asking for route table * One day we might react to it by asking for route table
* scan in near future. * 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 */ return 1; /* More data are likely to be ready */
} }
else if (errno != EWOULDBLOCK) else if (errno != EWOULDBLOCK)

View file

@ -2631,7 +2631,8 @@ io_loop(void)
if (pfd[s->index].revents & (POLLHUP | POLLERR)) if (pfd[s->index].revents & (POLLHUP | POLLERR))
{ {
sk_err(s, pfd[s->index].revents); sk_err(s, pfd[s->index].revents);
goto next2; if (s != current_sock)
goto next2;
} }
current_sock = sk_next(s); current_sock = sk_next(s);

View file

@ -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); vsnprintf(pos, sizeof(msg_buf) - (pos - msg_buf), fmt, argptr);
int chrs = 0; 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) if (i)
printf("\n"); printf("\n");

View file

@ -8,10 +8,10 @@ set -e
AC=`if [ -x /usr/bin/autoconf2.50 ] ; then echo autoconf2.50 ; else echo autoconf ; fi` AC=`if [ -x /usr/bin/autoconf2.50 ] ; then echo autoconf2.50 ; else echo autoconf ; fi`
$AC $AC
./configure ./configure
make docs
make distclean make distclean
$AC $AC
rm -rf autom4te*cache rm -rf autom4te*cache
( cd doc ; make docs ; make clean )
VERSION=`sed <sysdep/config.h '/BIRD_VERSION/!d;s/^.*"\(.*\)"$/\1/'` VERSION=`sed <sysdep/config.h '/BIRD_VERSION/!d;s/^.*"\(.*\)"$/\1/'`
REL=bird-$VERSION REL=bird-$VERSION
DREL=bird-doc-$VERSION DREL=bird-doc-$VERSION
@ -23,9 +23,10 @@ cp -a . $T/$REL
echo Generating ChangeLog echo Generating ChangeLog
git log >$T/$REL/ChangeLog git log >$T/$REL/ChangeLog
mv $T/$REL/doc/*.ps $T/$DREL/doc mv $T/$REL/doc/*.ps $T/$DREL/doc
mv $T/$REL/doc/*.pdf $T/$DREL/doc
rm -f $T/$REL/bird.conf* rm -f $T/$REL/bird.conf*
rm -rf $T/$REL/.git/ 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 $REL.tar.gz $REL )
( cd $T ; tar czvvf $DREL.tar.gz $DREL ) ( cd $T ; tar czvvf $DREL.tar.gz $DREL )
rm -rf $T/$REL $T/$DREL rm -rf $T/$REL $T/$DREL