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
arista_print_date_hms_time(netdissect_options * ndo,uint32_t seconds,uint32_t nanoseconds)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
arista_ethertype_print(netdissect_options * ndo,const u_char * bp,u_int len _U_)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