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
16From Bill Fenner:
17
18The Arista timestamp header consists of the following fields:
191. The Arista ethertype (0xd28b)
202. A 2-byte subtype field; 0x01 indicates the timestamp header
213. A 2-byte version field, described below.
224. A 48-bit or 64-bit timestamp field, depending on the contents of the version field
23
24This 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
42The two-byte version value is split into 3 fields:
431. The timescale in use.  Currently assigned values include:
44    0 = TAI
45    1 = UTC
462. The timestamp format and length.  Currently assigned values include:
47    1 = 64-bit timestamp
48    2 = 48-bit timestamp
493. 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
60See also: https://www.arista.com/assets/data/pdf/Whitepapers/Overview_Arista_Timestamps.pdf
61
62*/
63
64#define ARISTA_SUBTYPE_TIMESTAMP 0x0001
65static const struct tok subtype_str[] = {
66	{ ARISTA_SUBTYPE_TIMESTAMP, "Timestamp" },
67	{ 0, NULL }
68};
69
70static 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
78static const struct tok ts_format_str[] = {
79	{ FORMAT_64BIT, "64-bit" },
80	{ FORMAT_48BIT, "48-bit" },
81	{ 0, NULL }
82};
83
84static const struct tok hw_info_str[] = {
85	{ 0, "R/R2" },
86	{ 1, "R3" },
87	{ 0, NULL }
88};
89
90static inline void
91arista_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
104int
105arista_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