Commit 400e658a authored by Xu Du's avatar Xu Du Committed by Jakub Kicinski
Browse files

selftest: tun: Add test for sending gso packet into tun



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: default avatarXu Du <xudu@redhat.com>
Link: https://patch.msgid.link/a988dbc9ca109e4f1f0b33858c5035bce8ebede3.1768979440.git.xudu@redhat.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 24e59f26
Loading
Loading
Loading
Loading
+135 −9
Original line number Diff line number Diff line
@@ -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