This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit ca775b4301b5870a374bb322206de3efbb466c94 Author: Marco Casaroli <[email protected]> AuthorDate: Mon Nov 9 17:45:48 2026 +0100 testing/nettest: Add a test of packet sockets on the loopback device. The loopback driver polls with a NULL callback. A send() on a packet socket bound to lo, and the reassembly timeout of an IPv4 fragment that came in on lo, made the kernel call that NULL callback. Add cmocka_net_pkt (testing/nettest/pkt, CONFIG_TESTING_NET_PKT, default y). test_pkt_lo_send sends an IPv4 packet on lo from a packet socket and reads the same bytes on an ETH_P_ALL socket bound to lo. test_pkt_lo_frag sends the first fragment of an IPv4 packet on lo, waits for the reassembly timeout and reads the ICMP Time Exceeded message (it is skipped without CONFIG_NET_IPFRAG and the ICMP stack). Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- testing/nettest/CMakeLists.txt | 19 +++ testing/nettest/Kconfig | 9 ++ testing/nettest/Makefile | 6 + testing/nettest/pkt/test_pkt.c | 48 ++++++++ testing/nettest/pkt/test_pkt.h | 49 ++++++++ testing/nettest/pkt/test_pkt_lo.c | 246 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 377 insertions(+) diff --git a/testing/nettest/CMakeLists.txt b/testing/nettest/CMakeLists.txt index 296ae339d..7e2bfbe4a 100644 --- a/testing/nettest/CMakeLists.txt +++ b/testing/nettest/CMakeLists.txt @@ -236,4 +236,23 @@ if(CONFIG_TESTING_NET_TEST) ${SRCS}) endif() + if(CONFIG_TESTING_NET_PKT) + set(SRCS ${CMAKE_CURRENT_LIST_DIR}/pkt/test_pkt.c + ${CMAKE_CURRENT_LIST_DIR}/pkt/test_pkt_lo.c) + + nuttx_add_application( + NAME + cmocka_net_pkt + 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 e9fed0db8..5dcc86a04 100644 --- a/testing/nettest/Kconfig +++ b/testing/nettest/Kconfig @@ -98,4 +98,13 @@ config TESTING_NET_CAN index 0 (all CAN devices), and that an unbound socket cannot send (cmocka_net_can). +config TESTING_NET_PKT + bool "Enable cmocka net packet socket test" + depends on NET_PKT && NET_LOOPBACK && NET_IPv4 + default y + ---help--- + Test packet sockets bound to the loopback device: a frame sent on + lo comes back, and the reassembly timeout of an IPv4 fragment sent + on lo does not stop the kernel (cmocka_net_pkt). + endif diff --git a/testing/nettest/Makefile b/testing/nettest/Makefile index ff4c9aec2..2888b669e 100644 --- a/testing/nettest/Makefile +++ b/testing/nettest/Makefile @@ -115,5 +115,11 @@ PROGNAME += cmocka_net_can CSRCS += can/test_can_bind.c endif +ifeq ($(CONFIG_TESTING_NET_PKT),y) +MAINSRC += pkt/test_pkt.c +PROGNAME += cmocka_net_pkt +CSRCS += pkt/test_pkt_lo.c +endif + endif include $(APPDIR)/Application.mk diff --git a/testing/nettest/pkt/test_pkt.c b/testing/nettest/pkt/test_pkt.c new file mode 100644 index 000000000..3f15a4cae --- /dev/null +++ b/testing/nettest/pkt/test_pkt.c @@ -0,0 +1,48 @@ +/**************************************************************************** + * apps/testing/nettest/pkt/test_pkt.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <setjmp.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <cmocka.h> + +#include "test_pkt.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + const struct CMUnitTest pkt_tests[] = + { + cmocka_unit_test(test_pkt_lo_send), + cmocka_unit_test(test_pkt_lo_frag), + }; + + return cmocka_run_group_tests(pkt_tests, NULL, NULL); +} diff --git a/testing/nettest/pkt/test_pkt.h b/testing/nettest/pkt/test_pkt.h new file mode 100644 index 000000000..958bd54d1 --- /dev/null +++ b/testing/nettest/pkt/test_pkt.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * apps/testing/nettest/pkt/test_pkt.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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_PKT_TEST_PKT_H +#define __APPS_TESTING_NETTEST_PKT_TEST_PKT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/compiler.h> + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: test_pkt_lo_* + * + * Description: + * Packet sockets bound to the loopback device: a frame sent on lo comes + * back, and the reassembly timeout of an IPv4 fragment sent on lo does + * not stop the kernel. + * + ****************************************************************************/ + +void test_pkt_lo_send(FAR void **state); +void test_pkt_lo_frag(FAR void **state); + +#endif /* __APPS_TESTING_NETTEST_PKT_TEST_PKT_H */ diff --git a/testing/nettest/pkt/test_pkt_lo.c b/testing/nettest/pkt/test_pkt_lo.c new file mode 100644 index 000000000..bd9ae23e2 --- /dev/null +++ b/testing/nettest/pkt/test_pkt_lo.c @@ -0,0 +1,246 @@ +/**************************************************************************** + * apps/testing/nettest/pkt/test_pkt_lo.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 <nuttx/config.h> + +#include <errno.h> +#include <poll.h> +#include <setjmp.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <cmocka.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/if_ether.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netpacket/packet.h> +#include <sys/socket.h> + +#include "test_pkt.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The IPv4 header has no options */ + +#define TEST_PKT_IPLEN sizeof(struct iphdr) +#define TEST_PKT_PROTO 253 /* IP protocol for experiments, RFC 3692 */ +#define TEST_PKT_TYPE 0x88b5 /* Ethernet type for experiments */ +#define TEST_PKT_MF 0x2000 /* IPv4 flag "more fragments" */ +#define TEST_PKT_TIMXCEED 11 /* ICMP Time Exceeded */ +#define TEST_PKT_FRAGTIME 1 /* Its code: reassembly time exceeded */ +#define TEST_PKT_WAIT_MS 2000 /* Time to wait for a frame */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* A packet socket of the given protocol, bound to lo */ + +static int test_pkt_open(int proto) +{ + struct sockaddr_ll addr; + int sd; + + sd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC, htons(proto)); + assert_return_code(sd, errno); + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(proto); + addr.sll_ifindex = if_nametoindex("lo"); + assert_true(addr.sll_ifindex > 0); + assert_return_code(bind(sd, (FAR struct sockaddr *)&addr, sizeof(addr)), + errno); + return sd; +} + +static uint16_t test_pkt_chksum(FAR const void *data, size_t len) +{ + FAR const uint8_t *p = data; + uint32_t sum = 0; + size_t i; + + for (i = 0; i < len; i += 2) + { + sum += (p[i] << 8) | p[i + 1]; + } + + while (sum >> 16) + { + sum = (sum & 0xffff) + (sum >> 16); + } + + return ~sum & 0xffff; +} + +/* An IPv4 packet from 127.0.0.1 to 127.0.0.1 with 'plen' bytes of data and + * the given fragment field; returns its length. + */ + +static size_t test_pkt_ipv4(FAR uint8_t *f, size_t plen, uint16_t id, + uint16_t frag) +{ + struct iphdr ip; + size_t i; + + memset(&ip, 0, sizeof(ip)); + ip.version = 4; + ip.ihl = sizeof(ip) / 4; + ip.tot_len = htons(TEST_PKT_IPLEN + plen); + ip.id = htons(id); + ip.frag_off = htons(frag); + ip.ttl = 64; + ip.protocol = TEST_PKT_PROTO; + ip.saddr = htonl(INADDR_LOOPBACK); + ip.daddr = htonl(INADDR_LOOPBACK); + ip.check = htons(test_pkt_chksum(&ip, sizeof(ip))); + memcpy(f, &ip, sizeof(ip)); + + for (i = 0; i < plen; i++) + { + f[TEST_PKT_IPLEN + i] = 0xa0 + i; + } + + return TEST_PKT_IPLEN + plen; +} + +/* Read one frame; wait at most 'ms' milliseconds */ + +static ssize_t test_pkt_read(int sd, FAR uint8_t *buf, size_t len, int ms) +{ + struct pollfd pfd; + + pfd.fd = sd; + pfd.events = POLLIN; + pfd.revents = 0; + if (poll(&pfd, 1, ms) <= 0) + { + return -1; + } + + return recv(sd, buf, len, MSG_DONTWAIT); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: test_pkt_lo_send + * + * Description: + * send() of an IPv4 packet on a packet socket bound to lo returns its + * length, and an ETH_P_ALL socket bound to lo reads the same bytes. The + * sender uses another protocol, so that it does not read the frame. + * + ****************************************************************************/ + +void test_pkt_lo_send(FAR void **state) +{ + uint8_t frame[64]; + uint8_t buf[256]; + size_t len; + ssize_t ret; + int rsd; + int ssd; + + rsd = test_pkt_open(ETH_P_ALL); + ssd = test_pkt_open(TEST_PKT_TYPE); + + len = test_pkt_ipv4(frame, 8, 1, 0); + assert_int_equal(send(ssd, frame, len, 0), len); + + ret = test_pkt_read(rsd, buf, sizeof(buf), TEST_PKT_WAIT_MS); + assert_int_equal(ret, len); + assert_memory_equal(buf, frame, len); + + close(ssd); + close(rsd); +} + +/**************************************************************************** + * Name: test_pkt_lo_frag + * + * Description: + * The first fragment of an IPv4 packet, sent on lo by a packet socket. + * The reassembly times out, and the stack sends an ICMP Time Exceeded + * message on lo. The kernel must go on, and the ETH_P_ALL socket must + * read the ICMP message. + * + ****************************************************************************/ + +void test_pkt_lo_frag(FAR void **state) +{ +#if defined(CONFIG_NET_IPFRAG) && defined(CONFIG_NET_ICMP) && \ + !defined(CONFIG_NET_ICMP_NO_STACK) + struct iphdr ip; + uint8_t frame[64]; + uint8_t buf[256]; + bool found = false; + size_t len; + ssize_t ret; + int rsd; + int ssd; + + rsd = test_pkt_open(ETH_P_ALL); + ssd = test_pkt_open(TEST_PKT_TYPE); + + /* Fragment offset 0 with "more fragments" */ + + len = test_pkt_ipv4(frame, 16, 2, TEST_PKT_MF); + assert_int_equal(send(ssd, frame, len, 0), len); + + /* Wait for the reassembly timeout (deciseconds) */ + + sleep(CONFIG_NET_IPFRAG_REASS_MAXAGE / 10 + 3); + + /* The ICMP header follows the IP header: type, then code */ + + while ((ret = test_pkt_read(rsd, buf, sizeof(buf), 200)) > 0) + { + memcpy(&ip, buf, sizeof(ip)); + if (ret >= TEST_PKT_IPLEN + 8 && ip.protocol == IPPROTO_ICMP && + buf[TEST_PKT_IPLEN] == TEST_PKT_TIMXCEED && + buf[TEST_PKT_IPLEN + 1] == TEST_PKT_FRAGTIME) + { + found = true; + } + } + + close(ssd); + close(rsd); + assert_true(found); +#else + skip(); +#endif +}
