Add autotest for the PTP library covering: - Transport classification (L2, VLAN, QinQ, UDP/IPv4, UDP/IPv6) - Message type parsing and string conversion - Inline helpers (is_event, two_step, seq_id, version, domain) - Correction field arithmetic (add, accumulate, negative values) - Timestamp nanosecond conversion - Flag field extraction (TWO_STEP, UNICAST, LI_61, LI_59)
Signed-off-by: Rajesh Kumar <[email protected]> --- MAINTAINERS | 1 + app/test/meson.build | 1 + app/test/test_ptp.c | 1106 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1108 insertions(+) create mode 100644 app/test/test_ptp.c diff --git a/MAINTAINERS b/MAINTAINERS index 665e08dc90..8d509cbe8e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1668,6 +1668,7 @@ F: app/test-sad/ PTP - EXPERIMENTAL M: Rajesh Kumar <[email protected]> F: lib/ptp/ +F: app/test/test_ptp.c F: doc/guides/prog_guide/ptp_lib.rst F: examples/ptp_tap_relay_sw/ F: doc/guides/sample_app_ug/ptp_tap_relay_sw.rst diff --git a/app/test/meson.build b/app/test/meson.build index 7d458f9c07..ecaa8d0839 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -154,6 +154,7 @@ source_file_deps = { 'test_power_kvm_vm.c': ['power', 'power_kvm_vm'], 'test_prefetch.c': [], 'test_ptr_compress.c': ['ptr_compress'], + 'test_ptp.c': ['ptp'], 'test_rand_perf.c': [], 'test_rawdev.c': ['rawdev', 'bus_vdev', 'raw_skeleton'], 'test_rcu_qsbr.c': ['rcu', 'hash'], diff --git a/app/test/test_ptp.c b/app/test/test_ptp.c new file mode 100644 index 0000000000..803951b1fa --- /dev/null +++ b/app/test/test_ptp.c @@ -0,0 +1,1106 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2026 Intel Corporation + */ + +#include "test.h" + +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include <inttypes.h> + +#include <rte_mbuf.h> +#include <rte_mempool.h> +#include <rte_memory.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> +#include <rte_byteorder.h> +#include <rte_ptp.h> + +#define PTP_TEST_MP_NAME "test_ptp_pool" +#define PTP_TEST_MP_SIZE 63 +#define PTP_TEST_BUF_SIZE RTE_MBUF_DEFAULT_BUF_SIZE + +static struct rte_mempool *ptp_mp; + +/* Helper: fill a minimal PTP header */ +static void +fill_ptp_hdr(struct rte_ptp_hdr *ptp, uint8_t msg_type, uint16_t flags_host, + int64_t correction_scaled_ns, uint16_t seq_id) +{ + memset(ptp, 0, sizeof(*ptp)); + ptp->msg_type = msg_type; + ptp->version = 0x02; + ptp->msg_length = rte_cpu_to_be_16(34); + ptp->flags = rte_cpu_to_be_16(flags_host); + ptp->correction = rte_cpu_to_be_64(correction_scaled_ns); + ptp->sequence_id = rte_cpu_to_be_16(seq_id); +} + +/* ================================================================ + * Packet builders + * ================================================================ + */ + +static struct rte_mbuf * +build_l2_ptp(uint8_t msg_type) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *) + rte_pktmbuf_append(m, sizeof(*eth) + sizeof(struct rte_ptp_hdr)); + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(0x88F7); + + struct rte_ptp_hdr *ptp = (struct rte_ptp_hdr *)((uint8_t *)eth + sizeof(*eth)); + fill_ptp_hdr(ptp, msg_type, RTE_PTP_FLAG_TWO_STEP, 0, 100); + return m; +} + +/* Build L2 PTP with no TWO_STEP flag */ +static struct rte_mbuf * +build_l2_ptp_noflags(uint8_t msg_type) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *) + rte_pktmbuf_append(m, sizeof(*eth) + sizeof(struct rte_ptp_hdr)); + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(0x88F7); + + struct rte_ptp_hdr *ptp = (struct rte_ptp_hdr *)((uint8_t *)eth + sizeof(*eth)); + fill_ptp_hdr(ptp, msg_type, 0, 0, 200); + return m; +} + +static struct rte_mbuf * +build_vlan_l2_ptp(uint8_t msg_type, uint16_t tpid) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + sizeof(struct rte_vlan_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(tpid); + + struct rte_vlan_hdr *vlan = (struct rte_vlan_hdr *)(data + sizeof(*eth)); + vlan->vlan_tci = rte_cpu_to_be_16(100); + vlan->eth_proto = rte_cpu_to_be_16(0x88F7); + + struct rte_ptp_hdr *ptp = (struct rte_ptp_hdr *) + (data + sizeof(*eth) + sizeof(*vlan)); + fill_ptp_hdr(ptp, msg_type, 0, 0, 200); + return m; +} + +static struct rte_mbuf * +build_qinq_l2_ptp(uint8_t msg_type, uint16_t outer_tpid, uint16_t inner_tpid) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + 2 * sizeof(struct rte_vlan_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(outer_tpid); + + uint32_t off = sizeof(*eth); + struct rte_vlan_hdr *vo = (struct rte_vlan_hdr *)(data + off); + vo->vlan_tci = rte_cpu_to_be_16(200); + vo->eth_proto = rte_cpu_to_be_16(inner_tpid); + off += sizeof(*vo); + + struct rte_vlan_hdr *vi = (struct rte_vlan_hdr *)(data + off); + vi->vlan_tci = rte_cpu_to_be_16(300); + vi->eth_proto = rte_cpu_to_be_16(0x88F7); + off += sizeof(*vi); + + struct rte_ptp_hdr *ptp = (struct rte_ptp_hdr *)(data + off); + fill_ptp_hdr(ptp, msg_type, 0, 0, 300); + return m; +} + +/* Helper: append IPv4 + UDP + PTP after offset */ +static void +fill_ipv4_udp_ptp(uint8_t *data, uint32_t off, uint8_t msg_type, + uint16_t dst_port, uint16_t seq_id) +{ + struct rte_ipv4_hdr *iph = (struct rte_ipv4_hdr *)(data + off); + memset(iph, 0, sizeof(*iph)); + iph->version_ihl = 0x45; + iph->next_proto_id = IPPROTO_UDP; + iph->total_length = rte_cpu_to_be_16( + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr)); + iph->src_addr = rte_cpu_to_be_32(0x0A000001); + iph->dst_addr = rte_cpu_to_be_32(0xE0000181); + off += sizeof(*iph); + + struct rte_udp_hdr *udp = (struct rte_udp_hdr *)(data + off); + memset(udp, 0, sizeof(*udp)); + udp->src_port = rte_cpu_to_be_16(12345); + udp->dst_port = rte_cpu_to_be_16(dst_port); + udp->dgram_len = rte_cpu_to_be_16(sizeof(*udp) + + sizeof(struct rte_ptp_hdr)); + off += sizeof(*udp); + + struct rte_ptp_hdr *ptp = (struct rte_ptp_hdr *)(data + off); + fill_ptp_hdr(ptp, msg_type, 0, 0, seq_id); +} + +static struct rte_mbuf * +build_ipv4_udp_ptp(uint8_t msg_type, uint16_t dst_port) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + sizeof(struct rte_ipv4_hdr) + + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); + + fill_ipv4_udp_ptp(data, sizeof(*eth), msg_type, dst_port, 400); + return m; +} + +static struct rte_mbuf * +build_vlan_ipv4_udp_ptp(uint8_t msg_type, uint16_t dst_port) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + sizeof(struct rte_vlan_hdr) + + sizeof(struct rte_ipv4_hdr) + + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN); + + uint32_t off = sizeof(*eth); + struct rte_vlan_hdr *vlan = (struct rte_vlan_hdr *)(data + off); + vlan->vlan_tci = rte_cpu_to_be_16(100); + vlan->eth_proto = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); + off += sizeof(*vlan); + + fill_ipv4_udp_ptp(data, off, msg_type, dst_port, 500); + return m; +} + +static struct rte_mbuf * +build_qinq_ipv4_udp_ptp(uint8_t msg_type, uint16_t dst_port) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + 2 * sizeof(struct rte_vlan_hdr) + + sizeof(struct rte_ipv4_hdr) + + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ); + + uint32_t off = sizeof(*eth); + struct rte_vlan_hdr *vo = (struct rte_vlan_hdr *)(data + off); + vo->vlan_tci = rte_cpu_to_be_16(200); + vo->eth_proto = rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN); + off += sizeof(*vo); + + struct rte_vlan_hdr *vi = (struct rte_vlan_hdr *)(data + off); + vi->vlan_tci = rte_cpu_to_be_16(300); + vi->eth_proto = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); + off += sizeof(*vi); + + fill_ipv4_udp_ptp(data, off, msg_type, dst_port, 600); + return m; +} + +/* Helper: append IPv6 + UDP + PTP */ +static void +fill_ipv6_udp_ptp(uint8_t *data, uint32_t off, uint8_t msg_type, + uint16_t dst_port, uint16_t seq_id) +{ + struct rte_ipv6_hdr *ip6 = (struct rte_ipv6_hdr *)(data + off); + memset(ip6, 0, sizeof(*ip6)); + ip6->vtc_flow = rte_cpu_to_be_32(0x60000000); + ip6->payload_len = rte_cpu_to_be_16( + sizeof(struct rte_udp_hdr) + sizeof(struct rte_ptp_hdr)); + ip6->proto = IPPROTO_UDP; + ip6->hop_limits = 64; + off += sizeof(*ip6); + + struct rte_udp_hdr *udp = (struct rte_udp_hdr *)(data + off); + memset(udp, 0, sizeof(*udp)); + udp->src_port = rte_cpu_to_be_16(12345); + udp->dst_port = rte_cpu_to_be_16(dst_port); + udp->dgram_len = rte_cpu_to_be_16(sizeof(*udp) + + sizeof(struct rte_ptp_hdr)); + off += sizeof(*udp); + + struct rte_ptp_hdr *ptp = (struct rte_ptp_hdr *)(data + off); + fill_ptp_hdr(ptp, msg_type, 0, 0, seq_id); +} + +static struct rte_mbuf * +build_ipv6_udp_ptp(uint8_t msg_type, uint16_t dst_port) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + sizeof(struct rte_ipv6_hdr) + + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6); + + fill_ipv6_udp_ptp(data, sizeof(*eth), msg_type, dst_port, 700); + return m; +} + +static struct rte_mbuf * +build_vlan_ipv6_udp_ptp(uint8_t msg_type, uint16_t dst_port) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + sizeof(struct rte_vlan_hdr) + + sizeof(struct rte_ipv6_hdr) + + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN); + + uint32_t off = sizeof(*eth); + struct rte_vlan_hdr *vlan = (struct rte_vlan_hdr *)(data + off); + vlan->vlan_tci = rte_cpu_to_be_16(100); + vlan->eth_proto = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6); + off += sizeof(*vlan); + + fill_ipv6_udp_ptp(data, off, msg_type, dst_port, 800); + return m; +} + +static struct rte_mbuf * +build_qinq_ipv6_udp_ptp(uint8_t msg_type, uint16_t dst_port) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + 2 * sizeof(struct rte_vlan_hdr) + + sizeof(struct rte_ipv6_hdr) + + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ); + + uint32_t off = sizeof(*eth); + struct rte_vlan_hdr *vo = (struct rte_vlan_hdr *)(data + off); + vo->vlan_tci = rte_cpu_to_be_16(200); + vo->eth_proto = rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN); + off += sizeof(*vo); + + struct rte_vlan_hdr *vi = (struct rte_vlan_hdr *)(data + off); + vi->vlan_tci = rte_cpu_to_be_16(300); + vi->eth_proto = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6); + off += sizeof(*vi); + + fill_ipv6_udp_ptp(data, off, msg_type, dst_port, 900); + return m; +} + +static struct rte_mbuf * +build_non_ptp(void) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + 28; + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(data, 0, pkt_len); + eth->ether_type = rte_cpu_to_be_16(0x0806); + return m; +} + +static struct rte_mbuf * +build_ipv4_udp_non_ptp(void) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + if (!m) + return NULL; + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + sizeof(struct rte_ipv4_hdr) + + sizeof(struct rte_udp_hdr) + 20; + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(data, 0, pkt_len); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); + + struct rte_ipv4_hdr *iph = (struct rte_ipv4_hdr *)(data + sizeof(*eth)); + iph->version_ihl = 0x45; + iph->next_proto_id = IPPROTO_UDP; + iph->total_length = rte_cpu_to_be_16( + sizeof(*iph) + sizeof(struct rte_udp_hdr) + 20); + + struct rte_udp_hdr *udp = (struct rte_udp_hdr *) + (data + sizeof(*eth) + sizeof(*iph)); + udp->dst_port = rte_cpu_to_be_16(53); + return m; +} + +/* ================================================================ + * Individual test cases + * ================================================================ + */ + +/* Helper: classify + hdr_get for a given mbuf */ +static int +check_classify_and_hdr_get(struct rte_mbuf *m, int expected_type) +{ + int ret; + + TEST_ASSERT_NOT_NULL(m, "mbuf allocation failed"); + + ret = rte_ptp_classify(m); + TEST_ASSERT_EQUAL(ret, expected_type, + "classify: expected %d, got %d", expected_type, ret); + + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + if (expected_type == RTE_PTP_MSGTYPE_INVALID) { + TEST_ASSERT_NULL(hdr, + "hdr_get: expected NULL for non-PTP packet"); + } else { + TEST_ASSERT_NOT_NULL(hdr, + "hdr_get: expected non-NULL for PTP packet"); + TEST_ASSERT_EQUAL(rte_ptp_msg_type(hdr), + (uint8_t)expected_type, + "hdr_get: msg_type mismatch: expected %d, got %d", + expected_type, rte_ptp_msg_type(hdr)); + } + + rte_pktmbuf_free(m); + return TEST_SUCCESS; +} + +/* Section 1: Transport classification */ +static int +test_ptp_classify_l2(void) +{ + return check_classify_and_hdr_get( + build_l2_ptp(RTE_PTP_MSGTYPE_SYNC), RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_l2_delay_req(void) +{ + return check_classify_and_hdr_get( + build_l2_ptp(RTE_PTP_MSGTYPE_DELAY_REQ), + RTE_PTP_MSGTYPE_DELAY_REQ); +} + +static int +test_ptp_classify_vlan_8100(void) +{ + return check_classify_and_hdr_get( + build_vlan_l2_ptp(RTE_PTP_MSGTYPE_SYNC, RTE_ETHER_TYPE_VLAN), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_vlan_88a8(void) +{ + return check_classify_and_hdr_get( + build_vlan_l2_ptp(RTE_PTP_MSGTYPE_SYNC, RTE_ETHER_TYPE_QINQ), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_qinq(void) +{ + return check_classify_and_hdr_get( + build_qinq_l2_ptp(RTE_PTP_MSGTYPE_SYNC, + RTE_ETHER_TYPE_QINQ, RTE_ETHER_TYPE_VLAN), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_double_8100(void) +{ + return check_classify_and_hdr_get( + build_qinq_l2_ptp(RTE_PTP_MSGTYPE_SYNC, + RTE_ETHER_TYPE_VLAN, RTE_ETHER_TYPE_VLAN), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_ipv4_udp_319(void) +{ + return check_classify_and_hdr_get( + build_ipv4_udp_ptp(RTE_PTP_MSGTYPE_SYNC, 319), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_ipv4_udp_320(void) +{ + return check_classify_and_hdr_get( + build_ipv4_udp_ptp(RTE_PTP_MSGTYPE_FOLLOW_UP, 320), + RTE_PTP_MSGTYPE_FOLLOW_UP); +} + +static int +test_ptp_classify_vlan_ipv4_udp(void) +{ + return check_classify_and_hdr_get( + build_vlan_ipv4_udp_ptp(RTE_PTP_MSGTYPE_SYNC, 319), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_qinq_ipv4_udp(void) +{ + return check_classify_and_hdr_get( + build_qinq_ipv4_udp_ptp(RTE_PTP_MSGTYPE_SYNC, 319), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_ipv6_udp_319(void) +{ + return check_classify_and_hdr_get( + build_ipv6_udp_ptp(RTE_PTP_MSGTYPE_SYNC, 319), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_ipv6_udp_320(void) +{ + return check_classify_and_hdr_get( + build_ipv6_udp_ptp(RTE_PTP_MSGTYPE_FOLLOW_UP, 320), + RTE_PTP_MSGTYPE_FOLLOW_UP); +} + +static int +test_ptp_classify_vlan_ipv6_udp(void) +{ + return check_classify_and_hdr_get( + build_vlan_ipv6_udp_ptp(RTE_PTP_MSGTYPE_SYNC, 319), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_qinq_ipv6_udp(void) +{ + return check_classify_and_hdr_get( + build_qinq_ipv6_udp_ptp(RTE_PTP_MSGTYPE_SYNC, 319), + RTE_PTP_MSGTYPE_SYNC); +} + +static int +test_ptp_classify_non_ptp_arp(void) +{ + return check_classify_and_hdr_get( + build_non_ptp(), RTE_PTP_MSGTYPE_INVALID); +} + +static int +test_ptp_classify_non_ptp_udp(void) +{ + return check_classify_and_hdr_get( + build_ipv4_udp_non_ptp(), RTE_PTP_MSGTYPE_INVALID); +} + +/* IPv4 with invalid IHL (< 5) should be rejected */ +static int +test_ptp_classify_ipv4_bad_ihl(void) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + + uint32_t pkt_len = sizeof(struct rte_ether_hdr) + + sizeof(struct rte_ipv4_hdr) + + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr); + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); + + fill_ipv4_udp_ptp(data, sizeof(*eth), RTE_PTP_MSGTYPE_SYNC, 319, 999); + + /* Corrupt the IHL to 3 (< minimum 5) */ + struct rte_ipv4_hdr *iph = (struct rte_ipv4_hdr *) + (data + sizeof(*eth)); + iph->version_ihl = 0x43; + + return check_classify_and_hdr_get(m, RTE_PTP_MSGTYPE_INVALID); +} + +/* IPv4 with options (IHL > 5) should still be parsed correctly */ +static int +test_ptp_classify_ipv4_options(void) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + uint32_t pkt_len; + uint32_t off; + uint8_t *data; + struct rte_ether_hdr *eth; + struct rte_ipv4_hdr *iph; + struct rte_udp_hdr *udp; + struct rte_ptp_hdr *ptp; + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + + pkt_len = sizeof(struct rte_ether_hdr) + + sizeof(struct rte_ipv4_hdr) + 4 + + sizeof(struct rte_udp_hdr) + + sizeof(struct rte_ptp_hdr); + data = (uint8_t *)rte_pktmbuf_append(m, pkt_len); + TEST_ASSERT_NOT_NULL(data, "append failed"); + + eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); + + off = sizeof(*eth); + iph = (struct rte_ipv4_hdr *)(data + off); + memset(iph, 0, sizeof(*iph)); + iph->version_ihl = 0x46; /* IHL = 6 words = 24 bytes */ + iph->next_proto_id = IPPROTO_UDP; + iph->total_length = rte_cpu_to_be_16(sizeof(struct rte_ipv4_hdr) + 4 + + sizeof(struct rte_udp_hdr) + sizeof(struct rte_ptp_hdr)); + iph->src_addr = rte_cpu_to_be_32(0x0A000001); + iph->dst_addr = rte_cpu_to_be_32(0xE0000181); + off += sizeof(*iph); + + memset(data + off, 0, 4); /* IPv4 options bytes */ + off += 4; + + udp = (struct rte_udp_hdr *)(data + off); + memset(udp, 0, sizeof(*udp)); + udp->src_port = rte_cpu_to_be_16(12345); + udp->dst_port = rte_cpu_to_be_16(RTE_PTP_EVENT_PORT); + udp->dgram_len = rte_cpu_to_be_16(sizeof(*udp) + sizeof(struct rte_ptp_hdr)); + off += sizeof(*udp); + + ptp = (struct rte_ptp_hdr *)(data + off); + fill_ptp_hdr(ptp, RTE_PTP_MSGTYPE_SYNC, 0, 0, 1001); + + return check_classify_and_hdr_get(m, RTE_PTP_MSGTYPE_SYNC); +} + +/* Truncated packet: Ethernet header only, no payload */ +static int +test_ptp_classify_truncated(void) +{ + struct rte_mbuf *m = rte_pktmbuf_alloc(ptp_mp); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + + uint8_t *data = (uint8_t *)rte_pktmbuf_append(m, + sizeof(struct rte_ether_hdr)); + struct rte_ether_hdr *eth = (struct rte_ether_hdr *)data; + memset(eth, 0, sizeof(*eth)); + eth->ether_type = rte_cpu_to_be_16(0x88F7); + + return check_classify_and_hdr_get(m, RTE_PTP_MSGTYPE_INVALID); +} + +/* Section 2: All 10 message types via L2 */ +static int +test_ptp_all_msg_types(void) +{ + static const uint8_t types[] = { + RTE_PTP_MSGTYPE_SYNC, + RTE_PTP_MSGTYPE_DELAY_REQ, + RTE_PTP_MSGTYPE_PDELAY_REQ, + RTE_PTP_MSGTYPE_PDELAY_RESP, + RTE_PTP_MSGTYPE_FOLLOW_UP, + RTE_PTP_MSGTYPE_DELAY_RESP, + RTE_PTP_MSGTYPE_PDELAY_RESP_FU, + RTE_PTP_MSGTYPE_ANNOUNCE, + RTE_PTP_MSGTYPE_SIGNALING, + RTE_PTP_MSGTYPE_MANAGEMENT, + }; + unsigned int i; + + for (i = 0; i < RTE_DIM(types); i++) { + int ret = check_classify_and_hdr_get( + build_l2_ptp(types[i]), types[i]); + if (ret != TEST_SUCCESS) + return ret; + } + + return TEST_SUCCESS; +} + +/* Section 3: Inline helpers */ +static int +test_ptp_is_event(void) +{ + TEST_ASSERT(rte_ptp_is_event(RTE_PTP_MSGTYPE_SYNC), + "Sync should be event"); + TEST_ASSERT(rte_ptp_is_event(RTE_PTP_MSGTYPE_DELAY_REQ), + "Delay_Req should be event"); + TEST_ASSERT(rte_ptp_is_event(RTE_PTP_MSGTYPE_PDELAY_REQ), + "Pdelay_Req should be event"); + TEST_ASSERT(rte_ptp_is_event(RTE_PTP_MSGTYPE_PDELAY_RESP), + "Pdelay_Resp should be event"); + TEST_ASSERT(!rte_ptp_is_event(RTE_PTP_MSGTYPE_FOLLOW_UP), + "Follow_Up should not be event"); + TEST_ASSERT(!rte_ptp_is_event(RTE_PTP_MSGTYPE_ANNOUNCE), + "Announce should not be event"); + TEST_ASSERT(!rte_ptp_is_event(RTE_PTP_MSGTYPE_INVALID), + "INVALID (-1) should not be event"); + + return TEST_SUCCESS; +} + +static int +test_ptp_two_step(void) +{ + struct rte_mbuf *m; + struct rte_ptp_hdr *hdr; + + m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + TEST_ASSERT(rte_ptp_is_two_step(hdr), + "TWO_STEP flag should be set"); + rte_pktmbuf_free(m); + + m = build_l2_ptp_noflags(RTE_PTP_MSGTYPE_SYNC); + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + TEST_ASSERT(!rte_ptp_is_two_step(hdr), + "TWO_STEP flag should not be set"); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +static int +test_ptp_seq_id(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + TEST_ASSERT_EQUAL(rte_ptp_seq_id(hdr), 100, + "seq_id: expected 100, got %u", rte_ptp_seq_id(hdr)); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +static int +test_ptp_version(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + TEST_ASSERT_EQUAL(rte_ptp_version(hdr), 2, + "version: expected 2, got %u", rte_ptp_version(hdr)); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +static int +test_ptp_domain(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + TEST_ASSERT_EQUAL(rte_ptp_domain(hdr), 0, + "domain: expected 0, got %u", rte_ptp_domain(hdr)); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +/* Section 4: correctionField */ +static int +test_ptp_correction_zero(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + int64_t ns = rte_ptp_correction_ns(hdr); + TEST_ASSERT_EQUAL(ns, 0, + "correction_ns: expected 0, got %" PRId64, ns); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +static int +test_ptp_correction_known(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *ptp = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(ptp, "hdr_get failed"); + int64_t scaled_1000 = (int64_t)1000 << 16; + fill_ptp_hdr(ptp, RTE_PTP_MSGTYPE_SYNC, 0, scaled_1000, 0); + + int64_t ns = rte_ptp_correction_ns(ptp); + TEST_ASSERT_EQUAL(ns, 1000, + "correction_ns: expected 1000, got %" PRId64, ns); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +static int +test_ptp_add_correction(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + + rte_ptp_add_correction(hdr, 500); + int64_t ns = rte_ptp_correction_ns(hdr); + TEST_ASSERT_EQUAL(ns, 500, + "add 500: expected 500, got %" PRId64, ns); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +static int +test_ptp_add_correction_accumulate(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + + rte_ptp_add_correction(hdr, 300); + rte_ptp_add_correction(hdr, 700); + int64_t ns = rte_ptp_correction_ns(hdr); + TEST_ASSERT_EQUAL(ns, 1000, + "accumulate: expected 1000, got %" PRId64, ns); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +static int +test_ptp_add_correction_large(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + + rte_ptp_add_correction(hdr, 1000000000LL); + int64_t ns = rte_ptp_correction_ns(hdr); + TEST_ASSERT_EQUAL(ns, 1000000000LL, + "1s: expected 1000000000, got %" PRId64, ns); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +static int +test_ptp_add_correction_negative(void) +{ + struct rte_mbuf *m = build_l2_ptp(RTE_PTP_MSGTYPE_SYNC); + + TEST_ASSERT_NOT_NULL(m, "alloc failed"); + struct rte_ptp_hdr *hdr = rte_ptp_hdr_get(m); + TEST_ASSERT_NOT_NULL(hdr, "hdr_get failed"); + + rte_ptp_add_correction(hdr, -100LL); + int64_t ns = rte_ptp_correction_ns(hdr); + TEST_ASSERT_EQUAL(ns, -100LL, + "negative: expected -100, got %" PRId64, ns); + rte_pktmbuf_free(m); + + return TEST_SUCCESS; +} + +/* Section 5: Timestamp conversion */ +static int +test_ptp_timestamp_to_ns(void) +{ + struct rte_ptp_timestamp ts; + uint64_t ns; + + /* Zero */ + memset(&ts, 0, sizeof(ts)); + ns = rte_ptp_timestamp_to_ns(&ts); + TEST_ASSERT_EQUAL(ns, 0ULL, + "zero: expected 0, got %" PRIu64, ns); + + /* 1 second */ + ts.seconds_hi = 0; + ts.seconds_lo = rte_cpu_to_be_32(1); + ts.nanoseconds = 0; + ns = rte_ptp_timestamp_to_ns(&ts); + TEST_ASSERT_EQUAL(ns, 1000000000ULL, + "1s: expected 1000000000, got %" PRIu64, ns); + + /* 1.5 seconds */ + ts.seconds_lo = rte_cpu_to_be_32(1); + ts.nanoseconds = rte_cpu_to_be_32(500000000); + ns = rte_ptp_timestamp_to_ns(&ts); + TEST_ASSERT_EQUAL(ns, 1500000000ULL, + "1.5s: expected 1500000000, got %" PRIu64, ns); + + /* Large value with seconds_hi */ + ts.seconds_hi = rte_cpu_to_be_16(1); + ts.seconds_lo = 0; + ts.nanoseconds = 0; + ns = rte_ptp_timestamp_to_ns(&ts); + uint64_t expected = ((uint64_t)1 << 32) * 1000000000ULL; + TEST_ASSERT_EQUAL(ns, expected, + "2^32s: expected %" PRIu64 ", got %" PRIu64, expected, ns); + + return TEST_SUCCESS; +} + +/* Section 6: msg_type_str */ +static int +test_ptp_msg_type_str(void) +{ + static const struct { + int type; + const char *expected; + } cases[] = { + { RTE_PTP_MSGTYPE_SYNC, "Sync" }, + { RTE_PTP_MSGTYPE_DELAY_REQ, "Delay_Req" }, + { RTE_PTP_MSGTYPE_PDELAY_REQ, "PDelay_Req" }, + { RTE_PTP_MSGTYPE_PDELAY_RESP, "PDelay_Resp" }, + { RTE_PTP_MSGTYPE_FOLLOW_UP, "Follow_Up" }, + { RTE_PTP_MSGTYPE_DELAY_RESP, "Delay_Resp" }, + { RTE_PTP_MSGTYPE_PDELAY_RESP_FU, "PDelay_Resp_Follow_Up" }, + { RTE_PTP_MSGTYPE_ANNOUNCE, "Announce" }, + { RTE_PTP_MSGTYPE_SIGNALING, "Signaling" }, + { RTE_PTP_MSGTYPE_MANAGEMENT, "Management" }, + }; + unsigned int i; + + for (i = 0; i < RTE_DIM(cases); i++) { + const char *str = rte_ptp_msg_type_str(cases[i].type); + TEST_ASSERT_NOT_NULL(str, + "msg_type_str(%d) returned NULL", cases[i].type); + TEST_ASSERT(strcmp(str, cases[i].expected) == 0, + "msg_type_str(%d): expected \"%s\", got \"%s\"", + cases[i].type, cases[i].expected, str); + } + + /* Invalid type should still return non-NULL */ + const char *inv = rte_ptp_msg_type_str(RTE_PTP_MSGTYPE_INVALID); + TEST_ASSERT_NOT_NULL(inv, + "msg_type_str(INVALID) returned NULL"); + + return TEST_SUCCESS; +} + +/* Section 7: Flag bit positions */ +static int +test_ptp_flags(void) +{ + struct rte_ptp_hdr hdr; + uint16_t f; + + /* TWO_STEP */ + memset(&hdr, 0, sizeof(hdr)); + hdr.flags = rte_cpu_to_be_16(RTE_PTP_FLAG_TWO_STEP); + f = rte_be_to_cpu_16(hdr.flags); + TEST_ASSERT(f & RTE_PTP_FLAG_TWO_STEP, + "TWO_STEP bit not set: 0x%04x", f); + TEST_ASSERT(rte_ptp_is_two_step(&hdr), + "is_two_step() should return true"); + + /* UNICAST */ + memset(&hdr, 0, sizeof(hdr)); + hdr.flags = rte_cpu_to_be_16(RTE_PTP_FLAG_UNICAST); + f = rte_be_to_cpu_16(hdr.flags); + TEST_ASSERT(f & RTE_PTP_FLAG_UNICAST, + "UNICAST bit not set: 0x%04x", f); + + /* LI_61 */ + memset(&hdr, 0, sizeof(hdr)); + hdr.flags = rte_cpu_to_be_16(RTE_PTP_FLAG_LI_61); + f = rte_be_to_cpu_16(hdr.flags); + TEST_ASSERT(f & RTE_PTP_FLAG_LI_61, + "LI_61 bit not set: 0x%04x", f); + + /* LI_59 */ + memset(&hdr, 0, sizeof(hdr)); + hdr.flags = rte_cpu_to_be_16(RTE_PTP_FLAG_LI_59); + f = rte_be_to_cpu_16(hdr.flags); + TEST_ASSERT(f & RTE_PTP_FLAG_LI_59, + "LI_59 bit not set: 0x%04x", f); + + /* Combined TWO_STEP + UNICAST */ + memset(&hdr, 0, sizeof(hdr)); + hdr.flags = rte_cpu_to_be_16( + RTE_PTP_FLAG_TWO_STEP | RTE_PTP_FLAG_UNICAST); + f = rte_be_to_cpu_16(hdr.flags); + TEST_ASSERT((f & RTE_PTP_FLAG_TWO_STEP) && + (f & RTE_PTP_FLAG_UNICAST) && + !(f & RTE_PTP_FLAG_LI_61) && + !(f & RTE_PTP_FLAG_LI_59), + "combined flags incorrect: 0x%04x", f); + + return TEST_SUCCESS; +} + +/* ================================================================ + * Suite setup / teardown + * ================================================================ + */ + +static int +test_ptp_setup(void) +{ + ptp_mp = rte_pktmbuf_pool_create(PTP_TEST_MP_NAME, PTP_TEST_MP_SIZE, + 0, 0, PTP_TEST_BUF_SIZE, SOCKET_ID_ANY); + if (ptp_mp == NULL) { + printf("Cannot create ptp test mempool\n"); + return TEST_FAILED; + } + return TEST_SUCCESS; +} + +static void +test_ptp_teardown(void) +{ + rte_mempool_free(ptp_mp); + ptp_mp = NULL; +} + +static struct unit_test_suite ptp_test_suite = { + .suite_name = "PTP Library Unit Tests", + .setup = test_ptp_setup, + .teardown = test_ptp_teardown, + .unit_test_cases = { + /* Transport classification */ + TEST_CASE(test_ptp_classify_l2), + TEST_CASE(test_ptp_classify_l2_delay_req), + TEST_CASE(test_ptp_classify_vlan_8100), + TEST_CASE(test_ptp_classify_vlan_88a8), + TEST_CASE(test_ptp_classify_qinq), + TEST_CASE(test_ptp_classify_double_8100), + TEST_CASE(test_ptp_classify_ipv4_udp_319), + TEST_CASE(test_ptp_classify_ipv4_udp_320), + TEST_CASE(test_ptp_classify_vlan_ipv4_udp), + TEST_CASE(test_ptp_classify_qinq_ipv4_udp), + TEST_CASE(test_ptp_classify_ipv6_udp_319), + TEST_CASE(test_ptp_classify_ipv6_udp_320), + TEST_CASE(test_ptp_classify_vlan_ipv6_udp), + TEST_CASE(test_ptp_classify_qinq_ipv6_udp), + TEST_CASE(test_ptp_classify_non_ptp_arp), + TEST_CASE(test_ptp_classify_non_ptp_udp), + TEST_CASE(test_ptp_classify_ipv4_bad_ihl), + TEST_CASE(test_ptp_classify_ipv4_options), + TEST_CASE(test_ptp_classify_truncated), + + /* All message types */ + TEST_CASE(test_ptp_all_msg_types), + + /* Inline helpers */ + TEST_CASE(test_ptp_is_event), + TEST_CASE(test_ptp_two_step), + TEST_CASE(test_ptp_seq_id), + TEST_CASE(test_ptp_version), + TEST_CASE(test_ptp_domain), + + /* correctionField */ + TEST_CASE(test_ptp_correction_zero), + TEST_CASE(test_ptp_correction_known), + TEST_CASE(test_ptp_add_correction), + TEST_CASE(test_ptp_add_correction_accumulate), + TEST_CASE(test_ptp_add_correction_large), + TEST_CASE(test_ptp_add_correction_negative), + + /* Timestamp conversion */ + TEST_CASE(test_ptp_timestamp_to_ns), + + /* msg_type_str */ + TEST_CASE(test_ptp_msg_type_str), + + /* Flag field bit positions */ + TEST_CASE(test_ptp_flags), + + TEST_CASES_END() + }, +}; + +static int +test_ptp(void) +{ + return unit_test_suite_runner(&ptp_test_suite); +} + +REGISTER_FAST_TEST(ptp_autotest, NOHUGE_SKIP, ASAN_OK, test_ptp); -- 2.53.0

