From 11124141cb3282fc80aae20e1f0e6f3b95a60202 Mon Sep 17 00:00:00 2001 From: wenquan1 Date: Mon, 21 Sep 2026 10:50:35 +0800 Subject: [PATCH] testing/nettest: add SO_TIMESTAMPING cmocka test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add cmocka-based test program for SO_TIMESTAMPING socket option under testing/nettest/timestamp/. Covers all five test scenarios from the SO_TIMESTAMPING PR (apache/nuttx#20161): 1. SO_TIMESTAMPING setsockopt/getsockopt 2. SO_TIMESTAMP/SO_TIMESTAMPNS backward compatibility 3. TX timestamping + MSG_ERRQUEUE delivery 4. RX timestamp via SO_TIMESTAMP 5. TX timestamping with poll(POLLPRI) notification Enable with CONFIG_TESTING_NET_TIMESTAMP=y (requires CONFIG_NET_TIMESTAMP, CONFIG_NET_PKT, CONFIG_TESTING_CMOCKA). Tested on sim:dynconns — all 5 tests pass. Signed-off-by: wenquan1 --- testing/nettest/CMakeLists.txt | 19 + testing/nettest/Kconfig | 5 + testing/nettest/Makefile | 6 + testing/nettest/timestamp/test_timestamp.c | 51 +++ testing/nettest/timestamp/test_timestamp.h | 79 ++++ .../nettest/timestamp/test_timestamp_common.c | 433 ++++++++++++++++++ 6 files changed, 593 insertions(+) create mode 100644 testing/nettest/timestamp/test_timestamp.c create mode 100644 testing/nettest/timestamp/test_timestamp.h create mode 100644 testing/nettest/timestamp/test_timestamp_common.c diff --git a/testing/nettest/CMakeLists.txt b/testing/nettest/CMakeLists.txt index 6bbf95753..87dd889c5 100644 --- a/testing/nettest/CMakeLists.txt +++ b/testing/nettest/CMakeLists.txt @@ -97,4 +97,23 @@ if(CONFIG_TESTING_NET_TEST) ${SRCS}) endif() + if(CONFIG_TESTING_NET_TIMESTAMP) + set(SRCS ${CMAKE_CURRENT_LIST_DIR}/timestamp/test_timestamp.c + ${CMAKE_CURRENT_LIST_DIR}/timestamp/test_timestamp_common.c) + + nuttx_add_application( + NAME + cmocka_net_timestamp + PRIORITY + ${CONFIG_TESTING_NET_TEST_PRIORITY} + STACKSIZE + ${CONFIG_TESTING_NET_TEST_STACKSIZE} + MODULE + ${CONFIG_TESTING_NET_TEST} + DEPENDS + cmocka + SRCS + ${SRCS}) + endif() + endif() diff --git a/testing/nettest/Kconfig b/testing/nettest/Kconfig index 83e3ba722..d6d6e6a6d 100644 --- a/testing/nettest/Kconfig +++ b/testing/nettest/Kconfig @@ -44,4 +44,9 @@ config TESTING_NET_OTHERS bool "Enable cmocka net other test" default y +config TESTING_NET_TIMESTAMP + bool "Enable cmocka net timestamp test" + depends on NET_TIMESTAMP && NET_PKT + default n + endif diff --git a/testing/nettest/Makefile b/testing/nettest/Makefile index 3e5f4f22a..7da23be0e 100644 --- a/testing/nettest/Makefile +++ b/testing/nettest/Makefile @@ -58,5 +58,11 @@ CSRCS += others/test_others_common.c others/test_others_bufpool.c \ others/test_others_chksum.c endif +ifeq ($(CONFIG_TESTING_NET_TIMESTAMP),y) +MAINSRC += timestamp/test_timestamp.c +PROGNAME += cmocka_net_timestamp +CSRCS += timestamp/test_timestamp_common.c +endif + endif include $(APPDIR)/Application.mk diff --git a/testing/nettest/timestamp/test_timestamp.c b/testing/nettest/timestamp/test_timestamp.c new file mode 100644 index 000000000..c18974775 --- /dev/null +++ b/testing/nettest/timestamp/test_timestamp.c @@ -0,0 +1,51 @@ +/**************************************************************************** + * apps/testing/nettest/timestamp/test_timestamp.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "test_timestamp.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + const struct CMUnitTest timestamp_tests[] = + { + cmocka_unit_test(test_timestamp_setsockopt), + cmocka_unit_test(test_timestamp_compat), + cmocka_unit_test(test_timestamp_tx), + cmocka_unit_test(test_timestamp_rx), + cmocka_unit_test(test_timestamp_poll), + }; + + return cmocka_run_group_tests(timestamp_tests, + test_timestamp_group_setup, + test_timestamp_group_teardown); +} diff --git a/testing/nettest/timestamp/test_timestamp.h b/testing/nettest/timestamp/test_timestamp.h new file mode 100644 index 000000000..df54f9d09 --- /dev/null +++ b/testing/nettest/timestamp/test_timestamp.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * apps/testing/nettest/timestamp/test_timestamp.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_TESTING_NETTEST_TIMESTAMP_TEST_TIMESTAMP_H +#define __APPS_TESTING_NETTEST_TIMESTAMP_TEST_TIMESTAMP_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct nettest_timestamp_state_s +{ + unsigned int ifindex; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: test_timestamp_group_setup + ****************************************************************************/ + +int test_timestamp_group_setup(FAR void **state); + +/**************************************************************************** + * Name: test_timestamp_group_teardown + ****************************************************************************/ + +int test_timestamp_group_teardown(FAR void **state); + +/**************************************************************************** + * Name: test_timestamp_setsockopt + ****************************************************************************/ + +void test_timestamp_setsockopt(FAR void **state); + +/**************************************************************************** + * Name: test_timestamp_compat + ****************************************************************************/ + +void test_timestamp_compat(FAR void **state); + +/**************************************************************************** + * Name: test_timestamp_tx + ****************************************************************************/ + +void test_timestamp_tx(FAR void **state); + +/**************************************************************************** + * Name: test_timestamp_rx + ****************************************************************************/ + +void test_timestamp_rx(FAR void **state); + +/**************************************************************************** + * Name: test_timestamp_poll + ****************************************************************************/ + +void test_timestamp_poll(FAR void **state); + +#endif /* __APPS_TESTING_NETTEST_TIMESTAMP_TEST_TIMESTAMP_H */ diff --git a/testing/nettest/timestamp/test_timestamp_common.c b/testing/nettest/timestamp/test_timestamp_common.c new file mode 100644 index 000000000..89e7c7a55 --- /dev/null +++ b/testing/nettest/timestamp/test_timestamp_common.c @@ -0,0 +1,433 @@ +/**************************************************************************** + * apps/testing/nettest/timestamp/test_timestamp_common.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_timestamp.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TX_PKT_SIZE 46 +#define RX_RETRY_MAX 10 +#define RX_RETRY_USEC 50000 +#define POLL_TIMEOUT 1000 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: test_timestamp_group_setup + ****************************************************************************/ + +int test_timestamp_group_setup(FAR void **state) +{ + FAR struct nettest_timestamp_state_s *ts_state; + + ts_state = zalloc(sizeof(*ts_state)); + assert_non_null(ts_state); + + *state = ts_state; + + ts_state->ifindex = if_nametoindex("eth0"); + + return 0; +} + +/**************************************************************************** + * Name: test_timestamp_group_teardown + ****************************************************************************/ + +int test_timestamp_group_teardown(FAR void **state) +{ + FAR struct nettest_timestamp_state_s *ts_state = *state; + + free(ts_state); + + return 0; +} + +/**************************************************************************** + * Name: test_timestamp_setsockopt + * + * Description: + * Test 1: Verify SO_TIMESTAMPING setsockopt/getsockopt. + * + ****************************************************************************/ + +void test_timestamp_setsockopt(FAR void **state) +{ + socklen_t len; + int val; + int ret; + int fd; + + fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + assert_return_code(fd, errno); + + val = SOF_TIMESTAMPING_TX_SOFTWARE; + ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, + &val, sizeof(val)); + assert_return_code(ret, errno); + + val = 0; + len = sizeof(val); + ret = getsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, + &val, &len); + assert_return_code(ret, errno); + + assert_true(val != 0); + + close(fd); +} + +/**************************************************************************** + * Name: test_timestamp_compat + * + * Description: + * Test 2: SO_TIMESTAMP/SO_TIMESTAMPNS backward compatibility. + * + ****************************************************************************/ + +void test_timestamp_compat(FAR void **state) +{ + int val; + int ret; + int fd; + + fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + assert_return_code(fd, errno); + + val = 1; + ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, + &val, sizeof(val)); + assert_return_code(ret, errno); + + val = 1; + ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, + &val, sizeof(val)); + assert_return_code(ret, errno); + + close(fd); +} + +/**************************************************************************** + * Name: test_timestamp_tx + * + * Description: + * Test 3: TX timestamping + MSG_ERRQUEUE delivery. + * Matches PR #20161 test_tx_timestamp() exactly. + * + ****************************************************************************/ + +void test_timestamp_tx(FAR void **state) +{ + FAR struct nettest_timestamp_state_s *ts_state = *state; + FAR struct cmsghdr *cmsg; + FAR struct timespec *ts; + struct sockaddr_ll addr; + char cmsgbuf[256]; + char buf[64]; + struct msghdr msg; + struct iovec iov; + ssize_t n; + int val; + int ret; + int fd; + + if (ts_state->ifindex == 0) + { + skip(); + } + + fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + assert_return_code(fd, errno); + + val = SOF_TIMESTAMPING_TX_SOFTWARE; + ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, + &val, sizeof(val)); + assert_return_code(ret, errno); + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_ALL); + addr.sll_ifindex = ts_state->ifindex; + + ret = bind(fd, (FAR struct sockaddr *)&addr, sizeof(addr)); + assert_return_code(ret, errno); + + memset(buf, 0xaa, TX_PKT_SIZE); + addr.sll_halen = 6; + memset(addr.sll_addr, 0xff, 6); + + n = sendto(fd, buf, TX_PKT_SIZE, 0, + (FAR struct sockaddr *)&addr, sizeof(addr)); + assert_true(n > 0); + + usleep(10000); + + memset(&msg, 0, sizeof(msg)); + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + n = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + assert_true(n >= 0); + + ts = NULL; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMPING) + { + ts = (FAR struct timespec *)CMSG_DATA(cmsg); + break; + } + } + + assert_non_null(ts); + assert_true(ts[0].tv_sec > 0); + + close(fd); +} + +/**************************************************************************** + * Name: test_timestamp_rx + * + * Description: + * Test 4: RX timestamp via SO_TIMESTAMP. + * Matches PR #20161 test_rx_timestamp() exactly. + * + ****************************************************************************/ + +void test_timestamp_rx(FAR void **state) +{ + FAR struct nettest_timestamp_state_s *ts_state = *state; + FAR struct cmsghdr *cmsg; + FAR struct timeval *tv; + struct sockaddr_ll addr; + char cmsgbuf[256]; + char buf[128]; + struct msghdr msg; + struct iovec iov; + ssize_t n; + int retry; + int val; + int ret; + int fd; + + if (ts_state->ifindex == 0) + { + skip(); + } + + fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + assert_return_code(fd, errno); + + val = 1; + ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, + &val, sizeof(val)); + assert_return_code(ret, errno); + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_ALL); + addr.sll_ifindex = ts_state->ifindex; + + ret = bind(fd, (FAR struct sockaddr *)&addr, sizeof(addr)); + assert_return_code(ret, errno); + + /* Send broadcast via separate SOCK_DGRAM to trigger RX */ + + { + struct sockaddr_ll sa; + int sfd; + + sfd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + assert_return_code(sfd, errno); + + memset(&sa, 0, sizeof(sa)); + sa.sll_family = AF_PACKET; + sa.sll_protocol = htons(ETH_P_IP); + sa.sll_ifindex = ts_state->ifindex; + sa.sll_halen = 6; + memset(sa.sll_addr, 0xff, 6); + memset(buf, 0xcc, TX_PKT_SIZE); + + sendto(sfd, buf, TX_PKT_SIZE, 0, + (FAR struct sockaddr *)&sa, sizeof(sa)); + close(sfd); + } + + memset(&msg, 0, sizeof(msg)); + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + n = -1; + for (retry = 0; retry < RX_RETRY_MAX; retry++) + { + usleep(RX_RETRY_USEC); + n = recvmsg(fd, &msg, MSG_DONTWAIT); + if (n > 0) + { + break; + } + } + + assert_true(n > 0); + + tv = NULL; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMP) + { + tv = (FAR struct timeval *)CMSG_DATA(cmsg); + break; + } + } + + assert_non_null(tv); + assert_true(tv->tv_sec > 0); + + close(fd); +} + +/**************************************************************************** + * Name: test_timestamp_poll + * + * Description: + * Test 5: TX timestamping with poll(POLLPRI) notification. + * Matches PR #20161 test_tx_timestamp_poll() exactly. + * + ****************************************************************************/ + +void test_timestamp_poll(FAR void **state) +{ + FAR struct nettest_timestamp_state_s *ts_state = *state; + FAR struct cmsghdr *cmsg; + FAR struct timespec *ts; + struct sockaddr_ll addr; + struct pollfd pfd; + char cmsgbuf[256]; + char buf[64]; + struct msghdr msg; + struct iovec iov; + ssize_t n; + int val; + int ret; + int fd; + + if (ts_state->ifindex == 0) + { + skip(); + } + + fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + assert_return_code(fd, errno); + + val = SOF_TIMESTAMPING_TX_SOFTWARE; + ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, + &val, sizeof(val)); + assert_return_code(ret, errno); + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_ALL); + addr.sll_ifindex = ts_state->ifindex; + + ret = bind(fd, (FAR struct sockaddr *)&addr, sizeof(addr)); + assert_return_code(ret, errno); + + addr.sll_halen = 6; + memset(addr.sll_addr, 0xff, 6); + + memset(buf, 0xaa, TX_PKT_SIZE); + n = sendto(fd, buf, TX_PKT_SIZE, 0, + (FAR struct sockaddr *)&addr, sizeof(addr)); + assert_true(n > 0); + + pfd.fd = fd; + pfd.events = POLLPRI; + pfd.revents = 0; + + n = poll(&pfd, 1, POLL_TIMEOUT); + assert_true(n > 0); + assert_true((pfd.revents & POLLPRI) != 0); + + memset(&msg, 0, sizeof(msg)); + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + n = recvmsg(fd, &msg, MSG_ERRQUEUE); + assert_true(n >= 0); + + ts = NULL; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMPING) + { + ts = (FAR struct timespec *)CMSG_DATA(cmsg); + break; + } + } + + assert_non_null(ts); + assert_true(ts[0].tv_sec > 0); + + close(fd); +}