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