bgpd: Implement CEASE/Hard Reset notification

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>
This commit is contained in:
Donatas Abraitis 2022-04-30 23:04:58 +03:00
parent 54394daa2d
commit eea685b6d3
13 changed files with 223 additions and 88 deletions

View File

@ -49,6 +49,7 @@
#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;
@ -168,6 +169,7 @@ static const struct message bgp_notify_cease_msg[] = {
{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[] = {
@ -520,7 +522,7 @@ const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data,
/* 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;
@ -544,7 +546,8 @@ void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
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",
@ -554,7 +557,8 @@ void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
} 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",

View File

@ -170,7 +170,8 @@ extern bool bgp_dump_attr(struct attr *, char *, size_t);
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);

View File

@ -2160,7 +2160,8 @@ static int bgp_establish(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 */

View File

@ -145,3 +145,5 @@ DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service");
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");

View File

@ -144,4 +144,6 @@ DECLARE_MTYPE(BGP_SRV6_FUNCTION);
DECLARE_MTYPE(EVPN_REMOTE_IP);
DECLARE_MTYPE(BGP_NOTIFICATION);
#endif /* _QUAGGA_BGP_MEMORY_H */

View File

@ -517,22 +517,39 @@ static int bgp_capability_restart(struct peer *peer,
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) {
@ -1418,10 +1435,12 @@ static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer,
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);
}

View File

@ -88,6 +88,7 @@ struct graceful_restart_af {
/* Graceful Restart */
#define GRACEFUL_RESTART_R_BIT 0x8000
#define GRACEFUL_RESTART_N_BIT 0x4000
#define GRACEFUL_RESTART_F_BIT 0x80
/* Long-lived Graceful Restart */

View File

@ -712,6 +712,72 @@ static void bgp_write_notify(struct peer *peer)
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.
*
@ -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);

View File

@ -86,5 +86,9 @@ extern void bgp_send_delayed_eor(struct bgp *bgp);
/* 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 */

View File

@ -10052,6 +10052,9 @@ static void bgp_show_peer_reset(struct vty * vty, struct peer *peer,
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
@ -10085,11 +10088,16 @@ static void bgp_show_peer_reset(struct vty * vty, struct peer *peer,
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]);
@ -11246,36 +11254,27 @@ static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p,
}
}
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");
}
}

View File

@ -59,19 +59,18 @@ struct bgp;
"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 \

View File

@ -1153,7 +1153,7 @@ static void peer_free(struct peer *peer)
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)

View File

@ -819,6 +819,7 @@ struct bgp_notify {
char *data;
bgp_size_t length;
uint8_t *raw_data;
bool hard_reset;
};
/* Next hop self address. */
@ -1192,6 +1193,10 @@ struct peer {
#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];
@ -1852,6 +1857,7 @@ struct bgp_nlri {
#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