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