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