This patch adds support to tcpdump(8) to decode BGP Administrative Shutdown Communications in human readable form.
The draft-ietf-idr-shutdown (https://tools.ietf.org/html/draft-ietf-idr-shutdown) specification documents a mechanism to transmit a short freeform UTF-8 message as part of a BGP Cease NOTIFICATION message to inform the neighbor why the BGP session is being shutdown. This modified tcpdump(8) is used in the development of the draft-ietf-idr-shutdown feature for OpenBGPD. example, snipped from 'tcpdump -t -n -v -s 1500 host 165.254.255.17 and port 179': BGP (NOTIFICATION: error Cease, subcode #2, Shutdown Communication (len 124): "NTT will perform maintenance on this router. This is tracked in TICKET-1-24824294. Contact n...@ntt.net for more information.") UTF-8 stuff like 🦄 is printed like this: BGP (NOTIFICATION: error Cease, subcode #2, Shutdown Communication (len 26): "and here is a unicorn \360\237\246\204") Index: usr.sbin/tcpdump/print-bgp.c =================================================================== RCS file: /cvs/src/usr.sbin/tcpdump/print-bgp.c,v retrieving revision 1.20 diff -u -p -r1.20 print-bgp.c --- usr.sbin/tcpdump/print-bgp.c 27 Oct 2016 08:21:58 -0000 1.20 +++ usr.sbin/tcpdump/print-bgp.c 7 Jan 2017 11:15:01 -0000 @@ -228,6 +228,9 @@ static const char *bgpnotify_minor_updat /* RFC 4486 */ #define BGP_NOTIFY_MINOR_CEASE_MAXPRFX 1 +/* draft-ietf-idr-shutdown */ +#define BGP_NOTIFY_MINOR_CEASE_ADMIN 2 +#define BGP_NOTIFY_MINOR_CEASE_ADMIN_SHUTDOWN_LEN 128 static const char *bgpnotify_minor_cease[] = { NULL, "Maximum Number of Prefixes Reached", "Administratively Shutdown", "Peer De-configured", "Administratively Reset", "Connection Rejected", @@ -302,6 +305,13 @@ static const char *afnumber[] = AFNUM_NA sizeof(afnumber)/sizeof(afnumber[0]), (x))) +static void +print_hex(const u_char *p, u_int len) +{ + while (len--) + printf("%02x", *p++); +} + static const char * num_or_str(const char **table, size_t siz, int value) { @@ -996,6 +1006,9 @@ bgp_notification_print(const u_char *dat u_int16_t af; u_int8_t safi; const u_char *p; + uint8_t shutdown_comm_length; + uint8_t remainder_offset; + char string[128]; TCHECK2(dat[0], BGP_NOTIFICATION_SIZE); memcpy(&bgpn, dat, BGP_NOTIFICATION_SIZE); @@ -1026,9 +1039,57 @@ bgp_notification_print(const u_char *dat printf(" Max Prefixes: %u", EXTRACT_32BITS(p+3)); } + + /* + * draft-ietf-idr-shutdown describes a method to send a + * message intended for human consumption regarding the + * Administrative Shutdown event. This is called the + * "Administrative Shutdown Communication". The message + * is UTF-8 encoded and may be no longer than 128 bytes. + */ + + if (bgpn.bgpn_minor == BGP_NOTIFY_MINOR_CEASE_ADMIN && + length >= 1) { + p = dat + BGP_NOTIFICATION_SIZE; + TCHECK2(*p, 1); + shutdown_comm_length = *(p); + remainder_offset = 0; + /* seems we received garbage, hexdump it all */ + if (shutdown_comm_length > + BGP_NOTIFY_MINOR_CEASE_ADMIN_SHUTDOWN_LEN) + printf(", invalid Shutdown Communication (len %i > %i)", + shutdown_comm_length, + BGP_NOTIFY_MINOR_CEASE_ADMIN_SHUTDOWN_LEN); + else if (shutdown_comm_length == 0) { + printf(", empty Shutdown Communication"); + remainder_offset += 1; + } + /* a proper shutdown communication */ + else if (shutdown_comm_length <= + BGP_NOTIFY_MINOR_CEASE_ADMIN_SHUTDOWN_LEN) { + TCHECK2(*p, shutdown_comm_length); + printf(", Shutdown Communication (len %u): \"", + shutdown_comm_length); + memset(string, 0, 128); + memcpy(string, p+1, shutdown_comm_length); + safeputs(string); + printf("\""); + remainder_offset += shutdown_comm_length + 1; + } + /* if there is trailing data, hexdump it */ + if (length - + (remainder_offset + BGP_NOTIFICATION_SIZE) > 0) { + printf(", Data: (len %u) ", + length - (remainder_offset + + BGP_NOTIFICATION_SIZE)); + print_hex(p + remainder_offset, length - + (remainder_offset + BGP_NOTIFICATION_SIZE)); + } + } } return; + trunc: printf("[|BGP]"); }