Home | History | Annotate | Line # | Download | only in dist
      1 // Copyright (c) 2018 Arista Networks, Inc.  All rights reserved.
      2 
      3 /* \summary: EtherType protocol for Arista Networks printer */
      4 
      5 #include <config.h>
      6 
      7 #include "netdissect-stdinc.h"
      8 
      9 #include "netdissect.h"
     10 #include "extract.h"
     11 #include "timeval-operations.h"
     12 
     13 /*
     14 
     15 From Bill Fenner:
     16 
     17 The Arista timestamp header consists of the following fields:
     18 1. The Arista ethertype (0xd28b)
     19 2. A 2-byte subtype field; 0x01 indicates the timestamp header
     20 3. A 2-byte version field, described below.
     21 4. A 48-bit or 64-bit timestamp field, depending on the contents of the version field
     22 
     23 This header is then followed by the original ethertype and the remainder of the original packet.
     24 
     25  0                   1                   2                   3
     26  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     27 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     28 |                            dst mac                            |
     29 +                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     30 |                               |                               |
     31 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
     32 |                            src mac                            |
     33 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     34 |        ethertype 0xd28b       |          subtype 0x1          |
     35 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     36 |            version            |                               |
     37 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
     38 |                          timestamp...                         |
     39 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     40 
     41 The two-byte version value is split into 3 fields:
     42 1. The timescale in use.  Currently assigned values include:
     43     0 = TAI
     44     1 = UTC
     45 2. The timestamp format and length.  Currently assigned values include:
     46     1 = 64-bit timestamp
     47     2 = 48-bit timestamp
     48 3. The hardware info
     49     0 = R/R2 series
     50     1 = R3 series
     51 
     52  0                   1
     53  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
     54 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     55 |   timescale   | format|hw info|
     56 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     57 
     58 
     59 See also: https://www.arista.com/assets/data/pdf/Whitepapers/Overview_Arista_Timestamps.pdf
     60 
     61 */
     62 
     63 #define ARISTA_SUBTYPE_TIMESTAMP 0x0001
     64 static const struct tok subtype_str[] = {
     65 	{ ARISTA_SUBTYPE_TIMESTAMP, "Timestamp" },
     66 	{ 0, NULL }
     67 };
     68 
     69 static const struct tok ts_timescale_str[] = {
     70 	{ 0, "TAI" },
     71 	{ 1, "UTC" },
     72 	{ 0, NULL }
     73 };
     74 
     75 #define FORMAT_64BIT 0x1
     76 #define FORMAT_48BIT 0x2
     77 static const struct tok ts_format_str[] = {
     78 	{ FORMAT_64BIT, "64-bit" },
     79 	{ FORMAT_48BIT, "48-bit" },
     80 	{ 0, NULL }
     81 };
     82 
     83 static const struct tok hw_info_str[] = {
     84 	{ 0, "R/R2" },
     85 	{ 1, "R3" },
     86 	{ 0, NULL }
     87 };
     88 
     89 static inline void
     90 arista_print_date_hms_time(netdissect_options *ndo, const uint32_t seconds,
     91 		const uint32_t nanoseconds)
     92 {
     93 	const time_t ts = seconds;
     94 	char buf[sizeof("-yyyyyyyyyy-mm-dd hh:mm:ss")];
     95 
     96 	ND_PRINT("%s.%09u",
     97 	    nd_format_time(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S",
     98 	       gmtime(&ts)), nanoseconds);
     99 	if (nanoseconds > ND_NANO_PER_SEC - 1)
    100 		ND_PRINT(" " ND_INVALID_NANO_SEC_STR);
    101 }
    102 
    103 int
    104 arista_ethertype_print(netdissect_options *ndo, const u_char *bp, u_int len _U_)
    105 {
    106 	uint16_t subTypeId;
    107 	u_short bytesConsumed = 0;
    108 
    109 	ndo->ndo_protocol = "arista";
    110 
    111 	subTypeId = GET_BE_U_2(bp);
    112 	bp += 2;
    113 	bytesConsumed += 2;
    114 
    115 	ND_PRINT("SubType %s (0x%04x), ",
    116 	         tok2str(subtype_str, "Unknown", subTypeId),
    117 	         subTypeId);
    118 
    119 	// TapAgg Header Timestamping
    120 	if (subTypeId == ARISTA_SUBTYPE_TIMESTAMP) {
    121 		uint32_t seconds;
    122 		uint32_t nanoseconds;
    123 		uint8_t ts_timescale = GET_U_1(bp);
    124 		bp += 1;
    125 		bytesConsumed += 1;
    126 		ND_PRINT("Timescale %s (%u), ",
    127 		         tok2str(ts_timescale_str, "Unknown", ts_timescale),
    128 		         ts_timescale);
    129 
    130 		uint8_t ts_format = GET_U_1(bp) >> 4;
    131 		uint8_t hw_info = GET_U_1(bp) & 0x0f;
    132 		bp += 1;
    133 		bytesConsumed += 1;
    134 
    135 		// Timestamp has 32-bit lsb in nanosec and remaining msb in sec
    136 		ND_PRINT("Format %s (%u), HwInfo %s (%u), Timestamp ",
    137 		         tok2str(ts_format_str, "Unknown", ts_format),
    138 		         ts_format,
    139 		         tok2str(hw_info_str, "Unknown", hw_info),
    140 		         hw_info);
    141 		switch (ts_format) {
    142 		case FORMAT_64BIT:
    143 			seconds = GET_BE_U_4(bp);
    144 			nanoseconds = GET_BE_U_4(bp + 4);
    145 			arista_print_date_hms_time(ndo, seconds, nanoseconds);
    146 			bytesConsumed += 8;
    147 			break;
    148 		case FORMAT_48BIT:
    149 			seconds = GET_BE_U_2(bp);
    150 			nanoseconds = GET_BE_U_4(bp + 2);
    151 			ND_PRINT("%u.%09u", seconds, nanoseconds);
    152 			if (nanoseconds > ND_NANO_PER_SEC - 1)
    153 				ND_PRINT(" " ND_INVALID_NANO_SEC_STR);
    154 			bytesConsumed += 6;
    155 			break;
    156 		default:
    157 			return -1;
    158 		}
    159 	} else {
    160 		return -1;
    161 	}
    162 	ND_PRINT(": ");
    163 	return bytesConsumed;
    164 }
    165