mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-26 22:52:13 +00: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/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_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
|
||||
|
||||
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/quic_ack.h>
|
||||
#include <haproxy/quic_cid.h>
|
||||
#include <haproxy/quic_retransmit.h>
|
||||
#include <haproxy/quic_retry.h>
|
||||
#include <haproxy/quic_sock.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/quic_cid.h>
|
||||
#include <haproxy/quic_conn.h>
|
||||
#include <haproxy/quic_retransmit.h>
|
||||
#include <haproxy/quic_retry.h>
|
||||
#include <haproxy/quic_sock.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);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* Returns allocated buffer or NULL on error.
|
||||
|
Loading…
Reference in New Issue
Block a user