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:
Ondrej Zajicek (work) 2017-09-19 19:55:37 +02:00
parent 7b2c5f3d28
commit cd1d99611e
7 changed files with 149 additions and 54 deletions

View file

@ -696,12 +696,12 @@ echo_size:
} }
; ;
CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]]) CF_CLI(DISABLE, proto_patt text_or_none, (<protocol> | \"<pattern>\" | all) [message], [[Disable protocol]])
{ proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ; { proto_apply_cmd($2, proto_cmd_disable, 1, (uintptr_t) $3); } ;
CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]]) CF_CLI(ENABLE, proto_patt text_or_none, (<protocol> | \"<pattern>\" | all) [message], [[Enable protocol]])
{ proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ; { proto_apply_cmd($2, proto_cmd_enable, 1, (uintptr_t) $3); } ;
CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]]) CF_CLI(RESTART, proto_patt text_or_none, (<protocol> | \"<pattern>\" | all) [message], [[Restart protocol]])
{ proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ; { proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ;
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]]) CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
{ proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ; { proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]]) CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])

View file

@ -610,6 +610,7 @@ proto_rethink_goal(struct proto *p)
config_del_obstacle(p->cf->global); config_del_obstacle(p->cf->global);
rem_node(&p->n); rem_node(&p->n);
rem_node(&p->glob_node); rem_node(&p->glob_node);
mb_free(p->message);
mb_free(p); mb_free(p);
if (!nc) if (!nc)
return; return;
@ -1096,6 +1097,39 @@ proto_schedule_down(struct proto *p, byte restart, byte code)
tm_start_max(proto_shutdown_timer, restart ? 2 : 0); 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 * proto_request_feeding - request feeding routes to the protocol
@ -1497,7 +1531,7 @@ proto_show_basic_info(struct proto *p)
} }
void 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]; 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) if (p->cf->dsc)
cli_msg(-1006, " Description: %s", 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) if (p->cf->router_id)
cli_msg(-1006, " Router ID: %R", 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 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) 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); log(L_INFO "Disabling protocol %s", p->name);
p->disabled = 1; p->disabled = 1;
p->down_code = PDC_CMD_DISABLE; p->down_code = PDC_CMD_DISABLE;
proto_set_message(p, (char *) arg, -1);
proto_rethink_goal(p); proto_rethink_goal(p);
cli_msg(-9, "%s: disabled", p->name); cli_msg(-9, "%s: disabled", p->name);
} }
void 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) 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); log(L_INFO "Enabling protocol %s", p->name);
p->disabled = 0; p->disabled = 0;
proto_set_message(p, (char *) arg, -1);
proto_rethink_goal(p); proto_rethink_goal(p);
cli_msg(-11, "%s: enabled", p->name); cli_msg(-11, "%s: enabled", p->name);
} }
void 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) 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); log(L_INFO "Restarting protocol %s", p->name);
p->disabled = 1; p->disabled = 1;
p->down_code = PDC_CMD_RESTART; p->down_code = PDC_CMD_RESTART;
proto_set_message(p, (char *) arg, -1);
proto_rethink_goal(p); proto_rethink_goal(p);
p->disabled = 0; p->disabled = 0;
proto_rethink_goal(p); proto_rethink_goal(p);
@ -1582,7 +1623,7 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED)
} }
void 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) if (p->disabled)
{ {
@ -1624,19 +1665,19 @@ proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED)
} }
void 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; p->debug = mask;
} }
void 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; p->mrtdump = mask;
} }
static void 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) if (s->class != SYM_PROTO)
{ {
@ -1649,7 +1690,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int)
} }
static void 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; int cnt = 0;
@ -1669,8 +1710,8 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint a
} }
void void
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int),
int restricted, uint arg) int restricted, uintptr_t arg)
{ {
if (restricted && cli_access_restricted()) if (restricted && cli_access_restricted())
return; return;

View file

@ -164,6 +164,7 @@ struct proto {
u32 hash_key; /* Random key used for hashing of neighbors */ u32 hash_key; /* Random key used for hashing of neighbors */
bird_clock_t last_state_change; /* Time of last state transition */ 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 *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 */ struct proto_stats stats; /* Current protocol statistics */
/* /*
@ -250,6 +251,7 @@ struct proto_spec {
void *proto_new(struct proto_config *, unsigned size); void *proto_new(struct proto_config *, unsigned size);
void *proto_config_new(struct protocol *, int class); void *proto_config_new(struct protocol *, int class);
void proto_copy_config(struct proto_config *dest, struct proto_config *src); 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); void proto_request_feeding(struct proto *p);
static inline void 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_limit(struct proto_limit *l, const char *dsc);
void proto_show_basic_info(struct proto *p); void proto_show_basic_info(struct proto *p);
void proto_cmd_show(struct proto *, uint, int); void proto_cmd_show(struct proto *, uintptr_t, int);
void proto_cmd_disable(struct proto *, uint, int); void proto_cmd_disable(struct proto *, uintptr_t, int);
void proto_cmd_enable(struct proto *, uint, int); void proto_cmd_enable(struct proto *, uintptr_t, int);
void proto_cmd_restart(struct proto *, uint, int); void proto_cmd_restart(struct proto *, uintptr_t, int);
void proto_cmd_reload(struct proto *, uint, int); void proto_cmd_reload(struct proto *, uintptr_t, int);
void proto_cmd_debug(struct proto *, uint, int); void proto_cmd_debug(struct proto *, uintptr_t, int);
void proto_cmd_mrtdump(struct proto *, uint, 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 *); struct proto *proto_get_named(struct symbol *, struct protocol *);
#define CMD_RELOAD 0 #define CMD_RELOAD 0

View file

@ -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_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 TLV_HDR0(tlv,t) TLV_HDR(tlv, t, tlv_data[t].min_length)
#define BYTES(n) ((((uint) n) + 7) / 8)
static inline u16 static inline u16
get_time16(const void *p) get_time16(const void *p)
{ {

View file

@ -290,7 +290,7 @@ bgp_update_startup_delay(struct bgp_proto *p)
} }
static void 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) switch (conn->state)
{ {
@ -304,7 +304,7 @@ bgp_graceful_close_conn(struct bgp_conn *conn, unsigned subcode)
case BS_OPENSENT: case BS_OPENSENT:
case BS_OPENCONFIRM: case BS_OPENCONFIRM:
case BS_ESTABLISHED: case BS_ESTABLISHED:
bgp_error(conn, 6, subcode, NULL, 0); bgp_error(conn, 6, subcode, data, len);
return; return;
default: default:
bug("bgp_graceful_close_conn: Unknown state %d", conn->state); bug("bgp_graceful_close_conn: Unknown state %d", conn->state);
@ -340,11 +340,11 @@ bgp_decision(void *vp)
} }
void 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); proto_notify_state(&p->p, PS_STOP);
bgp_graceful_close_conn(&p->outgoing_conn, subcode); bgp_graceful_close_conn(&p->outgoing_conn, subcode, data, len);
bgp_graceful_close_conn(&p->incoming_conn, subcode); bgp_graceful_close_conn(&p->incoming_conn, subcode, data, len);
ev_schedule(p->event); ev_schedule(p->event);
} }
@ -420,7 +420,7 @@ bgp_conn_leave_established_state(struct bgp_proto *p)
bgp_free_bucket_table(p); bgp_free_bucket_table(p);
if (p->p.proto_state == PS_UP) if (p->p.proto_state == PS_UP)
bgp_stop(p, 0); bgp_stop(p, 0, NULL, 0);
} }
void void
@ -516,7 +516,7 @@ bgp_graceful_restart_timeout(timer *t)
struct bgp_proto *p = t->data; struct bgp_proto *p = t->data;
BGP_TRACE(D_EVENTS, "Neighbor graceful restart timeout"); 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_TRACE(D_EVENTS, "Neighbor lost");
bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST); bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST);
/* Perhaps also run bgp_update_startup_delay(p)? */ /* 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)) 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); bgp_store_error(p, NULL, BE_MISC, BEM_LINK_DOWN);
if (ps == PS_UP) if (ps == PS_UP)
bgp_update_startup_delay(p); bgp_update_startup_delay(p);
bgp_stop(p, 0); bgp_stop(p, 0, NULL, 0);
} }
} }
else else
@ -1009,7 +1009,7 @@ bgp_bfd_notify(struct bfd_request *req)
bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN); bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN);
if (ps == PS_UP) if (ps == PS_UP)
bgp_update_startup_delay(p); 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) bgp_shutdown(struct proto *P)
{ {
struct bgp_proto *p = (struct bgp_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"); BGP_TRACE(D_EVENTS, "Shutdown requested");
@ -1214,10 +1218,12 @@ bgp_shutdown(struct proto *P)
case PDC_CMD_DISABLE: case PDC_CMD_DISABLE:
case PDC_CMD_SHUTDOWN: case PDC_CMD_SHUTDOWN:
subcode = 2; // Errcode 6, 2 - administrative shutdown subcode = 2; // Errcode 6, 2 - administrative shutdown
message = P->message;
break; break;
case PDC_CMD_RESTART: case PDC_CMD_RESTART:
subcode = 4; // Errcode 6, 4 - administrative reset subcode = 4; // Errcode 6, 4 - administrative reset
message = P->message;
break; break;
case PDC_RX_LIMIT_HIT: case PDC_RX_LIMIT_HIT:
@ -1242,8 +1248,22 @@ bgp_shutdown(struct proto *P)
bgp_store_error(p, NULL, BE_MAN_DOWN, 0); bgp_store_error(p, NULL, BE_MAN_DOWN, 0);
p->startup_delay = 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: done:
bgp_stop(p, subcode); bgp_stop(p, subcode, data, len);
return p->p.proto_state; 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) if (code != 6)
{ {
bgp_update_startup_delay(p); bgp_update_startup_delay(p);
bgp_stop(p, 0); bgp_stop(p, 0, NULL, 0);
} }
} }

View file

@ -212,7 +212,7 @@ void bgp_graceful_restart_done(struct bgp_proto *p);
void bgp_refresh_begin(struct bgp_proto *p); void bgp_refresh_begin(struct bgp_proto *p);
void bgp_refresh_end(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_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_find_source(struct bgp_proto *p, u32 path_id);
struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id); struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);

View file

@ -1494,38 +1494,72 @@ bgp_error_dsc(unsigned code, unsigned subcode)
return buff; 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 void
bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len) bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len)
{ {
const byte *name; byte argbuf[256], *t = argbuf;
byte *t, argbuf[36];
unsigned i; unsigned i;
/* Don't report Cease messages generated by myself */ /* Don't report Cease messages generated by myself */
if (code == 6 && class == BE_BGP_TX) if (code == 6 && class == BE_BGP_TX)
return; return;
name = bgp_error_dsc(code, subcode); /* Reset shutdown message */
t = argbuf; if ((code == 6) && ((subcode == 2) || (subcode == 4)))
proto_set_message(&p->p, NULL, 0);
if (len) if (len)
{ {
*t++ = ':'; /* Bad peer AS - we would like to print the AS */
*t++ = ' ';
if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4))) if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4)))
{ {
/* Bad peer AS - we would like to print the AS */ t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data));
t += bsprintf(t, "%d", (len == 2) ? get_u16(data) : get_u32(data));
goto done; 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) if (len > 16)
len = 16; len = 16;
for (i=0; i<len; i++) for (i=0; i<len; i++)
t += bsprintf(t, "%02x", data[i]); t += bsprintf(t, "%02x", data[i]);
} }
done: done:
*t = 0; *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 static void
@ -1571,7 +1605,7 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, uint len)
if (err) if (err)
{ {
bgp_update_startup_delay(p); bgp_update_startup_delay(p);
bgp_stop(p, 0); bgp_stop(p, 0, NULL, 0);
} }
} }