]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: Shutdown communication (RFC 8203)
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 19 Sep 2017 17:55:37 +0000 (19:55 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 19 Sep 2017 17:57:52 +0000 (19:57 +0200)
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.

nest/config.Y
nest/proto.c
nest/protocol.h
proto/babel/packets.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/packets.c

index 025e896955ade054e27c54c496636b9643ef4c1e..f51be6ea0d70a703530e4826494a7edb8ae30d06 100644 (file)
@@ -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)]])
index 64422ee372db4c1d7413c4feeb821f7c296fe847..552d53ae110b0e6e2edc1239f3159d0c6c0e1351 100644 (file)
@@ -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;
index 18dfbd6fb71ff2138fde6ecf3b2156799f1e2504..5aca9a4eafcfae0f8d9adc0c1232248e102d499b 100644 (file)
@@ -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
index 9042183652c3f60b1738fe2274aa015a94eab10f..e9c6d51d1a29af298662f8d0e9e5168c14c0a9eb 100644 (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_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)
 {
index 8a6b2f028a3e2652a14ad947b7adbb379dd1d2d3..b99672f55ca8f6d8c653b0641847d640d22de563 100644 (file)
@@ -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;
 
- done:
-  bgp_stop(p, subcode);
+  /* 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, 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);
     }
 }
 
index e47a0eb1d8c894bc35afd878a58c5724aa68877c..22a150ab7e592110da2aba1c593f4c1b21893f7b 100644 (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_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);
index ab87bdcc6d0438cf6a223844388c189ad44b04f1..af3b15b59ca46a28c36829e2cf51993011ac2428 100644 (file)
@@ -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:
+
+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);
     }
 }