From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qk1-f180.google.com (mail-qk1-f180.google.com [209.85.222.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AB439155316; Sat, 8 Mar 2025 19:39:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.180 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741462748; cv=none; b=iiYVEWTv9uC4ssel32qCd7GOjci4jTnFVTsgozeOz/T/EXjXVxN1677FbEHNpwB6t9+fPHauKiztF3Nh2nq6gY4NBEnaociFJImXPG8APr0+/juOxLyTX0nIX6WS7PDXD0VQ0TP2xM0rlq/LTa/hM8oCRjQL2wD61eZhYxnTHkI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741462748; c=relaxed/simple; bh=jINclNBT2Wr7tmSpVcDI+rn5eFJlZXxiOwHSOhpGYJs=; h=Date:From:To:Message-ID:In-Reply-To:References:Subject: Mime-Version:Content-Type; b=O/pEQRz04LkyYkVpK3q5wIOCsunjLDtk4YjdXjwxVOZzWrQgvIOKaHjfUDLU2mpWlDm2FvLzflOcD63sq5LmykNt522VgNkMsz/CAcGTz49U3hchAHdcItRjuHunDmq5VrOEwRBR7tiKAQcZTCmxJPP0he5bsCtIBymk8yZoHe0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gGdYTkBS; arc=none smtp.client-ip=209.85.222.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gGdYTkBS" Received: by mail-qk1-f180.google.com with SMTP id af79cd13be357-7c2303a56d6so340064385a.3; Sat, 08 Mar 2025 11:39:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741462745; x=1742067545; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:subject:references :in-reply-to:message-id:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=2IWQwhkPuIBucQja63csZ9zvEwpAy8WdkO38UEbtuBY=; b=gGdYTkBST6lxe2qU7mpsr8X2aPCK7n6woZ0X0W7s6zH9E7wdEq2QvBlVo809veYkmN kIfVObdQABTANV00TMNNd4kbHWWtH5+bWavEJWbJYDWoaKLMeWE0tGo5tp10Um8YHrNM gewh9p4fZUntdecacJugkFezwXTnyfEwPgXZ1oqS3Z7ieApKADo1vMXETAuFIFiLIOMC Wt7ZiQoeEF9iTSRgXl2RUbd7sIIBirlvgQoBdf1lb40ZF4d2sReU7lmbVGVko9bljinb qAKRWbkrRqg9XylBDwlQ/mvk9zqvLC7bIBxYVkrMsgdWfau6lSCRB9f6UhJa3KgUxrhT Gbxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741462745; x=1742067545; h=content-transfer-encoding:mime-version:subject:references :in-reply-to:message-id:to:from:date:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=2IWQwhkPuIBucQja63csZ9zvEwpAy8WdkO38UEbtuBY=; b=qaYMkXXZ7XN2G4C9q/omoHPQEAbxPaklKH9zfA5bU68w4A0r14G6JOG3gcur/apl85 R+JotatjwRdzXVVu32h0pqvI8lCq2ZnCnYGni6yCMzSYbpeY5zcUH6FUrjpWh70S57c7 uPr50R8emPqnC+BKbmo7kuQsaQUMFvi2RjwojwYDFIRDdRCzHkZmyP0ZTOopuv9PlIDt bdsXVb2ZWa59tH68XUAwvmJZ2s2qTLOzXx5rOnUcZ/SEPPWlpWI3X7AvSHu5f2yRtuap iEWKUP7IHj9zFLJf1McVgA9MnyTEj6ET7/yew/M9TbOz8nWyvdzCbzokQfj2rzah5HHr nLvA== X-Forwarded-Encrypted: i=1; AJvYcCU6PRGeuX+G/OSjQxSgCKmCfD7FmstR7Y621TVJ2TLTi3DdfsHbB8xOnEVVzaTFRVF2QgiTAWJxt+nxK0ik@vger.kernel.org, AJvYcCUIykGgE0OncfGDeEXVmiMiCIx4xlpyJtJU1TI6I3H6Hx/0+SnDbkHXHsH+1Ta6DCuBe975JA6n@vger.kernel.org, AJvYcCVQ8yNmhpJzPxav1LKQbGR1JHSIlTR/2xI8r79HQzEop2Efb0VurR7nb3eiMY+L1mGu9GU=@vger.kernel.org, AJvYcCVrPZYDr/Am1mzcds5tLbDrIJ1qPSWePLmrhxvmQ1zhiv6OaqYrIxB9rMrE0MRb19PMK0yF2+wBDCwb@vger.kernel.org, AJvYcCW4VPN1T0+sp81mHnAxbfLyVOWj/dAgsjXLpzeRYPz8fc5W1B6PBPFyU5Y2P6jex+X06h5dumgl9C1N+36jMb6y@vger.kernel.org X-Gm-Message-State: AOJu0YzmFpYLLJH4oEcAEdrR3Umvjr7edX1ReTuVO805QyJiRiSYJYXO VJxMLhGvDZdUV1n7SChVgp5XSvLcxInrGE+q7ZPb7HzzAJh9W8T/ X-Gm-Gg: ASbGncvDR281chxjCksXS+5iVPg1ojLj4dj4Ugrq/JIpnlE3bocOYQ7VKHXXyeQJkwR D2y+ilnXTB+Mucuxi9MV7clJHtGYhaCzaCoGuoKraIZRHTknqcs3lo0pPU4zg0Rha3MpgpMeKPD WNztv4ws6oJQpWMNJE68HvCg0ffV1ty93ZRx/AIIMI50p+xGGswu9uRyZD/epK6aMDiQDwn1/ZX H2on2idCFwywmkNJwLwQWZzgwjpIHEf+2sWvON60ZSZHUZ5ndZQSysJ/QySuQ89wYu8GKwriFp2 KNr0GeoF7q5fFAMzf4PQncmVQv/AicCHy/u+l/4AO0QdbS6b7VO9PVWfNO9LcLKgdUsj+oYMl+T KBUoBGN6bnc1g69zUyNa7sQ== X-Google-Smtp-Source: AGHT+IEbAiNkRe5mCRPA6jGEsnKtVdOnwc/WLU1rix96DD9BsiiWGTqjlAollOT6vN0M2UQuU/aCDw== X-Received: by 2002:a05:6214:411b:b0:6e6:61f1:458a with SMTP id 6a1803df08f44-6e9005e8f70mr91739796d6.14.1741462745254; Sat, 08 Mar 2025 11:39:05 -0800 (PST) Received: from localhost (234.207.85.34.bc.googleusercontent.com. [34.85.207.234]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-6e8f70909c9sm34277706d6.26.2025.03.08.11.39.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 08 Mar 2025 11:39:04 -0800 (PST) Date: Sat, 08 Mar 2025 14:39:04 -0500 From: Willem de Bruijn To: Akihiko Odaki , Jonathan Corbet , Willem de Bruijn , Jason Wang , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Xuan Zhuo , Shuah Khan , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org, linux-kselftest@vger.kernel.org, Yuri Benditovich , Andrew Melnychenko , Stephen Hemminger , gur.stavi@huawei.com, Lei Yang , Simon Horman , Akihiko Odaki Message-ID: <67cc9cd8218f1_14b9f9294c4@willemb.c.googlers.com.notmuch> In-Reply-To: <20250307-rss-v9-5-df76624025eb@daynix.com> References: <20250307-rss-v9-0-df76624025eb@daynix.com> <20250307-rss-v9-5-df76624025eb@daynix.com> Subject: Re: [PATCH net-next v9 5/6] selftest: tun: Add tests for virtio-net hashing Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Akihiko Odaki wrote: > The added tests confirm tun can perform RSS and hash reporting, and > reject invalid configurations for them. > > Signed-off-by: Akihiko Odaki > Tested-by: Lei Yang > --- > tools/testing/selftests/net/Makefile | 2 +- > tools/testing/selftests/net/tun.c | 584 ++++++++++++++++++++++++++++++++++- > 2 files changed, 576 insertions(+), 10 deletions(-) > > diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile > index 73ee88d6b043004be23b444de667a1d99a6045de..9772f691a9a011d99212df32463cdb930cf0a1a0 100644 > --- a/tools/testing/selftests/net/Makefile > +++ b/tools/testing/selftests/net/Makefile > @@ -123,6 +123,6 @@ $(OUTPUT)/reuseport_bpf_numa: LDLIBS += -lnuma > $(OUTPUT)/tcp_mmap: LDLIBS += -lpthread -lcrypto > $(OUTPUT)/tcp_inq: LDLIBS += -lpthread > $(OUTPUT)/bind_bhash: LDLIBS += -lpthread > -$(OUTPUT)/io_uring_zerocopy_tx: CFLAGS += -I../../../include/ > +$(OUTPUT)/io_uring_zerocopy_tx $(OUTPUT)/tun: CFLAGS += -I../../../include/ > > include bpf.mk > diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/net/tun.c > index 463dd98f2b80b1bdcb398cee43c834e7dc5cf784..acadeea7194eaea9416a605b47f99f7a5f1f80cd 100644 > --- a/tools/testing/selftests/net/tun.c > +++ b/tools/testing/selftests/net/tun.c > @@ -2,21 +2,38 @@ > > #define _GNU_SOURCE > > +#include > #include > #include > +#include > +#include > #include > #include > #include > #include > -#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > #include > +#include > #include > #include > -#include > -#include > +#include > +#include > +#include > +#include > > #include "../kselftest_harness.h" > > +#define TUN_HWADDR_SOURCE { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 } > +#define TUN_HWADDR_DEST { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 } > +#define TUN_IPADDR_SOURCE htonl((172 << 24) | (17 << 16) | 0) > +#define TUN_IPADDR_DEST htonl((172 << 24) | (17 << 16) | 1) > + > static int tun_attach(int fd, char *dev) > { > struct ifreq ifr; > @@ -39,7 +56,7 @@ static int tun_detach(int fd, char *dev) > return ioctl(fd, TUNSETQUEUE, (void *) &ifr); > } > > -static int tun_alloc(char *dev) > +static int tun_alloc(char *dev, short flags) > { > struct ifreq ifr; > int fd, err; > @@ -52,7 +69,8 @@ static int tun_alloc(char *dev) > > memset(&ifr, 0, sizeof(ifr)); > strcpy(ifr.ifr_name, dev); > - ifr.ifr_flags = IFF_TAP | IFF_NAPI | IFF_MULTI_QUEUE; > + ifr.ifr_flags = flags | IFF_TAP | IFF_NAPI | IFF_NO_PI | > + IFF_MULTI_QUEUE; > > err = ioctl(fd, TUNSETIFF, (void *) &ifr); > if (err < 0) { > @@ -64,6 +82,40 @@ static int tun_alloc(char *dev) > return fd; > } > > +static bool tun_add_to_bridge(int local_fd, const char *name) > +{ > + struct ifreq ifreq = { > + .ifr_name = "xbridge", > + .ifr_ifindex = if_nametoindex(name) > + }; > + > + if (!ifreq.ifr_ifindex) { > + perror("if_nametoindex"); > + return false; > + } > + > + if (ioctl(local_fd, SIOCBRADDIF, &ifreq)) { > + perror("SIOCBRADDIF"); > + return false; > + } > + > + return true; > +} > + > +static bool tun_set_flags(int local_fd, const char *name, short flags) > +{ > + struct ifreq ifreq = { .ifr_flags = flags }; > + > + strcpy(ifreq.ifr_name, name); > + > + if (ioctl(local_fd, SIOCSIFFLAGS, &ifreq)) { > + perror("SIOCSIFFLAGS"); > + return false; > + } > + > + return true; > +} > + > static int tun_delete(char *dev) > { > struct { > @@ -102,6 +154,156 @@ static int tun_delete(char *dev) > return ret; > } > > +static uint32_t tun_sum(const void *buf, size_t len) > +{ > + const uint16_t *sbuf = buf; > + uint32_t sum = 0; > + > + while (len > 1) { > + sum += *sbuf++; > + len -= 2; > + } > + > + if (len) > + sum += *(uint8_t *)sbuf; > + > + return sum; > +} > + > +static uint16_t tun_build_ip_check(uint32_t sum) > +{ > + return ~((sum & 0xffff) + (sum >> 16)); > +} > + > +static uint32_t tun_build_ip_pseudo_sum(const void *iphdr) > +{ > + uint16_t tot_len = ntohs(((struct iphdr *)iphdr)->tot_len); > + > + return tun_sum((char *)iphdr + offsetof(struct iphdr, saddr), 8) + > + htons(((struct iphdr *)iphdr)->protocol) + > + htons(tot_len - sizeof(struct iphdr)); > +} > + > +static uint32_t tun_build_ipv6_pseudo_sum(const void *ipv6hdr) > +{ > + return tun_sum((char *)ipv6hdr + offsetof(struct ipv6hdr, saddr), 32) + > + ((struct ipv6hdr *)ipv6hdr)->payload_len + > + htons(((struct ipv6hdr *)ipv6hdr)->nexthdr); > +} > + > +static void tun_build_ethhdr(struct ethhdr *ethhdr, uint16_t proto) > +{ > + *ethhdr = (struct ethhdr) { > + .h_dest = TUN_HWADDR_DEST, > + .h_source = TUN_HWADDR_SOURCE, > + .h_proto = htons(proto) > + }; > +} > + > +static void tun_build_iphdr(void *dest, uint16_t len, uint8_t protocol) > +{ > + struct iphdr iphdr = { > + .ihl = sizeof(iphdr) / 4, > + .version = 4, > + .tot_len = htons(sizeof(iphdr) + len), > + .ttl = 255, > + .protocol = protocol, > + .saddr = TUN_IPADDR_SOURCE, > + .daddr = TUN_IPADDR_DEST > + }; > + > + iphdr.check = tun_build_ip_check(tun_sum(&iphdr, sizeof(iphdr))); > + memcpy(dest, &iphdr, sizeof(iphdr)); > +} > + > +static void tun_build_ipv6hdr(void *dest, uint16_t len, uint8_t protocol) > +{ > + struct ipv6hdr ipv6hdr = { > + .version = 6, > + .payload_len = htons(len), > + .nexthdr = protocol, > + .saddr = { > + .s6_addr32 = { > + htonl(0xffff0000), 0, 0, TUN_IPADDR_SOURCE > + } > + }, > + .daddr = { > + .s6_addr32 = { > + htonl(0xffff0000), 0, 0, TUN_IPADDR_DEST > + } > + }, > + }; > + > + memcpy(dest, &ipv6hdr, sizeof(ipv6hdr)); > +} > + > +static void tun_build_tcphdr(void *dest, uint32_t sum) > +{ > + struct tcphdr tcphdr = { > + .source = htons(9), > + .dest = htons(9), > + .fin = 1, > + .doff = sizeof(tcphdr) / 4, > + }; > + uint32_t tcp_sum = tun_sum(&tcphdr, sizeof(tcphdr)); > + > + tcphdr.check = tun_build_ip_check(sum + tcp_sum); > + memcpy(dest, &tcphdr, sizeof(tcphdr)); > +} > + > +static void tun_build_udphdr(void *dest, uint32_t sum) > +{ > + struct udphdr udphdr = { > + .source = htons(9), > + .dest = htons(9), > + .len = htons(sizeof(udphdr)), > + }; > + uint32_t udp_sum = tun_sum(&udphdr, sizeof(udphdr)); > + > + udphdr.check = tun_build_ip_check(sum + udp_sum); > + memcpy(dest, &udphdr, sizeof(udphdr)); > +} > + > +static bool tun_vnet_hash_check(int source_fd, const int *dest_fds, > + const void *buffer, size_t len, > + uint8_t flags, > + uint16_t hash_report, uint32_t hash_value) > +{ > + size_t read_len = sizeof(struct virtio_net_hdr_v1_hash) + len; > + struct virtio_net_hdr_v1_hash *read_buffer; > + struct virtio_net_hdr_v1_hash hdr = { > + .hdr = { .flags = flags }, > + .hash_value = htole32(hash_value), > + .hash_report = htole16(hash_report) > + }; > + int ret; > + int txq = hash_report ? hash_value & 1 : 2; > + > + if (write(source_fd, buffer, len) != len) { > + perror("write"); > + return false; > + } > + > + read_buffer = malloc(read_len); > + if (!read_buffer) { > + perror("malloc"); > + return false; > + } > + > + ret = read(dest_fds[txq], read_buffer, read_len); > + if (ret != read_len) { > + perror("read"); > + free(read_buffer); > + return false; > + } > + > + ret = !memcmp(read_buffer, &hdr, sizeof(*read_buffer)) && > + !memcmp(read_buffer + 1, buffer, len); > + > + free(read_buffer); > + return ret; > +} > + > FIXTURE(tun) > { > char ifname[IFNAMSIZ]; > @@ -112,10 +314,10 @@ FIXTURE_SETUP(tun) > { > memset(self->ifname, 0, sizeof(self->ifname)); > > - self->fd = tun_alloc(self->ifname); > + self->fd = tun_alloc(self->ifname, 0); > ASSERT_GE(self->fd, 0); > > - self->fd2 = tun_alloc(self->ifname); > + self->fd2 = tun_alloc(self->ifname, 0); > ASSERT_GE(self->fd2, 0); > } > > @@ -168,7 +370,7 @@ FIXTURE(tun_deleted) > FIXTURE_SETUP(tun_deleted) > { > self->ifname[0] = 0; > - self->fd = tun_alloc(self->ifname); > + self->fd = tun_alloc(self->ifname, 0); > ASSERT_LE(0, self->fd); > > ASSERT_EQ(0, tun_delete(self->ifname)) > @@ -233,4 +435,368 @@ TEST_F(tun_deleted, setvnethash) > EXPECT_EQ(EBADFD, errno); > } > > -TEST_HARNESS_MAIN > +FIXTURE(tun_vnet_hash) > +{ > + int local_fd; > + int source_fd; > + int dest_fds[3]; > +}; > + > +FIXTURE_SETUP(tun_vnet_hash) > +{ > + static const struct { > + struct tun_vnet_hash hdr; > + struct tun_vnet_hash_rss rss; > + uint16_t rss_indirection_table[2]; > + uint8_t rss_key[40]; > + } vnet_hash = { > + .hdr = { > + .flags = TUN_VNET_HASH_REPORT | TUN_VNET_HASH_RSS, > + .types = VIRTIO_NET_RSS_HASH_TYPE_IPv4 | > + VIRTIO_NET_RSS_HASH_TYPE_TCPv4 | > + VIRTIO_NET_RSS_HASH_TYPE_UDPv4 | > + VIRTIO_NET_RSS_HASH_TYPE_IPv6 | > + VIRTIO_NET_RSS_HASH_TYPE_TCPv6 | > + VIRTIO_NET_RSS_HASH_TYPE_UDPv6 > + }, > + .rss = { .indirection_table_mask = 1, .unclassified_queue = 5 }, > + .rss_indirection_table = { 3, 4 }, > + .rss_key = { > + 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, > + 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, > + 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, > + 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, > + 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa > + } > + }; > + > + struct { > + struct virtio_net_hdr_v1_hash vnet_hdr; > + struct ethhdr ethhdr; > + struct arphdr arphdr; > + unsigned char sender_hwaddr[6]; > + uint32_t sender_ipaddr; > + unsigned char target_hwaddr[6]; > + uint32_t target_ipaddr; > + } __packed packet = { > + .ethhdr = { > + .h_source = TUN_HWADDR_SOURCE, > + .h_dest = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, > + .h_proto = htons(ETH_P_ARP) > + }, > + .arphdr = { > + .ar_hrd = htons(ARPHRD_ETHER), > + .ar_pro = htons(ETH_P_IP), > + .ar_hln = ETH_ALEN, > + .ar_pln = 4, > + .ar_op = htons(ARPOP_REQUEST) > + }, > + .sender_hwaddr = TUN_HWADDR_DEST, > + .sender_ipaddr = TUN_IPADDR_DEST, > + .target_ipaddr = TUN_IPADDR_DEST > + }; > + > + struct tun_vnet_hash cap; > + char source_ifname[IFNAMSIZ] = ""; > + char dest_ifname[IFNAMSIZ] = ""; > + int i; > + > + self->local_fd = socket(AF_LOCAL, SOCK_STREAM, 0); > + ASSERT_LE(0, self->local_fd); > + > + self->source_fd = tun_alloc(source_ifname, 0); > + ASSERT_LE(0, self->source_fd) { > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + i = ioctl(self->source_fd, TUNGETVNETHASHCAP, &cap); > + if (i == -1 && errno == EINVAL) { > + EXPECT_EQ(0, close(self->local_fd)); > + SKIP(return, "TUNGETVNETHASHCAP not supported"); > + } > + > + ASSERT_EQ(0, i) > + EXPECT_EQ(0, close(self->local_fd)); > + > + if ((cap.flags & vnet_hash.hdr.flags) != vnet_hash.hdr.flags) { > + EXPECT_EQ(0, close(self->local_fd)); > + SKIP(return, "Lacks some hash flag support"); > + } > + > + if ((cap.types & vnet_hash.hdr.types) != vnet_hash.hdr.types) { > + EXPECT_EQ(0, close(self->local_fd)); > + SKIP(return, "Lacks some hash type support"); > + } > + > + ASSERT_TRUE(tun_set_flags(self->local_fd, source_ifname, IFF_UP)) > + EXPECT_EQ(0, close(self->local_fd)); > + > + self->dest_fds[0] = tun_alloc(dest_ifname, IFF_VNET_HDR); > + ASSERT_LE(0, self->dest_fds[0]) { > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + i = sizeof(struct virtio_net_hdr_v1_hash); > + ASSERT_EQ(0, ioctl(self->dest_fds[0], TUNSETVNETHDRSZ, &i)) { > + EXPECT_EQ(0, close(self->dest_fds[0])); > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + i = 1; > + ASSERT_EQ(0, ioctl(self->dest_fds[0], TUNSETVNETLE, &i)) { > + EXPECT_EQ(0, close(self->dest_fds[0])); > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + ASSERT_TRUE(tun_set_flags(self->local_fd, dest_ifname, IFF_UP)) { > + EXPECT_EQ(0, close(self->dest_fds[0])); > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + ASSERT_EQ(sizeof(packet), > + write(self->dest_fds[0], &packet, sizeof(packet))) { > + EXPECT_EQ(0, close(self->dest_fds[0])); > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + ASSERT_EQ(0, ioctl(self->dest_fds[0], TUNSETVNETHASH, &vnet_hash)) { > + EXPECT_EQ(0, close(self->dest_fds[0])); > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + for (i = 1; i < ARRAY_SIZE(self->dest_fds); i++) { > + self->dest_fds[i] = tun_alloc(dest_ifname, IFF_VNET_HDR); > + ASSERT_LE(0, self->dest_fds[i]) { > + while (i) { > + i--; > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + } > + > + ASSERT_EQ(0, ioctl(self->local_fd, SIOCBRADDBR, "xbridge")) { > + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); > + > + for (i = 0; i < ARRAY_SIZE(self->dest_fds); i++) > + EXPECT_EQ(0, close(self->dest_fds[i])); > + > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + ASSERT_TRUE(tun_add_to_bridge(self->local_fd, source_ifname)) { > + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); > + > + for (i = 0; i < ARRAY_SIZE(self->dest_fds); i++) > + EXPECT_EQ(0, close(self->dest_fds[i])); > + > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + ASSERT_TRUE(tun_add_to_bridge(self->local_fd, dest_ifname)) { > + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); > + > + for (i = 0; i < ARRAY_SIZE(self->dest_fds); i++) > + EXPECT_EQ(0, close(self->dest_fds[i])); > + > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + ASSERT_TRUE(tun_set_flags(self->local_fd, "xbridge", IFF_UP)) { > + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); > + > + for (i = 0; i < ARRAY_SIZE(self->dest_fds); i++) > + EXPECT_EQ(0, close(self->dest_fds[i])); > + > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > +} > + > +FIXTURE_TEARDOWN(tun_vnet_hash) > +{ > + ASSERT_TRUE(tun_set_flags(self->local_fd, "xbridge", 0)) { > + for (size_t i = 0; i < ARRAY_SIZE(self->dest_fds); i++) > + EXPECT_EQ(0, close(self->dest_fds[i])); > + > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > + } > + > + EXPECT_EQ(0, ioctl(self->local_fd, SIOCBRDELBR, "xbridge")); > + > + for (size_t i = 0; i < ARRAY_SIZE(self->dest_fds); i++) > + EXPECT_EQ(0, close(self->dest_fds[i])); > + > + EXPECT_EQ(0, close(self->source_fd)); > + EXPECT_EQ(0, close(self->local_fd)); > +} > + > +TEST_F(tun_vnet_hash, unclassified) > +{ > + struct { > + struct ethhdr ethhdr; > + struct iphdr iphdr; > + } __packed packet; > + > + tun_build_ethhdr(&packet.ethhdr, ETH_P_LOOPBACK); > + > + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, > + &packet, sizeof(packet), 0, > + VIRTIO_NET_HASH_REPORT_NONE, 0)); > +} > + > +TEST_F(tun_vnet_hash, ipv4) > +{ > + struct { > + struct ethhdr ethhdr; > + struct iphdr iphdr; > + } __packed packet; > + > + tun_build_ethhdr(&packet.ethhdr, ETH_P_IP); > + tun_build_iphdr(&packet.iphdr, 0, 253); > + > + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, > + &packet, sizeof(packet), 0, > + VIRTIO_NET_HASH_REPORT_IPv4, > + 0x6e45d952)); > +} > + > +TEST_F(tun_vnet_hash, tcpv4) > +{ > + struct { > + struct ethhdr ethhdr; > + struct iphdr iphdr; > + struct tcphdr tcphdr; > + } __packed packet; > + > + tun_build_ethhdr(&packet.ethhdr, ETH_P_IP); > + tun_build_iphdr(&packet.iphdr, sizeof(struct tcphdr), IPPROTO_TCP); > + > + tun_build_tcphdr(&packet.tcphdr, > + tun_build_ip_pseudo_sum(&packet.iphdr)); > + > + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, > + &packet, sizeof(packet), > + VIRTIO_NET_HDR_F_DATA_VALID, > + VIRTIO_NET_HASH_REPORT_TCPv4, > + 0xfb63539a)); > +} > + > +TEST_F(tun_vnet_hash, udpv4) > +{ > + struct { > + struct ethhdr ethhdr; > + struct iphdr iphdr; > + struct udphdr udphdr; > + } __packed packet; > + > + tun_build_ethhdr(&packet.ethhdr, ETH_P_IP); > + tun_build_iphdr(&packet.iphdr, sizeof(struct udphdr), IPPROTO_UDP); > + > + tun_build_udphdr(&packet.udphdr, > + tun_build_ip_pseudo_sum(&packet.iphdr)); > + > + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, > + &packet, sizeof(packet), > + VIRTIO_NET_HDR_F_DATA_VALID, > + VIRTIO_NET_HASH_REPORT_UDPv4, > + 0xfb63539a)); > +} > + > +TEST_F(tun_vnet_hash, ipv6) > +{ > + struct { > + struct ethhdr ethhdr; > + struct ipv6hdr ipv6hdr; > + } __packed packet; > + > + tun_build_ethhdr(&packet.ethhdr, ETH_P_IPV6); > + tun_build_ipv6hdr(&packet.ipv6hdr, 0, 253); > + > + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, > + &packet, sizeof(packet), 0, > + VIRTIO_NET_HASH_REPORT_IPv6, > + 0xd6eb560f)); > +} > + > +TEST_F(tun_vnet_hash, tcpv6) > +{ > + struct { > + struct ethhdr ethhdr; > + struct ipv6hdr ipv6hdr; > + struct tcphdr tcphdr; > + } __packed packet; > + > + tun_build_ethhdr(&packet.ethhdr, ETH_P_IPV6); > + tun_build_ipv6hdr(&packet.ipv6hdr, sizeof(struct tcphdr), IPPROTO_TCP); > + > + tun_build_tcphdr(&packet.tcphdr, > + tun_build_ipv6_pseudo_sum(&packet.ipv6hdr)); > + > + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, > + &packet, sizeof(packet), > + VIRTIO_NET_HDR_F_DATA_VALID, > + VIRTIO_NET_HASH_REPORT_TCPv6, > + 0xc2b9f251)); > +} > + > +TEST_F(tun_vnet_hash, udpv6) > +{ > + struct { > + struct ethhdr ethhdr; > + struct ipv6hdr ipv6hdr; > + struct udphdr udphdr; > + } __packed packet; > + > + tun_build_ethhdr(&packet.ethhdr, ETH_P_IPV6); > + tun_build_ipv6hdr(&packet.ipv6hdr, sizeof(struct udphdr), IPPROTO_UDP); > + > + tun_build_udphdr(&packet.udphdr, > + tun_build_ipv6_pseudo_sum(&packet.ipv6hdr)); > + > + EXPECT_TRUE(tun_vnet_hash_check(self->source_fd, self->dest_fds, > + &packet, sizeof(packet), > + VIRTIO_NET_HDR_F_DATA_VALID, > + VIRTIO_NET_HASH_REPORT_UDPv6, > + 0xc2b9f251)); > +} > + > +int main(int argc, char **argv) > +{ > + FILE *file; > + > + if (unshare(CLONE_NEWNET)) { > + perror("unshare"); > + return KSFT_FAIL; > + } > + > + file = fopen("/proc/sys/net/ipv6/conf/default/disable_ipv6", "w"); > + if (file) { > + if (fputc('1', file) != '1') { > + perror("fputc"); > + return KSFT_FAIL; > + } > + > + if (fclose(file)) { > + perror("fclose"); > + return KSFT_FAIL; > + } > + } else if (errno != ENOENT) { > + perror("fopen"); > + return KSFT_FAIL; > + } What is the purpose of this disable_ipv6? > + > + return test_harness_run(argc, argv); > +} > > -- > 2.48.1 >