Also, add N-Bit (Notification) flag for Graceful Restart.
This is a preparation for RFC8538.
More information: https://datatracker.ietf.org/doc/html/rfc8538
Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_packet.h"
unsigned long conf_bgp_debug_as4;
unsigned long conf_bgp_debug_neighbor_events;
{BGP_NOTIFY_CEASE_COLLISION_RESOLUTION,
"/Connection Collision Resolution"},
{BGP_NOTIFY_CEASE_OUT_OF_RESOURCE, "/Out of Resources"},
+ {BGP_NOTIFY_CEASE_HARD_RESET, "/Hard Reset"},
{0}};
static const struct message bgp_notify_route_refresh_msg[] = {
/* dump notify packet */
void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
- const char *direct)
+ const char *direct, bool hard_reset)
{
const char *subcode_str;
const char *code_str;
if (msg_str) {
zlog_info(
- "%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) \"%s\"",
+ "%%NOTIFICATION%s: %s neighbor %s %d/%d (%s%s) \"%s\"",
+ hard_reset ? "(Hard Reset)" : "",
strcmp(direct, "received") == 0
? "received from"
: "sent to",
} else {
msg_str = bgp_notify->data ? bgp_notify->data : "";
zlog_info(
- "%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) %d bytes %s",
+ "%%NOTIFICATION%s: %s neighbor %s %d/%d (%s%s) %d bytes %s",
+ hard_reset ? "(Hard Reset)" : "",
strcmp(direct, "received") == 0
? "received from"
: "sent to",
extern bool bgp_debug_peer_updout_enabled(char *host);
extern const char *bgp_notify_code_str(char);
extern const char *bgp_notify_subcode_str(char, char);
-extern void bgp_notify_print(struct peer *, struct bgp_notify *, const char *);
+extern void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
+ const char *direct, bool hard_reset);
extern const struct message bgp_status_msg[];
extern int bgp_debug_neighbor_events(struct peer *peer);
} else {
/* Peer sends R-bit. In this case, we need to send
* ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE to Zebra. */
- if (CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV)) {
+ if (CHECK_FLAG(peer->cap,
+ PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV)) {
FOREACH_AFI_SAFI (afi, safi)
/* Send route processing complete
message to RIB */
DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id");
DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function");
DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry");
+
+DEFINE_MTYPE(BGPD, BGP_NOTIFICATION, "BGP Notification Message");
DECLARE_MTYPE(EVPN_REMOTE_IP);
+DECLARE_MTYPE(BGP_NOTIFICATION);
+
#endif /* _QUAGGA_BGP_MEMORY_H */
SET_FLAG(peer->cap, PEER_CAP_RESTART_RCV);
restart_flag_time = stream_getw(s);
+
+ /* The most significant bit is defined in [RFC4724] as
+ * the Restart State ("R") bit.
+ */
if (CHECK_FLAG(restart_flag_time, GRACEFUL_RESTART_R_BIT))
SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV);
else
UNSET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV);
+ /* The second most significant bit is defined in this
+ * document as the Graceful Notification ("N") bit.
+ */
+ if (CHECK_FLAG(restart_flag_time, GRACEFUL_RESTART_N_BIT))
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV);
+ else
+ UNSET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV);
+
UNSET_FLAG(restart_flag_time, 0xF000);
peer->v_gr_restart = restart_flag_time;
if (bgp_debug_neighbor_events(peer)) {
- zlog_debug("%s Peer has%srestarted. Restart Time : %d",
- peer->host,
- CHECK_FLAG(peer->cap,
- PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV)
- ? " "
- : " not ",
- peer->v_gr_restart);
+ zlog_debug(
+ "%s Peer has%srestarted. Restart Time: %d, N-bit set: %s",
+ peer->host,
+ CHECK_FLAG(peer->cap,
+ PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV)
+ ? " "
+ : " not ",
+ peer->v_gr_restart,
+ CHECK_FLAG(peer->cap,
+ PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV)
+ ? "yes"
+ : "no");
}
while (stream_get_getp(s) + 4 <= end) {
restart_time = peer->bgp->restart_time;
if (peer->bgp->t_startup) {
SET_FLAG(restart_time, GRACEFUL_RESTART_R_BIT);
+ SET_FLAG(restart_time, GRACEFUL_RESTART_N_BIT);
SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV);
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV);
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
- zlog_debug("[BGP_GR] Sending R-Bit for Peer :%s :",
+ zlog_debug("[BGP_GR] Sending R-Bit/N-Bit for peer: %s",
peer->host);
}
/* Graceful Restart */
#define GRACEFUL_RESTART_R_BIT 0x8000
+#define GRACEFUL_RESTART_N_BIT 0x4000
#define GRACEFUL_RESTART_F_BIT 0x80
/* Long-lived Graceful Restart */
stream_free(s);
}
+/*
+ * 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.
*
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);
/* 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);
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);
*/
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 */
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;
}
}
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);
/* Task callback to handle socket error encountered in the io pthread */
void bgp_packet_process_error(struct thread *thread);
+extern struct bgp_notify
+bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify);
+extern bool bgp_notify_is_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode);
#endif /* _QUAGGA_BGP_PACKET_H */
json_object_string_add(json_peer,
"lastNotificationReason",
errorcodesubcode_str);
+ json_object_boolean_add(json_peer,
+ "lastNotificationHardReset",
+ peer->notify.hard_reset);
if (peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED
&& peer->notify.code == BGP_NOTIFY_CEASE
&& (peer->notify.subcode
subcode_str =
bgp_notify_subcode_str(peer->notify.code,
peer->notify.subcode);
- vty_out(vty, " Notification %s (%s%s)\n",
+ vty_out(vty, " Notification %s (%s%s%s)\n",
peer->last_reset == PEER_DOWN_NOTIFY_SEND
- ? "sent"
- : "received",
- code_str, subcode_str);
+ ? "sent"
+ : "received",
+ code_str, subcode_str,
+ peer->notify.hard_reset
+ ? bgp_notify_subcode_str(
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_HARD_RESET)
+ : "");
} else {
vty_out(vty, " %s\n",
peer_down_str[(int)peer->last_reset]);
}
}
-static void bgp_show_neighnor_graceful_restart_rbit(struct vty *vty,
- struct peer *p,
- bool use_json,
- json_object *json)
+static void bgp_show_neighnor_graceful_restart_flags(struct vty *vty,
+ struct peer *p,
+ bool use_json,
+ json_object *json)
{
- bool rbit_status = false;
-
- if (!use_json)
- vty_out(vty, "\n R bit: ");
+ bool rbit = false;
+ bool nbit = false;
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)
&& (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV))
&& (peer_established(p))) {
-
- if (CHECK_FLAG(p->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV))
- rbit_status = true;
- else
- rbit_status = false;
+ rbit = CHECK_FLAG(p->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV);
+ nbit = CHECK_FLAG(p->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV);
}
- if (rbit_status) {
- if (use_json)
- json_object_boolean_true_add(json, "rBit");
- else
- vty_out(vty, "True\n");
+ if (use_json) {
+ json_object_boolean_add(json, "rBit", rbit);
+ json_object_boolean_add(json, "nBit", nbit);
} else {
- if (use_json)
- json_object_boolean_false_add(json, "rBit");
- else
- vty_out(vty, "False\n");
+ vty_out(vty, "\n R bit: %s", rbit ? "True" : "False");
+ vty_out(vty, "\n N bit: %s\n", nbit ? "True" : "False");
}
}
"V AS LocalAS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc\n"
#define BGP_SHOW_SUMMARY_HEADER_FAILED "EstdCnt DropCnt ResetTime Reason\n"
-#define BGP_SHOW_PEER_GR_CAPABILITY( \
- vty, p, use_json, json) \
- do { \
- bgp_show_neighbor_graceful_restart_local_mode( \
- vty, p, use_json, json); \
- bgp_show_neighbor_graceful_restart_remote_mode( \
- vty, p, use_json, json); \
- bgp_show_neighnor_graceful_restart_rbit( \
- vty, p, use_json, json); \
- bgp_show_neighbor_graceful_restart_time( \
- vty, p, use_json, json); \
- bgp_show_neighbor_graceful_restart_capability_per_afi_safi(\
- vty, p, use_json, json); \
+#define BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, json) \
+ do { \
+ bgp_show_neighbor_graceful_restart_local_mode(vty, p, \
+ use_json, json); \
+ bgp_show_neighbor_graceful_restart_remote_mode( \
+ vty, p, use_json, json); \
+ bgp_show_neighnor_graceful_restart_flags(vty, p, use_json, \
+ json); \
+ bgp_show_neighbor_graceful_restart_time(vty, p, use_json, \
+ json); \
+ bgp_show_neighbor_graceful_restart_capability_per_afi_safi( \
+ vty, p, use_json, json); \
} while (0)
#define VTY_BGP_GR_DEFINE_LOOP_VARIABLE \
XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
- XFREE(MTYPE_TMP, peer->notify.data);
+ XFREE(MTYPE_BGP_NOTIFICATION, peer->notify.data);
memset(&peer->notify, 0, sizeof(struct bgp_notify));
if (peer->clear_node_queue)
char *data;
bgp_size_t length;
uint8_t *raw_data;
+ bool hard_reset;
};
/* Next hop self address. */
#define PEER_CAP_EXTENDED_MESSAGE_RCV (1U << 20)
#define PEER_CAP_LLGR_ADV (1U << 21)
#define PEER_CAP_LLGR_RCV (1U << 22)
+/* sent graceful-restart notification (N) bit */
+#define PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV (1U << 23)
+/* received graceful-restart notification (N) bit */
+#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24)
/* Capability flags (reset in bgp_stop) */
uint32_t af_cap[AFI_MAX][SAFI_MAX];
#define BGP_NOTIFY_CEASE_CONFIG_CHANGE 6
#define BGP_NOTIFY_CEASE_COLLISION_RESOLUTION 7
#define BGP_NOTIFY_CEASE_OUT_OF_RESOURCE 8
+#define BGP_NOTIFY_CEASE_HARD_RESET 9
/* BGP_NOTIFY_ROUTE_REFRESH_ERR sub codes (RFC 7313). */
#define BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN 1