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
+}

Reply via email to