Dear OpenBSD developers,

This patch adds support for the "BGP Administrative Shutdown
Communication" to bgpd(8) and bgpctl(8).

The draft-ietf-idr-shutdown 
(https://tools.ietf.org/html/draft-ietf-idr-shutdown)
document specifies a mechanism to transmit a short freeform message
across the wire as part of an Administrative Shutdown Cease NOTIFICATION
message. This message serves to inform the neighbor why the BGP session
was shut down. This is useful to communicate for instance a ticket
reference, contact person or other information to the neighbor.

Example usage:

    [job@kiera ~]$ bgpctl show
    Neighbor        AS    MsgRcvd  MsgSent  OutQ Up/Down   State/PrfRcvd
    165.254.255.24  15562  70684      60     0   00:21:16  33653

    [job@kiera ~]$ bgpctl neighbor 165.254.255.24 down "goodbye, we are 
upgrading to openbsd 6.1"
    request processed

    [job@kiera ~]$ bgpctl show
    Neighbor        AS    MsgRcvd  MsgSent  OutQ Up/Down  State/PrfRcvd
    165.254.255.24  15562  70708     62      0   00:00:08 Idle

    [job@kiera ~]$ bgpctl show neighbor 165.254.255.24 | grep reason
      BGP state = Idle, marked down with shutdown reason "goodbye, we are 
upgrading to openbsd 6.1", down for 00:00:17

On the neighbor's side you'll see the following in syslog:

    Jan  8 19:28:54 shutdown bgpd[50719]: neighbor 165.254.255.26: received 
notification: Cease, administratively down
    Jan  8 19:28:54 shutdown bgpd[50719]: neighbor 165.254.255.26: received 
shutdown reason: "goodbye, we are upgrading to openbsd 6.1"

and this is also visible through bgpctl(8):

    [job@shutdown~]$ bgpctl show neighbor 165.254.255.26 | grep reason
      Last received shutdown reason: "goodbye, we are upgrading to openbsd 6.1"

This work has been tested against pmacct and exabgp which also
support draft-ietf-idr-shutdown.

The BGP Administrative Shutdown Communication feature for OpenBGPD was
developed by Peter van Dijk <peter.van.d...@powerdns.com> and Job Snijders 
<j...@ntt.net>.

Kind regards,

Job


Index: usr.sbin/bgpctl/bgpctl.8
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.8,v
retrieving revision 1.69
diff -u -p -r1.69 bgpctl.8
--- usr.sbin/bgpctl/bgpctl.8    25 May 2016 14:15:59 -0000      1.69
+++ usr.sbin/bgpctl/bgpctl.8    8 Jan 2017 17:45:24 -0000
@@ -104,8 +104,14 @@ Destroy a previously cloned peer.
 The peer must be down before calling this function.
 .Ar peer
 may be the neighbor's address or description.
-.It Cm neighbor Ar peer Cm down
-Take the BGP session to the specified neighbor down.
+.It Cm neighbor Ar peer Cm down Op Ar reason
+Take the BGP session to the specified neighbor down. If a
+.Ar reason
+is provided, the 
+.Ar reason
+is send as Administrative Shutdown Communication to the neighbor. The
+.Ar reason
+cannot exceed 128 octets.
 .Ar peer
 may be the neighbor's address or description.
 .It Cm neighbor Ar peer Cm refresh
Index: usr.sbin/bgpctl/bgpctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
retrieving revision 1.188
diff -u -p -r1.188 bgpctl.c
--- usr.sbin/bgpctl/bgpctl.c    3 Jun 2016 17:36:37 -0000       1.188
+++ usr.sbin/bgpctl/bgpctl.c    8 Jan 2017 17:45:24 -0000
@@ -159,6 +159,7 @@ main(int argc, char *argv[])
 
        memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr));
        strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr));
+       strlcpy(neighbor.shutcomm, res->shutcomm, sizeof(neighbor.shutcomm));
 
        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
                err(1, "control_init: socket");
@@ -702,6 +703,13 @@ show_neighbor_msg(struct imsg *imsg, enu
                    inet_ntoa(ina));
                printf("%s\n", print_auth_method(p->auth.method));
                printf("  BGP state = %s", statenames[p->state]);
+               if (p->conf.down) {
+                       printf(", marked down");
+                       if (*(p->conf.shutcomm)) {
+                               printf(" with shutdown reason \"%s\"",
+                                   log_shutcomm(p->conf.shutcomm));
+                       }
+               }
                if (p->stats.last_updown != 0)
                        printf(", %s for %s",
                            p->state == STATE_ESTABLISHED ? "up" : "down",
@@ -736,6 +744,10 @@ show_neighbor_msg(struct imsg *imsg, enu
                        break;
                print_neighbor_msgstats(p);
                printf("\n");
+               if (*(p->stats.last_shutcomm)) {
+                       printf("  Last received shutdown reason: \"%s\"\n",
+                           log_shutcomm(p->stats.last_shutcomm));
+               }
                if (p->state == STATE_IDLE) {
                        static const char       *errstr;
 
Index: usr.sbin/bgpctl/parser.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/parser.c,v
retrieving revision 1.73
diff -u -p -r1.73 parser.c
--- usr.sbin/bgpctl/parser.c    11 Oct 2015 19:53:57 -0000      1.73
+++ usr.sbin/bgpctl/parser.c    8 Jan 2017 17:45:24 -0000
@@ -43,6 +43,7 @@ enum token_type {
        PREFIX,
        PEERDESC,
        RIBNAME,
+       SHUTDOWN_COMMUNICATION,
        COMMUNITY,
        LOCALPREF,
        MED,
@@ -239,9 +240,15 @@ static const struct token t_neighbor[] =
        { ENDTOKEN,     "",             NONE,           NULL}
 };
 
+static const struct token t_neighbor_modifiers_shutcomm[] = {
+       { NOTOKEN,      "",             NONE,           NULL},
+       { SHUTDOWN_COMMUNICATION,       "",             NONE,           NULL},
+       { ENDTOKEN,     "",             NONE,           NULL}
+};
+
 static const struct token t_neighbor_modifiers[] = {
        { KEYWORD,      "up",           NEIGHBOR_UP,            NULL},
-       { KEYWORD,      "down",         NEIGHBOR_DOWN,          NULL},
+       { KEYWORD,      "down",         NEIGHBOR_DOWN,          
t_neighbor_modifiers_shutcomm},
        { KEYWORD,      "clear",        NEIGHBOR_CLEAR,         NULL},
        { KEYWORD,      "refresh",      NEIGHBOR_RREFRESH,      NULL},
        { KEYWORD,      "destroy",      NEIGHBOR_DESTROY,       NULL},
@@ -549,6 +556,16 @@ match_token(int *argc, char **argv[], co
                                t = &table[i];
                        }
                        break;
+               case SHUTDOWN_COMMUNICATION:
+                       if (!match && word != NULL && wordlen > 0) {
+                               if (strlcpy(res.shutcomm, word,
+                                       sizeof(res.shutcomm)) >=
+                                       sizeof(res.shutcomm))
+                                       errx(1, "shutdown reason too long");
+                               match++;
+                               t = &table[i];
+                       }
+                       break;
                case COMMUNITY:
                        if (word != NULL && wordlen > 0 &&
                            parse_community(word, &res)) {
@@ -664,6 +681,9 @@ show_valid_args(const struct token table
                        break;
                case RIBNAME:
                        fprintf(stderr, "  <rib name>\n");
+                       break;
+               case SHUTDOWN_COMMUNICATION:
+                       fprintf(stderr, "  <shutdown reason>\n");
                        break;
                case COMMUNITY:
                        fprintf(stderr, "  <community>\n");
Index: usr.sbin/bgpctl/parser.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/parser.h,v
retrieving revision 1.27
diff -u -p -r1.27 parser.h
--- usr.sbin/bgpctl/parser.h    17 Apr 2015 07:51:09 -0000      1.27
+++ usr.sbin/bgpctl/parser.h    8 Jan 2017 17:45:24 -0000
@@ -65,6 +65,7 @@ struct parse_result {
        struct filter_community  community;
        char                     peerdesc[PEER_DESCR_LEN];
        char                     rib[PEER_DESCR_LEN];
+       char                     shutcomm[SHUT_COMM_LEN];
        char                    *irr_outdir;
        int                      flags;
        u_int                    rtableid;
Index: usr.sbin/bgpd/bgpd.8
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.8,v
retrieving revision 1.48
diff -u -p -r1.48 bgpd.8
--- usr.sbin/bgpd/bgpd.8        14 Aug 2013 06:32:36 -0000      1.48
+++ usr.sbin/bgpd/bgpd.8        8 Jan 2017 17:45:24 -0000
@@ -238,6 +238,26 @@ control socket
 .Re
 .Pp
 .Rs
+.%A J. Snijders
+.%A J. Heitz
+.%A K. Patel
+.%A I. Bagdonas
+.%A A. Simpson
+.%D January 2017
+.%R draft-ietf-idr-large-community
+.%T Large BGP Communities Attribute
+.Re
+.Pp
+.Rs
+.%A J. Snijders
+.%A J. Heitz
+.%A J. Scudder
+.%D January 2017
+.%R draft-ietf-idr-shutdown
+.%T BGP Administrative Shutdown Communication
+.Re
+.Pp
+.Rs
 .%A A. Heffernan
 .%D August 1998
 .%R RFC 2385
Index: usr.sbin/bgpd/bgpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v
retrieving revision 1.144
diff -u -p -r1.144 bgpd.conf.5
--- usr.sbin/bgpd/bgpd.conf.5   28 Jun 2016 16:59:14 -0000      1.144
+++ usr.sbin/bgpd/bgpd.conf.5   8 Jan 2017 17:45:24 -0000
@@ -722,9 +722,17 @@ The description is used when logging nei
 reports, for specifying neighbors, etc., but has no further meaning to
 .Xr bgpd 8 .
 .Pp
-.It Ic down
+.It Ic down Op Ar reason
 Do not start the session when bgpd comes up but stay in
 .Em IDLE .
+If the session is cleared at runtime, after a
+.Ic down
+.Ar reason
+was configured at runtime, the
+.Ar reason
+is sent as Administrative Shutdown Communication. The
+.Ar reason
+cannot exceed 128 octets.
 .Pp
 .It Xo
 .Ic dump
Index: usr.sbin/bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.294
diff -u -p -r1.294 bgpd.h
--- usr.sbin/bgpd/bgpd.h        6 Jun 2016 15:59:10 -0000       1.294
+++ usr.sbin/bgpd/bgpd.h        8 Jan 2017 17:45:24 -0000
@@ -38,6 +38,7 @@
 #define        CONFFILE                        "/etc/bgpd.conf"
 #define        BGPD_USER                       "_bgpd"
 #define        PEER_DESCR_LEN                  32
+#define        SHUT_COMM_LEN                   129
 #define        PFTABLE_LEN                     32
 #define        TCP_MD5_KEY_LEN                 80
 #define        IPSEC_ENC_KEY_LEN               32
@@ -296,6 +297,7 @@ struct peer_config {
        struct capabilities      capabilities;
        char                     group[PEER_DESCR_LEN];
        char                     descr[PEER_DESCR_LEN];
+       char                     shutcomm[SHUT_COMM_LEN];
        char                     rib[PEER_DESCR_LEN];
        char                     if_depend[IFNAMSIZ];
        char                     demote_group[IFNAMSIZ];
@@ -579,6 +581,7 @@ struct ctl_show_nexthop {
 struct ctl_neighbor {
        struct bgpd_addr        addr;
        char                    descr[PEER_DESCR_LEN];
+       char                    shutcomm[SHUT_COMM_LEN];
        int                     show_timers;
 };
 
@@ -1060,6 +1063,7 @@ const char        *log_sockaddr(struct sockaddr
 const char     *log_as(u_int32_t);
 const char     *log_rd(u_int64_t);
 const char     *log_ext_subtype(u_int8_t);
+const char     *log_shutcomm(const char *);
 int             aspath_snprint(char *, size_t, void *, u_int16_t);
 int             aspath_asprint(char **, void *, u_int16_t);
 size_t          aspath_strlen(void *, u_int16_t);
Index: usr.sbin/bgpd/control.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/control.c,v
retrieving revision 1.82
diff -u -p -r1.82 control.c
--- usr.sbin/bgpd/control.c     5 Dec 2015 18:28:04 -0000       1.82
+++ usr.sbin/bgpd/control.c     8 Jan 2017 17:45:24 -0000
@@ -337,9 +337,15 @@ control_dispatch_msg(struct pollfd *pfd,
                                switch (imsg.hdr.type) {
                                case IMSG_CTL_NEIGHBOR_UP:
                                        bgp_fsm(p, EVNT_START);
+                                       p->conf.down = 0;
+                                       p->conf.shutcomm[0] = '\0';
                                        control_result(c, CTL_RES_OK);
                                        break;
                                case IMSG_CTL_NEIGHBOR_DOWN:
+                                       p->conf.down = 1;
+                                       strlcpy(p->conf.shutcomm,
+                                           neighbor->shutcomm,
+                                           sizeof(neighbor->shutcomm));
                                        session_stop(p, ERR_CEASE_ADMIN_DOWN);
                                        control_result(c, CTL_RES_OK);
                                        break;
Index: usr.sbin/bgpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.288
diff -u -p -r1.288 parse.y
--- usr.sbin/bgpd/parse.y       21 Jun 2016 21:35:24 -0000      1.288
+++ usr.sbin/bgpd/parse.y       8 Jan 2017 17:45:24 -0000
@@ -1029,8 +1029,19 @@ peeropts : REMOTEAS as4number    {
                | PASSIVE               {
                        curpeer->conf.passive = 1;
                }
-               | DOWN          {
+               | DOWN                  {
                        curpeer->conf.down = 1;
+               }
+               | DOWN STRING           {
+                       curpeer->conf.down = 1;
+                       if (strlcpy(curpeer->conf.shutcomm, $2,
+                               sizeof(curpeer->conf.shutcomm)) >=
+                               sizeof(curpeer->conf.shutcomm)) {
+                                   yyerror("shutdown reason too long");
+                                   free($2);
+                                   YYERROR;
+                       }
+                       free($2);
                }
                | RIB STRING    {
                        if (!find_rib($2)) {
Index: usr.sbin/bgpd/session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.351
diff -u -p -r1.351 session.c
--- usr.sbin/bgpd/session.c     25 Jul 2016 14:29:28 -0000      1.351
+++ usr.sbin/bgpd/session.c     8 Jan 2017 17:45:24 -0000
@@ -552,6 +552,9 @@ session_main(int debug, int verbose)
 
        while ((p = peers) != NULL) {
                peers = p->next;
+               strlcpy(p->conf.shutcomm,
+                   "bgpd shutting down",
+                   sizeof(p->conf.shutcomm));
                session_stop(p, ERR_CEASE_ADMIN_DOWN);
                pfkey_remove(p);
                free(p);
@@ -2206,6 +2209,7 @@ parse_notification(struct peer *peer)
        u_int8_t         subcode;
        u_int8_t         capa_code;
        u_int8_t         capa_len;
+       u_int8_t         shutcomm_len;
        u_int8_t         i;
 
        /* just log */
@@ -2300,6 +2304,31 @@ parse_notification(struct peer *peer)
                return (1);
        }
 
+       if (errcode == ERR_CEASE && subcode == ERR_CEASE_ADMIN_DOWN) {
+               if (datalen >= sizeof(shutcomm_len)) {
+                       memcpy(&shutcomm_len, p, sizeof(shutcomm_len));
+                       p += sizeof(shutcomm_len);
+                       datalen -= sizeof(shutcomm_len);
+                       if(datalen < shutcomm_len) {
+                           log_peer_warnx(&peer->conf,
+                               "received truncated shutdown reason");
+                           return (0);
+                       }
+                       if (shutcomm_len > (SHUT_COMM_LEN-1)) {
+                           log_peer_warnx(&peer->conf,
+                               "received overly long shutdown reason");
+                           return (0);
+                       }
+                       memcpy(peer->stats.last_shutcomm, p, shutcomm_len);
+                       peer->stats.last_shutcomm[shutcomm_len] = '\0';
+                       log_peer_warnx(&peer->conf,
+                           "received shutdown reason: \"%s\"",
+                           log_shutcomm(peer->stats.last_shutcomm));
+                       p+=shutcomm_len;
+                       datalen-=shutcomm_len;
+               }
+       }
+
        return (0);
 }
 
@@ -3180,11 +3209,29 @@ session_demote(struct peer *p, int level
 void
 session_stop(struct peer *peer, u_int8_t subcode)
 {
+       char data[SHUT_COMM_LEN];
+       uint8_t datalen;
+       uint8_t shutcomm_len;
+       char *communication;
+
+       datalen=0;
+
+       communication = peer->conf.shutcomm;
+
+       if (subcode == ERR_CEASE_ADMIN_DOWN && communication &&
+           *communication) {
+               shutcomm_len=strlen(communication);
+               if(shutcomm_len < SHUT_COMM_LEN) {
+                       data[0] = shutcomm_len;
+                       datalen = shutcomm_len + sizeof(data[0]);
+                       memcpy(data + 1, communication, shutcomm_len);
+               }
+       }
        switch (peer->state) {
        case STATE_OPENSENT:
        case STATE_OPENCONFIRM:
        case STATE_ESTABLISHED:
-               session_notification(peer, ERR_CEASE, subcode, NULL, 0);
+               session_notification(peer, ERR_CEASE, subcode, data, datalen);
                break;
        default:
                /* session not open, no need to send notification */
Index: usr.sbin/bgpd/session.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.h,v
retrieving revision 1.121
diff -u -p -r1.121 session.h
--- usr.sbin/bgpd/session.h     25 Oct 2015 18:49:01 -0000      1.121
+++ usr.sbin/bgpd/session.h     8 Jan 2017 17:45:24 -0000
@@ -168,6 +168,7 @@ struct peer_stats {
        u_int32_t                prefix_cnt;
        u_int8_t                 last_sent_errcode;
        u_int8_t                 last_sent_suberr;
+       char                     last_shutcomm[SHUT_COMM_LEN];
 };
 
 enum Timer {
Index: usr.sbin/bgpd/util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.21
diff -u -p -r1.21 util.c
--- usr.sbin/bgpd/util.c        3 Jun 2016 17:36:37 -0000       1.21
+++ usr.sbin/bgpd/util.c        8 Jan 2017 17:45:24 -0000
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <vis.h>
 
 #include "bgpd.h"
 #include "rde.h"
@@ -156,6 +157,24 @@ log_ext_subtype(u_int8_t subtype)
                snprintf(etype, sizeof(etype), "[%u]", subtype);
                return (etype);
        }
+}
+
+const char *
+log_shutcomm(const char *communication) {
+       static char buf[(SHUT_COMM_LEN - 1) * 4 + 1];
+       const char *p;
+       char *q;
+
+       p = communication;
+       for (q = buf; *p && q < &buf[sizeof(buf) - 1]; p++) {
+               if (*p == '\n')
+                       *q++ = ' ';
+               else
+                       q = vis(q, *p, 0, 0);
+       }
+       *q = '\0';
+
+       return buf;
 }
 
 const char *

Reply via email to