From ac3ad139f648184d44707ab145fde3a03ef5cb6e Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Wed, 7 Dec 2016 18:28:07 +0100 Subject: [PATCH] BGP: Add support for flowspec (RFC 5575) --- lib/flowspec.h | 3 + proto/bgp/attrs.c | 16 +++- proto/bgp/bgp.c | 2 +- proto/bgp/bgp.h | 3 + proto/bgp/config.Y | 4 +- proto/bgp/packets.c | 225 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 247 insertions(+), 6 deletions(-) diff --git a/lib/flowspec.h b/lib/flowspec.h index 57809bec..aa9735f4 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; } diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 52b56efa..227ddadc 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1221,8 +1221,8 @@ 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; } static struct bgp_prefix * @@ -1237,7 +1237,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 +1258,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); } diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 2ca153ab..b7229429 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1846,7 +1846,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..db9ee8ea 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; diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 10a338d8..2a54db17 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -28,7 +28,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, BGP_CLUSTER_LIST, IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX, GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY, - STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST) + STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6) %type bgp_afi @@ -139,6 +139,8 @@ bgp_afi: | IPV6 { $$ = BGP_AF_IPV6; } | IPV4 MULTICAST { $$ = BGP_AF_IPV4_MC; } | IPV6 MULTICAST { $$ = BGP_AF_IPV6_MC; } + | FLOW4 { $$ = BGP_AF_FLOW4; } + | FLOW6 { $$ = BGP_AF_FLOW6; } ; bgp_channel_start: bgp_afi diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 66561ee4..1ae75a64 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" @@ -793,6 +794,26 @@ 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) +{ + // FIXME + return 0; +} + +static void +bgp_decode_next_hop_none(struct bgp_parse_state *s UNUSED, byte *data UNUSED, uint len UNUSED, rta *a UNUSED) +{ + // FIXME + return; +} + +static void +bgp_update_next_hop_none(struct bgp_export_state *s UNUSED, eattr *a UNUSED, ea_list **to UNUSED) +{ + // FIXME +} + /* * UPDATE @@ -1066,6 +1087,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 +1292,16 @@ static const struct bgp_af_desc bgp_af_table[] = { .decode_next_hop = bgp_decode_next_hop_ip4, .update_next_hop = bgp_update_next_hop_ip, }, + { + .afi = BGP_AF_FLOW4, + .net = NET_FLOW4, + .name = "flow4", + .encode_nlri = bgp_encode_nlri_flow4, + .decode_nlri = bgp_decode_nlri_flow4, + .encode_next_hop = bgp_encode_next_hop_none, + .decode_next_hop = bgp_decode_next_hop_none, + .update_next_hop = bgp_update_next_hop_none, + }, { .afi = BGP_AF_IPV6, .net = NET_IP6, @@ -1107,6 +1322,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 *