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