0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-09-07 22:15:19 +00:00

BMP protocol support

Initial implementation of a basic subset of the BMP (BGP Monitoring
Protocol, RFC 7854) from Akamai team. Submitted for further review
and improvement.
This commit is contained in:
Pawel Maslanka 2021-03-29 22:45:21 +02:00 committed by Ondrej Zajicek
parent 9e44ace392
commit a848dad40a
20 changed files with 2391 additions and 4 deletions

View File

@ -312,7 +312,7 @@ if test "$enable_mpls_kernel" != no ; then
fi
fi
all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip rpki static"
all_protocols="$proto_bfd babel bgp bmp mrt ospf perf pipe radv rip rpki static"
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
@ -323,6 +323,7 @@ fi
AH_TEMPLATE([CONFIG_BABEL], [Babel protocol])
AH_TEMPLATE([CONFIG_BFD], [BFD protocol])
AH_TEMPLATE([CONFIG_BGP], [BGP protocol])
AH_TEMPLATE([CONFIG_BMP], [BMP protocol])
AH_TEMPLATE([CONFIG_MRT], [MRT protocol])
AH_TEMPLATE([CONFIG_OSPF], [OSPF protocol])
AH_TEMPLATE([CONFIG_PIPE], [Pipe protocol])

View File

@ -42,6 +42,7 @@ enum protocol_class {
PROTOCOL_BABEL,
PROTOCOL_BFD,
PROTOCOL_BGP,
PROTOCOL_BMP,
PROTOCOL_DEVICE,
PROTOCOL_DIRECT,
PROTOCOL_KERNEL,
@ -103,7 +104,7 @@ void protos_dump_all(void);
extern struct protocol
proto_device, proto_radv, proto_rip, proto_static, proto_mrt,
proto_ospf, proto_perf,
proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki;
proto_pipe, proto_bgp, proto_bmp, proto_bfd, proto_babel, proto_rpki;
/*
* Routing Protocol Instance
@ -213,6 +214,8 @@ struct proto {
void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
void (*rte_update_in_notify)(const struct proto *, const struct channel *,
const net *net, const struct rte *new, const struct rte *old, const struct rte_src *src);
void (*neigh_notify)(struct neighbor *neigh);
int (*preexport)(struct channel *, struct rte *rt);
void (*reload_routes)(struct channel *);

View File

@ -3095,6 +3095,10 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
{
old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
if (c->proto->rte_update_in_notify)
c->proto->rte_update_in_notify(c->proto, c, net, new, old, src);
return 1;
}
@ -3118,6 +3122,9 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
if (!old)
goto drop_withdraw;
if (c->proto->rte_update_in_notify)
c->proto->rte_update_in_notify(c->proto, c, net, new, old, src);
if (!net->routes)
fib_delete(&tab->fib, net);
@ -3149,6 +3156,10 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
e->next = *pos;
*pos = e;
tab->rt_count++;
if (c->proto->rte_update_in_notify)
c->proto->rte_update_in_notify(c->proto, c, net, new, old, src);
return 1;
drop_update:

View File

@ -2,6 +2,7 @@ H Protocols
C babel
C bfd
C bgp
C bmp
C ospf
C pipe
C radv

View File

@ -1151,6 +1151,13 @@ bgp_attr_known(uint code)
return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name;
}
void bgp_fix_attr_flags(ea_list *attrs)
{
for (u8 i = 0; i < attrs->count; i++)
{
attrs->attrs[i].flags = bgp_attr_table[EA_ID(attrs->attrs[i].id)].flags;
}
}
/*
* Attribute export

View File

@ -125,6 +125,7 @@
#include "lib/string.h"
#include "bgp.h"
#include "proto/bmp/bmp.h"
static list STATIC_LIST_INIT(bgp_sockets); /* Global list of listening sockets */
@ -866,7 +867,10 @@ bgp_graceful_restart_timeout(timer *t)
}
}
else
{
bgp_stop(p, 0, NULL, 0);
bmp_peer_down(p, BE_NONE, NULL, BMP_PEER_DOWN_NULL_PKT_SIZE);
}
}
static void
@ -990,7 +994,10 @@ bgp_sock_err(sock *sk, int err)
if (err)
BGP_TRACE(D_EVENTS, "Connection lost (%M)", err);
else
{
BGP_TRACE(D_EVENTS, "Connection closed");
bmp_peer_down(p, BE_SOCKET, NULL, BMP_PEER_DOWN_NULL_PKT_SIZE);
}
if ((conn->state == BS_ESTABLISHED) && p->gr_ready)
bgp_handle_graceful_restart(p);
@ -1315,6 +1322,7 @@ bgp_neigh_notify(neighbor *n)
bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST);
/* Perhaps also run bgp_update_startup_delay(p)? */
bgp_stop(p, 0, NULL, 0);
bmp_peer_down(p, BE_MISC, NULL, BMP_PEER_DOWN_NULL_PKT_SIZE);
}
}
else if (p->cf->check_link && !(n->iface->flags & IF_LINK_UP))
@ -1326,6 +1334,7 @@ bgp_neigh_notify(neighbor *n)
if (ps == PS_UP)
bgp_update_startup_delay(p);
bgp_stop(p, 0, NULL, 0);
bmp_peer_down(p, BE_MISC, NULL, BMP_PEER_DOWN_NULL_PKT_SIZE);
}
}
else
@ -1367,6 +1376,7 @@ bgp_bfd_notify(struct bfd_request *req)
if (ps == PS_UP)
bgp_update_startup_delay(p);
bgp_stop(p, 0, NULL, 0);
bmp_peer_down(p, BE_MISC, NULL, BMP_PEER_DOWN_NULL_PKT_SIZE);
}
}
}
@ -1684,6 +1694,7 @@ bgp_init(struct proto_config *CF)
struct bgp_config *cf = (struct bgp_config *) CF;
P->rt_notify = bgp_rt_notify;
P->rte_update_in_notify = bgp_rte_update_in_notify;
P->preexport = bgp_preexport;
P->neigh_notify = bgp_neigh_notify;
P->reload_routes = bgp_reload_routes;

View File

@ -496,6 +496,13 @@ struct bgp_parse_state {
#define BGP_CF_WALK_CHANNELS(P,C) WALK_LIST(C, P->c.channels) if (C->c.channel == &channel_bgp)
#define BGP_WALK_CHANNELS(P,C) WALK_LIST(C, P->p.channels) if (C->c.channel == &channel_bgp)
#define BGP_MSG_HDR_MARKER_SIZE 16
#define BGP_MSG_HDR_MARKER_POS 0
#define BGP_MSG_HDR_LENGTH_SIZE 2
#define BGP_MSG_HDR_LENGTH_POS BGP_MSG_HDR_MARKER_SIZE
#define BGP_MSG_HDR_TYPE_SIZE 1
#define BGP_MSG_HDR_TYPE_POS (BGP_MSG_HDR_MARKER_SIZE + BGP_MSG_HDR_LENGTH_SIZE)
static inline int bgp_channel_is_ipv4(struct bgp_channel *c)
{ return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
@ -542,6 +549,8 @@ void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code
void bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len);
const char *bgp_format_role_name(u8 role);
void bgp_fix_attr_flags(ea_list *attrs);
static inline int
rte_resolvable(rte *rt)
{
@ -615,6 +624,9 @@ struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool);
u32 bgp_rte_igp_metric(struct rte *);
void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old);
int bgp_preexport(struct channel *, struct rte *);
void bgp_rte_update_in_notify(const struct proto *P, const struct channel *C,
const net *net, const struct rte *new, const struct rte *old,
const struct rte_src *src);
int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
void bgp_get_route_info(struct rte *, byte *buf);
int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
@ -648,6 +660,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
byte * bgp_create_end_mark(struct bgp_channel *c, byte *buf);
/* Packet types */
@ -658,6 +671,7 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
#define PKT_ROUTE_REFRESH 0x05 /* [RFC2918] */
#define PKT_BEGIN_REFRESH 0x1e /* Dummy type for BoRR packet [RFC7313] */
#define PKT_SCHEDULE_CLOSE 0x1f /* Used internally to schedule socket close */
#define PKT_BMP_MSG 0x20 /* BGP Monitoring Protocol message [RFC7854] */
/* Attributes */

View File

@ -26,6 +26,7 @@
#include "nest/cli.h"
#include "bgp.h"
#include "proto/bmp/bmp.h"
#define BGP_RR_REQUEST 0
@ -166,6 +167,7 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf)
buf[0] = conn->notify_code;
buf[1] = conn->notify_subcode;
memcpy(buf+2, conn->notify_data, conn->notify_size);
bmp_peer_down(p, BE_NONE, buf, conn->notify_size + 2);
return buf + 2 + conn->notify_size;
}
@ -975,6 +977,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
bgp_schedule_packet(conn, NULL, PKT_KEEPALIVE);
bgp_start_timer(conn->hold_timer, conn->hold_time);
bgp_conn_enter_openconfirm_state(conn);
bmp_put_recv_bgp_open_msg(p, pkt, len);
}
@ -2239,6 +2242,224 @@ bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to)
#define MAX_ATTRS_LENGTH (end-buf+BGP_HEADER_LENGTH - 1024)
/**
* Following functions starting with prefix bgp_bmp_* compose BGP UPDATE messages.
* Their implementation has been adopted from relevant function without 'bmp_' part in
* their names.
*/
// Buffer @buf should be big enough. It means that there should be available at least 19 bytes
static byte *
bgp_bmp_prepare_bgp_hdr(byte *buf, const u16 msg_size, const u8 msg_type)
{
if (!buf)
{
return NULL;
}
memset(buf + BGP_MSG_HDR_MARKER_POS, 0xff, BGP_MSG_HDR_MARKER_SIZE);
put_u16(buf + BGP_MSG_HDR_LENGTH_POS, msg_size);
put_u8(buf + BGP_MSG_HDR_TYPE_POS, msg_type);
return buf + BGP_MSG_HDR_TYPE_POS + BGP_MSG_HDR_TYPE_SIZE;
}
static uint
bgp_bmp_encode_nlri_ip4(struct bgp_write_state *s, const net *n,
const u32 path_id, byte *buf, uint size)
{
const struct net_addr_ip4 *naddr = (net_addr_ip4 *)n->n.addr;
byte *pos = buf;
/* Encode path ID */
if (s->add_path)
{
put_u32(pos, path_id);
ADVANCE(pos, size, 4);
}
/* Encode prefix length */
*pos = naddr->pxlen;
ADVANCE(pos, size, 1);
/* Encode MPLS labels */
if (s->mpls)
{
bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
}
/* Encode prefix body */
ip4_addr a = ip4_hton(naddr->prefix);
uint b = (naddr->pxlen + 7) / 8;
memcpy(pos, &a, b);
ADVANCE(pos, size, b);
return pos - buf;
}
static uint
bgp_bmp_encode_nlri_ip6(struct bgp_write_state *s, const net *n,
const u32 path_id, byte *buf, uint size)
{
if (size < BGP_NLRI_MAX)
{
return 0;
}
const struct net_addr_ip6 *naddr = (net_addr_ip6 *)n->n.addr;
byte *pos = buf;
/* Encode path ID */
if (s->add_path)
{
put_u32(pos, path_id);
ADVANCE(pos, size, 4);
}
/* Encode prefix length */
*pos = naddr->pxlen;
ADVANCE(pos, size, 1);
/* Encode MPLS labels */
if (s->mpls)
{
bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
}
/* Encode prefix body */
ip6_addr a = ip6_hton(naddr->prefix);
uint b = (naddr->pxlen + 7) / 8;
memcpy(pos, &a, b);
ADVANCE(pos, size, b);
return pos - buf;
}
static byte *
bgp_bmp_create_ip_reach(struct bgp_write_state *s, const net *n,
const struct rte *new, const struct rte *old, const u32 path_id,
byte *buf, uint size)
{
/*
* 2 B Withdrawn Routes Length (zero)
* --- IPv4 Withdrawn Routes NLRI (unused)
* 2 B Total Path Attribute Length
* var Path Attributes
* var IPv4 Network Layer Reachability Information
*/
int lr, la; // Route length, attribute length
ea_list *attrs = new ? new->attrs->eattrs : old->attrs->eattrs;
bgp_fix_attr_flags(attrs);
la = bgp_encode_attrs(s, attrs, buf + 2, buf + size - 2);
if (la < 0)
{
/* Attribute list too long */
log(L_ERR "Failed to encode UPDATE msg attributes");
return NULL;
}
put_u16(buf, la);
lr = bgp_bmp_encode_nlri_ip4(s, n, path_id, buf + 2 + la, size - (2 + la));
return buf + 2 + la + lr;
}
static byte *
bgp_bmp_create_mp_reach(struct bgp_write_state *s, const net *n,
const struct rte *new, const struct rte *old, const u32 path_id,
byte *buf, uint size)
{
/*
* 2 B IPv4 Withdrawn Routes Length (zero)
* --- IPv4 Withdrawn Routes NLRI (unused)
* 2 B Total Path Attribute Length
* 1 B MP_REACH_NLRI hdr - Attribute Flags
* 1 B MP_REACH_NLRI hdr - Attribute Type Code
* 2 B MP_REACH_NLRI hdr - Length of Attribute Data
* 2 B MP_REACH_NLRI data - Address Family Identifier
* 1 B MP_REACH_NLRI data - Subsequent Address Family Identifier
* 1 B MP_REACH_NLRI data - Length of Next Hop Network Address
* var MP_REACH_NLRI data - Network Address of Next Hop
* 1 B MP_REACH_NLRI data - Reserved (zero)
* var MP_REACH_NLRI data - Network Layer Reachability Information
* var Rest of Path Attributes
* --- IPv4 Network Layer Reachability Information (unused)
*/
int lh, lr, la; /* Lengths of next hop, NLRI and attributes */
/* Begin of MP_REACH_NLRI atribute */
buf[4] = BAF_OPTIONAL | BAF_EXT_LEN;
buf[5] = BA_MP_REACH_NLRI;
put_u16(buf+6, 0); /* Will be fixed later */
put_af3(buf+8, s->channel->afi);
byte *pos = buf+11;
byte *end = buf + size;
/* Encode attributes to temporary buffer */
byte *abuf = alloca(MAX_ATTRS_LENGTH);
ea_list *attrs = new ? new->attrs->eattrs : old->attrs->eattrs;
bgp_fix_attr_flags(attrs);
la = bgp_encode_attrs(s, attrs, abuf, abuf + MAX_ATTRS_LENGTH);
if (la < 0)
{
/* Attribute list too long */
log(L_ERR "Failed to encode UPDATE msg attributes");
return NULL;
}
/* Encode the next hop */
lh = bgp_encode_next_hop(s, s->mp_next_hop, pos+1);
*pos = lh;
pos += 1+lh;
/* Reserved field */
*pos++ = 0;
/* Encode the NLRI */
lr = bgp_bmp_encode_nlri_ip6(s, n, path_id, pos, end - (buf + la));
pos += lr;
/* End of MP_REACH_NLRI atribute, update data length */
put_u16(buf+6, pos-buf-8);
/* Copy remaining attributes */
memcpy(pos, abuf, la);
pos += la;
/* Initial UPDATE fields */
put_u16(buf+0, 0);
put_u16(buf+2, pos-buf-4);
return pos;
}
static byte *
bgp_bmp_create_ip_unreach(struct bgp_write_state *s, const net *n,
const struct rte *new, const struct rte *old, const u32 path_id,
byte *buf, uint size)
{
/*
* 2 B Withdrawn Routes Length
* var IPv4 Withdrawn Routes NLRI
* 2 B Total Path Attribute Length (zero)
* --- Path Attributes (unused)
* --- IPv4 Network Layer Reachability Information (unused)
*/
uint len = 0;
bool is_withdrawn = !new && old;
if (is_withdrawn)
{
len = bgp_bmp_encode_nlri_ip4(s, n, path_id, buf + 2, size - 2);
}
put_u16(buf, len);
return buf + 2 + len;
}
static byte *
bgp_create_ip_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, byte *end)
{
@ -2384,6 +2605,122 @@ bgp_create_mp_unreach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *
return buf+11+len;
}
static byte *
bgp_bmp_create_mp_unreach(struct bgp_write_state *s, const net *n,
const struct rte *new, const struct rte *old, const u32 path_id,
byte *buf, uint size)
{
/*
* 2 B Withdrawn Routes Length (zero)
* --- IPv4 Withdrawn Routes NLRI (unused)
* 2 B Total Path Attribute Length
* 1 B MP_UNREACH_NLRI hdr - Attribute Flags
* 1 B MP_UNREACH_NLRI hdr - Attribute Type Code
* 2 B MP_UNREACH_NLRI hdr - Length of Attribute Data
* 2 B MP_UNREACH_NLRI data - Address Family Identifier
* 1 B MP_UNREACH_NLRI data - Subsequent Address Family Identifier
* var MP_UNREACH_NLRI data - Network Layer Reachability Information
* --- IPv4 Network Layer Reachability Information (unused)
*/
uint len = 0;
bool is_withdrawn = !new && old;
if (is_withdrawn)
{
len = bgp_bmp_encode_nlri_ip6(s, n, path_id, buf + 11, size);
}
put_u16(buf+0, 0);
put_u16(buf+2, 7+len);
/* Begin of MP_UNREACH_NLRI atribute */
buf[4] = BAF_OPTIONAL | BAF_EXT_LEN;
buf[5] = BA_MP_UNREACH_NLRI;
put_u16(buf+6, 3+len);
put_af3(buf+8, s->channel->afi);
return buf+11+len;
}
void
bgp_rte_update_in_notify(const struct proto *P, const struct channel *C,
const net *n, const struct rte *new, const struct rte *old,
const struct rte_src *src)
{
struct bgp_proto *p = (struct bgp_proto *)P;
struct bgp_channel *c = (struct bgp_channel *)C;
byte buf[BGP_MAX_EXT_MSG_LENGTH] = { 0x00 };
byte *pkt = buf + BGP_HEADER_LENGTH;
byte *end = pkt + (bgp_max_packet_length(p->conn) - BGP_HEADER_LENGTH);
struct bgp_caps *peer = p->conn->remote_caps;
const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi);
struct bgp_write_state s = {
.proto = p,
.channel = c,
.pool = tmp_linpool,
.mp_reach = (bgp_channel_is_ipv6(c) || rem->ext_next_hop),
.as4_session = peer->as4_support,
.add_path = c->add_path_rx,
.mpls = c->desc->mpls,
};
const u32 path_id = c->add_path_rx ? src->private_id : 0;
byte *pos = pkt;
if (!s.mp_reach)
{
pos = bgp_bmp_create_ip_unreach(&s, n, new, old, path_id, pkt, end - pkt);
if (!pos)
{
log(L_ERR "Failed to create unreachable field in UPDATE message");
return;
}
pos = bgp_bmp_create_ip_reach(&s, n, new, old, path_id, pos, end - pos);
if (!pos)
{
log(L_ERR "Failed to create reachable field in UPDATE message");
return;
}
bgp_bmp_prepare_bgp_hdr(buf, pos - buf, PKT_UPDATE);
bmp_route_monitor_put_update_in_pre_msg(buf, pos - buf);
}
else if (new) // && s.mp_reach
{
pos = s.mp_reach
? bgp_bmp_create_mp_reach(&s, n, new, old, path_id, pos, end - pos)
: bgp_bmp_create_ip_reach(&s, n, new, old, path_id, pos, end - pos);
if (!pos)
{
log(L_ERR "Failed to create reachable field in UPDATE message");
return;
}
bgp_bmp_prepare_bgp_hdr(buf, pos - buf, PKT_UPDATE);
bmp_route_monitor_put_update_in_pre_msg(buf, pos - buf);
}
if (!new && old)
{
bmp_route_monitor_update_in_pre_commit(p);
bmp_route_monitor_update_in_pre_end();
bmp_route_monitor_update_in_pre_begin();
pkt = buf + BGP_HEADER_LENGTH;
end = pkt + (bgp_max_packet_length(p->conn) - BGP_HEADER_LENGTH);
pos = bgp_bmp_create_mp_unreach(&s, n, new, old, path_id, pkt, end - pkt);
if (!pos)
{
log(L_ERR "Failed to create unreachable field in UPDATE message");
return;
}
bgp_bmp_prepare_bgp_hdr(buf, pos - buf, PKT_UPDATE);
bmp_route_monitor_put_update_in_pre_msg(buf, pos - buf);
}
}
static byte *
bgp_create_update(struct bgp_channel *c, byte *buf)
{
@ -2484,7 +2821,7 @@ bgp_create_mp_end_mark(struct bgp_channel *c, byte *buf)
return buf+10;
}
static byte *
byte *
bgp_create_end_mark(struct bgp_channel *c, byte *buf)
{
struct bgp_proto *p = (void *) c->c.proto;
@ -2635,6 +2972,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
s.ip_reach_len = len - pos;
s.ip_reach_nlri = pkt + pos;
bmp_route_monitor_update_in_pre_begin();
if (s.attr_len)
ea = bgp_decode_attrs(&s, s.attrs, s.attr_len);
@ -2666,6 +3004,9 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
bgp_decode_nlri(&s, s.mp_reach_af, s.mp_reach_nlri, s.mp_reach_len,
ea, s.mp_next_hop_data, s.mp_next_hop_len);
bmp_route_monitor_update_in_pre_commit(p);
bmp_route_monitor_update_in_pre_end();
done:
rta_free(s.cached_rta);
lp_restore(tmp_linpool, &tmpp);
@ -2917,7 +3258,12 @@ bgp_fire_tx(struct bgp_conn *conn)
{
conn->packets_to_send &= ~(1 << PKT_OPEN);
end = bgp_create_open(conn, pkt);
return bgp_send(conn, PKT_OPEN, end - buf);
int rv = bgp_send(conn, PKT_OPEN, end - buf);
if (rv >= 0)
{
bmp_put_sent_bgp_open_msg(p, pkt, end - buf);
}
return rv;
}
else if (s & (1 << PKT_KEEPALIVE))
{
@ -3216,6 +3562,8 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, uint len)
p->p.disabled = 1;
}
}
bmp_peer_down(p, BE_NONE, pkt, len);
}
static void

1
proto/bmp/Doc Normal file
View File

@ -0,0 +1 @@
S bmp.c

2
proto/bmp/LICENSE Normal file
View File

@ -0,0 +1,2 @@
The patch is provided under the terms of the GNU General Public License, either
version 2, or any later version.

6
proto/bmp/Makefile Normal file
View File

@ -0,0 +1,6 @@
src := bmp.c buffer.c map.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

6
proto/bmp/README.txt Normal file
View File

@ -0,0 +1,6 @@
ABOUT
This package |proto/bmp/*| provide implementation of BGP Monitoring Protocol (BMP).
It has been started by Akamai Technologies, Inc. as a pilot program for support BMP in BIRD.
It provides only basic features of BMP specification which are needed by Akamai evaluation of
feasible BMP protocol.
Content of this package has been provided as a patch for BIRD release v2.0.7.

1279
proto/bmp/bmp.c Normal file

File diff suppressed because it is too large Load Diff

152
proto/bmp/bmp.h Normal file
View File

@ -0,0 +1,152 @@
/*
* BIRD -- The BGP Monitoring Protocol (BMP)
*
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_BMP_H_
#define _BIRD_BMP_H_
#include "nest/bird.h"
#include "nest/protocol.h"
#include "lib/lists.h"
#include "nest/route.h"
#include "lib/event.h"
#include "lib/hash.h"
#include "lib/socket.h"
#include "proto/bmp/map.h"
#include <stdbool.h>
// Max length of MIB-II description object
#define MIB_II_STR_LEN 255
enum bmp_peer_down {
// Value of packet size of 'pkt_size' argument of bmp_peer_down() function
// used for pointing out that there was not any packet to pass
BMP_PEER_DOWN_NULL_PKT_SIZE = 0
};
// The following fields of this structure controls whether there will be put
// specific routes into Route Monitoring message and send to BMP collector
struct monitoring_rib {
bool in_pre_policy; // Monitoring pre-policy Adj-Rib-In
bool in_post_policy; // Monitoring post-policy Adj-Rib-In
bool local; // Monitoring Local Rib
};
struct bmp_config {
struct proto_config c;
char *sys_descr; // sysDescr MIB-II [RFC1213] object
char *sys_name; // sysName MIB-II [RFC1213] object
ip_addr station_ip; // Monitoring station address
u16 station_port; // Monitoring station TCP port
bool disabled; // Manually disabled
bool monitoring_rib_in_pre_policy; // Route monitoring pre-policy Adj-Rib-In
bool monitoring_rib_in_post_policy; // Route monitoring post-policy Adj-Rib-In
bool monitoring_rib_local; // Route monitoring Local Rib
};
/* Forward declarations */
struct bgp_proto;
struct bmp_proto;
struct bmp_conn {
struct bmp_proto *bmp;
struct birdsock *sk;
event *tx_ev;
};
// Stores sent and received BGP OPEN MSGs
struct bmp_peer_open_msg {
struct bmp_peer_map tx_msg;
struct bmp_peer_map rx_msg;
};
// Keeps necessary information during composing BGP UPDATE MSG which is going
// to be sent to the BMP collector
struct rt_table_info {
list update_msg_queue; // Stores all composed BGP UPDATE MSGs
size_t update_msg_size; // Size of all BGP UPDATE MSGs
struct timeval update_begin_time; // Keeps timestamp of starting BGP UPDATE MSGs composing
bool update_in_progress; // Holds information whether composing process is still in progress
};
struct bmp_proto {
struct proto p; // Parent proto
const struct bmp_config *cf; // Shortcut to BMP configuration
struct bmp_conn *conn; // Connection we have established
char sys_descr[MIB_II_STR_LEN]; // sysDescr MIB-II [RFC1213] object
char sys_name[MIB_II_STR_LEN]; // sysName MIB-II [RFC1213] object
ip_addr station_ip; // Monitoring station IP address
u16 station_port; // Monitoring station TCP port
bool disabled; // Manually disabled
struct monitoring_rib monitoring_rib;
// Below fields are for internal use
int station_socket; // Socket associated with the BMP collector
struct bmp_peer_map bgp_peers; // Stores 'bgp_proto' structure per BGP peer
struct bmp_peer_open_msg peer_open_msg; // Stores sent and received BGP OPEN MSG per BGP peer
pool *buffer_mpool; // Memory pool used for BMP buffer allocations
pool *map_mem_pool; // Memory pool used for BMP map allocations
pool *tx_mem_pool; // Memory pool used for packet allocations designated to BMP collector
pool *update_msg_mem_pool; // Memory pool used for BPG UPDATE MSG allocations
list tx_queue; // Stores queued packets going to be sent
timer *connect_retry_timer; // Timer for retrying connection to the BMP collector
struct rt_table_info rt_table_in_pre_policy; // Pre-policy route import table
bool station_connected; // Flag that stores connection status with BMP station
bool started; // Flag that stores running status of BMP instance
};
/**
* bmp_put_sent_bgp_open_msg - save sent BGP OPEN msg packet in BMP implementation.
* NOTE: If there has been passed sent and received BGP OPEN MSGs to the BMP
* implementation, then there is going to be send BMP Peer Up Notification
* message to the BMP collector.
*/
void
bmp_put_sent_bgp_open_msg(const struct bgp_proto *bgp, const byte* pkt,
const size_t pkt_size);
/**
* bmp_put_recv_bgp_open_msg - save received BGP OPEN msg packet in BMP implementation.
* NOTE: If there has been passed sent and received BGP OPEN MSGs to the BMP
* implementation, then there is going to be send BMP Peer Up Notification
* message to the BMP collector.
*/
void
bmp_put_recv_bgp_open_msg(const struct bgp_proto *bgp, const byte* pkt,
const size_t pkt_size);
/**
* The following 4 functions create BMP Route Monitoring message based on
* pre-policy Adj-RIB-In. Composing Route Monitoring message consist of few
* stages. First of all call bmp_route_monitor_update_in_pre_begin() in order
* to start composing message. As a second step, call
* bmp_route_monitor_put_update_in_pre_msg() in order to save BGP UPDATE msg.
* As a third step call bmp_route_monitor_update_in_pre_commit() in order to
* send BMP Route Monitoring message to the BMP collector. As a last step,
* call bmp_route_monitor_update_in_pre_end() in order to release resources.
*/
void
bmp_route_monitor_update_in_pre_begin(void);
void
bmp_route_monitor_put_update_in_pre_msg(const byte *data, const size_t data_size);
void
bmp_route_monitor_update_in_pre_commit(const struct bgp_proto *bgp);
void
bmp_route_monitor_update_in_pre_end(void);
/**
* bmp_peer_down - send notification that BGP peer connection is not in
* established state
*/
void
bmp_peer_down(const struct bgp_proto *bgp, const int err_class, const byte *pkt,
size_t pkt_size);
#endif /* _BIRD_BMP_H_ */

58
proto/bmp/buffer.c Normal file
View File

@ -0,0 +1,58 @@
/*
* BIRD -- The BGP Monitoring Protocol (BMP)
*
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "proto/bmp/buffer.h"
buffer
bmp_buffer_alloc(pool *ppool, const size_t n)
{
buffer buf;
buf.start = mb_alloc(ppool, n);
buf.pos = buf.start;
buf.end = buf.start + n;
return buf;
}
void
bmp_buffer_free(buffer *buf)
{
mb_free(buf->start);
buf->start = buf->pos = buf->end = NULL;
}
static void
bmp_buffer_grow(buffer *buf, const size_t n)
{
const size_t pos = bmp_buffer_pos(buf);
buf->start = mb_realloc(buf->start, n);
buf->pos = buf->start + pos;
buf->end = buf->start + n;
}
void
bmp_buffer_need(buffer *buf, const size_t n)
{
if (bmp_buffer_avail(buf) < n)
{
bmp_buffer_grow(buf, n);
}
}
void
bmp_put_data(buffer *buf, const void *src, const size_t n)
{
if (!n)
{
return;
}
bmp_buffer_need(buf, n);
memcpy(buf->pos, src, n);
buf->pos += n;
}

78
proto/bmp/buffer.h Normal file
View File

@ -0,0 +1,78 @@
/*
* BIRD -- The BGP Monitoring Protocol (BMP)
*
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_BMP_BUFFER_H_
#define _BIRD_BMP_BUFFER_H_
#include "proto/bmp/bmp.h"
#include "proto/bmp/utils.h"
#include <stdlib.h>
#include "lib/resource.h"
buffer
bmp_buffer_alloc(pool *ppool, const size_t n);
void
bmp_buffer_free(buffer *buf);
static inline void
bmp_buffer_flush(buffer *buf)
{
buf->pos = buf->start;
}
static inline size_t
bmp_buffer_size(const buffer *buf)
{
return buf->end - buf->start;
}
static inline size_t
bmp_buffer_avail(const buffer *buf)
{
return buf->end - buf->pos;
}
static inline size_t
bmp_buffer_pos(const buffer *buf)
{
return buf->pos - buf->start;
}
static inline byte *
bmp_buffer_data(const buffer *buf)
{
return buf->start;
}
void
bmp_buffer_need(buffer *buf, const size_t n);
// Idea for following macros has been taken from |proto/mrt/mrt.c|
#define BMP_DEFINE_PUT_FUNC(S, T) \
static inline void \
bmp_put_##S(buffer *b, const T x) \
{ \
bmp_buffer_need(b, sizeof(T)); \
put_##S(b->pos, x); \
b->pos += sizeof(T); \
}
BMP_DEFINE_PUT_FUNC(u8, u8)
BMP_DEFINE_PUT_FUNC(u16, u16)
BMP_DEFINE_PUT_FUNC(u32, u32)
BMP_DEFINE_PUT_FUNC(u64, u64)
BMP_DEFINE_PUT_FUNC(ip4, ip4_addr)
BMP_DEFINE_PUT_FUNC(ip6, ip6_addr)
void
bmp_put_data(buffer *buf, const void *src, const size_t n);
#endif /* _BIRD_BMP_BUFFER_H_ */

90
proto/bmp/config.Y Normal file
View File

@ -0,0 +1,90 @@
/*
* BIRD -- The BGP Monitoring Protocol (BMP)
*
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
CF_HDR
#include "proto/bmp/bmp.h"
CF_DEFINES
#define BMP_CFG ((struct bmp_config *) this_proto)
CF_DECLS
CF_KEYWORDS(BMP, DESCRIPTION, ENABLED, IN, IP, MONITORING, NAME, PORT,
PRE_POLICY, POST_POLICY, RIB, STATION, SYSTEM)
CF_GRAMMAR
proto: bmp_proto '}' ;
bmp_proto_start: proto_start BMP {
this_proto = proto_config_new(&proto_bmp, $1);
BMP_CFG->disabled = false;
BMP_CFG->station_ip = IPA_NONE4;
BMP_CFG->station_port = 0;
BMP_CFG->sys_descr = "Not defined";
BMP_CFG->sys_name = "Not defined";
BMP_CFG->monitoring_rib_in_pre_policy = false;
BMP_CFG->monitoring_rib_in_post_policy = false;
BMP_CFG->monitoring_rib_local = false;
}
;
bmp_station_address:
/* empty */
| bmp_station_address IP ipa {
if (!BMP_CFG->disabled)
if (ipa_zero($3))
cf_error("Invalid BMP monitoring station IP address");
BMP_CFG->station_ip = $3;
}
| bmp_station_address PORT expr {
if (($3 < 1) || ($3 > 65535))
cf_error("Invalid BMP monitoring station port number");
BMP_CFG->station_port = $3;
}
;
bmp_proto:
bmp_proto_start proto_name '{'
| bmp_proto proto_item ';'
| bmp_proto ENABLED bool ';' {
BMP_CFG->disabled = $3;
}
| bmp_proto STATION ADDRESS bmp_station_address ';'
| bmp_proto SYSTEM DESCRIPTION text ';' {
if (!$4 || (strlen($4) == 0))
cf_error("String is empty");
else if (strlen($4) > 255)
cf_error("Invalid string length");
BMP_CFG->sys_descr = $4;
}
| bmp_proto SYSTEM NAME text ';' {
if (!$4 || (strlen($4) == 0))
cf_error("String is empty");
else if (strlen($4) > 255)
cf_error("Invalid string length");
BMP_CFG->sys_name = $4;
}
| bmp_proto MONITORING RIB IN PRE_POLICY bool ';' {
BMP_CFG->monitoring_rib_in_pre_policy = $6;
}
| bmp_proto MONITORING RIB IN POST_POLICY bool ';' {
BMP_CFG->monitoring_rib_in_post_policy = $6;
}
| bmp_proto MONITORING RIB LOCAL bool ';' {
BMP_CFG->monitoring_rib_local = $5;
}
| bmp_proto DISABLED bool ';' {
BMP_CFG->disabled = $3; }
;
CF_CODE
CF_END

155
proto/bmp/map.c Normal file
View File

@ -0,0 +1,155 @@
/*
* BIRD -- The BGP Monitoring Protocol (BMP)
*
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "proto/bmp/map.h"
#include "proto/bmp/utils.h"
/* Peer Index Table */
#define PEER_KEY(n) (n)->peer_as, (n)->peer_ip
#define PEER_NEXT(n) (n)->next
#define PEER_EQ(as1,ip1,as2,ip2) \
(as1) == (as2) && ipa_equal(ip1, ip2)
#define PEER_FN(as,ip) ipa_hash(ip)
#define PEER_REHASH bmp_peer_rehash
#define PEER_PARAMS /8, *2, 2, 2, 6, 20
HASH_DEFINE_REHASH_FN(PEER, struct bmp_peer_map_key)
#define PEER_INIT_ORDER 6
enum bmp_result
bmp_peer_map_init(struct bmp_peer_map *map, pool *mpool)
{
if (IS_NULL(map) || IS_NULL(mpool))
{
return BMP_E_NULL_REF;
}
map->mpool = mpool;
HASH_INIT(map->peer_hash, map->mpool, PEER_INIT_ORDER);
return BMP_E_NONE;
}
struct bmp_peer_map_key
bmp_peer_map_key_create(const ip_addr peer_ip, const u32 peer_as)
{
struct bmp_peer_map_key key;
key.next = NULL;
key.peer_ip = peer_ip;
key.peer_as = peer_as;
return key;
}
enum bmp_result
bmp_peer_map_flush(struct bmp_peer_map *map)
{
if (IS_NULL(map))
{
return BMP_E_NULL_REF;
}
struct bmp_peer_map_entry *entry;
HASH_WALK_DELSAFE(map->peer_hash, next, e)
{
entry = (struct bmp_peer_map_entry *) e;
mb_free(entry->data.buf);
HASH_DELETE(map->peer_hash, PEER, PEER_KEY(&entry->key));
mb_free(entry);
}
HASH_WALK_DELSAFE_END;
HASH_MAY_RESIZE_DOWN(map->peer_hash, PEER, map->mpool);
return BMP_E_NONE;
}
enum bmp_result
bmp_peer_map_free(struct bmp_peer_map *map)
{
if (IS_NULL(map))
{
return BMP_E_NULL_REF;
}
IF_BMP_FAILED_RETURN_RC(bmp_peer_map_flush(map));
HASH_FREE(map->peer_hash);
return BMP_E_NONE;
}
enum bmp_result
bmp_peer_map_insert(struct bmp_peer_map *map, const struct bmp_peer_map_key key,
const byte *data, const size_t data_size)
{
if (IS_NULL(map))
{
return BMP_E_NULL_REF;
}
if (HASH_FIND(map->peer_hash, PEER, PEER_KEY(&key)))
{
return BMP_E_EXISTS;
}
struct bmp_peer_map_entry *entry = mb_alloc(map->mpool,
sizeof (struct bmp_peer_map_entry));
entry->data.buf = mb_alloc(map->mpool, data_size);
memcpy(entry->data.buf, data, data_size);
entry->data.buf_size = data_size;
entry->key = key;
HASH_INSERT2(map->peer_hash, PEER, map->mpool, &entry->key);
return BMP_E_NONE;
}
enum bmp_result
bmp_peer_map_remove(struct bmp_peer_map *map, const struct bmp_peer_map_key key)
{
if (IS_NULL(map))
{
return BMP_E_NULL_REF;
}
struct bmp_peer_map_entry *entry
= (struct bmp_peer_map_entry *) HASH_FIND(map->peer_hash, PEER, PEER_KEY(&key));
if (IS_NULL(entry))
{
return BMP_E_NOT_EXISTS;
}
mb_free(entry->data.buf);
HASH_DELETE(map->peer_hash, PEER, PEER_KEY(&entry->key));
mb_free(entry);
return BMP_E_NONE;
}
const struct bmp_peer_map_entry *
bmp_peer_map_get(struct bmp_peer_map *map, const struct bmp_peer_map_key key)
{
if (IS_NULL(map))
{
return NULL;
}
return (struct bmp_peer_map_entry *) HASH_FIND(map->peer_hash, PEER, PEER_KEY(&key));
}
void
bmp_peer_map_walk(const struct bmp_peer_map *map, bmp_peer_map_walk_action action)
{
struct bmp_peer_map_entry *entry;
HASH_WALK_FILTER(map->peer_hash, next, e, _)
{
entry = (struct bmp_peer_map_entry *) e;
action(entry->key, entry->data.buf, entry->data.buf_size);
}
HASH_WALK_FILTER_END;
}

70
proto/bmp/map.h Normal file
View File

@ -0,0 +1,70 @@
/*
* BIRD -- The BGP Monitoring Protocol (BMP)
*
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/**
* This map implementation binds peer IP address as container key with custom data.
*/
#ifndef _BIRD_BMP_MAP_H_
#define _BIRD_BMP_MAP_H_
#include "nest/bird.h"
#include "lib/hash.h"
#include "lib/resource.h"
#include "proto/bmp/utils.h"
struct bmp_peer_map_key {
struct bmp_peer_map_key *next;
ip_addr peer_ip;
u32 peer_as;
};
struct bmp_peer_map_data {
void *buf;
size_t buf_size;
};
struct bmp_peer_map_entry {
struct bmp_peer_map_key key;
struct bmp_peer_map_data data;
};
struct bmp_peer_map {
pool *mpool; // Memory pool for peer entries in peer_hash
HASH(struct bmp_peer_map_key) peer_hash; // Hash for peers to find the index
};
enum bmp_result
bmp_peer_map_init(struct bmp_peer_map *map, pool *mpool);
struct bmp_peer_map_key
bmp_peer_map_key_create(const ip_addr peer_ip, const u32 peer_as);
enum bmp_result
bmp_peer_map_free(struct bmp_peer_map *map);
enum bmp_result
bmp_peer_map_flush(struct bmp_peer_map *map);
enum bmp_result
bmp_peer_map_insert(struct bmp_peer_map *map, const struct bmp_peer_map_key key,
const byte *data, const size_t data_size);
enum bmp_result
bmp_peer_map_remove(struct bmp_peer_map *map, const struct bmp_peer_map_key key);
const struct bmp_peer_map_entry *
bmp_peer_map_get(struct bmp_peer_map *map, const struct bmp_peer_map_key key);
typedef void (*bmp_peer_map_walk_action)(const struct bmp_peer_map_key key,
const byte *data, const size_t data_size);
void
bmp_peer_map_walk(const struct bmp_peer_map *map, bmp_peer_map_walk_action action);
#endif /* _BIRD_BMP_MAP_H_ */

94
proto/bmp/utils.h Normal file
View File

@ -0,0 +1,94 @@
/*
* BIRD -- The BGP Monitoring Protocol (BMP)
*
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_BMP_UTILS_H_
#define _BIRD_BMP_UTILS_H_
/**
* TODO:
* - Provide descriptive explanation for the follwing enums
*/
enum bmp_result {
BMP_E_NONE = 0,
BMP_E_INVALID_ARG,
BMP_E_NULL_REF,
BMP_E_EXISTS,
BMP_E_NOT_EXISTS,
BMP_E_OPEN_SOCKET,
BMP_E_CONNECT_TO_SRV,
BMP_E_SEND_DATA,
BMP_E_BIRDSOCK_NULL_REF,
BMP_E_REMOTE_CAPS_NULL_REF,
BMP_E_NEW_TX_EVENT
};
#define BMP_FAILED(v) \
(v != BMP_E_NONE)
#define IF_BMP_FAILED_RETURN_RC(func) \
do { \
enum bmp_result rc = func; \
if (BMP_FAILED(rc)) \
{ \
return rc; \
} \
} while (0)
// The following macro requires to define locally enum bmp_result rc;
#define IF_BMP_FAILED_GOTO_LABEL(func, label) \
do { \
rc = func; \
if (BMP_FAILED(rc)) \
{ \
goto label; \
} \
} while (0)
#define IF_BMP_FAILED_PRINT_ERR_MSG(func, msg) \
do { \
enum bmp_result rc = func; \
if (BMP_FAILED(rc)) \
{ \
log(L_WARN "[BMP] " msg " (rc = %d)", rc); \
} \
} while (0)
#define IF_BMP_FAILED_PRINT_ERR_MSG_AND_GOTO_LABEL(func, msg, label) \
do { \
enum bmp_result rc = func; \
if (BMP_FAILED(rc)) \
{ \
log(L_WARN "[BMP] " msg " (rc = %d)", rc); \
goto label; \
} \
} while (0)
#define IS_NULL(ptr) \
((ptr) == NULL)
#define IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(expr, msg, rv...) \
do { \
if ((expr)) \
{ \
log(L_WARN "[BMP] " msg); \
return rv; \
} \
} while (0)
#define IF_PTR_IS_NULL_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(p, msg, rv...) \
do { \
IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(IS_NULL(p), msg, rv); \
} while (0)
#define IF_BMP_FAILED_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(func, msg, rv...) \
do { \
enum bmp_result rc = func; \
IF_COND_TRUE_PRINT_ERR_MSG_AND_RETURN_OPT_VAL(BMP_FAILED(rc), msg, rv); \
} while (0)
#endif /* _BIRD_BMP_UTILS_H_ */