From 9a3e897dc74b10e699b77c449cea37e1449d0701 Mon Sep 17 00:00:00 2001 From: Colin Pfingstl <19822061+koflin@users.noreply.github.com> Date: Fri, 6 Jun 2025 16:20:55 +0200 Subject: [PATCH 1/2] SCION: support QUIC/UDP over SCION --- .gitignore | 1 + CMakeLists.txt | 16 ++++ docs/examples/CMakeLists.txt | 2 + docs/examples/Makefile.inc | 1 + docs/examples/http3-scion.c | 68 +++++++++++++++ include/curl/curl.h | 4 + lib/cf-socket.c | 158 +++++++++++++++++++++++++++++++++-- lib/cf-socket.h | 8 ++ lib/curl_config.h.cmake | 2 + lib/curl_setup_once.h | 4 + lib/setopt.c | 10 +++ lib/urldata.h | 4 + lib/vquic/curl_ngtcp2.c | 10 +++ lib/vquic/vquic.c | 83 ++++++++++++++++-- lib/vquic/vquic_int.h | 3 + topology.json | 47 +++++++++++ 16 files changed, 410 insertions(+), 11 deletions(-) create mode 100644 docs/examples/http3-scion.c create mode 100644 topology.json diff --git a/.gitignore b/.gitignore index 9c901f9fffef..d3d9369bdda1 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ .libs .project .settings +.idea /.vs /bld/ /build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 825577e2e83d..76129d5a10d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1242,6 +1242,22 @@ if(USE_OPENSSL_QUIC) endif() endif() +option(USE_SCION "Use QUIC over SCION" OFF) +if (USE_SCION) + option(SCION_INCLUDE_DIR "Path to the SCION include directory" OFF) + option(SCION_LIBRARY_DIR "Path to the SCION lib directory" OFF) + + if (SCION_INCLUDE_DIR AND SCION_LIBRARY_DIR) + list(APPEND CURL_LIBS "scion" "nghttp2" "protobuf-c") + list(APPEND CURL_LIBDIRS ${SCION_LIBRARY_DIR}) + + include_directories(SYSTEM ${SCION_INCLUDE_DIR}) + link_directories("${SCION_LIBRARY_DIR}") + else() + message(FATAL_ERROR "If SCION is enabled both SCION_INCLUDE_DIR and SCION_LIBRARY_DIR need to be set") + endif() +endif () + if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)) message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") endif() diff --git a/docs/examples/CMakeLists.txt b/docs/examples/CMakeLists.txt index ff7cf9b6cf0a..90db6bfd5c0f 100644 --- a/docs/examples/CMakeLists.txt +++ b/docs/examples/CMakeLists.txt @@ -28,6 +28,8 @@ add_custom_target(curl-examples) curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") +configure_file("../../topology.json" "${CMAKE_CURRENT_BINARY_DIR}/topology.json" COPYONLY) + foreach(_target IN LISTS check_PROGRAMS) set(_target_name "curl-example-${_target}") add_executable(${_target_name} EXCLUDE_FROM_ALL "${_target}.c") diff --git a/docs/examples/Makefile.inc b/docs/examples/Makefile.inc index 99980e39f41f..8b17b6bb61d2 100644 --- a/docs/examples/Makefile.inc +++ b/docs/examples/Makefile.inc @@ -58,6 +58,7 @@ check_PROGRAMS = \ http2-serverpush \ http2-upload \ http3 \ + http3-scion \ http3-present \ httpcustomheader \ httpput \ diff --git a/docs/examples/http3-scion.c b/docs/examples/http3-scion.c new file mode 100644 index 000000000000..bd96bcd86849 --- /dev/null +++ b/docs/examples/http3-scion.c @@ -0,0 +1,68 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* + * Very simple HTTP/3 GET over SCION + * + */ +#include +#include + +int main(void) +{ + CURL *curl; + CURLcode res; + + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://127.0.0.132/json"); + + curl_easy_setopt(curl, CURLOPT_SCION_DST_IA, 0x2ff0000000221); + + curl_easy_setopt(curl, CURLOPT_SCION_TOPOLOGY_PATH, "topology.json"); + + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, + (long)CURL_HTTP_VERSION_3ONLY); + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 0L); + + curl_global_trace("all"); + + /* Perform the request, res gets the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } + return 0; +} diff --git a/include/curl/curl.h b/include/curl/curl.h index fb225c7903c9..deb9ee77b7f8 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2261,6 +2261,10 @@ typedef enum { /* set TLS supported signature algorithms */ CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328), + CURLOPT(CURLOPT_SCION_DST_IA, CURLOPTTYPE_LONG, 329), + + CURLOPT(CURLOPT_SCION_TOPOLOGY_PATH, CURLOPTTYPE_CBPOINT, 330), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/cf-socket.c b/lib/cf-socket.c index e31977201c68..e704a65fdd96 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -91,6 +91,11 @@ #include "curl_memory.h" #include "memdebug.h" +#ifdef USE_SCION +#define setsockopt_quic(ctx,level,optname,optval,optlen) scion_setsockopt(ctx->socket,level,optname,optval,optlen) +#else +#define setsockopt_quic(ctx,level,optname,optval,optlen) setsockopt(ctx->sock,level,optname,optval,optlen) +#endif #if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) /* It makes support for IPv4-mapped IPv6 addresses. @@ -345,6 +350,46 @@ CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, return CURLE_OK; } +#ifdef USE_SCION +static CURLcode socket_open_scion(struct Curl_easy *data, struct Curl_sockaddr_ex *addr, + curl_socket_t *sockfd, struct scion_topology **topology, struct scion_network **network, struct scion_socket **socket) { + int result; + + DEBUGASSERT(addr->protocol == IPPROTO_UDP); + result = scion_topology_from_file(topology, data->set.topology_file_path); + if (result) + goto err; + + result = scion_network(network, *topology); + if (result) + goto cleanup_topology; + + result = scion_socket(socket, addr->family, SCION_PROTO_UDP, *network); + if (result) + goto cleanup_network; + + scion_getsockfd(*socket, sockfd); + +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) + if(data->conn->scope_id && (addr->family == AF_INET6)) { + struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr; + sa6->sin6_scope_id = data->conn->scope_id; + } +#endif + return CURLE_OK; + +cleanup_network: + scion_network_free(*network); + +cleanup_topology: + scion_topology_free(*topology); + +err: + /* no socket, no connection */ + return CURLE_COULDNT_CONNECT; +} +#endif + static CURLcode socket_open(struct Curl_easy *data, struct Curl_sockaddr_ex *addr, curl_socket_t *sockfd) @@ -414,6 +459,31 @@ CURLcode Curl_socket_open(struct Curl_easy *data, return socket_open(data, addr, sockfd); } +#ifdef USE_SCION +static int socket_close_scion(struct Curl_easy *data, struct connectdata *conn, + int use_callback, curl_socket_t sock, struct scion_socket *socket) { + if(socket == NULL) + return 0; + + if(use_callback && conn && conn->fclosesocket) { + int rc; + Curl_multi_will_close(data, sock); + Curl_set_in_callback(data, TRUE); + rc = conn->fclosesocket(conn->closesocket_client, sock); + Curl_set_in_callback(data, FALSE); + return rc; + } + + if(conn) + /* tell the multi-socket code about this */ + Curl_multi_will_close(data, sock); + + scion_close(socket); + + return 0; +} +#endif + static int socket_close(struct Curl_easy *data, struct connectdata *conn, int use_callback, curl_socket_t sock) { @@ -561,7 +631,7 @@ CURLcode Curl_parse_interface(const char *input, return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY; } -#ifndef CURL_DISABLE_BINDLOCAL +#if !defined(CURL_DISABLE_BINDLOCAL) && !defined(USE_SCION) static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sockfd, int af, unsigned int scope) { @@ -917,6 +987,11 @@ static CURLcode socket_connect_result(struct Curl_easy *data, struct cf_socket_ctx { int transport; struct Curl_sockaddr_ex addr; /* address to connect to */ +#ifdef USE_SCION + struct scion_topology *topology; + struct scion_network *network; + struct scion_socket *socket; +#endif curl_socket_t sock; /* current attempt socket */ struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */ struct curltime started_at; /* when socket was created */ @@ -994,7 +1069,17 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock); if(ctx->sock == cf->conn->sock[cf->sockindex]) cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; +#ifdef USE_SCION + if (ctx->transport == TRNSPRT_QUIC) { + socket_close_scion(data, cf->conn, !ctx->accepted, ctx->sock, ctx->socket); + scion_network_free(ctx->network); + scion_topology_free(ctx->topology); + } else { + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + } +#else socket_close(data, cf->conn, !ctx->accepted, ctx->sock); +#endif ctx->sock = CURL_SOCKET_BAD; if(ctx->active && cf->sockindex == FIRSTSOCKET) cf->conn->remote_addr = NULL; @@ -1053,12 +1138,31 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf, curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); memset(&ssloc, 0, sizeof(ssloc)); + +#ifdef USE_SCION + if (ctx->transport == TRNSPRT_QUIC) { + int result = scion_getsockname(ctx->socket, (struct sockaddr*) &ssloc, &slen, NULL); + if (result) { + failf(data, "scion_getsockname() failed with errno %d: %s", + result, scion_strerror(result)); + return CURLE_FAILED_INIT; + } + } else { + if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { + int error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } + } +#else if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { int error = SOCKERRNO; failf(data, "getsockname() failed with errno %d: %s", error, Curl_strerror(error, buffer, sizeof(buffer))); return CURLE_FAILED_INIT; } +#endif if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, ctx->ip.local_ip, &ctx->ip.local_port)) { failf(data, "ssloc inet_ntop() failed with errno %d: %s", @@ -1114,7 +1218,17 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, if(!data->set.fopensocket) ctx->addr.socktype |= SOCK_NONBLOCK; #endif + +#ifdef USE_SCION + if (ctx->transport == TRNSPRT_QUIC) { + result = socket_open_scion(data, &ctx->addr, &ctx->sock, &ctx->topology, &ctx->network, &ctx->socket); + } else { + result = socket_open(data, &ctx->addr, &ctx->sock); + } +#else result = socket_open(data, &ctx->addr, &ctx->sock); +#endif + #ifdef SOCK_NONBLOCK /* Restore the socktype after the socket is created. */ if(!data->set.fopensocket) @@ -1170,7 +1284,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, } } -#ifndef CURL_DISABLE_BINDLOCAL +#if !defined(CURL_DISABLE_BINDLOCAL) && !defined(USE_SCION) /* possibly bind the local end to an IP, interface or port */ if(ctx->addr.family == AF_INET #ifdef USE_IPV6 @@ -1823,11 +1937,21 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, /* error: The 1st argument to 'connect' is -1 but should be >= 0 NOLINTNEXTLINE(clang-analyzer-unix.StdCLibraryFunctions) */ +#ifdef USE_SCION + rc = scion_connect(ctx->socket, &ctx->addr.curl_sa_addr, + (curl_socklen_t)ctx->addr.addrlen, data->set.ia); + if (rc != 0) { + printf("scion connect failed: %d\n", rc); + return CURLE_COULDNT_CONNECT; + } +#else rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, (curl_socklen_t)ctx->addr.addrlen); if(-1 == rc) { return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO); } +#endif + ctx->sock_connected = TRUE; set_local_ip(cf, data); CURL_TRC_CF(data, cf, "%s socket %" FMT_SOCKET_T @@ -1846,7 +1970,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, #ifdef IP_MTU_DISCOVER case AF_INET: { int val = IP_PMTUDISC_DO; - (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, + (void)setsockopt_quic(ctx, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); break; } @@ -1854,7 +1978,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, #ifdef IPV6_MTU_DISCOVER case AF_INET6: { int val = IPV6_PMTUDISC_DO; - (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, + (void)setsockopt_quic(ctx, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val)); break; } @@ -1864,7 +1988,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, #if defined(UDP_GRO) && \ (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \ ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE)) - (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one, + (void)setsockopt_quic(ctx, IPPROTO_UDP, UDP_GRO, &one, (socklen_t)sizeof(one)); #endif #endif @@ -2269,6 +2393,30 @@ static bool cf_is_socket(struct Curl_cfilter *cf) cf->cft == &Curl_cft_tcp_accept); } +#ifdef USE_SCION +CURLcode Curl_cf_socket_peek_scion(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *psock, + const struct Curl_sockaddr_ex **paddr, + struct ip_quadruple *pip, struct scion_socket **socket) { + (void)data; + if(cf_is_socket(cf) && cf->ctx) { + struct cf_socket_ctx *ctx = cf->ctx; + + if(psock) + *psock = ctx->sock; + if(paddr) + *paddr = &ctx->addr; + if(pip) + *pip = ctx->ip; + if (socket) + *socket = ctx->socket; + return CURLE_OK; + } + return CURLE_FAILED_INIT; +} +#endif + CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *psock, diff --git a/lib/cf-socket.h b/lib/cf-socket.h index d3e35098421e..0a0b6c0fab2d 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -167,6 +167,14 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, const struct Curl_sockaddr_ex **paddr, struct ip_quadruple *pip); +#ifdef USE_SCION +CURLcode Curl_cf_socket_peek_scion(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *psock, + const struct Curl_sockaddr_ex **paddr, + struct ip_quadruple *pip, struct scion_socket **socket); +#endif + extern struct Curl_cftype Curl_cft_tcp; extern struct Curl_cftype Curl_cft_udp; extern struct Curl_cftype Curl_cft_unix; diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index efaa8a4594a2..950f92899fed 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -844,3 +844,5 @@ ${SIZEOF_TIME_T_CODE} /* Define to 1 if you have the SSL_set1_ech_config_list function. */ #cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST + +#cmakedefine USE_SCION 1 diff --git a/lib/curl_setup_once.h b/lib/curl_setup_once.h index c1051e0faefe..b7b0c26341c3 100644 --- a/lib/curl_setup_once.h +++ b/lib/curl_setup_once.h @@ -100,6 +100,10 @@ #include #endif +#ifdef USE_SCION +#include +#endif + #include "functypes.h" #ifdef __hpux diff --git a/lib/setopt.c b/lib/setopt.c index d5ddf6d0c26d..afcf91be7b30 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1422,6 +1422,11 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, case CURLOPT_UPLOAD_FLAGS: data->set.upload_flags = (unsigned char)arg; break; +#ifdef USE_SCION + case CURLOPT_SCION_DST_IA: + data->set.ia = arg; + break; +#endif default: /* unknown option */ return CURLE_UNKNOWN_OPTION; @@ -2703,6 +2708,11 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, } break; } +#endif +#ifdef USE_SCION + case CURLOPT_SCION_TOPOLOGY_PATH: + data->set.topology_file_path = ptr; + break; #endif default: return CURLE_UNKNOWN_OPTION; diff --git a/lib/urldata.h b/lib/urldata.h index 45052e84b18b..5b2e1ccde66f 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1664,6 +1664,10 @@ struct UserDefined { unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ unsigned char upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ +#ifdef USE_SCION + scion_ia ia; + char *topology_file_path; +#endif #ifdef HAVE_GSSAPI /* GSS-API credential delegation, see the documentation of CURLOPT_GSSAPI_DELEGATION */ diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index f529f7e4fa85..2c602cdbf048 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -2496,12 +2496,22 @@ static const struct alpn_spec ALPN_SPEC_H3 = { if(result) return result; +#ifdef USE_SCION + Curl_cf_socket_peek_scion(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL, &ctx->q.socket); +#else Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL); +#endif if(!sockaddr) return CURLE_QUIC_CONNECT_ERROR; ctx->q.local_addrlen = sizeof(ctx->q.local_addr); +#ifdef USE_SCION + rv = scion_getsockname(ctx->q.socket, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen, NULL); +#else rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, &ctx->q.local_addrlen); +#endif + if(rv == -1) return CURLE_QUIC_CONNECT_ERROR; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index b5dc44f8aa90..16719185468a 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -127,7 +127,29 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, const uint8_t *pkt, size_t pktlen, size_t gsolen, size_t *psent) { -#ifdef HAVE_SENDMSG +#ifdef USE_SCION + ssize_t sent; + (void)gsolen; + + *psent = 0; + + sent = scion_send(qctx->socket, + (const char *)pkt, (SEND_TYPE_ARG3)pktlen, MSG_DONTWAIT); + + if(sent < 0) { + if(sent == SCION_WOULD_BLOCK) { + return CURLE_AGAIN; + } + else { + failf(data, "send() returned %zd (%s)", sent, scion_strerror((int)sent)); + if(sent != SCION_MSG_TOO_LARGE) { + return CURLE_SEND_ERROR; + } + /* UDP datagram is too large; caused by PMTUD. Just let it be + lost. */ + } + } +#elif defined(HAVE_SENDMSG) struct iovec msg_iov; struct msghdr msg = {0}; ssize_t sent; @@ -328,7 +350,7 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, return vquic_flush(cf, data, qctx); } -#if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG) +#if !defined(USE_SCION) && (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg) { int gso_size = 0; @@ -358,7 +380,54 @@ static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg) } #endif -#ifdef HAVE_SENDMMSG +#ifdef USE_SCION +static CURLcode recvfrom_packets_scion(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) +{ + uint8_t buf[64*1024]; + int bufsize = (int)sizeof(buf); + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen = sizeof(remote_addr); + size_t total_nread, pkts; + ssize_t nread; + char errstr[STRERROR_LEN]; + CURLcode result = CURLE_OK; + + DEBUGASSERT(max_pkts > 0); + for(pkts = 0, total_nread = 0; pkts < max_pkts;) { + nread = scion_recvfrom(qctx->socket, (char *)buf, bufsize, MSG_DONTWAIT, + (struct sockaddr *)&remote_addr, + &remote_addrlen, NULL, NULL); + if(nread < 0) { + if (nread == SCION_WOULD_BLOCK) { + CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN"); + goto out; + } + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvfrom() unexpectedly returned %zd (%s)", + nread, scion_strerror((int)nread)); + result = CURLE_RECV_ERROR; + goto out; + } + + ++pkts; + total_nread += (size_t)nread; + result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen, + 0, userp); + if(result) + goto out; + } + + out: + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); + return result; +} +#elif defined(HAVE_SENDMMSG) static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -549,7 +618,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, return result; } -#else /* HAVE_SENDMMSG || HAVE_SENDMSG */ +#else /* USE_SCION || HAVE_SENDMMSG || HAVE_SENDMSG */ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -606,7 +675,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, pkts, total_nread, result); return result; } -#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ +#endif /* !(USE_SCION || HAVE_SENDMMSG || HAVE_SENDMSG) */ CURLcode vquic_recv_packets(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -615,7 +684,9 @@ CURLcode vquic_recv_packets(struct Curl_cfilter *cf, vquic_recv_pkt_cb *recv_cb, void *userp) { CURLcode result; -#if defined(HAVE_SENDMMSG) +#if defined(USE_SCION) + result = recvfrom_packets_scion(cf, data, qctx, max_pkts, recv_cb, userp); +#elif defined(HAVE_SENDMMSG) result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); #elif defined(HAVE_SENDMSG) result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index 4641c3125b01..b909613a474c 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -33,6 +33,9 @@ #define MAX_UDP_PAYLOAD_SIZE 1452 struct cf_quic_ctx { +#ifdef USE_SCION + struct scion_socket *socket; +#endif curl_socket_t sockfd; /* connected UDP socket */ struct sockaddr_storage local_addr; /* address socket is bound to */ socklen_t local_addrlen; /* length of local address */ diff --git a/topology.json b/topology.json new file mode 100644 index 000000000000..deebd40b04fe --- /dev/null +++ b/topology.json @@ -0,0 +1,47 @@ +{ + "attributes": [], + "isd_as": "1-ff00:0:133", + "mtu": 1472, + "dispatched_ports": "1-65535", + "control_service": { + "cs1-ff00_0_133-1": { + "addr": "127.0.0.99:31066" + } + }, + "discovery_service": { + "cs1-ff00_0_133-1": { + "addr": "127.0.0.99:31066" + } + }, + "border_routers": { + "br1-ff00_0_133-1": { + "internal_addr": "127.0.0.97:31068", + "interfaces": { + "1": { + "underlay": { + "local": "127.0.0.29:50000", + "remote": "127.0.0.28:50000" + }, + "isd_as": "1-ff00:0:122", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 1 + } + } + }, + "br1-ff00_0_133-2": { + "internal_addr": "127.0.0.98:31070", + "interfaces": { + "2": { + "underlay": { + "local": "127.0.0.35:50000", + "remote": "127.0.0.34:50000" + }, + "isd_as": "1-ff00:0:132", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} From ef2248362e141398001e0cc446b2d3a6e578b40e Mon Sep 17 00:00:00 2001 From: Colin Pfingstl <19822061+koflin@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:54:18 +0200 Subject: [PATCH 2/2] SCION: add command line support for SCION --- src/config2setopts.c | 7 +++++++ src/tool_cfgable.c | 4 ++++ src/tool_cfgable.h | 6 ++++++ src/tool_getparam.c | 14 ++++++++++++++ src/tool_getparam.h | 4 ++++ src/tool_listhelp.c | 8 ++++++++ 6 files changed, 43 insertions(+) diff --git a/src/config2setopts.c b/src/config2setopts.c index d7a4187a8bbd..abe1fad45676 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -521,6 +521,13 @@ static CURLcode http_setopts(struct GlobalConfig *global, my_setopt_long(curl, CURLOPT_EXPECT_100_TIMEOUT_MS, config->expect100timeout_ms); +#ifdef USE_SCION + if (config->scion_topology_path) + my_setopt_str(curl, CURLOPT_SCION_TOPOLOGY_PATH, config->scion_topology_path); + if (config->scion_dst_ia) + my_setopt_long(curl, CURLOPT_SCION_DST_IA, config->scion_dst_ia); +#endif + return CURLE_OK; } diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 99fc91b09724..3de685f93410 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -182,6 +182,10 @@ static void free_config_fields(struct OperationConfig *config) tool_safefree(config->ech); tool_safefree(config->ech_config); tool_safefree(config->ech_public); + +#ifdef USE_SCION + tool_safefree(config->scion_topology_path); +#endif } void config_free(struct OperationConfig *config) diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index c4f1d6b23448..1faa3f49fdfc 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -242,6 +242,12 @@ struct OperationConfig { } file_clobber_mode; unsigned char upload_flags; /* Bitmask for --upload-flags */ unsigned short porttouse; + +#ifdef USE_SCION + char *scion_topology_path; + scion_ia scion_dst_ia; +#endif + BIT(remote_name_all); /* --remote-name-all */ BIT(remote_time); BIT(cookiesession); /* new session? */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 51156e46b97e..5324eab3a6a4 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -298,6 +298,10 @@ static const struct LongShort aliases[]= { {"retry-max-time", ARG_STRG, ' ', C_RETRY_MAX_TIME}, {"sasl-authzid", ARG_STRG, ' ', C_SASL_AUTHZID}, {"sasl-ir", ARG_BOOL, ' ', C_SASL_IR}, +#ifdef USE_SCION + {"scion-dst-ia", ARG_STRG, ' ', C_SCION_DST_IA }, + {"scion-topology-path", ARG_FILE, ' ', C_SCION_TOPOLOGY_PATH }, +#endif {"service-name", ARG_STRG, ' ', C_SERVICE_NAME}, {"sessionid", ARG_BOOL|ARG_NO, ' ', C_SESSIONID}, {"show-error", ARG_BOOL, 'S', C_SHOW_ERROR}, @@ -2793,6 +2797,16 @@ static ParameterError opt_filestring(struct GlobalConfig *global, case C_UPLOAD_FLAGS: /* --upload-flags */ err = parse_upload_flags(config, nextarg); break; +#ifdef USE_SCION + case C_SCION_TOPOLOGY_PATH: + err = getstr(&config->scion_topology_path, nextarg, DENY_BLANK); + break; + case C_SCION_DST_IA: { + int ret = scion_ia_parse(nextarg, strlen(nextarg), &config->scion_dst_ia); + err = ret == 0 ? PARAM_OK : PARAM_BAD_USE; + break; + } +#endif } return err; } diff --git a/src/tool_getparam.h b/src/tool_getparam.h index d84e85222571..d10674440589 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -237,6 +237,10 @@ typedef enum { C_RETRY_MAX_TIME, C_SASL_AUTHZID, C_SASL_IR, +#ifdef USE_SCION + C_SCION_DST_IA, + C_SCION_TOPOLOGY_PATH, +#endif C_SERVICE_NAME, C_SESSIONID, C_SHOW_ERROR, diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 785f4cd936ed..ea1d5a4a123a 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -650,6 +650,14 @@ const struct helptxt helptext[] = { {" --sasl-ir", "Initial response in SASL authentication", CURLHELP_AUTH}, +#ifdef USE_SCION + {" --scion-dst-ia ", + "IA number of the destination AS", + CURLHELP_HTTP }, + {" --scion-topology-path ", + "Path to the SCION topology file", + CURLHELP_HTTP }, +#endif {" --service-name ", "SPNEGO service name", CURLHELP_AUTH},