← Commit list

079a11bcba

Viktor Szakats
Viktor Szakats
2026-07-29
SUCCESS Duration: 353s

Subject

vquic: add option to use Apple fast UDP

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

.github/workflows/macos.yml+5 -2
@@ -37,6 +37,7 @@ permissions: {}
#
# - 10.7 Lion (2011) - GSS (build-time, deprecated MIT Kerberos shim)
# - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), memset_s(), OCSP (runtime)
+# - 10.10 Yosemite (2014) - SYS_recvmsg_x, SYS_sendmsg_x (runtime)
# - 10.11 El Capitan (2015) - connectx() (runtime)
# - 10.12 Sierra (2016) - clock_gettime() (build-time, runtime)
# - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime), GSS Framework
@@ -281,7 +282,9 @@ jobs:
compiler: clang
install: libnghttp3 libngtcp2
install_steps: pytest
- configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm --enable-proxy-http3 --with-gssapi
+ configure: >-
+ --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm --enable-proxy-http3 --with-gssapi
+ --enable-apple-fast-udp
- name: 'OpenSSL event-based'
compiler: clang
@@ -326,7 +329,7 @@ jobs:
-DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5
-DCURL_BROTLI=ON -DCURL_ZSTD=ON
-DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/opt/homebrew/opt/llvm/bin/clang-tidy
- -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON
+ -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON -DCURL_ENABLE_APPLE_FAST_UDP=ON
- name: 'LibreSSL openldap AppleGSS c-ares +examples'
compiler: clang
CMakeLists.txt+8 -0
@@ -805,6 +805,14 @@ else()
set(USE_APPLE_SECTRUST OFF)
endif()
+set(USE_APPLE_FAST_UDP 0)
+if(APPLE)
+ option(CURL_ENABLE_APPLE_FAST_UDP "Use Apple fast UDP (experimental)" OFF)
+ if(CURL_ENABLE_APPLE_FAST_UDP)
+ set(USE_APPLE_FAST_UDP 1)
+ endif()
+endif()
+
if(_use_core_foundation_and_core_services)
find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation")
mark_as_advanced(COREFOUNDATION_FRAMEWORK)
configure.ac+22 -0
@@ -2852,6 +2852,28 @@ AC_SUBST([CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME], ["4"]) dnl Keep in sync with
AM_CONDITIONAL([CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS],
[test "$versioned_symbols" = "yes"])
+dnl ---------------------------
+dnl check Apple fast UDP option
+dnl ---------------------------
+
+if test "$curl_cv_apple" = "yes"; then
+ AC_MSG_CHECKING([whether to use Apple fast UDP])
+ AC_ARG_ENABLE(apple-fast-udp,
+AS_HELP_STRING([--enable-apple-fast-udp],[Enable using Apple fast UDP (experimental)])
+AS_HELP_STRING([--disable-apple-fast-udp],[Disable using Apple fast UDP (experimental) (default)]),
+ [ case "$enableval" in
+ yes)
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(USE_APPLE_FAST_UDP, 1, [to use Apple fast UDP (SYS_recvmsg_x, SYS_sendmsg_x)])
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac ],
+ AC_MSG_RESULT([no])
+ )
+fi
+
dnl ----------------------------
dnl check Windows Unicode option
dnl ----------------------------
docs/INSTALL-CMAKE.md+1 -0
@@ -242,6 +242,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl)
## Enabling features
+- `CURL_ENABLE_APPLE_FAST_UDP`: Use Apple fast UDP (experimental). Default: `OFF`
- `CURL_ENABLE_NTLM`: Enable NTLM support. Default: `OFF`
- `CURL_ENABLE_SMB`: Enable SMB. Default: `OFF`
- `CURL_ENABLE_SSL`: Enable SSL support. Default: `ON`
lib/curl_config-cmake.h.in+3 -0
@@ -774,6 +774,9 @@ ${SIZEOF_TIME_T_CODE}
/* to enable Apple OS-native certificate verification */
#cmakedefine USE_APPLE_SECTRUST 1
+/* to use Apple fast UDP (SYS_recvmsg_x, SYS_sendmsg_x) */
+#cmakedefine USE_APPLE_FAST_UDP
+
/* Define to 1 to query for HTTPSRR when using DoH */
#cmakedefine USE_HTTPSRR 1
lib/vquic/vquic.c+220 -13
@@ -37,6 +37,13 @@
#include <nghttp3/nghttp3.h>
#endif
+#if defined(USE_APPLE_FAST_UDP) && defined(__APPLE__)
+#include <sys/syscall.h>
+#if defined(SYS_recvmsg_x) && defined(SYS_sendmsg_x)
+#define HAVE_APPLE_MSG_X
+#endif
+#endif
+
#include "bufq.h"
#include "curlx/dynbuf.h"
#include "curlx/fopen.h"
@@ -57,6 +64,29 @@
#define NW_CHUNK_SIZE (64 * 1024)
#define NW_SEND_CHUNKS 1
+#ifdef HAVE_APPLE_MSG_X
+struct msghdr_x {
+ void *msg_name; /* optional address */
+ socklen_t msg_namelen; /* size of address */
+ struct iovec *msg_iov; /* scatter/gather array */
+ int msg_iovlen; /* # elements in msg_iov */
+ void *msg_control; /* ancillary data, see below */
+ socklen_t msg_controllen; /* ancillary data buffer len */
+ int msg_flags; /* flags on received message */
+ size_t msg_datalen; /* byte length of buffer in msg_iov */
+};
+#endif
+
+#ifdef CURLVERBOSE
+#ifdef HAVE_APPLE_MSG_X
+#define VQUIC_SEND_METHOD "sendmsg_x"
+#elif defined(HAVE_SENDMSG)
+#define VQUIC_SEND_METHOD "sendmsg"
+#else
+#define VQUIC_SEND_METHOD "send"
+#endif
+#endif
+
int Curl_vquic_init(void)
{
#if defined(USE_NGTCP2) && defined(OPENSSL_QUIC_API2)
@@ -91,6 +121,8 @@ CURLcode Curl_vquic_ctx_init(struct Curl_easy *data,
BUFQ_OPT_SOFT_LIMIT);
#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
qctx->no_gso = FALSE;
+#elif defined(HAVE_APPLE_MSG_X)
+ qctx->no_gso = FALSE;
#else
qctx->no_gso = TRUE;
#endif
@@ -126,6 +158,79 @@ void Curl_vquic_ctx_update_time(struct cf_quic_ctx *qctx,
qctx->last_op = *pnow;
}
+#ifdef HAVE_APPLE_MSG_X
+static CURLcode do_sendmsg(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen,
+ size_t *psent)
+{
+#define MSG_X_SNUM 64
+ struct iovec msg_iov[MSG_X_SNUM];
+ struct msghdr_x mmsg[MSG_X_SNUM];
+ char errstr[STRERROR_LEN];
+ size_t n, i = 0, sent = 0;
+ int rc;
+ CURLcode result = CURLE_OK;
+ VERBOSE(size_t calls = 0);
+
+ *psent = 0;
+ if(!pktlen)
+ return CURLE_OK;
+ if(!gsolen || (pktlen < gsolen))
+ gsolen = pktlen;
+ n = pktlen / gsolen;
+ while(i < n) {
+ size_t j, batch = CURLMIN(n - i, MSG_X_SNUM);
+
+ for(j = 0; j < batch; ++j) {
+ msg_iov[j].iov_base = CURL_UNCONST(&pkt[(i + j) * gsolen]);
+ msg_iov[j].iov_len = gsolen;
+ memset(&mmsg[j], 0, sizeof(mmsg[j]));
+ mmsg[j].msg_iov = &msg_iov[j];
+ mmsg[j].msg_iovlen = 1;
+ }
+
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ while((rc = syscall(SYS_sendmsg_x, qctx->sockfd, &mmsg, batch, 0)) == -1 &&
+ (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
+ ;
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
+ if(rc == -1) {
+ if(SOCK_EAGAIN(SOCKERRNO)) {
+ CURL_TRC_CF(data, cf, "egress, sendmsg_x -> EAGAIN");
+ result = sent ? CURLE_OK : CURLE_AGAIN;
+ goto out;
+ }
+ curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
+ failf(data, "QUIC: sendmsg_x() unexpectedly returned %d (errno=%d; %s)",
+ rc, SOCKERRNO, errstr);
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+ VERBOSE(++calls);
+ i += batch;
+ sent += (batch * gsolen);
+ }
+
+out:
+ *psent = sent;
+ if(sent || result)
+ CURL_TRC_CF(data, cf,
+ "vquic_sendmsg_x(len=%zu, gso=%zu, packets=%zu, "
+ "calls=%zu) -> %d",
+ sent, gsolen, n, calls, (int)result);
+ return result;
+}
+
+#else /* HAVE_APPLE_MSG_X */
+
static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_quic_ctx *qctx,
@@ -231,16 +336,13 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
(void)cf;
out:
+ CURL_TRC_CF(data, cf,
+ "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu",
+ VQUIC_SEND_METHOD, pktlen, gsolen, (int)result, *psent);
return result;
}
-#ifdef CURLVERBOSE
-#ifdef HAVE_SENDMSG
-#define VQUIC_SEND_METHOD "sendmsg"
-#else
-#define VQUIC_SEND_METHOD "send"
-#endif
-#endif
+#endif /* !HAVE_APPLE_MSG_X */
static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -333,9 +435,6 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf,
}
else {
result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
- CURL_TRC_CF(data, cf,
- "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu",
- VQUIC_SEND_METHOD, pktlen, gsolen, (int)result, *psent);
}
if(!result)
qctx->last_io = qctx->last_op;
@@ -405,13 +504,13 @@ CURLcode Curl_vquic_send_tail_split(struct Curl_cfilter *cf,
qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len;
qctx->split_gsolen = gsolen;
qctx->gsolen = tail_gsolen;
- CURL_TRC_CF(data, cf, "Curl_vquic_send_tail_split: "
- "[%zu gso=%zu][%zu gso=%zu]",
+ CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
qctx->split_len, qctx->split_gsolen, tail_len, qctx->gsolen);
return Curl_vquic_flush(cf, data, qctx);
}
-#if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)
+#if (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \
+ !defined(HAVE_APPLE_MSG_X)
static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg)
{
int gso_size = 0;
@@ -547,6 +646,112 @@ out:
return result;
}
+#elif defined(HAVE_APPLE_MSG_X)
+
+static CURLcode recvmsg_x_packets(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ size_t max_pkts,
+ Curl_vquic_recv_pkts_cb *recv_cb,
+ void *userp)
+{
+#define MSG_X_NUM 64
+#define MSG_BUF_SIZE (2048)
+ struct iovec msg_iov[MSG_X_NUM];
+ struct msghdr_x mmsg[MSG_X_NUM];
+ uint8_t msg_ctrl[MSG_X_NUM * CMSG_SPACE(sizeof(int))];
+ struct sockaddr_storage remote_addr[MSG_X_NUM];
+ size_t total_nread = 0, pkts = 0;
+#ifdef CURLVERBOSE
+ size_t calls = 0;
+#endif
+ int mcount, i;
+ char errstr[STRERROR_LEN];
+ CURLcode result = CURLE_OK;
+ size_t gso_size;
+ char *sockbuf = NULL;
+ uint8_t (*bufs)[MSG_BUF_SIZE] = NULL;
+
+ DEBUGASSERT(max_pkts > 0);
+ result = Curl_multi_xfer_sockbuf_borrow(data, MSG_X_NUM * MSG_BUF_SIZE,
+ &sockbuf);
+ if(result)
+ goto out;
+ bufs = (uint8_t (*)[MSG_BUF_SIZE])sockbuf;
+
+ total_nread = 0;
+ while(pkts < max_pkts) {
+ int n = (int)CURLMIN(CURLMIN(MSG_X_NUM, IOV_MAX), max_pkts);
+ memset(&mmsg, 0, sizeof(mmsg));
+ for(i = 0; i < n; ++i) {
+ msg_iov[i].iov_base = bufs[i];
+ msg_iov[i].iov_len = sizeof(bufs[i]);
+ mmsg[i].msg_iov = &msg_iov[i];
+ mmsg[i].msg_iovlen = 1;
+ mmsg[i].msg_name = &remote_addr[i];
+ mmsg[i].msg_namelen = sizeof(remote_addr[i]);
+ mmsg[i].msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))];
+ mmsg[i].msg_controllen = CMSG_SPACE(sizeof(int));
+ }
+
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ while((mcount = syscall(SYS_recvmsg_x, qctx->sockfd, mmsg, n, 0)) == -1 &&
+ (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
+ ;
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+ if(mcount == -1) {
+ if(SOCK_EAGAIN(SOCKERRNO)) {
+ CURL_TRC_CF(data, cf, "ingress, recvmsg_x -> EAGAIN");
+ goto out;
+ }
+ if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
+ struct ip_quadruple ip;
+ if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
+ failf(data, "QUIC: connection to %s port %u refused",
+ ip.remote_ip, ip.remote_port);
+ result = CURLE_COULDNT_CONNECT;
+ goto out;
+ }
+ curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
+ failf(data, "QUIC: recvmsg_x() unexpectedly returned %d (errno=%d; %s)",
+ mcount, SOCKERRNO, errstr);
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+
+ VERBOSE(++calls);
+ for(i = 0; i < mcount; ++i) {
+ /* A zero-length UDP packet is no QUIC packet. Ignore. */
+ if(!mmsg[i].msg_datalen) {
+ ++pkts;
+ continue;
+ }
+ total_nread += mmsg[i].msg_datalen;
+ gso_size = mmsg[i].msg_datalen;
+
+ result = recv_cb(bufs[i], mmsg[i].msg_datalen, gso_size,
+ mmsg[i].msg_name,
+ mmsg[i].msg_namelen, 0, userp);
+ if(result)
+ goto out;
+ pkts += (mmsg[i].msg_datalen + gso_size - 1) / gso_size;
+ }
+ }
+
+out:
+ if(total_nread || result)
+ CURL_TRC_CF(data, cf,
+ "vquic_recvmsg_x(len=%zu, packets=%zu, calls=%zu) -> %d",
+ total_nread, pkts, calls, (int)result);
+ Curl_multi_xfer_sockbuf_release(data, sockbuf);
+ return result;
+}
+
#elif defined(HAVE_SENDMSG)
static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -705,6 +910,8 @@ CURLcode Curl_vquic_recv_packets(struct Curl_cfilter *cf,
CURLcode result;
#ifdef HAVE_SENDMMSG
result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
+#elif defined(HAVE_APPLE_MSG_X)
+ result = recvmsg_x_packets(cf, data, qctx, max_pkts, recv_cb, userp);
#elif defined(HAVE_SENDMSG)
result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
#else