quic: improve UDP GRO receives

Closes #19101
This commit is contained in:
Stefan Eissing 2025-10-17 13:50:49 +02:00 committed by Daniel Stenberg
parent 1d01d4975f
commit 5cefb455d4
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
4 changed files with 96 additions and 99 deletions

View file

@ -1731,37 +1731,43 @@ denied:
return result;
}
static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
struct sockaddr_storage *remote_addr,
socklen_t remote_addrlen, int ecn,
void *userp)
static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen,
size_t gso_size,
struct sockaddr_storage *remote_addr,
socklen_t remote_addrlen, int ecn,
void *userp)
{
struct pkt_io_ctx *pktx = userp;
struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx;
ngtcp2_pkt_info pi;
ngtcp2_path path;
size_t offset, pktlen;
int rv;
if(ecn)
CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, ecn=%x)",
pktlen, ecn);
CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)",
buflen, gso_size, ecn);
ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr,
(socklen_t)ctx->q.local_addrlen);
ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr,
remote_addrlen);
pi.ecn = (uint8_t)ecn;
rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts);
if(rv) {
CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)",
ngtcp2_strerror(rv), rv);
cf_ngtcp2_err_set(pktx->cf, pktx->data, rv);
for(offset = 0; offset < buflen; offset += gso_size) {
pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset);
rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi,
buf + offset, pktlen, pktx->ts);
if(rv) {
CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)",
ngtcp2_strerror(rv), rv);
cf_ngtcp2_err_set(pktx->cf, pktx->data, rv);
if(rv == NGTCP2_ERR_CRYPTO)
/* this is a "TLS problem", but a failed certificate verification
is a common reason for this */
return CURLE_PEER_FAILED_VERIFICATION;
return CURLE_RECV_ERROR;
if(rv == NGTCP2_ERR_CRYPTO)
/* this is a "TLS problem", but a failed certificate verification
is a common reason for this */
return CURLE_PEER_FAILED_VERIFICATION;
return CURLE_RECV_ERROR;
}
}
return CURLE_OK;
}
@ -1787,7 +1793,8 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
if(result)
return result;
return vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, pktx);
return vquic_recv_packets(cf, data, &ctx->q, 1000,
cf_ngtcp2_recv_pkts, pktx);
}
/**

View file

@ -625,58 +625,63 @@ struct recv_ctx {
int pkts;
};
static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
struct sockaddr_storage *remote_addr,
socklen_t remote_addrlen, int ecn,
void *userp)
static CURLcode cf_quiche_recv_pkts(const unsigned char *buf, size_t buflen,
size_t gso_size,
struct sockaddr_storage *remote_addr,
socklen_t remote_addrlen, int ecn,
void *userp)
{
struct recv_ctx *r = userp;
struct cf_quiche_ctx *ctx = r->cf->ctx;
quiche_recv_info recv_info;
size_t pktlen, offset;
ssize_t nread;
(void)ecn;
++r->pkts;
recv_info.to = (struct sockaddr *)&ctx->q.local_addr;
recv_info.to_len = ctx->q.local_addrlen;
recv_info.from = (struct sockaddr *)remote_addr;
recv_info.from_len = remote_addrlen;
nread = quiche_conn_recv(ctx->qconn,
(unsigned char *)CURL_UNCONST(pkt), pktlen,
&recv_info);
if(nread < 0) {
if(QUICHE_ERR_DONE == nread) {
if(quiche_conn_is_draining(ctx->qconn)) {
CURL_TRC_CF(r->data, r->cf, "ingress, connection is draining");
for(offset = 0; offset < buflen; offset += gso_size) {
pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset);
nread = quiche_conn_recv(ctx->qconn,
(unsigned char *)CURL_UNCONST(buf + offset),
pktlen, &recv_info);
if(nread < 0) {
if(QUICHE_ERR_DONE == nread) {
if(quiche_conn_is_draining(ctx->qconn)) {
CURL_TRC_CF(r->data, r->cf, "ingress, connection is draining");
return CURLE_RECV_ERROR;
}
if(quiche_conn_is_closed(ctx->qconn)) {
CURL_TRC_CF(r->data, r->cf, "ingress, connection is closed");
return CURLE_RECV_ERROR;
}
CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE");
return CURLE_OK;
}
else if(QUICHE_ERR_TLS_FAIL == nread) {
long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl);
if(verify_ok != X509_V_OK) {
failf(r->data, "SSL certificate problem: %s",
X509_verify_cert_error_string(verify_ok));
return CURLE_PEER_FAILED_VERIFICATION;
}
failf(r->data, "ingress, quiche reports TLS fail");
return CURLE_RECV_ERROR;
}
if(quiche_conn_is_closed(ctx->qconn)) {
CURL_TRC_CF(r->data, r->cf, "ingress, connection is closed");
else {
failf(r->data, "quiche reports error %zd on receive", nread);
return CURLE_RECV_ERROR;
}
CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE");
return CURLE_OK;
}
else if(QUICHE_ERR_TLS_FAIL == nread) {
long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl);
if(verify_ok != X509_V_OK) {
failf(r->data, "SSL certificate problem: %s",
X509_verify_cert_error_string(verify_ok));
return CURLE_PEER_FAILED_VERIFICATION;
}
failf(r->data, "ingress, quiche reports TLS fail");
return CURLE_RECV_ERROR;
else if((size_t)nread < pktlen) {
CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes",
nread, pktlen);
}
else {
failf(r->data, "quiche reports error %zd on receive", nread);
return CURLE_RECV_ERROR;
}
}
else if((size_t)nread < pktlen) {
CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes",
nread, pktlen);
++r->pkts;
}
return CURLE_OK;
@ -698,7 +703,8 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
rctx.data = data;
rctx.pkts = 0;
result = vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, &rctx);
result = vquic_recv_packets(cf, data, &ctx->q, 1000,
cf_quiche_recv_pkts, &rctx);
if(result)
return result;

View file

@ -378,9 +378,16 @@ static CURLcode recvmmsg_packets(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)
vquic_recv_pkts_cb *recv_cb, void *userp)
{
#if defined(__linux__) && defined(UDP_GRO)
#define MMSG_NUM 16
#define UDP_GRO_CNT_MAX 64
#else
#define MMSG_NUM 64
#define UDP_GRO_CNT_MAX 1
#endif
#define MSG_BUF_SIZE (UDP_GRO_CNT_MAX * 1500)
struct iovec msg_iov[MMSG_NUM];
struct mmsghdr mmsg[MMSG_NUM];
uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))];
@ -390,17 +397,15 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
char errstr[STRERROR_LEN];
CURLcode result = CURLE_OK;
size_t gso_size;
size_t pktlen;
size_t offset, to;
char *sockbuf = NULL;
uint8_t (*bufs)[64*1024] = NULL;
uint8_t (*bufs)[MSG_BUF_SIZE] = NULL;
DEBUGASSERT(max_pkts > 0);
result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * sizeof(bufs[0]),
result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * MSG_BUF_SIZE,
&sockbuf);
if(result)
goto out;
bufs = (uint8_t (*)[64*1024])sockbuf;
bufs = (uint8_t (*)[MSG_BUF_SIZE])sockbuf;
total_nread = 0;
while(pkts < max_pkts) {
@ -449,22 +454,12 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
gso_size = mmsg[i].msg_len;
}
for(offset = 0; offset < mmsg[i].msg_len; offset = to) {
++pkts;
to = offset + gso_size;
if(to > mmsg[i].msg_len) {
pktlen = mmsg[i].msg_len - offset;
}
else {
pktlen = gso_size;
}
result = recv_cb(bufs[i] + offset, pktlen, mmsg[i].msg_hdr.msg_name,
mmsg[i].msg_hdr.msg_namelen, 0, userp);
if(result)
goto out;
}
result = recv_cb(bufs[i], mmsg[i].msg_len, gso_size,
mmsg[i].msg_hdr.msg_name,
mmsg[i].msg_hdr.msg_namelen, 0, userp);
if(result)
goto out;
pkts += (mmsg[i].msg_len + gso_size - 1) / gso_size;
}
}
@ -481,7 +476,7 @@ static CURLcode recvmsg_packets(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)
vquic_recv_pkts_cb *recv_cb, void *userp)
{
struct iovec msg_iov;
struct msghdr msg;
@ -494,8 +489,6 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
CURLcode result = CURLE_OK;
uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))];
size_t gso_size;
size_t pktlen;
size_t offset, to;
DEBUGASSERT(max_pkts > 0);
for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) {
@ -542,20 +535,10 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
gso_size = nread;
}
for(offset = 0; offset < nread; offset = to) {
++pkts;
to = offset + gso_size;
if(to > nread)
pktlen = nread - offset;
else
pktlen = gso_size;
result =
recv_cb(buf + offset, pktlen, msg.msg_name, msg.msg_namelen, 0, userp);
if(result)
goto out;
}
result = recv_cb(buf, nread, gso_size,
msg.msg_name, msg.msg_namelen, 0, userp);
if(result)
goto out;
}
out:
@ -570,7 +553,7 @@ static CURLcode recvfrom_packets(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)
vquic_recv_pkts_cb *recv_cb, void *userp)
{
uint8_t buf[64*1024];
int bufsize = (int)sizeof(buf);
@ -611,8 +594,8 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
++pkts;
++calls;
total_nread += (size_t)nread;
result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen,
0, userp);
result = recv_cb(buf, (size_t)nread, (size_t)nread,
&remote_addr, remote_addrlen, 0, userp);
if(result)
goto out;
}
@ -629,7 +612,7 @@ CURLcode vquic_recv_packets(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)
vquic_recv_pkts_cb *recv_cb, void *userp)
{
CURLcode result;
#ifdef HAVE_SENDMMSG

View file

@ -78,16 +78,17 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data,
struct cf_quic_ctx *qctx);
typedef CURLcode vquic_recv_pkt_cb(const unsigned char *pkt, size_t pktlen,
struct sockaddr_storage *remote_addr,
socklen_t remote_addrlen, int ecn,
void *userp);
typedef CURLcode vquic_recv_pkts_cb(const unsigned char *buf, size_t buflen,
size_t gso_size,
struct sockaddr_storage *remote_addr,
socklen_t remote_addrlen, int ecn,
void *userp);
CURLcode vquic_recv_packets(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);
vquic_recv_pkts_cb *recv_cb, void *userp);
#endif /* !USE_HTTP3 */