Job Snijders(j...@instituut.net) on 2017.01.08 20:24:19 +0100: > Dear OpenBSD developers, > > This patch adds support for the "BGP Administrative Shutdown > Communication" to bgpd(8) and bgpctl(8).
Hi Job and Peter, thanks, this is nice! some comments below. > 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 this duplicates this entry also newer stuff sould go to the bottom of the list i believe. > +.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; please add spaces around += > + datalen-=shutcomm_len; same > + } > + } > + > 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; same > + > + communication = peer->conf.shutcomm; > + > + if (subcode == ERR_CEASE_ADMIN_DOWN && communication && > + *communication) { > + shutcomm_len=strlen(communication); same > + 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; while i think this is correct, would it not be easier to encode \n as control char with VIS_NL? then you could just use strnvis() > } > > const char * >