From 433a1d790435a01d3bfbe9b4cd2f22d5e9598e9c Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Fri, 20 Oct 2023 11:15:34 -0400 Subject: [PATCH] timestamp: support multiple trailer timestamp formats Make packet trailer timestamp format configurable. EF_RX_TIMESTAMPING_ORDERING allows selecting timestamps embedded in packet trailers for ordering and SO_TIMESTAMPING. The current implementation assumes a single fixed trailer format, cPacket. Make the format selectable, with EF_RX_TIMESTAMPING_TRAILER_FORMAT. Introduce initially two new formats to demonstrate the feature. Implementation: - expand ci_rx_pkt_timestamp_cpacket into ci_rx_pkt_timestamp_trailer, to demultiplex type. Minimal refactoring of the existing function to avoid code duplication. Cpacket func can be simplified further, but with more churn, so left for a v2 revision. - expand src/tests/unit/header/ci/internal/ip_timestamp.c with basic unit tests for the new types. For more information on cpacket and ttag trailer formats, also see - https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-metamako.c - https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-cisco-ttag.c Tested: scripts/run_unit_tests.sh --- Changes v1->v2 - add nsec_frac initialization to ci_rx_pkt_timestamp_ttag and nsec_frac validation to test_rx_pkt_timestamp_trailer_ttag - rename Type3 to Broadcom, after getting approval - update origin field and document FCS behavior - convert ci_rx_pkt_timestamp_ttag to common ci_rx_pkt_.. arg fmt: - now remove duplicative tests - remove two levels of indentation - (unfortunately) have to update all direct callers in tests - no other changes to the logic (hard to spot without --word-diff) - tighten ttag and brcm tests: encode largest supported value - avoid ISO C90 forbids mixed declarations and code warnings --- src/include/ci/internal/ip_timestamp.h | 238 ++++++++++++------ src/include/ci/internal/opts_netif_def.h | 17 +- src/lib/transport/ip/ip_cmsg.c | 3 +- src/lib/transport/ip/netif_init.c | 9 +- src/lib/transport/unix/tcp_fd.c | 3 +- src/lib/transport/unix/udp_fd.c | 3 +- .../unit/header/ci/internal/ip_timestamp.c | 106 +++++++- 7 files changed, 284 insertions(+), 95 deletions(-) diff --git a/src/include/ci/internal/ip_timestamp.h b/src/include/ci/internal/ip_timestamp.h index 97eef1188..d76dfcd06 100644 --- a/src/include/ci/internal/ip_timestamp.h +++ b/src/include/ci/internal/ip_timestamp.h @@ -81,98 +81,173 @@ ci_rx_pkt_timestamp_nic(const ci_ip_pkt_fmt* pkt, } static inline void -ci_rx_pkt_timestamp_cpacket(const ci_ip_pkt_fmt* pkt, - struct onload_timestamp* ts_out) +ci_rx_pkt_timestamp_cpacket(struct onload_timestamp* ts_out, + const char *buf_end, const char *pkt_end) { - ts_out->sec = 0; + /* These fields are appended to the packet, after the IP payload, the + * original Ethernet FCS, and any extension tags. A new FCS was added to + * the end to make a valid packet, then checked and removed by the NIC. + * + * We look for this at the very end of the packet, assuming that nothing + * else has added any other trailer after it. If there are multiple cpacket + * trailers, then we will take the last (most recent) one. + */ + struct cpacket { + uint32_t sec; + uint32_t nsec; + uint8_t flags; + uint16_t dev; + uint8_t port; + } __attribute__((packed)); - /* fixme: fragmented packets will need different treatment */ - if(CI_LIKELY( OO_PP_IS_NULL(pkt->frag_next) )) { - - /* These fields are appended to the packet, after the IP payload, the - * original Ethernet FCS, and any extension tags. A new FCS was added to - * the end to make a valid packet, then checked and removed by the NIC. - * - * We look for this at the very end of the packet, assuming that nothing - * else has added any other trailer after it. If there are multiple cpacket - * trailers, then we will take the last (most recent) one. - */ - struct cpacket { - uint32_t sec; - uint32_t nsec; - uint8_t flags; - uint16_t dev; - uint8_t port; - } __attribute__((packed)); - - const char* buf_end = PKT_START(pkt) + pkt->pay_len; - const char* pkt_end = (char*)RX_PKT_IPX_HDR(pkt) + RX_PKT_PAYLOAD_LEN(pkt); - ci_assert_ge(buf_end, pkt_end); - - if( buf_end >= pkt_end + sizeof(struct cpacket) ) { - const struct cpacket* cp = (const struct cpacket*)buf_end - 1; - ts_out->sec = ntohl(cp->sec); - ts_out->nsec = ntohl(cp->nsec); - ts_out->nsec_frac = 0; - - /* If extensions are present, search for a sub-ns timestamp */ - if( cp->flags & 0x2 ) { - /* This points to the last byte of the current tag */ - const uint8_t* ext = (const uint8_t*)cp - 1; - const uint8_t* end = (const uint8_t*)pkt_end; - - for( ;; ) { - int tag = *ext; - int type = tag & 0x1f; - int len; - - /* Optimise for the expected case of a single sub-ns timestamp tag. - * - * NOTE: it's possible that a malformed trailer could cause us to read - * a garbage value here, and we do not check for that. That should be - * the worst possible failure since, however badly formed the trailer, - * we will always calculate a non-zero length below and eventually - * reach the packet data and bail out. */ - if(CI_LIKELY( type == 0x01 )) { - ts_out->nsec_frac = ext[-1] | (ext[-2] << 8) | (ext[-3] << 16); - break; - } - - /* Check the flag that indicates this is the last extension */ - if( tag & 0x20 ) - break; - - /* Extract the length depending on tag type */ - if( type == 0x1f ) - /* secondary tag: 10 bits excluding first and second words */ - len = ((tag >> 6) | (ext[-1] << 2)) + 2; - else - /* primary tag: 2 bits excluding first word */ - len = (tag >> 6) + 1; - - /* length field gives 32-bit words */ - len *= 4; - - /* Bail out if a bogus length takes us out of range */ - if( ext - end < len ) - break; - - ext -= len; - } + const struct cpacket* cp = (const struct cpacket*)buf_end - 1; + + if( buf_end - pkt_end < sizeof(struct cpacket) ) + return; + + ts_out->sec = ntohl(cp->sec); + ts_out->nsec = ntohl(cp->nsec); + ts_out->nsec_frac = 0; + + /* If extensions are present, search for a sub-ns timestamp */ + if( cp->flags & 0x2 ) { + /* This points to the last byte of the current tag */ + const uint8_t* ext = (const uint8_t*)cp - 1; + const uint8_t* end = (const uint8_t*)pkt_end; + + for( ;; ) { + int tag = *ext; + int type = tag & 0x1f; + int len; + + /* Optimise for the expected case of a single sub-ns timestamp tag. + * + * NOTE: it's possible that a malformed trailer could cause us to read + * a garbage value here, and we do not check for that. That should be + * the worst possible failure since, however badly formed the trailer, + * we will always calculate a non-zero length below and eventually + * reach the packet data and bail out. */ + if(CI_LIKELY( type == 0x01 )) { + ts_out->nsec_frac = ext[-1] | (ext[-2] << 8) | (ext[-3] << 16); + break; } + + /* Check the flag that indicates this is the last extension */ + if( tag & 0x20 ) + break; + + /* Extract the length depending on tag type */ + if( type == 0x1f ) + /* secondary tag: 10 bits excluding first and second words */ + len = ((tag >> 6) | (ext[-1] << 2)) + 2; + else + /* primary tag: 2 bits excluding first word */ + len = (tag >> 6) + 1; + + /* length field gives 32-bit words */ + len *= 4; + + /* Bail out if a bogus length takes us out of range */ + if( ext - end < len ) + break; + + ext -= len; } } } static inline void -ci_rx_pkt_timestamp(const ci_ip_pkt_fmt* pkt, struct onload_timestamp* ts_out, int src) +ci_rx_pkt_timestamp_ttag(struct onload_timestamp* ts_out, + const char *buf_end, const char *pkt_end) +{ + struct ttag { + uint8_t ts[6]; /* 48b nsecs */ + } __attribute__((packed)); + + const struct ttag* ttag = (const struct ttag*)buf_end - 1; + const uint64_t nsec_per_sec = 1000ULL * 1000 * 1000; + uint64_t ts; + + if( buf_end - pkt_end < sizeof(struct ttag) ) + return; + + ts = ((uint64_t)ttag->ts[0] << 40) | ((uint64_t)ttag->ts[1] << 32) | + ((uint64_t)ttag->ts[2] << 24) | ((uint64_t)ttag->ts[3] << 16) | + ((uint64_t)ttag->ts[4] << 8) | ((uint64_t)ttag->ts[5]); + + ts_out->sec = ts / nsec_per_sec; + ts_out->nsec = ts - (ts_out->sec * nsec_per_sec); + ts_out->nsec_frac = 0; +} + +static inline void +ci_rx_pkt_timestamp_brcm(struct onload_timestamp* ts_out, + const char *buf_end, const char *pkt_end) +{ + struct brcm_trailer { + uint8_t ts[6]; /* 48b timestamp: 18b sec + 30b nsec */ + + uint8_t reserved; + uint8_t origin[3]; + /* Followed by FCS, already removed by the NIC for this packet. + * With multiple trailers, each ends in such a 4B reserved field. + */ + } __attribute__((packed)); + + const struct brcm_trailer* tt = (const struct brcm_trailer*)buf_end - 1; + + if( buf_end - pkt_end < sizeof(struct brcm_trailer) ) + return; + + ts_out->sec = ((uint32_t)(tt->ts[0]) << 10) | + ((uint32_t)(tt->ts[1]) << 2) | + (tt->ts[2] >> 6); + ts_out->nsec = ((tt->ts[2] & 0x3F) << 24) | + ((uint32_t)(tt->ts[3]) << 16) | + ((uint32_t)(tt->ts[4]) << 8) | + tt->ts[5]; + ts_out->nsec_frac = 0; +} + +static inline void +ci_rx_pkt_timestamp_trailer(const ci_ip_pkt_fmt* pkt, + struct onload_timestamp* ts_out, int format) +{ + const char *buf_end, *pkt_end; + + ts_out->sec = 0; + + /* fixme: fragmented packets will need different treatment */ + if(CI_UNLIKELY( !OO_PP_IS_NULL(pkt->frag_next) )) + return; + + buf_end = PKT_START(pkt) + pkt->pay_len; + pkt_end = (char*)RX_PKT_IPX_HDR(pkt) + RX_PKT_PAYLOAD_LEN(pkt); + ci_assert_ge(buf_end, pkt_end); + + switch( format ) { + case CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET: + ci_rx_pkt_timestamp_cpacket(ts_out, buf_end, pkt_end); + break; + case CITP_RX_TIMESTAMPING_TRAILER_FORMAT_TTAG: + ci_rx_pkt_timestamp_ttag(ts_out, buf_end, pkt_end); + break; + case CITP_RX_TIMESTAMPING_TRAILER_FORMAT_BRCM: + ci_rx_pkt_timestamp_brcm(ts_out, buf_end, pkt_end); + break; + } +} + +static inline void +ci_rx_pkt_timestamp(const ci_ip_pkt_fmt* pkt, struct onload_timestamp* ts_out, + int src, int format) { switch( src ) { case CITP_RX_TIMESTAMPING_SOURCE_NIC: ci_rx_pkt_timestamp_nic(pkt, ts_out); break; - case CITP_RX_TIMESTAMPING_SOURCE_CPACKET: - ci_rx_pkt_timestamp_cpacket(pkt, ts_out); + case CITP_RX_TIMESTAMPING_SOURCE_TRAILER: + ci_rx_pkt_timestamp_trailer(pkt, ts_out, format); break; default: ts_out->sec = 0; @@ -181,10 +256,11 @@ ci_rx_pkt_timestamp(const ci_ip_pkt_fmt* pkt, struct onload_timestamp* ts_out, i } static inline void -ci_rx_pkt_timespec(const ci_ip_pkt_fmt* pkt, ef_timespec* ts_out, int src) +ci_rx_pkt_timespec(const ci_ip_pkt_fmt* pkt, ef_timespec* ts_out, int src, + int trailer_type) { struct onload_timestamp ts; - ci_rx_pkt_timestamp(pkt, &ts, src); + ci_rx_pkt_timestamp(pkt, &ts, src, trailer_type); onload_timestamp_to_timespec(&ts, ts_out); } diff --git a/src/include/ci/internal/opts_netif_def.h b/src/include/ci/internal/opts_netif_def.h index 34fe65197..05a581bfe 100644 --- a/src/include/ci/internal/opts_netif_def.h +++ b/src/include/ci/internal/opts_netif_def.h @@ -346,12 +346,23 @@ CI_CFG_OPT("EF_RX_TIMESTAMPING", rx_timestamping, ci_uint32, 2, , 0, 0, 3, count) #define CITP_RX_TIMESTAMPING_SOURCE_NIC 0 -#define CITP_RX_TIMESTAMPING_SOURCE_CPACKET 1 +#define CITP_RX_TIMESTAMPING_SOURCE_TRAILER 1 CI_CFG_OPT("EF_RX_TIMESTAMPING_ORDERING", rx_timestamping_ordering, ci_uint32, "Select the source of timestamps to use to order received packets.\n" " nic - use hardware timestamps generated by the NIC\n" -" cpacket - use cPacket timestamps on received packets\n", - 2, , 0, 0, 1, oneof:nic;cpacket) +" trailer - use trailer timestamps on received packets\n" +" cPacket - synonym for trailer (legacy)\n", + 1, , 0, 0, 2, oneof:nic;trailer;cpacket) + +#define CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET 0 +#define CITP_RX_TIMESTAMPING_TRAILER_FORMAT_TTAG 1 +#define CITP_RX_TIMESTAMPING_TRAILER_FORMAT_BRCM 2 +CI_CFG_OPT("EF_RX_TIMESTAMPING_TRAILER_FORMAT", rx_timestamping_trailer_fmt, ci_uint32, +"Select the format of timestamps received as a packet trailer.\n" +" cpacket - use cPacket trailer timestamp format\n" +" ttag - use TTAG trailer timestamp format\n" +" brcm - use Broadcom trailer timestamp format\n", + 2, , 0, 0, 2, oneof:nic;cpacket;ttag;brcm) CI_CFG_OPT("EF_TX_TIMESTAMPING", tx_timestamping, ci_uint32, "Control of hardware timestamping of transmitted packets, possible values:\n" diff --git a/src/lib/transport/ip/ip_cmsg.c b/src/lib/transport/ip/ip_cmsg.c index ebd5fc327..212a59004 100644 --- a/src/lib/transport/ip/ip_cmsg.c +++ b/src/lib/transport/ip/ip_cmsg.c @@ -329,7 +329,8 @@ void ip_cmsg_recv_timestamping(ci_netif *ni, const ci_ip_pkt_fmt *pkt, } if( flags & ONLOAD_TIMESTAMPING_FLAG_RX_CPACKET ) { ci_assert_lt(n, ONLOAD_TIMESTAMPING_FLAG_RX_COUNT); - ci_rx_pkt_timestamp_cpacket(pkt, &ts[n++]); + ci_rx_pkt_timestamp_trailer(pkt, &ts[n++], + NI_OPTS(ni).rx_timestamping_trailer_fmt); } ci_put_cmsg(cmsg_state, SOL_SOCKET, ONLOAD_SO_TIMESTAMPING, diff --git a/src/lib/transport/ip/netif_init.c b/src/lib/transport/ip/netif_init.c index 99157e185..0d2bc2470 100644 --- a/src/lib/transport/ip/netif_init.c +++ b/src/lib/transport/ip/netif_init.c @@ -1176,9 +1176,16 @@ void ci_netif_config_opts_getenv(ci_netif_config_opts* opts) if( (s = getenv("EF_RX_TIMESTAMPING")) ) opts->rx_timestamping = atoi(s); - static const char* const timestamping_opts[] = { "nic", "cpacket", 0 }; + static const char* const timestamping_opts[] = { "nic", "trailer", "cpacket", 0 }; opts->rx_timestamping_ordering = parse_enum(opts, "EF_RX_TIMESTAMPING_ORDERING", timestamping_opts, "nic"); + /* cpacket (2) is a synonym for trailer (1) */ + if (opts->rx_timestamping_ordering > 1) + opts->rx_timestamping_ordering--; + + static const char* const ts_trailer_formats[] = { "cpacket", "ttag", "brcm", 0 }; + opts->rx_timestamping_trailer_fmt = + parse_enum(opts, "EF_RX_TIMESTAMPING_TRAILER_FORMAT", ts_trailer_formats, "cpacket"); if( (s = getenv("EF_TX_TIMESTAMPING")) ) opts->tx_timestamping = atoi(s); diff --git a/src/lib/transport/unix/tcp_fd.c b/src/lib/transport/unix/tcp_fd.c index efe2e8604..f93538c90 100644 --- a/src/lib/transport/unix/tcp_fd.c +++ b/src/lib/transport/unix/tcp_fd.c @@ -2166,7 +2166,8 @@ citp_tcp_ordered_data(citp_fdinfo* fdi, struct timespec* limit, do { struct timespec stamp; ci_rx_pkt_timespec(pkt, &stamp, - NI_OPTS(epi->sock.netif).rx_timestamping_ordering); + NI_OPTS(epi->sock.netif).rx_timestamping_ordering, + NI_OPTS(epi->sock.netif).rx_timestamping_trailer_fmt); if( citp_timespec_compare(&stamp, limit) < 1 ) { *bytes_out += oo_offbuf_left(&pkt->buf); diff --git a/src/lib/transport/unix/udp_fd.c b/src/lib/transport/unix/udp_fd.c index a69c82ad7..2e04bf85f 100644 --- a/src/lib/transport/unix/udp_fd.c +++ b/src/lib/transport/unix/udp_fd.c @@ -715,7 +715,8 @@ citp_udp_ordered_data(citp_fdinfo* fdi, struct timespec* limit, do { struct timespec stamp; ci_rx_pkt_timespec(pkt, &stamp, - NI_OPTS(epi->sock.netif).rx_timestamping_ordering); + NI_OPTS(epi->sock.netif).rx_timestamping_ordering, + NI_OPTS(epi->sock.netif).rx_timestamping_trailer_fmt); if( citp_timespec_compare(&stamp, limit) < 1 ) { /* We have data before the limit, add on the number of readable bytes. */ diff --git a/src/tests/unit/header/ci/internal/ip_timestamp.c b/src/tests/unit/header/ci/internal/ip_timestamp.c index 785cf68c4..ea3eaf913 100644 --- a/src/tests/unit/header/ci/internal/ip_timestamp.c +++ b/src/tests/unit/header/ci/internal/ip_timestamp.c @@ -65,6 +65,46 @@ static void pkt_cpacket(ci_ip_pkt_fmt* pkt, uint32_t sec, uint32_t nsec) pkt_append(pkt, data, 12); } +static void pkt_ttag(ci_ip_pkt_fmt* pkt, uint32_t sec, uint32_t nsec) +{ + const uint64_t nsec_per_sec = 1000ULL * 1000 * 1000; + uint8_t ttag[6]; + uint64_t ts; + + ts = (sec * nsec_per_sec) + nsec; + assert((ts & 0xFF00000000000000ULL) == 0); + + ttag[0] = (ts & 0xFF0000000000) >> 40; + ttag[1] = (ts & 0xFF00000000) >> 32; + ttag[2] = (ts & 0xFF000000) >> 24; + ttag[3] = (ts & 0xFF0000) >> 16; + ttag[4] = (ts & 0xFF00) >> 8; + ttag[5] = ts & 0xFF; + + pkt_fcs(pkt); + pkt_append(pkt, ttag, sizeof(ttag)); +} + +static void pkt_trailer_brcm(ci_ip_pkt_fmt* pkt, uint32_t sec, uint32_t nsec) +{ + uint8_t trailer[10]; + + assert((sec & ~0x3FFFF) == 0); + assert((nsec & ~0x3FFFFFFF) == 0); + + trailer[2] = (nsec & 0x3F000000) >> 24; + trailer[3] = (nsec & 0x00FF0000) >> 16; + trailer[4] = (nsec & 0x0000FF00) >> 8; + trailer[5] = nsec & 0x000000FF; + + trailer[0] = ((sec >> 2) & 0xFF00) >> 8; + trailer[1] = (sec >> 2) & 0x00FF; + trailer[2] |= (sec & 0x3) << 6; + + pkt_fcs(pkt); + pkt_append(pkt, trailer, sizeof(trailer)); +} + static void pkt_tag_header(ci_ip_pkt_fmt* pkt, int type, int len) { assert(type < (1 << 5)); @@ -134,7 +174,7 @@ static void test_rx_pkt_timestamp_cpacket_none(void) pkt_udp(pkt, NULL, 0); STATE_STASH(buf); - ci_rx_pkt_timestamp_cpacket(pkt, ts); + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET); STATE_CHECK(ts, sec, 0); STATE_FREE(buf); @@ -157,7 +197,7 @@ static void test_rx_pkt_timestamp_cpacket_basic(void) pkt_cpacket(pkt, sec, nsec); STATE_STASH(buf); - ci_rx_pkt_timestamp_cpacket(pkt, ts); + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET); STATE_CHECK(ts, sec, sec); STATE_CHECK(ts, nsec, nsec); @@ -183,7 +223,7 @@ static void test_rx_pkt_timestamp_cpacket_subnano(void) pkt_cpacket(pkt, sec, nsec); STATE_STASH(buf); - ci_rx_pkt_timestamp_cpacket(pkt, ts); + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET); STATE_CHECK(ts, sec, sec); STATE_CHECK(ts, nsec, nsec); STATE_CHECK(ts, nsec_frac, subnano); @@ -218,7 +258,7 @@ static void test_rx_pkt_timestamp_cpacket_many(void) pkt_cpacket(pkt, sec, nsec); STATE_STASH(buf); - ci_rx_pkt_timestamp_cpacket(pkt, ts); + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET); STATE_CHECK(ts, sec, sec); STATE_CHECK(ts, nsec, nsec); STATE_CHECK(ts, nsec_frac, subnano); @@ -248,7 +288,7 @@ static void test_rx_pkt_timestamp_cpacket_bogus(void) pkt_cpacket(pkt, sec, nsec); STATE_STASH(buf); - ci_rx_pkt_timestamp_cpacket(pkt, ts); + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET); STATE_CHECK(ts, sec, sec); STATE_CHECK(ts, nsec, nsec); STATE_CHECK(ts, nsec_frac, 0); /* didn't find it, but no disastrous outcome */ @@ -369,7 +409,7 @@ static void test_rx_pkt_timestamp_cpacket_pcap_metawatch(void) pkt_append(pkt, pcap, sizeof(pcap)); STATE_STASH(buf); - ci_rx_pkt_timestamp_cpacket(pkt, ts); + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET); STATE_CHECK(ts, sec, 0x5c9a4c08); STATE_CHECK(ts, nsec, 0x313ac683); STATE_CHECK(ts, nsec_frac, 0x536c8b); @@ -433,7 +473,7 @@ static void test_rx_pkt_timestamp_cpacket_pcap_wireshark(void) pkt_append(pkt, pcap, sizeof(pcap)); STATE_STASH(buf); - ci_rx_pkt_timestamp_cpacket(pkt, ts); + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_CPACKET); STATE_CHECK(ts, sec, 0x40a34b24); STATE_CHECK(ts, nsec, 0x0d4399db); STATE_CHECK(ts, nsec_frac, 0x2c52de); @@ -442,6 +482,56 @@ static void test_rx_pkt_timestamp_cpacket_pcap_wireshark(void) STATE_FREE(ts); } +static void test_rx_pkt_timestamp_trailer_ttag(void) +{ + const uint64_t nsec_per_sec = 1000ULL * 1000 * 1000; + const uint64_t max_48b_nsec = (1UL << 48) - 1; + ci_ip_pkt_fmt* pkt; + uint32_t sec = (max_48b_nsec / nsec_per_sec) - 1; + uint32_t nsec = 999999999; + + STATE_ALLOC(union pkt_buf, buf); + STATE_ALLOC(struct onload_timestamp, ts); + + pkt = &buf->pkt; + pkt_init(pkt); + pkt_udp(pkt, NULL, 0); + pkt_ttag(pkt, sec, nsec); + STATE_STASH(buf); + + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_TTAG); + STATE_CHECK(ts, sec, sec); + STATE_CHECK(ts, nsec, nsec); + STATE_CHECK(ts, nsec_frac, 0); + + STATE_FREE(buf); + STATE_FREE(ts); +} + +static void test_rx_pkt_timestamp_trailer_brcm(void) +{ + ci_ip_pkt_fmt* pkt; + uint32_t sec = 0x3FFFF; /* largest 18b value */ + uint32_t nsec = 999999999; + + STATE_ALLOC(union pkt_buf, buf); + STATE_ALLOC(struct onload_timestamp, ts); + + pkt = &buf->pkt; + pkt_init(pkt); + pkt_udp(pkt, NULL, 0); + pkt_trailer_brcm(pkt, sec, nsec); + STATE_STASH(buf); + + ci_rx_pkt_timestamp_trailer(pkt, ts, CITP_RX_TIMESTAMPING_TRAILER_FORMAT_BRCM); + STATE_CHECK(ts, sec, sec); + STATE_CHECK(ts, nsec, nsec); + STATE_CHECK(ts, nsec_frac, 0); + + STATE_FREE(buf); + STATE_FREE(ts); +} + int main(void) { TEST_RUN(test_rx_pkt_timestamp_cpacket_none); TEST_RUN(test_rx_pkt_timestamp_cpacket_basic); @@ -450,5 +540,7 @@ int main(void) { TEST_RUN(test_rx_pkt_timestamp_cpacket_bogus); TEST_RUN(test_rx_pkt_timestamp_cpacket_pcap_metawatch); TEST_RUN(test_rx_pkt_timestamp_cpacket_pcap_wireshark); + TEST_RUN(test_rx_pkt_timestamp_trailer_ttag); + TEST_RUN(test_rx_pkt_timestamp_trailer_brcm); TEST_END(); }