From: Andrew Dinh Date: Mon, 16 Dec 2024 17:23:59 +0000 (-0500) Subject: IPv6 support for quic interop X-Git-Tag: openssl-3.5.0-alpha1~277 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e9aa0b6c0efe65e712bf87a9b20acb46d13d7e85;p=thirdparty%2Fopenssl.git IPv6 support for quic interop Modify the QUIC HQ interop server/client to support both IPv4 and IPv6. Reviewed-by: Neil Horman Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/26194) --- diff --git a/.github/workflows/run_quic_interop.yml b/.github/workflows/run_quic_interop.yml index 16c0f7fe913..7459e8a8e3b 100644 --- a/.github/workflows/run_quic_interop.yml +++ b/.github/workflows/run_quic_interop.yml @@ -10,7 +10,7 @@ jobs: run_quic_interop: strategy: matrix: - tests: [http3, transfer, handshake, retry, chacha20, resumption, amplificationlimit] + tests: [http3, transfer, handshake, retry, chacha20, resumption, amplificationlimit, ipv6] servers: [quic-go, ngtcp2, mvfst, quiche, nginx, msquic, haproxy] fail-fast: false runs-on: ubuntu-latest diff --git a/demos/guide/quic-hq-interop-server.c b/demos/guide/quic-hq-interop-server.c index bea9c44438b..0fc5e8d2061 100644 --- a/demos/guide/quic-hq-interop-server.c +++ b/demos/guide/quic-hq-interop-server.c @@ -14,7 +14,7 @@ * This file implements a lightweight QUIC server supporting the HTTP/0.9 * protocol for interoperability testing. It includes functions for setting * up a secure QUIC connection, handling ALPN negotiation, and serving client - * requests. Intended for use with the quic-interop-runner + * requests. Intended for use with the quic-interop-runner * available at https://interop.seemann.io * * Key functionalities: @@ -43,6 +43,7 @@ #ifdef _WIN32 # include # include +# include #else # include # include @@ -261,7 +262,8 @@ err: * connections. * * Notes: - * - This function assumes the use of IPv4 (`AF_INET`) and UDP (`SOCK_DGRAM`). + * - This function assumes UDP (`SOCK_DGRAM`). + * - This function accepts on both IPv4 and IPv6. * - The specified port is converted to network byte order using `htons`. */ static BIO *create_socket(uint16_t port) @@ -269,16 +271,31 @@ static BIO *create_socket(uint16_t port) int fd = -1; BIO *sock = NULL; BIO_ADDR *addr = NULL; - struct in_addr ina; + int opt = 0; +#ifdef _WIN32 + struct in6_addr in6addr_any; - ina.s_addr = INADDR_ANY; + memset(&in6addr_any, 0, sizeof(in6addr_any)); +#endif /* Retrieve the file descriptor for a new UDP socket */ - if ((fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0)) < 0) { + if ((fd = BIO_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0)) < 0) { fprintf(stderr, "cannot create socket"); goto err; } + /* + * IPv6_V6ONLY is only available on some platforms. If it is defined, + * disable it to accept both IPv4 and IPv6 connections. Otherwise, the + * server will only accept IPv6 connections. + */ +#ifdef IPV6_V6ONLY + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) { + fprintf(stderr, "setsockopt IPV6_V6ONLY failed"); + goto err; + } +#endif + /* * Create a new BIO_ADDR */ @@ -289,9 +306,9 @@ static BIO *create_socket(uint16_t port) } /* - * Build a INADDR_ANY BIO_ADDR + * Build an INADDR_ANY BIO_ADDR */ - if (!BIO_ADDR_rawmake(addr, AF_INET, &ina, sizeof(ina), htons(port))) { + if (!BIO_ADDR_rawmake(addr, AF_INET6, &in6addr_any, sizeof(in6addr_any), htons(port))) { fprintf(stderr, "unable to bind to port %d\n", port); goto err; } diff --git a/demos/guide/quic-hq-interop.c b/demos/guide/quic-hq-interop.c index 7c158fc0908..a798476cc87 100644 --- a/demos/guide/quic-hq-interop.c +++ b/demos/guide/quic-hq-interop.c @@ -82,8 +82,6 @@ static BIO *session_bio = NULL; * * @param hostname The hostname of the server to connect to. * @param port The port number of the server to connect to. - * @param family The desired address family (e.g., AF_INET for IPv4, - * AF_INET6 for IPv6). * @param peer_addr A pointer to a BIO_ADDR pointer that will hold the address * of the connected peer on success. The caller is responsible * for freeing this memory using BIO_ADDR_free(). @@ -100,7 +98,7 @@ static BIO *session_bio = NULL; * freed. */ static BIO *create_socket_bio(const char *hostname, const char *port, - int family, BIO_ADDR **peer_addr) + BIO_ADDR **peer_addr) { int sock = -1; BIO_ADDRINFO *res; @@ -110,8 +108,8 @@ static BIO *create_socket_bio(const char *hostname, const char *port, /* * Lookup IP address info for the server. */ - if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0, - &res)) + if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, AF_UNSPEC, SOCK_DGRAM, + 0, &res)) return NULL; /* @@ -676,13 +674,12 @@ static BIO_ADDR *peer_addr = NULL; * * @param hostname Hostname to connect to. * @param port Port to connect to. - * @param ipv6 Whether to use IPv6 (non-zero for IPv6, zero for IPv4). * @param ctx Pointer to an SSL_CTX object, which will be created. * @param ssl Pointer to an SSL object, which will be created. * * @return Returns 0 on success, 1 on error. */ -static int setup_connection(char *hostname, char *port, int ipv6, +static int setup_connection(char *hostname, char *port, SSL_CTX **ctx, SSL **ssl) { unsigned char alpn[] = {10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p'}; @@ -747,8 +744,7 @@ static int setup_connection(char *hostname, char *port, int ipv6, * Create the underlying transport socket/BIO and associate it with the * connection. */ - bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET, - &peer_addr); + bio = create_socket_bio(hostname, port, &peer_addr); if (bio == NULL) { fprintf(stderr, "Failed to crete the BIO\n"); goto end; @@ -818,16 +814,16 @@ end: * * This function sets up an SSL/TLS connection using QUIC, sends HTTP GET * requests for files specified in the command-line arguments, and saves - * the responses to disk. It handles various configurations such as IPv6 - * support, session caching, and key logging. + * the responses to disk. It handles various configurations such as session + * caching, and key logging. * * @param argc The number of command-line arguments. * @param argv The array of command-line arguments. The expected format is - * "[-6] hostname port file". + * "hostname port file". * @return EXIT_SUCCESS on success, or EXIT_FAILURE on error. * * @note The function performs the following main tasks: - * - Parses command-line arguments and configures IPv6 if specified. + * - Parses command-line arguments. * - Reads the list of requests from the specified file. * - Sets up the SSL context and configures certificate verification. * - Optionally enables key logging and session caching. @@ -859,21 +855,12 @@ int main(int argc, char *argv[]) size_t this_poll_count = 0; char *req = NULL; char *hostname, *port; - int ipv6 = 0; if (argc < 4) { - fprintf(stderr, "Usage: quic-hq-interop [-6] hostname port reqfile\n"); + fprintf(stderr, "Usage: quic-hq-interop hostname port reqfile\n"); goto end; } - if (!strcmp(argv[argnext], "-6")) { - if (argc < 5) { - fprintf(stderr, "Usage: quic-hq-interop [-6] hostname port reqfile\n"); - goto end; - } - ipv6 = 1; - argnext++; - } hostname = argv[argnext++]; port = argv[argnext++]; reqfile = argv[argnext]; @@ -901,7 +888,7 @@ int main(int argc, char *argv[]) req_bio = NULL; reqnames[read_offset + 1] = '\0'; - if (!setup_connection(hostname, port, ipv6, &ctx, &ssl)) { + if (!setup_connection(hostname, port, &ctx, &ssl)) { fprintf(stderr, "Unable to establish connection\n"); goto end; } diff --git a/test/quic-openssl-docker/run_endpoint.sh b/test/quic-openssl-docker/run_endpoint.sh index aa1575ca2f1..6e5252642cb 100644 --- a/test/quic-openssl-docker/run_endpoint.sh +++ b/test/quic-openssl-docker/run_endpoint.sh @@ -42,43 +42,43 @@ if [ "$ROLE" == "client" ]; then SSL_CERT_FILE=/certs/ca.pem curl --config $CURLRC || exit 1 exit 0 ;; - "handshake"|"transfer"|"retry") - HOSTNAME=none - for req in $REQUESTS - do - OUTFILE=$(basename $req) - if [ "$HOSTNAME" == "none" ] - then - HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p') - HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p') - fi - echo -n "$OUTFILE " >> ./reqfile.txt - done - SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1 - exit 0 - ;; + "handshake"|"transfer"|"retry"|"ipv6") + HOSTNAME=none + for req in $REQUESTS + do + OUTFILE=$(basename $req) + if [ "$HOSTNAME" == "none" ] + then + HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p') + HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p') + fi + echo -n "$OUTFILE " >> ./reqfile.txt + done + SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1 + exit 0 + ;; "resumption") - for req in $REQUESTS - do - OUTFILE=$(basename $req) - echo -n "$OUTFILE " > ./reqfile.txt - HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p') - HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p') - SSL_SESSION_FILE=./session.db SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1 - done - exit 0 - ;; + for req in $REQUESTS + do + OUTFILE=$(basename $req) + echo -n "$OUTFILE " > ./reqfile.txt + HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p') + HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p') + SSL_SESSION_FILE=./session.db SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1 + done + exit 0 + ;; "chacha20") - for req in $REQUESTS - do - OUTFILE=$(basename $req) - printf "%s " "$OUTFILE" >> ./reqfile.txt - HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p') - HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p') - done - SSL_CIPHER_SUITES=TLS_CHACHA20_POLY1305_SHA256 SSL_SESSION_FILE=./session.db SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1 - exit 0 - ;; + for req in $REQUESTS + do + OUTFILE=$(basename $req) + printf "%s " "$OUTFILE" >> ./reqfile.txt + HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p') + HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p') + done + SSL_CIPHER_SUITES=TLS_CHACHA20_POLY1305_SHA256 SSL_SESSION_FILE=./session.db SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1 + exit 0 + ;; *) echo "UNSUPPORTED TESTCASE $TESTCASE" exit 127 @@ -88,7 +88,7 @@ elif [ "$ROLE" == "server" ]; then echo "TESTCASE is $TESTCASE" rm -f $CURLRC case "$TESTCASE" in - "handshake"|"transfer") + "handshake"|"transfer"|"ipv6") NO_ADDR_VALIDATE=yes SSLKEYLOGFILE=/logs/keys.log FILEPREFIX=/www quic-hq-interop-server 443 /certs/cert.pem /certs/priv.key ;; "retry"|"resumption")