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