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