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]> --- v1 -> v2: - Correct spelling of 'recieve' to 'receive'. - Avoid busy waiting caused by recv() returning empty. tools/testing/selftests/net/tun.c | 149 ++++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/net/tun.c index add5e91df6c9..dc114237adda 100644 --- a/tools/testing/selftests/net/tun.c +++ b/tools/testing/selftests/net/tun.c @@ -77,6 +77,31 @@ static struct in6_addr param_ipaddr6_inner_src = { #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 + ETH_MAX_MTU) + +#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)) + struct vxlan_setup_config { struct sockaddr_storage local_ip; struct sockaddr_storage remote_ip; @@ -402,15 +427,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 +591,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

