summaryrefslogtreecommitdiff
path: root/proto/bgp/packets.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp/packets.c')
-rw-r--r--proto/bgp/packets.c352
1 files changed, 350 insertions, 2 deletions
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