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