]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
IPv6 support for quic interop
authorAndrew Dinh <andrewd@openssl.org>
Mon, 16 Dec 2024 17:23:59 +0000 (12:23 -0500)
committerNeil Horman <nhorman@openssl.org>
Mon, 17 Feb 2025 16:27:33 +0000 (11:27 -0500)
Modify the QUIC HQ interop server/client to support both IPv4 and IPv6.

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26194)

.github/workflows/run_quic_interop.yml
demos/guide/quic-hq-interop-server.c
demos/guide/quic-hq-interop.c
test/quic-openssl-docker/run_endpoint.sh

index 16c0f7fe913679c68829b8390863d988715f0a96..7459e8a8e3b8ed669468d64519faacb3bb141eea 100644 (file)
@@ -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 
index bea9c44438bea0942a41684d30786925dfaf2dfb..0fc5e8d2061efa0a787b3d85719b1c548167b64a 100644 (file)
@@ -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 <stdarg.h>
 # include <winsock2.h>
+# include <ws2tcpip.h>
 #else
 # include <sys/socket.h>
 # include <netinet/in.h>
@@ -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;
     }
index 7c158fc09087a4e12769f3b2158ac1dcd3c512ad..a798476cc877452456d71f1649d90e2bb3000920 100644 (file)
@@ -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;
     }
index aa1575ca2f17387f72af42ffa942064eb9c60aa9..6e5252642cbef7aef3abf2ee283a4ab2e7f2fde6 100644 (file)
@@ -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")