BGP: Shutdown communication (RFC 8203)
The patch implements BGP Administrative Shutdown Communication (RFC 8203) allowing BGP operators to pass messages related to BGP session administrative shutdown/restart. It handles both transmit and receive of shutdown messages. Messages are logged and may be displayed by show protocol all command. Thanks to Job Snijders for the basic patch.
This commit is contained in:
parent
7b2c5f3d28
commit
cd1d99611e
7 changed files with 149 additions and 54 deletions
|
@ -696,12 +696,12 @@ echo_size:
|
|||
}
|
||||
;
|
||||
|
||||
CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ;
|
||||
CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ;
|
||||
CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ;
|
||||
CF_CLI(DISABLE, proto_patt text_or_none, (<protocol> | \"<pattern>\" | all) [message], [[Disable protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_disable, 1, (uintptr_t) $3); } ;
|
||||
CF_CLI(ENABLE, proto_patt text_or_none, (<protocol> | \"<pattern>\" | all) [message], [[Enable protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_enable, 1, (uintptr_t) $3); } ;
|
||||
CF_CLI(RESTART, proto_patt text_or_none, (<protocol> | \"<pattern>\" | all) [message], [[Restart protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ;
|
||||
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
|
||||
{ proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
|
||||
CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
|
||||
|
|
63
nest/proto.c
63
nest/proto.c
|
@ -610,6 +610,7 @@ proto_rethink_goal(struct proto *p)
|
|||
config_del_obstacle(p->cf->global);
|
||||
rem_node(&p->n);
|
||||
rem_node(&p->glob_node);
|
||||
mb_free(p->message);
|
||||
mb_free(p);
|
||||
if (!nc)
|
||||
return;
|
||||
|
@ -1096,6 +1097,39 @@ proto_schedule_down(struct proto *p, byte restart, byte code)
|
|||
tm_start_max(proto_shutdown_timer, restart ? 2 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* proto_set_message - set administrative message to protocol
|
||||
* @p: protocol
|
||||
* @msg: message
|
||||
* @len: message length (-1 for NULL-terminated string)
|
||||
*
|
||||
* The function sets administrative message (string) related to protocol state
|
||||
* change. It is called by the nest code for manual enable/disable/restart
|
||||
* commands all routes to the protocol, and by protocol-specific code when the
|
||||
* protocol state change is initiated by the protocol. Using NULL message clears
|
||||
* the last message. The message string may be either NULL-terminated or with an
|
||||
* explicit length.
|
||||
*/
|
||||
void
|
||||
proto_set_message(struct proto *p, char *msg, int len)
|
||||
{
|
||||
mb_free(p->message);
|
||||
p->message = NULL;
|
||||
|
||||
if (!msg || !len)
|
||||
return;
|
||||
|
||||
if (len < 0)
|
||||
len = strlen(msg);
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
p->message = mb_alloc(proto_pool, len + 1);
|
||||
memcpy(p->message, msg, len);
|
||||
p->message[len] = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* proto_request_feeding - request feeding routes to the protocol
|
||||
|
@ -1497,7 +1531,7 @@ proto_show_basic_info(struct proto *p)
|
|||
}
|
||||
|
||||
void
|
||||
proto_cmd_show(struct proto *p, uint verbose, int cnt)
|
||||
proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
|
||||
{
|
||||
byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE];
|
||||
|
||||
|
@ -1520,6 +1554,10 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt)
|
|||
{
|
||||
if (p->cf->dsc)
|
||||
cli_msg(-1006, " Description: %s", p->cf->dsc);
|
||||
|
||||
if (p->message)
|
||||
cli_msg(-1006, " Message: %s", p->message);
|
||||
|
||||
if (p->cf->router_id)
|
||||
cli_msg(-1006, " Router ID: %R", p->cf->router_id);
|
||||
|
||||
|
@ -1533,7 +1571,7 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt)
|
|||
}
|
||||
|
||||
void
|
||||
proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED)
|
||||
{
|
||||
if (p->disabled)
|
||||
{
|
||||
|
@ -1544,12 +1582,13 @@ proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
|||
log(L_INFO "Disabling protocol %s", p->name);
|
||||
p->disabled = 1;
|
||||
p->down_code = PDC_CMD_DISABLE;
|
||||
proto_set_message(p, (char *) arg, -1);
|
||||
proto_rethink_goal(p);
|
||||
cli_msg(-9, "%s: disabled", p->name);
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
proto_cmd_enable(struct proto *p, uintptr_t arg, int cnt UNUSED)
|
||||
{
|
||||
if (!p->disabled)
|
||||
{
|
||||
|
@ -1559,12 +1598,13 @@ proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
|||
|
||||
log(L_INFO "Enabling protocol %s", p->name);
|
||||
p->disabled = 0;
|
||||
proto_set_message(p, (char *) arg, -1);
|
||||
proto_rethink_goal(p);
|
||||
cli_msg(-11, "%s: enabled", p->name);
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
||||
proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED)
|
||||
{
|
||||
if (p->disabled)
|
||||
{
|
||||
|
@ -1575,6 +1615,7 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
|||
log(L_INFO "Restarting protocol %s", p->name);
|
||||
p->disabled = 1;
|
||||
p->down_code = PDC_CMD_RESTART;
|
||||
proto_set_message(p, (char *) arg, -1);
|
||||
proto_rethink_goal(p);
|
||||
p->disabled = 0;
|
||||
proto_rethink_goal(p);
|
||||
|
@ -1582,7 +1623,7 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
|
|||
}
|
||||
|
||||
void
|
||||
proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
|
||||
proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED)
|
||||
{
|
||||
if (p->disabled)
|
||||
{
|
||||
|
@ -1624,19 +1665,19 @@ proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
|
|||
}
|
||||
|
||||
void
|
||||
proto_cmd_debug(struct proto *p, uint mask, int cnt UNUSED)
|
||||
proto_cmd_debug(struct proto *p, uintptr_t mask, int cnt UNUSED)
|
||||
{
|
||||
p->debug = mask;
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_mrtdump(struct proto *p, uint mask, int cnt UNUSED)
|
||||
proto_cmd_mrtdump(struct proto *p, uintptr_t mask, int cnt UNUSED)
|
||||
{
|
||||
p->mrtdump = mask;
|
||||
}
|
||||
|
||||
static void
|
||||
proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg)
|
||||
proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
{
|
||||
if (s->class != SYM_PROTO)
|
||||
{
|
||||
|
@ -1649,7 +1690,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int)
|
|||
}
|
||||
|
||||
static void
|
||||
proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg)
|
||||
proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
|
@ -1669,8 +1710,8 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint a
|
|||
}
|
||||
|
||||
void
|
||||
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int),
|
||||
int restricted, uint arg)
|
||||
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int),
|
||||
int restricted, uintptr_t arg)
|
||||
{
|
||||
if (restricted && cli_access_restricted())
|
||||
return;
|
||||
|
|
|
@ -164,6 +164,7 @@ struct proto {
|
|||
u32 hash_key; /* Random key used for hashing of neighbors */
|
||||
bird_clock_t last_state_change; /* Time of last state transition */
|
||||
char *last_state_name_announced; /* Last state name we've announced to the user */
|
||||
char *message; /* State-change message, allocated from proto_pool */
|
||||
struct proto_stats stats; /* Current protocol statistics */
|
||||
|
||||
/*
|
||||
|
@ -250,6 +251,7 @@ struct proto_spec {
|
|||
void *proto_new(struct proto_config *, unsigned size);
|
||||
void *proto_config_new(struct protocol *, int class);
|
||||
void proto_copy_config(struct proto_config *dest, struct proto_config *src);
|
||||
void proto_set_message(struct proto *p, char *msg, int len);
|
||||
void proto_request_feeding(struct proto *p);
|
||||
|
||||
static inline void
|
||||
|
@ -267,15 +269,15 @@ void proto_graceful_restart_unlock(struct proto *p);
|
|||
void proto_show_limit(struct proto_limit *l, const char *dsc);
|
||||
void proto_show_basic_info(struct proto *p);
|
||||
|
||||
void proto_cmd_show(struct proto *, uint, int);
|
||||
void proto_cmd_disable(struct proto *, uint, int);
|
||||
void proto_cmd_enable(struct proto *, uint, int);
|
||||
void proto_cmd_restart(struct proto *, uint, int);
|
||||
void proto_cmd_reload(struct proto *, uint, int);
|
||||
void proto_cmd_debug(struct proto *, uint, int);
|
||||
void proto_cmd_mrtdump(struct proto *, uint, int);
|
||||
void proto_cmd_show(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_disable(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_enable(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_restart(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_reload(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_debug(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_mrtdump(struct proto *, uintptr_t, int);
|
||||
|
||||
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), int restricted, uint arg);
|
||||
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), int restricted, uintptr_t arg);
|
||||
struct proto *proto_get_named(struct symbol *, struct protocol *);
|
||||
|
||||
#define CMD_RELOAD 0
|
||||
|
|
|
@ -146,8 +146,6 @@ struct babel_write_state {
|
|||
#define TLV_HDR(tlv,t,l) ({ tlv->type = t; tlv->length = l - sizeof(struct babel_tlv); })
|
||||
#define TLV_HDR0(tlv,t) TLV_HDR(tlv, t, tlv_data[t].min_length)
|
||||
|
||||
#define BYTES(n) ((((uint) n) + 7) / 8)
|
||||
|
||||
static inline u16
|
||||
get_time16(const void *p)
|
||||
{
|
||||
|
|
|
@ -290,7 +290,7 @@ bgp_update_startup_delay(struct bgp_proto *p)
|
|||
}
|
||||
|
||||
static void
|
||||
bgp_graceful_close_conn(struct bgp_conn *conn, unsigned subcode)
|
||||
bgp_graceful_close_conn(struct bgp_conn *conn, uint subcode, byte *data, uint len)
|
||||
{
|
||||
switch (conn->state)
|
||||
{
|
||||
|
@ -304,7 +304,7 @@ bgp_graceful_close_conn(struct bgp_conn *conn, unsigned subcode)
|
|||
case BS_OPENSENT:
|
||||
case BS_OPENCONFIRM:
|
||||
case BS_ESTABLISHED:
|
||||
bgp_error(conn, 6, subcode, NULL, 0);
|
||||
bgp_error(conn, 6, subcode, data, len);
|
||||
return;
|
||||
default:
|
||||
bug("bgp_graceful_close_conn: Unknown state %d", conn->state);
|
||||
|
@ -340,11 +340,11 @@ bgp_decision(void *vp)
|
|||
}
|
||||
|
||||
void
|
||||
bgp_stop(struct bgp_proto *p, unsigned subcode)
|
||||
bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len)
|
||||
{
|
||||
proto_notify_state(&p->p, PS_STOP);
|
||||
bgp_graceful_close_conn(&p->outgoing_conn, subcode);
|
||||
bgp_graceful_close_conn(&p->incoming_conn, subcode);
|
||||
bgp_graceful_close_conn(&p->outgoing_conn, subcode, data, len);
|
||||
bgp_graceful_close_conn(&p->incoming_conn, subcode, data, len);
|
||||
ev_schedule(p->event);
|
||||
}
|
||||
|
||||
|
@ -420,7 +420,7 @@ bgp_conn_leave_established_state(struct bgp_proto *p)
|
|||
bgp_free_bucket_table(p);
|
||||
|
||||
if (p->p.proto_state == PS_UP)
|
||||
bgp_stop(p, 0);
|
||||
bgp_stop(p, 0, NULL, 0);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -516,7 +516,7 @@ bgp_graceful_restart_timeout(timer *t)
|
|||
struct bgp_proto *p = t->data;
|
||||
|
||||
BGP_TRACE(D_EVENTS, "Neighbor graceful restart timeout");
|
||||
bgp_stop(p, 0);
|
||||
bgp_stop(p, 0, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -973,7 +973,7 @@ bgp_neigh_notify(neighbor *n)
|
|||
BGP_TRACE(D_EVENTS, "Neighbor lost");
|
||||
bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST);
|
||||
/* Perhaps also run bgp_update_startup_delay(p)? */
|
||||
bgp_stop(p, 0);
|
||||
bgp_stop(p, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
else if (p->cf->check_link && !(n->iface->flags & IF_LINK_UP))
|
||||
|
@ -984,7 +984,7 @@ bgp_neigh_notify(neighbor *n)
|
|||
bgp_store_error(p, NULL, BE_MISC, BEM_LINK_DOWN);
|
||||
if (ps == PS_UP)
|
||||
bgp_update_startup_delay(p);
|
||||
bgp_stop(p, 0);
|
||||
bgp_stop(p, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1009,7 +1009,7 @@ bgp_bfd_notify(struct bfd_request *req)
|
|||
bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN);
|
||||
if (ps == PS_UP)
|
||||
bgp_update_startup_delay(p);
|
||||
bgp_stop(p, 0);
|
||||
bgp_stop(p, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1196,7 +1196,11 @@ static int
|
|||
bgp_shutdown(struct proto *P)
|
||||
{
|
||||
struct bgp_proto *p = (struct bgp_proto *) P;
|
||||
unsigned subcode = 0;
|
||||
uint subcode = 0;
|
||||
|
||||
char *message = NULL;
|
||||
byte *data = NULL;
|
||||
uint len = 0;
|
||||
|
||||
BGP_TRACE(D_EVENTS, "Shutdown requested");
|
||||
|
||||
|
@ -1214,10 +1218,12 @@ bgp_shutdown(struct proto *P)
|
|||
case PDC_CMD_DISABLE:
|
||||
case PDC_CMD_SHUTDOWN:
|
||||
subcode = 2; // Errcode 6, 2 - administrative shutdown
|
||||
message = P->message;
|
||||
break;
|
||||
|
||||
case PDC_CMD_RESTART:
|
||||
subcode = 4; // Errcode 6, 4 - administrative reset
|
||||
message = P->message;
|
||||
break;
|
||||
|
||||
case PDC_RX_LIMIT_HIT:
|
||||
|
@ -1242,8 +1248,22 @@ bgp_shutdown(struct proto *P)
|
|||
bgp_store_error(p, NULL, BE_MAN_DOWN, 0);
|
||||
p->startup_delay = 0;
|
||||
|
||||
/* RFC 8203 - shutdown communication */
|
||||
if (message)
|
||||
{
|
||||
uint msg_len = strlen(message);
|
||||
msg_len = MIN(msg_len, 128);
|
||||
|
||||
/* Buffer will be freed automatically by protocol shutdown */
|
||||
data = mb_alloc(p->p.pool, msg_len + 1);
|
||||
len = msg_len + 1;
|
||||
|
||||
data[0] = msg_len;
|
||||
memcpy(data+1, message, msg_len);
|
||||
}
|
||||
|
||||
done:
|
||||
bgp_stop(p, subcode);
|
||||
bgp_stop(p, subcode, data, len);
|
||||
return p->p.proto_state;
|
||||
}
|
||||
|
||||
|
@ -1433,7 +1453,7 @@ bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int l
|
|||
if (code != 6)
|
||||
{
|
||||
bgp_update_startup_delay(p);
|
||||
bgp_stop(p, 0);
|
||||
bgp_stop(p, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ void bgp_graceful_restart_done(struct bgp_proto *p);
|
|||
void bgp_refresh_begin(struct bgp_proto *p);
|
||||
void bgp_refresh_end(struct bgp_proto *p);
|
||||
void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code);
|
||||
void bgp_stop(struct bgp_proto *p, unsigned subcode);
|
||||
void bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len);
|
||||
|
||||
struct rte_source *bgp_find_source(struct bgp_proto *p, u32 path_id);
|
||||
struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
|
||||
|
|
|
@ -1494,38 +1494,72 @@ bgp_error_dsc(unsigned code, unsigned subcode)
|
|||
return buff;
|
||||
}
|
||||
|
||||
/* RFC 8203 - shutdown communication message */
|
||||
static int
|
||||
bgp_handle_message(struct bgp_proto *p, byte *data, uint len, byte **bp)
|
||||
{
|
||||
byte *msg = data + 1;
|
||||
uint msg_len = data[0];
|
||||
uint i;
|
||||
|
||||
/* Handle zero length message */
|
||||
if (msg_len == 0)
|
||||
return 1;
|
||||
|
||||
/* Handle proper message */
|
||||
if ((msg_len > 128) && (msg_len + 1 > len))
|
||||
return 0;
|
||||
|
||||
/* Some elementary cleanup */
|
||||
for (i = 0; i < msg_len; i++)
|
||||
if (msg[i] < ' ')
|
||||
msg[i] = ' ';
|
||||
|
||||
proto_set_message(&p->p, msg, msg_len);
|
||||
*bp += bsprintf(*bp, ": \"%s\"", p->p.message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len)
|
||||
{
|
||||
const byte *name;
|
||||
byte *t, argbuf[36];
|
||||
byte argbuf[256], *t = argbuf;
|
||||
unsigned i;
|
||||
|
||||
/* Don't report Cease messages generated by myself */
|
||||
if (code == 6 && class == BE_BGP_TX)
|
||||
return;
|
||||
|
||||
name = bgp_error_dsc(code, subcode);
|
||||
t = argbuf;
|
||||
/* Reset shutdown message */
|
||||
if ((code == 6) && ((subcode == 2) || (subcode == 4)))
|
||||
proto_set_message(&p->p, NULL, 0);
|
||||
|
||||
if (len)
|
||||
{
|
||||
*t++ = ':';
|
||||
*t++ = ' ';
|
||||
|
||||
/* Bad peer AS - we would like to print the AS */
|
||||
if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4)))
|
||||
{
|
||||
/* Bad peer AS - we would like to print the AS */
|
||||
t += bsprintf(t, "%d", (len == 2) ? get_u16(data) : get_u32(data));
|
||||
t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* RFC 8203 - shutdown communication */
|
||||
if (((code == 6) && ((subcode == 2) || (subcode == 4))))
|
||||
if (bgp_handle_message(p, data, len, &t))
|
||||
goto done;
|
||||
|
||||
*t++ = ':';
|
||||
*t++ = ' ';
|
||||
if (len > 16)
|
||||
len = 16;
|
||||
for (i=0; i<len; i++)
|
||||
t += bsprintf(t, "%02x", data[i]);
|
||||
}
|
||||
|
||||
done:
|
||||
*t = 0;
|
||||
log(L_REMOTE "%s: %s: %s%s", p->p.name, msg, name, argbuf);
|
||||
const byte *dsc = bgp_error_dsc(code, subcode);
|
||||
log(L_REMOTE "%s: %s: %s%s", p->p.name, msg, dsc, argbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1571,7 +1605,7 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, uint len)
|
|||
if (err)
|
||||
{
|
||||
bgp_update_startup_delay(p);
|
||||
bgp_stop(p, 0);
|
||||
bgp_stop(p, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue