summaryrefslogtreecommitdiffstats
path: root/print-arista.c
blob: 6d00956aeeb0749d778e63fac44cb5089240decc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright (c) 2018 Arista Networks, Inc.  All rights reserved.

/* \summary: EtherType protocol for Arista Networks printer */

#include <config.h>

#include "netdissect-stdinc.h"

#include "netdissect.h"
#include "extract.h"

/*

From Bill Fenner:

The Arista timestamp header consists of the following fields:
1. The Arista ethertype (0xd28b)
2. A 2-byte subtype field; 0x01 indicates the timestamp header
3. A 2-byte version field, described below.
4. A 48-bit or 64-bit timestamp field, depending on the contents of the version field

This header is then followed by the original ethertype and the remainder of the original packet.

 0                   1                   2                   3
 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            dst mac                            |
+                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
|                            src mac                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        ethertype 0xd28b       |          subtype 0x1          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            version            |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
|                          timestamp...                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The two-byte version value is split into 3 fields:
1. The timescale in use.  Currently assigned values include:
    0 = TAI
    1 = UTC
2. The timestamp format and length.  Currently assigned values include:
    1 = 64-bit timestamp
    2 = 48-bit timestamp
3. The hardware info
    0 = R/R2 series
    1 = R3 series

 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   timescale   | format|hw info|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


See also: https://www.arista.com/assets/data/pdf/Whitepapers/Overview_Arista_Timestamps.pdf

*/

#define ARISTA_SUBTYPE_TIMESTAMP 0x0001
static const struct tok subtype_str[] = {
	{ ARISTA_SUBTYPE_TIMESTAMP, "Timestamp" },
	{ 0, NULL }
};

static const struct tok ts_timescale_str[] = {
	{ 0, "TAI" },
	{ 1, "UTC" },
	{ 0, NULL }
};

#define FORMAT_64BIT 0x1
#define FORMAT_48BIT 0x2
static const struct tok ts_format_str[] = {
	{ FORMAT_64BIT, "64-bit" },
	{ FORMAT_48BIT, "48-bit" },
	{ 0, NULL }
};

static const struct tok hw_info_str[] = {
	{ 0, "R/R2" },
	{ 1, "R3" },
	{ 0, NULL }
};

static inline void
arista_print_date_hms_time(netdissect_options *ndo, uint32_t seconds,
		uint32_t nanoseconds)
{
	time_t ts;
	char buf[sizeof("-yyyyyyyyyy-mm-dd hh:mm:ss")];

	ts = seconds + (nanoseconds / 1000000000);
	nanoseconds %= 1000000000;
	ND_PRINT("%s.%09u",
	    nd_format_time(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S",
	       gmtime(&ts)), nanoseconds);
}

int
arista_ethertype_print(netdissect_options *ndo, const u_char *bp, u_int len _U_)
{
	uint16_t subTypeId;
	u_short bytesConsumed = 0;

	ndo->ndo_protocol = "arista";

	subTypeId = GET_BE_U_2(bp);
	bp += 2;
	bytesConsumed += 2;

	ND_PRINT("SubType %s (0x%04x), ",
	         tok2str(subtype_str, "Unknown", subTypeId),
	         subTypeId);

	// TapAgg Header Timestamping
	if (subTypeId == ARISTA_SUBTYPE_TIMESTAMP) {
		uint64_t seconds;
		uint32_t nanoseconds;
		uint8_t ts_timescale = GET_U_1(bp);
		bp += 1;
		bytesConsumed += 1;
		ND_PRINT("Timescale %s (%u), ",
		         tok2str(ts_timescale_str, "Unknown", ts_timescale),
		         ts_timescale);

		uint8_t ts_format = GET_U_1(bp) >> 4;
		uint8_t hw_info = GET_U_1(bp) & 0x0f;
		bp += 1;
		bytesConsumed += 1;

		// Timestamp has 32-bit lsb in nanosec and remaining msb in sec
		ND_PRINT("Format %s (%u), HwInfo %s (%u), Timestamp ",
		         tok2str(ts_format_str, "Unknown", ts_format),
		         ts_format,
		         tok2str(hw_info_str, "Unknown", hw_info),
		         hw_info);
		switch (ts_format) {
		case FORMAT_64BIT:
			seconds = GET_BE_U_4(bp);
			nanoseconds = GET_BE_U_4(bp + 4);
			arista_print_date_hms_time(ndo, seconds, nanoseconds);
			bytesConsumed += 8;
			break;
		case FORMAT_48BIT:
			seconds = GET_BE_U_2(bp);
			nanoseconds = GET_BE_U_4(bp + 2);
			seconds += nanoseconds / 1000000000;
			nanoseconds %= 1000000000;
			ND_PRINT("%" PRIu64 ".%09u", seconds, nanoseconds);
			bytesConsumed += 6;
			break;
		default:
			return -1;
		}
	} else {
		return -1;
	}
	ND_PRINT(": ");
	return bytesConsumed;
}