IPv6 support for quic interop
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)
This commit is contained in:
parent
fcd8f8f7dd
commit
e9aa0b6c0e
4 changed files with 72 additions and 68 deletions
2
.github/workflows/run_quic_interop.yml
vendored
2
.github/workflows/run_quic_interop.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
||||||
run_quic_interop:
|
run_quic_interop:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
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]
|
servers: [quic-go, ngtcp2, mvfst, quiche, nginx, msquic, haproxy]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* This file implements a lightweight QUIC server supporting the HTTP/0.9
|
* This file implements a lightweight QUIC server supporting the HTTP/0.9
|
||||||
* protocol for interoperability testing. It includes functions for setting
|
* protocol for interoperability testing. It includes functions for setting
|
||||||
* up a secure QUIC connection, handling ALPN negotiation, and serving client
|
* 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
|
* available at https://interop.seemann.io
|
||||||
*
|
*
|
||||||
* Key functionalities:
|
* Key functionalities:
|
||||||
|
@ -43,6 +43,7 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <stdarg.h>
|
# include <stdarg.h>
|
||||||
# include <winsock2.h>
|
# include <winsock2.h>
|
||||||
|
# include <ws2tcpip.h>
|
||||||
#else
|
#else
|
||||||
# include <sys/socket.h>
|
# include <sys/socket.h>
|
||||||
# include <netinet/in.h>
|
# include <netinet/in.h>
|
||||||
|
@ -261,7 +262,8 @@ err:
|
||||||
* connections.
|
* connections.
|
||||||
*
|
*
|
||||||
* Notes:
|
* 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`.
|
* - The specified port is converted to network byte order using `htons`.
|
||||||
*/
|
*/
|
||||||
static BIO *create_socket(uint16_t port)
|
static BIO *create_socket(uint16_t port)
|
||||||
|
@ -269,16 +271,31 @@ static BIO *create_socket(uint16_t port)
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
BIO *sock = NULL;
|
BIO *sock = NULL;
|
||||||
BIO_ADDR *addr = 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 */
|
/* 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");
|
fprintf(stderr, "cannot create socket");
|
||||||
goto err;
|
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
|
* 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);
|
fprintf(stderr, "unable to bind to port %d\n", port);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,8 +82,6 @@ static BIO *session_bio = NULL;
|
||||||
*
|
*
|
||||||
* @param hostname The hostname of the server to connect to.
|
* @param hostname The hostname of the server to connect to.
|
||||||
* @param port The port number 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
|
* @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
|
* of the connected peer on success. The caller is responsible
|
||||||
* for freeing this memory using BIO_ADDR_free().
|
* for freeing this memory using BIO_ADDR_free().
|
||||||
|
@ -100,7 +98,7 @@ static BIO *session_bio = NULL;
|
||||||
* freed.
|
* freed.
|
||||||
*/
|
*/
|
||||||
static BIO *create_socket_bio(const char *hostname, const char *port,
|
static BIO *create_socket_bio(const char *hostname, const char *port,
|
||||||
int family, BIO_ADDR **peer_addr)
|
BIO_ADDR **peer_addr)
|
||||||
{
|
{
|
||||||
int sock = -1;
|
int sock = -1;
|
||||||
BIO_ADDRINFO *res;
|
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.
|
* Lookup IP address info for the server.
|
||||||
*/
|
*/
|
||||||
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0,
|
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, AF_UNSPEC, SOCK_DGRAM,
|
||||||
&res))
|
0, &res))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -676,13 +674,12 @@ static BIO_ADDR *peer_addr = NULL;
|
||||||
*
|
*
|
||||||
* @param hostname Hostname to connect to.
|
* @param hostname Hostname to connect to.
|
||||||
* @param port Port 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 ctx Pointer to an SSL_CTX object, which will be created.
|
||||||
* @param ssl Pointer to an SSL object, which will be created.
|
* @param ssl Pointer to an SSL object, which will be created.
|
||||||
*
|
*
|
||||||
* @return Returns 0 on success, 1 on error.
|
* @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)
|
SSL_CTX **ctx, SSL **ssl)
|
||||||
{
|
{
|
||||||
unsigned char alpn[] = {10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p'};
|
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
|
* Create the underlying transport socket/BIO and associate it with the
|
||||||
* connection.
|
* connection.
|
||||||
*/
|
*/
|
||||||
bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET,
|
bio = create_socket_bio(hostname, port, &peer_addr);
|
||||||
&peer_addr);
|
|
||||||
if (bio == NULL) {
|
if (bio == NULL) {
|
||||||
fprintf(stderr, "Failed to crete the BIO\n");
|
fprintf(stderr, "Failed to crete the BIO\n");
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -818,16 +814,16 @@ end:
|
||||||
*
|
*
|
||||||
* This function sets up an SSL/TLS connection using QUIC, sends HTTP GET
|
* This function sets up an SSL/TLS connection using QUIC, sends HTTP GET
|
||||||
* requests for files specified in the command-line arguments, and saves
|
* requests for files specified in the command-line arguments, and saves
|
||||||
* the responses to disk. It handles various configurations such as IPv6
|
* the responses to disk. It handles various configurations such as session
|
||||||
* support, session caching, and key logging.
|
* caching, and key logging.
|
||||||
*
|
*
|
||||||
* @param argc The number of command-line arguments.
|
* @param argc The number of command-line arguments.
|
||||||
* @param argv The array of command-line arguments. The expected format is
|
* @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.
|
* @return EXIT_SUCCESS on success, or EXIT_FAILURE on error.
|
||||||
*
|
*
|
||||||
* @note The function performs the following main tasks:
|
* @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.
|
* - Reads the list of requests from the specified file.
|
||||||
* - Sets up the SSL context and configures certificate verification.
|
* - Sets up the SSL context and configures certificate verification.
|
||||||
* - Optionally enables key logging and session caching.
|
* - Optionally enables key logging and session caching.
|
||||||
|
@ -859,21 +855,12 @@ int main(int argc, char *argv[])
|
||||||
size_t this_poll_count = 0;
|
size_t this_poll_count = 0;
|
||||||
char *req = NULL;
|
char *req = NULL;
|
||||||
char *hostname, *port;
|
char *hostname, *port;
|
||||||
int ipv6 = 0;
|
|
||||||
|
|
||||||
if (argc < 4) {
|
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;
|
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++];
|
hostname = argv[argnext++];
|
||||||
port = argv[argnext++];
|
port = argv[argnext++];
|
||||||
reqfile = argv[argnext];
|
reqfile = argv[argnext];
|
||||||
|
@ -901,7 +888,7 @@ int main(int argc, char *argv[])
|
||||||
req_bio = NULL;
|
req_bio = NULL;
|
||||||
reqnames[read_offset + 1] = '\0';
|
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");
|
fprintf(stderr, "Unable to establish connection\n");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,43 +42,43 @@ if [ "$ROLE" == "client" ]; then
|
||||||
SSL_CERT_FILE=/certs/ca.pem curl --config $CURLRC || exit 1
|
SSL_CERT_FILE=/certs/ca.pem curl --config $CURLRC || exit 1
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
"handshake"|"transfer"|"retry")
|
"handshake"|"transfer"|"retry"|"ipv6")
|
||||||
HOSTNAME=none
|
HOSTNAME=none
|
||||||
for req in $REQUESTS
|
for req in $REQUESTS
|
||||||
do
|
do
|
||||||
OUTFILE=$(basename $req)
|
OUTFILE=$(basename $req)
|
||||||
if [ "$HOSTNAME" == "none" ]
|
if [ "$HOSTNAME" == "none" ]
|
||||||
then
|
then
|
||||||
HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
|
HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
|
||||||
HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
|
HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
|
||||||
fi
|
fi
|
||||||
echo -n "$OUTFILE " >> ./reqfile.txt
|
echo -n "$OUTFILE " >> ./reqfile.txt
|
||||||
done
|
done
|
||||||
SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1
|
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
|
exit 0
|
||||||
;;
|
;;
|
||||||
"resumption")
|
"resumption")
|
||||||
for req in $REQUESTS
|
for req in $REQUESTS
|
||||||
do
|
do
|
||||||
OUTFILE=$(basename $req)
|
OUTFILE=$(basename $req)
|
||||||
echo -n "$OUTFILE " > ./reqfile.txt
|
echo -n "$OUTFILE " > ./reqfile.txt
|
||||||
HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
|
HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
|
||||||
HOSTPORT=$(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
|
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
|
done
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
"chacha20")
|
"chacha20")
|
||||||
for req in $REQUESTS
|
for req in $REQUESTS
|
||||||
do
|
do
|
||||||
OUTFILE=$(basename $req)
|
OUTFILE=$(basename $req)
|
||||||
printf "%s " "$OUTFILE" >> ./reqfile.txt
|
printf "%s " "$OUTFILE" >> ./reqfile.txt
|
||||||
HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
|
HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
|
||||||
HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
|
HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
|
||||||
done
|
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
|
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
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "UNSUPPORTED TESTCASE $TESTCASE"
|
echo "UNSUPPORTED TESTCASE $TESTCASE"
|
||||||
exit 127
|
exit 127
|
||||||
|
@ -88,7 +88,7 @@ elif [ "$ROLE" == "server" ]; then
|
||||||
echo "TESTCASE is $TESTCASE"
|
echo "TESTCASE is $TESTCASE"
|
||||||
rm -f $CURLRC
|
rm -f $CURLRC
|
||||||
case "$TESTCASE" in
|
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
|
NO_ADDR_VALIDATE=yes SSLKEYLOGFILE=/logs/keys.log FILEPREFIX=/www quic-hq-interop-server 443 /certs/cert.pem /certs/priv.key
|
||||||
;;
|
;;
|
||||||
"retry"|"resumption")
|
"retry"|"resumption")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue