summaryrefslogtreecommitdiff
path: root/bgpd/bgp_packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_packet.c')
-rw-r--r--bgpd/bgp_packet.c167
1 files changed, 132 insertions, 35 deletions
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index b0d852ee63..fc4c0b5154 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -713,6 +713,72 @@ static void bgp_write_notify(struct peer *peer)
}
/*
+ * Encapsulate an original BGP CEASE Notification into Hard Reset
+ */
+static uint8_t *bgp_notify_encapsulate_hard_reset(uint8_t code, uint8_t subcode,
+ uint8_t *data, size_t datalen)
+{
+ uint8_t *message = XCALLOC(MTYPE_BGP_NOTIFICATION, datalen + 2);
+
+ /* ErrCode */
+ message[0] = code;
+ /* Subcode */
+ message[1] = subcode;
+ /* Data */
+ if (datalen)
+ memcpy(message + 2, data, datalen);
+
+ return message;
+}
+
+/*
+ * Decapsulate an original BGP CEASE Notification from Hard Reset
+ */
+struct bgp_notify bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify)
+{
+ struct bgp_notify bn = {};
+
+ bn.code = notify->raw_data[0];
+ bn.subcode = notify->raw_data[1];
+ bn.length = notify->length - 2;
+
+ bn.raw_data = XCALLOC(MTYPE_BGP_NOTIFICATION, bn.length);
+ memcpy(bn.raw_data, notify->raw_data + 2, bn.length);
+
+ return bn;
+}
+
+/*
+ * Check if to send BGP CEASE Notification/Hard Reset?
+ */
+bool bgp_notify_is_hard_reset(struct peer *peer, uint8_t code, uint8_t subcode)
+{
+ /* When the "N" bit has been exchanged, a Hard Reset message is used to
+ * indicate to the peer that the session is to be fully terminated.
+ */
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV) ||
+ !CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV))
+ return false;
+
+ /*
+ * https://datatracker.ietf.org/doc/html/rfc8538#section-5.1
+ */
+ if (code == BGP_NOTIFY_CEASE || code == BGP_NOTIFY_HOLD_ERR) {
+ switch (subcode) {
+ case BGP_NOTIFY_CEASE_MAX_PREFIX:
+ case BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN:
+ case BGP_NOTIFY_CEASE_PEER_UNCONFIG:
+ case BGP_NOTIFY_CEASE_HARD_RESET:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+/*
* Creates a BGP Notify and appends it to the peer's output queue.
*
* This function attempts to write the packet from the thread it is called
@@ -736,6 +802,7 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
uint8_t sub_code, uint8_t *data, size_t datalen)
{
struct stream *s;
+ bool hard_reset = bgp_notify_is_hard_reset(peer, code, sub_code);
/* Lock I/O mutex to prevent other threads from pushing packets */
frr_mutex_lock_autounlock(&peer->io_mtx);
@@ -747,13 +814,25 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
/* Make notify packet. */
bgp_packet_set_marker(s, BGP_MSG_NOTIFY);
- /* Set notify packet values. */
- stream_putc(s, code); /* BGP notify code */
- stream_putc(s, sub_code); /* BGP notify sub_code */
+ /* Check if we should send Hard Reset Notification or not */
+ if (hard_reset) {
+ uint8_t *hard_reset_message = bgp_notify_encapsulate_hard_reset(
+ code, sub_code, data, datalen);
- /* If notify data is present. */
- if (data)
- stream_write(s, data, datalen);
+ /* Hard Reset encapsulates another NOTIFICATION message
+ * in its data portion.
+ */
+ stream_putc(s, BGP_NOTIFY_CEASE);
+ stream_putc(s, BGP_NOTIFY_CEASE_HARD_RESET);
+ stream_write(s, hard_reset_message, datalen + 2);
+
+ XFREE(MTYPE_BGP_NOTIFICATION, hard_reset_message);
+ } else {
+ stream_putc(s, code);
+ stream_putc(s, sub_code);
+ if (data)
+ stream_write(s, data, datalen);
+ }
/* Set BGP packet length. */
bgp_packet_set_size(s);
@@ -808,7 +887,7 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
bgp_notify.length);
}
}
- bgp_notify_print(peer, &bgp_notify, "sending");
+ bgp_notify_print(peer, &bgp_notify, "sending", hard_reset);
if (bgp_notify.data) {
XFREE(MTYPE_TMP, bgp_notify.data);
@@ -1894,27 +1973,42 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)
*/
static int bgp_notify_receive(struct peer *peer, bgp_size_t size)
{
- struct bgp_notify bgp_notify;
+ struct bgp_notify outer;
+ struct bgp_notify inner;
+ bool hard_reset = false;
if (peer->notify.data) {
- XFREE(MTYPE_TMP, peer->notify.data);
+ XFREE(MTYPE_BGP_NOTIFICATION, peer->notify.data);
peer->notify.length = 0;
}
- bgp_notify.code = stream_getc(peer->curr);
- bgp_notify.subcode = stream_getc(peer->curr);
- bgp_notify.length = size - 2;
- bgp_notify.data = NULL;
- bgp_notify.raw_data = NULL;
+ outer.code = stream_getc(peer->curr);
+ outer.subcode = stream_getc(peer->curr);
+ outer.length = size - 2;
+ outer.data = NULL;
+ outer.raw_data = NULL;
+ if (outer.length) {
+ outer.raw_data = XMALLOC(MTYPE_BGP_NOTIFICATION, outer.length);
+ memcpy(outer.raw_data, stream_pnt(peer->curr), outer.length);
+ }
+
+ hard_reset = bgp_notify_is_hard_reset(peer, outer.code, outer.subcode);
+ if (hard_reset && outer.length) {
+ inner = bgp_notify_decapsulate_hard_reset(&outer);
+ peer->notify.hard_reset = true;
+ } else {
+ inner = outer;
+ }
/* Preserv notify code and sub code. */
- peer->notify.code = bgp_notify.code;
- peer->notify.subcode = bgp_notify.subcode;
+ peer->notify.code = inner.code;
+ peer->notify.subcode = inner.subcode;
/* For further diagnostic record returned Data. */
- if (bgp_notify.length) {
- peer->notify.length = size - 2;
- peer->notify.data = XMALLOC(MTYPE_TMP, size - 2);
- memcpy(peer->notify.data, stream_pnt(peer->curr), size - 2);
+ if (inner.length) {
+ peer->notify.length = inner.length;
+ peer->notify.data =
+ XMALLOC(MTYPE_BGP_NOTIFICATION, inner.length);
+ memcpy(peer->notify.data, inner.raw_data, inner.length);
}
/* For debug */
@@ -1923,32 +2017,35 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size)
int first = 0;
char c[4];
- if (bgp_notify.length) {
- bgp_notify.data =
- XMALLOC(MTYPE_TMP, bgp_notify.length * 3);
- for (i = 0; i < bgp_notify.length; i++)
+ if (inner.length) {
+ inner.data = XMALLOC(MTYPE_BGP_NOTIFICATION,
+ inner.length * 3);
+ for (i = 0; i < inner.length; i++)
if (first) {
snprintf(c, sizeof(c), " %02x",
stream_getc(peer->curr));
- strlcat(bgp_notify.data, c,
- bgp_notify.length * 3);
+ strlcat(inner.data, c,
+ inner.length * 3);
} else {
first = 1;
snprintf(c, sizeof(c), "%02x",
stream_getc(peer->curr));
- strlcpy(bgp_notify.data, c,
- bgp_notify.length * 3);
+ strlcpy(inner.data, c,
+ inner.length * 3);
}
- bgp_notify.raw_data = (uint8_t *)peer->notify.data;
}
- bgp_notify_print(peer, &bgp_notify, "received");
- if (bgp_notify.data) {
- XFREE(MTYPE_TMP, bgp_notify.data);
- bgp_notify.length = 0;
+ bgp_notify_print(peer, &inner, "received", hard_reset);
+ if (inner.data) {
+ XFREE(MTYPE_BGP_NOTIFICATION, inner.data);
+ inner.length = 0;
+ }
+ if (outer.length) {
+ XFREE(MTYPE_BGP_NOTIFICATION, outer.data);
+ outer.length = 0;
}
}
@@ -1961,8 +2058,8 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size)
in that case we fallback to open without the capability option.
But this done in bgp_stop. We just mark it here to avoid changing
the fsm tables. */
- if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR
- && bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM)
+ if (inner.code == BGP_NOTIFY_OPEN_ERR &&
+ inner.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM)
UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
bgp_peer_gr_flags_update(peer);