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