Release 2.19.0

- [FEATURE] DPLPMTUD support.  IETF connections now search for the
  maximum packet size, improving throughput.
- [DEBUG] Record event in stream history when on_close() is called
  in dtor.
This commit is contained in:
Dmitri Tikhonov 2020-07-29 11:33:52 -04:00
parent b329a00e5e
commit b8fa619567
46 changed files with 3629 additions and 854 deletions

View file

@ -1,3 +1,10 @@
2020-07-29
- 2.19.0
- [FEATURE] DPLPMTUD support. IETF connections now search for the
maximum packet size, improving throughput.
- [DEBUG] Record event in stream history when on_close() is called
in dtor.
2020-07-22 2020-07-22
- 2.18.2 - 2.18.2
- [BUGFIX] Send prediction: lone path challenges do not get squeezed out - [BUGFIX] Send prediction: lone path challenges do not get squeezed out

View file

@ -105,9 +105,8 @@ Compilation
- Build both the http_client and http_server test programs. - Build both the http_client and http_server test programs.
Running Instructions Running Instructions
- On the server side, define the environment variable - Specify maximum packet size using -o base_plpmtu=$number.
LSQUIC_CN_PACK_SIZE and set it to the intended packet size. Valid sizes are up to 65535.
Valid sizes are up to 65507 (IPv4).
- Use the -W flag for http_client and http_server for the ability - Use the -W flag for http_client and http_server for the ability
to send packets of large size. to send packets of large size.
- On the client side, use the -z flag to specify the maximum size - On the client side, use the -z flag to specify the maximum size
@ -115,9 +114,9 @@ Running Instructions
Example Usage Example Usage
./http_client -p /file-1M -H www.litespeedtech.com -s 192.168.0.85:5443 ./http_client -p /file-1M -H www.litespeedtech.com -s 192.168.0.85:5443
-o version=FF000014 -z 65507 -W -o version=FF000014
./http_server -c www.litespeedtech.com,certschain,privkey ./http_server -c www.litespeedtech.com,certschain,privkey
-s 0.0.0.0:5443 -W -s 0.0.0.0:5443 -W -o base_plpmtu=65535
Additional Notes Additional Notes
Since this feature does not have MTU discovery enabled at the time of Since this feature does not have MTU discovery enabled at the time of

View file

@ -140,8 +140,8 @@ prog_print_common_options (const struct prog *prog, FILE *out)
#if LSQUIC_DONTFRAG_SUPPORTED #if LSQUIC_DONTFRAG_SUPPORTED
" -D Do not set `do not fragment' flag on outgoing UDP packets\n" " -D Do not set `do not fragment' flag on outgoing UDP packets\n"
#endif #endif
" -z BYTES Maximum size of outgoing UDP packets. The default is 1370\n" " -z BYTES Maximum size of outgoing UDP packets (client only).\n"
" bytes for IPv4 socket and 1350 bytes for IPv6 socket\n" " Overrides -o base_plpmtu.\n"
" -L LEVEL Log level for all modules. Possible values are `debug',\n" " -L LEVEL Log level for all modules. Possible values are `debug',\n"
" `info', `notice', `warn', `error', `alert', `emerg',\n" " `info', `notice', `warn', `error', `alert', `emerg',\n"
" and `crit'.\n" " and `crit'.\n"

View file

@ -1816,6 +1816,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
settings->es_scid_len = atoi(val); settings->es_scid_len = atoi(val);
return 0; return 0;
} }
if (0 == strncmp(name, "dplpmtud", 8))
{
settings->es_dplpmtud = atoi(val);
return 0;
}
break; break;
case 9: case 9:
if (0 == strncmp(name, "send_prst", 9)) if (0 == strncmp(name, "send_prst", 9))
@ -1835,6 +1840,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
settings->es_timestamps = atoi(val); settings->es_timestamps = atoi(val);
return 0; return 0;
} }
if (0 == strncmp(name, "max_plpmtu", 10))
{
settings->es_max_plpmtu = atoi(val);
return 0;
}
break; break;
case 11: case 11:
if (0 == strncmp(name, "ping_period", 11)) if (0 == strncmp(name, "ping_period", 11))
@ -1842,6 +1852,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
settings->es_ping_period = atoi(val); settings->es_ping_period = atoi(val);
return 0; return 0;
} }
if (0 == strncmp(name, "base_plpmtu", 11))
{
settings->es_base_plpmtu = atoi(val);
return 0;
}
break; break;
case 12: case 12:
if (0 == strncmp(name, "idle_conn_to", 12)) if (0 == strncmp(name, "idle_conn_to", 12))
@ -1975,7 +1990,7 @@ set_engine_option (struct lsquic_engine_settings *settings,
} }
break; break;
case 23: case 23:
if (0 == strncmp(name, "max_udp_payload_size_rx", 18)) if (0 == strncmp(name, "max_udp_payload_size_rx", 23))
{ {
settings->es_max_udp_payload_size_rx = atoi(val); settings->es_max_udp_payload_size_rx = atoi(val);
return 0; return 0;
@ -2008,7 +2023,9 @@ set_engine_option (struct lsquic_engine_settings *settings,
} }
#define MAX_PACKOUT_BUF_SZ 1370 /* So that largest allocation in PBA fits in 4KB */
#define PBA_SIZE_MAX 0x1000
#define PBA_SIZE_THRESH (PBA_SIZE_MAX - sizeof(uintptr_t))
struct packout_buf struct packout_buf
{ {
@ -2032,12 +2049,6 @@ pba_allocate (void *packout_buf_allocator, void *peer_ctx, unsigned short size,
struct packout_buf_allocator *const pba = packout_buf_allocator; struct packout_buf_allocator *const pba = packout_buf_allocator;
struct packout_buf *pb; struct packout_buf *pb;
if (size > MAX_PACKOUT_BUF_SZ)
{
fprintf(stderr, "packout buf size too large: %hu", size);
abort();
}
if (pba->max && pba->n_out >= pba->max) if (pba->max && pba->n_out >= pba->max)
{ {
LSQ_DEBUG("# outstanding packout bufs reached the limit of %u, " LSQ_DEBUG("# outstanding packout bufs reached the limit of %u, "
@ -2047,16 +2058,24 @@ pba_allocate (void *packout_buf_allocator, void *peer_ctx, unsigned short size,
#if LSQUIC_USE_POOLS #if LSQUIC_USE_POOLS
pb = SLIST_FIRST(&pba->free_packout_bufs); pb = SLIST_FIRST(&pba->free_packout_bufs);
if (pb) if (pb && size <= PBA_SIZE_THRESH)
SLIST_REMOVE_HEAD(&pba->free_packout_bufs, next_free_pb); SLIST_REMOVE_HEAD(&pba->free_packout_bufs, next_free_pb);
else if (size <= PBA_SIZE_THRESH)
pb = malloc(PBA_SIZE_MAX);
else else
pb = malloc(sizeof(uintptr_t) + size);
#else
pb = malloc(sizeof(uintptr_t) + size);
#endif #endif
pb = malloc(MAX_PACKOUT_BUF_SZ);
if (pb) if (pb)
{
* (uintptr_t *) pb = size;
++pba->n_out; ++pba->n_out;
return (uintptr_t *) pb + 1;
return pb; }
else
return NULL;
} }
@ -2064,12 +2083,16 @@ void
pba_release (void *packout_buf_allocator, void *peer_ctx, void *obj, char ipv6) pba_release (void *packout_buf_allocator, void *peer_ctx, void *obj, char ipv6)
{ {
struct packout_buf_allocator *const pba = packout_buf_allocator; struct packout_buf_allocator *const pba = packout_buf_allocator;
obj = (uintptr_t *) obj - 1;
#if LSQUIC_USE_POOLS #if LSQUIC_USE_POOLS
struct packout_buf *const pb = obj; if (* (uintptr_t *) obj <= PBA_SIZE_THRESH)
SLIST_INSERT_HEAD(&pba->free_packout_bufs, pb, next_free_pb); {
#else struct packout_buf *const pb = obj;
free(obj); SLIST_INSERT_HEAD(&pba->free_packout_bufs, pb, next_free_pb);
}
else
#endif #endif
free(obj);
--pba->n_out; --pba->n_out;
} }

View file

@ -739,6 +739,29 @@ settings structure:
Default value is :macro:`LSQUIC_DF_MAX_UDP_PAYLOAD_SIZE_RX` Default value is :macro:`LSQUIC_DF_MAX_UDP_PAYLOAD_SIZE_RX`
.. member:: int es_dplpmtud
If set to true value, enable DPLPMTUD -- Datagram Packetization
Layer Path MTU Discovery.
Default value is :macro:`LSQUIC_DF_DPLPMTUD`
.. member:: unsigned short es_base_plpmtu
PLPMTU size expected to work for most paths.
If set to zero, this value is calculated based on QUIC and IP versions.
Default value is :macro:`LSQUIC_DF_BASE_PLPMTU`
.. member:: unsigned short es_max_plpmtu
Largest PLPMTU size the engine will try.
If set to zero, picking this value is left to the engine.
Default value is :macro:`LSQUIC_DF_MAX_PLPMTU`
.. member:: unsigned es_noprogress_timeout .. member:: unsigned es_noprogress_timeout
No progress timeout. No progress timeout.
@ -938,6 +961,18 @@ out of date. Please check your :file:`lsquic.h` for actual values.*
By default, incoming packet size is not limited. By default, incoming packet size is not limited.
.. macro:: LSQUIC_DF_DPLPMTUD
By default, DPLPMTUD is enabled
.. macro:: LSQUIC_DF_BASE_PLPMTU
By default, this value is left up to the engine.
.. macro:: LSQUIC_DF_MAX_PLPMTU
By default, this value is left up to the engine.
.. macro:: LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER .. macro:: LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER
By default, drop no-progress connections after 60 seconds on the server. By default, drop no-progress connections after 60 seconds on the server.
@ -1170,7 +1205,7 @@ callback.
In client mode, a new connection is created by In client mode, a new connection is created by
.. function:: lsquic_conn_t * lsquic_engine_connect (lsquic_engine_t *engine, enum lsquic_version version, const struct sockaddr *local_sa, const struct sockaddr *peer_sa, void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, const char *sni, unsigned short max_udp_payload_size, const unsigned char *sess_resume, size_t sess_resume_len, const unsigned char *token, size_t token_sz) .. function:: lsquic_conn_t * lsquic_engine_connect (lsquic_engine_t *engine, enum lsquic_version version, const struct sockaddr *local_sa, const struct sockaddr *peer_sa, void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, const char *sni, unsigned short base_plpmtu, const unsigned char *sess_resume, size_t sess_resume_len, const unsigned char *token, size_t token_sz)
:param engine: Engine to use. :param engine: Engine to use.
@ -1203,10 +1238,12 @@ In client mode, a new connection is created by
The SNI is required for Google QUIC connections; it is optional for The SNI is required for Google QUIC connections; it is optional for
IETF QUIC and may be set to NULL. IETF QUIC and may be set to NULL.
:param max_udp_payload_size: :param base_plpmtu:
Maximum packet size. If set to zero, it is inferred based on `peer_sa` Base PLPMTU. If set to zero, it is selected based on the
and `version`. engine settings (see
:member:`lsquic_engine_settings.es_base_plpmtu`),
QUIC version, and IP version.
:param sess_resume: :param sess_resume:

View file

@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
author = u'LiteSpeed Technologies' author = u'LiteSpeed Technologies'
# The short X.Y version # The short X.Y version
version = u'2.18' version = u'2.19'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = u'2.18.2' release = u'2.19.0'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------

View file

@ -24,8 +24,8 @@ extern "C" {
#endif #endif
#define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 18 #define LSQUIC_MINOR_VERSION 19
#define LSQUIC_PATCH_VERSION 2 #define LSQUIC_PATCH_VERSION 0
/** /**
* Engine flags: * Engine flags:
@ -350,12 +350,24 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
*/ */
#define LSQUIC_DF_GREASE_QUIC_BIT 1 #define LSQUIC_DF_GREASE_QUIC_BIT 1
/** By default, DPLPMTUD is enabled */
#define LSQUIC_DF_DPLPMTUD 1
/** By default, this value is left up to the engine. */
#define LSQUIC_DF_BASE_PLPMTU 0
/** By default, this value is left up to the engine. */
#define LSQUIC_DF_MAX_PLPMTU 0
/** By default, drop no-progress connections after 60 seconds on the server */ /** By default, drop no-progress connections after 60 seconds on the server */
#define LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER 60 #define LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER 60
/** By default, do not use no-progress timeout on the client */ /** By default, do not use no-progress timeout on the client */
#define LSQUIC_DF_NOPROGRESS_TIMEOUT_CLIENT 0 #define LSQUIC_DF_NOPROGRESS_TIMEOUT_CLIENT 0
/** By default, we use the minimum timer of 1000 milliseconds */
#define LSQUIC_DF_MTU_PROBE_TIMER 1000
struct lsquic_engine_settings { struct lsquic_engine_settings {
/** /**
* This is a bit mask wherein each bit corresponds to a value in * This is a bit mask wherein each bit corresponds to a value in
@ -818,6 +830,49 @@ struct lsquic_engine_settings {
* Default value is @ref LSQUIC_DF_GREASE_QUIC_BIT * Default value is @ref LSQUIC_DF_GREASE_QUIC_BIT
*/ */
int es_grease_quic_bit; int es_grease_quic_bit;
/**
* If set to true value, enable DPLPMTUD -- Datagram Packetization
* Layer Path MTU Discovery.
*
* Default value is @ref LSQUIC_DF_DPLPMTUD
*/
int es_dplpmtud;
/**
* PLPMTU size expected to work for most paths.
*
* If set to zero, this value is calculated based on QUIC and IP versions.
*
* Default value is @ref LSQUIC_DF_BASE_PLPMTU.
*/
unsigned short es_base_plpmtu;
/**
* Largest PLPMTU size the engine will try.
*
* If set to zero, picking this value is left to the engine.
*
* Default value is @ref LSQUIC_DF_MAX_PLPMTU.
*/
unsigned short es_max_plpmtu;
/**
* This value specifies how long the DPLPMTUD probe timer is, in
* milliseconds. [draft-ietf-tsvwg-datagram-plpmtud-17] says:
*
" PROBE_TIMER: The PROBE_TIMER is configured to expire after a period
" longer than the maximum time to receive an acknowledgment to a
" probe packet. This value MUST NOT be smaller than 1 second, and
" SHOULD be larger than 15 seconds. Guidance on selection of the
" timer value are provided in section 3.1.1 of the UDP Usage
" Guidelines [RFC8085].
*
* If set to zero, the default is used.
*
* Default value is @ref LSQUIC_DF_MTU_PROBE_TIMER.
*/
unsigned es_mtu_probe_timer;
}; };
/* Initialize `settings' to default values */ /* Initialize `settings' to default values */
@ -1146,15 +1201,15 @@ lsquic_engine_new (unsigned lsquic_engine_flags,
* To let the engine specify QUIC version, use N_LSQVER. If session resumption * To let the engine specify QUIC version, use N_LSQVER. If session resumption
* information is supplied, version is picked from there instead. * information is supplied, version is picked from there instead.
* *
* If `max_udp_payload_size' is set to zero, it is inferred based on `peer_sa': * If `base_plpmtu' is set to zero, it is selected based on the
* 1350 for IPv6 and 1370 for IPv4. * engine settings, QUIC version, and IP version.
*/ */
lsquic_conn_t * lsquic_conn_t *
lsquic_engine_connect (lsquic_engine_t *, enum lsquic_version, lsquic_engine_connect (lsquic_engine_t *, enum lsquic_version,
const struct sockaddr *local_sa, const struct sockaddr *local_sa,
const struct sockaddr *peer_sa, const struct sockaddr *peer_sa,
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, void *peer_ctx, lsquic_conn_ctx_t *conn_ctx,
const char *hostname, unsigned short max_udp_payload_size, const char *hostname, unsigned short base_plpmtu,
const unsigned char *sess_resume, size_t sess_resume_len, const unsigned char *sess_resume, size_t sess_resume_len,
/** Resumption token: optional */ /** Resumption token: optional */
const unsigned char *token, size_t token_sz); const unsigned char *token, size_t token_sz);

View file

@ -48,6 +48,7 @@ SET(lsquic_STAT_SRCS
lsquic_packet_gquic.c lsquic_packet_gquic.c
lsquic_packet_in.c lsquic_packet_in.c
lsquic_packet_out.c lsquic_packet_out.c
lsquic_packet_resize.c
lsquic_packints.c lsquic_packints.c
lsquic_parse_Q046.c lsquic_parse_Q046.c
lsquic_parse_Q050.c lsquic_parse_Q050.c

View file

@ -47,6 +47,7 @@ const char *const lsquic_alid2str[] =
[AL_PATH_CHAL_1] = "PATH_CHAL_1", [AL_PATH_CHAL_1] = "PATH_CHAL_1",
[AL_SESS_TICKET] = "SESS_TICKET", [AL_SESS_TICKET] = "SESS_TICKET",
[AL_BLOCKED_KA] = "BLOCKED_KA", [AL_BLOCKED_KA] = "BLOCKED_KA",
[AL_MTU_PROBE] = "MTU_PROBE",
}; };

View file

@ -26,6 +26,7 @@ enum alarm_id {
AL_RETX_HSK = AL_RETX_INIT + PNS_HSK, AL_RETX_HSK = AL_RETX_INIT + PNS_HSK,
AL_RETX_APP = AL_RETX_INIT + PNS_APP, AL_RETX_APP = AL_RETX_INIT + PNS_APP,
AL_PING, AL_PING,
AL_MTU_PROBE,
AL_IDLE, AL_IDLE,
AL_ACK_APP, AL_ACK_APP,
AL_RET_CIDS, AL_RET_CIDS,
@ -57,6 +58,7 @@ enum alarm_id_bit {
ALBIT_PATH_CHAL_3 = 1 << AL_PATH_CHAL_3, ALBIT_PATH_CHAL_3 = 1 << AL_PATH_CHAL_3,
ALBIT_SESS_TICKET = 1 << AL_SESS_TICKET, ALBIT_SESS_TICKET = 1 << AL_SESS_TICKET,
ALBIT_BLOCKED_KA = 1 << AL_BLOCKED_KA, ALBIT_BLOCKED_KA = 1 << AL_BLOCKED_KA,
ALBIT_MTU_PROBE = 1 << AL_MTU_PROBE,
}; };

View file

@ -65,10 +65,10 @@ int
lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn, lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
struct lsquic_engine_public *enpub, lsquic_packet_in_t *packet_in) struct lsquic_engine_public *enpub, lsquic_packet_in_t *packet_in)
{ {
unsigned char *copy;
assert(!(packet_in->pi_flags & PI_OWN_DATA)); assert(!(packet_in->pi_flags & PI_OWN_DATA));
/* The size should be guarded in lsquic_engine_packet_in(): */ copy = lsquic_mm_get_packet_in_buf(&enpub->enp_mm, packet_in->pi_data_sz);
assert(packet_in->pi_data_sz <= GQUIC_MAX_PACKET_SZ);
unsigned char *const copy = lsquic_mm_get_packet_in_buf(&enpub->enp_mm, 1370);
if (!copy) if (!copy)
{ {
LSQ_WARN("cannot allocate memory to copy incoming packet data"); LSQ_WARN("cannot allocate memory to copy incoming packet data");

View file

@ -79,7 +79,7 @@ struct network_path
void *np_peer_ctx; void *np_peer_ctx;
lsquic_cid_t np_dcid; lsquic_cid_t np_dcid;
unsigned short np_pack_size; unsigned short np_pack_size;
unsigned char np_path_id; /* Used for logging */ unsigned char np_path_id;
}; };
#define NP_LOCAL_SA(path_) (&(path_)->np_local_addr_u.sockaddr) #define NP_LOCAL_SA(path_) (&(path_)->np_local_addr_u.sockaddr)
@ -111,6 +111,9 @@ struct conn_iface
void void
(*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *); (*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
void
(*ci_packet_too_large) (struct lsquic_conn *, struct lsquic_packet_out *);
void void
(*ci_hsk_done) (struct lsquic_conn *, enum lsquic_hsk_status); (*ci_hsk_done) (struct lsquic_conn *, enum lsquic_hsk_status);
@ -247,6 +250,15 @@ struct conn_iface
/* Optional method. Only used by IETF connections */ /* Optional method. Only used by IETF connections */
void void
(*ci_count_garbage) (struct lsquic_conn *, size_t); (*ci_count_garbage) (struct lsquic_conn *, size_t);
/* Optional method. Must be implemented if connection sends MTU probes */
void
(*ci_mtu_probe_acked) (struct lsquic_conn *,
const struct lsquic_packet_out *);
/* Optional method. It is called when RTO occurs. */
void
(*ci_retx_timeout) (struct lsquic_conn *);
}; };
#define LSCONN_CCE_BITS 3 #define LSCONN_CCE_BITS 3

View file

@ -357,6 +357,8 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
settings->es_delayed_acks = LSQUIC_DF_DELAYED_ACKS; settings->es_delayed_acks = LSQUIC_DF_DELAYED_ACKS;
settings->es_timestamps = LSQUIC_DF_TIMESTAMPS; settings->es_timestamps = LSQUIC_DF_TIMESTAMPS;
settings->es_grease_quic_bit = LSQUIC_DF_GREASE_QUIC_BIT; settings->es_grease_quic_bit = LSQUIC_DF_GREASE_QUIC_BIT;
settings->es_mtu_probe_timer = LSQUIC_DF_MTU_PROBE_TIMER;
settings->es_dplpmtud = LSQUIC_DF_DPLPMTUD;
} }
@ -440,6 +442,14 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
return -1; return -1;
} }
if (settings->es_mtu_probe_timer && settings->es_mtu_probe_timer < 1000)
{
if (err_buf)
snprintf(err_buf, err_buf_sz, "mtu probe timer is too small: "
"%u ms", settings->es_mtu_probe_timer);
return -1;
}
return 0; return 0;
} }
@ -612,6 +622,10 @@ lsquic_engine_new (unsigned flags,
if (engine->pub.enp_settings.es_noprogress_timeout) if (engine->pub.enp_settings.es_noprogress_timeout)
engine->pub.enp_noprog_timeout engine->pub.enp_noprog_timeout
= engine->pub.enp_settings.es_noprogress_timeout * 1000000; = engine->pub.enp_settings.es_noprogress_timeout * 1000000;
engine->pub.enp_mtu_probe_timer = 1000
* (engine->pub.enp_settings.es_mtu_probe_timer
? engine->pub.enp_settings.es_mtu_probe_timer
: LSQUIC_DF_MTU_PROBE_TIMER);
if (flags & ENG_SERVER) if (flags & ENG_SERVER)
{ {
engine->pr_queue = lsquic_prq_create( engine->pr_queue = lsquic_prq_create(
@ -1580,7 +1594,7 @@ lsquic_engine_connect (lsquic_engine_t *engine, enum lsquic_version version,
const struct sockaddr *local_sa, const struct sockaddr *local_sa,
const struct sockaddr *peer_sa, const struct sockaddr *peer_sa,
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, void *peer_ctx, lsquic_conn_ctx_t *conn_ctx,
const char *hostname, unsigned short max_packet_size, const char *hostname, unsigned short base_plpmtu,
const unsigned char *sess_resume, size_t sess_resume_len, const unsigned char *sess_resume, size_t sess_resume_len,
const unsigned char *token, size_t token_sz) const unsigned char *token, size_t token_sz)
{ {
@ -1627,11 +1641,11 @@ lsquic_engine_connect (lsquic_engine_t *engine, enum lsquic_version version,
versions = 1u << version; versions = 1u << version;
if (versions & LSQUIC_IETF_VERSIONS) if (versions & LSQUIC_IETF_VERSIONS)
conn = lsquic_ietf_full_conn_client_new(&engine->pub, versions, conn = lsquic_ietf_full_conn_client_new(&engine->pub, versions,
flags, hostname, max_packet_size, flags, hostname, base_plpmtu,
is_ipv4, sess_resume, sess_resume_len, token, token_sz); is_ipv4, sess_resume, sess_resume_len, token, token_sz);
else else
conn = lsquic_gquic_full_conn_client_new(&engine->pub, versions, conn = lsquic_gquic_full_conn_client_new(&engine->pub, versions,
flags, hostname, max_packet_size, is_ipv4, flags, hostname, base_plpmtu, is_ipv4,
sess_resume, sess_resume_len); sess_resume, sess_resume_len);
if (!conn) if (!conn)
goto err; goto err;
@ -2181,7 +2195,7 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx,
{ {
int n_sent, i, e_val; int n_sent, i, e_val;
lsquic_time_t now; lsquic_time_t now;
unsigned off; unsigned off, skip;
size_t count; size_t count;
CONST_BATCH struct out_batch *const batch = sb_ctx->batch; CONST_BATCH struct out_batch *const batch = sb_ctx->batch;
struct lsquic_packet_out *CONST_BATCH *packet_out, *CONST_BATCH *end; struct lsquic_packet_out *CONST_BATCH *packet_out, *CONST_BATCH *end;
@ -2191,9 +2205,11 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx,
if (engine->flags & ENG_LOSE_PACKETS) if (engine->flags & ENG_LOSE_PACKETS)
lose_matching_packets(engine, batch, n_to_send); lose_matching_packets(engine, batch, n_to_send);
#endif #endif
skip = 0;
restart_batch:
/* Set sent time before the write to avoid underestimating RTT */ /* Set sent time before the write to avoid underestimating RTT */
now = lsquic_time_now(); now = lsquic_time_now();
for (i = 0; i < (int) n_to_send; ++i) for (i = skip; i < (int) (n_to_send - skip); ++i)
{ {
off = batch->pack_off[i]; off = batch->pack_off[i];
count = batch->outs[i].iovlen; count = batch->outs[i].iovlen;
@ -2204,10 +2220,10 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx,
(*packet_out)->po_sent = now; (*packet_out)->po_sent = now;
while (++packet_out < end); while (++packet_out < end);
} }
n_sent = engine->packets_out(engine->packets_out_ctx, batch->outs, n_sent = engine->packets_out(engine->packets_out_ctx, batch->outs + skip,
n_to_send); n_to_send - skip);
e_val = errno; e_val = errno;
if (n_sent < (int) n_to_send) if (n_sent < (int) (n_to_send - skip) && e_val != EMSGSIZE)
{ {
engine->pub.enp_flags &= ~ENPUB_CAN_SEND; engine->pub.enp_flags &= ~ENPUB_CAN_SEND;
engine->resume_sending_at = now + 1000000; engine->resume_sending_at = now + 1000000;
@ -2218,7 +2234,8 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx,
n_sent < 0 ? 0 : n_sent, e_val); n_sent < 0 ? 0 : n_sent, e_val);
} }
if (n_sent >= 0) if (n_sent >= 0)
LSQ_DEBUG("packets out returned %d (out of %u)", n_sent, n_to_send); LSQ_DEBUG("packets out returned %d (out of %u)", n_sent,
n_to_send - skip);
else else
{ {
LSQ_DEBUG("packets out returned an error: %s", strerror(e_val)); LSQ_DEBUG("packets out returned an error: %s", strerror(e_val));
@ -2226,7 +2243,7 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx,
} }
if (n_sent > 0) if (n_sent > 0)
engine->last_sent = now + n_sent; engine->last_sent = now + n_sent;
for (i = 0; i < n_sent; ++i) for (i = skip; i < (int) (skip + n_sent); ++i)
{ {
eng_hist_inc(&engine->history, now, sl_packets_out); eng_hist_inc(&engine->history, now, sl_packets_out);
/* `i' is added to maintain relative order */ /* `i' is added to maintain relative order */
@ -2258,6 +2275,27 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx,
} }
while (++packet_out < end); while (++packet_out < end);
} }
if (i < (int) n_to_send && e_val == EMSGSIZE)
{
LSQ_DEBUG("packet #%d could not be sent out for being too large", i);
if (batch->conns[i]->cn_if->ci_packet_too_large
&& batch->outs[i].iovlen == 1)
{
off = batch->pack_off[i];
packet_out = &batch->packets[off];
batch->conns[i]->cn_if->ci_packet_too_large(batch->conns[i],
*packet_out);
++i;
if (i < (int) n_to_send)
{
skip = i;
LSQ_DEBUG("restart batch starting at packet #%u", skip);
goto restart_batch;
}
}
else
close_conn_on_send_error(engine, sb_ctx, i, e_val);
}
if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) if (LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT))
for ( ; i < (int) n_to_send; ++i) for ( ; i < (int) n_to_send; ++i)
{ {
@ -2274,7 +2312,7 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx,
/* Return packets to the connection in reverse order so that the packet /* Return packets to the connection in reverse order so that the packet
* ordering is maintained. * ordering is maintained.
*/ */
for (i = (int) n_to_send - 1; i >= n_sent; --i) for (i = (int) n_to_send - 1; i >= (int) (skip + n_sent); --i)
{ {
off = batch->pack_off[i]; off = batch->pack_off[i];
count = batch->outs[i].iovlen; count = batch->outs[i].iovlen;
@ -2288,7 +2326,7 @@ send_batch (lsquic_engine_t *engine, const struct send_batch_ctx *sb_ctx,
if (!(batch->conns[i]->cn_flags & (LSCONN_COI_ACTIVE|LSCONN_EVANESCENT))) if (!(batch->conns[i]->cn_flags & (LSCONN_COI_ACTIVE|LSCONN_EVANESCENT)))
coi_reactivate(sb_ctx->conns_iter, batch->conns[i]); coi_reactivate(sb_ctx->conns_iter, batch->conns[i]);
} }
return n_sent; return skip + n_sent;
} }

View file

@ -68,6 +68,7 @@ struct lsquic_engine_public {
unsigned char *enp_alpn; /* May be set if not HTTP */ unsigned char *enp_alpn; /* May be set if not HTTP */
/* es_noprogress_timeout converted to microseconds for speed */ /* es_noprogress_timeout converted to microseconds for speed */
lsquic_time_t enp_noprog_timeout; lsquic_time_t enp_noprog_timeout;
lsquic_time_t enp_mtu_probe_timer;
}; };
/* Put connection onto the Tickable Queue if it is not already on it. If /* Put connection onto the Tickable Queue if it is not already on it. If

View file

@ -737,7 +737,11 @@ lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub,
lsquic_generate_cid_gquic(&cid); lsquic_generate_cid_gquic(&cid);
if (!max_packet_size) if (!max_packet_size)
{ {
if (is_ipv4) if (enpub->enp_settings.es_base_plpmtu)
max_packet_size = enpub->enp_settings.es_base_plpmtu
- (is_ipv4 ? 20 : 40) /* IP header */
- 8; /* UDP header */
else if (is_ipv4)
max_packet_size = GQUIC_MAX_IPv4_PACKET_SZ; max_packet_size = GQUIC_MAX_IPv4_PACKET_SZ;
else else
max_packet_size = GQUIC_MAX_IPv6_PACKET_SZ; max_packet_size = GQUIC_MAX_IPv6_PACKET_SZ;
@ -1269,6 +1273,12 @@ full_conn_ci_write_ack (struct lsquic_conn *lconn,
lsquic_send_ctl_scheduled_ack(&conn->fc_send_ctl, PNS_APP, lsquic_send_ctl_scheduled_ack(&conn->fc_send_ctl, PNS_APP,
packet_out->po_ack2ed); packet_out->po_ack2ed);
packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK; packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK;
if (0 != lsquic_packet_out_add_frame(packet_out, conn->fc_pub.mm, 0,
QUIC_FRAME_ACK, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, w);
packet_out->po_regen_sz += w; packet_out->po_regen_sz += w;
if (has_missing) if (has_missing)
@ -2529,7 +2539,6 @@ get_writeable_packet (struct full_conn *conn, unsigned need_at_least)
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
int is_err; int is_err;
assert(need_at_least <= GQUIC_MAX_PAYLOAD_SZ);
packet_out = lsquic_send_ctl_get_writeable_packet(&conn->fc_send_ctl, packet_out = lsquic_send_ctl_get_writeable_packet(&conn->fc_send_ctl,
PNS_APP, need_at_least, &conn->fc_path, 0, &is_err); PNS_APP, need_at_least, &conn->fc_path, 0, &is_err);
if (!packet_out && is_err) if (!packet_out && is_err)
@ -2709,7 +2718,7 @@ generate_rst_stream_frame (struct full_conn *conn, lsquic_stream_t *stream)
return 0; return 0;
/* TODO Possible optimization: instead of using stream->tosend_off as the /* TODO Possible optimization: instead of using stream->tosend_off as the
* offset, keep track of the offset that was actually sent: include it * offset, keep track of the offset that was actually sent: include it
* into stream_rec and update a new per-stream "maximum offset actually * into frame_rec and update a new per-stream "maximum offset actually
* sent" field. Then, if a stream is reset, the connection cap can be * sent" field. Then, if a stream is reset, the connection cap can be
* increased. * increased.
*/ */
@ -2802,6 +2811,12 @@ generate_stop_waiting_frame (struct full_conn *conn)
ABORT_ERROR("gen_stop_waiting_frame failed"); ABORT_ERROR("gen_stop_waiting_frame failed");
return; return;
} }
if (0 != lsquic_packet_out_add_frame(packet_out, conn->fc_pub.mm, 0,
QUIC_FRAME_STOP_WAITING, packet_out->po_data_sz, sz))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz); lsquic_send_ctl_incr_pack_sz(&conn->fc_send_ctl, packet_out, sz);
packet_out->po_regen_sz += sz; packet_out->po_regen_sz += sz;
packet_out->po_frame_types |= 1 << QUIC_FRAME_STOP_WAITING; packet_out->po_frame_types |= 1 << QUIC_FRAME_STOP_WAITING;

View file

@ -17,7 +17,7 @@ struct lsquic_conn *
lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *, lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *,
unsigned versions, unsigned versions,
unsigned flags /* Only FC_SERVER and FC_HTTP */, unsigned flags /* Only FC_SERVER and FC_HTTP */,
const char *hostname, unsigned short max_packet_size, int is_ipv4, const char *hostname, unsigned short base_plpmtu, int is_ipv4,
const unsigned char *sess_resume, size_t, const unsigned char *sess_resume, size_t,
const unsigned char *token, size_t); const unsigned char *token, size_t);

View file

@ -142,6 +142,7 @@ enum more_flags
{ {
MF_VALIDATE_PATH = 1 << 0, MF_VALIDATE_PATH = 1 << 0,
MF_NOPROG_TIMEOUT = 1 << 1, MF_NOPROG_TIMEOUT = 1 << 1,
MF_CHECK_MTU_PROBE = 1 << 2,
}; };
@ -281,6 +282,21 @@ struct conn_err
}; };
struct dplpmtud_state
{
lsquic_packno_t ds_probe_packno;
#ifndef NDEBUG
lsquic_time_t ds_probe_sent;
#endif
enum {
DS_PROBE_SENT = 1 << 0,
} ds_flags;
unsigned short ds_probed_size,
ds_failed_size; /* If non-zero, defines ceiling */
unsigned char ds_probe_count;
};
struct conn_path struct conn_path
{ {
struct network_path cop_path; struct network_path cop_path;
@ -298,8 +314,10 @@ struct conn_path
*/ */
COP_GOT_NONPROB = 1 << 2, COP_GOT_NONPROB = 1 << 2,
} cop_flags; } cop_flags;
unsigned short cop_max_plpmtu;
unsigned char cop_n_chals; unsigned char cop_n_chals;
unsigned char cop_cce_idx; unsigned char cop_cce_idx;
struct dplpmtud_state cop_dplpmtud;
}; };
@ -402,6 +420,7 @@ struct ietf_full_conn
unsigned ifc_last_pack_tol; unsigned ifc_last_pack_tol;
unsigned ifc_max_ack_freq_seqno; /* Incoming */ unsigned ifc_max_ack_freq_seqno; /* Incoming */
unsigned ifc_max_peer_ack_usec; unsigned ifc_max_peer_ack_usec;
unsigned short ifc_max_udp_payload; /* Cached TP */
lsquic_time_t ifc_last_live_update; lsquic_time_t ifc_last_live_update;
struct conn_path ifc_paths[N_PATHS]; struct conn_path ifc_paths[N_PATHS];
union { union {
@ -481,6 +500,9 @@ insert_new_dcid (struct ietf_full_conn *, uint64_t seqno,
static struct conn_cid_elem * static struct conn_cid_elem *
find_cce_by_cid (struct ietf_full_conn *, const lsquic_cid_t *); find_cce_by_cid (struct ietf_full_conn *, const lsquic_cid_t *);
static void
mtu_probe_too_large (struct ietf_full_conn *, const struct lsquic_packet_out *);
static unsigned static unsigned
highest_bit_set (unsigned sz) highest_bit_set (unsigned sz)
{ {
@ -655,6 +677,18 @@ blocked_ka_alarm_expired (enum alarm_id al_id, void *ctx,
} }
static void
mtu_probe_alarm_expired (enum alarm_id al_id, void *ctx,
lsquic_time_t expiry, lsquic_time_t now)
{
struct ietf_full_conn *const conn = (struct ietf_full_conn *) ctx;
LSQ_DEBUG("MTU probe alarm expired: set `check MTU probe' flag");
assert(!(conn->ifc_mflags & MF_CHECK_MTU_PROBE));
conn->ifc_mflags |= MF_CHECK_MTU_PROBE;
}
static int static int
migra_is_on (const struct ietf_full_conn *conn, unsigned path_id) migra_is_on (const struct ietf_full_conn *conn, unsigned path_id)
{ {
@ -663,6 +697,24 @@ migra_is_on (const struct ietf_full_conn *conn, unsigned path_id)
} }
#define TRANSPORT_OVERHEAD(is_ipv6) (((is_ipv6) ? 40 : 20) + 8 /* UDP */)
static unsigned short
calc_base_packet_size (const struct ietf_full_conn *conn, int is_ipv6)
{
unsigned short size;
if (conn->ifc_settings->es_base_plpmtu)
size = conn->ifc_settings->es_base_plpmtu - TRANSPORT_OVERHEAD(is_ipv6);
else if (is_ipv6)
size = IQUIC_MAX_IPv6_PACKET_SZ;
else
size = IQUIC_MAX_IPv4_PACKET_SZ;
return size;
}
static void static void
migra_begin (struct ietf_full_conn *conn, struct conn_path *copath, migra_begin (struct ietf_full_conn *conn, struct conn_path *copath,
struct dcid_elem *dce, const struct sockaddr *dest_sa, struct dcid_elem *dce, const struct sockaddr *dest_sa,
@ -674,15 +726,10 @@ migra_begin (struct ietf_full_conn *conn, struct conn_path *copath,
copath->cop_flags |= COP_INITIALIZED; copath->cop_flags |= COP_INITIALIZED;
copath->cop_path.np_dcid = dce->de_cid; copath->cop_path.np_dcid = dce->de_cid;
copath->cop_path.np_peer_ctx = CUR_NPATH(conn)->np_peer_ctx; copath->cop_path.np_peer_ctx = CUR_NPATH(conn)->np_peer_ctx;
if (NP_IS_IPv6(CUR_NPATH(conn))) copath->cop_path.np_pack_size
copath->cop_path.np_pack_size = IQUIC_MAX_IPv6_PACKET_SZ; = calc_base_packet_size(conn, NP_IS_IPv6(CUR_NPATH(conn)));
else if (conn->ifc_max_udp_payload < copath->cop_path.np_pack_size)
copath->cop_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ; copath->cop_path.np_pack_size = conn->ifc_max_udp_payload;
if ((params->tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE))
&& params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE]
< copath->cop_path.np_pack_size)
copath->cop_path.np_pack_size
= params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE];
memcpy(&copath->cop_path.np_local_addr, NP_LOCAL_SA(CUR_NPATH(conn)), memcpy(&copath->cop_path.np_local_addr, NP_LOCAL_SA(CUR_NPATH(conn)),
sizeof(copath->cop_path.np_local_addr)); sizeof(copath->cop_path.np_local_addr));
memcpy(&copath->cop_path.np_peer_addr, dest_sa, memcpy(&copath->cop_path.np_peer_addr, dest_sa,
@ -1117,6 +1164,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_PATH_CHAL_2, path_chal_alarm_expired, conn); lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_PATH_CHAL_2, path_chal_alarm_expired, conn);
lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_PATH_CHAL_3, path_chal_alarm_expired, conn); lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_PATH_CHAL_3, path_chal_alarm_expired, conn);
lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_BLOCKED_KA, blocked_ka_alarm_expired, conn); lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_BLOCKED_KA, blocked_ka_alarm_expired, conn);
lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_MTU_PROBE, mtu_probe_alarm_expired, conn);
lsquic_rechist_init(&conn->ifc_rechist[PNS_INIT], &conn->ifc_conn, 1); lsquic_rechist_init(&conn->ifc_rechist[PNS_INIT], &conn->ifc_conn, 1);
lsquic_rechist_init(&conn->ifc_rechist[PNS_HSK], &conn->ifc_conn, 1); lsquic_rechist_init(&conn->ifc_rechist[PNS_HSK], &conn->ifc_conn, 1);
lsquic_rechist_init(&conn->ifc_rechist[PNS_APP], &conn->ifc_conn, 1); lsquic_rechist_init(&conn->ifc_rechist[PNS_APP], &conn->ifc_conn, 1);
@ -1157,7 +1205,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
struct lsquic_conn * struct lsquic_conn *
lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub, lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
unsigned versions, unsigned flags, unsigned versions, unsigned flags,
const char *hostname, unsigned short max_packet_size, int is_ipv4, const char *hostname, unsigned short base_plpmtu, int is_ipv4,
const unsigned char *sess_resume, size_t sess_resume_sz, const unsigned char *sess_resume, size_t sess_resume_sz,
const unsigned char *token, size_t token_sz) const unsigned char *token, size_t token_sz)
{ {
@ -1189,14 +1237,12 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
} }
esfi = select_esf_iquic_by_ver(ver); esfi = select_esf_iquic_by_ver(ver);
if (!max_packet_size) if (base_plpmtu)
{ conn->ifc_paths[0].cop_path.np_pack_size
if (is_ipv4) = base_plpmtu - TRANSPORT_OVERHEAD(!is_ipv4);
max_packet_size = IQUIC_MAX_IPv4_PACKET_SZ; else
else conn->ifc_paths[0].cop_path.np_pack_size
max_packet_size = IQUIC_MAX_IPv6_PACKET_SZ; = calc_base_packet_size(conn, !is_ipv4);
}
conn->ifc_paths[0].cop_path.np_pack_size = max_packet_size;
if (0 != ietf_full_conn_init(conn, enpub, flags, if (0 != ietf_full_conn_init(conn, enpub, flags,
enpub->enp_settings.es_ecn)) enpub->enp_settings.es_ecn))
@ -1368,15 +1414,6 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
LSQ_DEBUG("path changed during mini conn: schedule PATH_CHALLENGE"); LSQ_DEBUG("path changed during mini conn: schedule PATH_CHALLENGE");
conn->ifc_send_flags |= SF_SEND_PATH_CHAL_PATH_0; conn->ifc_send_flags |= SF_SEND_PATH_CHAL_PATH_0;
} }
#ifndef NDEBUG
if (getenv("LSQUIC_CN_PACK_SIZE"))
{
conn->ifc_paths[0].cop_path.np_pack_size
= atoi(getenv("LSQUIC_CN_PACK_SIZE"));
LSQ_INFO("set packet size to %hu (env)",
conn->ifc_paths[0].cop_path.np_pack_size);
}
#endif
conn->ifc_max_streams_in[SD_BIDI] conn->ifc_max_streams_in[SD_BIDI]
= enpub->enp_settings.es_init_max_streams_bidi; = enpub->enp_settings.es_init_max_streams_bidi;
@ -1607,6 +1644,12 @@ generate_timestamp_frame (struct ietf_full_conn *conn,
timestamp << TP_DEF_ACK_DELAY_EXP); timestamp << TP_DEF_ACK_DELAY_EXP);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated TIMESTAMP(%" EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated TIMESTAMP(%"
PRIu64" us) frame", timestamp << TP_DEF_ACK_DELAY_EXP); PRIu64" us) frame", timestamp << TP_DEF_ACK_DELAY_EXP);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_TIMESTAMP, packet_out->po_data_sz, w))
{
LSQ_DEBUG("%s: adding frame to packet failed: %d", __func__, errno);
return;
}
packet_out->po_frame_types |= 1 << QUIC_FRAME_TIMESTAMP; packet_out->po_frame_types |= 1 << QUIC_FRAME_TIMESTAMP;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
packet_out->po_regen_sz += w; packet_out->po_regen_sz += w;
@ -1640,6 +1683,12 @@ generate_ack_frame_for_pns (struct ietf_full_conn *conn,
lsquic_send_ctl_scheduled_ack(&conn->ifc_send_ctl, pns, lsquic_send_ctl_scheduled_ack(&conn->ifc_send_ctl, pns,
packet_out->po_ack2ed); packet_out->po_ack2ed);
packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK; packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK;
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_ACK, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return -1;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
packet_out->po_regen_sz += w; packet_out->po_regen_sz += w;
if (has_missing) if (has_missing)
@ -1753,6 +1802,12 @@ generate_max_data_frame (struct ietf_full_conn *conn)
LSQ_DEBUG("generated %d-byte MAX_DATA frame (offset: %"PRIu64")", w, offset); LSQ_DEBUG("generated %d-byte MAX_DATA frame (offset: %"PRIu64")", w, offset);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated MAX_DATA frame, offset=%" EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated MAX_DATA frame, offset=%"
PRIu64, offset); PRIu64, offset);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_MAX_DATA, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
packet_out->po_frame_types |= QUIC_FTBIT_MAX_DATA; packet_out->po_frame_types |= QUIC_FTBIT_MAX_DATA;
conn->ifc_send_flags &= ~SF_SEND_MAX_DATA; conn->ifc_send_flags &= ~SF_SEND_MAX_DATA;
@ -1822,6 +1877,12 @@ generate_new_cid_frame (struct ietf_full_conn *conn, lsquic_time_t now)
w, CID_BITS(&cce->cce_cid)); w, CID_BITS(&cce->cce_cid));
EV_LOG_GENERATED_NEW_CONNECTION_ID_FRAME(LSQUIC_LOG_CONN_ID, EV_LOG_GENERATED_NEW_CONNECTION_ID_FRAME(LSQUIC_LOG_CONN_ID,
conn->ifc_conn.cn_pf, packet_out->po_data + packet_out->po_data_sz, w); conn->ifc_conn.cn_pf, packet_out->po_data + packet_out->po_data_sz, w);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_NEW_CONNECTION_ID, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return -1;
}
packet_out->po_frame_types |= QUIC_FTBIT_NEW_CONNECTION_ID; packet_out->po_frame_types |= QUIC_FTBIT_NEW_CONNECTION_ID;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
@ -1929,6 +1990,12 @@ generate_retire_cid_frame (struct ietf_full_conn *conn)
w, dce->de_seqno); w, dce->de_seqno);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated RETIRE_CONNECTION_ID " EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated RETIRE_CONNECTION_ID "
"frame, seqno=%u", dce->de_seqno); "frame, seqno=%u", dce->de_seqno);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_RETIRE_CONNECTION_ID, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return -1;
}
packet_out->po_frame_types |= QUIC_FTBIT_RETIRE_CONNECTION_ID; packet_out->po_frame_types |= QUIC_FTBIT_RETIRE_CONNECTION_ID;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
@ -1979,6 +2046,12 @@ generate_streams_blocked_frame (struct ietf_full_conn *conn, enum stream_dir sd)
"limit: %"PRIu64")", w, sd == SD_UNI, limit); "limit: %"PRIu64")", w, sd == SD_UNI, limit);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte STREAMS_BLOCKED " EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte STREAMS_BLOCKED "
"frame (uni: %d, limit: %"PRIu64")", w, sd == SD_UNI, limit); "frame (uni: %d, limit: %"PRIu64")", w, sd == SD_UNI, limit);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_STREAMS_BLOCKED, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
packet_out->po_frame_types |= QUIC_FTBIT_STREAM_BLOCKED; packet_out->po_frame_types |= QUIC_FTBIT_STREAM_BLOCKED;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
conn->ifc_send_flags &= ~(SF_SEND_STREAMS_BLOCKED << sd); conn->ifc_send_flags &= ~(SF_SEND_STREAMS_BLOCKED << sd);
@ -2028,6 +2101,12 @@ generate_max_streams_frame (struct ietf_full_conn *conn, enum stream_dir sd)
"limit: %"PRIu64")", w, sd == SD_UNI, limit); "limit: %"PRIu64")", w, sd == SD_UNI, limit);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte MAX_STREAMS " EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte MAX_STREAMS "
"frame (uni: %d, limit: %"PRIu64")", w, sd == SD_UNI, limit); "frame (uni: %d, limit: %"PRIu64")", w, sd == SD_UNI, limit);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_MAX_STREAMS, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
packet_out->po_frame_types |= QUIC_FTBIT_MAX_STREAMS; packet_out->po_frame_types |= QUIC_FTBIT_MAX_STREAMS;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
conn->ifc_send_flags &= ~(SF_SEND_MAX_STREAMS << sd); conn->ifc_send_flags &= ~(SF_SEND_MAX_STREAMS << sd);
@ -2078,6 +2157,12 @@ generate_blocked_frame (struct ietf_full_conn *conn)
LSQ_DEBUG("generated %d-byte BLOCKED frame (offset: %"PRIu64")", w, offset); LSQ_DEBUG("generated %d-byte BLOCKED frame (offset: %"PRIu64")", w, offset);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated BLOCKED frame, offset=%" EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated BLOCKED frame, offset=%"
PRIu64, offset); PRIu64, offset);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_BLOCKED, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return 0;
}
packet_out->po_frame_types |= QUIC_FTBIT_BLOCKED; packet_out->po_frame_types |= QUIC_FTBIT_BLOCKED;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
@ -2110,6 +2195,12 @@ generate_max_stream_data_frame (struct ietf_full_conn *conn,
} }
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte MAX_STREAM_DATA " EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte MAX_STREAM_DATA "
"frame; stream_id: %"PRIu64"; offset: %"PRIu64, sz, stream->id, off); "frame; stream_id: %"PRIu64"; offset: %"PRIu64, sz, stream->id, off);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_MAX_STREAM_DATA, packet_out->po_data_sz, sz))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return 0;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_MAX_STREAM_DATA; packet_out->po_frame_types |= 1 << QUIC_FRAME_MAX_STREAM_DATA;
lsquic_stream_max_stream_data_sent(stream); lsquic_stream_max_stream_data_sent(stream);
@ -2142,6 +2233,12 @@ generate_stream_blocked_frame (struct ietf_full_conn *conn,
} }
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte STREAM_BLOCKED " EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte STREAM_BLOCKED "
"frame; stream_id: %"PRIu64"; offset: %"PRIu64, sz, stream->id, off); "frame; stream_id: %"PRIu64"; offset: %"PRIu64, sz, stream->id, off);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_STREAM_BLOCKED, packet_out->po_data_sz, sz))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return 0;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM_BLOCKED; packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM_BLOCKED;
lsquic_stream_blocked_frame_sent(stream); lsquic_stream_blocked_frame_sent(stream);
@ -2176,6 +2273,12 @@ generate_stop_sending_frame (struct ietf_full_conn *conn,
"error code: %u)", w, stream_id, error_code); "error code: %u)", w, stream_id, error_code);
EV_LOG_GENERATED_STOP_SENDING_FRAME(LSQUIC_LOG_CONN_ID, stream_id, EV_LOG_GENERATED_STOP_SENDING_FRAME(LSQUIC_LOG_CONN_ID, stream_id,
error_code); error_code);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_STOP_SENDING, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return -1;
}
packet_out->po_frame_types |= QUIC_FTBIT_STOP_SENDING; packet_out->po_frame_types |= QUIC_FTBIT_STOP_SENDING;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
@ -2215,7 +2318,7 @@ generate_rst_stream_frame (struct ietf_full_conn *conn,
{ {
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
unsigned need; unsigned need;
int sz, s; int sz;
need = conn->ifc_conn.cn_pf->pf_rst_frame_size(stream->id, need = conn->ifc_conn.cn_pf->pf_rst_frame_size(stream->id,
stream->tosend_off, stream->error_code); stream->tosend_off, stream->error_code);
@ -2234,15 +2337,14 @@ generate_rst_stream_frame (struct ietf_full_conn *conn,
ABORT_ERROR("gen_rst_frame failed"); ABORT_ERROR("gen_rst_frame failed");
return 0; return 0;
} }
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz); if (0 != lsquic_packet_out_add_stream(packet_out, conn->ifc_pub.mm, stream,
packet_out->po_frame_types |= 1 << QUIC_FRAME_RST_STREAM; QUIC_FRAME_RST_STREAM, packet_out->po_data_sz, sz))
s = lsquic_packet_out_add_stream(packet_out, conn->ifc_pub.mm, stream,
QUIC_FRAME_RST_STREAM, packet_out->po_data_sz, sz);
if (s != 0)
{ {
ABORT_ERROR("adding stream to packet failed: %s", strerror(errno)); ABORT_ERROR("adding frame to packet failed: %d", errno);
return 0; return 0;
} }
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_RST_STREAM;
lsquic_stream_rst_frame_sent(stream); lsquic_stream_rst_frame_sent(stream);
LSQ_DEBUG("wrote RST: stream %"PRIu64"; offset 0x%"PRIX64"; error code " LSQ_DEBUG("wrote RST: stream %"PRIu64"; offset 0x%"PRIX64"; error code "
"%"PRIu64, stream->id, stream->tosend_off, stream->error_code); "%"PRIu64, stream->id, stream->tosend_off, stream->error_code);
@ -3161,11 +3263,18 @@ handshake_ok (struct lsquic_conn *lconn)
conn->ifc_max_peer_ack_usec = params->tp_max_ack_delay * 1000; conn->ifc_max_peer_ack_usec = params->tp_max_ack_delay * 1000;
if ((params->tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE)) if ((params->tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE))
/* Second check is so that we don't truncate a large value when
* storing it in unsigned short.
*/
&& params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE] && params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE]
< CUR_NPATH(conn)->np_pack_size) < TP_DEF_MAX_UDP_PAYLOAD_SIZE)
conn->ifc_max_udp_payload = params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE];
else
conn->ifc_max_udp_payload = TP_DEF_MAX_UDP_PAYLOAD_SIZE;
if (conn->ifc_max_udp_payload < CUR_NPATH(conn)->np_pack_size)
{ {
CUR_NPATH(conn)->np_pack_size CUR_NPATH(conn)->np_pack_size = conn->ifc_max_udp_payload;
= params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE];
LSQ_DEBUG("decrease packet size to %hu bytes", LSQ_DEBUG("decrease packet size to %hu bytes",
CUR_NPATH(conn)->np_pack_size); CUR_NPATH(conn)->np_pack_size);
} }
@ -3261,6 +3370,9 @@ handshake_ok (struct lsquic_conn *lconn)
conn->ifc_active_cids_limit = params->tp_active_connection_id_limit; conn->ifc_active_cids_limit = params->tp_active_connection_id_limit;
conn->ifc_first_active_cid_seqno = conn->ifc_scid_seqno; conn->ifc_first_active_cid_seqno = conn->ifc_scid_seqno;
if (conn->ifc_settings->es_dplpmtud)
conn->ifc_mflags |= MF_CHECK_MTU_PROBE;
if (can_issue_cids(conn) && CN_SCID(&conn->ifc_conn)->len != 0) if (can_issue_cids(conn) && CN_SCID(&conn->ifc_conn)->len != 0)
conn->ifc_send_flags |= SF_SEND_NEW_CID; conn->ifc_send_flags |= SF_SEND_NEW_CID;
maybe_create_delayed_streams(conn); maybe_create_delayed_streams(conn);
@ -3675,6 +3787,12 @@ immediate_close (struct ietf_full_conn *conn)
LSQ_WARN("%s failed", __func__); LSQ_WARN("%s failed", __func__);
return TICK_CLOSE; return TICK_CLOSE;
} }
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_CONNECTION_CLOSE, packet_out->po_data_sz, sz))
{
LSQ_WARN("%s: adding frame to packet failed: %d", __func__, errno);
return TICK_CLOSE;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE; packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
LSQ_DEBUG("generated CONNECTION_CLOSE frame in its own packet"); LSQ_DEBUG("generated CONNECTION_CLOSE frame in its own packet");
@ -3844,6 +3962,12 @@ generate_connection_close_packet (struct ietf_full_conn *conn)
ABORT_ERROR("generate_connection_close_packet failed"); ABORT_ERROR("generate_connection_close_packet failed");
return; return;
} }
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_CONNECTION_CLOSE, packet_out->po_data_sz, sz))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE; packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
LSQ_DEBUG("generated CONNECTION_CLOSE frame in its own packet"); LSQ_DEBUG("generated CONNECTION_CLOSE frame in its own packet");
@ -3870,6 +3994,12 @@ generate_ping_frame (struct ietf_full_conn *conn, lsquic_time_t unused)
ABORT_ERROR("gen_ping_frame failed"); ABORT_ERROR("gen_ping_frame failed");
return; return;
} }
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_PING, packet_out->po_data_sz, sz))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_PING; packet_out->po_frame_types |= 1 << QUIC_FRAME_PING;
LSQ_DEBUG("wrote PING frame"); LSQ_DEBUG("wrote PING frame");
@ -3898,6 +4028,12 @@ generate_handshake_done_frame (struct ietf_full_conn *conn,
return; return;
} }
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_HANDSHAKE_DONE, packet_out->po_data_sz, sz))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= QUIC_FTBIT_HANDSHAKE_DONE; packet_out->po_frame_types |= QUIC_FTBIT_HANDSHAKE_DONE;
LSQ_DEBUG("generated HANDSHAKE_DONE frame"); LSQ_DEBUG("generated HANDSHAKE_DONE frame");
@ -3929,7 +4065,13 @@ generate_ack_frequency_frame (struct ietf_full_conn *conn, lsquic_time_t unused)
conn->ifc_max_peer_ack_usec); conn->ifc_max_peer_ack_usec);
if (sz < 0) if (sz < 0)
{ {
ABORT_ERROR("gen_rst_frame failed"); ABORT_ERROR("gen_ack_frequency_frame failed");
return;
}
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_ACK_FREQUENCY, packet_out->po_data_sz, sz))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return; return;
} }
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
@ -3989,6 +4131,12 @@ generate_path_chal_frame (struct ietf_full_conn *conn, lsquic_time_t now,
++copath->cop_n_chals; ++copath->cop_n_chals;
EV_LOG_GENERATED_PATH_CHAL_FRAME(LSQUIC_LOG_CONN_ID, conn->ifc_conn.cn_pf, EV_LOG_GENERATED_PATH_CHAL_FRAME(LSQUIC_LOG_CONN_ID, conn->ifc_conn.cn_pf,
packet_out->po_data + packet_out->po_data_sz, w); packet_out->po_data + packet_out->po_data_sz, w);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_PATH_CHALLENGE, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
packet_out->po_frame_types |= QUIC_FTBIT_PATH_CHALLENGE; packet_out->po_frame_types |= QUIC_FTBIT_PATH_CHALLENGE;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
packet_out->po_regen_sz += w; packet_out->po_regen_sz += w;
@ -4054,6 +4202,12 @@ generate_path_resp_frame (struct ietf_full_conn *conn, lsquic_time_t now,
w, copath->cop_inc_chal); w, copath->cop_inc_chal);
EV_LOG_GENERATED_PATH_RESP_FRAME(LSQUIC_LOG_CONN_ID, conn->ifc_conn.cn_pf, EV_LOG_GENERATED_PATH_RESP_FRAME(LSQUIC_LOG_CONN_ID, conn->ifc_conn.cn_pf,
packet_out->po_data + packet_out->po_data_sz, w); packet_out->po_data + packet_out->po_data_sz, w);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_PATH_RESPONSE, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
packet_out->po_frame_types |= QUIC_FTBIT_PATH_RESPONSE; packet_out->po_frame_types |= QUIC_FTBIT_PATH_RESPONSE;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w); lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
packet_out->po_regen_sz += w; packet_out->po_regen_sz += w;
@ -5820,8 +5974,6 @@ static int
init_new_path (struct ietf_full_conn *conn, struct conn_path *path, init_new_path (struct ietf_full_conn *conn, struct conn_path *path,
int dcid_changed) int dcid_changed)
{ {
struct lsquic_conn *const lconn = &conn->ifc_conn;
const struct transport_params *params;
struct dcid_elem *dce; struct dcid_elem *dce;
dce = find_unassigned_dcid(conn); dce = find_unassigned_dcid(conn);
@ -5849,17 +6001,11 @@ init_new_path (struct ietf_full_conn *conn, struct conn_path *path,
return -1; return -1;
} }
if (NP_IS_IPv6(&path->cop_path)) path->cop_path.np_pack_size
path->cop_path.np_pack_size = IQUIC_MAX_IPv6_PACKET_SZ; = calc_base_packet_size(conn, NP_IS_IPv6(&path->cop_path));
else
path->cop_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ; if (conn->ifc_max_udp_payload < path->cop_path.np_pack_size)
params = lconn->cn_esf.i->esfi_get_peer_transport_params( path->cop_path.np_pack_size = conn->ifc_max_udp_payload;
lconn->cn_enc_session);
if (params && (params->tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE))
&& params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE]
< path->cop_path.np_pack_size)
path->cop_path.np_pack_size
= params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE];
LSQ_DEBUG("initialized path %u", (unsigned) (path - conn->ifc_paths)); LSQ_DEBUG("initialized path %u", (unsigned) (path - conn->ifc_paths));
@ -6621,6 +6767,35 @@ ietf_full_conn_ci_packet_not_sent (struct lsquic_conn *lconn,
} }
static void
ietf_full_conn_ci_packet_too_large (struct lsquic_conn *lconn,
struct lsquic_packet_out *packet_out)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
#ifndef NDEBUG
assert(packet_out->po_lflags & POL_HEADER_PROT);
#endif
lsquic_senhist_add(&conn->ifc_send_ctl.sc_senhist, packet_out->po_packno);
lsquic_send_ctl_sanity_check(&conn->ifc_send_ctl);
if (packet_out->po_flags & PO_MTU_PROBE)
{
LSQ_DEBUG("%zu-byte MTU probe in packet %"PRIu64" is too large",
lsquic_packet_out_sent_sz(&conn->ifc_conn, packet_out),
packet_out->po_packno);
mtu_probe_too_large(conn, packet_out);
}
else
ABORT_WARN("non-MTU probe %zu-byte packet %"PRIu64" is too large",
lsquic_packet_out_sent_sz(&conn->ifc_conn, packet_out),
packet_out->po_packno);
lsquic_packet_out_destroy(packet_out, conn->ifc_enpub,
packet_out->po_path->np_peer_ctx);
}
/* Calling of ignore_init() must be delayed until all batched packets have /* Calling of ignore_init() must be delayed until all batched packets have
* been returned by the engine. * been returned by the engine.
*/ */
@ -6752,6 +6927,204 @@ maybe_set_noprogress_alarm (struct ietf_full_conn *conn, lsquic_time_t now)
} }
static void
check_or_schedule_mtu_probe (struct ietf_full_conn *conn, lsquic_time_t now)
{
struct conn_path *const cpath = CUR_CPATH(conn);
struct dplpmtud_state *const ds = &cpath->cop_dplpmtud;
struct lsquic_packet_out *packet_out;
unsigned short saved_packet_sz, avail, mtu_ceiling, net_header_sz, probe_sz;
int sz;
if (ds->ds_flags & DS_PROBE_SENT)
{
assert(ds->ds_probe_sent + conn->ifc_enpub->enp_mtu_probe_timer < now);
LSQ_DEBUG("MTU probe of %hu bytes lost", ds->ds_probed_size);
ds->ds_flags &= ~DS_PROBE_SENT;
conn->ifc_mflags |= MF_CHECK_MTU_PROBE;
if (ds->ds_probe_count >= 3)
{
LSQ_DEBUG("MTU probe of %hu bytes lost after %hhu tries",
ds->ds_probed_size, ds->ds_probe_count);
ds->ds_failed_size = ds->ds_probed_size;
ds->ds_probe_count = 0;
}
}
assert(0 == ds->ds_probe_sent
|| ds->ds_probe_sent + conn->ifc_enpub->enp_mtu_probe_timer < now);
if (!(conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|| lsquic_senhist_largest(&conn->ifc_send_ctl.sc_senhist) < 30
|| lsquic_send_ctl_in_recovery(&conn->ifc_send_ctl)
|| !lsquic_send_ctl_can_send_probe(&conn->ifc_send_ctl,
&cpath->cop_path))
{
return;
}
net_header_sz = TRANSPORT_OVERHEAD(NP_IS_IPv6(&cpath->cop_path));
if (ds->ds_failed_size)
mtu_ceiling = ds->ds_failed_size; /* Don't subtract net_header_sz */
else if (conn->ifc_settings->es_max_plpmtu)
mtu_ceiling = conn->ifc_settings->es_max_plpmtu - net_header_sz;
else
mtu_ceiling = 1500 - net_header_sz;
if (conn->ifc_max_udp_payload < mtu_ceiling)
{
LSQ_DEBUG("cap MTU ceiling to peer's max_udp_payload_size TP of %hu "
"bytes", conn->ifc_max_udp_payload);
mtu_ceiling = conn->ifc_max_udp_payload;
}
if (cpath->cop_path.np_pack_size >= mtu_ceiling
|| (float) cpath->cop_path.np_pack_size / (float) mtu_ceiling >= 0.99)
{
LSQ_DEBUG("stop MTU probing on path %hhu having achieved about "
"%.1f%% efficiency (detected MTU: %hu; failed MTU: %hu)",
cpath->cop_path.np_path_id,
100. * (float) cpath->cop_path.np_pack_size / (float) mtu_ceiling,
cpath->cop_path.np_pack_size, ds->ds_failed_size);
conn->ifc_mflags &= ~MF_CHECK_MTU_PROBE;
return;
}
LSQ_DEBUG("MTU ratio: %hu / %hu = %.4f",
cpath->cop_path.np_pack_size, mtu_ceiling,
(float) cpath->cop_path.np_pack_size / (float) mtu_ceiling);
if (!ds->ds_failed_size && mtu_ceiling < 1500)
/* Try the largest ethernet MTU immediately */
probe_sz = mtu_ceiling;
else if (cpath->cop_path.np_pack_size * 2 >= mtu_ceiling)
/* Pick half-way point */
probe_sz = (mtu_ceiling + cpath->cop_path.np_pack_size) / 2;
else
probe_sz = cpath->cop_path.np_pack_size * 2;
/* XXX Changing np_pack_size is action at a distance */
saved_packet_sz = cpath->cop_path.np_pack_size;
cpath->cop_path.np_pack_size = probe_sz;
packet_out = lsquic_send_ctl_new_packet_out(&conn->ifc_send_ctl,
0, PNS_APP, CUR_NPATH(conn));
if (!packet_out)
goto restore_packet_size;
sz = conn->ifc_conn.cn_pf->pf_gen_ping_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out));
if (sz < 0) {
ABORT_ERROR("gen_ping_frame failed");
goto restore_packet_size;
}
/* We don't record frame records for MTU probes as they are never
* resized, only discarded.
*/
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_PING;
avail = lsquic_packet_out_avail(packet_out);
if (avail)
{
memset(packet_out->po_data + packet_out->po_data_sz, 0, avail);
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, avail);
packet_out->po_frame_types |= 1 << QUIC_FRAME_PADDING;
}
packet_out->po_flags |= PO_MTU_PROBE;
lsquic_send_ctl_scheduled_one(&conn->ifc_send_ctl, packet_out);
LSQ_DEBUG("generated MTU probe of %hu bytes in packet %"PRIu64,
cpath->cop_path.np_pack_size, packet_out->po_packno);
#ifndef NDEBUG
ds->ds_probe_sent = now;
#endif
ds->ds_probe_packno = packet_out->po_packno;
ds->ds_probed_size = probe_sz;
ds->ds_flags |= DS_PROBE_SENT;
++ds->ds_probe_count;
conn->ifc_mflags &= ~MF_CHECK_MTU_PROBE;
assert(!lsquic_alarmset_is_set(&conn->ifc_alset, AL_MTU_PROBE));
lsquic_alarmset_set(&conn->ifc_alset, AL_MTU_PROBE,
now + conn->ifc_enpub->enp_mtu_probe_timer);
restore_packet_size:
cpath->cop_path.np_pack_size = saved_packet_sz;
}
static void
ietf_full_conn_ci_mtu_probe_acked (struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out)
{
struct ietf_full_conn *const conn = (struct ietf_full_conn *) lconn;
struct conn_path *cpath;
struct dplpmtud_state *ds;
unsigned char path_id;
path_id = packet_out->po_path->np_path_id;
cpath = &conn->ifc_paths[path_id];
ds = &cpath->cop_dplpmtud;
if (ds->ds_probe_packno != packet_out->po_packno)
{
LSQ_DEBUG("Acked MTU probe packet %"PRIu64" on path %hhu, but it is "
"old: discard", packet_out->po_packno, path_id);
return;
}
ds->ds_flags &= ~DS_PROBE_SENT;
ds->ds_probe_count = 0;
cpath->cop_path.np_pack_size = lsquic_packet_out_sent_sz(&conn->ifc_conn,
packet_out);
LSQ_INFO("update path %hhu MTU to %hu bytes", path_id,
cpath->cop_path.np_pack_size);
conn->ifc_mflags &= ~MF_CHECK_MTU_PROBE;
lsquic_alarmset_set(&conn->ifc_alset, AL_MTU_PROBE,
packet_out->po_sent + conn->ifc_enpub->enp_mtu_probe_timer);
LSQ_DEBUG("set alarm to %"PRIu64" usec ", packet_out->po_sent + conn->ifc_enpub->enp_mtu_probe_timer);
}
static void
mtu_probe_too_large (struct ietf_full_conn *conn,
const struct lsquic_packet_out *packet_out)
{
struct conn_path *cpath;
unsigned char path_id;
path_id = packet_out->po_path->np_path_id;
cpath = &conn->ifc_paths[path_id];
cpath->cop_dplpmtud.ds_failed_size
= lsquic_packet_out_sent_sz(&conn->ifc_conn, packet_out);
}
static void
ietf_full_conn_ci_retx_timeout (struct lsquic_conn *lconn)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
unsigned short pack_size;
struct conn_path *cpath;
int resize;
resize = 0;
for (cpath = conn->ifc_paths; cpath < conn->ifc_paths + N_PATHS; ++cpath)
if (cpath->cop_flags & COP_INITIALIZED)
{
pack_size = calc_base_packet_size(conn,
NP_IS_IPv6(&cpath->cop_path));
if (cpath->cop_path.np_pack_size > pack_size)
{
LSQ_DEBUG("RTO occurred: change packet size of path %hhu "
"to %hu bytes", cpath->cop_path.np_path_id, pack_size);
cpath->cop_path.np_pack_size = pack_size;
resize |= 1;
}
}
if (resize)
lsquic_send_ctl_resize(&conn->ifc_send_ctl);
else
LSQ_DEBUG("RTO occurred, but no MTUs to reset");
}
static enum tick_st static enum tick_st
ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now) ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
{ {
@ -6878,6 +7251,9 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
} }
} }
if (conn->ifc_mflags & MF_CHECK_MTU_PROBE)
check_or_schedule_mtu_probe(conn, now);
n = lsquic_send_ctl_reschedule_packets(&conn->ifc_send_ctl); n = lsquic_send_ctl_reschedule_packets(&conn->ifc_send_ctl);
if (n > 0) if (n > 0)
CLOSE_IF_NECESSARY(); CLOSE_IF_NECESSARY();
@ -7358,6 +7734,7 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
.ci_is_push_enabled = ietf_full_conn_ci_is_push_enabled, \ .ci_is_push_enabled = ietf_full_conn_ci_is_push_enabled, \
.ci_is_tickable = ietf_full_conn_ci_is_tickable, \ .ci_is_tickable = ietf_full_conn_ci_is_tickable, \
.ci_make_stream = ietf_full_conn_ci_make_stream, \ .ci_make_stream = ietf_full_conn_ci_make_stream, \
.ci_mtu_probe_acked = ietf_full_conn_ci_mtu_probe_acked, \
.ci_n_avail_streams = ietf_full_conn_ci_n_avail_streams, \ .ci_n_avail_streams = ietf_full_conn_ci_n_avail_streams, \
.ci_n_pending_streams = ietf_full_conn_ci_n_pending_streams, \ .ci_n_pending_streams = ietf_full_conn_ci_n_pending_streams, \
.ci_next_tick_time = ietf_full_conn_ci_next_tick_time, \ .ci_next_tick_time = ietf_full_conn_ci_next_tick_time, \
@ -7365,6 +7742,7 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
.ci_push_stream = ietf_full_conn_ci_push_stream, \ .ci_push_stream = ietf_full_conn_ci_push_stream, \
.ci_record_addrs = ietf_full_conn_ci_record_addrs, \ .ci_record_addrs = ietf_full_conn_ci_record_addrs, \
.ci_report_live = ietf_full_conn_ci_report_live, \ .ci_report_live = ietf_full_conn_ci_report_live, \
.ci_retx_timeout = ietf_full_conn_ci_retx_timeout, \
.ci_set_ctx = ietf_full_conn_ci_set_ctx, \ .ci_set_ctx = ietf_full_conn_ci_set_ctx, \
.ci_status = ietf_full_conn_ci_status, \ .ci_status = ietf_full_conn_ci_status, \
.ci_stateless_reset = ietf_full_conn_ci_stateless_reset, \ .ci_stateless_reset = ietf_full_conn_ci_stateless_reset, \
@ -7377,6 +7755,7 @@ static const struct conn_iface ietf_full_conn_iface = {
.ci_next_packet_to_send = ietf_full_conn_ci_next_packet_to_send, .ci_next_packet_to_send = ietf_full_conn_ci_next_packet_to_send,
.ci_packet_not_sent = ietf_full_conn_ci_packet_not_sent, .ci_packet_not_sent = ietf_full_conn_ci_packet_not_sent,
.ci_packet_sent = ietf_full_conn_ci_packet_sent, .ci_packet_sent = ietf_full_conn_ci_packet_sent,
.ci_packet_too_large = ietf_full_conn_ci_packet_too_large,
}; };
static const struct conn_iface *ietf_full_conn_iface_ptr = static const struct conn_iface *ietf_full_conn_iface_ptr =
&ietf_full_conn_iface; &ietf_full_conn_iface;

View file

@ -94,6 +94,7 @@ enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_QPACK_DEC] = LSQ_LOG_WARN, [LSQLM_QPACK_DEC] = LSQ_LOG_WARN,
[LSQLM_PRIO] = LSQ_LOG_WARN, [LSQLM_PRIO] = LSQ_LOG_WARN,
[LSQLM_BW_SAMPLER] = LSQ_LOG_WARN, [LSQLM_BW_SAMPLER] = LSQ_LOG_WARN,
[LSQLM_PACKET_RESIZE] = LSQ_LOG_WARN,
}; };
const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = { const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
@ -136,6 +137,7 @@ const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_QPACK_DEC] = "qpack-dec", [LSQLM_QPACK_DEC] = "qpack-dec",
[LSQLM_PRIO] = "prio", [LSQLM_PRIO] = "prio",
[LSQLM_BW_SAMPLER] = "bw-sampler", [LSQLM_BW_SAMPLER] = "bw-sampler",
[LSQLM_PACKET_RESIZE] = "packet-resize",
}; };
const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = { const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {

View file

@ -85,6 +85,7 @@ enum lsquic_logger_module {
LSQLM_QPACK_DEC, LSQLM_QPACK_DEC,
LSQLM_PRIO, LSQLM_PRIO,
LSQLM_BW_SAMPLER, LSQLM_BW_SAMPLER,
LSQLM_PACKET_RESIZE,
N_LSQUIC_LOGGER_MODULES N_LSQUIC_LOGGER_MODULES
}; };

View file

@ -826,7 +826,7 @@ mini_stream_read (void *stream, void *buf, size_t len, int *reached_fin)
/* Wrapper to throw out reached_fin */ /* Wrapper to throw out reached_fin */
static size_t static size_t
mini_stream_read_for_crypto (void *stream, void *buf, size_t len) mini_stream_read_for_crypto (void *stream, void *buf, size_t len, int *fin)
{ {
size_t retval; size_t retval;
int reached_fin; int reached_fin;
@ -969,7 +969,7 @@ to_packet_Q050plus (struct mini_conn *mc, struct mini_stream_ctx *ms_ctx,
cur_off = ms_ctx->off; cur_off = ms_ctx->off;
len = mc->mc_conn.cn_pf->pf_gen_crypto_frame( len = mc->mc_conn.cn_pf->pf_gen_crypto_frame(
packet_out->po_data + packet_out->po_data_sz, packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), mc->mc_write_off, lsquic_packet_out_avail(packet_out), 0, mc->mc_write_off, 0,
mini_stream_size(ms_ctx), mini_stream_read_for_crypto, ms_ctx); mini_stream_size(ms_ctx), mini_stream_read_for_crypto, ms_ctx);
if (len < 0) if (len < 0)
{ {

View file

@ -154,7 +154,7 @@ struct msg_ctx
static size_t static size_t
read_from_msg_ctx (void *ctx, void *buf, size_t len) read_from_msg_ctx (void *ctx, void *buf, size_t len, int *fin)
{ {
struct msg_ctx *msg_ctx = ctx; struct msg_ctx *msg_ctx = ctx;
if (len > (uintptr_t) (msg_ctx->end - msg_ctx->buf)) if (len > (uintptr_t) (msg_ctx->end - msg_ctx->buf))
@ -244,7 +244,7 @@ imico_stream_write (void *stream, const void *bufp, size_t bufsz)
p = msg_ctx.buf; p = msg_ctx.buf;
len = pf->pf_gen_crypto_frame(packet_out->po_data + packet_out->po_data_sz, len = pf->pf_gen_crypto_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), cryst->mcs_write_off, lsquic_packet_out_avail(packet_out), 0, cryst->mcs_write_off, 0,
msg_ctx.end - msg_ctx.buf, read_from_msg_ctx, &msg_ctx); msg_ctx.end - msg_ctx.buf, read_from_msg_ctx, &msg_ctx);
if (len < 0) if (len < 0)
return len; return len;
@ -513,12 +513,14 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
conn->imc_enpub = enpub; conn->imc_enpub = enpub;
conn->imc_created = packet_in->pi_received; conn->imc_created = packet_in->pi_received;
conn->imc_path.np_pack_size = is_ipv4 ? IQUIC_MAX_IPv4_PACKET_SZ if (enpub->enp_settings.es_base_plpmtu)
: IQUIC_MAX_IPv6_PACKET_SZ; conn->imc_path.np_pack_size = enpub->enp_settings.es_base_plpmtu
#ifndef NDEBUG - (is_ipv4 ? 20 : 40) /* IP header */
if (getenv("LSQUIC_CN_PACK_SIZE")) - 8; /* UDP header */
conn->imc_path.np_pack_size = atoi(getenv("LSQUIC_CN_PACK_SIZE")); else if (is_ipv4)
#endif conn->imc_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ;
else
conn->imc_path.np_pack_size = IQUIC_MAX_IPv6_PACKET_SZ;
conn->imc_conn.cn_pf = select_pf_by_ver(version); conn->imc_conn.cn_pf = select_pf_by_ver(version);
conn->imc_conn.cn_esf.i = esfi; conn->imc_conn.cn_esf.i = esfi;
conn->imc_conn.cn_enc_session = enc_sess; conn->imc_conn.cn_enc_session = enc_sess;

View file

@ -80,7 +80,7 @@ lsquic_mm_init (struct lsquic_mm *mm)
mm->acki = malloc(sizeof(*mm->acki)); mm->acki = malloc(sizeof(*mm->acki));
mm->malo.stream_frame = lsquic_malo_create(sizeof(struct stream_frame)); mm->malo.stream_frame = lsquic_malo_create(sizeof(struct stream_frame));
mm->malo.stream_rec_arr = lsquic_malo_create(sizeof(struct stream_rec_arr)); mm->malo.frame_rec_arr = lsquic_malo_create(sizeof(struct frame_rec_arr));
mm->malo.mini_conn = lsquic_malo_create(sizeof(struct mini_conn)); mm->malo.mini_conn = lsquic_malo_create(sizeof(struct mini_conn));
mm->malo.mini_conn_ietf = lsquic_malo_create(sizeof(struct ietf_mini_conn)); mm->malo.mini_conn_ietf = lsquic_malo_create(sizeof(struct ietf_mini_conn));
mm->malo.packet_in = lsquic_malo_create(sizeof(struct lsquic_packet_in)); mm->malo.packet_in = lsquic_malo_create(sizeof(struct lsquic_packet_in));
@ -98,7 +98,7 @@ lsquic_mm_init (struct lsquic_mm *mm)
SLIST_INIT(&mm->four_k_pages); SLIST_INIT(&mm->four_k_pages);
SLIST_INIT(&mm->sixteen_k_pages); SLIST_INIT(&mm->sixteen_k_pages);
#endif #endif
if (mm->acki && mm->malo.stream_frame && mm->malo.stream_rec_arr if (mm->acki && mm->malo.stream_frame && mm->malo.frame_rec_arr
&& mm->malo.mini_conn && mm->malo.mini_conn_ietf && mm->malo.packet_in && mm->malo.mini_conn && mm->malo.mini_conn_ietf && mm->malo.packet_in
&& mm->malo.packet_out && mm->malo.dcid_elem && mm->malo.packet_out && mm->malo.dcid_elem
&& mm->malo.stream_hq_frame && mm->ack_str) && mm->malo.stream_hq_frame && mm->ack_str)
@ -127,7 +127,7 @@ lsquic_mm_cleanup (struct lsquic_mm *mm)
lsquic_malo_destroy(mm->malo.packet_in); lsquic_malo_destroy(mm->malo.packet_in);
lsquic_malo_destroy(mm->malo.packet_out); lsquic_malo_destroy(mm->malo.packet_out);
lsquic_malo_destroy(mm->malo.stream_frame); lsquic_malo_destroy(mm->malo.stream_frame);
lsquic_malo_destroy(mm->malo.stream_rec_arr); lsquic_malo_destroy(mm->malo.frame_rec_arr);
lsquic_malo_destroy(mm->malo.mini_conn); lsquic_malo_destroy(mm->malo.mini_conn);
lsquic_malo_destroy(mm->malo.mini_conn_ietf); lsquic_malo_destroy(mm->malo.mini_conn_ietf);
free(mm->ack_str); free(mm->ack_str);
@ -553,7 +553,7 @@ lsquic_mm_mem_used (const struct lsquic_mm *mm)
size = sizeof(*mm); size = sizeof(*mm);
size += sizeof(*mm->acki); size += sizeof(*mm->acki);
size += lsquic_malo_mem_used(mm->malo.stream_frame); size += lsquic_malo_mem_used(mm->malo.stream_frame);
size += lsquic_malo_mem_used(mm->malo.stream_rec_arr); size += lsquic_malo_mem_used(mm->malo.frame_rec_arr);
size += lsquic_malo_mem_used(mm->malo.mini_conn); size += lsquic_malo_mem_used(mm->malo.mini_conn);
size += lsquic_malo_mem_used(mm->malo.mini_conn_ietf); size += lsquic_malo_mem_used(mm->malo.mini_conn_ietf);
size += lsquic_malo_mem_used(mm->malo.packet_in); size += lsquic_malo_mem_used(mm->malo.packet_in);

View file

@ -33,7 +33,7 @@ struct lsquic_mm {
struct ack_info *acki; struct ack_info *acki;
struct { struct {
struct malo *stream_frame; /* For struct stream_frame */ struct malo *stream_frame; /* For struct stream_frame */
struct malo *stream_rec_arr;/* For struct stream_rec_arr */ struct malo *frame_rec_arr; /* For struct frame_rec_arr */
struct malo *mini_conn; /* For struct mini_conn */ struct malo *mini_conn; /* For struct mini_conn */
struct malo *mini_conn_ietf;/* For struct ietf_mini_conn */ struct malo *mini_conn_ietf;/* For struct ietf_mini_conn */
struct malo *retry_conn; /* For struct retry_conn */ struct malo *retry_conn; /* For struct retry_conn */

View file

@ -125,6 +125,16 @@ lsquic_pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
} }
int
lsquic_pacer_can_schedule_probe (const struct pacer *pacer,
unsigned n_in_flight, lsquic_time_t tx_time)
{
return pacer->pa_burst_tokens > 1 /* Double packet size, want two tokens */
|| n_in_flight == 0
|| pacer->pa_next_sched > pacer->pa_now + tx_time / 2;
}
void void
lsquic_pacer_tick_in (struct pacer *pacer, lsquic_time_t now) lsquic_pacer_tick_in (struct pacer *pacer, lsquic_time_t now)
{ {

View file

@ -59,4 +59,8 @@ lsquic_pacer_loss_event (struct pacer *);
#define lsquic_pacer_next_sched(pacer) (+(pacer)->pa_next_sched) #define lsquic_pacer_next_sched(pacer) (+(pacer)->pa_next_sched)
int
lsquic_pacer_can_schedule_probe (const struct pacer *,
unsigned n_in_flight, lsquic_time_t tx_time);
#endif #endif

View file

@ -16,6 +16,9 @@ enum PACKET_PUBLIC_FLAGS
PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7, PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7,
}; };
/* XXX The name of this macro no longer matches: it applies both to gQUIC and
* IETF QUIC.
*/
#define GQUIC_FRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \ #define GQUIC_FRAME_REGEN_MASK ((1 << QUIC_FRAME_ACK) \
| (1 << QUIC_FRAME_PATH_CHALLENGE) | (1 << QUIC_FRAME_PATH_RESPONSE) \ | (1 << QUIC_FRAME_PATH_CHALLENGE) | (1 << QUIC_FRAME_PATH_RESPONSE) \
| (1 << QUIC_FRAME_STOP_WAITING) | (1 << QUIC_FRAME_TIMESTAMP)) | (1 << QUIC_FRAME_STOP_WAITING) | (1 << QUIC_FRAME_TIMESTAMP))

View file

@ -30,84 +30,84 @@
#include "lsquic_enc_sess.h" #include "lsquic_enc_sess.h"
typedef char _stream_rec_arr_is_at_most_64bytes[ typedef char _stream_rec_arr_is_at_most_64bytes[
(sizeof(struct stream_rec_arr) <= 64)? 1: - 1]; (sizeof(struct frame_rec_arr) <= 64)? 1: - 1];
static struct stream_rec * static struct frame_rec *
srec_one_posi_first (struct packet_out_srec_iter *posi, frec_one_pofi_first (struct packet_out_frec_iter *pofi,
struct lsquic_packet_out *packet_out) struct lsquic_packet_out *packet_out)
{ {
if (packet_out->po_srecs.one.sr_frame_type) if (packet_out->po_frecs.one.fe_frame_type)
return &packet_out->po_srecs.one; return &packet_out->po_frecs.one;
else else
return NULL; return NULL;
} }
static struct stream_rec * static struct frame_rec *
srec_one_posi_next (struct packet_out_srec_iter *posi) frec_one_pofi_next (struct packet_out_frec_iter *pofi)
{ {
return NULL; return NULL;
} }
static struct stream_rec * static struct frame_rec *
srec_arr_posi_next (struct packet_out_srec_iter *posi) frec_arr_pofi_next (struct packet_out_frec_iter *pofi)
{ {
while (posi->cur_srec_arr) while (pofi->cur_frec_arr)
{ {
for (; posi->srec_idx < sizeof(posi->cur_srec_arr->srecs) / sizeof(posi->cur_srec_arr->srecs[0]); for (; pofi->frec_idx < sizeof(pofi->cur_frec_arr->frecs) / sizeof(pofi->cur_frec_arr->frecs[0]);
++posi->srec_idx) ++pofi->frec_idx)
{ {
if (posi->cur_srec_arr->srecs[ posi->srec_idx ].sr_frame_type) if (pofi->cur_frec_arr->frecs[ pofi->frec_idx ].fe_frame_type)
return &posi->cur_srec_arr->srecs[ posi->srec_idx++ ]; return &pofi->cur_frec_arr->frecs[ pofi->frec_idx++ ];
} }
posi->cur_srec_arr = TAILQ_NEXT(posi->cur_srec_arr, next_stream_rec_arr); pofi->cur_frec_arr = TAILQ_NEXT(pofi->cur_frec_arr, next_stream_rec_arr);
posi->srec_idx = 0; pofi->frec_idx = 0;
} }
return NULL; return NULL;
} }
static struct stream_rec * static struct frame_rec *
srec_arr_posi_first (struct packet_out_srec_iter *posi, frec_arr_pofi_first (struct packet_out_frec_iter *pofi,
struct lsquic_packet_out *packet_out) struct lsquic_packet_out *packet_out)
{ {
posi->packet_out = packet_out; pofi->packet_out = packet_out;
posi->cur_srec_arr = TAILQ_FIRST(&packet_out->po_srecs.arr); pofi->cur_frec_arr = TAILQ_FIRST(&packet_out->po_frecs.arr);
posi->srec_idx = 0; pofi->frec_idx = 0;
return srec_arr_posi_next(posi); return frec_arr_pofi_next(pofi);
} }
static struct stream_rec * (* const posi_firsts[]) static struct frame_rec * (* const pofi_firsts[])
(struct packet_out_srec_iter *, struct lsquic_packet_out *) = (struct packet_out_frec_iter *, struct lsquic_packet_out *) =
{ {
srec_one_posi_first, frec_one_pofi_first,
srec_arr_posi_first, frec_arr_pofi_first,
}; };
static struct stream_rec * (* const posi_nexts[]) static struct frame_rec * (* const pofi_nexts[])
(struct packet_out_srec_iter *posi) = (struct packet_out_frec_iter *pofi) =
{ {
srec_one_posi_next, frec_one_pofi_next,
srec_arr_posi_next, frec_arr_pofi_next,
}; };
struct stream_rec * struct frame_rec *
lsquic_posi_first (struct packet_out_srec_iter *posi, lsquic_pofi_first (struct packet_out_frec_iter *pofi,
lsquic_packet_out_t *packet_out) lsquic_packet_out_t *packet_out)
{ {
posi->impl_idx = !!(packet_out->po_flags & PO_SREC_ARR); pofi->impl_idx = !!(packet_out->po_flags & PO_FREC_ARR);
return posi_firsts[posi->impl_idx](posi, packet_out); return pofi_firsts[pofi->impl_idx](pofi, packet_out);
} }
struct stream_rec * struct frame_rec *
lsquic_posi_next (struct packet_out_srec_iter *posi) lsquic_pofi_next (struct packet_out_frec_iter *pofi)
{ {
return posi_nexts[posi->impl_idx](posi); return pofi_nexts[pofi->impl_idx](pofi);
} }
@ -116,73 +116,87 @@ lsquic_posi_next (struct packet_out_srec_iter *posi)
* in packet_out->po_data. There is no assertion to guard for for this. * in packet_out->po_data. There is no assertion to guard for for this.
*/ */
int int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out, lsquic_packet_out_add_frame (lsquic_packet_out_t *packet_out,
struct lsquic_mm *mm, struct lsquic_mm *mm,
struct lsquic_stream *new_stream, uintptr_t data,
enum quic_frame_type frame_type, enum quic_frame_type frame_type,
unsigned short off, unsigned short len) unsigned short off, unsigned short len)
{ {
struct stream_rec_arr *srec_arr; struct frame_rec_arr *frec_arr;
int last_taken; int last_taken;
unsigned i; unsigned i;
assert(!(new_stream->stream_flags & STREAM_FINISHED)); if (!(packet_out->po_flags & PO_FREC_ARR))
if (!(packet_out->po_flags & PO_SREC_ARR))
{ {
if (!srec_taken(&packet_out->po_srecs.one)) if (!frec_taken(&packet_out->po_frecs.one))
{ {
packet_out->po_srecs.one.sr_frame_type = frame_type; packet_out->po_frecs.one.fe_frame_type = frame_type;
packet_out->po_srecs.one.sr_stream = new_stream; packet_out->po_frecs.one.fe_u.data = data;
packet_out->po_srecs.one.sr_off = off; packet_out->po_frecs.one.fe_off = off;
packet_out->po_srecs.one.sr_len = len; packet_out->po_frecs.one.fe_len = len;
++new_stream->n_unacked;
return 0; /* Insert in first slot */ return 0; /* Insert in first slot */
} }
srec_arr = lsquic_malo_get(mm->malo.stream_rec_arr); frec_arr = lsquic_malo_get(mm->malo.frame_rec_arr);
if (!srec_arr) if (!frec_arr)
return -1; return -1;
memset(srec_arr, 0, sizeof(*srec_arr)); memset(frec_arr, 0, sizeof(*frec_arr));
srec_arr->srecs[0] = packet_out->po_srecs.one; frec_arr->frecs[0] = packet_out->po_frecs.one;
TAILQ_INIT(&packet_out->po_srecs.arr); TAILQ_INIT(&packet_out->po_frecs.arr);
TAILQ_INSERT_TAIL(&packet_out->po_srecs.arr, srec_arr, TAILQ_INSERT_TAIL(&packet_out->po_frecs.arr, frec_arr,
next_stream_rec_arr); next_stream_rec_arr);
packet_out->po_flags |= PO_SREC_ARR; packet_out->po_flags |= PO_FREC_ARR;
i = 1; i = 1;
goto set_elem; goto set_elem;
} }
/* New records go at the very end: */ /* New records go at the very end: */
srec_arr = TAILQ_LAST(&packet_out->po_srecs.arr, stream_rec_arr_tailq); frec_arr = TAILQ_LAST(&packet_out->po_frecs.arr, frame_rec_arr_tailq);
last_taken = -1; last_taken = -1;
for (i = 0; i < sizeof(srec_arr->srecs) / sizeof(srec_arr->srecs[0]); ++i) for (i = 0; i < sizeof(frec_arr->frecs) / sizeof(frec_arr->frecs[0]); ++i)
if (srec_taken(&srec_arr->srecs[i])) if (frec_taken(&frec_arr->frecs[i]))
last_taken = i; last_taken = i;
i = last_taken + 1; i = last_taken + 1;
if (i < sizeof(srec_arr->srecs) / sizeof(srec_arr->srecs[0])) if (i < sizeof(frec_arr->frecs) / sizeof(frec_arr->frecs[0]))
{ {
set_elem: set_elem:
srec_arr->srecs[i].sr_frame_type = frame_type; frec_arr->frecs[i].fe_frame_type = frame_type;
srec_arr->srecs[i].sr_stream = new_stream; frec_arr->frecs[i].fe_u.data = data;
srec_arr->srecs[i].sr_off = off; frec_arr->frecs[i].fe_off = off;
srec_arr->srecs[i].sr_len = len; frec_arr->frecs[i].fe_len = len;
++new_stream->n_unacked; return 0; /* Insert in existing frec */
return 0; /* Insert in existing srec */
} }
srec_arr = lsquic_malo_get(mm->malo.stream_rec_arr); frec_arr = lsquic_malo_get(mm->malo.frame_rec_arr);
if (!srec_arr) if (!frec_arr)
return -1; return -1;
memset(srec_arr, 0, sizeof(*srec_arr)); memset(frec_arr, 0, sizeof(*frec_arr));
srec_arr->srecs[0].sr_frame_type = frame_type; frec_arr->frecs[0].fe_frame_type = frame_type;
srec_arr->srecs[0].sr_stream = new_stream; frec_arr->frecs[0].fe_u.data = data;
srec_arr->srecs[0].sr_off = off; frec_arr->frecs[0].fe_off = off;
srec_arr->srecs[0].sr_len = len; frec_arr->frecs[0].fe_len = len;
TAILQ_INSERT_TAIL(&packet_out->po_srecs.arr, srec_arr, next_stream_rec_arr); TAILQ_INSERT_TAIL(&packet_out->po_frecs.arr, frec_arr, next_stream_rec_arr);
++new_stream->n_unacked; return 0; /* Insert in new frec */
return 0; /* Insert in new srec */ }
int
lsquic_packet_out_add_stream (struct lsquic_packet_out *packet_out,
struct lsquic_mm *mm, struct lsquic_stream *new_stream,
enum quic_frame_type frame_type, unsigned short off, unsigned short len)
{
assert(!(new_stream->stream_flags & STREAM_FINISHED));
assert((1 << frame_type)
& (QUIC_FTBIT_STREAM|QUIC_FTBIT_CRYPTO|QUIC_FTBIT_RST_STREAM));
if (0 == lsquic_packet_out_add_frame(packet_out, mm,
(uintptr_t) new_stream, frame_type, off, len))
{
++new_stream->n_unacked;
return 0;
}
else
return -1;
} }
@ -262,14 +276,14 @@ void
lsquic_packet_out_destroy (lsquic_packet_out_t *packet_out, lsquic_packet_out_destroy (lsquic_packet_out_t *packet_out,
struct lsquic_engine_public *enpub, void *peer_ctx) struct lsquic_engine_public *enpub, void *peer_ctx)
{ {
if (packet_out->po_flags & PO_SREC_ARR) if (packet_out->po_flags & PO_FREC_ARR)
{ {
struct stream_rec_arr *srec_arr, *next; struct frame_rec_arr *frec_arr, *next;
for (srec_arr = TAILQ_FIRST(&packet_out->po_srecs.arr); for (frec_arr = TAILQ_FIRST(&packet_out->po_frecs.arr);
srec_arr; srec_arr = next) frec_arr; frec_arr = next)
{ {
next = TAILQ_NEXT(srec_arr, next_stream_rec_arr); next = TAILQ_NEXT(frec_arr, next_stream_rec_arr);
lsquic_malo_put(srec_arr); lsquic_malo_put(frec_arr);
} }
} }
if (packet_out->po_flags & PO_ENCRYPTED) if (packet_out->po_flags & PO_ENCRYPTED)
@ -290,46 +304,46 @@ unsigned
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out, lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out,
lsquic_stream_id_t stream_id) lsquic_stream_id_t stream_id)
{ {
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
struct stream_rec *srec; struct frame_rec *frec;
unsigned short adj = 0; unsigned short adj = 0;
int n_stream_frames = 0, n_elided = 0; int n_stream_frames = 0, n_elided = 0;
int victim; int victim;
for (srec = lsquic_posi_first(&posi, packet_out); srec; for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
srec = lsquic_posi_next(&posi)) frec = lsquic_pofi_next(&pofi))
{ {
if (srec->sr_frame_type == QUIC_FRAME_STREAM) /* Offsets of all frame records should be adjusted */
frec->fe_off -= adj;
if (frec->fe_frame_type == QUIC_FRAME_STREAM)
{ {
++n_stream_frames; ++n_stream_frames;
/* Offsets of all STREAM frames should be adjusted */
srec->sr_off -= adj;
if (stream_id) if (stream_id)
{ {
victim = srec->sr_stream->id == stream_id; victim = frec->fe_stream->id == stream_id;
if (victim) if (victim)
{ {
assert(lsquic_stream_is_reset(srec->sr_stream)); assert(lsquic_stream_is_reset(frec->fe_stream));
} }
} }
else else
victim = lsquic_stream_is_reset(srec->sr_stream); victim = lsquic_stream_is_reset(frec->fe_stream);
if (victim) if (victim)
{ {
++n_elided; ++n_elided;
/* Move the data and adjust sizes */ /* Move the data and adjust sizes */
adj += srec->sr_len; adj += frec->fe_len;
memmove(packet_out->po_data + srec->sr_off, memmove(packet_out->po_data + frec->fe_off,
packet_out->po_data + srec->sr_off + srec->sr_len, packet_out->po_data + frec->fe_off + frec->fe_len,
packet_out->po_data_sz - srec->sr_off - srec->sr_len); packet_out->po_data_sz - frec->fe_off - frec->fe_len);
packet_out->po_data_sz -= srec->sr_len; packet_out->po_data_sz -= frec->fe_len;
lsquic_stream_acked(srec->sr_stream, srec->sr_frame_type); lsquic_stream_acked(frec->fe_stream, frec->fe_frame_type);
srec->sr_frame_type = 0; frec->fe_frame_type = 0;
} }
} }
} }
@ -348,385 +362,45 @@ lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out,
void void
lsquic_packet_out_chop_regen (lsquic_packet_out_t *packet_out) lsquic_packet_out_chop_regen (lsquic_packet_out_t *packet_out)
{ {
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
struct stream_rec *srec; struct frame_rec *frec;
unsigned delta; unsigned short adj;
delta = packet_out->po_regen_sz; adj = 0;
packet_out->po_data_sz -= delta; for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
memmove(packet_out->po_data, packet_out->po_data + delta, frec = lsquic_pofi_next(&pofi))
packet_out->po_data_sz); {
frec->fe_off -= adj;
if (GQUIC_FRAME_REGEN_MASK & (1 << frec->fe_frame_type))
{
assert(frec->fe_off == 0); /* This checks that all the regen
frames are at the beginning of the packet. It can be removed
when this is no longer the case. */
adj += frec->fe_len;
memmove(packet_out->po_data + frec->fe_off,
packet_out->po_data + frec->fe_off + frec->fe_len,
packet_out->po_data_sz - frec->fe_off - frec->fe_len);
packet_out->po_data_sz -= frec->fe_len;
frec->fe_frame_type = 0;
}
}
assert(adj); /* Otherwise why are we called? */
assert(packet_out->po_regen_sz == adj);
packet_out->po_regen_sz = 0; packet_out->po_regen_sz = 0;
for (srec = lsquic_posi_first(&posi, packet_out); srec;
srec = lsquic_posi_next(&posi))
if (srec->sr_frame_type == QUIC_FRAME_STREAM)
srec->sr_off -= delta;
} }
void void
lsquic_packet_out_ack_streams (lsquic_packet_out_t *packet_out) lsquic_packet_out_ack_streams (lsquic_packet_out_t *packet_out)
{ {
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
struct stream_rec *srec; struct frame_rec *frec;
for (srec = lsquic_posi_first(&posi, packet_out); srec; for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
srec = lsquic_posi_next(&posi)) frec = lsquic_pofi_next(&pofi))
lsquic_stream_acked(srec->sr_stream, srec->sr_frame_type); if ((1 << frec->fe_frame_type)
} & (QUIC_FTBIT_STREAM|QUIC_FTBIT_CRYPTO|QUIC_FTBIT_RST_STREAM))
lsquic_stream_acked(frec->fe_stream, frec->fe_frame_type);
static int
split_off_last_frames (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
lsquic_packet_out_t *new_packet_out, struct stream_rec **srecs,
unsigned n_srecs, enum quic_frame_type frame_type)
{
unsigned n;
for (n = 0; n < n_srecs; ++n)
{
struct stream_rec *const srec = srecs[n];
memcpy(new_packet_out->po_data + new_packet_out->po_data_sz,
packet_out->po_data + srec->sr_off, srec->sr_len);
if (0 != lsquic_packet_out_add_stream(new_packet_out, mm,
srec->sr_stream, frame_type,
new_packet_out->po_data_sz, srec->sr_len))
return -1;
srec->sr_frame_type = 0;
assert(srec->sr_stream->n_unacked > 1);
--srec->sr_stream->n_unacked;
new_packet_out->po_data_sz += srec->sr_len;
}
packet_out->po_data_sz = srecs[0]->sr_off;
return 0;
}
static int
move_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
lsquic_packet_out_t *new_packet_out, struct stream_rec **srecs,
unsigned n_srecs, unsigned max_idx, enum quic_frame_type frame_type)
{
unsigned n;
struct stream_rec *const max_srec = srecs[max_idx];
memcpy(new_packet_out->po_data + new_packet_out->po_data_sz,
packet_out->po_data + max_srec->sr_off, max_srec->sr_len);
memmove(packet_out->po_data + max_srec->sr_off,
packet_out->po_data + max_srec->sr_off + max_srec->sr_len,
packet_out->po_data_sz - max_srec->sr_off - max_srec->sr_len);
if (0 != lsquic_packet_out_add_stream(new_packet_out, mm,
max_srec->sr_stream, frame_type,
new_packet_out->po_data_sz, max_srec->sr_len))
return -1;
max_srec->sr_frame_type = 0;
assert(max_srec->sr_stream->n_unacked > 1);
--max_srec->sr_stream->n_unacked;
new_packet_out->po_data_sz += max_srec->sr_len;
packet_out->po_data_sz -= max_srec->sr_len;
for (n = max_idx + 1; n < n_srecs; ++n)
srecs[n]->sr_off -= max_srec->sr_len;
return 0;
}
struct split_reader_ctx
{
unsigned off;
unsigned len;
signed char fin;
unsigned char buf[GQUIC_MAX_PAYLOAD_SZ / 2 + 1];
};
static int
split_reader_fin (void *ctx)
{
struct split_reader_ctx *const reader_ctx = ctx;
return reader_ctx->off == reader_ctx->len && reader_ctx->fin;
}
static size_t
split_reader_size (void *ctx)
{
struct split_reader_ctx *const reader_ctx = ctx;
return reader_ctx->len - reader_ctx->off;
}
static size_t
split_stream_reader_read (void *ctx, void *buf, size_t len, int *fin)
{
struct split_reader_ctx *const reader_ctx = ctx;
if (len > reader_ctx->len - reader_ctx->off)
len = reader_ctx->len - reader_ctx->off;
memcpy(buf, reader_ctx->buf, len);
reader_ctx->off += len;
*fin = split_reader_fin(reader_ctx);
return len;
}
static size_t
split_crypto_reader_read (void *ctx, void *buf, size_t len)
{
struct split_reader_ctx *const reader_ctx = ctx;
if (len > reader_ctx->len - reader_ctx->off)
len = reader_ctx->len - reader_ctx->off;
memcpy(buf, reader_ctx->buf, len);
reader_ctx->off += len;
return len;
}
static int
split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
lsquic_packet_out_t *new_packet_out, const struct parse_funcs *pf,
struct stream_rec **srecs, unsigned n_srecs, unsigned max_idx,
enum quic_frame_type frame_type)
{
struct stream_rec *const max_srec = srecs[max_idx];
struct stream_frame frame;
int len;
unsigned n;
struct split_reader_ctx reader_ctx;
if (frame_type == QUIC_FRAME_STREAM)
len = pf->pf_parse_stream_frame(packet_out->po_data + max_srec->sr_off,
max_srec->sr_len, &frame);
else
len = pf->pf_parse_crypto_frame(packet_out->po_data + max_srec->sr_off,
max_srec->sr_len, &frame);
if (len < 0)
{
LSQ_ERROR("could not parse own frame");
return -1;
}
assert(frame.data_frame.df_size / 2 <= sizeof(reader_ctx.buf));
if (frame.data_frame.df_size / 2 > sizeof(reader_ctx.buf))
return -1;
memcpy(reader_ctx.buf,
frame.data_frame.df_data + frame.data_frame.df_size / 2,
frame.data_frame.df_size - frame.data_frame.df_size / 2);
reader_ctx.off = 0;
reader_ctx.len = frame.data_frame.df_size - frame.data_frame.df_size / 2;
reader_ctx.fin = frame.data_frame.df_fin;
if (frame_type == QUIC_FRAME_STREAM)
len = pf->pf_gen_stream_frame(
new_packet_out->po_data + new_packet_out->po_data_sz,
lsquic_packet_out_avail(new_packet_out), frame.stream_id,
frame.data_frame.df_offset + frame.data_frame.df_size / 2,
split_reader_fin(&reader_ctx), split_reader_size(&reader_ctx),
split_stream_reader_read, &reader_ctx);
else
len = pf->pf_gen_crypto_frame(
new_packet_out->po_data + new_packet_out->po_data_sz,
lsquic_packet_out_avail(new_packet_out),
frame.data_frame.df_offset + frame.data_frame.df_size / 2,
split_reader_size(&reader_ctx),
split_crypto_reader_read, &reader_ctx);
if (len < 0)
{
LSQ_ERROR("could not generate new frame 1");
return -1;
}
if (0 != lsquic_packet_out_add_stream(new_packet_out, mm,
max_srec->sr_stream, max_srec->sr_frame_type,
new_packet_out->po_data_sz, len))
return -1;
new_packet_out->po_data_sz += len;
if (0 == lsquic_packet_out_avail(new_packet_out))
{
assert(0); /* We really should not fill here, but JIC */
new_packet_out->po_flags |= PO_STREAM_END;
}
memcpy(reader_ctx.buf, frame.data_frame.df_data,
frame.data_frame.df_size / 2);
reader_ctx.off = 0;
reader_ctx.len = frame.data_frame.df_size / 2;
reader_ctx.fin = 0;
if (frame_type == QUIC_FRAME_STREAM)
len = pf->pf_gen_stream_frame(
packet_out->po_data + max_srec->sr_off, max_srec->sr_len,
frame.stream_id, frame.data_frame.df_offset,
split_reader_fin(&reader_ctx), split_reader_size(&reader_ctx),
split_stream_reader_read, &reader_ctx);
else
len = pf->pf_gen_crypto_frame(
packet_out->po_data + max_srec->sr_off, max_srec->sr_len,
frame.data_frame.df_offset,
split_reader_size(&reader_ctx),
split_crypto_reader_read, &reader_ctx);
if (len < 0)
{
LSQ_ERROR("could not generate new frame 2");
return -1;
}
const unsigned short adj = max_srec->sr_len - (unsigned short) len;
max_srec->sr_len = len;
for (n = max_idx + 1; n < n_srecs; ++n)
srecs[n]->sr_off -= adj;
packet_out->po_data_sz -= adj;
return 0;
}
#ifndef NDEBUG
static void
verify_srecs (lsquic_packet_out_t *packet_out, enum quic_frame_type frame_type)
{
struct packet_out_srec_iter posi;
const struct stream_rec *srec;
unsigned off;
srec = lsquic_posi_first(&posi, packet_out);
assert(srec);
off = 0;
for ( ; srec; srec = lsquic_posi_next(&posi))
{
assert(srec->sr_off == off);
assert(srec->sr_frame_type == frame_type);
off += srec->sr_len;
}
assert(packet_out->po_data_sz == off);
}
#endif
int
lsquic_packet_out_split_in_two (struct lsquic_mm *mm,
lsquic_packet_out_t *packet_out, lsquic_packet_out_t *new_packet_out,
const struct parse_funcs *pf, unsigned excess_bytes)
{
struct packet_out_srec_iter posi;
struct stream_rec *local_arr[4];
struct stream_rec **new_srecs, **srecs = local_arr;
struct stream_rec *srec;
unsigned n_srecs_alloced = sizeof(local_arr) / sizeof(local_arr[0]);
unsigned n_srecs, max_idx, n, nbytes;
enum quic_frame_type frame_type;
#ifndef NDEBUG
unsigned short frame_sum = 0;
#endif
int rv;
/* We only split buffered packets or initial packets with CRYPTO frames.
* Either contain just one frame type: STREAM or CRYPTO.
*/
assert(packet_out->po_frame_types == (1 << QUIC_FRAME_STREAM)
|| packet_out->po_frame_types == (1 << QUIC_FRAME_CRYPTO));
if (packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM))
frame_type = QUIC_FRAME_STREAM;
else
frame_type = QUIC_FRAME_CRYPTO;
n_srecs = 0;
#ifdef WIN32
max_idx = 0;
#endif
for (srec = lsquic_posi_first(&posi, packet_out); srec;
srec = lsquic_posi_next(&posi))
{
assert(srec->sr_frame_type == QUIC_FRAME_STREAM
|| srec->sr_frame_type == QUIC_FRAME_CRYPTO);
if (n_srecs >= n_srecs_alloced)
{
n_srecs_alloced *= 2;
if (srecs == local_arr)
{
srecs = malloc(sizeof(srecs[0]) * n_srecs_alloced);
if (!srecs)
goto err;
memcpy(srecs, local_arr, sizeof(local_arr));
}
else
{
new_srecs = realloc(srecs, sizeof(srecs[0]) * n_srecs_alloced);
if (!new_srecs)
goto err;
srecs = new_srecs;
}
}
#ifndef NDEBUG
frame_sum += srec->sr_len;
#endif
if (n_srecs == 0 || srecs[max_idx]->sr_len < srec->sr_len)
max_idx = n_srecs;
srecs[n_srecs++] = srec;
}
assert(frame_sum == packet_out->po_data_sz);
if (n_srecs == 1)
goto common_case;
if (n_srecs < 1)
goto err;
/* Case 1: see if we can remove one or more trailing frames to make
* packet smaller.
*/
nbytes = 0;
for (n = n_srecs - 1; n > max_idx && nbytes < excess_bytes; --n)
nbytes += srecs[n]->sr_len;
if (nbytes >= excess_bytes)
{
rv = split_off_last_frames(mm, packet_out, new_packet_out,
srecs + n + 1, n_srecs - n - 1, frame_type);
goto end;
}
/* Case 2: see if we can move the largest frame to new packet. */
nbytes = 0;
for (n = 0; n < n_srecs; ++n)
if (n != max_idx)
nbytes += srecs[n]->sr_len;
if (nbytes >= excess_bytes)
{
rv = move_largest_frame(mm, packet_out, new_packet_out, srecs,
n_srecs, max_idx, frame_type);
goto end;
}
common_case:
/* Case 3: we have to split the largest frame (which could be the
* the only frame) in two.
*/
rv = split_largest_frame(mm, packet_out, new_packet_out, pf, srecs,
n_srecs, max_idx, frame_type);
end:
if (srecs != local_arr)
free(srecs);
if (0 == rv)
{
new_packet_out->po_frame_types |= 1 << frame_type;
#ifndef NDEBUG
verify_srecs(packet_out, frame_type);
verify_srecs(new_packet_out, frame_type);
#endif
}
return rv;
err:
rv = -1;
goto end;
} }
@ -746,7 +420,7 @@ lsquic_packet_out_zero_pad (lsquic_packet_out_t *packet_out)
size_t size_t
lsquic_packet_out_mem_used (const struct lsquic_packet_out *packet_out) lsquic_packet_out_mem_used (const struct lsquic_packet_out *packet_out)
{ {
const struct stream_rec_arr *srec_arr; const struct frame_rec_arr *frec_arr;
size_t size; size_t size;
size = 0; /* The struct is allocated using malo */ size = 0; /* The struct is allocated using malo */
@ -757,9 +431,9 @@ lsquic_packet_out_mem_used (const struct lsquic_packet_out *packet_out)
if (packet_out->po_nonce) if (packet_out->po_nonce)
size += 32; size += 32;
if (packet_out->po_flags & PO_SREC_ARR) if (packet_out->po_flags & PO_FREC_ARR)
TAILQ_FOREACH(srec_arr, &packet_out->po_srecs.arr, next_stream_rec_arr) TAILQ_FOREACH(frec_arr, &packet_out->po_frecs.arr, next_stream_rec_arr)
size += sizeof(*srec_arr); size += sizeof(*frec_arr);
return size; return size;
} }
@ -770,19 +444,19 @@ lsquic_packet_out_turn_on_fin (struct lsquic_packet_out *packet_out,
const struct parse_funcs *pf, const struct parse_funcs *pf,
const struct lsquic_stream *stream) const struct lsquic_stream *stream)
{ {
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
const struct stream_rec *srec; const struct frame_rec *frec;
struct stream_frame stream_frame; struct stream_frame stream_frame;
uint64_t last_offset; uint64_t last_offset;
int len; int len;
for (srec = lsquic_posi_first(&posi, packet_out); srec; for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
srec = lsquic_posi_next(&posi)) frec = lsquic_pofi_next(&pofi))
if (srec->sr_frame_type == QUIC_FRAME_STREAM if (frec->fe_frame_type == QUIC_FRAME_STREAM
&& srec->sr_stream == stream) && frec->fe_stream == stream)
{ {
len = pf->pf_parse_stream_frame(packet_out->po_data + srec->sr_off, len = pf->pf_parse_stream_frame(packet_out->po_data + frec->fe_off,
srec->sr_len, &stream_frame); frec->fe_len, &stream_frame);
assert(len >= 0); assert(len >= 0);
if (len < 0) if (len < 0)
return -1; return -1;
@ -790,10 +464,10 @@ lsquic_packet_out_turn_on_fin (struct lsquic_packet_out *packet_out,
+ stream_frame.data_frame.df_size; + stream_frame.data_frame.df_size;
if (last_offset == stream->tosend_off) if (last_offset == stream->tosend_off)
{ {
pf->pf_turn_on_fin(packet_out->po_data + srec->sr_off); pf->pf_turn_on_fin(packet_out->po_data + frec->fe_off);
EV_LOG_UPDATED_STREAM_FRAME( EV_LOG_UPDATED_STREAM_FRAME(
lsquic_conn_log_cid(lsquic_stream_conn(stream)), lsquic_conn_log_cid(lsquic_stream_conn(stream)),
pf, packet_out->po_data + srec->sr_off, srec->sr_len); pf, packet_out->po_data + frec->fe_off, frec->fe_len);
return 0; return 0;
} }
} }

View file

@ -17,14 +17,15 @@ struct network_path;
struct parse_funcs; struct parse_funcs;
struct bwp_state; struct bwp_state;
/* Each stream_rec is associated with one packet_out. packet_out can have /* Each frame_rec is associated with one packet_out. packet_out can have
* zero or more stream_rec structures. stream_rec keeps a pointer to a stream * zero or more frame_rec structures. frame_rec keeps a pointer to a stream
* that has STREAM or RST_STREAM frames inside packet_out. `sr_frame_type' * that has STREAM, CRYPTO, or RST_STREAM frames inside packet_out.
* specifies the type of the frame; if this value is zero, values of the * `fe_frame_type' specifies the type of the frame; if this value is zero
* other struct members are not valid. `sr_off' indicates where inside * (this happens when a frame is elided), values of the other struct members
* packet_out->po_data the frame begins and `sr_len' is its length. * are not valid. `fe_off' indicates where inside packet_out->po_data the
* frame begins and `fe_len' is its length.
* *
* We need this information for three reasons: * We need this information for four reasons:
* 1. A stream is not destroyed until all of its STREAM and RST_STREAM * 1. A stream is not destroyed until all of its STREAM and RST_STREAM
* frames are acknowledged. This is to make sure that we do not exceed * frames are acknowledged. This is to make sure that we do not exceed
* maximum allowed number of streams. * maximum allowed number of streams.
@ -34,27 +35,37 @@ struct bwp_state;
* occurs if we guessed incorrectly the number of bytes required to * occurs if we guessed incorrectly the number of bytes required to
* encode the packet number and the actual number would make packet * encode the packet number and the actual number would make packet
* larger than the max). * larger than the max).
* 4. A lost or scheduled packet may need to be resized (down) when path
* changes or MTU is reduced due to an RTO.
* *
* In IETF, all frames are recorded. In gQUIC, only STREAM, RST_STREAM,
* ACK, and STOP_WAITING are recorded. The latter two are done so that
* ACK-deleting code in send controller (see po_regen_sz) is the same for
* both QUIC versions.
*/ */
struct stream_rec { struct frame_rec {
struct lsquic_stream *sr_stream; union {
unsigned short sr_off, struct lsquic_stream *stream;
sr_len; uintptr_t data;
enum quic_frame_type sr_frame_type:16; } fe_u;
#define fe_stream fe_u.stream
unsigned short fe_off,
fe_len;
enum quic_frame_type fe_frame_type;
}; };
#define srec_taken(srec) ((srec)->sr_frame_type) #define frec_taken(frec) ((frec)->fe_frame_type)
struct stream_rec_arr { struct frame_rec_arr {
TAILQ_ENTRY(stream_rec_arr) next_stream_rec_arr; TAILQ_ENTRY(frame_rec_arr) next_stream_rec_arr;
struct stream_rec srecs[ struct frame_rec frecs[
( 64 /* Efficient size for malo allocator */ ( 64 /* Efficient size for malo allocator */
- sizeof(TAILQ_ENTRY(stream_rec)) /* next_stream_rec_arr */ - sizeof(TAILQ_ENTRY(frame_rec)) /* next_stream_rec_arr */
) / sizeof(struct stream_rec) ) / sizeof(struct frame_rec)
]; ];
}; };
TAILQ_HEAD(stream_rec_arr_tailq, stream_rec_arr); TAILQ_HEAD(frame_rec_arr_tailq, frame_rec_arr);
typedef struct lsquic_packet_out typedef struct lsquic_packet_out
@ -76,14 +87,14 @@ typedef struct lsquic_packet_out
enum packet_out_flags { enum packet_out_flags {
/* TODO XXX Phase out PO_MINI in favor of a more specialized flag: /* TODO XXX Phase out PO_MINI in favor of a more specialized flag:
* we only need an indicator that a packet contains STREAM frames * we only need an indicator that a packet contains STREAM frames
* but no associated srecs. This type of packets in only created * but no associated frecs. This type of packets in only created
* by GQUIC mini conn. * by GQUIC mini conn.
*/ */
PO_MINI = (1 << 0), /* Allocated by mini connection */ PO_MINI = (1 << 0), /* Allocated by mini connection */
PO_HELLO = (1 << 1), /* Packet contains SHLO or CHLO data */ PO_HELLO = (1 << 1), /* Packet contains SHLO or CHLO data */
PO_SENT = (1 << 2), /* Packet has been sent (mini only) */ PO_SENT = (1 << 2), /* Packet has been sent (mini only) */
PO_ENCRYPTED= (1 << 3), /* po_enc_data has encrypted data */ PO_ENCRYPTED= (1 << 3), /* po_enc_data has encrypted data */
PO_SREC_ARR = (1 << 4), PO_FREC_ARR = (1 << 4),
#define POBIT_SHIFT 5 #define POBIT_SHIFT 5
PO_BITS_0 = (1 << 5), /* PO_BITS_0 and PO_BITS_1 encode the */ PO_BITS_0 = (1 << 5), /* PO_BITS_0 and PO_BITS_1 encode the */
PO_BITS_1 = (1 << 6), /* packet number length. See macros below. */ PO_BITS_1 = (1 << 6), /* packet number length. See macros below. */
@ -104,7 +115,7 @@ typedef struct lsquic_packet_out
PO_IPv6 = (1 <<20), /* Set if pmi_allocate was passed is_ipv6=1, PO_IPv6 = (1 <<20), /* Set if pmi_allocate was passed is_ipv6=1,
* otherwise unset. * otherwise unset.
*/ */
PO_LIMITED = (1 <<21), /* Used to credit sc_next_limit if needed. */ PO_MTU_PROBE= (1 <<21), /* Special loss and ACK rules apply */
#define POPNS_SHIFT 22 #define POPNS_SHIFT 22
PO_PNS_HSK = (1 <<22), /* PNS bits contain the value of the */ PO_PNS_HSK = (1 <<22), /* PNS bits contain the value of the */
PO_PNS_APP = (1 <<23), /* packet number space. */ PO_PNS_APP = (1 <<23), /* packet number space. */
@ -124,6 +135,11 @@ typedef struct lsquic_packet_out
unsigned short po_data_sz; /* Number of usable bytes in data */ unsigned short po_data_sz; /* Number of usable bytes in data */
unsigned short po_enc_data_sz; /* Number of usable bytes in data */ unsigned short po_enc_data_sz; /* Number of usable bytes in data */
unsigned short po_sent_sz; /* If PO_SENT_SZ is set, real size of sent buffer. */ unsigned short po_sent_sz; /* If PO_SENT_SZ is set, real size of sent buffer. */
/* TODO Revisit po_regen_sz once gQUIC is dropped. Now that all frames
* are recorded, we have more flexibility where to place ACK frames; they
* no longer really have to be at the beginning of the packet, since we
* can locate them.
*/
unsigned short po_regen_sz; /* Number of bytes at the beginning unsigned short po_regen_sz; /* Number of bytes at the beginning
* of data containing bytes that are * of data containing bytes that are
* not to be retransmitted, e.g. ACK * not to be retransmitted, e.g. ACK
@ -132,7 +148,6 @@ typedef struct lsquic_packet_out
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */ unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
unsigned short po_token_len; unsigned short po_token_len;
enum header_type po_header_type:8; enum header_type po_header_type:8;
unsigned char po_path_id;
enum { enum {
POL_GQUIC = 1 << 0, /* Used for logging */ POL_GQUIC = 1 << 0, /* Used for logging */
#define POLEV_SHIFT 1 #define POLEV_SHIFT 1
@ -149,18 +164,18 @@ typedef struct lsquic_packet_out
#ifndef NDEBUG #ifndef NDEBUG
POL_HEADER_PROT = 1 << 9, /* Header protection applied */ POL_HEADER_PROT = 1 << 9, /* Header protection applied */
#endif #endif
POL_LIMITED = 1 << 10, /* Used to credit sc_next_limit if needed. */
} po_lflags:16; } po_lflags:16;
unsigned char *po_data; unsigned char *po_data;
/* A lot of packets contain data belonging to only one stream. Thus, /* A lot of packets contain only one frame. Thus, `one' is used first.
* `one' is used first. If this is not enough, any number of * If this is not enough, any number of frame_rec_arr structures can be
* stream_rec_arr structures can be allocated to handle more stream * allocated to handle more frame records.
* records.
*/ */
union { union {
struct stream_rec one; struct frame_rec one;
struct stream_rec_arr_tailq arr; struct frame_rec_arr_tailq arr;
} po_srecs; } po_frecs;
/* If PO_ENCRYPTED is set, this points to the buffer that holds encrypted /* If PO_ENCRYPTED is set, this points to the buffer that holds encrypted
* data. * data.
@ -274,19 +289,19 @@ typedef struct lsquic_packet_out
#define lsquic_packet_out_ecn(p) (((p)->po_lflags >> POECN_SHIFT) & 3) #define lsquic_packet_out_ecn(p) (((p)->po_lflags >> POECN_SHIFT) & 3)
struct packet_out_srec_iter { struct packet_out_frec_iter {
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
struct stream_rec_arr *cur_srec_arr; struct frame_rec_arr *cur_frec_arr;
unsigned srec_idx; unsigned frec_idx;
int impl_idx; int impl_idx;
}; };
struct stream_rec * struct frame_rec *
lsquic_posi_first (struct packet_out_srec_iter *posi, lsquic_packet_out_t *); lsquic_pofi_first (struct packet_out_frec_iter *pofi, lsquic_packet_out_t *);
struct stream_rec * struct frame_rec *
lsquic_posi_next (struct packet_out_srec_iter *posi); lsquic_pofi_next (struct packet_out_frec_iter *pofi);
lsquic_packet_out_t * lsquic_packet_out_t *
lsquic_packet_out_new (struct lsquic_mm *, struct malo *, int use_cid, lsquic_packet_out_new (struct lsquic_mm *, struct malo *, int use_cid,
@ -298,6 +313,11 @@ void
lsquic_packet_out_destroy (lsquic_packet_out_t *, lsquic_packet_out_destroy (lsquic_packet_out_t *,
struct lsquic_engine_public *, void *peer_ctx); struct lsquic_engine_public *, void *peer_ctx);
int
lsquic_packet_out_add_frame (struct lsquic_packet_out *,
struct lsquic_mm *, uintptr_t data, enum quic_frame_type,
unsigned short off, unsigned short len);
int int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out, lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
struct lsquic_mm *mm, struct lsquic_mm *mm,
@ -309,10 +329,6 @@ unsigned
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *, lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *,
lsquic_stream_id_t); lsquic_stream_id_t);
int
lsquic_packet_out_split_in_two (struct lsquic_mm *, lsquic_packet_out_t *,
lsquic_packet_out_t *, const struct parse_funcs *, unsigned excess_bytes);
void void
lsquic_packet_out_chop_regen (lsquic_packet_out_t *); lsquic_packet_out_chop_regen (lsquic_packet_out_t *);

View file

@ -0,0 +1,258 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/* Functions to resize packets */
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_packet_resize.h"
#include "lsquic_parse.h"
#include "lsquic_hash.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_conn.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PACKET_RESIZE
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(prctx->prc_conn)
#include "lsquic_logger.h"
void
lsquic_packet_resize_init (struct packet_resize_ctx *prctx,
struct lsquic_engine_public *enpub, struct lsquic_conn *lconn, void *ctx,
const struct packet_resize_if *pr_if)
{
memset(prctx, 0, sizeof(*prctx));
prctx->prc_conn = lconn;
prctx->prc_pri = pr_if;
prctx->prc_enpub = enpub;
prctx->prc_data = ctx;
LSQ_DEBUG("initialized");
}
static const struct frame_rec *
packet_resize_next_frec (struct packet_resize_ctx *prctx)
{
const struct frame_rec *frec;
assert(!prctx->prc_cur_frec);
if (prctx->prc_cur_packet)
{
LSQ_DEBUG("get next frec from current packet %"PRIu64,
prctx->prc_cur_packet->po_packno);
frec = lsquic_pofi_next(&prctx->prc_pofi);
if (frec)
return frec;
LSQ_DEBUG("discard packet %"PRIu64, prctx->prc_cur_packet->po_packno);
prctx->prc_pri->pri_discard_packet(prctx->prc_data,
prctx->prc_cur_packet);
prctx->prc_cur_packet = NULL; /* Not necessary; just future-proofing */
}
prctx->prc_cur_packet = prctx->prc_pri->pri_next_packet(prctx->prc_data);
if (!prctx->prc_cur_packet)
{
LSQ_DEBUG("out of input packets");
return NULL;
}
frec = lsquic_pofi_first(&prctx->prc_pofi, prctx->prc_cur_packet);
assert(frec);
LSQ_DEBUG("return first frec from new current packet %"PRIu64,
prctx->prc_cur_packet->po_packno);
return frec;
}
static const struct frame_rec *
packet_resize_get_frec (struct packet_resize_ctx *prctx)
{
if (!prctx->prc_cur_frec)
{
prctx->prc_cur_frec = packet_resize_next_frec(prctx);
if (prctx->prc_cur_frec)
prctx->prc_flags |= PRC_NEW_FREC;
}
return prctx->prc_cur_frec;
}
static size_t
packet_resize_gsf_read (void *ctx, void *buf, size_t len, int *fin)
{
struct packet_resize_ctx *const prctx = ctx;
size_t left;
left = (size_t) prctx->prc_data_frame.df_size
- (size_t) prctx->prc_data_frame.df_read_off;
if (len > left)
len = left;
memcpy(buf,
prctx->prc_data_frame.df_data + prctx->prc_data_frame.df_read_off, len);
prctx->prc_data_frame.df_read_off += len;
*fin = prctx->prc_data_frame.df_fin
&& prctx->prc_data_frame.df_size == prctx->prc_data_frame.df_read_off;
return len;
}
struct lsquic_packet_out *
lsquic_packet_resize_next (struct packet_resize_ctx *prctx)
{
const unsigned char *data_in;
struct lsquic_packet_out *new;
struct stream_frame stream_frame;
const struct frame_rec *frec;
int s, w, fin, parsed_len;
size_t nbytes;
if (frec = packet_resize_get_frec(prctx), frec == NULL)
return NULL;
new = prctx->prc_pri->pri_new_packet(prctx->prc_data);
if (!new)
{
LSQ_DEBUG("cannot allocate new packet");
goto err;
}
proc_frec:
if ((1 << frec->fe_frame_type) & (QUIC_FTBIT_STREAM|QUIC_FTBIT_CRYPTO))
{
if (prctx->prc_flags & PRC_NEW_FREC)
{
data_in = prctx->prc_cur_packet->po_data + frec->fe_off;
parsed_len = (&prctx->prc_conn->cn_pf->pf_parse_stream_frame)
[frec->fe_frame_type == QUIC_FRAME_CRYPTO]
(data_in, frec->fe_len, &stream_frame);
if (parsed_len < 0)
{
LSQ_WARN("cannot parse %s frame",
frame_type_2_str[frec->fe_frame_type]);
goto err;
}
if ((unsigned) parsed_len != frec->fe_len)
{
LSQ_WARN("parsed %s frame size does not match frame record",
frame_type_2_str[frec->fe_frame_type]);
goto err;
}
prctx->prc_data_frame = stream_frame.data_frame;
prctx->prc_flags &= ~PRC_NEW_FREC;
LSQ_DEBUG("parsed %s frame record for stream %"PRIu64
"; off: %"PRIu64"; size: %"PRIu16"; fin: %d",
frame_type_2_str[frec->fe_frame_type],
frec->fe_stream->id,
stream_frame.data_frame.df_offset,
stream_frame.data_frame.df_size,
stream_frame.data_frame.df_fin);
}
fin = prctx->prc_data_frame.df_fin
&& prctx->prc_data_frame.df_read_off == prctx->prc_data_frame.df_size;
nbytes = prctx->prc_data_frame.df_size - prctx->prc_data_frame.df_read_off;
w = (&prctx->prc_conn->cn_pf->pf_gen_stream_frame)
[frec->fe_frame_type == QUIC_FRAME_CRYPTO](
new->po_data + new->po_data_sz, lsquic_packet_out_avail(new),
frec->fe_stream->id,
prctx->prc_data_frame.df_offset + prctx->prc_data_frame.df_read_off,
fin, nbytes, packet_resize_gsf_read, prctx);
if (w < 0)
{
/* We rely on stream-generating function returning an error instead
* of pre-calculating required size and checking.
*/
LSQ_DEBUG("cannot fit another %s frame, new packet done",
frame_type_2_str[frec->fe_frame_type]);
goto done;
}
if (0 != lsquic_packet_out_add_stream(new, &prctx->prc_enpub->enp_mm,
frec->fe_stream, frec->fe_frame_type,
new->po_data_sz, w))
{
LSQ_WARN("cannot add stream frame record to new packet");
goto err;
}
new->po_data_sz += w;
new->po_frame_types |= 1 << frec->fe_frame_type;
if (0 == lsquic_packet_out_avail(new))
new->po_flags |= PO_STREAM_END;
if (prctx->prc_data_frame.df_size == prctx->prc_data_frame.df_read_off)
{
LSQ_DEBUG("finished using %s frame record",
frame_type_2_str[frec->fe_frame_type]);
--frec->fe_stream->n_unacked;
frec = prctx->prc_cur_frec = NULL;
if (lsquic_packet_out_avail(new) > 0)
if (frec = packet_resize_get_frec(prctx), frec != NULL)
goto proc_frec;
}
}
else if (prctx->prc_cur_frec->fe_len <= lsquic_packet_out_avail(new))
{
if ((1 << frec->fe_frame_type) & GQUIC_FRAME_REGEN_MASK)
{
if (new->po_regen_sz == new->po_data_sz)
new->po_regen_sz += frec->fe_len;
else
{
LSQ_DEBUG("got non-contiguous regen frame %s, packet done",
frame_type_2_str[frec->fe_frame_type]);
goto done;
}
}
memcpy(new->po_data + new->po_data_sz,
prctx->prc_cur_packet->po_data + frec->fe_off, frec->fe_len);
if (frec->fe_frame_type == QUIC_FRAME_RST_STREAM)
s = lsquic_packet_out_add_stream(new, &prctx->prc_enpub->enp_mm,
frec->fe_stream, frec->fe_frame_type,
new->po_data_sz, frec->fe_len);
else
s = lsquic_packet_out_add_frame(new, &prctx->prc_enpub->enp_mm,
frec->fe_u.data, frec->fe_frame_type,
new->po_data_sz, frec->fe_len);
if (s != 0)
{
LSQ_WARN("cannot add %s frame record to new packet",
frame_type_2_str[frec->fe_frame_type]);
goto err;
}
new->po_data_sz += frec->fe_len;
new->po_frame_types |= 1 << frec->fe_frame_type;
LSQ_DEBUG("copy %hu-byte %s frame into new packet", frec->fe_len,
frame_type_2_str[frec->fe_frame_type]);
if (frec->fe_frame_type == QUIC_FRAME_RST_STREAM)
--frec->fe_stream->n_unacked;
frec = prctx->prc_cur_frec = NULL;
if (lsquic_packet_out_avail(new) > 0)
if (frec = packet_resize_get_frec(prctx), frec != NULL)
goto proc_frec;
}
done:
if (0 == new->po_data_sz)
{
LSQ_WARN("frame too large");
goto err;
}
return new;
err:
if (new)
lsquic_packet_out_destroy(new, prctx->prc_enpub,
new->po_path->np_peer_ctx);
prctx->prc_flags |= PRC_ERROR;
return NULL;
}

View file

@ -0,0 +1,53 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_packet_resize.h -- functions to resize packets
*/
#ifndef LSQUIC_PACKET_RESIZE_H
#define LSQUIC_PACKET_RESIZE_H 1
struct lsquic_packet_out;
struct lsquic_conn;
struct frame_rec;
struct lsquic_engine_public;
struct packet_resize_if
{
/* Get next packet to convert */
struct lsquic_packet_out *
(*pri_next_packet)(void *ctx);
/* Discard packet after it was converted */
void (*pri_discard_packet)(void *ctx, struct lsquic_packet_out *);
/* Get new packet to write frames to */
struct lsquic_packet_out *
(*pri_new_packet)(void *ctx);
};
struct packet_resize_ctx
{
const struct lsquic_conn *prc_conn;
void *prc_data; /* First arg to prc_pri */
const struct packet_resize_if *prc_pri;
struct lsquic_engine_public *prc_enpub;
const struct frame_rec *prc_cur_frec;
struct lsquic_packet_out *prc_cur_packet;
struct data_frame prc_data_frame;
struct packet_out_frec_iter prc_pofi;
enum {
PRC_ERROR = 1 << 0,
PRC_NEW_FREC = 1 << 1,
} prc_flags;
};
void
lsquic_packet_resize_init (struct packet_resize_ctx *,
struct lsquic_engine_public *, struct lsquic_conn *, void *ctx,
const struct packet_resize_if *);
struct lsquic_packet_out *
lsquic_packet_resize_next (struct packet_resize_ctx *);
#define lsquic_packet_resize_is_error(prctx_) \
(!!((prctx_)->prc_flags & PRC_ERROR))
#endif

View file

@ -58,9 +58,6 @@ typedef lsquic_time_t
/* gsf_: generate stream frame */ /* gsf_: generate stream frame */
typedef size_t (*gsf_read_f) (void *stream, void *buf, size_t len, int *fin); typedef size_t (*gsf_read_f) (void *stream, void *buf, size_t len, int *fin);
/* gcf_: generate CRYPTO frame */
typedef size_t (*gcf_read_f) (void *stream, void *buf, size_t len);
/* This structure contains functions that parse and generate packets and /* This structure contains functions that parse and generate packets and
* frames in version-specific manner. To begin with, there is difference * frames in version-specific manner. To begin with, there is difference
* between GQUIC's little-endian (Q038 and lower) and big-endian formats * between GQUIC's little-endian (Q038 and lower) and big-endian formats
@ -83,10 +80,23 @@ struct parse_funcs
* exception is -1, which is a generic error code, as we always need * exception is -1, which is a generic error code, as we always need
* more than 1 byte to write a STREAM frame. * more than 1 byte to write a STREAM frame.
*/ */
/* pf_gen_stream_frame and pf_gen_crypto_frame must be adjacent so that
* they can be cast to an array.
*/
int int
(*pf_gen_stream_frame) (unsigned char *buf, size_t bufsz, (*pf_gen_stream_frame) (unsigned char *buf, size_t bufsz,
lsquic_stream_id_t stream_id, uint64_t offset, lsquic_stream_id_t stream_id, uint64_t offset,
int fin, size_t size, gsf_read_f, void *stream); int fin, size_t size, gsf_read_f, void *stream);
/* The two "UNUSED" parameters are here so that it matches
* pf_gen_stream_frame.
*/
int
(*pf_gen_crypto_frame) (unsigned char *buf, size_t bufsz,
lsquic_stream_id_t UNUSED_1, uint64_t offset,
int UNUSED_2, size_t size, gsf_read_f, void *stream);
/* pf_parse_stream_frame and pf_parse_crypto_frame must be adjacent so that
* they can be cast to an array.
*/
int int
(*pf_parse_stream_frame) (const unsigned char *buf, size_t rem_packet_sz, (*pf_parse_stream_frame) (const unsigned char *buf, size_t rem_packet_sz,
struct stream_frame *); struct stream_frame *);
@ -94,9 +104,6 @@ struct parse_funcs
(*pf_parse_crypto_frame) (const unsigned char *buf, size_t rem_packet_sz, (*pf_parse_crypto_frame) (const unsigned char *buf, size_t rem_packet_sz,
struct stream_frame *); struct stream_frame *);
int int
(*pf_gen_crypto_frame) (unsigned char *buf, size_t bufsz, uint64_t offset,
size_t size, gcf_read_f, void *stream);
int
(*pf_parse_ack_frame) (const unsigned char *buf, size_t buf_len, (*pf_parse_ack_frame) (const unsigned char *buf, size_t buf_len,
struct ack_info *ack_info, uint8_t exp); struct ack_info *ack_info, uint8_t exp);
int int

View file

@ -264,7 +264,8 @@ gquic_Q046_parse_packet_in_finish (struct lsquic_packet_in *packet_in,
static int static int
gquic_Q046_gen_crypto_frame (unsigned char *buf, size_t buf_len, gquic_Q046_gen_crypto_frame (unsigned char *buf, size_t buf_len,
uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) lsquic_stream_id_t stream_id, uint64_t offset, int fin, size_t size,
gsf_read_f gsf_read, void *stream)
{ {
assert(0); assert(0);
return -1; return -1;

View file

@ -796,10 +796,11 @@ gquic_Q050_parse_frame_type (const unsigned char *buf, size_t len)
static int static int
gquic_Q050_gen_crypto_frame (unsigned char *buf, size_t buf_len, gquic_Q050_gen_crypto_frame (unsigned char *buf, size_t buf_len,
uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) lsquic_stream_id_t stream_id, uint64_t offset, int fin,
size_t size, gsf_read_f gsf_read, void *stream)
{ {
return lsquic_ietf_v1_gen_crypto_frame(buf, 0x8, buf_len, offset, return lsquic_ietf_v1_gen_crypto_frame(buf, 0x8, stream_id, buf_len,
size, gcf_read, stream); offset, fin, size, gsf_read, stream);
} }

View file

@ -986,7 +986,8 @@ lsquic_gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
static int static int
lsquic_gquic_be_gen_crypto_frame (unsigned char *buf, size_t buf_len, lsquic_gquic_be_gen_crypto_frame (unsigned char *buf, size_t buf_len,
uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) lsquic_stream_id_t stream_if, uint64_t offset, int fin,
size_t size, gsf_read_f gsf_read, void *stream)
{ {
assert(0); assert(0);
return -1; return -1;

View file

@ -10,7 +10,7 @@ lsquic_ietf_v1_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_s
struct stream_frame *stream_frame); struct stream_frame *stream_frame);
int int
lsquic_ietf_v1_gen_crypto_frame (unsigned char *buf, unsigned char first_byte, lsquic_ietf_v1_gen_crypto_frame (unsigned char *buf, unsigned char first_byte,
size_t buf_len, uint64_t offset, size_t size, gcf_read_f gcf_read, size_t buf_len, lsquic_stream_id_t UNUSED_1, uint64_t offset,
void *stream); int UNUSED_2, size_t size, gsf_read_f gsf_read, void *stream);
#endif #endif

View file

@ -443,14 +443,15 @@ ietf_v1_gen_stream_frame (unsigned char *buf, size_t buf_len,
int int
lsquic_ietf_v1_gen_crypto_frame (unsigned char *buf, unsigned char first_byte, lsquic_ietf_v1_gen_crypto_frame (unsigned char *buf, unsigned char first_byte,
size_t buf_len, uint64_t offset, size_t size, gcf_read_f gcf_read, size_t buf_len, lsquic_stream_id_t UNUSED_1, uint64_t offset,
void *stream) int UNUSED_2, size_t size, gsf_read_f gsf_read, void *stream)
{ {
unsigned char *const end = buf + buf_len; unsigned char *const end = buf + buf_len;
unsigned char *p; unsigned char *p;
unsigned obits, dbits; unsigned obits, dbits;
unsigned olen, dlen; unsigned olen, dlen;
size_t nr, n_avail; size_t nr, n_avail;
int dummy_fin;
obits = vint_val2bits(offset); obits = vint_val2bits(offset);
olen = 1 << obits; olen = 1 << obits;
@ -470,7 +471,7 @@ lsquic_ietf_v1_gen_crypto_frame (unsigned char *buf, unsigned char first_byte,
vint_write(p, offset, obits, olen); vint_write(p, offset, obits, olen);
p += olen; p += olen;
nr = gcf_read(stream, p + dlen, size); nr = gsf_read(stream, p + dlen, size, &dummy_fin);
assert(nr != 0); /* This indicates error in the caller */ assert(nr != 0); /* This indicates error in the caller */
assert(nr <= size); /* This also indicates an error in the caller */ assert(nr <= size); /* This also indicates an error in the caller */
@ -483,10 +484,11 @@ lsquic_ietf_v1_gen_crypto_frame (unsigned char *buf, unsigned char first_byte,
static int static int
ietf_v1_gen_crypto_frame (unsigned char *buf, size_t buf_len, ietf_v1_gen_crypto_frame (unsigned char *buf, size_t buf_len,
uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) lsquic_stream_id_t stream_id, uint64_t offset, int fin,
size_t size, gsf_read_f gsf_read, void *stream)
{ {
return lsquic_ietf_v1_gen_crypto_frame(buf, 0x6, buf_len, offset, return lsquic_ietf_v1_gen_crypto_frame(buf, 0x6, buf_len, stream_id,
size, gcf_read, stream); offset, fin, size, gsf_read, stream);
} }

View file

@ -20,7 +20,9 @@
#include "lsquic_packet_common.h" #include "lsquic_packet_common.h"
#include "lsquic_alarmset.h" #include "lsquic_alarmset.h"
#include "lsquic_parse.h" #include "lsquic_parse.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h" #include "lsquic_packet_out.h"
#include "lsquic_packet_resize.h"
#include "lsquic_senhist.h" #include "lsquic_senhist.h"
#include "lsquic_rtt.h" #include "lsquic_rtt.h"
#include "lsquic_cubic.h" #include "lsquic_cubic.h"
@ -125,6 +127,9 @@ send_ctl_can_send_pre_hsk (struct lsquic_send_ctl *ctl);
static int static int
send_ctl_can_send (struct lsquic_send_ctl *ctl); send_ctl_can_send (struct lsquic_send_ctl *ctl);
static int
split_lost_packet (struct lsquic_send_ctl *, struct lsquic_packet_out *const);
#ifdef NDEBUG #ifdef NDEBUG
static static
#elif __GNUC__ #elif __GNUC__
@ -244,6 +249,7 @@ static void
retx_alarm_rings (enum alarm_id al_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now) retx_alarm_rings (enum alarm_id al_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now)
{ {
lsquic_send_ctl_t *ctl = ctx; lsquic_send_ctl_t *ctl = ctx;
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
enum packnum_space pns; enum packnum_space pns;
enum retx_mode rm; enum retx_mode rm;
@ -276,6 +282,8 @@ retx_alarm_rings (enum alarm_id al_id, void *ctx, lsquic_time_t expiry, lsquic_t
LSQ_DEBUG("packet RTO is %"PRIu64" usec", expiry); LSQ_DEBUG("packet RTO is %"PRIu64" usec", expiry);
send_ctl_expire(ctl, pns, EXFI_ALL); send_ctl_expire(ctl, pns, EXFI_ALL);
ctl->sc_ci->cci_timeout(CGP(ctl)); ctl->sc_ci->cci_timeout(CGP(ctl));
if (lconn->cn_if->ci_retx_timeout)
lconn->cn_if->ci_retx_timeout(lconn);
break; break;
} }
@ -479,14 +487,6 @@ set_retx_alarm (struct lsquic_send_ctl *ctl, enum packnum_space pns,
} }
static int
send_ctl_in_recovery (lsquic_send_ctl_t *ctl)
{
return ctl->sc_largest_acked_packno
&& ctl->sc_largest_acked_packno <= ctl->sc_largest_sent_at_cutback;
}
#define SC_PACK_SIZE(ctl_) (+(ctl_)->sc_conn_pub->path->np_pack_size) #define SC_PACK_SIZE(ctl_) (+(ctl_)->sc_conn_pub->path->np_pack_size)
static lsquic_time_t static lsquic_time_t
@ -840,11 +840,8 @@ send_ctl_record_loss (struct lsquic_send_ctl *ctl,
} }
/* Returns true if packet was rescheduled, false otherwise. In the latter
* case, you should not dereference packet_out after the function returns.
*/
static int static int
send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl, send_ctl_handle_regular_lost_packet (struct lsquic_send_ctl *ctl,
lsquic_packet_out_t *packet_out, struct lsquic_packet_out **next) lsquic_packet_out_t *packet_out, struct lsquic_packet_out **next)
{ {
unsigned packet_sz; unsigned packet_sz;
@ -895,6 +892,35 @@ send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
} }
static int
send_ctl_handle_lost_mtu_probe (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
unsigned packet_sz;
LSQ_DEBUG("lost MTU probe in packet %"PRIu64, packet_out->po_packno);
packet_sz = packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
assert(packet_out->po_loss_chain == packet_out);
send_ctl_destroy_packet(ctl, packet_out);
return 0;
}
/* Returns true if packet was rescheduled, false otherwise. In the latter
* case, you should not dereference packet_out after the function returns.
*/
static int
send_ctl_handle_lost_packet (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out, struct lsquic_packet_out **next)
{
if (0 == (packet_out->po_flags & PO_MTU_PROBE))
return send_ctl_handle_regular_lost_packet(ctl, packet_out, next);
else
return send_ctl_handle_lost_mtu_probe(ctl, packet_out);
}
static lsquic_packno_t static lsquic_packno_t
largest_retx_packet_number (const struct lsquic_send_ctl *ctl, largest_retx_packet_number (const struct lsquic_send_ctl *ctl,
enum packnum_space pns) enum packnum_space pns)
@ -936,13 +962,15 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
{ {
LSQ_DEBUG("loss by FACK detected, packet %"PRIu64, LSQ_DEBUG("loss by FACK detected, packet %"PRIu64,
packet_out->po_packno); packet_out->po_packno);
largest_lost_packno = packet_out->po_packno; if (0 == (packet_out->po_flags & PO_MTU_PROBE))
largest_lost_packno = packet_out->po_packno;
(void) send_ctl_handle_lost_packet(ctl, packet_out, &next); (void) send_ctl_handle_lost_packet(ctl, packet_out, &next);
continue; continue;
} }
if (largest_retx_packno if (largest_retx_packno
&& (packet_out->po_frame_types & ctl->sc_retx_frames) && (packet_out->po_frame_types & ctl->sc_retx_frames)
&& 0 == (packet_out->po_flags & PO_MTU_PROBE)
&& largest_retx_packno <= ctl->sc_largest_acked_packno) && largest_retx_packno <= ctl->sc_largest_acked_packno)
{ {
LSQ_DEBUG("loss by early retransmit detected, packet %"PRIu64, LSQ_DEBUG("loss by early retransmit detected, packet %"PRIu64,
@ -961,7 +989,8 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
{ {
LSQ_DEBUG("loss by sent time detected: packet %"PRIu64, LSQ_DEBUG("loss by sent time detected: packet %"PRIu64,
packet_out->po_packno); packet_out->po_packno);
if (packet_out->po_frame_types & ctl->sc_retx_frames) if ((packet_out->po_frame_types & ctl->sc_retx_frames)
&& 0 == (packet_out->po_flags & PO_MTU_PROBE))
largest_lost_packno = packet_out->po_packno; largest_lost_packno = packet_out->po_packno;
else { /* don't count it as a loss */; } else { /* don't count it as a loss */; }
(void) send_ctl_handle_lost_packet(ctl, packet_out, &next); (void) send_ctl_handle_lost_packet(ctl, packet_out, &next);
@ -989,6 +1018,20 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
} }
static void
send_ctl_mtu_probe_acked (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
LSQ_DEBUG("MTU probe in packet %"PRIu64" has been ACKed",
packet_out->po_packno);
assert(lconn->cn_if->ci_mtu_probe_acked);
if (lconn->cn_if->ci_mtu_probe_acked)
lconn->cn_if->ci_mtu_probe_acked(lconn, packet_out);
}
int int
lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl, lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
const struct ack_info *acki, const struct ack_info *acki,
@ -1092,7 +1135,8 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
ecn_total_acked += lsquic_packet_out_ecn(packet_out) != ECN_NOT_ECT; ecn_total_acked += lsquic_packet_out_ecn(packet_out) != ECN_NOT_ECT;
ecn_ce_cnt += lsquic_packet_out_ecn(packet_out) == ECN_CE; ecn_ce_cnt += lsquic_packet_out_ecn(packet_out) == ECN_CE;
one_rtt_cnt += lsquic_packet_out_enc_level(packet_out) == ENC_LEV_FORW; one_rtt_cnt += lsquic_packet_out_enc_level(packet_out) == ENC_LEV_FORW;
if (0 == (packet_out->po_flags & (PO_LOSS_REC|PO_POISON))) if (0 == (packet_out->po_flags
& (PO_LOSS_REC|PO_POISON|PO_MTU_PROBE)))
{ {
packet_sz = packet_out_sent_sz(packet_out); packet_sz = packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz); send_ctl_unacked_remove(ctl, packet_out, packet_sz);
@ -1111,6 +1155,12 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
++ctl->sc_conn_pub->conn_stats->out.acked_via_loss; ++ctl->sc_conn_pub->conn_stats->out.acked_via_loss;
#endif #endif
} }
else if (packet_out->po_flags & PO_MTU_PROBE)
{
packet_sz = packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
send_ctl_mtu_probe_acked(ctl, packet_out);
}
else else
{ {
LSQ_WARN("poisoned packet %"PRIu64" acked", LSQ_WARN("poisoned packet %"PRIu64" acked",
@ -1231,6 +1281,7 @@ lsquic_send_ctl_smallest_unacked (lsquic_send_ctl_t *ctl)
static struct lsquic_packet_out * static struct lsquic_packet_out *
send_ctl_next_lost (lsquic_send_ctl_t *ctl) send_ctl_next_lost (lsquic_send_ctl_t *ctl)
{ {
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
struct lsquic_packet_out *lost_packet; struct lsquic_packet_out *lost_packet;
get_next_lost: get_next_lost:
@ -1265,9 +1316,27 @@ send_ctl_next_lost (lsquic_send_ctl_t *ctl)
if (!lsquic_send_ctl_can_send(ctl)) if (!lsquic_send_ctl_can_send(ctl))
return NULL; return NULL;
TAILQ_REMOVE(&ctl->sc_lost_packets, lost_packet, po_next); if (packet_out_total_sz(lost_packet) <= SC_PACK_SIZE(ctl))
lost_packet->po_flags &= ~PO_LOST; {
lost_packet->po_flags |= PO_RETX; pop_lost_packet:
TAILQ_REMOVE(&ctl->sc_lost_packets, lost_packet, po_next);
lost_packet->po_flags &= ~PO_LOST;
lost_packet->po_flags |= PO_RETX;
}
else
{
/* We delay resizing lost packets as long as possible, hoping that
* it may be ACKed. At this point, however, we have to resize.
*/
if (0 == split_lost_packet(ctl, lost_packet))
{
lost_packet = TAILQ_FIRST(&ctl->sc_lost_packets);
goto pop_lost_packet;
}
lconn->cn_if->ci_internal_error(lconn,
"error resizing lost packet");
return NULL;
}
} }
return lost_packet; return lost_packet;
@ -1770,10 +1839,10 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
if (dec_limit) if (dec_limit)
{ {
--ctl->sc_next_limit; --ctl->sc_next_limit;
packet_out->po_flags |= PO_LIMITED; packet_out->po_lflags |= POL_LIMITED;
} }
else else
packet_out->po_flags &= ~PO_LIMITED; packet_out->po_lflags &= ~POL_LIMITED;
if (UNLIKELY(packet_out->po_header_type == HETY_INITIAL) if (UNLIKELY(packet_out->po_header_type == HETY_INITIAL)
&& !(ctl->sc_conn_pub->lconn->cn_flags & LSCONN_SERVER) && !(ctl->sc_conn_pub->lconn->cn_flags & LSCONN_SERVER)
@ -1812,7 +1881,7 @@ lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *ctl,
lsquic_packet_out_t *packet_out) lsquic_packet_out_t *packet_out)
{ {
send_ctl_sched_prepend(ctl, packet_out); send_ctl_sched_prepend(ctl, packet_out);
if (packet_out->po_flags & PO_LIMITED) if (packet_out->po_lflags & POL_LIMITED)
++ctl->sc_next_limit; ++ctl->sc_next_limit;
LSQ_DEBUG("packet %"PRIu64" has been delayed", packet_out->po_packno); LSQ_DEBUG("packet %"PRIu64" has been delayed", packet_out->po_packno);
#if LSQUIC_SEND_STATS #if LSQUIC_SEND_STATS
@ -1902,7 +1971,17 @@ send_ctl_allocate_packet (struct lsquic_send_ctl *ctl, enum packno_bits bits,
{ {
packet_out->po_header_type = HETY_INITIAL; packet_out->po_header_type = HETY_INITIAL;
if (ctl->sc_token) if (ctl->sc_token)
{
(void) send_ctl_set_packet_out_token(ctl, packet_out); (void) send_ctl_set_packet_out_token(ctl, packet_out);
if (packet_out->po_n_alloc > packet_out->po_token_len)
packet_out->po_n_alloc -= packet_out->po_token_len;
else
{
/* XXX fail earlier: when retry token is parsed out */
LSQ_INFO("token is too long: cannot allocate packet");
return NULL;
}
}
} }
else else
packet_out->po_header_type = HETY_HANDSHAKE; packet_out->po_header_type = HETY_HANDSHAKE;
@ -2607,38 +2686,112 @@ lsquic_send_ctl_packno_bits (lsquic_send_ctl_t *ctl)
} }
struct resize_one_packet_ctx
{
struct lsquic_send_ctl *const ctl;
struct lsquic_packet_out *const victim;
const struct network_path *const path;
const enum packnum_space pns;
int discarded, fetched;
};
static struct lsquic_packet_out *
resize_one_next_packet (void *ctx)
{
struct resize_one_packet_ctx *const one_ctx = ctx;
if (one_ctx->fetched)
return NULL;
++one_ctx->fetched;
return one_ctx->victim;
}
static void
resize_one_discard_packet (void *ctx, struct lsquic_packet_out *packet_out)
{
struct resize_one_packet_ctx *const one_ctx = ctx;
/* Delay discarding the packet: we need it for TAILQ_INSERT_BEFORE */
++one_ctx->discarded;
}
static struct lsquic_packet_out *
resize_one_new_packet (void *ctx)
{
struct resize_one_packet_ctx *const one_ctx = ctx;
struct lsquic_send_ctl *const ctl = one_ctx->ctl;
struct lsquic_packet_out *packet_out;
enum packno_bits bits;
bits = lsquic_send_ctl_calc_packno_bits(ctl);
packet_out = send_ctl_allocate_packet(ctl, bits, 0, one_ctx->pns,
one_ctx->path);
return packet_out;
}
static const struct packet_resize_if resize_one_funcs =
{
resize_one_next_packet,
resize_one_discard_packet,
resize_one_new_packet,
};
static int static int
split_buffered_packet (lsquic_send_ctl_t *ctl, split_buffered_packet (lsquic_send_ctl_t *ctl,
enum buf_packet_type packet_type, lsquic_packet_out_t *packet_out, enum buf_packet_type packet_type, struct lsquic_packet_out *packet_out)
enum packno_bits bits, unsigned excess_bytes)
{ {
struct buf_packet_q *const packet_q = struct buf_packet_q *const packet_q =
&ctl->sc_buffered_packets[packet_type]; &ctl->sc_buffered_packets[packet_type];
lsquic_packet_out_t *new_packet_out; struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
struct lsquic_packet_out *new;
struct packet_resize_ctx prctx;
struct resize_one_packet_ctx one_ctx = {
ctl, packet_out, packet_out->po_path,
lsquic_packet_out_pns(packet_out), 0, 0,
};
unsigned count;
assert(TAILQ_FIRST(&packet_q->bpq_packets) == packet_out); assert(TAILQ_FIRST(&packet_q->bpq_packets) == packet_out);
new_packet_out = send_ctl_allocate_packet(ctl, bits, 0, lsquic_packet_resize_init(&prctx, ctl->sc_enpub, lconn, &one_ctx,
lsquic_packet_out_pns(packet_out), packet_out->po_path); &resize_one_funcs);
if (!new_packet_out) count = 0;
return -1; while (new = lsquic_packet_resize_next(&prctx), new != NULL)
if (0 == lsquic_packet_out_split_in_two(&ctl->sc_enpub->enp_mm, packet_out,
new_packet_out, ctl->sc_conn_pub->lconn->cn_pf, excess_bytes))
{ {
lsquic_packet_out_set_packno_bits(packet_out, bits); ++count;
TAILQ_INSERT_AFTER(&packet_q->bpq_packets, packet_out, new_packet_out, TAILQ_INSERT_BEFORE(packet_out, new, po_next);
po_next);
++packet_q->bpq_count; ++packet_q->bpq_count;
LSQ_DEBUG("Add split packet to buffered queue #%u; count: %u", LSQ_DEBUG("Add split packet to buffered queue #%u; count: %u",
packet_type, packet_q->bpq_count); packet_type, packet_q->bpq_count);
return 0;
} }
else if (lsquic_packet_resize_is_error(&prctx))
{ {
send_ctl_destroy_packet(ctl, new_packet_out); LSQ_WARN("error resizing buffered packet #%"PRIu64,
packet_out->po_packno);
return -1; return -1;
} }
if (!(count > 1 && one_ctx.fetched == 1 && one_ctx.discarded == 1))
{
/* A bit of insurance, this being new code */
LSQ_WARN("unexpected values resizing buffered packet: count: %u; "
"fetched: %d; discarded: %d", count, one_ctx.fetched,
one_ctx.discarded);
return -1;
}
LSQ_DEBUG("added %u packets to the buffered queue #%u", count, packet_type);
LSQ_DEBUG("drop oversized buffered packet #%"PRIu64, packet_out->po_packno);
TAILQ_REMOVE(&packet_q->bpq_packets, packet_out, po_next);
++packet_q->bpq_count;
assert(packet_out->po_loss_chain == packet_out);
send_ctl_destroy_packet(ctl, packet_out);
return 0;
} }
@ -2650,7 +2803,7 @@ lsquic_send_ctl_schedule_buffered (lsquic_send_ctl_t *ctl,
&ctl->sc_buffered_packets[packet_type]; &ctl->sc_buffered_packets[packet_type];
const struct parse_funcs *const pf = ctl->sc_conn_pub->lconn->cn_pf; const struct parse_funcs *const pf = ctl->sc_conn_pub->lconn->cn_pf;
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
unsigned used, excess; unsigned used;
assert(lsquic_send_ctl_schedule_stream_packets_immediately(ctl)); assert(lsquic_send_ctl_schedule_stream_packets_immediately(ctl));
const enum packno_bits bits = lsquic_send_ctl_calc_packno_bits(ctl); const enum packno_bits bits = lsquic_send_ctl_calc_packno_bits(ctl);
@ -2685,12 +2838,10 @@ lsquic_send_ctl_schedule_buffered (lsquic_send_ctl_t *ctl,
if (need > used if (need > used
&& need - used > lsquic_packet_out_avail(packet_out)) && need - used > lsquic_packet_out_avail(packet_out))
{ {
excess = need - used - lsquic_packet_out_avail(packet_out); if (0 == split_buffered_packet(ctl, packet_type, packet_out))
if (0 != split_buffered_packet(ctl, packet_type, packet_out = TAILQ_FIRST(&packet_q->bpq_packets);
packet_out, bits, excess)) else
{
return -1; return -1;
}
} }
} }
TAILQ_REMOVE(&packet_q->bpq_packets, packet_out, po_next); TAILQ_REMOVE(&packet_q->bpq_packets, packet_out, po_next);
@ -2774,14 +2925,14 @@ lsquic_send_ctl_verneg_done (struct lsquic_send_ctl *ctl)
static void static void
strip_trailing_padding (struct lsquic_packet_out *packet_out) strip_trailing_padding (struct lsquic_packet_out *packet_out)
{ {
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
const struct stream_rec *srec; const struct frame_rec *frec;
unsigned off; unsigned off;
off = 0; off = 0;
for (srec = lsquic_posi_first(&posi, packet_out); srec; for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
srec = lsquic_posi_next(&posi)) frec = lsquic_pofi_next(&pofi))
off = srec->sr_off + srec->sr_len; off = frec->fe_off + frec->fe_len;
assert(off); assert(off);
@ -2790,11 +2941,59 @@ strip_trailing_padding (struct lsquic_packet_out *packet_out)
} }
static int
split_lost_packet (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *const packet_out)
{
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
struct lsquic_packet_out *new;
struct packet_resize_ctx prctx;
struct resize_one_packet_ctx one_ctx = {
ctl, packet_out, packet_out->po_path,
lsquic_packet_out_pns(packet_out), 0, 0,
};
unsigned count;
assert(packet_out->po_flags & PO_LOST);
lsquic_packet_resize_init(&prctx, ctl->sc_enpub, lconn, &one_ctx,
&resize_one_funcs);
count = 0;
while (new = lsquic_packet_resize_next(&prctx), new != NULL)
{
++count;
TAILQ_INSERT_BEFORE(packet_out, new, po_next);
new->po_flags |= PO_LOST;
}
if (lsquic_packet_resize_is_error(&prctx))
{
LSQ_WARN("error resizing lost packet #%"PRIu64, packet_out->po_packno);
return -1;
}
if (!(count > 1 && one_ctx.fetched == 1 && one_ctx.discarded == 1))
{
/* A bit of insurance, this being new code */
LSQ_WARN("unexpected values resizing lost packet: count: %u; "
"fetched: %d; discarded: %d", count, one_ctx.fetched,
one_ctx.discarded);
return -1;
}
LSQ_DEBUG("added %u packets to the lost queue", count);
LSQ_DEBUG("drop oversized lost packet #%"PRIu64, packet_out->po_packno);
TAILQ_REMOVE(&ctl->sc_lost_packets, packet_out, po_next);
packet_out->po_flags &= ~PO_LOST;
send_ctl_destroy_chain(ctl, packet_out, NULL);
send_ctl_destroy_packet(ctl, packet_out);
return 0;
}
int int
lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl, lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl,
const unsigned char *token, size_t token_sz) const unsigned char *token, size_t token_sz)
{ {
struct lsquic_packet_out *packet_out, *next, *new_packet_out; struct lsquic_packet_out *packet_out, *next;
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn; struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
size_t sz; size_t sz;
@ -2839,38 +3038,9 @@ lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl,
strip_trailing_padding(packet_out); strip_trailing_padding(packet_out);
sz = lconn->cn_pf->pf_packout_size(lconn, packet_out); sz = lconn->cn_pf->pf_packout_size(lconn, packet_out);
if (sz > 1200) if (sz > packet_out->po_path->np_pack_size
{ && 0 != split_lost_packet(ctl, packet_out))
const enum packno_bits bits = lsquic_send_ctl_calc_packno_bits(ctl); return -1;
new_packet_out = send_ctl_allocate_packet(ctl, bits, 0, PNS_INIT,
packet_out->po_path);
if (!new_packet_out)
return -1;
if (0 != send_ctl_set_packet_out_token(ctl, new_packet_out))
{
send_ctl_destroy_packet(ctl, new_packet_out);
LSQ_INFO("cannot set out token on packet");
return -1;
}
if (0 == lsquic_packet_out_split_in_two(&ctl->sc_enpub->enp_mm,
packet_out, new_packet_out,
ctl->sc_conn_pub->lconn->cn_pf, sz - 1200))
{
LSQ_DEBUG("split lost packet %"PRIu64" into two",
packet_out->po_packno);
lsquic_packet_out_set_packno_bits(packet_out, bits);
TAILQ_INSERT_AFTER(&ctl->sc_lost_packets, packet_out,
new_packet_out, po_next);
new_packet_out->po_flags |= PO_LOST;
packet_out->po_flags &= ~PO_SENT_SZ;
}
else
{
LSQ_DEBUG("could not split lost packet into two");
send_ctl_destroy_packet(ctl, new_packet_out);
return -1;
}
}
} }
return 0; return 0;
@ -2967,6 +3137,159 @@ lsquic_send_ctl_empty_pns (struct lsquic_send_ctl *ctl, enum packnum_space pns)
} }
struct resize_many_packet_ctx
{
struct lsquic_send_ctl *ctl;
struct lsquic_packets_tailq input_q;
const struct network_path *path;
};
static struct lsquic_packet_out *
resize_many_next_packet (void *ctx)
{
struct resize_many_packet_ctx *const many_ctx = ctx;
struct lsquic_packet_out *packet_out;
packet_out = TAILQ_FIRST(&many_ctx->input_q);
if (packet_out)
TAILQ_REMOVE(&many_ctx->input_q, packet_out, po_next);
return packet_out;
}
static void
resize_many_discard_packet (void *ctx, struct lsquic_packet_out *packet_out)
{
struct resize_many_packet_ctx *const many_ctx = ctx;
struct lsquic_send_ctl *const ctl = many_ctx->ctl;
send_ctl_destroy_chain(ctl, packet_out, NULL);
send_ctl_destroy_packet(ctl, packet_out);
}
static struct lsquic_packet_out *
resize_many_new_packet (void *ctx)
{
struct resize_many_packet_ctx *const many_ctx = ctx;
struct lsquic_send_ctl *const ctl = many_ctx->ctl;
struct lsquic_packet_out *packet_out;
enum packno_bits bits;
bits = lsquic_send_ctl_calc_packno_bits(ctl);
packet_out = send_ctl_allocate_packet(ctl, bits, 0, PNS_APP,
many_ctx->path);
return packet_out;
}
static const struct packet_resize_if resize_many_funcs =
{
resize_many_next_packet,
resize_many_discard_packet,
resize_many_new_packet,
};
static void
send_ctl_resize_q (struct lsquic_send_ctl *ctl, struct lsquic_packets_tailq *q,
const struct network_path *const path)
{
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
struct lsquic_packet_out *next, *packet_out;
struct resize_many_packet_ctx many_ctx;
struct packet_resize_ctx prctx;
const char *q_name;
unsigned count_src = 0, count_dst = 0;
int idx;
/* Initialize input, removing packets from source queue, filtering by path.
* Note: this may reorder packets from different paths.
*/
many_ctx.ctl = ctl;
many_ctx.path = path;
TAILQ_INIT(&many_ctx.input_q);
if (q == &ctl->sc_scheduled_packets)
{
ctl->sc_cur_packno = lsquic_senhist_largest(&ctl->sc_senhist);
q_name = "scheduled";
for (packet_out = TAILQ_FIRST(q); packet_out != NULL; packet_out = next)
{
next = TAILQ_NEXT(packet_out, po_next);
if (packet_out->po_path == path
&& !(packet_out->po_flags & PO_MTU_PROBE))
{
send_ctl_sched_remove(ctl, packet_out);
TAILQ_INSERT_TAIL(&many_ctx.input_q, packet_out, po_next);
++count_src;
}
}
}
else
{
/* This function only deals with scheduled or buffered queues */
assert(q == &ctl->sc_buffered_packets[0].bpq_packets
|| q == &ctl->sc_buffered_packets[1].bpq_packets);
idx = q == &ctl->sc_buffered_packets[1].bpq_packets;
q_name = "buffered";
for (packet_out = TAILQ_FIRST(q); packet_out != NULL; packet_out = next)
{
next = TAILQ_NEXT(packet_out, po_next);
if (packet_out->po_path == path)
{
TAILQ_REMOVE(q, packet_out, po_next);
--ctl->sc_buffered_packets[idx].bpq_count;
TAILQ_INSERT_TAIL(&many_ctx.input_q, packet_out, po_next);
++count_src;
}
}
}
lsquic_packet_resize_init(&prctx, ctl->sc_enpub, lconn, &many_ctx,
&resize_many_funcs);
/* Get new packets, appending them to appropriate queue */
if (q == &ctl->sc_scheduled_packets)
while (packet_out = lsquic_packet_resize_next(&prctx), packet_out != NULL)
{
++count_dst;
packet_out->po_packno = send_ctl_next_packno(ctl);
send_ctl_sched_append(ctl, packet_out);
LSQ_DEBUG("created packet %"PRIu64, packet_out->po_packno);
EV_LOG_PACKET_CREATED(LSQUIC_LOG_CONN_ID, packet_out);
}
else
while (packet_out = lsquic_packet_resize_next(&prctx), packet_out != NULL)
{
++count_dst;
TAILQ_INSERT_TAIL(q, packet_out, po_next);
++ctl->sc_buffered_packets[idx].bpq_count;
}
/* Verify success */
if (lsquic_packet_resize_is_error(&prctx))
{
LSQ_WARN("error resizing packets in %s queue", q_name);
goto err;
}
if (count_dst < 1 || !TAILQ_EMPTY(&many_ctx.input_q))
{
/* A bit of insurance, this being new code */
LSQ_WARN("unexpected values resizing packets in %s queue: count: %d; "
"empty: %d", q_name, count_dst, TAILQ_EMPTY(&many_ctx.input_q));
goto err;
}
LSQ_DEBUG("resized %u packets in %s queue, outputting %u packets",
count_src, q_name, count_dst);
return;
err:
lconn->cn_if->ci_internal_error(lconn, "error resizing packets");
return;
}
void void
lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl, struct network_path *old, lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl, struct network_path *old,
struct network_path *new) struct network_path *new)
@ -2999,12 +3322,58 @@ lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl, struct network_path *old,
LSQ_DEBUG("repathed %u packet%.*s", count, count != 1, "s"); LSQ_DEBUG("repathed %u packet%.*s", count, count != 1, "s");
lsquic_send_ctl_resize(ctl);
memset(&ctl->sc_conn_pub->rtt_stats, 0, memset(&ctl->sc_conn_pub->rtt_stats, 0,
sizeof(ctl->sc_conn_pub->rtt_stats)); sizeof(ctl->sc_conn_pub->rtt_stats));
ctl->sc_ci->cci_reinit(CGP(ctl)); ctl->sc_ci->cci_reinit(CGP(ctl));
} }
/* Examine packets in scheduled and buffered queues and resize packets if
* they exceed path MTU.
*/
void
lsquic_send_ctl_resize (struct lsquic_send_ctl *ctl)
{
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
struct lsquic_packet_out *packet_out;
struct lsquic_packets_tailq *const *q;
struct lsquic_packets_tailq *const queues[] = {
&ctl->sc_scheduled_packets,
&ctl->sc_buffered_packets[0].bpq_packets,
&ctl->sc_buffered_packets[1].bpq_packets,
};
size_t size;
int path_ids /* assuming a reasonable number of paths */, q_idxs;
assert(ctl->sc_flags & SC_IETF);
q_idxs = 0;
for (q = queues; q < queues + sizeof(queues) / sizeof(queues[0]); ++q)
{
path_ids = 0;
redo_q:
TAILQ_FOREACH(packet_out, *q, po_next)
if (0 == (path_ids & (1 << packet_out->po_path->np_path_id))
&& !(packet_out->po_flags & PO_MTU_PROBE))
{
size = lsquic_packet_out_total_sz(lconn, packet_out);
if (size > packet_out->po_path->np_pack_size)
{
send_ctl_resize_q(ctl, *q, packet_out->po_path);
path_ids |= 1 << packet_out->po_path->np_path_id;
q_idxs |= 1 << (q - queues);
goto redo_q;
}
}
}
LSQ_DEBUG("resized packets in queues: 0x%X", q_idxs);
lsquic_send_ctl_sanity_check(ctl);
}
void void
lsquic_send_ctl_return_enc_data (struct lsquic_send_ctl *ctl) lsquic_send_ctl_return_enc_data (struct lsquic_send_ctl *ctl)
{ {
@ -3073,3 +3442,29 @@ lsquic_send_ctl_path_validated (struct lsquic_send_ctl *ctl)
LSQ_DEBUG("path validated: switch to regular can_send"); LSQ_DEBUG("path validated: switch to regular can_send");
ctl->sc_can_send = send_ctl_can_send; ctl->sc_can_send = send_ctl_can_send;
} }
int
lsquic_send_ctl_can_send_probe (const struct lsquic_send_ctl *ctl,
const struct network_path *path)
{
uint64_t cwnd, pacing_rate;
lsquic_time_t tx_time;
unsigned n_out;
assert(!send_ctl_in_recovery(ctl));
n_out = send_ctl_all_bytes_out(ctl);
cwnd = ctl->sc_ci->cci_get_cwnd(CGP(ctl));
if (ctl->sc_flags & SC_PACE)
{
if (n_out + path->np_pack_size >= cwnd)
return 0;
pacing_rate = ctl->sc_ci->cci_pacing_rate(CGP(ctl), 0);
tx_time = (uint64_t) path->np_pack_size * 1000000 / pacing_rate;
return lsquic_pacer_can_schedule_probe(&ctl->sc_pacer,
ctl->sc_n_scheduled + ctl->sc_n_in_flight_all, tx_time);
}
else
return n_out + path->np_pack_size < cwnd;
}

View file

@ -365,6 +365,9 @@ void
lsquic_send_ctl_repath (struct lsquic_send_ctl *, struct network_path *old, lsquic_send_ctl_repath (struct lsquic_send_ctl *, struct network_path *old,
struct network_path *new); struct network_path *new);
void
lsquic_send_ctl_resize (struct lsquic_send_ctl *);
void void
lsquic_send_ctl_return_enc_data (struct lsquic_send_ctl *); lsquic_send_ctl_return_enc_data (struct lsquic_send_ctl *);
@ -395,4 +398,13 @@ lsquic_send_ctl_path_validated (struct lsquic_send_ctl *);
(lsquic_send_ctl_n_scheduled(ctl_) > 0 \ (lsquic_send_ctl_n_scheduled(ctl_) > 0 \
&& lsquic_send_ctl_next_packet_to_send_predict(ctl_)) && lsquic_send_ctl_next_packet_to_send_predict(ctl_))
#define lsquic_send_ctl_in_recovery(ctl_) ((ctl_)->sc_largest_acked_packno \
&& (ctl_)->sc_largest_acked_packno <= (ctl_)->sc_largest_sent_at_cutback)
#define send_ctl_in_recovery lsquic_send_ctl_in_recovery
int
lsquic_send_ctl_can_send_probe (const struct lsquic_send_ctl *,
const struct network_path *);
#endif #endif

View file

@ -52,6 +52,7 @@
#include "lsquic_conn.h" #include "lsquic_conn.h"
#include "lsquic_data_in_if.h" #include "lsquic_data_in_if.h"
#include "lsquic_parse.h" #include "lsquic_parse.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h" #include "lsquic_packet_out.h"
#include "lsquic_engine_public.h" #include "lsquic_engine_public.h"
#include "lsquic_senhist.h" #include "lsquic_senhist.h"
@ -585,6 +586,7 @@ lsquic_stream_destroy (lsquic_stream_t *stream)
STREAM_ONNEW_DONE) STREAM_ONNEW_DONE)
{ {
stream->stream_flags |= STREAM_ONCLOSE_DONE; stream->stream_flags |= STREAM_ONCLOSE_DONE;
SM_HISTORY_APPEND(stream, SHE_ONCLOSE_CALL);
stream->stream_if->on_close(stream, stream->st_ctx); stream->stream_if->on_close(stream, stream->st_ctx);
} }
if (stream->sm_qflags & SMQF_SENDING_FLAGS) if (stream->sm_qflags & SMQF_SENDING_FLAGS)
@ -2752,15 +2754,6 @@ frame_hq_gen_read (void *ctx, void *begin_buf, size_t len, int *fin)
} }
static size_t
crypto_frame_gen_read (void *ctx, void *buf, size_t len)
{
int fin_ignored;
return frame_std_gen_read(ctx, buf, len, &fin_ignored);
}
static void static void
check_flush_threshold (lsquic_stream_t *stream) check_flush_threshold (lsquic_stream_t *stream)
{ {
@ -2985,8 +2978,8 @@ stream_write_to_packet_crypto (struct frame_gen_ctx *fg_ctx, const size_t size)
off = packet_out->po_data_sz; off = packet_out->po_data_sz;
len = pf->pf_gen_crypto_frame(packet_out->po_data + packet_out->po_data_sz, len = pf->pf_gen_crypto_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), stream->tosend_off, lsquic_packet_out_avail(packet_out), 0, stream->tosend_off, 0,
size, crypto_frame_gen_read, fg_ctx); size, frame_std_gen_read, fg_ctx);
if (len < 0) if (len < 0)
return len; return len;

View file

@ -48,6 +48,7 @@ SET(TESTS
hkdf hkdf
lsquic_hash lsquic_hash
packet_out packet_out
packet_resize
packno_len packno_len
parse_packet_in parse_packet_in
purga purga

View file

@ -74,7 +74,7 @@ struct test_ctx {
static size_t static size_t
crypto_read (void *ctx, void *buf, size_t len) crypto_read (void *ctx, void *buf, size_t len, int *fin)
{ {
struct test_ctx *test_ctx = ctx; struct test_ctx *test_ctx = ctx;
if (test_ctx->test->data_sz - test_ctx->off < len) if (test_ctx->test->data_sz - test_ctx->off < len)
@ -109,20 +109,20 @@ run_test (int i)
for (min = 0; min < test->min_sz; ++min) for (min = 0; min < test->min_sz; ++min)
{ {
init_ctx(&test_ctx, test); init_ctx(&test_ctx, test);
len = test->pf->pf_gen_crypto_frame(out, min, test->offset, len = test->pf->pf_gen_crypto_frame(out, min, 0, test->offset, 0,
test->data_sz, crypto_read, &test_ctx); test->data_sz, crypto_read, &test_ctx);
assert(-1 == len); assert(-1 == len);
} }
/* Test that it succeeds now: */ /* Test that it succeeds now: */
init_ctx(&test_ctx, test); init_ctx(&test_ctx, test);
len = test->pf->pf_gen_crypto_frame(out, min, test->offset, len = test->pf->pf_gen_crypto_frame(out, min, 0, test->offset, 0,
test->data_sz, crypto_read, &test_ctx); test->data_sz, crypto_read, &test_ctx);
assert(len == (int) min); assert(len == (int) min);
} }
init_ctx(&test_ctx, test); init_ctx(&test_ctx, test);
len = test->pf->pf_gen_crypto_frame(out, test->avail, test->offset, len = test->pf->pf_gen_crypto_frame(out, test->avail, 0, test->offset, 0,
test->data_sz, crypto_read, &test_ctx); test->data_sz, crypto_read, &test_ctx);
if (test->len > 0) { if (test->len > 0) {

View file

@ -96,7 +96,7 @@ lsquic_stream_acked (lsquic_stream_t *stream, enum quic_frame_type frame_type)
static void static void
elide_single_stream_frame (void) elide_single_stream_frame (void)
{ {
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
struct lsquic_engine_public enpub; struct lsquic_engine_public enpub;
lsquic_stream_t streams[1]; lsquic_stream_t streams[1];
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
@ -120,14 +120,14 @@ elide_single_stream_frame (void)
lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[0], lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[0],
QUIC_FRAME_STREAM, off, len); QUIC_FRAME_STREAM, off, len);
assert(1 == streams[0].n_unacked); assert(1 == streams[0].n_unacked);
assert(lsquic_posi_first(&posi, packet_out)); assert(lsquic_pofi_first(&pofi, packet_out));
streams[0].stream_flags |= STREAM_RST_SENT; streams[0].stream_flags |= STREAM_RST_SENT;
lsquic_packet_out_elide_reset_stream_frames(packet_out, 0); lsquic_packet_out_elide_reset_stream_frames(packet_out, 0);
assert(0 == streams[0].n_unacked); assert(0 == streams[0].n_unacked);
assert(0 == packet_out->po_frame_types); assert(0 == packet_out->po_frame_types);
assert(!lsquic_posi_first(&posi, packet_out)); assert(!lsquic_pofi_first(&pofi, packet_out));
lsquic_packet_out_destroy(packet_out, &enpub, NULL); lsquic_packet_out_destroy(packet_out, &enpub, NULL);
lsquic_mm_cleanup(&enpub.enp_mm); lsquic_mm_cleanup(&enpub.enp_mm);
@ -142,11 +142,11 @@ elide_single_stream_frame (void)
static void static void
shrink_packet_post_elision (void) shrink_packet_post_elision (void)
{ {
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
struct lsquic_engine_public enpub; struct lsquic_engine_public enpub;
lsquic_stream_t streams[2]; lsquic_stream_t streams[2];
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
const struct stream_rec *srec; const struct frame_rec *frec;
int len, off = 0; int len, off = 0;
unsigned char stream2_data[0x1000]; unsigned char stream2_data[0x1000];
@ -189,7 +189,7 @@ shrink_packet_post_elision (void)
assert(1 == streams[0].n_unacked); assert(1 == streams[0].n_unacked);
assert(1 == streams[1].n_unacked); assert(1 == streams[1].n_unacked);
assert(lsquic_posi_first(&posi, packet_out)); assert(lsquic_pofi_first(&pofi, packet_out));
streams[0].stream_flags |= STREAM_RST_SENT; streams[0].stream_flags |= STREAM_RST_SENT;
@ -197,8 +197,8 @@ shrink_packet_post_elision (void)
assert(0 == streams[0].n_unacked); assert(0 == streams[0].n_unacked);
assert(QUIC_FTBIT_STREAM == packet_out->po_frame_types); assert(QUIC_FTBIT_STREAM == packet_out->po_frame_types);
srec = lsquic_posi_first(&posi, packet_out); frec = lsquic_pofi_first(&pofi, packet_out);
assert(srec->sr_stream == &streams[1]); assert(frec->fe_stream == &streams[1]);
assert(packet_out->po_data_sz == exp); assert(packet_out->po_data_sz == exp);
lsquic_packet_out_destroy(packet_out, &enpub, NULL); lsquic_packet_out_destroy(packet_out, &enpub, NULL);
@ -222,11 +222,11 @@ shrink_packet_post_elision (void)
static void static void
elide_three_stream_frames (int chop_regen) elide_three_stream_frames (int chop_regen)
{ {
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
struct lsquic_engine_public enpub; struct lsquic_engine_public enpub;
lsquic_stream_t streams[5]; lsquic_stream_t streams[5];
lsquic_packet_out_t *packet_out, *ref_out; lsquic_packet_out_t *packet_out, *ref_out;
struct stream_rec *srec; struct frame_rec *frec;
unsigned short b_off, d_off; unsigned short b_off, d_off;
int len; int len;
@ -241,6 +241,8 @@ elide_three_stream_frames (int chop_regen)
ref_out = lsquic_mm_get_packet_out(&enpub.enp_mm, NULL, GQUIC_MAX_PAYLOAD_SZ); ref_out = lsquic_mm_get_packet_out(&enpub.enp_mm, NULL, GQUIC_MAX_PAYLOAD_SZ);
/* This is fake data for regeneration */ /* This is fake data for regeneration */
strcpy((char *) ref_out->po_data, "REGEN"); strcpy((char *) ref_out->po_data, "REGEN");
lsquic_packet_out_add_frame(ref_out, &enpub.enp_mm, 0,
QUIC_FRAME_ACK, ref_out->po_data_sz, 5);
ref_out->po_data_sz = ref_out->po_regen_sz = 5; ref_out->po_data_sz = ref_out->po_regen_sz = 5;
/* STREAM B */ /* STREAM B */
setup_stream_contents(123, "BBBBBBBBBB"); setup_stream_contents(123, "BBBBBBBBBB");
@ -278,6 +280,8 @@ elide_three_stream_frames (int chop_regen)
packet_out = lsquic_mm_get_packet_out(&enpub.enp_mm, NULL, GQUIC_MAX_PAYLOAD_SZ); packet_out = lsquic_mm_get_packet_out(&enpub.enp_mm, NULL, GQUIC_MAX_PAYLOAD_SZ);
/* This is fake data for regeneration */ /* This is fake data for regeneration */
strcpy((char *) packet_out->po_data, "REGEN"); strcpy((char *) packet_out->po_data, "REGEN");
lsquic_packet_out_add_frame(packet_out, &enpub.enp_mm, 0,
QUIC_FRAME_ACK, packet_out->po_data_sz, 5);
packet_out->po_data_sz = packet_out->po_regen_sz = 5; packet_out->po_data_sz = packet_out->po_regen_sz = 5;
/* STREAM A */ /* STREAM A */
setup_stream_contents(123, "AAAAAAAAAA"); setup_stream_contents(123, "AAAAAAAAAA");
@ -377,22 +381,24 @@ elide_three_stream_frames (int chop_regen)
assert(packet_out->po_frame_types == ((1 << QUIC_FRAME_STREAM) | (1 << QUIC_FRAME_RST_STREAM))); assert(packet_out->po_frame_types == ((1 << QUIC_FRAME_STREAM) | (1 << QUIC_FRAME_RST_STREAM)));
srec = lsquic_posi_first(&posi, packet_out); frec = lsquic_pofi_first(&pofi, packet_out);
assert(srec->sr_stream == &streams[1]); if (!chop_regen)
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_off == b_off - (chop_regen ? 5 : 0)); assert(frec->fe_stream == &streams[1]);
assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
assert(frec->fe_off == b_off - (chop_regen ? 5 : 0));
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_stream == &streams[0]); assert(frec->fe_stream == &streams[0]);
assert(srec->sr_frame_type == QUIC_FRAME_RST_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_RST_STREAM);
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_stream == &streams[3]); assert(frec->fe_stream == &streams[3]);
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
assert(srec->sr_off == d_off - (chop_regen ? 5 : 0)); assert(frec->fe_off == d_off - (chop_regen ? 5 : 0));
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(!srec); assert(!frec);
lsquic_packet_out_destroy(packet_out, &enpub, NULL); lsquic_packet_out_destroy(packet_out, &enpub, NULL);
lsquic_packet_out_destroy(ref_out, &enpub, NULL); lsquic_packet_out_destroy(ref_out, &enpub, NULL);

View file

@ -184,8 +184,8 @@ read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t str
const struct parse_funcs *const pf_local = send_ctl->sc_conn_pub->lconn->cn_pf; const struct parse_funcs *const pf_local = send_ctl->sc_conn_pub->lconn->cn_pf;
unsigned char *p = begin; unsigned char *p = begin;
unsigned char *const end = p + bufsz; unsigned char *const end = p + bufsz;
const struct stream_rec *srec; const struct frame_rec *frec;
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
struct lsquic_packet_out *packet_out; struct lsquic_packet_out *packet_out;
struct stream_frame frame; struct stream_frame frame;
enum quic_frame_type expected_type; enum quic_frame_type expected_type;
@ -194,38 +194,38 @@ read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t str
expected_type = QUIC_FRAME_STREAM; expected_type = QUIC_FRAME_STREAM;
TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next) TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next)
for (srec = lsquic_posi_first(&posi, packet_out); srec; for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
srec = lsquic_posi_next(&posi)) frec = lsquic_pofi_next(&pofi))
{ {
if (fullcheck) if (fullcheck)
{ {
assert(srec->sr_frame_type == expected_type); assert(frec->fe_frame_type == expected_type);
if (0 && packet_out->po_packno != 1) if (0 && packet_out->po_packno != 1)
{ {
/* First packet may contain two stream frames, do not /* First packet may contain two stream frames, do not
* check it. * check it.
*/ */
assert(!lsquic_posi_next(&posi)); assert(!lsquic_pofi_next(&pofi));
if (TAILQ_NEXT(packet_out, po_next)) if (TAILQ_NEXT(packet_out, po_next))
{ {
assert(packet_out->po_data_sz == packet_out->po_n_alloc); assert(packet_out->po_data_sz == packet_out->po_n_alloc);
assert(srec->sr_len == packet_out->po_data_sz); assert(frec->fe_len == packet_out->po_data_sz);
} }
} }
} }
if (srec->sr_frame_type == expected_type && if (frec->fe_frame_type == expected_type &&
srec->sr_stream->id == stream_id) frec->fe_stream->id == stream_id)
{ {
assert(!fin); assert(!fin);
if (QUIC_FRAME_STREAM == expected_type) if (QUIC_FRAME_STREAM == expected_type)
len = pf_local->pf_parse_stream_frame(packet_out->po_data + srec->sr_off, len = pf_local->pf_parse_stream_frame(packet_out->po_data + frec->fe_off,
packet_out->po_data_sz - srec->sr_off, &frame); packet_out->po_data_sz - frec->fe_off, &frame);
else else
len = pf_local->pf_parse_crypto_frame(packet_out->po_data + srec->sr_off, len = pf_local->pf_parse_crypto_frame(packet_out->po_data + frec->fe_off,
packet_out->po_data_sz - srec->sr_off, &frame); packet_out->po_data_sz - frec->fe_off, &frame);
assert(len > 0); assert(len > 0);
if (QUIC_FRAME_STREAM == expected_type) if (QUIC_FRAME_STREAM == expected_type)
assert(frame.stream_id == srec->sr_stream->id); assert(frame.stream_id == frec->fe_stream->id);
else else
assert(frame.stream_id == ~0ULL); assert(frame.stream_id == ~0ULL);
/* Otherwise not enough to copy to: */ /* Otherwise not enough to copy to: */
@ -238,7 +238,7 @@ read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t str
assert(!fin); assert(!fin);
fin = 1; fin = 1;
} }
memcpy(p, packet_out->po_data + srec->sr_off + len - memcpy(p, packet_out->po_data + frec->fe_off + len -
frame.data_frame.df_size, frame.data_frame.df_size); frame.data_frame.df_size, frame.data_frame.df_size);
p += frame.data_frame.df_size; p += frame.data_frame.df_size;
} }

View file

@ -37,10 +37,10 @@ int
main (void) main (void)
{ {
struct lsquic_engine_public enpub; struct lsquic_engine_public enpub;
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
struct lsquic_stream streams[6]; struct lsquic_stream streams[6];
struct stream_rec *srec; struct frame_rec *frec;
memset(&enpub, 0, sizeof(enpub)); memset(&enpub, 0, sizeof(enpub));
memset(&streams, 0, sizeof(streams)); memset(&streams, 0, sizeof(streams));
@ -55,45 +55,45 @@ main (void)
lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[4], QUIC_FRAME_STREAM, 12, 1); lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[4], QUIC_FRAME_STREAM, 12, 1);
lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[5], QUIC_FRAME_STREAM, 13, 1); lsquic_packet_out_add_stream(packet_out, &enpub.enp_mm, &streams[5], QUIC_FRAME_STREAM, 13, 1);
srec = lsquic_posi_first(&posi, packet_out); frec = lsquic_pofi_first(&pofi, packet_out);
assert(srec->sr_stream == &streams[0]); assert(frec->fe_stream == &streams[0]);
assert(srec->sr_off == 7); assert(frec->fe_off == 7);
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_stream == &streams[1]); assert(frec->fe_stream == &streams[1]);
assert(srec->sr_off == 8); assert(frec->fe_off == 8);
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_stream == &streams[2]); assert(frec->fe_stream == &streams[2]);
assert(srec->sr_off == 9); assert(frec->fe_off == 9);
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_stream == &streams[1]); assert(frec->fe_stream == &streams[1]);
assert(srec->sr_off == 10); assert(frec->fe_off == 10);
assert(srec->sr_frame_type == QUIC_FRAME_RST_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_RST_STREAM);
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_stream == &streams[3]); assert(frec->fe_stream == &streams[3]);
assert(srec->sr_off == 11); assert(frec->fe_off == 11);
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_stream == &streams[4]); assert(frec->fe_stream == &streams[4]);
assert(srec->sr_off == 12); assert(frec->fe_off == 12);
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
srec = lsquic_posi_next(&posi); frec = lsquic_pofi_next(&pofi);
assert(srec->sr_stream == &streams[5]); assert(frec->fe_stream == &streams[5]);
assert(srec->sr_off == 13); assert(frec->fe_off == 13);
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
assert((void *) 0 == lsquic_posi_next(&posi)); assert((void *) 0 == lsquic_pofi_next(&pofi));
lsquic_packet_out_destroy(packet_out, &enpub, NULL); lsquic_packet_out_destroy(packet_out, &enpub, NULL);
assert(!lsquic_malo_first(enpub.enp_mm.malo.stream_rec_arr)); assert(!lsquic_malo_first(enpub.enp_mm.malo.frame_rec_arr));
lsquic_mm_cleanup(&enpub.enp_mm); lsquic_mm_cleanup(&enpub.enp_mm);
return 0; return 0;

1640
tests/test_packet_resize.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -205,8 +205,8 @@ read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t str
const struct parse_funcs *const pf_local = send_ctl->sc_conn_pub->lconn->cn_pf; const struct parse_funcs *const pf_local = send_ctl->sc_conn_pub->lconn->cn_pf;
unsigned char *p = begin; unsigned char *p = begin;
unsigned char *const end = p + bufsz; unsigned char *const end = p + bufsz;
const struct stream_rec *srec; const struct frame_rec *frec;
struct packet_out_srec_iter posi; struct packet_out_frec_iter pofi;
struct lsquic_packet_out *packet_out; struct lsquic_packet_out *packet_out;
struct stream_frame frame; struct stream_frame frame;
enum quic_frame_type expected_type; enum quic_frame_type expected_type;
@ -218,38 +218,38 @@ read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t str
expected_type = QUIC_FRAME_STREAM; expected_type = QUIC_FRAME_STREAM;
TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next) TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next)
for (srec = lsquic_posi_first(&posi, packet_out); srec; for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
srec = lsquic_posi_next(&posi)) frec = lsquic_pofi_next(&pofi))
{ {
if (fullcheck) if (fullcheck)
{ {
assert(srec->sr_frame_type == expected_type); assert(frec->fe_frame_type == expected_type);
if (packet_out->po_packno != 1) if (packet_out->po_packno != 1)
{ {
/* First packet may contain two stream frames, do not /* First packet may contain two stream frames, do not
* check it. * check it.
*/ */
assert(!lsquic_posi_next(&posi)); assert(!lsquic_pofi_next(&pofi));
if (TAILQ_NEXT(packet_out, po_next)) if (TAILQ_NEXT(packet_out, po_next))
{ {
assert(packet_out->po_data_sz == packet_out->po_n_alloc); assert(packet_out->po_data_sz == packet_out->po_n_alloc);
assert(srec->sr_len == packet_out->po_data_sz); assert(frec->fe_len == packet_out->po_data_sz);
} }
} }
} }
if (srec->sr_frame_type == expected_type && if (frec->fe_frame_type == expected_type &&
srec->sr_stream->id == stream_id) frec->fe_stream->id == stream_id)
{ {
assert(!fin); assert(!fin);
if (QUIC_FRAME_STREAM == expected_type) if (QUIC_FRAME_STREAM == expected_type)
len = pf_local->pf_parse_stream_frame(packet_out->po_data + srec->sr_off, len = pf_local->pf_parse_stream_frame(packet_out->po_data + frec->fe_off,
packet_out->po_data_sz - srec->sr_off, &frame); packet_out->po_data_sz - frec->fe_off, &frame);
else else
len = pf_local->pf_parse_crypto_frame(packet_out->po_data + srec->sr_off, len = pf_local->pf_parse_crypto_frame(packet_out->po_data + frec->fe_off,
packet_out->po_data_sz - srec->sr_off, &frame); packet_out->po_data_sz - frec->fe_off, &frame);
assert(len > 0); assert(len > 0);
if (QUIC_FRAME_STREAM == expected_type) if (QUIC_FRAME_STREAM == expected_type)
assert(frame.stream_id == srec->sr_stream->id); assert(frame.stream_id == frec->fe_stream->id);
else else
assert(frame.stream_id == ~0ULL); assert(frame.stream_id == ~0ULL);
/* Otherwise not enough to copy to: */ /* Otherwise not enough to copy to: */
@ -262,7 +262,7 @@ read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t str
assert(!fin); assert(!fin);
fin = 1; fin = 1;
} }
memcpy(p, packet_out->po_data + srec->sr_off + len - memcpy(p, packet_out->po_data + frec->fe_off + len -
frame.data_frame.df_size, frame.data_frame.df_size); frame.data_frame.df_size, frame.data_frame.df_size);
p += frame.data_frame.df_size; p += frame.data_frame.df_size;
} }
@ -2366,9 +2366,7 @@ test_window_update1 (void)
} }
/* Test two: large frame in the middle -- it is the one that is moved out /* Test two: large frame in the middle */
* into new packet.
*/
static void static void
test_bad_packbits_guess_2 (void) test_bad_packbits_guess_2 (void)
{ {
@ -2455,8 +2453,8 @@ test_bad_packbits_guess_2 (void)
assert(1 == streams[2]->n_unacked); assert(1 == streams[2]->n_unacked);
ack_packet(&tobjs.send_ctl, 1); ack_packet(&tobjs.send_ctl, 1);
assert(0 == streams[0]->n_unacked); assert(0 == streams[0]->n_unacked);
assert(1 == streams[1]->n_unacked); assert(0 == streams[1]->n_unacked);
assert(0 == streams[2]->n_unacked); assert(1 == streams[2]->n_unacked);
ack_packet(&tobjs.send_ctl, 2); ack_packet(&tobjs.send_ctl, 2);
assert(0 == streams[0]->n_unacked); assert(0 == streams[0]->n_unacked);
assert(0 == streams[1]->n_unacked); assert(0 == streams[1]->n_unacked);
@ -2508,7 +2506,7 @@ test_bad_packbits_guess_3 (void)
assert(1 == streams[0]->n_unacked); assert(1 == streams[0]->n_unacked);
g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; g_ctl_settings.tcs_schedule_stream_packets_immediately = 1;
g_ctl_settings.tcs_calc_packno_bits = GQUIC_PACKNO_LEN_4; g_ctl_settings.tcs_calc_packno_bits = GQUIC_PACKNO_LEN_6;
s = lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl, s = lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl,
g_ctl_settings.tcs_bp_type); g_ctl_settings.tcs_bp_type);
assert(2 == lsquic_send_ctl_n_scheduled(&tobjs.send_ctl)); assert(2 == lsquic_send_ctl_n_scheduled(&tobjs.send_ctl));
@ -2522,12 +2520,12 @@ test_bad_packbits_guess_3 (void)
/* Verify packets */ /* Verify packets */
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl, 0); packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl, 0);
assert(lsquic_packet_out_packno_bits(packet_out) == GQUIC_PACKNO_LEN_4); assert(lsquic_packet_out_packno_bits(packet_out) == GQUIC_PACKNO_LEN_6);
assert(1 == packet_out->po_packno); assert(1 == packet_out->po_packno);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM)); assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out); lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl, 0); packet_out = lsquic_send_ctl_next_packet_to_send(&tobjs.send_ctl, 0);
assert(lsquic_packet_out_packno_bits(packet_out) == GQUIC_PACKNO_LEN_4); assert(lsquic_packet_out_packno_bits(packet_out) == GQUIC_PACKNO_LEN_6);
assert(2 == packet_out->po_packno); assert(2 == packet_out->po_packno);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM)); assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out); lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
@ -2543,6 +2541,128 @@ test_bad_packbits_guess_3 (void)
} }
/* Test resizing of buffered packets:
* 1. Write data to buffered packets
* 2. Reduce packet size
* 3. Resize buffered packets
* 4. Schedule them
* 5. Check contents
*/
static void
test_resize_buffered (void)
{
ssize_t nw;
struct test_objs tobjs;
struct lsquic_stream *streams[1];
const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_ID27);
char buf[0x10000];
unsigned char buf_out[0x10000];
int s, fin;
unsigned packet_counts[2];
init_buf(buf, sizeof(buf));
lsquic_send_ctl_set_max_bpq_count(UINT_MAX);
init_test_ctl_settings(&g_ctl_settings);
g_ctl_settings.tcs_schedule_stream_packets_immediately = 0;
init_test_objs(&tobjs, 0x100000, 0x100000, pf);
tobjs.send_ctl.sc_flags |= SC_IETF; /* work around asserts lsquic_send_ctl_resize() */
network_path.np_pack_size = 4096;
streams[0] = new_stream_ext(&tobjs, 7, 0x100000);
nw = lsquic_stream_write(streams[0], buf, sizeof(buf));
assert(nw == sizeof(buf));
s = lsquic_stream_shutdown(streams[0], 1);
assert(s == 0);
packet_counts[0] = tobjs.send_ctl.sc_buffered_packets[g_ctl_settings.tcs_bp_type].bpq_count;
assert(streams[0]->n_unacked > 0);
network_path.np_pack_size = 1234;
lsquic_send_ctl_resize(&tobjs.send_ctl);
packet_counts[1] = tobjs.send_ctl.sc_buffered_packets[g_ctl_settings.tcs_bp_type].bpq_count;
assert(packet_counts[1] > packet_counts[0]);
g_ctl_settings.tcs_schedule_stream_packets_immediately = 1;
s = lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl,
g_ctl_settings.tcs_bp_type);
assert(lsquic_send_ctl_n_scheduled(&tobjs.send_ctl) > 0);
/* Verify written data: */
nw = read_from_scheduled_packets(&tobjs.send_ctl, streams[0]->id, buf_out,
sizeof(buf_out), 0, &fin, 0);
assert(nw == sizeof(buf));
assert(fin);
assert(0 == memcmp(buf, buf_out, nw));
lsquic_stream_destroy(streams[0]);
deinit_test_objs(&tobjs);
lsquic_send_ctl_set_max_bpq_count(10);
}
/* Test resizing of buffered packets:
* 1. Write data to buffered packets
* 2. Schedule them
* 3. Reduce packet size
* 4. Resize packets
* 5. Check contents
*/
static void
test_resize_scheduled (void)
{
ssize_t nw;
struct test_objs tobjs;
struct lsquic_stream *streams[1];
const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_ID27);
char buf[0x10000];
unsigned char buf_out[0x10000];
int s, fin;
unsigned packet_counts[2];
init_buf(buf, sizeof(buf));
lsquic_send_ctl_set_max_bpq_count(UINT_MAX);
init_test_ctl_settings(&g_ctl_settings);
g_ctl_settings.tcs_schedule_stream_packets_immediately = 0;
init_test_objs(&tobjs, 0x100000, 0x100000, pf);
tobjs.send_ctl.sc_flags |= SC_IETF; /* work around asserts lsquic_send_ctl_resize() */
network_path.np_pack_size = 4096;
streams[0] = new_stream_ext(&tobjs, 7, 0x100000);
nw = lsquic_stream_write(streams[0], buf, sizeof(buf));
assert(nw == sizeof(buf));
s = lsquic_stream_shutdown(streams[0], 1);
assert(s == 0);
assert(streams[0]->n_unacked > 0);
g_ctl_settings.tcs_schedule_stream_packets_immediately = 1;
s = lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl,
g_ctl_settings.tcs_bp_type);
packet_counts[0] = lsquic_send_ctl_n_scheduled(&tobjs.send_ctl);
assert(packet_counts[0] > 0);
network_path.np_pack_size = 1234;
lsquic_send_ctl_resize(&tobjs.send_ctl);
packet_counts[1] = lsquic_send_ctl_n_scheduled(&tobjs.send_ctl);
assert(packet_counts[1] > packet_counts[0]);
/* Verify written data: */
nw = read_from_scheduled_packets(&tobjs.send_ctl, streams[0]->id, buf_out,
sizeof(buf_out), 0, &fin, 0);
assert(nw == sizeof(buf));
assert(fin);
assert(0 == memcmp(buf, buf_out, nw));
lsquic_stream_destroy(streams[0]);
deinit_test_objs(&tobjs);
lsquic_send_ctl_set_max_bpq_count(10);
}
struct packetization_test_stream_ctx struct packetization_test_stream_ctx
{ {
const unsigned char *buf; const unsigned char *buf;
@ -3164,6 +3284,9 @@ main (int argc, char **argv)
test_bad_packbits_guess_2(); test_bad_packbits_guess_2();
test_bad_packbits_guess_3(); test_bad_packbits_guess_3();
test_resize_buffered();
test_resize_scheduled();
main_test_packetization(); main_test_packetization();
enum lsquic_version ver; enum lsquic_version ver;