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]");
 }

Reply via email to