xref: /freebsd/contrib/tcpdump/print-arista.c (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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
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
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