blob: 079ad684bc1145dfb6e3264cc8b671474569f180 [file] [log] [blame]
// Copyright (c) 2018 Arista Networks, Inc. All rights reserved.
/* \summary: EtherType protocol for Arista Networks printer */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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;
}