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 16 From Bill Fenner: 17 18 The Arista timestamp header consists of the following fields: 19 1. The Arista ethertype (0xd28b) 20 2. A 2-byte subtype field; 0x01 indicates the timestamp header 21 3. A 2-byte version field, described below. 22 4. A 48-bit or 64-bit timestamp field, depending on the contents of the version field 23 24 This 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 42 The two-byte version value is split into 3 fields: 43 1. The timescale in use. Currently assigned values include: 44 0 = TAI 45 1 = UTC 46 2. The timestamp format and length. Currently assigned values include: 47 1 = 64-bit timestamp 48 2 = 48-bit timestamp 49 3. 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 60 See also: https://www.arista.com/assets/data/pdf/Whitepapers/Overview_Arista_Timestamps.pdf 61 62 */ 63 64 #define ARISTA_SUBTYPE_TIMESTAMP 0x0001 65 static const struct tok subtype_str[] = { 66 { ARISTA_SUBTYPE_TIMESTAMP, "Timestamp" }, 67 { 0, NULL } 68 }; 69 70 static 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 78 static const struct tok ts_format_str[] = { 79 { FORMAT_64BIT, "64-bit" }, 80 { FORMAT_48BIT, "48-bit" }, 81 { 0, NULL } 82 }; 83 84 static const struct tok hw_info_str[] = { 85 { 0, "R/R2" }, 86 { 1, "R3" }, 87 { 0, NULL } 88 }; 89 90 static inline void 91 arista_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 104 int 105 arista_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