mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 15:17:01 +02:00
REORG: quic: Add a new module for retransmissions
Move several functions in relation with the retransmissions from TX part (quic_tx.c) to quic_retransmit.c new C file.
This commit is contained in:
parent
714d1096bc
commit
95e9033fd2
3
Makefile
3
Makefile
@ -620,7 +620,8 @@ OPTIONS_OBJS += src/quic_conn.o src/mux_quic.o src/h3.o src/xprt_quic.o \
|
|||||||
src/h3_stats.o src/qmux_http.o src/cfgparse-quic.o \
|
src/h3_stats.o src/qmux_http.o src/cfgparse-quic.o \
|
||||||
src/cbuf.o src/quic_cc.o src/quic_cc_nocc.o src/quic_ack.o \
|
src/cbuf.o src/quic_cc.o src/quic_cc_nocc.o src/quic_ack.o \
|
||||||
src/quic_trace.o src/quic_cli.o src/quic_ssl.o \
|
src/quic_trace.o src/quic_cli.o src/quic_ssl.o \
|
||||||
src/quic_rx.o src/quic_tx.o src/quic_cid.o src/quic_retry.o
|
src/quic_rx.o src/quic_tx.o src/quic_cid.o src/quic_retry.o\
|
||||||
|
src/quic_retransmit.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_QUIC_OPENSSL_COMPAT),)
|
ifneq ($(USE_QUIC_OPENSSL_COMPAT),)
|
||||||
|
20
include/haproxy/quic_retransmit.h
Normal file
20
include/haproxy/quic_retransmit.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef _HAPROXY_QUIC_RETRANSMIT_H
|
||||||
|
#define _HAPROXY_QUIC_RETRANSMIT_H
|
||||||
|
|
||||||
|
#ifdef USE_QUIC
|
||||||
|
#ifndef USE_OPENSSL
|
||||||
|
#error "Must define USE_OPENSSL"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <haproxy/list-t.h>
|
||||||
|
#include <haproxy/quic_conn-t.h>
|
||||||
|
#include <haproxy/quic_tls-t.h>
|
||||||
|
|
||||||
|
void qc_prep_fast_retrans(struct quic_conn *qc,
|
||||||
|
struct quic_pktns *pktns,
|
||||||
|
struct list *frms1, struct list *frms2);
|
||||||
|
void qc_prep_hdshk_fast_retrans(struct quic_conn *qc,
|
||||||
|
struct list *ifrms, struct list *hfrms);
|
||||||
|
|
||||||
|
#endif /* USE_QUIC */
|
||||||
|
#endif /* _HAPROXY_QUIC_RETRANSMIT_H */
|
252
src/quic_retransmit.c
Normal file
252
src/quic_retransmit.c
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#include <import/eb64tree.h>
|
||||||
|
|
||||||
|
#include <haproxy/quic_conn.h>
|
||||||
|
#include <haproxy/quic_frame.h>
|
||||||
|
#include <haproxy/quic_retransmit.h>
|
||||||
|
#include <haproxy/quic_trace.h>
|
||||||
|
#include <haproxy/quic_tx.h>
|
||||||
|
#include <haproxy/trace.h>
|
||||||
|
|
||||||
|
#define TRACE_SOURCE &trace_quic
|
||||||
|
|
||||||
|
/* Duplicate all frames from <pkt_frm_list> list into <out_frm_list> list
|
||||||
|
* for <qc> QUIC connection.
|
||||||
|
* This is a best effort function which never fails even if no memory could be
|
||||||
|
* allocated to duplicate these frames.
|
||||||
|
*/
|
||||||
|
static void qc_dup_pkt_frms(struct quic_conn *qc,
|
||||||
|
struct list *pkt_frm_list, struct list *out_frm_list)
|
||||||
|
{
|
||||||
|
struct quic_frame *frm, *frmbak;
|
||||||
|
struct list tmp = LIST_HEAD_INIT(tmp);
|
||||||
|
|
||||||
|
TRACE_ENTER(QUIC_EV_CONN_PRSAFRM, qc);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(frm, frmbak, pkt_frm_list, list) {
|
||||||
|
struct quic_frame *dup_frm, *origin;
|
||||||
|
|
||||||
|
if (frm->flags & QUIC_FL_TX_FRAME_ACKED) {
|
||||||
|
TRACE_DEVEL("already acknowledged frame", QUIC_EV_CONN_PRSAFRM, qc, frm);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (frm->type) {
|
||||||
|
case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F:
|
||||||
|
{
|
||||||
|
struct qf_stream *strm_frm = &frm->stream;
|
||||||
|
struct eb64_node *node = NULL;
|
||||||
|
struct qc_stream_desc *stream_desc;
|
||||||
|
|
||||||
|
node = eb64_lookup(&qc->streams_by_id, strm_frm->id);
|
||||||
|
if (!node) {
|
||||||
|
TRACE_DEVEL("ignored frame for a released stream", QUIC_EV_CONN_PRSAFRM, qc, frm);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_desc = eb64_entry(node, struct qc_stream_desc, by_id);
|
||||||
|
/* Do not resend this frame if in the "already acked range" */
|
||||||
|
if (strm_frm->offset.key + strm_frm->len <= stream_desc->ack_offset) {
|
||||||
|
TRACE_DEVEL("ignored frame in already acked range",
|
||||||
|
QUIC_EV_CONN_PRSAFRM, qc, frm);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (strm_frm->offset.key < stream_desc->ack_offset) {
|
||||||
|
uint64_t diff = stream_desc->ack_offset - strm_frm->offset.key;
|
||||||
|
|
||||||
|
qc_stream_frm_mv_fwd(frm, diff);
|
||||||
|
TRACE_DEVEL("updated partially acked frame",
|
||||||
|
QUIC_EV_CONN_PRSAFRM, qc, frm);
|
||||||
|
}
|
||||||
|
|
||||||
|
strm_frm->dup = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If <frm> is already a copy of another frame, we must take
|
||||||
|
* its original frame as source for the copy.
|
||||||
|
*/
|
||||||
|
origin = frm->origin ? frm->origin : frm;
|
||||||
|
dup_frm = qc_frm_dup(origin);
|
||||||
|
if (!dup_frm) {
|
||||||
|
TRACE_ERROR("could not duplicate frame", QUIC_EV_CONN_PRSAFRM, qc, frm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_DEVEL("built probing frame", QUIC_EV_CONN_PRSAFRM, qc, origin);
|
||||||
|
if (origin->pkt) {
|
||||||
|
TRACE_DEVEL("duplicated from packet", QUIC_EV_CONN_PRSAFRM,
|
||||||
|
qc, dup_frm, &origin->pkt->pn_node.key);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* <origin> is a frame which was sent from a packet detected as lost. */
|
||||||
|
TRACE_DEVEL("duplicated from lost packet", QUIC_EV_CONN_PRSAFRM, qc);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_APPEND(&tmp, &dup_frm->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_SPLICE(out_frm_list, &tmp);
|
||||||
|
|
||||||
|
TRACE_LEAVE(QUIC_EV_CONN_PRSAFRM, qc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Boolean function which return 1 if <pkt> TX packet is only made of
|
||||||
|
* already acknowledged frame.
|
||||||
|
*/
|
||||||
|
static inline int qc_pkt_with_only_acked_frms(struct quic_tx_packet *pkt)
|
||||||
|
{
|
||||||
|
struct quic_frame *frm;
|
||||||
|
|
||||||
|
list_for_each_entry(frm, &pkt->frms, list)
|
||||||
|
if (!(frm->flags & QUIC_FL_TX_FRAME_ACKED))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare a fast retransmission from <qel> encryption level */
|
||||||
|
void qc_prep_fast_retrans(struct quic_conn *qc,
|
||||||
|
struct quic_pktns *pktns,
|
||||||
|
struct list *frms1, struct list *frms2)
|
||||||
|
{
|
||||||
|
struct eb_root *pkts = &pktns->tx.pkts;
|
||||||
|
struct list *frms = frms1;
|
||||||
|
struct eb64_node *node;
|
||||||
|
struct quic_tx_packet *pkt;
|
||||||
|
|
||||||
|
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
||||||
|
|
||||||
|
BUG_ON(frms1 == frms2);
|
||||||
|
|
||||||
|
pkt = NULL;
|
||||||
|
node = eb64_first(pkts);
|
||||||
|
start:
|
||||||
|
while (node) {
|
||||||
|
struct quic_tx_packet *p;
|
||||||
|
|
||||||
|
p = eb64_entry(node, struct quic_tx_packet, pn_node);
|
||||||
|
node = eb64_next(node);
|
||||||
|
/* Skip the empty and coalesced packets */
|
||||||
|
TRACE_PRINTF(TRACE_LEVEL_PROTO, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
|
||||||
|
"--> pn=%llu (%d %d %d)", (ull)p->pn_node.key,
|
||||||
|
LIST_ISEMPTY(&p->frms), !!(p->flags & QUIC_FL_TX_PACKET_COALESCED),
|
||||||
|
qc_pkt_with_only_acked_frms(p));
|
||||||
|
if (!LIST_ISEMPTY(&p->frms) && !qc_pkt_with_only_acked_frms(p)) {
|
||||||
|
pkt = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pkt)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
/* When building a packet from another one, the field which may increase the
|
||||||
|
* packet size is the packet number. And the maximum increase is 4 bytes.
|
||||||
|
*/
|
||||||
|
if (!quic_peer_validated_addr(qc) && qc_is_listener(qc) &&
|
||||||
|
pkt->len + 4 > quic_may_send_bytes(qc)) {
|
||||||
|
qc->flags |= QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED;
|
||||||
|
TRACE_PROTO("anti-amplification limit would be reached", QUIC_EV_CONN_SPPKTS, qc, pkt);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_PROTO("duplicating packet", QUIC_EV_CONN_SPPKTS, qc, pkt);
|
||||||
|
qc_dup_pkt_frms(qc, &pkt->frms, frms);
|
||||||
|
if (frms == frms1 && frms2) {
|
||||||
|
frms = frms2;
|
||||||
|
goto start;
|
||||||
|
}
|
||||||
|
leave:
|
||||||
|
TRACE_LEAVE(QUIC_EV_CONN_SPPKTS, qc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare a fast retransmission during a handshake after a client
|
||||||
|
* has resent Initial packets. According to the RFC a server may retransmit
|
||||||
|
* Initial packets send them coalescing with others (Handshake here).
|
||||||
|
* (Listener only function).
|
||||||
|
*/
|
||||||
|
void qc_prep_hdshk_fast_retrans(struct quic_conn *qc,
|
||||||
|
struct list *ifrms, struct list *hfrms)
|
||||||
|
{
|
||||||
|
struct list itmp = LIST_HEAD_INIT(itmp);
|
||||||
|
struct list htmp = LIST_HEAD_INIT(htmp);
|
||||||
|
|
||||||
|
struct quic_enc_level *iqel = qc->iel;
|
||||||
|
struct quic_enc_level *hqel = qc->hel;
|
||||||
|
struct quic_enc_level *qel = iqel;
|
||||||
|
struct eb_root *pkts;
|
||||||
|
struct eb64_node *node;
|
||||||
|
struct quic_tx_packet *pkt;
|
||||||
|
struct list *tmp = &itmp;
|
||||||
|
|
||||||
|
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
||||||
|
start:
|
||||||
|
pkt = NULL;
|
||||||
|
pkts = &qel->pktns->tx.pkts;
|
||||||
|
node = eb64_first(pkts);
|
||||||
|
/* Skip the empty packet (they have already been retransmitted) */
|
||||||
|
while (node) {
|
||||||
|
struct quic_tx_packet *p;
|
||||||
|
|
||||||
|
p = eb64_entry(node, struct quic_tx_packet, pn_node);
|
||||||
|
TRACE_PRINTF(TRACE_LEVEL_PROTO, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
|
||||||
|
"--> pn=%llu (%d %d)", (ull)p->pn_node.key,
|
||||||
|
LIST_ISEMPTY(&p->frms), !!(p->flags & QUIC_FL_TX_PACKET_COALESCED));
|
||||||
|
if (!LIST_ISEMPTY(&p->frms) && !(p->flags & QUIC_FL_TX_PACKET_COALESCED) &&
|
||||||
|
!qc_pkt_with_only_acked_frms(p)) {
|
||||||
|
pkt = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = eb64_next(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pkt)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* When building a packet from another one, the field which may increase the
|
||||||
|
* packet size is the packet number. And the maximum increase is 4 bytes.
|
||||||
|
*/
|
||||||
|
if (!quic_peer_validated_addr(qc) && qc_is_listener(qc)) {
|
||||||
|
size_t dglen = pkt->len + 4;
|
||||||
|
size_t may_send;
|
||||||
|
|
||||||
|
may_send = quic_may_send_bytes(qc);
|
||||||
|
dglen += pkt->next ? pkt->next->len + 4 : 0;
|
||||||
|
if (dglen > may_send) {
|
||||||
|
qc->flags |= QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED;
|
||||||
|
TRACE_PROTO("anti-amplification limit would be reached", QUIC_EV_CONN_SPPKTS, qc, pkt);
|
||||||
|
if (pkt->next)
|
||||||
|
TRACE_PROTO("anti-amplification limit would be reached", QUIC_EV_CONN_SPPKTS, qc, pkt->next);
|
||||||
|
if (qel == iqel && may_send >= QUIC_INITIAL_PACKET_MINLEN)
|
||||||
|
TRACE_PROTO("will probe Initial packet number space", QUIC_EV_CONN_SPPKTS, qc);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qel->pktns->tx.pto_probe += 1;
|
||||||
|
|
||||||
|
/* No risk to loop here, #packet per datagram is bounded */
|
||||||
|
requeue:
|
||||||
|
TRACE_PROTO("duplicating packet", QUIC_EV_CONN_PRSAFRM, qc, NULL, &pkt->pn_node.key);
|
||||||
|
qc_dup_pkt_frms(qc, &pkt->frms, tmp);
|
||||||
|
if (qel == iqel) {
|
||||||
|
if (pkt->next && pkt->next->type == QUIC_PACKET_TYPE_HANDSHAKE) {
|
||||||
|
pkt = pkt->next;
|
||||||
|
tmp = &htmp;
|
||||||
|
hqel->pktns->tx.pto_probe += 1;
|
||||||
|
TRACE_DEVEL("looping for next packet", QUIC_EV_CONN_SPPKTS, qc);
|
||||||
|
goto requeue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
LIST_SPLICE(ifrms, &itmp);
|
||||||
|
LIST_SPLICE(hfrms, &htmp);
|
||||||
|
|
||||||
|
TRACE_LEAVE(QUIC_EV_CONN_SPPKTS, qc);
|
||||||
|
}
|
@ -20,6 +20,7 @@
|
|||||||
#include <haproxy/proto_quic.h>
|
#include <haproxy/proto_quic.h>
|
||||||
#include <haproxy/quic_ack.h>
|
#include <haproxy/quic_ack.h>
|
||||||
#include <haproxy/quic_cid.h>
|
#include <haproxy/quic_cid.h>
|
||||||
|
#include <haproxy/quic_retransmit.h>
|
||||||
#include <haproxy/quic_retry.h>
|
#include <haproxy/quic_retry.h>
|
||||||
#include <haproxy/quic_sock.h>
|
#include <haproxy/quic_sock.h>
|
||||||
#include <haproxy/quic_stream.h>
|
#include <haproxy/quic_stream.h>
|
||||||
|
243
src/quic_tx.c
243
src/quic_tx.c
@ -18,6 +18,7 @@
|
|||||||
#include <haproxy/trace.h>
|
#include <haproxy/trace.h>
|
||||||
#include <haproxy/quic_cid.h>
|
#include <haproxy/quic_cid.h>
|
||||||
#include <haproxy/quic_conn.h>
|
#include <haproxy/quic_conn.h>
|
||||||
|
#include <haproxy/quic_retransmit.h>
|
||||||
#include <haproxy/quic_retry.h>
|
#include <haproxy/quic_retry.h>
|
||||||
#include <haproxy/quic_sock.h>
|
#include <haproxy/quic_sock.h>
|
||||||
#include <haproxy/quic_tls.h>
|
#include <haproxy/quic_tls.h>
|
||||||
@ -80,248 +81,6 @@ static inline void free_quic_tx_packet(struct quic_conn *qc,
|
|||||||
TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc);
|
TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Duplicate all frames from <pkt_frm_list> list into <out_frm_list> list
|
|
||||||
* for <qc> QUIC connection.
|
|
||||||
* This is a best effort function which never fails even if no memory could be
|
|
||||||
* allocated to duplicate these frames.
|
|
||||||
*/
|
|
||||||
static void qc_dup_pkt_frms(struct quic_conn *qc,
|
|
||||||
struct list *pkt_frm_list, struct list *out_frm_list)
|
|
||||||
{
|
|
||||||
struct quic_frame *frm, *frmbak;
|
|
||||||
struct list tmp = LIST_HEAD_INIT(tmp);
|
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_PRSAFRM, qc);
|
|
||||||
|
|
||||||
list_for_each_entry_safe(frm, frmbak, pkt_frm_list, list) {
|
|
||||||
struct quic_frame *dup_frm, *origin;
|
|
||||||
|
|
||||||
if (frm->flags & QUIC_FL_TX_FRAME_ACKED) {
|
|
||||||
TRACE_DEVEL("already acknowledged frame", QUIC_EV_CONN_PRSAFRM, qc, frm);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (frm->type) {
|
|
||||||
case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F:
|
|
||||||
{
|
|
||||||
struct qf_stream *strm_frm = &frm->stream;
|
|
||||||
struct eb64_node *node = NULL;
|
|
||||||
struct qc_stream_desc *stream_desc;
|
|
||||||
|
|
||||||
node = eb64_lookup(&qc->streams_by_id, strm_frm->id);
|
|
||||||
if (!node) {
|
|
||||||
TRACE_DEVEL("ignored frame for a released stream", QUIC_EV_CONN_PRSAFRM, qc, frm);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream_desc = eb64_entry(node, struct qc_stream_desc, by_id);
|
|
||||||
/* Do not resend this frame if in the "already acked range" */
|
|
||||||
if (strm_frm->offset.key + strm_frm->len <= stream_desc->ack_offset) {
|
|
||||||
TRACE_DEVEL("ignored frame in already acked range",
|
|
||||||
QUIC_EV_CONN_PRSAFRM, qc, frm);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (strm_frm->offset.key < stream_desc->ack_offset) {
|
|
||||||
uint64_t diff = stream_desc->ack_offset - strm_frm->offset.key;
|
|
||||||
|
|
||||||
qc_stream_frm_mv_fwd(frm, diff);
|
|
||||||
TRACE_DEVEL("updated partially acked frame",
|
|
||||||
QUIC_EV_CONN_PRSAFRM, qc, frm);
|
|
||||||
}
|
|
||||||
|
|
||||||
strm_frm->dup = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If <frm> is already a copy of another frame, we must take
|
|
||||||
* its original frame as source for the copy.
|
|
||||||
*/
|
|
||||||
origin = frm->origin ? frm->origin : frm;
|
|
||||||
dup_frm = qc_frm_dup(origin);
|
|
||||||
if (!dup_frm) {
|
|
||||||
TRACE_ERROR("could not duplicate frame", QUIC_EV_CONN_PRSAFRM, qc, frm);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE_DEVEL("built probing frame", QUIC_EV_CONN_PRSAFRM, qc, origin);
|
|
||||||
if (origin->pkt) {
|
|
||||||
TRACE_DEVEL("duplicated from packet", QUIC_EV_CONN_PRSAFRM,
|
|
||||||
qc, dup_frm, &origin->pkt->pn_node.key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* <origin> is a frame which was sent from a packet detected as lost. */
|
|
||||||
TRACE_DEVEL("duplicated from lost packet", QUIC_EV_CONN_PRSAFRM, qc);
|
|
||||||
}
|
|
||||||
|
|
||||||
LIST_APPEND(&tmp, &dup_frm->list);
|
|
||||||
}
|
|
||||||
|
|
||||||
LIST_SPLICE(out_frm_list, &tmp);
|
|
||||||
|
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_PRSAFRM, qc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Boolean function which return 1 if <pkt> TX packet is only made of
|
|
||||||
* already acknowledged frame.
|
|
||||||
*/
|
|
||||||
static inline int qc_pkt_with_only_acked_frms(struct quic_tx_packet *pkt)
|
|
||||||
{
|
|
||||||
struct quic_frame *frm;
|
|
||||||
|
|
||||||
list_for_each_entry(frm, &pkt->frms, list)
|
|
||||||
if (!(frm->flags & QUIC_FL_TX_FRAME_ACKED))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare a fast retransmission from <qel> encryption level */
|
|
||||||
static void qc_prep_fast_retrans(struct quic_conn *qc,
|
|
||||||
struct quic_pktns *pktns,
|
|
||||||
struct list *frms1, struct list *frms2)
|
|
||||||
{
|
|
||||||
struct eb_root *pkts = &pktns->tx.pkts;
|
|
||||||
struct list *frms = frms1;
|
|
||||||
struct eb64_node *node;
|
|
||||||
struct quic_tx_packet *pkt;
|
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
|
||||||
|
|
||||||
BUG_ON(frms1 == frms2);
|
|
||||||
|
|
||||||
pkt = NULL;
|
|
||||||
node = eb64_first(pkts);
|
|
||||||
start:
|
|
||||||
while (node) {
|
|
||||||
struct quic_tx_packet *p;
|
|
||||||
|
|
||||||
p = eb64_entry(node, struct quic_tx_packet, pn_node);
|
|
||||||
node = eb64_next(node);
|
|
||||||
/* Skip the empty and coalesced packets */
|
|
||||||
TRACE_PRINTF(TRACE_LEVEL_PROTO, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
|
|
||||||
"--> pn=%llu (%d %d %d)", (ull)p->pn_node.key,
|
|
||||||
LIST_ISEMPTY(&p->frms), !!(p->flags & QUIC_FL_TX_PACKET_COALESCED),
|
|
||||||
qc_pkt_with_only_acked_frms(p));
|
|
||||||
if (!LIST_ISEMPTY(&p->frms) && !qc_pkt_with_only_acked_frms(p)) {
|
|
||||||
pkt = p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pkt)
|
|
||||||
goto leave;
|
|
||||||
|
|
||||||
/* When building a packet from another one, the field which may increase the
|
|
||||||
* packet size is the packet number. And the maximum increase is 4 bytes.
|
|
||||||
*/
|
|
||||||
if (!quic_peer_validated_addr(qc) && qc_is_listener(qc) &&
|
|
||||||
pkt->len + 4 > quic_may_send_bytes(qc)) {
|
|
||||||
qc->flags |= QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED;
|
|
||||||
TRACE_PROTO("anti-amplification limit would be reached", QUIC_EV_CONN_SPPKTS, qc, pkt);
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE_PROTO("duplicating packet", QUIC_EV_CONN_SPPKTS, qc, pkt);
|
|
||||||
qc_dup_pkt_frms(qc, &pkt->frms, frms);
|
|
||||||
if (frms == frms1 && frms2) {
|
|
||||||
frms = frms2;
|
|
||||||
goto start;
|
|
||||||
}
|
|
||||||
leave:
|
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_SPPKTS, qc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare a fast retransmission during a handshake after a client
|
|
||||||
* has resent Initial packets. According to the RFC a server may retransmit
|
|
||||||
* Initial packets send them coalescing with others (Handshake here).
|
|
||||||
* (Listener only function).
|
|
||||||
*/
|
|
||||||
void qc_prep_hdshk_fast_retrans(struct quic_conn *qc,
|
|
||||||
struct list *ifrms, struct list *hfrms)
|
|
||||||
{
|
|
||||||
struct list itmp = LIST_HEAD_INIT(itmp);
|
|
||||||
struct list htmp = LIST_HEAD_INIT(htmp);
|
|
||||||
|
|
||||||
struct quic_enc_level *iqel = qc->iel;
|
|
||||||
struct quic_enc_level *hqel = qc->hel;
|
|
||||||
struct quic_enc_level *qel = iqel;
|
|
||||||
struct eb_root *pkts;
|
|
||||||
struct eb64_node *node;
|
|
||||||
struct quic_tx_packet *pkt;
|
|
||||||
struct list *tmp = &itmp;
|
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
|
||||||
start:
|
|
||||||
pkt = NULL;
|
|
||||||
pkts = &qel->pktns->tx.pkts;
|
|
||||||
node = eb64_first(pkts);
|
|
||||||
/* Skip the empty packet (they have already been retransmitted) */
|
|
||||||
while (node) {
|
|
||||||
struct quic_tx_packet *p;
|
|
||||||
|
|
||||||
p = eb64_entry(node, struct quic_tx_packet, pn_node);
|
|
||||||
TRACE_PRINTF(TRACE_LEVEL_PROTO, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
|
|
||||||
"--> pn=%llu (%d %d)", (ull)p->pn_node.key,
|
|
||||||
LIST_ISEMPTY(&p->frms), !!(p->flags & QUIC_FL_TX_PACKET_COALESCED));
|
|
||||||
if (!LIST_ISEMPTY(&p->frms) && !(p->flags & QUIC_FL_TX_PACKET_COALESCED) &&
|
|
||||||
!qc_pkt_with_only_acked_frms(p)) {
|
|
||||||
pkt = p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = eb64_next(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pkt)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
/* When building a packet from another one, the field which may increase the
|
|
||||||
* packet size is the packet number. And the maximum increase is 4 bytes.
|
|
||||||
*/
|
|
||||||
if (!quic_peer_validated_addr(qc) && qc_is_listener(qc)) {
|
|
||||||
size_t dglen = pkt->len + 4;
|
|
||||||
size_t may_send;
|
|
||||||
|
|
||||||
may_send = quic_may_send_bytes(qc);
|
|
||||||
dglen += pkt->next ? pkt->next->len + 4 : 0;
|
|
||||||
if (dglen > may_send) {
|
|
||||||
qc->flags |= QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED;
|
|
||||||
TRACE_PROTO("anti-amplification limit would be reached", QUIC_EV_CONN_SPPKTS, qc, pkt);
|
|
||||||
if (pkt->next)
|
|
||||||
TRACE_PROTO("anti-amplification limit would be reached", QUIC_EV_CONN_SPPKTS, qc, pkt->next);
|
|
||||||
if (qel == iqel && may_send >= QUIC_INITIAL_PACKET_MINLEN)
|
|
||||||
TRACE_PROTO("will probe Initial packet number space", QUIC_EV_CONN_SPPKTS, qc);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qel->pktns->tx.pto_probe += 1;
|
|
||||||
|
|
||||||
/* No risk to loop here, #packet per datagram is bounded */
|
|
||||||
requeue:
|
|
||||||
TRACE_PROTO("duplicating packet", QUIC_EV_CONN_PRSAFRM, qc, NULL, &pkt->pn_node.key);
|
|
||||||
qc_dup_pkt_frms(qc, &pkt->frms, tmp);
|
|
||||||
if (qel == iqel) {
|
|
||||||
if (pkt->next && pkt->next->type == QUIC_PACKET_TYPE_HANDSHAKE) {
|
|
||||||
pkt = pkt->next;
|
|
||||||
tmp = &htmp;
|
|
||||||
hqel->pktns->tx.pto_probe += 1;
|
|
||||||
TRACE_DEVEL("looping for next packet", QUIC_EV_CONN_SPPKTS, qc);
|
|
||||||
goto requeue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
LIST_SPLICE(ifrms, &itmp);
|
|
||||||
LIST_SPLICE(hfrms, &htmp);
|
|
||||||
|
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_SPPKTS, qc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate Tx buffer from <qc> quic-conn if needed.
|
/* Allocate Tx buffer from <qc> quic-conn if needed.
|
||||||
*
|
*
|
||||||
* Returns allocated buffer or NULL on error.
|
* Returns allocated buffer or NULL on error.
|
||||||
|
Loading…
Reference in New Issue
Block a user