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 <[email protected]>
---
v4 -> v5:
 - Use Geneve instead of VXLAN due to missing YNL support.

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 | 144 ++++++++++++++++++++++++++++--
 1 file changed, 135 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/net/tun.c 
b/tools/testing/selftests/net/tun.c
index ec089355312b..f0ddb5d37683 100644
--- a/tools/testing/selftests/net/tun.c
+++ b/tools/testing/selftests/net/tun.c
@@ -75,7 +75,34 @@ static struct in6_addr param_ipaddr6_inner_src = {
 #define UDP_TUNNEL_OUTER_IPV4 (UDP_TUNNEL_GENEVE_4IN4 | UDP_TUNNEL_GENEVE_6IN4)
 #define UDP_TUNNEL_INNER_IPV4 (UDP_TUNNEL_GENEVE_4IN4 | UDP_TUNNEL_GENEVE_4IN6)
 
+#define UDP_TUNNEL_GENEVE_4IN4_HDRLEN                        \
+       (ETH_HLEN + 2 * sizeof(struct iphdr) + GENEVE_HLEN + \
+        2 * sizeof(struct udphdr))
+#define UDP_TUNNEL_GENEVE_6IN6_HDRLEN                          \
+       (ETH_HLEN + 2 * sizeof(struct ipv6hdr) + GENEVE_HLEN + \
+        2 * sizeof(struct udphdr))
+#define UDP_TUNNEL_GENEVE_4IN6_HDRLEN                               \
+       (ETH_HLEN + sizeof(struct iphdr) + sizeof(struct ipv6hdr) + \
+        GENEVE_HLEN + 2 * sizeof(struct udphdr))
+#define UDP_TUNNEL_GENEVE_6IN4_HDRLEN                               \
+       (ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct iphdr) + \
+        GENEVE_HLEN + 2 * sizeof(struct udphdr))
+
+#define UDP_TUNNEL_HDRLEN(type)                                             \
+       ((type) == UDP_TUNNEL_GENEVE_4IN4 ? UDP_TUNNEL_GENEVE_4IN4_HDRLEN : \
+        (type) == UDP_TUNNEL_GENEVE_6IN6 ? UDP_TUNNEL_GENEVE_6IN6_HDRLEN : \
+        (type) == UDP_TUNNEL_GENEVE_4IN6 ? UDP_TUNNEL_GENEVE_4IN6_HDRLEN : \
+        (type) == UDP_TUNNEL_GENEVE_6IN4 ? UDP_TUNNEL_GENEVE_6IN4_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_GENEVE_6IN6_HDRLEN + \
+        ETH_MAX_MTU)
 
 struct geneve_setup_config {
        int family;
@@ -408,15 +435,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_GENEVE_4IN4, 4in4);
@@ -544,14 +579,105 @@ 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 pktlen, hlen, proto, inner_family, outer_family;
+       int tunnel_type = variant->tunnel_type;
+       int payload_len = variant->data_size;
+       int gso_size = variant->gso_size;
+       uint8_t *outer_udph, *cur = buf;
+       void *sip, *dip, *smac, *dmac;
+       bool is_tap = variant->is_tap;
 
-       sprintf(cmd, "ip addr show %s > /dev/null 2>&1", param_dev_geneve_name);
-       ret = system(cmd);
-       ASSERT_EQ(ret, 0);
+       hlen = (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, hlen, gso_size,
+                                                  outer_family, inner_family);
+
+       pktlen = hlen + payload_len;
+       assign_ifaddr_vars(outer_family, 1, &sip, &dip, &smac, &dmac);
+
+       if (is_tap) {
+               proto = outer_family == AF_INET ? ETH_P_IP : ETH_P_IPV6;
+               pktlen -= ETH_HLEN;
+               cur += build_eth(cur, proto, dmac, smac);
+       }
+
+       if (outer_family == AF_INET) {
+               pktlen = pktlen - sizeof(struct iphdr);
+               cur += build_ipv4_header(cur, IPPROTO_UDP, pktlen, dip, sip);
+       } else {
+               pktlen = pktlen - sizeof(struct ipv6hdr);
+               cur += build_ipv6_header(cur, IPPROTO_UDP, 0, pktlen, dip, sip);
+       }
+
+       outer_udph = cur;
+       assign_ifaddr_vars(inner_family, 0, &sip, &dip, &smac, &dmac);
+
+       pktlen -= sizeof(struct udphdr);
+       proto = inner_family == AF_INET ? ETH_P_IP : ETH_P_IPV6;
+       cur += build_udp_header(cur, UDP_SRC_PORT, VN_PORT, pktlen);
+       cur += build_geneve_header(cur, VN_ID);
+       cur += build_eth(cur, proto, dmac, smac);
+
+       pktlen = sizeof(struct udphdr) + payload_len;
+       if (inner_family == AF_INET)
+               cur += build_ipv4_header(cur, IPPROTO_UDP, pktlen, dip, sip);
+       else
+               cur += build_ipv6_header(cur, IPPROTO_UDP, 0, pktlen, dip, sip);
+
+       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


Reply via email to