Add self tests for ULP operations, flow setup and crypto tests.
Signed-off-by: Adel Abouchaev adel.abushaev@gmail.com
---
Restored the test build. Changed the QUIC context reference variable names for the keys and iv to match the uAPI.
Updated alignment, added SPDX license line.
v3: Added Chacha20-Poly1305 test. v3: Added test to fail sending with wrong key generation bit. --- tools/testing/selftests/net/.gitignore | 1 + tools/testing/selftests/net/Makefile | 3 +- tools/testing/selftests/net/quic.c | 1369 ++++++++++++++++++++++++ tools/testing/selftests/net/quic.sh | 46 + 4 files changed, 1418 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/quic.c create mode 100755 tools/testing/selftests/net/quic.sh
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 3d7adee7a3e6..78970a09d73c 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -14,6 +14,7 @@ nettest psock_fanout psock_snd psock_tpacket +quic reuseaddr_conflict reuseaddr_ports_exhausted reuseport_addr_any diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index f5ac1433c301..b4e9586a2d03 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -44,6 +44,7 @@ TEST_PROGS += arp_ndisc_untracked_subnets.sh TEST_PROGS += stress_reuseport_listen.sh TEST_PROGS += l2_tos_ttl_inherit.sh TEST_PROGS += bind_bhash.sh +TEST_PROGS += quic.sh TEST_PROGS_EXTENDED := in_netns.sh setup_loopback.sh setup_veth.sh TEST_PROGS_EXTENDED += toeplitz_client.sh toeplitz.sh TEST_GEN_FILES = socket nettest @@ -59,7 +60,7 @@ TEST_GEN_FILES += ipsec TEST_GEN_FILES += ioam6_parser TEST_GEN_FILES += gro TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa -TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls tun tap +TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls tun tap quic TEST_GEN_FILES += toeplitz TEST_GEN_FILES += cmsg_sender TEST_GEN_FILES += stress_reuseport_listen diff --git a/tools/testing/selftests/net/quic.c b/tools/testing/selftests/net/quic.c new file mode 100644 index 000000000000..81285a6d9601 --- /dev/null +++ b/tools/testing/selftests/net/quic.c @@ -0,0 +1,1369 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE + +#include <arpa/inet.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <poll.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <linux/limits.h> +#include <linux/quic.h> +#include <linux/socket.h> +#include <linux/tls.h> +#include <linux/tcp.h> +#include <linux/types.h> +#include <linux/udp.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/sendfile.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include "../kselftest_harness.h" + +#define UDP_ULP 105 + +#ifndef SOL_UDP +#define SOL_UDP 17 +#endif + +// 1. QUIC ULP Registration Test + +FIXTURE(quic_ulp) +{ + int sfd; + socklen_t len_s; + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + } server; + int default_net_ns_fd; + int server_net_ns_fd; +}; + +FIXTURE_VARIANT(quic_ulp) +{ + unsigned int af_server; + char *server_address; + unsigned short server_port; +}; + +FIXTURE_VARIANT_ADD(quic_ulp, ipv4) +{ + .af_server = AF_INET, + .server_address = "10.0.0.2", + .server_port = 7101, +}; + +FIXTURE_VARIANT_ADD(quic_ulp, ipv6) +{ + .af_server = AF_INET6, + .server_address = "2001::2", + .server_port = 7102, +}; + +FIXTURE_SETUP(quic_ulp) +{ + char path[PATH_MAX]; + int optval = 1; + + snprintf(path, sizeof(path), "/proc/%d/ns/net", getpid()); + self->default_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->default_net_ns_fd, 0); + strcpy(path, "/var/run/netns/ns2"); + self->server_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->server_net_ns_fd, 0); + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + self->sfd = socket(variant->af_server, SOCK_DGRAM, 0); + ASSERT_NE(setsockopt(self->sfd, SOL_SOCKET, SO_REUSEPORT, &optval, + sizeof(optval)), -1); + if (variant->af_server == AF_INET) { + self->len_s = sizeof(self->server.addr); + self->server.addr.sin_family = variant->af_server; + inet_pton(variant->af_server, variant->server_address, + &self->server.addr.sin_addr); + self->server.addr.sin_port = htons(variant->server_port); + ASSERT_EQ(bind(self->sfd, &self->server.addr, self->len_s), 0); + ASSERT_EQ(getsockname(self->sfd, &self->server.addr, + &self->len_s), 0); + } else { + self->len_s = sizeof(self->server.addr6); + self->server.addr6.sin6_family = variant->af_server; + inet_pton(variant->af_server, variant->server_address, + &self->server.addr6.sin6_addr); + self->server.addr6.sin6_port = htons(variant->server_port); + ASSERT_EQ(bind(self->sfd, &self->server.addr6, self->len_s), 0); + ASSERT_EQ(getsockname(self->sfd, &self->server.addr6, + &self->len_s), 0); + } + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +}; + +FIXTURE_TEARDOWN(quic_ulp) +{ + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + close(self->sfd); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +}; + +TEST_F(quic_ulp, request_nonexistent_udp_ulp) +{ + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_ULP, + "nonexistent", sizeof("nonexistent")), -1); + // If UDP_ULP option is not present, the error would be ENOPROTOOPT. + ASSERT_EQ(errno, ENOENT); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +}; + +TEST_F(quic_ulp, request_quic_crypto_udp_ulp) +{ + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_ULP, + "quic-crypto", sizeof("quic-crypto")), 0); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +}; + +// 2. QUIC Data Path Operation Tests + +#define DO_NOT_SETUP_FLOW 0 +#define SETUP_FLOW 1 + +#define DO_NOT_USE_CLIENT 0 +#define USE_CLIENT 1 + +FIXTURE(quic_data) +{ + int sfd, c1fd, c2fd; + socklen_t len_c1; + socklen_t len_c2; + socklen_t len_s; + + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + } client_1; + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + } client_2; + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + } server; + int default_net_ns_fd; + int client_1_net_ns_fd; + int client_2_net_ns_fd; + int server_net_ns_fd; +}; + +FIXTURE_VARIANT(quic_data) +{ + unsigned int af_client_1; + char *client_1_address; + unsigned short client_1_port; + uint8_t conn_id_1[8]; + uint8_t conn_1_key[16]; + uint8_t conn_1_iv[12]; + uint8_t conn_1_hdr_key[16]; + size_t conn_id_1_len; + bool setup_flow_1; + bool use_client_1; + unsigned int af_client_2; + char *client_2_address; + unsigned short client_2_port; + uint8_t conn_id_2[8]; + uint8_t conn_2_key[16]; + uint8_t conn_2_iv[12]; + uint8_t conn_2_hdr_key[16]; + size_t conn_id_2_len; + bool setup_flow_2; + bool use_client_2; + unsigned int af_server; + char *server_address; + unsigned short server_port; +}; + +FIXTURE_VARIANT_ADD(quic_data, ipv4) +{ + .af_client_1 = AF_INET, + .client_1_address = "10.0.0.1", + .client_1_port = 6667, + .conn_id_1 = {0x11, 0x12, 0x13, 0x14}, + .conn_id_1_len = 4, + .setup_flow_1 = SETUP_FLOW, + .use_client_1 = USE_CLIENT, + .af_client_2 = AF_INET, + .client_2_address = "10.0.0.3", + .client_2_port = 6668, + .conn_id_2 = {0x21, 0x22, 0x23, 0x24}, + .conn_id_2_len = 4, + .setup_flow_2 = SETUP_FLOW, + //.use_client_2 = USE_CLIENT, + .af_server = AF_INET, + .server_address = "10.0.0.2", + .server_port = 6669, +}; + +FIXTURE_VARIANT_ADD(quic_data, ipv6_mapped_ipv4_two_conns) +{ + .af_client_1 = AF_INET6, + .client_1_address = "::ffff:10.0.0.1", + .client_1_port = 6670, + .conn_id_1 = {0x11, 0x12, 0x13, 0x14}, + .conn_id_1_len = 4, + .setup_flow_1 = SETUP_FLOW, + .use_client_1 = USE_CLIENT, + .af_client_2 = AF_INET6, + .client_2_address = "::ffff:10.0.0.3", + .client_2_port = 6671, + .conn_id_2 = {0x21, 0x22, 0x23, 0x24}, + .conn_id_2_len = 4, + .setup_flow_2 = SETUP_FLOW, + .use_client_2 = USE_CLIENT, + .af_server = AF_INET6, + .server_address = "::ffff:10.0.0.2", + .server_port = 6672, +}; + +FIXTURE_VARIANT_ADD(quic_data, ipv6_mapped_ipv4_setup_ipv4_one_conn) +{ + .af_client_1 = AF_INET, + .client_1_address = "10.0.0.3", + .client_1_port = 6676, + .conn_id_1 = {0x11, 0x12, 0x13, 0x14}, + .conn_id_1_len = 4, + .setup_flow_1 = SETUP_FLOW, + .use_client_1 = DO_NOT_USE_CLIENT, + .af_client_2 = AF_INET6, + .client_2_address = "::ffff:10.0.0.3", + .client_2_port = 6676, + .conn_id_2 = {0x11, 0x12, 0x13, 0x14}, + .conn_id_2_len = 4, + .setup_flow_2 = DO_NOT_SETUP_FLOW, + .use_client_2 = USE_CLIENT, + .af_server = AF_INET6, + .server_address = "::ffff:10.0.0.2", + .server_port = 6677, +}; + +FIXTURE_VARIANT_ADD(quic_data, ipv6_mapped_ipv4_setup_ipv6_one_conn) +{ + .af_client_1 = AF_INET6, + .client_1_address = "::ffff:10.0.0.3", + .client_1_port = 6678, + .conn_id_1 = {0x11, 0x12, 0x13, 0x14}, + .setup_flow_1 = SETUP_FLOW, + .use_client_1 = DO_NOT_USE_CLIENT, + .af_client_2 = AF_INET, + .client_2_address = "10.0.0.3", + .client_2_port = 6678, + .conn_id_2 = {0x11, 0x12, 0x13, 0x14}, + .setup_flow_2 = DO_NOT_SETUP_FLOW, + .use_client_2 = USE_CLIENT, + .af_server = AF_INET6, + .server_address = "::ffff:10.0.0.2", + .server_port = 6679, +}; + +FIXTURE_SETUP(quic_data) +{ + char path[PATH_MAX]; + int optval = 1; + + if (variant->af_client_1 == AF_INET) { + self->len_c1 = sizeof(self->client_1.addr); + self->client_1.addr.sin_family = variant->af_client_1; + inet_pton(variant->af_client_1, variant->client_1_address, + &self->client_1.addr.sin_addr); + self->client_1.addr.sin_port = htons(variant->client_1_port); + } else { + self->len_c1 = sizeof(self->client_1.addr6); + self->client_1.addr6.sin6_family = variant->af_client_1; + inet_pton(variant->af_client_1, variant->client_1_address, + &self->client_1.addr6.sin6_addr); + self->client_1.addr6.sin6_port = htons(variant->client_1_port); + } + + if (variant->af_client_2 == AF_INET) { + self->len_c2 = sizeof(self->client_2.addr); + self->client_2.addr.sin_family = variant->af_client_2; + inet_pton(variant->af_client_2, variant->client_2_address, + &self->client_2.addr.sin_addr); + self->client_2.addr.sin_port = htons(variant->client_2_port); + } else { + self->len_c2 = sizeof(self->client_2.addr6); + self->client_2.addr6.sin6_family = variant->af_client_2; + inet_pton(variant->af_client_2, variant->client_2_address, + &self->client_2.addr6.sin6_addr); + self->client_2.addr6.sin6_port = htons(variant->client_2_port); + } + + if (variant->af_server == AF_INET) { + self->len_s = sizeof(self->server.addr); + self->server.addr.sin_family = variant->af_server; + inet_pton(variant->af_server, variant->server_address, + &self->server.addr.sin_addr); + self->server.addr.sin_port = htons(variant->server_port); + } else { + self->len_s = sizeof(self->server.addr6); + self->server.addr6.sin6_family = variant->af_server; + inet_pton(variant->af_server, variant->server_address, + &self->server.addr6.sin6_addr); + self->server.addr6.sin6_port = htons(variant->server_port); + } + + snprintf(path, sizeof(path), "/proc/%d/ns/net", getpid()); + self->default_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->default_net_ns_fd, 0); + strcpy(path, "/var/run/netns/ns11"); + self->client_1_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->client_1_net_ns_fd, 0); + strcpy(path, "/var/run/netns/ns12"); + self->client_2_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->client_2_net_ns_fd, 0); + strcpy(path, "/var/run/netns/ns2"); + self->server_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->server_net_ns_fd, 0); + + if (variant->use_client_1) { + ASSERT_NE(setns(self->client_1_net_ns_fd, 0), -1); + self->c1fd = socket(variant->af_client_1, SOCK_DGRAM, 0); + ASSERT_NE(setsockopt(self->c1fd, SOL_SOCKET, SO_REUSEPORT, + &optval, sizeof(optval)), -1); + if (variant->af_client_1 == AF_INET) { + ASSERT_EQ(bind(self->c1fd, &self->client_1.addr, + self->len_c1), 0); + ASSERT_EQ(getsockname(self->c1fd, &self->client_1.addr, + &self->len_c1), 0); + } else { + ASSERT_EQ(bind(self->c1fd, &self->client_1.addr6, + self->len_c1), 0); + ASSERT_EQ(getsockname(self->c1fd, &self->client_1.addr6, + &self->len_c1), 0); + } + } + + if (variant->use_client_2) { + ASSERT_NE(setns(self->client_2_net_ns_fd, 0), -1); + self->c2fd = socket(variant->af_client_2, SOCK_DGRAM, 0); + ASSERT_NE(setsockopt(self->c2fd, SOL_SOCKET, SO_REUSEPORT, + &optval, sizeof(optval)), -1); + if (variant->af_client_2 == AF_INET) { + ASSERT_EQ(bind(self->c2fd, &self->client_2.addr, + self->len_c2), 0); + ASSERT_EQ(getsockname(self->c2fd, &self->client_2.addr, + &self->len_c2), 0); + } else { + ASSERT_EQ(bind(self->c2fd, &self->client_2.addr6, + self->len_c2), 0); + ASSERT_EQ(getsockname(self->c2fd, &self->client_2.addr6, + &self->len_c2), 0); + } + } + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + self->sfd = socket(variant->af_server, SOCK_DGRAM, 0); + ASSERT_NE(setsockopt(self->sfd, SOL_SOCKET, SO_REUSEPORT, &optval, + sizeof(optval)), -1); + if (variant->af_server == AF_INET) { + ASSERT_EQ(bind(self->sfd, &self->server.addr, self->len_s), 0); + ASSERT_EQ(getsockname(self->sfd, &self->server.addr, + &self->len_s), 0); + } else { + ASSERT_EQ(bind(self->sfd, &self->server.addr6, self->len_s), 0); + ASSERT_EQ(getsockname(self->sfd, &self->server.addr6, + &self->len_s), 0); + } + + ASSERT_EQ(setsockopt(self->sfd, IPPROTO_UDP, UDP_ULP, + "quic-crypto", sizeof("quic-crypto")), 0); + + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +} + +FIXTURE_TEARDOWN(quic_data) +{ + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + close(self->sfd); + ASSERT_NE(setns(self->client_1_net_ns_fd, 0), -1); + close(self->c1fd); + ASSERT_NE(setns(self->client_2_net_ns_fd, 0), -1); + close(self->c2fd); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +} + +TEST_F(quic_data, send_fail_no_flow) +{ + char const *test_str = "test_read"; + int send_len = 10; + + ASSERT_EQ(strlen(test_str) + 1, send_len); + EXPECT_EQ(sendto(self->sfd, test_str, send_len, 0, + &self->client_1.addr, self->len_c1), -1); +}; + +TEST_F(quic_data, fail_wrong_key_generation_bit) +{ + size_t cmsg_tx_len = sizeof(struct quic_tx_ancillary_data); + uint8_t cmsg_buf[CMSG_SPACE(cmsg_tx_len)]; + struct quic_connection_info conn_1_info; + struct quic_connection_info conn_2_info; + struct quic_tx_ancillary_data *anc_data; + struct cmsghdr *cmsg_hdr; + int frag_size = 1200; + struct iovec iov[2]; + int msg_len = 4500; + struct msghdr msg; + char *test_str_1; + char *test_str_2; + char *buf_1; + char *buf_2; + int i; + + test_str_1 = (char *)malloc(9000); + test_str_2 = (char *)malloc(9000); + memset(test_str_1, 0, 9000); + memset(test_str_2, 0, 9000); + + buf_1 = (char *)malloc(10000); + buf_2 = (char *)malloc(10000); + for (i = 0; i < 9000; i += (1200 - 16)) { + test_str_1[i] = 0x44; + memcpy(&test_str_1[i + 1], &variant->conn_id_1, + variant->conn_id_1_len); + test_str_1[i + 1 + variant->conn_id_1_len] = 0xca; + + test_str_2[i] = 0x44; + memcpy(&test_str_2[i + 1], &variant->conn_id_2, + variant->conn_id_2_len); + test_str_2[i + 1 + variant->conn_id_2_len] = 0xca; + } + + // program the connection into the offload + conn_1_info.cipher_type = TLS_CIPHER_AES_GCM_128; + memset(&conn_1_info.key, 0, sizeof(struct quic_connection_info_key)); + conn_1_info.key.dst_conn_id_length = variant->conn_id_1_len; + memcpy(conn_1_info.key.dst_conn_id, + &variant->conn_id_1, + variant->conn_id_1_len); + conn_1_info.conn_payload_key_gen = 0; + + if (self->client_1.addr.sin_family == AF_INET) { + memcpy(&conn_1_info.key.addr.ipv4_addr, + &self->client_1.addr.sin_addr, sizeof(struct in_addr)); + conn_1_info.key.udp_port = self->client_1.addr.sin_port; + } else { + memcpy(&conn_1_info.key.addr.ipv6_addr, + &self->client_1.addr6.sin6_addr, + sizeof(struct in6_addr)); + conn_1_info.key.udp_port = self->client_1.addr6.sin6_port; + } + + conn_2_info.cipher_type = TLS_CIPHER_AES_GCM_128; + memset(&conn_2_info.key, 0, sizeof(struct quic_connection_info_key)); + conn_2_info.key.dst_conn_id_length = variant->conn_id_2_len; + memcpy(conn_2_info.key.dst_conn_id, + &variant->conn_id_2, + variant->conn_id_2_len); + conn_2_info.conn_payload_key_gen = 0; + + if (self->client_2.addr.sin_family == AF_INET) { + memcpy(&conn_2_info.key.addr.ipv4_addr, + &self->client_2.addr.sin_addr, sizeof(struct in_addr)); + conn_2_info.key.udp_port = self->client_2.addr.sin_port; + } else { + memcpy(&conn_2_info.key.addr.ipv6_addr, + &self->client_2.addr6.sin6_addr, + sizeof(struct in6_addr)); + conn_2_info.key.udp_port = self->client_2.addr6.sin6_port; + } + + memcpy(&conn_1_info.aes_gcm_128.payload_key, + &variant->conn_1_key, 16); + memcpy(&conn_1_info.aes_gcm_128.payload_iv, + &variant->conn_1_iv, 12); + memcpy(&conn_1_info.aes_gcm_128.header_key, + &variant->conn_1_hdr_key, 16); + memcpy(&conn_2_info.aes_gcm_128.payload_key, + &variant->conn_2_key, 16); + memcpy(&conn_2_info.aes_gcm_128.payload_iv, + &variant->conn_2_iv, 12); + memcpy(&conn_2_info.aes_gcm_128.header_key, + &variant->conn_2_hdr_key, + 16); + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_SEGMENT, &frag_size, + sizeof(frag_size)), 0); + + if (variant->setup_flow_1) + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, + UDP_QUIC_ADD_TX_CONNECTION, + &conn_1_info, sizeof(conn_1_info)), 0); + + if (variant->setup_flow_2) + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, + UDP_QUIC_ADD_TX_CONNECTION, + &conn_2_info, sizeof(conn_2_info)), 0); + + iov[0].iov_base = test_str_1; + iov[0].iov_len = msg_len; + iov[1].iov_base = (void *)test_str_1 + 4500; + iov[1].iov_len = msg_len; + + msg.msg_name = (self->client_1.addr.sin_family == AF_INET) + ? (void *)&self->client_1.addr + : (void *)&self->client_1.addr6; + msg.msg_namelen = self->len_c1; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + cmsg_hdr = CMSG_FIRSTHDR(&msg); + cmsg_hdr->cmsg_level = IPPROTO_UDP; + cmsg_hdr->cmsg_type = UDP_QUIC_ENCRYPT; + cmsg_hdr->cmsg_len = CMSG_LEN(cmsg_tx_len); + anc_data = (struct quic_tx_ancillary_data *)CMSG_DATA(cmsg_hdr); + anc_data->next_pkt_num = 0x0d65c9; + anc_data->flags = 0; + anc_data->dst_conn_id_length = variant->conn_id_1_len; + + if (variant->use_client_1) + EXPECT_EQ(sendmsg(self->sfd, &msg, 0), -1); + + iov[0].iov_base = test_str_2; + iov[0].iov_len = msg_len; + iov[1].iov_base = (void *)test_str_2 + 4500; + iov[1].iov_len = msg_len; + msg.msg_name = (self->client_2.addr.sin_family == AF_INET) + ? (void *)&self->client_2.addr + : (void *)&self->client_2.addr6; + msg.msg_namelen = self->len_c2; + cmsg_hdr = CMSG_FIRSTHDR(&msg); + anc_data = (struct quic_tx_ancillary_data *)CMSG_DATA(cmsg_hdr); + anc_data->next_pkt_num = 0x0d65c9; + anc_data->dst_conn_id_length = variant->conn_id_2_len; + anc_data->flags = 0; + + if (variant->use_client_2) + EXPECT_EQ(sendmsg(self->sfd, &msg, 0), -1); + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + if (variant->setup_flow_1) { + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, + UDP_QUIC_DEL_TX_CONNECTION, + &conn_1_info, sizeof(conn_1_info)), + 0); + } + if (variant->setup_flow_2) { + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, + UDP_QUIC_DEL_TX_CONNECTION, + &conn_2_info, sizeof(conn_2_info)), + 0); + } + free(test_str_1); + free(test_str_2); + free(buf_1); + free(buf_2); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +} + +TEST_F(quic_data, encrypt_two_conn_gso_1200_iov_2_size_9000_aesgcm128) +{ + size_t cmsg_tx_len = sizeof(struct quic_tx_ancillary_data); + uint8_t cmsg_buf[CMSG_SPACE(cmsg_tx_len)]; + struct quic_connection_info conn_1_info; + struct quic_connection_info conn_2_info; + struct quic_tx_ancillary_data *anc_data; + socklen_t recv_addr_len_1; + socklen_t recv_addr_len_2; + struct cmsghdr *cmsg_hdr; + int frag_size = 1200; + int send_len = 9000; + struct iovec iov[2]; + int msg_len = 4500; + struct msghdr msg; + char *test_str_1; + char *test_str_2; + char *buf_1; + char *buf_2; + int i; + + test_str_1 = (char *)malloc(9000); + test_str_2 = (char *)malloc(9000); + memset(test_str_1, 0, 9000); + memset(test_str_2, 0, 9000); + + buf_1 = (char *)malloc(10000); + buf_2 = (char *)malloc(10000); + for (i = 0; i < 9000; i += (1200 - 16)) { + test_str_1[i] = 0x40; + memcpy(&test_str_1[i + 1], &variant->conn_id_1, + variant->conn_id_1_len); + test_str_1[i + 1 + variant->conn_id_1_len] = 0xca; + + test_str_2[i] = 0x40; + memcpy(&test_str_2[i + 1], &variant->conn_id_2, + variant->conn_id_2_len); + test_str_2[i + 1 + variant->conn_id_2_len] = 0xca; + } + + // program the connection into the offload + conn_1_info.cipher_type = TLS_CIPHER_AES_GCM_128; + memset(&conn_1_info.key, 0, sizeof(struct quic_connection_info_key)); + conn_1_info.key.dst_conn_id_length = variant->conn_id_1_len; + memcpy(conn_1_info.key.dst_conn_id, + &variant->conn_id_1, + variant->conn_id_1_len); + conn_1_info.conn_payload_key_gen = 0; + + if (self->client_1.addr.sin_family == AF_INET) { + memcpy(&conn_1_info.key.addr.ipv4_addr, + &self->client_1.addr.sin_addr, sizeof(struct in_addr)); + conn_1_info.key.udp_port = self->client_1.addr.sin_port; + } else { + memcpy(&conn_1_info.key.addr.ipv6_addr, + &self->client_1.addr6.sin6_addr, + sizeof(struct in6_addr)); + conn_1_info.key.udp_port = self->client_1.addr6.sin6_port; + } + + conn_2_info.cipher_type = TLS_CIPHER_AES_GCM_128; + memset(&conn_2_info.key, 0, sizeof(struct quic_connection_info_key)); + conn_2_info.key.dst_conn_id_length = variant->conn_id_2_len; + memcpy(conn_2_info.key.dst_conn_id, + &variant->conn_id_2, + variant->conn_id_2_len); + conn_2_info.conn_payload_key_gen = 0; + + if (self->client_2.addr.sin_family == AF_INET) { + memcpy(&conn_2_info.key.addr.ipv4_addr, + &self->client_2.addr.sin_addr, sizeof(struct in_addr)); + conn_2_info.key.udp_port = self->client_2.addr.sin_port; + } else { + memcpy(&conn_2_info.key.addr.ipv6_addr, + &self->client_2.addr6.sin6_addr, + sizeof(struct in6_addr)); + conn_2_info.key.udp_port = self->client_2.addr6.sin6_port; + } + + memcpy(&conn_1_info.aes_gcm_128.payload_key, + &variant->conn_1_key, 16); + memcpy(&conn_1_info.aes_gcm_128.payload_iv, + &variant->conn_1_iv, 12); + memcpy(&conn_1_info.aes_gcm_128.header_key, + &variant->conn_1_hdr_key, 16); + memcpy(&conn_2_info.aes_gcm_128.payload_key, + &variant->conn_2_key, 16); + memcpy(&conn_2_info.aes_gcm_128.payload_iv, + &variant->conn_2_iv, 12); + memcpy(&conn_2_info.aes_gcm_128.header_key, + &variant->conn_2_hdr_key, + 16); + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_SEGMENT, &frag_size, + sizeof(frag_size)), 0); + + if (variant->setup_flow_1) + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, + UDP_QUIC_ADD_TX_CONNECTION, + &conn_1_info, sizeof(conn_1_info)), 0); + + if (variant->setup_flow_2) + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, + UDP_QUIC_ADD_TX_CONNECTION, + &conn_2_info, sizeof(conn_2_info)), 0); + + recv_addr_len_1 = self->len_c1; + recv_addr_len_2 = self->len_c2; + + iov[0].iov_base = test_str_1; + iov[0].iov_len = msg_len; + iov[1].iov_base = (void *)test_str_1 + 4500; + iov[1].iov_len = msg_len; + + msg.msg_name = (self->client_1.addr.sin_family == AF_INET) + ? (void *)&self->client_1.addr + : (void *)&self->client_1.addr6; + msg.msg_namelen = self->len_c1; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + cmsg_hdr = CMSG_FIRSTHDR(&msg); + cmsg_hdr->cmsg_level = IPPROTO_UDP; + cmsg_hdr->cmsg_type = UDP_QUIC_ENCRYPT; + cmsg_hdr->cmsg_len = CMSG_LEN(cmsg_tx_len); + anc_data = (struct quic_tx_ancillary_data *)CMSG_DATA(cmsg_hdr); + anc_data->next_pkt_num = 0x0d65c9; + anc_data->flags = 0; + anc_data->dst_conn_id_length = variant->conn_id_1_len; + + if (variant->use_client_1) + EXPECT_EQ(sendmsg(self->sfd, &msg, 0), send_len); + + iov[0].iov_base = test_str_2; + iov[0].iov_len = msg_len; + iov[1].iov_base = (void *)test_str_2 + 4500; + iov[1].iov_len = msg_len; + msg.msg_name = (self->client_2.addr.sin_family == AF_INET) + ? (void *)&self->client_2.addr + : (void *)&self->client_2.addr6; + msg.msg_namelen = self->len_c2; + cmsg_hdr = CMSG_FIRSTHDR(&msg); + anc_data = (struct quic_tx_ancillary_data *)CMSG_DATA(cmsg_hdr); + anc_data->next_pkt_num = 0x0d65c9; + anc_data->dst_conn_id_length = variant->conn_id_2_len; + anc_data->flags = 0; + + if (variant->use_client_2) + EXPECT_EQ(sendmsg(self->sfd, &msg, 0), send_len); + + if (variant->use_client_1) { + ASSERT_NE(setns(self->client_1_net_ns_fd, 0), -1); + if (variant->af_client_1 == AF_INET) { + for (i = 0; i < 7; ++i) { + EXPECT_EQ(recvfrom(self->c1fd, buf_1, 9000, 0, + &self->client_1.addr, + &recv_addr_len_1), + 1200); + // Validate framing is intact. + EXPECT_EQ(memcmp((void *)buf_1 + 1, + &variant->conn_id_1, + variant->conn_id_1_len), 0); + } + EXPECT_EQ(recvfrom(self->c1fd, buf_1, 9000, 0, + &self->client_1.addr, + &recv_addr_len_1), + 728); + EXPECT_EQ(memcmp((void *)buf_1 + 1, + &variant->conn_id_1, + variant->conn_id_1_len), 0); + } else { + for (i = 0; i < 7; ++i) { + EXPECT_EQ(recvfrom(self->c1fd, buf_1, 9000, 0, + &self->client_1.addr6, + &recv_addr_len_1), + 1200); + } + EXPECT_EQ(recvfrom(self->c1fd, buf_1, 9000, 0, + &self->client_1.addr6, + &recv_addr_len_1), + 728); + EXPECT_EQ(memcmp((void *)buf_1 + 1, + &variant->conn_id_1, + variant->conn_id_1_len), 0); + } + EXPECT_NE(memcmp(buf_1, test_str_1, send_len), 0); + } + + if (variant->use_client_2) { + ASSERT_NE(setns(self->client_2_net_ns_fd, 0), -1); + if (variant->af_client_2 == AF_INET) { + for (i = 0; i < 7; ++i) { + EXPECT_EQ(recvfrom(self->c2fd, buf_2, 9000, 0, + &self->client_2.addr, + &recv_addr_len_2), + 1200); + EXPECT_EQ(memcmp((void *)buf_2 + 1, + &variant->conn_id_2, + variant->conn_id_2_len), 0); + } + EXPECT_EQ(recvfrom(self->c2fd, buf_2, 9000, 0, + &self->client_2.addr, + &recv_addr_len_2), + 728); + EXPECT_EQ(memcmp((void *)buf_2 + 1, + &variant->conn_id_2, + variant->conn_id_2_len), 0); + } else { + for (i = 0; i < 7; ++i) { + EXPECT_EQ(recvfrom(self->c2fd, buf_2, 9000, 0, + &self->client_2.addr6, + &recv_addr_len_2), + 1200); + EXPECT_EQ(memcmp((void *)buf_2 + 1, + &variant->conn_id_2, + variant->conn_id_2_len), 0); + } + EXPECT_EQ(recvfrom(self->c2fd, buf_2, 9000, 0, + &self->client_2.addr6, + &recv_addr_len_2), + 728); + EXPECT_EQ(memcmp((void *)buf_2 + 1, + &variant->conn_id_2, + variant->conn_id_2_len), 0); + } + EXPECT_NE(memcmp(buf_2, test_str_2, send_len), 0); + } + + if (variant->use_client_1 && variant->use_client_2) + EXPECT_NE(memcmp(buf_1, buf_2, send_len), 0); + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + if (variant->setup_flow_1) { + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, + UDP_QUIC_DEL_TX_CONNECTION, + &conn_1_info, sizeof(conn_1_info)), + 0); + } + if (variant->setup_flow_2) { + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, + UDP_QUIC_DEL_TX_CONNECTION, + &conn_2_info, sizeof(conn_2_info)), + 0); + } + free(test_str_1); + free(test_str_2); + free(buf_1); + free(buf_2); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +} + +// 3. QUIC Encryption Tests + +FIXTURE(quic_crypto) +{ + int sfd, cfd; + socklen_t len_c; + socklen_t len_s; + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + } client; + union { + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + } server; + int default_net_ns_fd; + int client_net_ns_fd; + int server_net_ns_fd; +}; + +FIXTURE_VARIANT(quic_crypto) +{ + unsigned int af_client; + char *client_address; + unsigned short client_port; + uint32_t algo; + size_t conn_key_len; + uint8_t conn_id[8]; + union { + uint8_t conn_key_16[16]; + uint8_t conn_key_32[32]; + } conn_key; + uint8_t conn_iv[12]; + union { + uint8_t conn_hdr_key_16[16]; + uint8_t conn_hdr_key_32[32]; + } conn_hdr_key; + size_t conn_id_len; + bool setup_flow; + bool use_client; + unsigned int af_server; + char *server_address; + unsigned short server_port; + char plain[128]; + size_t plain_len; + char match[128]; + size_t match_len; + uint32_t next_pkt_num; +}; + +FIXTURE_SETUP(quic_crypto) +{ + char path[PATH_MAX]; + int optval = 1; + + if (variant->af_client == AF_INET) { + self->len_c = sizeof(self->client.addr); + self->client.addr.sin_family = variant->af_client; + inet_pton(variant->af_client, variant->client_address, + &self->client.addr.sin_addr); + self->client.addr.sin_port = htons(variant->client_port); + } else { + self->len_c = sizeof(self->client.addr6); + self->client.addr6.sin6_family = variant->af_client; + inet_pton(variant->af_client, variant->client_address, + &self->client.addr6.sin6_addr); + self->client.addr6.sin6_port = htons(variant->client_port); + } + + if (variant->af_server == AF_INET) { + self->len_s = sizeof(self->server.addr); + self->server.addr.sin_family = variant->af_server; + inet_pton(variant->af_server, variant->server_address, + &self->server.addr.sin_addr); + self->server.addr.sin_port = htons(variant->server_port); + } else { + self->len_s = sizeof(self->server.addr6); + self->server.addr6.sin6_family = variant->af_server; + inet_pton(variant->af_server, variant->server_address, + &self->server.addr6.sin6_addr); + self->server.addr6.sin6_port = htons(variant->server_port); + } + + snprintf(path, sizeof(path), "/proc/%d/ns/net", getpid()); + self->default_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->default_net_ns_fd, 0); + strcpy(path, "/var/run/netns/ns11"); + self->client_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->client_net_ns_fd, 0); + strcpy(path, "/var/run/netns/ns2"); + self->server_net_ns_fd = open(path, O_RDONLY); + ASSERT_GE(self->server_net_ns_fd, 0); + + if (variant->use_client) { + ASSERT_NE(setns(self->client_net_ns_fd, 0), -1); + self->cfd = socket(variant->af_client, SOCK_DGRAM, 0); + ASSERT_NE(setsockopt(self->cfd, SOL_SOCKET, SO_REUSEPORT, + &optval, sizeof(optval)), -1); + if (variant->af_client == AF_INET) { + ASSERT_EQ(bind(self->cfd, &self->client.addr, + self->len_c), 0); + ASSERT_EQ(getsockname(self->cfd, &self->client.addr, + &self->len_c), 0); + } else { + ASSERT_EQ(bind(self->cfd, &self->client.addr6, + self->len_c), 0); + ASSERT_EQ(getsockname(self->cfd, &self->client.addr6, + &self->len_c), 0); + } + } + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + self->sfd = socket(variant->af_server, SOCK_DGRAM, 0); + ASSERT_NE(setsockopt(self->sfd, SOL_SOCKET, SO_REUSEPORT, &optval, + sizeof(optval)), -1); + if (variant->af_server == AF_INET) { + ASSERT_EQ(bind(self->sfd, &self->server.addr, self->len_s), 0); + ASSERT_EQ(getsockname(self->sfd, &self->server.addr, + &self->len_s), + 0); + } else { + ASSERT_EQ(bind(self->sfd, &self->server.addr6, self->len_s), 0); + ASSERT_EQ(getsockname(self->sfd, &self->server.addr6, + &self->len_s), + 0); + } + + ASSERT_EQ(setsockopt(self->sfd, IPPROTO_UDP, UDP_ULP, + "quic-crypto", sizeof("quic-crypto")), 0); + + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +} + +FIXTURE_TEARDOWN(quic_crypto) +{ + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + close(self->sfd); + ASSERT_NE(setns(self->client_net_ns_fd, 0), -1); + close(self->cfd); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +} + +FIXTURE_VARIANT_ADD(quic_crypto, ipv4_aes_gcm_128) +{ + .af_client = AF_INET, + .client_address = "10.0.0.1", + .client_port = 7667, + .algo = TLS_CIPHER_AES_GCM_128, + .conn_key_len = 16, + .conn_id = {0x08, 0x6b, 0xbf, 0x88, 0x82, 0xb9, 0x12, 0x49}, + .conn_key = { + .conn_key_16 = {0x87, 0x71, 0xea, 0x1d, + 0xfb, 0xbe, 0x7a, 0x45, + 0xbb, 0xe2, 0x7e, 0xbc, + 0x0b, 0x53, 0x94, 0x99 + }, + }, + .conn_iv = {0x3A, 0xA7, 0x46, 0x72, 0xE9, 0x83, 0x6B, 0x55, 0xDA, + 0x66, 0x7B, 0xDA}, + .conn_hdr_key = { + .conn_hdr_key_16 = {0xc9, 0x8e, 0xfd, 0xf2, + 0x0b, 0x64, 0x8c, 0x57, + 0xb5, 0x0a, 0xb2, 0xd2, + 0x21, 0xd3, 0x66, 0xa5}, + }, + .conn_id_len = 8, + .setup_flow = SETUP_FLOW, + .use_client = USE_CLIENT, + .af_server = AF_INET, + .server_address = "10.0.0.2", + .server_port = 7669, + .plain = { 0x40, 0x08, 0x6b, 0xbf, 0x88, 0x82, 0xb9, 0x12, + 0x49, 0xca, + // payload + 0x02, 0x80, 0xde, 0x40, 0x39, 0x40, 0xf6, 0x00, + 0x01, 0x0b, 0x00, 0x0f, 0x65, 0x63, 0x68, 0x6f, + 0x20, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39 + }, + .plain_len = 37, + .match = { + 0x46, 0x08, 0x6b, 0xbf, 0x88, 0x82, 0xb9, 0x12, + 0x49, 0x1c, 0x44, 0xb8, 0x41, 0xbb, 0xcf, 0x6e, + 0x0a, 0x2a, 0x24, 0xfb, 0xb4, 0x79, 0x62, 0xea, + 0x59, 0x38, 0x1a, 0x0e, 0x50, 0x1e, 0x59, 0xed, + 0x3f, 0x8e, 0x7e, 0x5a, 0x70, 0xe4, 0x2a, 0xbc, + 0x2a, 0xfa, 0x2b, 0x54, 0xeb, 0x89, 0xc3, 0x2c, + 0xb6, 0x8c, 0x1e, 0xab, 0x2d + }, + .match_len = 53, + .next_pkt_num = 0x0d65c9, +}; + +FIXTURE_VARIANT_ADD(quic_crypto, ipv4_chacha20_poly1305) +{ + .af_client = AF_INET, + .client_address = "10.0.0.1", + .client_port = 7801, + .algo = TLS_CIPHER_CHACHA20_POLY1305, + .conn_key_len = 32, + .conn_id = {}, + .conn_id_len = 0, + .conn_key = { + .conn_key_32 = { + 0x3b, 0xfc, 0xdd, 0xd7, 0x2b, 0xcf, 0x02, 0x54, + 0x1d, 0x7f, 0xa0, 0xdd, 0x1f, 0x5f, 0x9e, 0xee, + 0xa8, 0x17, 0xe0, 0x9a, 0x69, 0x63, 0xa0, 0xe6, + 0xc7, 0xdf, 0x0f, 0x9a, 0x1b, 0xab, 0x90, 0xf2, + }, + }, + .conn_iv = { + 0xa6, 0xb5, 0xbc, 0x6a, 0xb7, 0xda, 0xfc, 0xe3, + 0x0f, 0xff, 0xf5, 0xdd, + }, + .conn_hdr_key = { + .conn_hdr_key_32 = { + 0xd6, 0x59, 0x76, 0x0d, 0x2b, 0xa4, 0x34, 0xa2, + 0x26, 0xfd, 0x37, 0xb3, 0x5c, 0x69, 0xe2, 0xda, + 0x82, 0x11, 0xd1, 0x0c, 0x4f, 0x12, 0x53, 0x87, + 0x87, 0xd6, 0x56, 0x45, 0xd5, 0xd1, 0xb8, 0xe2, + }, + }, + .setup_flow = SETUP_FLOW, + .use_client = USE_CLIENT, + .af_server = AF_INET, + .server_address = "10.0.0.2", + .server_port = 7802, + .plain = { 0x42, 0x00, 0xbf, 0xf4, 0x01 }, + .plain_len = 5, + .match = { 0x55, 0x58, 0xb1, 0xc6, 0x0a, 0xe7, 0xb6, 0xb9, + 0x32, 0xbc, 0x27, 0xd7, 0x86, 0xf4, 0xbc, 0x2b, + 0xb2, 0x0f, 0x21, 0x62, 0xba }, + .match_len = 21, + .next_pkt_num = 0x2700bff5, +}; + +FIXTURE_VARIANT_ADD(quic_crypto, ipv6_aes_gcm_128) +{ + .af_client = AF_INET6, + .client_address = "2001::1", + .client_port = 7673, + .algo = TLS_CIPHER_AES_GCM_128, + .conn_key_len = 16, + .conn_id = {0x08, 0x6b, 0xbf, 0x88, 0x82, 0xb9, 0x12, 0x49}, + .conn_key = { + .conn_key_16 = {0x87, 0x71, 0xea, 0x1d, + 0xfb, 0xbe, 0x7a, 0x45, + 0xbb, 0xe2, 0x7e, 0xbc, + 0x0b, 0x53, 0x94, 0x99 + }, + }, + .conn_iv = {0x3a, 0xa7, 0x46, 0x72, 0xe9, 0x83, 0x6b, 0x55, 0xda, + 0x66, 0x7b, 0xda}, + .conn_hdr_key = { + .conn_hdr_key_16 = {0xc9, 0x8e, 0xfd, 0xf2, + 0x0b, 0x64, 0x8c, 0x57, + 0xb5, 0x0a, 0xb2, 0xd2, + 0x21, 0xd3, 0x66, 0xa5}, + }, + .conn_id_len = 8, + .setup_flow = SETUP_FLOW, + .use_client = USE_CLIENT, + .af_server = AF_INET6, + .server_address = "2001::2", + .server_port = 7675, + .plain = { 0x40, 0x08, 0x6b, 0xbf, 0x88, 0x82, 0xb9, 0x12, + 0x49, 0xca, + // Payload + 0x02, 0x80, 0xde, 0x40, 0x39, 0x40, 0xf6, 0x00, + 0x01, 0x0b, 0x00, 0x0f, 0x65, 0x63, 0x68, 0x6f, + 0x20, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39 + }, + .plain_len = 37, + .match = { + 0x46, 0x08, 0x6b, 0xbf, 0x88, 0x82, 0xb9, 0x12, + 0x49, 0x1c, 0x44, 0xb8, 0x41, 0xbb, 0xcf, 0x6e, + 0x0a, 0x2a, 0x24, 0xfb, 0xb4, 0x79, 0x62, 0xea, + 0x59, 0x38, 0x1a, 0x0e, 0x50, 0x1e, 0x59, 0xed, + 0x3f, 0x8e, 0x7e, 0x5a, 0x70, 0xe4, 0x2a, 0xbc, + 0x2a, 0xfa, 0x2b, 0x54, 0xeb, 0x89, 0xc3, 0x2c, + 0xb6, 0x8c, 0x1e, 0xab, 0x2d + }, + .match_len = 53, + .next_pkt_num = 0x0d65c9, +}; + +FIXTURE_VARIANT_ADD(quic_crypto, ipv6_chacha20_poly1305) +{ + .af_client = AF_INET6, + .client_address = "2001::1", + .client_port = 7803, + .algo = TLS_CIPHER_CHACHA20_POLY1305, + .conn_key_len = 32, + .conn_id = {}, + .conn_id_len = 0, + .conn_key = { + .conn_key_32 = { + 0x3b, 0xfc, 0xdd, 0xd7, 0x2b, 0xcf, 0x02, 0x54, + 0x1d, 0x7f, 0xa0, 0xdd, 0x1f, 0x5f, 0x9e, 0xee, + 0xa8, 0x17, 0xe0, 0x9a, 0x69, 0x63, 0xa0, 0xe6, + 0xc7, 0xdf, 0x0f, 0x9a, 0x1b, 0xab, 0x90, 0xf2, + }, + }, + .conn_iv = { + 0xa6, 0xb5, 0xbc, 0x6a, 0xb7, 0xda, 0xfc, 0xe3, + 0x0f, 0xff, 0xf5, 0xdd, + }, + .conn_hdr_key = { + .conn_hdr_key_32 = { + 0xd6, 0x59, 0x76, 0x0d, 0x2b, 0xa4, 0x34, 0xa2, + 0x26, 0xfd, 0x37, 0xb3, 0x5c, 0x69, 0xe2, 0xda, + 0x82, 0x11, 0xd1, 0x0c, 0x4f, 0x12, 0x53, 0x87, + 0x87, 0xd6, 0x56, 0x45, 0xd5, 0xd1, 0xb8, 0xe2, + }, + }, + .setup_flow = SETUP_FLOW, + .use_client = USE_CLIENT, + .af_server = AF_INET6, + .server_address = "2001::2", + .server_port = 7804, + .plain = { 0x42, 0x00, 0xbf, 0xf4, 0x01 }, + .plain_len = 5, + .match = { 0x55, 0x58, 0xb1, 0xc6, 0x0a, 0xe7, 0xb6, 0xb9, + 0x32, 0xbc, 0x27, 0xd7, 0x86, 0xf4, 0xbc, 0x2b, + 0xb2, 0x0f, 0x21, 0x62, 0xba }, + .match_len = 21, + .next_pkt_num = 0x2700bff5, +}; + +TEST_F(quic_crypto, encrypt_test_vector_single_flow_gso_in_control) +{ + uint8_t cmsg_buf[CMSG_SPACE(sizeof(struct quic_tx_ancillary_data)) + + CMSG_SPACE(sizeof(uint16_t))]; + struct quic_tx_ancillary_data *anc_data; + struct quic_connection_info conn_info; + uint16_t frag_size = 1200; + struct cmsghdr *cmsg_hdr; + int wrong_frag_size = 26; + socklen_t recv_addr_len; + struct iovec iov; + struct msghdr msg; + char *buf; + + buf = (char *)malloc(9000); + conn_info.cipher_type = variant->algo; + memset(&conn_info.key, 0, sizeof(struct quic_connection_info_key)); + conn_info.key.dst_conn_id_length = variant->conn_id_len; + memcpy(conn_info.key.dst_conn_id, + &variant->conn_id, + variant->conn_id_len); + conn_info.conn_payload_key_gen = 0; + + if (self->client.addr.sin_family == AF_INET) { + memcpy(&conn_info.key.addr.ipv4_addr, + &self->client.addr.sin_addr, sizeof(struct in_addr)); + conn_info.key.udp_port = self->client.addr.sin_port; + } else { + memcpy(&conn_info.key.addr.ipv6_addr, + &self->client.addr6.sin6_addr, + sizeof(struct in6_addr)); + conn_info.key.udp_port = self->client.addr6.sin6_port; + } + + ASSERT_TRUE(variant->algo == TLS_CIPHER_AES_GCM_128 || + variant->algo == TLS_CIPHER_CHACHA20_POLY1305); + switch (variant->algo) { + case TLS_CIPHER_AES_GCM_128: + memcpy(&conn_info.aes_gcm_128.payload_key, + &variant->conn_key, 16); + memcpy(&conn_info.aes_gcm_128.payload_iv, + &variant->conn_iv, 12); + memcpy(&conn_info.aes_gcm_128.header_key, + &variant->conn_hdr_key, 16); + break; + case TLS_CIPHER_CHACHA20_POLY1305: + memcpy(&conn_info.chacha20_poly1305.payload_key, + &variant->conn_key, 32); + memcpy(&conn_info.chacha20_poly1305.payload_iv, + &variant->conn_iv, 12); + memcpy(&conn_info.chacha20_poly1305.header_key, + &variant->conn_hdr_key, 32); + break; + } + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_SEGMENT, &wrong_frag_size, + sizeof(wrong_frag_size)), 0); + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_QUIC_ADD_TX_CONNECTION, + &conn_info, sizeof(conn_info)), 0); + + recv_addr_len = self->len_c; + iov.iov_base = (void *)variant->plain; + iov.iov_len = variant->plain_len; + memset(cmsg_buf, 0, sizeof(cmsg_buf)); + msg.msg_name = (self->client.addr.sin_family == AF_INET) + ? (void *)&self->client.addr + : (void *)&self->client.addr6; + msg.msg_namelen = self->len_c; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + cmsg_hdr = CMSG_FIRSTHDR(&msg); + cmsg_hdr->cmsg_level = IPPROTO_UDP; + cmsg_hdr->cmsg_type = UDP_QUIC_ENCRYPT; + cmsg_hdr->cmsg_len = CMSG_LEN(sizeof(struct quic_tx_ancillary_data)); + anc_data = (struct quic_tx_ancillary_data *)CMSG_DATA(cmsg_hdr); + anc_data->flags = 0; + anc_data->next_pkt_num = variant->next_pkt_num; + anc_data->dst_conn_id_length = variant->conn_id_len; + cmsg_hdr = CMSG_NXTHDR(&msg, cmsg_hdr); + cmsg_hdr->cmsg_level = IPPROTO_UDP; + cmsg_hdr->cmsg_type = UDP_SEGMENT; + cmsg_hdr->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + memcpy(CMSG_DATA(cmsg_hdr), (void *)&frag_size, sizeof(frag_size)); + + EXPECT_EQ(sendmsg(self->sfd, &msg, 0), variant->plain_len); + ASSERT_NE(setns(self->client_net_ns_fd, 0), -1); + if (variant->af_client == AF_INET) { + EXPECT_EQ(recvfrom(self->cfd, buf, 9000, 0, + &self->client.addr, &recv_addr_len), + variant->match_len); + } else { + EXPECT_EQ(recvfrom(self->cfd, buf, 9000, 0, + &self->client.addr6, &recv_addr_len), + variant->match_len); + } + EXPECT_STREQ(buf, variant->match); + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_QUIC_DEL_TX_CONNECTION, + &conn_info, sizeof(conn_info)), 0); + free(buf); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +} + +TEST_F(quic_crypto, encrypt_test_vector_single_flow_gso_in_setsockopt) +{ + uint8_t cmsg_buf[CMSG_SPACE(sizeof(struct quic_tx_ancillary_data))]; + struct quic_tx_ancillary_data *anc_data; + struct quic_connection_info conn_info; + int frag_size = 1200; + struct cmsghdr *cmsg_hdr; + socklen_t recv_addr_len; + struct iovec iov; + struct msghdr msg; + char *buf; + + buf = (char *)malloc(9000); + conn_info.cipher_type = variant->algo; + memset(&conn_info.key, 0, sizeof(struct quic_connection_info_key)); + conn_info.key.dst_conn_id_length = variant->conn_id_len; + memcpy(conn_info.key.dst_conn_id, + &variant->conn_id, + variant->conn_id_len); + conn_info.conn_payload_key_gen = 0; + + if (self->client.addr.sin_family == AF_INET) { + memcpy(&conn_info.key.addr.ipv4_addr, + &self->client.addr.sin_addr, sizeof(struct in_addr)); + conn_info.key.udp_port = self->client.addr.sin_port; + } else { + memcpy(&conn_info.key.addr.ipv6_addr, + &self->client.addr6.sin6_addr, + sizeof(struct in6_addr)); + conn_info.key.udp_port = self->client.addr6.sin6_port; + } + ASSERT_TRUE(variant->algo == TLS_CIPHER_AES_GCM_128 || + variant->algo == TLS_CIPHER_CHACHA20_POLY1305); + switch (variant->algo) { + case TLS_CIPHER_AES_GCM_128: + memcpy(&conn_info.aes_gcm_128.payload_key, + &variant->conn_key, 16); + memcpy(&conn_info.aes_gcm_128.payload_iv, + &variant->conn_iv, 12); + memcpy(&conn_info.aes_gcm_128.header_key, + &variant->conn_hdr_key, 16); + break; + case TLS_CIPHER_CHACHA20_POLY1305: + memcpy(&conn_info.chacha20_poly1305.payload_key, + &variant->conn_key, 32); + memcpy(&conn_info.chacha20_poly1305.payload_iv, + &variant->conn_iv, 12); + memcpy(&conn_info.chacha20_poly1305.header_key, + &variant->conn_hdr_key, 32); + break; + } + + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_SEGMENT, &frag_size, + sizeof(frag_size)), 0); + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_QUIC_ADD_TX_CONNECTION, + &conn_info, sizeof(conn_info)), 0); + + recv_addr_len = self->len_c; + iov.iov_base = (void *)variant->plain; + iov.iov_len = variant->plain_len; + memset(cmsg_buf, 0, sizeof(cmsg_buf)); + msg.msg_name = (self->client.addr.sin_family == AF_INET) + ? (void *)&self->client.addr + : (void *)&self->client.addr6; + msg.msg_namelen = self->len_c; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + cmsg_hdr = CMSG_FIRSTHDR(&msg); + cmsg_hdr->cmsg_level = IPPROTO_UDP; + cmsg_hdr->cmsg_type = UDP_QUIC_ENCRYPT; + cmsg_hdr->cmsg_len = CMSG_LEN(sizeof(struct quic_tx_ancillary_data)); + anc_data = (struct quic_tx_ancillary_data *)CMSG_DATA(cmsg_hdr); + anc_data->flags = 0; + anc_data->next_pkt_num = variant->next_pkt_num; + anc_data->dst_conn_id_length = variant->conn_id_len; + + EXPECT_EQ(sendmsg(self->sfd, &msg, 0), variant->plain_len); + ASSERT_NE(setns(self->client_net_ns_fd, 0), -1); + if (variant->af_client == AF_INET) { + EXPECT_EQ(recvfrom(self->cfd, buf, 9000, 0, + &self->client.addr, &recv_addr_len), + variant->match_len); + } else { + EXPECT_EQ(recvfrom(self->cfd, buf, 9000, 0, + &self->client.addr6, &recv_addr_len), + variant->match_len); + } + EXPECT_STREQ(buf, variant->match); + ASSERT_NE(setns(self->server_net_ns_fd, 0), -1); + ASSERT_EQ(setsockopt(self->sfd, SOL_UDP, UDP_QUIC_DEL_TX_CONNECTION, + &conn_info, sizeof(conn_info)), 0); + free(buf); + ASSERT_NE(setns(self->default_net_ns_fd, 0), -1); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/quic.sh b/tools/testing/selftests/net/quic.sh new file mode 100755 index 000000000000..8ff8bc494671 --- /dev/null +++ b/tools/testing/selftests/net/quic.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +sudo ip netns add ns11 +sudo ip netns add ns12 +sudo ip netns add ns2 +sudo ip link add veth11 type veth peer name br-veth11 +sudo ip link add veth12 type veth peer name br-veth12 +sudo ip link add veth2 type veth peer name br-veth2 +sudo ip link set veth11 netns ns11 +sudo ip link set veth12 netns ns12 +sudo ip link set veth2 netns ns2 +sudo ip netns exec ns11 ip addr add 10.0.0.1/24 dev veth11 +sudo ip netns exec ns11 ip addr add ::ffff:10.0.0.1/96 dev veth11 +sudo ip netns exec ns11 ip addr add 2001::1/64 dev veth11 +sudo ip netns exec ns12 ip addr add 10.0.0.3/24 dev veth12 +sudo ip netns exec ns12 ip addr add ::ffff:10.0.0.3/96 dev veth12 +sudo ip netns exec ns12 ip addr add 2001::3/64 dev veth12 +sudo ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth2 +sudo ip netns exec ns2 ip addr add ::ffff:10.0.0.2/96 dev veth2 +sudo ip netns exec ns2 ip addr add 2001::2/64 dev veth2 +sudo ip link add name br1 type bridge forward_delay 0 +sudo ip link set br1 up +sudo ip link set br-veth11 up +sudo ip link set br-veth12 up +sudo ip link set br-veth2 up +sudo ip netns exec ns11 ip link set veth11 up +sudo ip netns exec ns12 ip link set veth12 up +sudo ip netns exec ns2 ip link set veth2 up +sudo ip link set br-veth11 master br1 +sudo ip link set br-veth12 master br1 +sudo ip link set br-veth2 master br1 +sudo ip netns exec ns2 cat /proc/net/quic_stat + +printf "%s" "Waiting for bridge to start fowarding ..." +while ! timeout 0.5 sudo ip netns exec ns2 ping -c 1 -n 2001::1 &> /dev/null +do + printf "%c" "." +done +printf "\n%s\n" "Bridge is operational" + +sudo ./quic +sudo ip netns exec ns2 cat /proc/net/quic_stat +sudo ip netns delete ns2 +sudo ip netns delete ns12 +sudo ip netns delete ns11