From: Xu Du <xudu@redhat.com>
To: davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, shuah@kernel.org
Cc: netdev@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH net-next v4 6/8] selftest: tun: Add test for sending gso packet into tun
Date: Tue, 6 Jan 2026 09:35:19 +0800 [thread overview]
Message-ID: <cd042fefae2613b0d519cbc0fadf84e82b4ea93d.1767597114.git.xudu@redhat.com> (raw)
In-Reply-To: <cover.1767597114.git.xudu@redhat.com>
The test constructs a raw packet, prepends a virtio_net_hdr,
and writes the result to the TUN device. This mimics the behavior
of a vm forwarding a guest's packet to the host networking stack.
Signed-off-by: Xu Du <xudu@redhat.com>
---
v3 -> v4:
- Expand the buffer to accommodate the maximum length of test packets.
v1 -> v2:
- Correct spelling of 'recieve' to 'receive'.
- Avoid busy waiting caused by recv() returning empty.
tools/testing/selftests/net/tun.c | 151 ++++++++++++++++++++++++++++--
1 file changed, 142 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/net/tun.c
index add5e91df6c9..ececc5ed4909 100644
--- a/tools/testing/selftests/net/tun.c
+++ b/tools/testing/selftests/net/tun.c
@@ -75,8 +75,35 @@ static struct in6_addr param_ipaddr6_inner_src = {
#define UDP_TUNNEL_OUTER_IPV4 (UDP_TUNNEL_VXLAN_4IN4 | UDP_TUNNEL_VXLAN_6IN4)
#define UDP_TUNNEL_INNER_IPV4 (UDP_TUNNEL_VXLAN_4IN4 | UDP_TUNNEL_VXLAN_4IN6)
+#define UDP_TUNNEL_VXLAN_4IN4_HDRLEN \
+ (ETH_HLEN + 2 * sizeof(struct iphdr) + 8 + 2 * sizeof(struct udphdr))
+#define UDP_TUNNEL_VXLAN_6IN6_HDRLEN \
+ (ETH_HLEN + 2 * sizeof(struct ipv6hdr) + 8 + 2 * sizeof(struct udphdr))
+#define UDP_TUNNEL_VXLAN_4IN6_HDRLEN \
+ (ETH_HLEN + sizeof(struct iphdr) + sizeof(struct ipv6hdr) + 8 + \
+ 2 * sizeof(struct udphdr))
+#define UDP_TUNNEL_VXLAN_6IN4_HDRLEN \
+ (ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct iphdr) + 8 + \
+ 2 * sizeof(struct udphdr))
+
+#define UDP_TUNNEL_HDRLEN(type) \
+ ((type) == UDP_TUNNEL_VXLAN_4IN4 ? UDP_TUNNEL_VXLAN_4IN4_HDRLEN : \
+ (type) == UDP_TUNNEL_VXLAN_6IN4 ? UDP_TUNNEL_VXLAN_6IN4_HDRLEN : \
+ (type) == UDP_TUNNEL_VXLAN_4IN6 ? UDP_TUNNEL_VXLAN_4IN6_HDRLEN : \
+ (type) == UDP_TUNNEL_VXLAN_6IN6 ? UDP_TUNNEL_VXLAN_6IN6_HDRLEN : \
+ 0)
+
+#define UDP_TUNNEL_MSS(type) (ETH_DATA_LEN - UDP_TUNNEL_HDRLEN(type))
+
+#define UDP_TUNNEL_MAX(type, is_tap) \
+ (ETH_MAX_MTU - UDP_TUNNEL_HDRLEN(type) - ((is_tap) ? ETH_HLEN : 0))
+
#define TUN_VNET_TNL_SIZE sizeof(struct virtio_net_hdr_v1_hash_tunnel)
+#define MAX_VNET_TUNNEL_PACKET_SZ \
+ (TUN_VNET_TNL_SIZE + ETH_HLEN + UDP_TUNNEL_VXLAN_6IN6_HDRLEN + \
+ ETH_MAX_MTU)
+
struct vxlan_setup_config {
struct sockaddr_storage local_ip;
struct sockaddr_storage remote_ip;
@@ -402,15 +429,23 @@ FIXTURE(tun_vnet_udptnl)
FIXTURE_VARIANT(tun_vnet_udptnl)
{
int tunnel_type;
- bool is_tap;
+ int gso_size;
+ int data_size;
+ int r_num_mss;
+ bool is_tap, no_gso;
};
/* clang-format off */
#define TUN_VNET_UDPTNL_VARIANT_ADD(type, desc) \
- FIXTURE_VARIANT_ADD(tun_vnet_udptnl, desc##udptnl) { \
+ FIXTURE_VARIANT_ADD(tun_vnet_udptnl, desc##_1mss) { \
+ /* send a single MSS: fall back to no GSO */ \
.tunnel_type = type, \
+ .gso_size = UDP_TUNNEL_MSS(type), \
+ .data_size = UDP_TUNNEL_MSS(type), \
+ .r_num_mss = 1, \
.is_tap = true, \
- }
+ .no_gso = true, \
+ };
/* clang-format on */
TUN_VNET_UDPTNL_VARIANT_ADD(UDP_TUNNEL_VXLAN_4IN4, 4in4);
@@ -558,14 +593,112 @@ FIXTURE_TEARDOWN(tun_vnet_udptnl)
EXPECT_EQ(ret, 0);
}
-TEST_F(tun_vnet_udptnl, basic)
+static int build_gso_packet_into_tun(const FIXTURE_VARIANT(tun_vnet_udptnl) *
+ variant,
+ uint8_t *buf)
{
- int ret;
- char cmd[256] = { 0 };
+ int tunnel_type = variant->tunnel_type;
+ int payload_len = variant->data_size;
+ int gso_size = variant->gso_size;
+ int inner_family, outer_family;
+ bool is_tap = variant->is_tap;
+ uint8_t *outer_udph = NULL;
+ uint8_t *cur = buf;
+ int len, proto;
+
+ len = (is_tap ? ETH_HLEN : 0) + UDP_TUNNEL_HDRLEN(tunnel_type);
+ inner_family = (tunnel_type & UDP_TUNNEL_INNER_IPV4) ? AF_INET :
+ AF_INET6;
+ outer_family = (tunnel_type & UDP_TUNNEL_OUTER_IPV4) ? AF_INET :
+ AF_INET6;
+
+ cur += build_virtio_net_hdr_v1_hash_tunnel(cur, is_tap, len, gso_size,
+ outer_family, inner_family);
+
+ if (is_tap) {
+ proto = outer_family == AF_INET ? ETH_P_IP : ETH_P_IPV6;
+ cur += build_eth(cur, proto, param_hwaddr_outer_dst,
+ param_hwaddr_outer_src);
+ len -= ETH_HLEN;
+ }
- sprintf(cmd, "ip addr show %s > /dev/null 2>&1", param_dev_vxlan_name);
- ret = system(cmd);
- ASSERT_EQ(ret, 0);
+ if (outer_family == AF_INET) {
+ len = len - sizeof(struct iphdr) + payload_len;
+ cur += build_ipv4_header(cur, IPPROTO_UDP, len,
+ ¶m_ipaddr4_outer_dst,
+ ¶m_ipaddr4_outer_src);
+ } else {
+ len = len - sizeof(struct ipv6hdr) + payload_len;
+ cur += build_ipv6_header(cur, IPPROTO_UDP, 0, len,
+ ¶m_ipaddr6_outer_dst,
+ ¶m_ipaddr6_outer_src);
+ }
+
+ outer_udph = cur;
+ len -= sizeof(struct udphdr);
+ proto = inner_family == AF_INET ? ETH_P_IP : ETH_P_IPV6;
+ cur += build_udp_header(cur, UDP_SRC_PORT, VN_PORT, len);
+ cur += build_vxlan_header(cur, VN_ID);
+ cur += build_eth(cur, proto, param_hwaddr_inner_dst,
+ param_hwaddr_inner_src);
+
+ len = sizeof(struct udphdr) + payload_len;
+ if (inner_family == AF_INET) {
+ cur += build_ipv4_header(cur, IPPROTO_UDP, len,
+ ¶m_ipaddr4_inner_dst,
+ ¶m_ipaddr4_inner_src);
+ } else {
+ cur += build_ipv6_header(cur, IPPROTO_UDP, 0, len,
+ ¶m_ipaddr6_inner_dst,
+ ¶m_ipaddr6_inner_src);
+ }
+
+ cur += build_udp_packet(cur, UDP_DST_PORT, UDP_SRC_PORT, payload_len,
+ inner_family, false);
+
+ build_udp_packet_csum(outer_udph, outer_family, false);
+
+ return cur - buf;
+}
+
+static int
+receive_gso_packet_from_tunnel(FIXTURE_DATA(tun_vnet_udptnl) * self,
+ const FIXTURE_VARIANT(tun_vnet_udptnl) * variant,
+ int *r_num_mss)
+{
+ uint8_t packet_buf[MAX_VNET_TUNNEL_PACKET_SZ];
+ int len, total_len = 0, socket = self->sock;
+ int payload_len = variant->data_size;
+
+ while (total_len < payload_len) {
+ len = recv(socket, packet_buf, sizeof(packet_buf), 0);
+ if (len <= 0) {
+ if (len < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
+ perror("recv");
+ break;
+ }
+
+ (*r_num_mss)++;
+ total_len += len;
+ }
+
+ return total_len;
+}
+
+TEST_F(tun_vnet_udptnl, send_gso_packet)
+{
+ uint8_t pkt[MAX_VNET_TUNNEL_PACKET_SZ];
+ int r_num_mss = 0;
+ int ret, off;
+
+ memset(pkt, 0, sizeof(pkt));
+ off = build_gso_packet_into_tun(variant, pkt);
+ ret = write(self->fd, pkt, off);
+ ASSERT_EQ(ret, off);
+
+ ret = receive_gso_packet_from_tunnel(self, variant, &r_num_mss);
+ ASSERT_EQ(ret, variant->data_size);
+ ASSERT_EQ(r_num_mss, variant->r_num_mss);
}
TEST_HARNESS_MAIN
--
2.49.0
next prev parent reply other threads:[~2026-01-06 1:36 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-06 1:35 [PATCH net-next v4 0/8] selftest: Extend tun/virtio coverage for GSO over UDP tunnel Xu Du
2026-01-06 1:35 ` [PATCH net-next v4 1/8] selftest: tun: Format tun.c existing code Xu Du
2026-01-06 1:35 ` [PATCH net-next v4 2/8] selftest: tun: Introduce tuntap_helpers.h header for TUN/TAP testing Xu Du
2026-01-06 1:35 ` [PATCH net-next v4 3/8] selftest: tun: Refactor tun_delete to use tuntap_helpers Xu Du
2026-01-06 1:35 ` [PATCH net-next v4 4/8] selftest: tap: Refactor tap test " Xu Du
2026-01-06 1:35 ` [PATCH net-next v4 5/8] selftest: tun: Add helpers for GSO over UDP tunnel Xu Du
2026-01-06 1:35 ` Xu Du [this message]
2026-01-06 1:35 ` [PATCH net-next v4 7/8] selftest: tun: Add test for receiving gso packet from tun Xu Du
2026-01-06 1:35 ` [PATCH net-next v4 8/8] selftest: tun: Add test data for success and failure paths Xu Du
2026-01-06 1:57 ` [PATCH net-next v4 0/8] selftest: Extend tun/virtio coverage for GSO over UDP tunnel Jakub Kicinski
2026-01-06 2:17 ` Xu Du
2026-01-06 22:14 ` Willem de Bruijn
2026-01-06 22:58 ` Jakub Kicinski
2026-01-07 10:08 ` Xu Du
2026-01-07 14:59 ` Willem de Bruijn
2026-01-08 2:09 ` Xu Du
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cd042fefae2613b0d519cbc0fadf84e82b4ea93d.1767597114.git.xudu@redhat.com \
--to=xudu@redhat.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®