diff options
author | Pawel Maslanka <pmaslank@akamai.com> | 2021-03-29 22:45:21 +0200 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2023-04-16 20:05:15 +0200 |
commit | a848dad40aa618e5e24417e4ef46b62c860de679 (patch) | |
tree | f14426246ec7f6ef89b0c12460f4979f6b2a047e /proto/bgp | |
parent | 9e44ace3928a19560058dc713fcbff3a8bad3b3c (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.c | 7 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 11 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 14 | ||||
-rw-r--r-- | proto/bgp/packets.c | 352 |
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 |