summaryrefslogtreecommitdiff
path: root/proto/bgp
diff options
context:
space:
mode:
authorPawel Maslanka <pmaslank@akamai.com>2021-03-29 22:45:21 +0200
committerOndrej Zajicek <santiago@crfreenet.org>2023-04-16 20:05:15 +0200
commita848dad40aa618e5e24417e4ef46b62c860de679 (patch)
treef14426246ec7f6ef89b0c12460f4979f6b2a047e /proto/bgp
parent9e44ace3928a19560058dc713fcbff3a8bad3b3c (diff)
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.
Diffstat (limited to 'proto/bgp')
-rw-r--r--proto/bgp/attrs.c7
-rw-r--r--proto/bgp/bgp.c11
-rw-r--r--proto/bgp/bgp.h14
-rw-r--r--proto/bgp/packets.c352
4 files changed, 382 insertions, 2 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 204151c3..de45cae0 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -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
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 9408715e..709625ea 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -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;
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 302f58e7..c4f4f3be 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -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 */
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 5c17c370..bec8cd91 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -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)
{
@@ -2385,6 +2606,122 @@ bgp_create_mp_unreach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *
}
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)
{
struct bgp_proto *p = (void *) c->c.proto;
@@ -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