mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 00:53:34 +02:00
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: Xu Du <xudu@redhat.com> Link: https://patch.msgid.link/a988dbc9ca109e4f1f0b33858c5035bce8ebede3.1768979440.git.xudu@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
24e59f26ee
commit
400e658aa0
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user