mirror of
				https://gitea.invidious.io/iv-org/litespeed-quic.git
				synced 2024-08-15 00:53:43 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									b329a00e5e
								
							
						
					
					
						commit
						b8fa619567
					
				
					 46 changed files with 3629 additions and 854 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
    - 2.18.2
 | 
			
		||||
    - [BUGFIX] Send prediction: lone path challenges do not get squeezed out
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,9 +105,8 @@ Compilation
 | 
			
		|||
    - Build both the http_client and http_server test programs.
 | 
			
		||||
 | 
			
		||||
Running Instructions
 | 
			
		||||
    - On the server side, define the environment variable
 | 
			
		||||
      LSQUIC_CN_PACK_SIZE and set it to the intended packet size.
 | 
			
		||||
      Valid sizes are up to 65507 (IPv4).
 | 
			
		||||
    - Specify maximum packet size using -o base_plpmtu=$number.
 | 
			
		||||
      Valid sizes are up to 65535.
 | 
			
		||||
    - Use the -W flag for http_client and http_server for the ability
 | 
			
		||||
      to send packets of large size.
 | 
			
		||||
    - On the client side, use the -z flag to specify the maximum size
 | 
			
		||||
| 
						 | 
				
			
			@ -115,9 +114,9 @@ Running Instructions
 | 
			
		|||
 | 
			
		||||
Example Usage
 | 
			
		||||
    ./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
 | 
			
		||||
        -s 0.0.0.0:5443 -W
 | 
			
		||||
        -s 0.0.0.0:5443 -W -o base_plpmtu=65535
 | 
			
		||||
 | 
			
		||||
Additional Notes
 | 
			
		||||
    Since this feature does not have MTU discovery enabled at the time of
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -140,8 +140,8 @@ prog_print_common_options (const struct prog *prog, FILE *out)
 | 
			
		|||
#if LSQUIC_DONTFRAG_SUPPORTED
 | 
			
		||||
"   -D          Do not set `do not fragment' flag on outgoing UDP packets\n"
 | 
			
		||||
#endif
 | 
			
		||||
"   -z BYTES    Maximum size of outgoing UDP packets.  The default is 1370\n"
 | 
			
		||||
"                 bytes for IPv4 socket and 1350 bytes for IPv6 socket\n"
 | 
			
		||||
"   -z BYTES    Maximum size of outgoing UDP packets (client only).\n"
 | 
			
		||||
"                 Overrides -o base_plpmtu.\n"
 | 
			
		||||
"   -L LEVEL    Log level for all modules.  Possible values are `debug',\n"
 | 
			
		||||
"                 `info', `notice', `warn', `error', `alert', `emerg',\n"
 | 
			
		||||
"                 and `crit'.\n"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1816,6 +1816,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
 | 
			
		|||
            settings->es_scid_len = atoi(val);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (0 == strncmp(name, "dplpmtud", 8))
 | 
			
		||||
        {
 | 
			
		||||
            settings->es_dplpmtud = atoi(val);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 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);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (0 == strncmp(name, "max_plpmtu", 10))
 | 
			
		||||
        {
 | 
			
		||||
            settings->es_max_plpmtu = atoi(val);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 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);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (0 == strncmp(name, "base_plpmtu", 11))
 | 
			
		||||
        {
 | 
			
		||||
            settings->es_base_plpmtu = atoi(val);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 12:
 | 
			
		||||
        if (0 == strncmp(name, "idle_conn_to", 12))
 | 
			
		||||
| 
						 | 
				
			
			@ -1975,7 +1990,7 @@ set_engine_option (struct lsquic_engine_settings *settings,
 | 
			
		|||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    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);
 | 
			
		||||
            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
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -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 *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)
 | 
			
		||||
    {
 | 
			
		||||
        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
 | 
			
		||||
    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);
 | 
			
		||||
    else if (size <= PBA_SIZE_THRESH)
 | 
			
		||||
        pb = malloc(PBA_SIZE_MAX);
 | 
			
		||||
    else
 | 
			
		||||
        pb = malloc(sizeof(uintptr_t) + size);
 | 
			
		||||
#else
 | 
			
		||||
    pb = malloc(sizeof(uintptr_t) + size);
 | 
			
		||||
#endif
 | 
			
		||||
        pb = malloc(MAX_PACKOUT_BUF_SZ);
 | 
			
		||||
 | 
			
		||||
    if (pb)
 | 
			
		||||
    {
 | 
			
		||||
        * (uintptr_t *) pb = size;
 | 
			
		||||
        ++pba->n_out;
 | 
			
		||||
 | 
			
		||||
    return pb;
 | 
			
		||||
        return (uintptr_t *) pb + 1;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2064,12 +2083,16 @@ void
 | 
			
		|||
pba_release (void *packout_buf_allocator, void *peer_ctx, void *obj, char ipv6)
 | 
			
		||||
{
 | 
			
		||||
    struct packout_buf_allocator *const pba = packout_buf_allocator;
 | 
			
		||||
    obj = (uintptr_t *) obj - 1;
 | 
			
		||||
#if LSQUIC_USE_POOLS
 | 
			
		||||
    struct packout_buf *const pb = obj;
 | 
			
		||||
    SLIST_INSERT_HEAD(&pba->free_packout_bufs, pb, next_free_pb);
 | 
			
		||||
#else
 | 
			
		||||
    free(obj);
 | 
			
		||||
    if (* (uintptr_t *) obj <= PBA_SIZE_THRESH)
 | 
			
		||||
    {
 | 
			
		||||
        struct packout_buf *const pb = obj;
 | 
			
		||||
        SLIST_INSERT_HEAD(&pba->free_packout_bufs, pb, next_free_pb);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
#endif
 | 
			
		||||
        free(obj);
 | 
			
		||||
    --pba->n_out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -739,6 +739,29 @@ settings structure:
 | 
			
		|||
 | 
			
		||||
       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
 | 
			
		||||
 | 
			
		||||
       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.
 | 
			
		||||
 | 
			
		||||
.. 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
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
.. 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.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
        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`
 | 
			
		||||
        and `version`.
 | 
			
		||||
        Base PLPMTU.  If set to zero, it is selected based on the
 | 
			
		||||
        engine settings (see
 | 
			
		||||
        :member:`lsquic_engine_settings.es_base_plpmtu`),
 | 
			
		||||
        QUIC version, and IP version.
 | 
			
		||||
 | 
			
		||||
    :param sess_resume:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
 | 
			
		|||
author = u'LiteSpeed Technologies'
 | 
			
		||||
 | 
			
		||||
# The short X.Y version
 | 
			
		||||
version = u'2.18'
 | 
			
		||||
version = u'2.19'
 | 
			
		||||
# The full version, including alpha/beta/rc tags
 | 
			
		||||
release = u'2.18.2'
 | 
			
		||||
release = u'2.19.0'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- General configuration ---------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,8 +24,8 @@ extern "C" {
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
#define LSQUIC_MAJOR_VERSION 2
 | 
			
		||||
#define LSQUIC_MINOR_VERSION 18
 | 
			
		||||
#define LSQUIC_PATCH_VERSION 2
 | 
			
		||||
#define LSQUIC_MINOR_VERSION 19
 | 
			
		||||
#define LSQUIC_PATCH_VERSION 0
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Engine flags:
 | 
			
		||||
| 
						 | 
				
			
			@ -350,12 +350,24 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
 | 
			
		|||
 */
 | 
			
		||||
#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 */
 | 
			
		||||
#define LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER 60
 | 
			
		||||
 | 
			
		||||
/** By default, do not use no-progress timeout on the client */
 | 
			
		||||
#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 {
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
     */
 | 
			
		||||
    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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -1146,15 +1201,15 @@ lsquic_engine_new (unsigned lsquic_engine_flags,
 | 
			
		|||
 * To let the engine specify QUIC version, use N_LSQVER.  If session resumption
 | 
			
		||||
 * 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':
 | 
			
		||||
 * 1350 for IPv6 and 1370 for IPv4.
 | 
			
		||||
 * If `base_plpmtu' is set to zero, it is selected based on the
 | 
			
		||||
 * engine settings, QUIC version, and IP version.
 | 
			
		||||
 */
 | 
			
		||||
lsquic_conn_t *
 | 
			
		||||
lsquic_engine_connect (lsquic_engine_t *, enum lsquic_version,
 | 
			
		||||
                       const struct sockaddr *local_sa,
 | 
			
		||||
                       const struct sockaddr *peer_sa,
 | 
			
		||||
                       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,
 | 
			
		||||
                       /** Resumption token: optional */
 | 
			
		||||
                       const unsigned char *token, size_t token_sz);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,7 @@ SET(lsquic_STAT_SRCS
 | 
			
		|||
    lsquic_packet_gquic.c
 | 
			
		||||
    lsquic_packet_in.c
 | 
			
		||||
    lsquic_packet_out.c
 | 
			
		||||
    lsquic_packet_resize.c
 | 
			
		||||
    lsquic_packints.c
 | 
			
		||||
    lsquic_parse_Q046.c
 | 
			
		||||
    lsquic_parse_Q050.c
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,6 +47,7 @@ const char *const lsquic_alid2str[] =
 | 
			
		|||
    [AL_PATH_CHAL_1] = "PATH_CHAL_1",
 | 
			
		||||
    [AL_SESS_TICKET] = "SESS_TICKET",
 | 
			
		||||
    [AL_BLOCKED_KA] = "BLOCKED_KA",
 | 
			
		||||
    [AL_MTU_PROBE]  = "MTU_PROBE",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ enum alarm_id {
 | 
			
		|||
    AL_RETX_HSK = AL_RETX_INIT + PNS_HSK,
 | 
			
		||||
    AL_RETX_APP = AL_RETX_INIT + PNS_APP,
 | 
			
		||||
    AL_PING,
 | 
			
		||||
    AL_MTU_PROBE,
 | 
			
		||||
    AL_IDLE,
 | 
			
		||||
    AL_ACK_APP,
 | 
			
		||||
    AL_RET_CIDS,
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +58,7 @@ enum alarm_id_bit {
 | 
			
		|||
    ALBIT_PATH_CHAL_3 = 1 << AL_PATH_CHAL_3,
 | 
			
		||||
    ALBIT_SESS_TICKET = 1 << AL_SESS_TICKET,
 | 
			
		||||
    ALBIT_BLOCKED_KA  = 1 << AL_BLOCKED_KA,
 | 
			
		||||
    ALBIT_MTU_PROBE = 1 << AL_MTU_PROBE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,10 +65,10 @@ int
 | 
			
		|||
lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn,
 | 
			
		||||
          struct lsquic_engine_public *enpub, lsquic_packet_in_t *packet_in)
 | 
			
		||||
{
 | 
			
		||||
    unsigned char *copy;
 | 
			
		||||
 | 
			
		||||
    assert(!(packet_in->pi_flags & PI_OWN_DATA));
 | 
			
		||||
    /* The size should be guarded in lsquic_engine_packet_in(): */
 | 
			
		||||
    assert(packet_in->pi_data_sz <= GQUIC_MAX_PACKET_SZ);
 | 
			
		||||
    unsigned char *const copy = lsquic_mm_get_packet_in_buf(&enpub->enp_mm, 1370);
 | 
			
		||||
    copy = lsquic_mm_get_packet_in_buf(&enpub->enp_mm, packet_in->pi_data_sz);
 | 
			
		||||
    if (!copy)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("cannot allocate memory to copy incoming packet data");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ struct network_path
 | 
			
		|||
    void           *np_peer_ctx;
 | 
			
		||||
    lsquic_cid_t    np_dcid;
 | 
			
		||||
    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)
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +111,9 @@ struct conn_iface
 | 
			
		|||
    void
 | 
			
		||||
    (*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    (*ci_packet_too_large) (struct lsquic_conn *, struct lsquic_packet_out *);
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    (*ci_hsk_done) (struct lsquic_conn *, enum lsquic_hsk_status);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -247,6 +250,15 @@ struct conn_iface
 | 
			
		|||
    /* Optional method.  Only used by IETF connections */
 | 
			
		||||
    void
 | 
			
		||||
    (*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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -357,6 +357,8 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
 | 
			
		|||
    settings->es_delayed_acks    = LSQUIC_DF_DELAYED_ACKS;
 | 
			
		||||
    settings->es_timestamps      = LSQUIC_DF_TIMESTAMPS;
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -612,6 +622,10 @@ lsquic_engine_new (unsigned flags,
 | 
			
		|||
    if (engine->pub.enp_settings.es_noprogress_timeout)
 | 
			
		||||
        engine->pub.enp_noprog_timeout
 | 
			
		||||
            = 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)
 | 
			
		||||
    {
 | 
			
		||||
        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 *peer_sa,
 | 
			
		||||
                       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 *token, size_t token_sz)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1627,11 +1641,11 @@ lsquic_engine_connect (lsquic_engine_t *engine, enum lsquic_version version,
 | 
			
		|||
        versions = 1u << version;
 | 
			
		||||
    if (versions & LSQUIC_IETF_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);
 | 
			
		||||
    else
 | 
			
		||||
        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);
 | 
			
		||||
    if (!conn)
 | 
			
		||||
        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;
 | 
			
		||||
    lsquic_time_t now;
 | 
			
		||||
    unsigned off;
 | 
			
		||||
    unsigned off, skip;
 | 
			
		||||
    size_t count;
 | 
			
		||||
    CONST_BATCH struct out_batch *const batch = sb_ctx->batch;
 | 
			
		||||
    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)
 | 
			
		||||
        lose_matching_packets(engine, batch, n_to_send);
 | 
			
		||||
#endif
 | 
			
		||||
    skip = 0;
 | 
			
		||||
  restart_batch:
 | 
			
		||||
    /* Set sent time before the write to avoid underestimating RTT */
 | 
			
		||||
    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];
 | 
			
		||||
        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;
 | 
			
		||||
        while (++packet_out < end);
 | 
			
		||||
    }
 | 
			
		||||
    n_sent = engine->packets_out(engine->packets_out_ctx, batch->outs,
 | 
			
		||||
                                                                n_to_send);
 | 
			
		||||
    n_sent = engine->packets_out(engine->packets_out_ctx, batch->outs + skip,
 | 
			
		||||
                                                            n_to_send - skip);
 | 
			
		||||
    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->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);
 | 
			
		||||
    }
 | 
			
		||||
    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
 | 
			
		||||
    {
 | 
			
		||||
        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)
 | 
			
		||||
        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);
 | 
			
		||||
        /* `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);
 | 
			
		||||
    }
 | 
			
		||||
    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))
 | 
			
		||||
        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
 | 
			
		||||
     * 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];
 | 
			
		||||
        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)))
 | 
			
		||||
            coi_reactivate(sb_ctx->conns_iter, batch->conns[i]);
 | 
			
		||||
    }
 | 
			
		||||
    return n_sent;
 | 
			
		||||
    return skip + n_sent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,6 +68,7 @@ struct lsquic_engine_public {
 | 
			
		|||
    unsigned char                  *enp_alpn;   /* May be set if not HTTP */
 | 
			
		||||
    /* es_noprogress_timeout converted to microseconds for speed */
 | 
			
		||||
    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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -737,7 +737,11 @@ lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub,
 | 
			
		|||
    lsquic_generate_cid_gquic(&cid);
 | 
			
		||||
    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;
 | 
			
		||||
        else
 | 
			
		||||
            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,
 | 
			
		||||
                                                    packet_out->po_ack2ed);
 | 
			
		||||
    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);
 | 
			
		||||
    packet_out->po_regen_sz += w;
 | 
			
		||||
    if (has_missing)
 | 
			
		||||
| 
						 | 
				
			
			@ -2529,7 +2539,6 @@ get_writeable_packet (struct full_conn *conn, unsigned need_at_least)
 | 
			
		|||
    lsquic_packet_out_t *packet_out;
 | 
			
		||||
    int is_err;
 | 
			
		||||
 | 
			
		||||
    assert(need_at_least <= GQUIC_MAX_PAYLOAD_SZ);
 | 
			
		||||
    packet_out = lsquic_send_ctl_get_writeable_packet(&conn->fc_send_ctl,
 | 
			
		||||
                            PNS_APP, need_at_least, &conn->fc_path, 0, &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;
 | 
			
		||||
    /* TODO Possible optimization: instead of using stream->tosend_off as the
 | 
			
		||||
     * 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
 | 
			
		||||
     * increased.
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -2802,6 +2811,12 @@ generate_stop_waiting_frame (struct full_conn *conn)
 | 
			
		|||
        ABORT_ERROR("gen_stop_waiting_frame failed");
 | 
			
		||||
        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);
 | 
			
		||||
    packet_out->po_regen_sz += sz;
 | 
			
		||||
    packet_out->po_frame_types |= 1 << QUIC_FRAME_STOP_WAITING;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ struct lsquic_conn *
 | 
			
		|||
lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *,
 | 
			
		||||
           unsigned versions,
 | 
			
		||||
               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 *token, size_t);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -142,6 +142,7 @@ enum more_flags
 | 
			
		|||
{
 | 
			
		||||
    MF_VALIDATE_PATH    = 1 << 0,
 | 
			
		||||
    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 network_path         cop_path;
 | 
			
		||||
| 
						 | 
				
			
			@ -298,8 +314,10 @@ struct conn_path
 | 
			
		|||
         */
 | 
			
		||||
        COP_GOT_NONPROB = 1 << 2,
 | 
			
		||||
    }                           cop_flags;
 | 
			
		||||
    unsigned short              cop_max_plpmtu;
 | 
			
		||||
    unsigned char               cop_n_chals;
 | 
			
		||||
    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_max_ack_freq_seqno; /* Incoming */
 | 
			
		||||
    unsigned                    ifc_max_peer_ack_usec;
 | 
			
		||||
    unsigned short              ifc_max_udp_payload;    /* Cached TP */
 | 
			
		||||
    lsquic_time_t               ifc_last_live_update;
 | 
			
		||||
    struct conn_path            ifc_paths[N_PATHS];
 | 
			
		||||
    union {
 | 
			
		||||
| 
						 | 
				
			
			@ -481,6 +500,9 @@ insert_new_dcid (struct ietf_full_conn *, uint64_t seqno,
 | 
			
		|||
static struct conn_cid_elem *
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
migra_begin (struct ietf_full_conn *conn, struct conn_path *copath,
 | 
			
		||||
                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_path.np_dcid = dce->de_cid;
 | 
			
		||||
    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 = IQUIC_MAX_IPv6_PACKET_SZ;
 | 
			
		||||
    else
 | 
			
		||||
        copath->cop_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ;
 | 
			
		||||
    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];
 | 
			
		||||
    copath->cop_path.np_pack_size
 | 
			
		||||
                = calc_base_packet_size(conn, NP_IS_IPv6(CUR_NPATH(conn)));
 | 
			
		||||
    if (conn->ifc_max_udp_payload < copath->cop_path.np_pack_size)
 | 
			
		||||
        copath->cop_path.np_pack_size = conn->ifc_max_udp_payload;
 | 
			
		||||
    memcpy(&copath->cop_path.np_local_addr, NP_LOCAL_SA(CUR_NPATH(conn)),
 | 
			
		||||
                                    sizeof(copath->cop_path.np_local_addr));
 | 
			
		||||
    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_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_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_HSK], &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 *
 | 
			
		||||
lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
 | 
			
		||||
           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 *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);
 | 
			
		||||
 | 
			
		||||
    if (!max_packet_size)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_ipv4)
 | 
			
		||||
            max_packet_size = IQUIC_MAX_IPv4_PACKET_SZ;
 | 
			
		||||
        else
 | 
			
		||||
            max_packet_size = IQUIC_MAX_IPv6_PACKET_SZ;
 | 
			
		||||
    }
 | 
			
		||||
    conn->ifc_paths[0].cop_path.np_pack_size = max_packet_size;
 | 
			
		||||
    if (base_plpmtu)
 | 
			
		||||
        conn->ifc_paths[0].cop_path.np_pack_size
 | 
			
		||||
                                = base_plpmtu - TRANSPORT_OVERHEAD(!is_ipv4);
 | 
			
		||||
    else
 | 
			
		||||
        conn->ifc_paths[0].cop_path.np_pack_size
 | 
			
		||||
                                = calc_base_packet_size(conn, !is_ipv4);
 | 
			
		||||
 | 
			
		||||
    if (0 != ietf_full_conn_init(conn, enpub, flags,
 | 
			
		||||
                                                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");
 | 
			
		||||
        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]
 | 
			
		||||
        = 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);
 | 
			
		||||
    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated TIMESTAMP(%"
 | 
			
		||||
                    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;
 | 
			
		||||
    lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, 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,
 | 
			
		||||
                                                    packet_out->po_ack2ed);
 | 
			
		||||
    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);
 | 
			
		||||
    packet_out->po_regen_sz += w;
 | 
			
		||||
    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);
 | 
			
		||||
    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated MAX_DATA frame, 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);
 | 
			
		||||
    packet_out->po_frame_types |= QUIC_FTBIT_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));
 | 
			
		||||
    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);
 | 
			
		||||
    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;
 | 
			
		||||
    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);
 | 
			
		||||
    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated RETIRE_CONNECTION_ID "
 | 
			
		||||
                                            "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;
 | 
			
		||||
    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);
 | 
			
		||||
    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte STREAMS_BLOCKED "
 | 
			
		||||
                "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;
 | 
			
		||||
    lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
 | 
			
		||||
    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);
 | 
			
		||||
    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated %d-byte MAX_STREAMS "
 | 
			
		||||
                "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;
 | 
			
		||||
    lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
 | 
			
		||||
    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);
 | 
			
		||||
    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "generated BLOCKED frame, 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;
 | 
			
		||||
    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 "
 | 
			
		||||
        "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);
 | 
			
		||||
    packet_out->po_frame_types |= 1 << QUIC_FRAME_MAX_STREAM_DATA;
 | 
			
		||||
    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 "
 | 
			
		||||
        "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);
 | 
			
		||||
    packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM_BLOCKED;
 | 
			
		||||
    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);
 | 
			
		||||
    EV_LOG_GENERATED_STOP_SENDING_FRAME(LSQUIC_LOG_CONN_ID, stream_id,
 | 
			
		||||
                                                                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;
 | 
			
		||||
    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;
 | 
			
		||||
    unsigned need;
 | 
			
		||||
    int sz, s;
 | 
			
		||||
    int sz;
 | 
			
		||||
 | 
			
		||||
    need = conn->ifc_conn.cn_pf->pf_rst_frame_size(stream->id,
 | 
			
		||||
                                    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");
 | 
			
		||||
        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;
 | 
			
		||||
    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)
 | 
			
		||||
    if (0 != lsquic_packet_out_add_stream(packet_out, conn->ifc_pub.mm, stream,
 | 
			
		||||
                            QUIC_FRAME_RST_STREAM, packet_out->po_data_sz, sz))
 | 
			
		||||
    {
 | 
			
		||||
        ABORT_ERROR("adding stream to packet failed: %s", strerror(errno));
 | 
			
		||||
        ABORT_ERROR("adding frame to packet failed: %d", errno);
 | 
			
		||||
        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);
 | 
			
		||||
    LSQ_DEBUG("wrote RST: stream %"PRIu64"; offset 0x%"PRIX64"; 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;
 | 
			
		||||
 | 
			
		||||
    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]
 | 
			
		||||
                                            < 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
 | 
			
		||||
                                = params->tp_numerics[TPI_MAX_UDP_PAYLOAD_SIZE];
 | 
			
		||||
        CUR_NPATH(conn)->np_pack_size = conn->ifc_max_udp_payload;
 | 
			
		||||
        LSQ_DEBUG("decrease packet size to %hu bytes",
 | 
			
		||||
                                                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_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)
 | 
			
		||||
        conn->ifc_send_flags |= SF_SEND_NEW_CID;
 | 
			
		||||
    maybe_create_delayed_streams(conn);
 | 
			
		||||
| 
						 | 
				
			
			@ -3675,6 +3787,12 @@ immediate_close (struct ietf_full_conn *conn)
 | 
			
		|||
        LSQ_WARN("%s failed", __func__);
 | 
			
		||||
        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);
 | 
			
		||||
    packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
 | 
			
		||||
    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");
 | 
			
		||||
        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);
 | 
			
		||||
    packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
 | 
			
		||||
    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");
 | 
			
		||||
        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);
 | 
			
		||||
    packet_out->po_frame_types |= 1 << QUIC_FRAME_PING;
 | 
			
		||||
    LSQ_DEBUG("wrote PING frame");
 | 
			
		||||
| 
						 | 
				
			
			@ -3898,6 +4028,12 @@ generate_handshake_done_frame (struct ietf_full_conn *conn,
 | 
			
		|||
        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);
 | 
			
		||||
    packet_out->po_frame_types |= QUIC_FTBIT_HANDSHAKE_DONE;
 | 
			
		||||
    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);
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
    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;
 | 
			
		||||
    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);
 | 
			
		||||
    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;
 | 
			
		||||
    lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, 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);
 | 
			
		||||
    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);
 | 
			
		||||
    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;
 | 
			
		||||
    lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, 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,
 | 
			
		||||
                                                            int dcid_changed)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_conn *const lconn = &conn->ifc_conn;
 | 
			
		||||
    const struct transport_params *params;
 | 
			
		||||
    struct dcid_elem *dce;
 | 
			
		||||
 | 
			
		||||
    dce = find_unassigned_dcid(conn);
 | 
			
		||||
| 
						 | 
				
			
			@ -5849,17 +6001,11 @@ init_new_path (struct ietf_full_conn *conn, struct conn_path *path,
 | 
			
		|||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (NP_IS_IPv6(&path->cop_path))
 | 
			
		||||
        path->cop_path.np_pack_size = IQUIC_MAX_IPv6_PACKET_SZ;
 | 
			
		||||
    else
 | 
			
		||||
        path->cop_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ;
 | 
			
		||||
    params = lconn->cn_esf.i->esfi_get_peer_transport_params(
 | 
			
		||||
                                                        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];
 | 
			
		||||
    path->cop_path.np_pack_size
 | 
			
		||||
                = calc_base_packet_size(conn, NP_IS_IPv6(&path->cop_path));
 | 
			
		||||
 | 
			
		||||
    if (conn->ifc_max_udp_payload < path->cop_path.np_pack_size)
 | 
			
		||||
        path->cop_path.np_pack_size = conn->ifc_max_udp_payload;
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 * 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
 | 
			
		||||
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);
 | 
			
		||||
    if (n > 0)
 | 
			
		||||
        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_tickable          =  ietf_full_conn_ci_is_tickable, \
 | 
			
		||||
    .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_pending_streams    =  ietf_full_conn_ci_n_pending_streams, \
 | 
			
		||||
    .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_record_addrs         =  ietf_full_conn_ci_record_addrs, \
 | 
			
		||||
    .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_status               =  ietf_full_conn_ci_status, \
 | 
			
		||||
    .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_packet_not_sent     =  ietf_full_conn_ci_packet_not_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 =
 | 
			
		||||
                                                &ietf_full_conn_iface;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,6 +94,7 @@ enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
 | 
			
		|||
    [LSQLM_QPACK_DEC]    = LSQ_LOG_WARN,
 | 
			
		||||
    [LSQLM_PRIO]        = 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] = {
 | 
			
		||||
| 
						 | 
				
			
			@ -136,6 +137,7 @@ const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
 | 
			
		|||
    [LSQLM_QPACK_DEC]    = "qpack-dec",
 | 
			
		||||
    [LSQLM_PRIO]        = "prio",
 | 
			
		||||
    [LSQLM_BW_SAMPLER]  = "bw-sampler",
 | 
			
		||||
    [LSQLM_PACKET_RESIZE] = "packet-resize",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,6 +85,7 @@ enum lsquic_logger_module {
 | 
			
		|||
    LSQLM_QPACK_DEC,
 | 
			
		||||
    LSQLM_PRIO,
 | 
			
		||||
    LSQLM_BW_SAMPLER,
 | 
			
		||||
    LSQLM_PACKET_RESIZE,
 | 
			
		||||
    N_LSQUIC_LOGGER_MODULES
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -826,7 +826,7 @@ mini_stream_read (void *stream, void *buf, size_t len, int *reached_fin)
 | 
			
		|||
 | 
			
		||||
/* Wrapper to throw out reached_fin */
 | 
			
		||||
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;
 | 
			
		||||
    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;
 | 
			
		||||
    len = mc->mc_conn.cn_pf->pf_gen_crypto_frame(
 | 
			
		||||
            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);
 | 
			
		||||
    if (len < 0)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -154,7 +154,7 @@ struct msg_ctx
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    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;
 | 
			
		||||
        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);
 | 
			
		||||
        if (len < 0)
 | 
			
		||||
            return len;
 | 
			
		||||
| 
						 | 
				
			
			@ -513,12 +513,14 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
 | 
			
		|||
 | 
			
		||||
    conn->imc_enpub = enpub;
 | 
			
		||||
    conn->imc_created = packet_in->pi_received;
 | 
			
		||||
    conn->imc_path.np_pack_size = is_ipv4 ? IQUIC_MAX_IPv4_PACKET_SZ
 | 
			
		||||
                                                    : IQUIC_MAX_IPv6_PACKET_SZ;
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
    if (getenv("LSQUIC_CN_PACK_SIZE"))
 | 
			
		||||
        conn->imc_path.np_pack_size = atoi(getenv("LSQUIC_CN_PACK_SIZE"));
 | 
			
		||||
#endif
 | 
			
		||||
    if (enpub->enp_settings.es_base_plpmtu)
 | 
			
		||||
        conn->imc_path.np_pack_size = enpub->enp_settings.es_base_plpmtu
 | 
			
		||||
                                    - (is_ipv4 ? 20 : 40)   /* IP header */
 | 
			
		||||
                                    - 8;                    /* UDP header */
 | 
			
		||||
    else if (is_ipv4)
 | 
			
		||||
        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_esf.i = esfi;
 | 
			
		||||
    conn->imc_conn.cn_enc_session = enc_sess;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ lsquic_mm_init (struct lsquic_mm *mm)
 | 
			
		|||
 | 
			
		||||
    mm->acki = malloc(sizeof(*mm->acki));
 | 
			
		||||
    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_ietf = lsquic_malo_create(sizeof(struct ietf_mini_conn));
 | 
			
		||||
    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->sixteen_k_pages);
 | 
			
		||||
#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.packet_out && mm->malo.dcid_elem
 | 
			
		||||
        && 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_out);
 | 
			
		||||
    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_ietf);
 | 
			
		||||
    free(mm->ack_str);
 | 
			
		||||
| 
						 | 
				
			
			@ -553,7 +553,7 @@ lsquic_mm_mem_used (const struct lsquic_mm *mm)
 | 
			
		|||
    size = sizeof(*mm);
 | 
			
		||||
    size += sizeof(*mm->acki);
 | 
			
		||||
    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_ietf);
 | 
			
		||||
    size += lsquic_malo_mem_used(mm->malo.packet_in);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ struct lsquic_mm {
 | 
			
		|||
    struct ack_info     *acki;
 | 
			
		||||
    struct {
 | 
			
		||||
        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_ietf;/* For struct ietf_mini_conn */
 | 
			
		||||
        struct malo     *retry_conn;    /* For struct retry_conn */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
lsquic_pacer_tick_in (struct pacer *pacer, lsquic_time_t now)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,4 +59,8 @@ lsquic_pacer_loss_event (struct pacer *);
 | 
			
		|||
 | 
			
		||||
#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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,9 @@ enum PACKET_PUBLIC_FLAGS
 | 
			
		|||
  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)                \
 | 
			
		||||
  | (1 << QUIC_FRAME_PATH_CHALLENGE) | (1 << QUIC_FRAME_PATH_RESPONSE) \
 | 
			
		||||
  | (1 << QUIC_FRAME_STOP_WAITING) | (1 << QUIC_FRAME_TIMESTAMP))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,84 +30,84 @@
 | 
			
		|||
#include "lsquic_enc_sess.h"
 | 
			
		||||
 | 
			
		||||
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 *
 | 
			
		||||
srec_one_posi_first (struct packet_out_srec_iter *posi,
 | 
			
		||||
static struct frame_rec *
 | 
			
		||||
frec_one_pofi_first (struct packet_out_frec_iter *pofi,
 | 
			
		||||
                     struct lsquic_packet_out *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    if (packet_out->po_srecs.one.sr_frame_type)
 | 
			
		||||
        return &packet_out->po_srecs.one;
 | 
			
		||||
    if (packet_out->po_frecs.one.fe_frame_type)
 | 
			
		||||
        return &packet_out->po_frecs.one;
 | 
			
		||||
    else
 | 
			
		||||
        return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct stream_rec *
 | 
			
		||||
srec_one_posi_next (struct packet_out_srec_iter *posi)
 | 
			
		||||
static struct frame_rec *
 | 
			
		||||
frec_one_pofi_next (struct packet_out_frec_iter *pofi)
 | 
			
		||||
{
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct stream_rec *
 | 
			
		||||
srec_arr_posi_next (struct packet_out_srec_iter *posi)
 | 
			
		||||
static struct frame_rec *
 | 
			
		||||
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]);
 | 
			
		||||
                ++posi->srec_idx)
 | 
			
		||||
        for (; pofi->frec_idx < sizeof(pofi->cur_frec_arr->frecs) / sizeof(pofi->cur_frec_arr->frecs[0]);
 | 
			
		||||
                ++pofi->frec_idx)
 | 
			
		||||
        {
 | 
			
		||||
            if (posi->cur_srec_arr->srecs[ posi->srec_idx ].sr_frame_type)
 | 
			
		||||
                return &posi->cur_srec_arr->srecs[ posi->srec_idx++ ];
 | 
			
		||||
            if (pofi->cur_frec_arr->frecs[ pofi->frec_idx ].fe_frame_type)
 | 
			
		||||
                return &pofi->cur_frec_arr->frecs[ pofi->frec_idx++ ];
 | 
			
		||||
        }
 | 
			
		||||
        posi->cur_srec_arr = TAILQ_NEXT(posi->cur_srec_arr, next_stream_rec_arr);
 | 
			
		||||
        posi->srec_idx = 0;
 | 
			
		||||
        pofi->cur_frec_arr = TAILQ_NEXT(pofi->cur_frec_arr, next_stream_rec_arr);
 | 
			
		||||
        pofi->frec_idx = 0;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct stream_rec *
 | 
			
		||||
srec_arr_posi_first (struct packet_out_srec_iter *posi,
 | 
			
		||||
static struct frame_rec *
 | 
			
		||||
frec_arr_pofi_first (struct packet_out_frec_iter *pofi,
 | 
			
		||||
                     struct lsquic_packet_out *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    posi->packet_out = packet_out;
 | 
			
		||||
    posi->cur_srec_arr = TAILQ_FIRST(&packet_out->po_srecs.arr);
 | 
			
		||||
    posi->srec_idx = 0;
 | 
			
		||||
    return srec_arr_posi_next(posi);
 | 
			
		||||
    pofi->packet_out = packet_out;
 | 
			
		||||
    pofi->cur_frec_arr = TAILQ_FIRST(&packet_out->po_frecs.arr);
 | 
			
		||||
    pofi->frec_idx = 0;
 | 
			
		||||
    return frec_arr_pofi_next(pofi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct stream_rec * (* const posi_firsts[])
 | 
			
		||||
    (struct packet_out_srec_iter *, struct lsquic_packet_out *) =
 | 
			
		||||
static struct frame_rec * (* const pofi_firsts[])
 | 
			
		||||
    (struct packet_out_frec_iter *, struct lsquic_packet_out *) =
 | 
			
		||||
{
 | 
			
		||||
    srec_one_posi_first,
 | 
			
		||||
    srec_arr_posi_first,
 | 
			
		||||
    frec_one_pofi_first,
 | 
			
		||||
    frec_arr_pofi_first,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct stream_rec * (* const posi_nexts[])
 | 
			
		||||
    (struct packet_out_srec_iter *posi) =
 | 
			
		||||
static struct frame_rec * (* const pofi_nexts[])
 | 
			
		||||
    (struct packet_out_frec_iter *pofi) =
 | 
			
		||||
{
 | 
			
		||||
    srec_one_posi_next,
 | 
			
		||||
    srec_arr_posi_next,
 | 
			
		||||
    frec_one_pofi_next,
 | 
			
		||||
    frec_arr_pofi_next,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct stream_rec *
 | 
			
		||||
lsquic_posi_first (struct packet_out_srec_iter *posi,
 | 
			
		||||
struct frame_rec *
 | 
			
		||||
lsquic_pofi_first (struct packet_out_frec_iter *pofi,
 | 
			
		||||
            lsquic_packet_out_t *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    posi->impl_idx = !!(packet_out->po_flags & PO_SREC_ARR);
 | 
			
		||||
    return posi_firsts[posi->impl_idx](posi, packet_out);
 | 
			
		||||
    pofi->impl_idx = !!(packet_out->po_flags & PO_FREC_ARR);
 | 
			
		||||
    return pofi_firsts[pofi->impl_idx](pofi, packet_out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct stream_rec *
 | 
			
		||||
lsquic_posi_next (struct packet_out_srec_iter *posi)
 | 
			
		||||
struct frame_rec *
 | 
			
		||||
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.
 | 
			
		||||
 */
 | 
			
		||||
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_stream *new_stream,
 | 
			
		||||
                              uintptr_t data,
 | 
			
		||||
                              enum quic_frame_type frame_type,
 | 
			
		||||
                              unsigned short off, unsigned short len)
 | 
			
		||||
{
 | 
			
		||||
    struct stream_rec_arr *srec_arr;
 | 
			
		||||
    struct frame_rec_arr *frec_arr;
 | 
			
		||||
    int last_taken;
 | 
			
		||||
    unsigned i;
 | 
			
		||||
 | 
			
		||||
    assert(!(new_stream->stream_flags & STREAM_FINISHED));
 | 
			
		||||
 | 
			
		||||
    if (!(packet_out->po_flags & PO_SREC_ARR))
 | 
			
		||||
    if (!(packet_out->po_flags & PO_FREC_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_srecs.one.sr_stream      = new_stream;
 | 
			
		||||
            packet_out->po_srecs.one.sr_off         = off;
 | 
			
		||||
            packet_out->po_srecs.one.sr_len         = len;
 | 
			
		||||
            ++new_stream->n_unacked;
 | 
			
		||||
            packet_out->po_frecs.one.fe_frame_type  = frame_type;
 | 
			
		||||
            packet_out->po_frecs.one.fe_u.data      = data;
 | 
			
		||||
            packet_out->po_frecs.one.fe_off         = off;
 | 
			
		||||
            packet_out->po_frecs.one.fe_len         = len;
 | 
			
		||||
            return 0;                           /* Insert in first slot */
 | 
			
		||||
        }
 | 
			
		||||
        srec_arr = lsquic_malo_get(mm->malo.stream_rec_arr);
 | 
			
		||||
        if (!srec_arr)
 | 
			
		||||
        frec_arr = lsquic_malo_get(mm->malo.frame_rec_arr);
 | 
			
		||||
        if (!frec_arr)
 | 
			
		||||
            return -1;
 | 
			
		||||
        memset(srec_arr, 0, sizeof(*srec_arr));
 | 
			
		||||
        srec_arr->srecs[0] = packet_out->po_srecs.one;
 | 
			
		||||
        TAILQ_INIT(&packet_out->po_srecs.arr);
 | 
			
		||||
        TAILQ_INSERT_TAIL(&packet_out->po_srecs.arr, srec_arr,
 | 
			
		||||
        memset(frec_arr, 0, sizeof(*frec_arr));
 | 
			
		||||
        frec_arr->frecs[0] = packet_out->po_frecs.one;
 | 
			
		||||
        TAILQ_INIT(&packet_out->po_frecs.arr);
 | 
			
		||||
        TAILQ_INSERT_TAIL(&packet_out->po_frecs.arr, frec_arr,
 | 
			
		||||
                           next_stream_rec_arr);
 | 
			
		||||
        packet_out->po_flags |= PO_SREC_ARR;
 | 
			
		||||
        packet_out->po_flags |= PO_FREC_ARR;
 | 
			
		||||
        i = 1;
 | 
			
		||||
        goto set_elem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 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;
 | 
			
		||||
    for (i = 0; i < sizeof(srec_arr->srecs) / sizeof(srec_arr->srecs[0]); ++i)
 | 
			
		||||
        if (srec_taken(&srec_arr->srecs[i]))
 | 
			
		||||
    for (i = 0; i < sizeof(frec_arr->frecs) / sizeof(frec_arr->frecs[0]); ++i)
 | 
			
		||||
        if (frec_taken(&frec_arr->frecs[i]))
 | 
			
		||||
            last_taken = i;
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
        srec_arr->srecs[i].sr_frame_type  = frame_type;
 | 
			
		||||
        srec_arr->srecs[i].sr_stream      = new_stream;
 | 
			
		||||
        srec_arr->srecs[i].sr_off         = off;
 | 
			
		||||
        srec_arr->srecs[i].sr_len         = len;
 | 
			
		||||
        ++new_stream->n_unacked;
 | 
			
		||||
        return 0;                   /* Insert in existing srec */
 | 
			
		||||
        frec_arr->frecs[i].fe_frame_type  = frame_type;
 | 
			
		||||
        frec_arr->frecs[i].fe_u.data      = data;
 | 
			
		||||
        frec_arr->frecs[i].fe_off         = off;
 | 
			
		||||
        frec_arr->frecs[i].fe_len         = len;
 | 
			
		||||
        return 0;                   /* Insert in existing frec */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    srec_arr = lsquic_malo_get(mm->malo.stream_rec_arr);
 | 
			
		||||
    if (!srec_arr)
 | 
			
		||||
    frec_arr = lsquic_malo_get(mm->malo.frame_rec_arr);
 | 
			
		||||
    if (!frec_arr)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
    memset(srec_arr, 0, sizeof(*srec_arr));
 | 
			
		||||
    srec_arr->srecs[0].sr_frame_type  = frame_type;
 | 
			
		||||
    srec_arr->srecs[0].sr_stream      = new_stream;
 | 
			
		||||
    srec_arr->srecs[0].sr_off         = off;
 | 
			
		||||
    srec_arr->srecs[0].sr_len         = len;
 | 
			
		||||
    TAILQ_INSERT_TAIL(&packet_out->po_srecs.arr, srec_arr, next_stream_rec_arr);
 | 
			
		||||
    ++new_stream->n_unacked;
 | 
			
		||||
    return 0;                               /* Insert in new srec */
 | 
			
		||||
    memset(frec_arr, 0, sizeof(*frec_arr));
 | 
			
		||||
    frec_arr->frecs[0].fe_frame_type  = frame_type;
 | 
			
		||||
    frec_arr->frecs[0].fe_u.data      = data;
 | 
			
		||||
    frec_arr->frecs[0].fe_off         = off;
 | 
			
		||||
    frec_arr->frecs[0].fe_len         = len;
 | 
			
		||||
    TAILQ_INSERT_TAIL(&packet_out->po_frecs.arr, frec_arr, next_stream_rec_arr);
 | 
			
		||||
    return 0;                               /* Insert in new frec */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
                           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;
 | 
			
		||||
        for (srec_arr = TAILQ_FIRST(&packet_out->po_srecs.arr);
 | 
			
		||||
                                             srec_arr; srec_arr = next)
 | 
			
		||||
        struct frame_rec_arr *frec_arr, *next;
 | 
			
		||||
        for (frec_arr = TAILQ_FIRST(&packet_out->po_frecs.arr);
 | 
			
		||||
                                             frec_arr; frec_arr = next)
 | 
			
		||||
        {
 | 
			
		||||
            next = TAILQ_NEXT(srec_arr, next_stream_rec_arr);
 | 
			
		||||
            lsquic_malo_put(srec_arr);
 | 
			
		||||
            next = TAILQ_NEXT(frec_arr, next_stream_rec_arr);
 | 
			
		||||
            lsquic_malo_put(frec_arr);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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_stream_id_t stream_id)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    struct stream_rec *srec;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    struct frame_rec *frec;
 | 
			
		||||
    unsigned short adj = 0;
 | 
			
		||||
    int n_stream_frames = 0, n_elided = 0;
 | 
			
		||||
    int victim;
 | 
			
		||||
 | 
			
		||||
    for (srec = lsquic_posi_first(&posi, packet_out); srec;
 | 
			
		||||
                                            srec = lsquic_posi_next(&posi))
 | 
			
		||||
    for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
 | 
			
		||||
                                            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;
 | 
			
		||||
 | 
			
		||||
            /* Offsets of all STREAM frames should be adjusted */
 | 
			
		||||
            srec->sr_off -= adj;
 | 
			
		||||
 | 
			
		||||
            if (stream_id)
 | 
			
		||||
            {
 | 
			
		||||
                victim = srec->sr_stream->id == stream_id;
 | 
			
		||||
                victim = frec->fe_stream->id == stream_id;
 | 
			
		||||
                if (victim)
 | 
			
		||||
                {
 | 
			
		||||
                    assert(lsquic_stream_is_reset(srec->sr_stream));
 | 
			
		||||
                    assert(lsquic_stream_is_reset(frec->fe_stream));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                victim = lsquic_stream_is_reset(srec->sr_stream);
 | 
			
		||||
                victim = lsquic_stream_is_reset(frec->fe_stream);
 | 
			
		||||
 | 
			
		||||
            if (victim)
 | 
			
		||||
            {
 | 
			
		||||
                ++n_elided;
 | 
			
		||||
 | 
			
		||||
                /* Move the data and adjust sizes */
 | 
			
		||||
                adj += srec->sr_len;
 | 
			
		||||
                memmove(packet_out->po_data + srec->sr_off,
 | 
			
		||||
                        packet_out->po_data + srec->sr_off + srec->sr_len,
 | 
			
		||||
                        packet_out->po_data_sz - srec->sr_off - srec->sr_len);
 | 
			
		||||
                packet_out->po_data_sz -= srec->sr_len;
 | 
			
		||||
                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;
 | 
			
		||||
 | 
			
		||||
                lsquic_stream_acked(srec->sr_stream, srec->sr_frame_type);
 | 
			
		||||
                srec->sr_frame_type = 0;
 | 
			
		||||
                lsquic_stream_acked(frec->fe_stream, frec->fe_frame_type);
 | 
			
		||||
                frec->fe_frame_type = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -348,385 +362,45 @@ lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out,
 | 
			
		|||
void
 | 
			
		||||
lsquic_packet_out_chop_regen (lsquic_packet_out_t *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    struct stream_rec *srec;
 | 
			
		||||
    unsigned delta;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    struct frame_rec *frec;
 | 
			
		||||
    unsigned short adj;
 | 
			
		||||
 | 
			
		||||
    delta = packet_out->po_regen_sz;
 | 
			
		||||
    packet_out->po_data_sz -= delta;
 | 
			
		||||
    memmove(packet_out->po_data, packet_out->po_data + delta,
 | 
			
		||||
                                                    packet_out->po_data_sz);
 | 
			
		||||
    adj = 0;
 | 
			
		||||
    for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
 | 
			
		||||
                                                frec = lsquic_pofi_next(&pofi))
 | 
			
		||||
    {
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
lsquic_packet_out_ack_streams (lsquic_packet_out_t *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    struct stream_rec *srec;
 | 
			
		||||
    for (srec = lsquic_posi_first(&posi, packet_out); srec;
 | 
			
		||||
                                                srec = lsquic_posi_next(&posi))
 | 
			
		||||
        lsquic_stream_acked(srec->sr_stream, srec->sr_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;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    struct frame_rec *frec;
 | 
			
		||||
    for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
 | 
			
		||||
                                                frec = lsquic_pofi_next(&pofi))
 | 
			
		||||
        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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -746,7 +420,7 @@ lsquic_packet_out_zero_pad (lsquic_packet_out_t *packet_out)
 | 
			
		|||
size_t
 | 
			
		||||
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 = 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)
 | 
			
		||||
        size += 32;
 | 
			
		||||
 | 
			
		||||
    if (packet_out->po_flags & PO_SREC_ARR)
 | 
			
		||||
        TAILQ_FOREACH(srec_arr, &packet_out->po_srecs.arr, next_stream_rec_arr)
 | 
			
		||||
            size += sizeof(*srec_arr);
 | 
			
		||||
    if (packet_out->po_flags & PO_FREC_ARR)
 | 
			
		||||
        TAILQ_FOREACH(frec_arr, &packet_out->po_frecs.arr, next_stream_rec_arr)
 | 
			
		||||
            size += sizeof(*frec_arr);
 | 
			
		||||
 | 
			
		||||
    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 lsquic_stream *stream)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    const struct stream_rec *srec;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    const struct frame_rec *frec;
 | 
			
		||||
    struct stream_frame stream_frame;
 | 
			
		||||
    uint64_t last_offset;
 | 
			
		||||
    int len;
 | 
			
		||||
 | 
			
		||||
    for (srec = lsquic_posi_first(&posi, packet_out); srec;
 | 
			
		||||
                                                srec = lsquic_posi_next(&posi))
 | 
			
		||||
        if (srec->sr_frame_type == QUIC_FRAME_STREAM
 | 
			
		||||
            && srec->sr_stream == stream)
 | 
			
		||||
    for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
 | 
			
		||||
                                                frec = lsquic_pofi_next(&pofi))
 | 
			
		||||
        if (frec->fe_frame_type == QUIC_FRAME_STREAM
 | 
			
		||||
            && frec->fe_stream == stream)
 | 
			
		||||
        {
 | 
			
		||||
            len = pf->pf_parse_stream_frame(packet_out->po_data + srec->sr_off,
 | 
			
		||||
                                            srec->sr_len, &stream_frame);
 | 
			
		||||
            len = pf->pf_parse_stream_frame(packet_out->po_data + frec->fe_off,
 | 
			
		||||
                                            frec->fe_len, &stream_frame);
 | 
			
		||||
            assert(len >= 0);
 | 
			
		||||
            if (len < 0)
 | 
			
		||||
                return -1;
 | 
			
		||||
| 
						 | 
				
			
			@ -790,10 +464,10 @@ lsquic_packet_out_turn_on_fin (struct lsquic_packet_out *packet_out,
 | 
			
		|||
                        + stream_frame.data_frame.df_size;
 | 
			
		||||
            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(
 | 
			
		||||
                    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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,14 +17,15 @@ struct network_path;
 | 
			
		|||
struct parse_funcs;
 | 
			
		||||
struct bwp_state;
 | 
			
		||||
 | 
			
		||||
/* Each stream_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
 | 
			
		||||
 * that has STREAM or RST_STREAM frames inside packet_out.  `sr_frame_type'
 | 
			
		||||
 * specifies the type of the frame; if this value is zero, values of the
 | 
			
		||||
 * other struct members are not valid.  `sr_off' indicates where inside
 | 
			
		||||
 * packet_out->po_data the frame begins and `sr_len' is its length.
 | 
			
		||||
/* Each frame_rec is associated with one packet_out.  packet_out can have
 | 
			
		||||
 * zero or more frame_rec structures.  frame_rec keeps a pointer to a stream
 | 
			
		||||
 * that has STREAM, CRYPTO, or RST_STREAM frames inside packet_out.
 | 
			
		||||
 * `fe_frame_type' specifies the type of the frame; if this value is zero
 | 
			
		||||
 * (this happens when a frame is elided), values of the other struct members
 | 
			
		||||
 * 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
 | 
			
		||||
 *      frames are acknowledged.  This is to make sure that we do not exceed
 | 
			
		||||
 *      maximum allowed number of streams.
 | 
			
		||||
| 
						 | 
				
			
			@ -34,27 +35,37 @@ struct bwp_state;
 | 
			
		|||
 *      occurs if we guessed incorrectly the number of bytes required to
 | 
			
		||||
 *      encode the packet number and the actual number would make packet
 | 
			
		||||
 *      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 lsquic_stream    *sr_stream;
 | 
			
		||||
    unsigned short           sr_off,
 | 
			
		||||
                             sr_len;
 | 
			
		||||
    enum quic_frame_type     sr_frame_type:16;
 | 
			
		||||
struct frame_rec {
 | 
			
		||||
    union {
 | 
			
		||||
        struct lsquic_stream   *stream;
 | 
			
		||||
        uintptr_t               data;
 | 
			
		||||
    }                        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 {
 | 
			
		||||
    TAILQ_ENTRY(stream_rec_arr)     next_stream_rec_arr;
 | 
			
		||||
    struct stream_rec               srecs[
 | 
			
		||||
struct frame_rec_arr {
 | 
			
		||||
    TAILQ_ENTRY(frame_rec_arr)     next_stream_rec_arr;
 | 
			
		||||
    struct frame_rec               frecs[
 | 
			
		||||
      ( 64                              /* Efficient size for malo allocator */
 | 
			
		||||
      - sizeof(TAILQ_ENTRY(stream_rec)) /* next_stream_rec_arr */
 | 
			
		||||
      ) / sizeof(struct stream_rec)
 | 
			
		||||
      - sizeof(TAILQ_ENTRY(frame_rec))  /* next_stream_rec_arr */
 | 
			
		||||
      ) / 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
 | 
			
		||||
| 
						 | 
				
			
			@ -76,14 +87,14 @@ typedef struct lsquic_packet_out
 | 
			
		|||
    enum packet_out_flags {
 | 
			
		||||
            /* TODO XXX Phase out PO_MINI in favor of a more specialized flag:
 | 
			
		||||
             * 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.
 | 
			
		||||
             */
 | 
			
		||||
        PO_MINI     = (1 << 0),         /* Allocated by mini connection */
 | 
			
		||||
        PO_HELLO    = (1 << 1),         /* Packet contains SHLO or CHLO data */
 | 
			
		||||
        PO_SENT     = (1 << 2),         /* Packet has been sent (mini only) */
 | 
			
		||||
        PO_ENCRYPTED= (1 << 3),         /* po_enc_data has encrypted data */
 | 
			
		||||
        PO_SREC_ARR = (1 << 4),
 | 
			
		||||
        PO_FREC_ARR = (1 << 4),
 | 
			
		||||
#define POBIT_SHIFT 5
 | 
			
		||||
        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. */
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +115,7 @@ typedef struct lsquic_packet_out
 | 
			
		|||
        PO_IPv6     = (1 <<20),         /* Set if pmi_allocate was passed is_ipv6=1,
 | 
			
		||||
                                         *   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
 | 
			
		||||
        PO_PNS_HSK  = (1 <<22),         /* PNS bits contain the value of the */
 | 
			
		||||
        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_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. */
 | 
			
		||||
    /* 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
 | 
			
		||||
                                         * of data containing bytes that are
 | 
			
		||||
                                         * 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_token_len;
 | 
			
		||||
    enum header_type   po_header_type:8;
 | 
			
		||||
    unsigned char      po_path_id;
 | 
			
		||||
    enum {
 | 
			
		||||
        POL_GQUIC    = 1 << 0,         /* Used for logging */
 | 
			
		||||
#define POLEV_SHIFT 1
 | 
			
		||||
| 
						 | 
				
			
			@ -149,18 +164,18 @@ typedef struct lsquic_packet_out
 | 
			
		|||
#ifndef NDEBUG
 | 
			
		||||
        POL_HEADER_PROT = 1 << 9,       /* Header protection applied */
 | 
			
		||||
#endif
 | 
			
		||||
        POL_LIMITED     = 1 << 10,      /* Used to credit sc_next_limit if needed. */
 | 
			
		||||
    }                  po_lflags:16;
 | 
			
		||||
    unsigned char     *po_data;
 | 
			
		||||
 | 
			
		||||
    /* A lot of packets contain data belonging to only one stream.  Thus,
 | 
			
		||||
     * `one' is used first.  If this is not enough, any number of
 | 
			
		||||
     * stream_rec_arr structures can be allocated to handle more stream
 | 
			
		||||
     * records.
 | 
			
		||||
    /* A lot of packets contain only one frame.  Thus, `one' is used first.
 | 
			
		||||
     * If this is not enough, any number of frame_rec_arr structures can be
 | 
			
		||||
     * allocated to handle more frame records.
 | 
			
		||||
     */
 | 
			
		||||
    union {
 | 
			
		||||
        struct stream_rec               one;
 | 
			
		||||
        struct stream_rec_arr_tailq     arr;
 | 
			
		||||
    }                  po_srecs;
 | 
			
		||||
        struct frame_rec               one;
 | 
			
		||||
        struct frame_rec_arr_tailq     arr;
 | 
			
		||||
    }                  po_frecs;
 | 
			
		||||
 | 
			
		||||
    /* If PO_ENCRYPTED is set, this points to the buffer that holds encrypted
 | 
			
		||||
     * data.
 | 
			
		||||
| 
						 | 
				
			
			@ -274,19 +289,19 @@ typedef struct lsquic_packet_out
 | 
			
		|||
 | 
			
		||||
#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;
 | 
			
		||||
    struct stream_rec_arr       *cur_srec_arr;
 | 
			
		||||
    unsigned                     srec_idx;
 | 
			
		||||
    struct frame_rec_arr        *cur_frec_arr;
 | 
			
		||||
    unsigned                     frec_idx;
 | 
			
		||||
    int                          impl_idx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct stream_rec *
 | 
			
		||||
lsquic_posi_first (struct packet_out_srec_iter *posi, lsquic_packet_out_t *);
 | 
			
		||||
struct frame_rec *
 | 
			
		||||
lsquic_pofi_first (struct packet_out_frec_iter *pofi, lsquic_packet_out_t *);
 | 
			
		||||
 | 
			
		||||
struct stream_rec *
 | 
			
		||||
lsquic_posi_next (struct packet_out_srec_iter *posi);
 | 
			
		||||
struct frame_rec *
 | 
			
		||||
lsquic_pofi_next (struct packet_out_frec_iter *pofi);
 | 
			
		||||
 | 
			
		||||
lsquic_packet_out_t *
 | 
			
		||||
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 *,
 | 
			
		||||
                        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
 | 
			
		||||
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
 | 
			
		||||
                              struct lsquic_mm *mm,
 | 
			
		||||
| 
						 | 
				
			
			@ -309,10 +329,6 @@ unsigned
 | 
			
		|||
lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_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
 | 
			
		||||
lsquic_packet_out_chop_regen (lsquic_packet_out_t *);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										258
									
								
								src/liblsquic/lsquic_packet_resize.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								src/liblsquic/lsquic_packet_resize.c
									
										
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								src/liblsquic/lsquic_packet_resize.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/liblsquic/lsquic_packet_resize.h
									
										
									
									
									
										Normal 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
 | 
			
		||||
| 
						 | 
				
			
			@ -58,9 +58,6 @@ typedef lsquic_time_t
 | 
			
		|||
/* gsf_: generate stream frame */
 | 
			
		||||
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
 | 
			
		||||
 * frames in version-specific manner.  To begin with, there is difference
 | 
			
		||||
 * 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
 | 
			
		||||
     * 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
 | 
			
		||||
    (*pf_gen_stream_frame) (unsigned char *buf, size_t bufsz,
 | 
			
		||||
                            lsquic_stream_id_t stream_id, uint64_t offset,
 | 
			
		||||
                            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
 | 
			
		||||
    (*pf_parse_stream_frame) (const unsigned char *buf, size_t rem_packet_sz,
 | 
			
		||||
                                                    struct stream_frame *);
 | 
			
		||||
| 
						 | 
				
			
			@ -94,9 +104,6 @@ struct parse_funcs
 | 
			
		|||
    (*pf_parse_crypto_frame) (const unsigned char *buf, size_t rem_packet_sz,
 | 
			
		||||
                                                    struct stream_frame *);
 | 
			
		||||
    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,
 | 
			
		||||
                                    struct ack_info *ack_info, uint8_t exp);
 | 
			
		||||
    int
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -264,7 +264,8 @@ gquic_Q046_parse_packet_in_finish (struct lsquic_packet_in *packet_in,
 | 
			
		|||
 | 
			
		||||
static int
 | 
			
		||||
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);
 | 
			
		||||
    return -1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -796,10 +796,11 @@ gquic_Q050_parse_frame_type (const unsigned char *buf, size_t len)
 | 
			
		|||
 | 
			
		||||
static int
 | 
			
		||||
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,
 | 
			
		||||
                                                size, gcf_read, stream);
 | 
			
		||||
    return lsquic_ietf_v1_gen_crypto_frame(buf, 0x8, stream_id, buf_len,
 | 
			
		||||
                                        offset, fin, size, gsf_read, stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -986,7 +986,8 @@ lsquic_gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
 | 
			
		|||
 | 
			
		||||
static int
 | 
			
		||||
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);
 | 
			
		||||
    return -1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ lsquic_ietf_v1_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_s
 | 
			
		|||
                                        struct stream_frame *stream_frame);
 | 
			
		||||
int
 | 
			
		||||
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,
 | 
			
		||||
        void *stream);
 | 
			
		||||
        size_t buf_len, lsquic_stream_id_t UNUSED_1, uint64_t offset,
 | 
			
		||||
        int UNUSED_2, size_t size, gsf_read_f gsf_read, void *stream);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -443,14 +443,15 @@ ietf_v1_gen_stream_frame (unsigned char *buf, size_t buf_len,
 | 
			
		|||
 | 
			
		||||
int
 | 
			
		||||
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,
 | 
			
		||||
            void *stream)
 | 
			
		||||
            size_t buf_len, lsquic_stream_id_t UNUSED_1, uint64_t offset,
 | 
			
		||||
            int UNUSED_2, size_t size, gsf_read_f gsf_read, void *stream)
 | 
			
		||||
{
 | 
			
		||||
    unsigned char *const end = buf + buf_len;
 | 
			
		||||
    unsigned char *p;
 | 
			
		||||
    unsigned obits, dbits;
 | 
			
		||||
    unsigned olen, dlen;
 | 
			
		||||
    size_t nr, n_avail;
 | 
			
		||||
    int dummy_fin;
 | 
			
		||||
 | 
			
		||||
    obits = vint_val2bits(offset);
 | 
			
		||||
    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);
 | 
			
		||||
    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 <= 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
 | 
			
		||||
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,
 | 
			
		||||
                                                size, gcf_read, stream);
 | 
			
		||||
    return lsquic_ietf_v1_gen_crypto_frame(buf, 0x6, buf_len, stream_id,
 | 
			
		||||
                                        offset, fin, size, gsf_read, stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,9 @@
 | 
			
		|||
#include "lsquic_packet_common.h"
 | 
			
		||||
#include "lsquic_alarmset.h"
 | 
			
		||||
#include "lsquic_parse.h"
 | 
			
		||||
#include "lsquic_packet_in.h"
 | 
			
		||||
#include "lsquic_packet_out.h"
 | 
			
		||||
#include "lsquic_packet_resize.h"
 | 
			
		||||
#include "lsquic_senhist.h"
 | 
			
		||||
#include "lsquic_rtt.h"
 | 
			
		||||
#include "lsquic_cubic.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -125,6 +127,9 @@ send_ctl_can_send_pre_hsk (struct lsquic_send_ctl *ctl);
 | 
			
		|||
static int
 | 
			
		||||
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
 | 
			
		||||
static
 | 
			
		||||
#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)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_send_ctl_t *ctl = ctx;
 | 
			
		||||
    struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
 | 
			
		||||
    lsquic_packet_out_t *packet_out;
 | 
			
		||||
    enum packnum_space pns;
 | 
			
		||||
    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);
 | 
			
		||||
        send_ctl_expire(ctl, pns, EXFI_ALL);
 | 
			
		||||
        ctl->sc_ci->cci_timeout(CGP(ctl));
 | 
			
		||||
        if (lconn->cn_if->ci_retx_timeout)
 | 
			
		||||
            lconn->cn_if->ci_retx_timeout(lconn);
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
    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
 | 
			
		||||
largest_retx_packet_number (const struct lsquic_send_ctl *ctl,
 | 
			
		||||
                                                    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,
 | 
			
		||||
                                                    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);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (largest_retx_packno
 | 
			
		||||
            && (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)
 | 
			
		||||
        {
 | 
			
		||||
            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,
 | 
			
		||||
                                                    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;
 | 
			
		||||
            else { /* don't count it as a loss */; }
 | 
			
		||||
            (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
 | 
			
		||||
lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
 | 
			
		||||
                         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_ce_cnt += lsquic_packet_out_ecn(packet_out) == ECN_CE;
 | 
			
		||||
            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);
 | 
			
		||||
                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;
 | 
			
		||||
#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
 | 
			
		||||
            {
 | 
			
		||||
                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 *
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
  get_next_lost:
 | 
			
		||||
| 
						 | 
				
			
			@ -1265,9 +1316,27 @@ send_ctl_next_lost (lsquic_send_ctl_t *ctl)
 | 
			
		|||
        if (!lsquic_send_ctl_can_send(ctl))
 | 
			
		||||
            return NULL;
 | 
			
		||||
 | 
			
		||||
        TAILQ_REMOVE(&ctl->sc_lost_packets, lost_packet, po_next);
 | 
			
		||||
        lost_packet->po_flags &= ~PO_LOST;
 | 
			
		||||
        lost_packet->po_flags |= PO_RETX;
 | 
			
		||||
        if (packet_out_total_sz(lost_packet) <= SC_PACK_SIZE(ctl))
 | 
			
		||||
        {
 | 
			
		||||
  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;
 | 
			
		||||
| 
						 | 
				
			
			@ -1770,10 +1839,10 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
 | 
			
		|||
    if (dec_limit)
 | 
			
		||||
    {
 | 
			
		||||
        --ctl->sc_next_limit;
 | 
			
		||||
        packet_out->po_flags |= PO_LIMITED;
 | 
			
		||||
        packet_out->po_lflags |= POL_LIMITED;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        packet_out->po_flags &= ~PO_LIMITED;
 | 
			
		||||
        packet_out->po_lflags &= ~POL_LIMITED;
 | 
			
		||||
 | 
			
		||||
    if (UNLIKELY(packet_out->po_header_type == HETY_INITIAL)
 | 
			
		||||
                    && !(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)
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
    LSQ_DEBUG("packet %"PRIu64" has been delayed", packet_out->po_packno);
 | 
			
		||||
#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;
 | 
			
		||||
            if (ctl->sc_token)
 | 
			
		||||
            {
 | 
			
		||||
                (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
 | 
			
		||||
            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
 | 
			
		||||
split_buffered_packet (lsquic_send_ctl_t *ctl,
 | 
			
		||||
        enum buf_packet_type packet_type, lsquic_packet_out_t *packet_out,
 | 
			
		||||
        enum packno_bits bits, unsigned excess_bytes)
 | 
			
		||||
        enum buf_packet_type packet_type, struct lsquic_packet_out *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    struct buf_packet_q *const packet_q =
 | 
			
		||||
                                    &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);
 | 
			
		||||
 | 
			
		||||
    new_packet_out = send_ctl_allocate_packet(ctl, bits, 0,
 | 
			
		||||
                        lsquic_packet_out_pns(packet_out), packet_out->po_path);
 | 
			
		||||
    if (!new_packet_out)
 | 
			
		||||
        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, excess_bytes))
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
        lsquic_packet_out_set_packno_bits(packet_out, bits);
 | 
			
		||||
        TAILQ_INSERT_AFTER(&packet_q->bpq_packets, packet_out, new_packet_out,
 | 
			
		||||
                           po_next);
 | 
			
		||||
        ++count;
 | 
			
		||||
        TAILQ_INSERT_BEFORE(packet_out, new, po_next);
 | 
			
		||||
        ++packet_q->bpq_count;
 | 
			
		||||
        LSQ_DEBUG("Add split packet to buffered queue #%u; count: %u",
 | 
			
		||||
                  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;
 | 
			
		||||
    }
 | 
			
		||||
    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];
 | 
			
		||||
    const struct parse_funcs *const pf = ctl->sc_conn_pub->lconn->cn_pf;
 | 
			
		||||
    lsquic_packet_out_t *packet_out;
 | 
			
		||||
    unsigned used, excess;
 | 
			
		||||
    unsigned used;
 | 
			
		||||
 | 
			
		||||
    assert(lsquic_send_ctl_schedule_stream_packets_immediately(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
 | 
			
		||||
                && 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, bits, excess))
 | 
			
		||||
                {
 | 
			
		||||
                if (0 == split_buffered_packet(ctl, packet_type, packet_out))
 | 
			
		||||
                    packet_out = TAILQ_FIRST(&packet_q->bpq_packets);
 | 
			
		||||
                else
 | 
			
		||||
                    return -1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
strip_trailing_padding (struct lsquic_packet_out *packet_out)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    const struct stream_rec *srec;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    const struct frame_rec *frec;
 | 
			
		||||
    unsigned off;
 | 
			
		||||
 | 
			
		||||
    off = 0;
 | 
			
		||||
    for (srec = lsquic_posi_first(&posi, packet_out); srec;
 | 
			
		||||
                                                srec = lsquic_posi_next(&posi))
 | 
			
		||||
        off = srec->sr_off + srec->sr_len;
 | 
			
		||||
    for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
 | 
			
		||||
                                                frec = lsquic_pofi_next(&pofi))
 | 
			
		||||
        off = frec->fe_off + frec->fe_len;
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl,
 | 
			
		||||
                                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;
 | 
			
		||||
    size_t sz;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2839,38 +3038,9 @@ lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl,
 | 
			
		|||
            strip_trailing_padding(packet_out);
 | 
			
		||||
 | 
			
		||||
        sz = lconn->cn_pf->pf_packout_size(lconn, packet_out);
 | 
			
		||||
        if (sz > 1200)
 | 
			
		||||
        {
 | 
			
		||||
            const enum packno_bits bits = lsquic_send_ctl_calc_packno_bits(ctl);
 | 
			
		||||
            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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (sz > packet_out->po_path->np_pack_size
 | 
			
		||||
                                && 0 != split_lost_packet(ctl, packet_out))
 | 
			
		||||
            return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl, struct network_path *old,
 | 
			
		||||
                                                    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");
 | 
			
		||||
 | 
			
		||||
    lsquic_send_ctl_resize(ctl);
 | 
			
		||||
 | 
			
		||||
    memset(&ctl->sc_conn_pub->rtt_stats, 0,
 | 
			
		||||
                                    sizeof(ctl->sc_conn_pub->rtt_stats));
 | 
			
		||||
    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
 | 
			
		||||
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");
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -365,6 +365,9 @@ void
 | 
			
		|||
lsquic_send_ctl_repath (struct lsquic_send_ctl *, struct network_path *old,
 | 
			
		||||
                                struct network_path *new);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
lsquic_send_ctl_resize (struct lsquic_send_ctl *);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
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_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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,6 +52,7 @@
 | 
			
		|||
#include "lsquic_conn.h"
 | 
			
		||||
#include "lsquic_data_in_if.h"
 | 
			
		||||
#include "lsquic_parse.h"
 | 
			
		||||
#include "lsquic_packet_in.h"
 | 
			
		||||
#include "lsquic_packet_out.h"
 | 
			
		||||
#include "lsquic_engine_public.h"
 | 
			
		||||
#include "lsquic_senhist.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -585,6 +586,7 @@ lsquic_stream_destroy (lsquic_stream_t *stream)
 | 
			
		|||
                                                            STREAM_ONNEW_DONE)
 | 
			
		||||
    {
 | 
			
		||||
        stream->stream_flags |= STREAM_ONCLOSE_DONE;
 | 
			
		||||
        SM_HISTORY_APPEND(stream, SHE_ONCLOSE_CALL);
 | 
			
		||||
        stream->stream_if->on_close(stream, stream->st_ctx);
 | 
			
		||||
    }
 | 
			
		||||
    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
 | 
			
		||||
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;
 | 
			
		||||
    len = pf->pf_gen_crypto_frame(packet_out->po_data + packet_out->po_data_sz,
 | 
			
		||||
                lsquic_packet_out_avail(packet_out), stream->tosend_off,
 | 
			
		||||
                size, crypto_frame_gen_read, fg_ctx);
 | 
			
		||||
                lsquic_packet_out_avail(packet_out), 0, stream->tosend_off, 0,
 | 
			
		||||
                size, frame_std_gen_read, fg_ctx);
 | 
			
		||||
    if (len < 0)
 | 
			
		||||
        return len;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,7 @@ SET(TESTS
 | 
			
		|||
    hkdf
 | 
			
		||||
    lsquic_hash
 | 
			
		||||
    packet_out
 | 
			
		||||
    packet_resize
 | 
			
		||||
    packno_len
 | 
			
		||||
    parse_packet_in
 | 
			
		||||
    purga
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,7 +74,7 @@ struct test_ctx {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    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)
 | 
			
		||||
        {
 | 
			
		||||
            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);
 | 
			
		||||
            assert(-1 == len);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Test that it succeeds now: */
 | 
			
		||||
        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);
 | 
			
		||||
        assert(len == (int) min);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    if (test->len > 0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,7 +96,7 @@ lsquic_stream_acked (lsquic_stream_t *stream, enum quic_frame_type frame_type)
 | 
			
		|||
static void
 | 
			
		||||
elide_single_stream_frame (void)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    struct lsquic_engine_public enpub;
 | 
			
		||||
    lsquic_stream_t streams[1];
 | 
			
		||||
    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],
 | 
			
		||||
                                                QUIC_FRAME_STREAM, off, len);
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    lsquic_packet_out_elide_reset_stream_frames(packet_out, 0);
 | 
			
		||||
    assert(0 == streams[0].n_unacked);
 | 
			
		||||
    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_mm_cleanup(&enpub.enp_mm);
 | 
			
		||||
| 
						 | 
				
			
			@ -142,11 +142,11 @@ elide_single_stream_frame (void)
 | 
			
		|||
static void
 | 
			
		||||
shrink_packet_post_elision (void)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    struct lsquic_engine_public enpub;
 | 
			
		||||
    lsquic_stream_t streams[2];
 | 
			
		||||
    lsquic_packet_out_t *packet_out;
 | 
			
		||||
    const struct stream_rec *srec;
 | 
			
		||||
    const struct frame_rec *frec;
 | 
			
		||||
    int len, off = 0;
 | 
			
		||||
    unsigned char stream2_data[0x1000];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +189,7 @@ shrink_packet_post_elision (void)
 | 
			
		|||
 | 
			
		||||
    assert(1 == streams[0].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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -197,8 +197,8 @@ shrink_packet_post_elision (void)
 | 
			
		|||
    assert(0 == streams[0].n_unacked);
 | 
			
		||||
 | 
			
		||||
    assert(QUIC_FTBIT_STREAM == packet_out->po_frame_types);
 | 
			
		||||
    srec = lsquic_posi_first(&posi, packet_out);
 | 
			
		||||
    assert(srec->sr_stream == &streams[1]);
 | 
			
		||||
    frec = lsquic_pofi_first(&pofi, packet_out);
 | 
			
		||||
    assert(frec->fe_stream == &streams[1]);
 | 
			
		||||
    assert(packet_out->po_data_sz == exp);
 | 
			
		||||
 | 
			
		||||
    lsquic_packet_out_destroy(packet_out, &enpub, NULL);
 | 
			
		||||
| 
						 | 
				
			
			@ -222,11 +222,11 @@ shrink_packet_post_elision (void)
 | 
			
		|||
static void
 | 
			
		||||
elide_three_stream_frames (int chop_regen)
 | 
			
		||||
{
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    struct lsquic_engine_public enpub;
 | 
			
		||||
    lsquic_stream_t streams[5];
 | 
			
		||||
    lsquic_packet_out_t *packet_out, *ref_out;
 | 
			
		||||
    struct stream_rec *srec;
 | 
			
		||||
    struct frame_rec *frec;
 | 
			
		||||
    unsigned short b_off, d_off;
 | 
			
		||||
    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);
 | 
			
		||||
        /* This is fake data for regeneration */
 | 
			
		||||
        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;
 | 
			
		||||
        /* STREAM B */
 | 
			
		||||
        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);
 | 
			
		||||
        /* This is fake data for regeneration */
 | 
			
		||||
        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;
 | 
			
		||||
        /* STREAM A */
 | 
			
		||||
        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)));
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_first(&posi, packet_out);
 | 
			
		||||
    assert(srec->sr_stream == &streams[1]);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    assert(srec->sr_off == b_off - (chop_regen ? 5 : 0));
 | 
			
		||||
    frec = lsquic_pofi_first(&pofi, packet_out);
 | 
			
		||||
    if (!chop_regen)
 | 
			
		||||
        frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    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);
 | 
			
		||||
    assert(srec->sr_stream == &streams[0]);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_RST_STREAM);
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(frec->fe_stream == &streams[0]);
 | 
			
		||||
    assert(frec->fe_frame_type == QUIC_FRAME_RST_STREAM);
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_next(&posi);
 | 
			
		||||
    assert(srec->sr_stream == &streams[3]);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    assert(srec->sr_off == d_off - (chop_regen ? 5 : 0));
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(frec->fe_stream == &streams[3]);
 | 
			
		||||
    assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    assert(frec->fe_off == d_off - (chop_regen ? 5 : 0));
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_next(&posi);
 | 
			
		||||
    assert(!srec);
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(!frec);
 | 
			
		||||
 | 
			
		||||
    lsquic_packet_out_destroy(packet_out, &enpub, NULL);
 | 
			
		||||
    lsquic_packet_out_destroy(ref_out, &enpub, NULL);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    unsigned char *p = begin;
 | 
			
		||||
    unsigned char *const end = p + bufsz;
 | 
			
		||||
    const struct stream_rec *srec;
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    const struct frame_rec *frec;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    struct lsquic_packet_out *packet_out;
 | 
			
		||||
    struct stream_frame frame;
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next)
 | 
			
		||||
        for (srec = lsquic_posi_first(&posi, packet_out); srec;
 | 
			
		||||
                                                    srec = lsquic_posi_next(&posi))
 | 
			
		||||
        for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
 | 
			
		||||
                                                    frec = lsquic_pofi_next(&pofi))
 | 
			
		||||
        {
 | 
			
		||||
            if (fullcheck)
 | 
			
		||||
            {
 | 
			
		||||
                assert(srec->sr_frame_type == expected_type);
 | 
			
		||||
                assert(frec->fe_frame_type == expected_type);
 | 
			
		||||
                if (0 && packet_out->po_packno != 1)
 | 
			
		||||
                {
 | 
			
		||||
                    /* First packet may contain two stream frames, do not
 | 
			
		||||
                     * check it.
 | 
			
		||||
                     */
 | 
			
		||||
                    assert(!lsquic_posi_next(&posi));
 | 
			
		||||
                    assert(!lsquic_pofi_next(&pofi));
 | 
			
		||||
                    if (TAILQ_NEXT(packet_out, po_next))
 | 
			
		||||
                    {
 | 
			
		||||
                        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 &&
 | 
			
		||||
                                            srec->sr_stream->id == stream_id)
 | 
			
		||||
            if (frec->fe_frame_type == expected_type &&
 | 
			
		||||
                                            frec->fe_stream->id == stream_id)
 | 
			
		||||
            {
 | 
			
		||||
                assert(!fin);
 | 
			
		||||
                if (QUIC_FRAME_STREAM == expected_type)
 | 
			
		||||
                    len = pf_local->pf_parse_stream_frame(packet_out->po_data + srec->sr_off,
 | 
			
		||||
                        packet_out->po_data_sz - srec->sr_off, &frame);
 | 
			
		||||
                    len = pf_local->pf_parse_stream_frame(packet_out->po_data + frec->fe_off,
 | 
			
		||||
                        packet_out->po_data_sz - frec->fe_off, &frame);
 | 
			
		||||
                else
 | 
			
		||||
                    len = pf_local->pf_parse_crypto_frame(packet_out->po_data + srec->sr_off,
 | 
			
		||||
                        packet_out->po_data_sz - srec->sr_off, &frame);
 | 
			
		||||
                    len = pf_local->pf_parse_crypto_frame(packet_out->po_data + frec->fe_off,
 | 
			
		||||
                        packet_out->po_data_sz - frec->fe_off, &frame);
 | 
			
		||||
                assert(len > 0);
 | 
			
		||||
                if (QUIC_FRAME_STREAM == expected_type)
 | 
			
		||||
                    assert(frame.stream_id == srec->sr_stream->id);
 | 
			
		||||
                    assert(frame.stream_id == frec->fe_stream->id);
 | 
			
		||||
                else
 | 
			
		||||
                    assert(frame.stream_id == ~0ULL);
 | 
			
		||||
                /* 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);
 | 
			
		||||
                    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);
 | 
			
		||||
                p += frame.data_frame.df_size;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,10 +37,10 @@ int
 | 
			
		|||
main (void)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_engine_public enpub;
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    lsquic_packet_out_t *packet_out;
 | 
			
		||||
    struct lsquic_stream streams[6];
 | 
			
		||||
    struct stream_rec *srec;
 | 
			
		||||
    struct frame_rec *frec;
 | 
			
		||||
 | 
			
		||||
    memset(&enpub, 0, sizeof(enpub));
 | 
			
		||||
    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[5], QUIC_FRAME_STREAM,  13, 1);
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_first(&posi, packet_out);
 | 
			
		||||
    assert(srec->sr_stream == &streams[0]);
 | 
			
		||||
    assert(srec->sr_off == 7);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    frec = lsquic_pofi_first(&pofi, packet_out);
 | 
			
		||||
    assert(frec->fe_stream == &streams[0]);
 | 
			
		||||
    assert(frec->fe_off == 7);
 | 
			
		||||
    assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_next(&posi);
 | 
			
		||||
    assert(srec->sr_stream == &streams[1]);
 | 
			
		||||
    assert(srec->sr_off == 8);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(frec->fe_stream == &streams[1]);
 | 
			
		||||
    assert(frec->fe_off == 8);
 | 
			
		||||
    assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_next(&posi);
 | 
			
		||||
    assert(srec->sr_stream == &streams[2]);
 | 
			
		||||
    assert(srec->sr_off == 9);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(frec->fe_stream == &streams[2]);
 | 
			
		||||
    assert(frec->fe_off == 9);
 | 
			
		||||
    assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_next(&posi);
 | 
			
		||||
    assert(srec->sr_stream == &streams[1]);
 | 
			
		||||
    assert(srec->sr_off == 10);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_RST_STREAM);
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(frec->fe_stream == &streams[1]);
 | 
			
		||||
    assert(frec->fe_off == 10);
 | 
			
		||||
    assert(frec->fe_frame_type == QUIC_FRAME_RST_STREAM);
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_next(&posi);
 | 
			
		||||
    assert(srec->sr_stream == &streams[3]);
 | 
			
		||||
    assert(srec->sr_off == 11);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(frec->fe_stream == &streams[3]);
 | 
			
		||||
    assert(frec->fe_off == 11);
 | 
			
		||||
    assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_next(&posi);
 | 
			
		||||
    assert(srec->sr_stream == &streams[4]);
 | 
			
		||||
    assert(srec->sr_off == 12);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(frec->fe_stream == &streams[4]);
 | 
			
		||||
    assert(frec->fe_off == 12);
 | 
			
		||||
    assert(frec->fe_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
 | 
			
		||||
    srec = lsquic_posi_next(&posi);
 | 
			
		||||
    assert(srec->sr_stream == &streams[5]);
 | 
			
		||||
    assert(srec->sr_off == 13);
 | 
			
		||||
    assert(srec->sr_frame_type == QUIC_FRAME_STREAM);
 | 
			
		||||
    frec = lsquic_pofi_next(&pofi);
 | 
			
		||||
    assert(frec->fe_stream == &streams[5]);
 | 
			
		||||
    assert(frec->fe_off == 13);
 | 
			
		||||
    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);
 | 
			
		||||
    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);
 | 
			
		||||
    return 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1640
									
								
								tests/test_packet_resize.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1640
									
								
								tests/test_packet_resize.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    unsigned char *p = begin;
 | 
			
		||||
    unsigned char *const end = p + bufsz;
 | 
			
		||||
    const struct stream_rec *srec;
 | 
			
		||||
    struct packet_out_srec_iter posi;
 | 
			
		||||
    const struct frame_rec *frec;
 | 
			
		||||
    struct packet_out_frec_iter pofi;
 | 
			
		||||
    struct lsquic_packet_out *packet_out;
 | 
			
		||||
    struct stream_frame frame;
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next)
 | 
			
		||||
        for (srec = lsquic_posi_first(&posi, packet_out); srec;
 | 
			
		||||
                                                srec = lsquic_posi_next(&posi))
 | 
			
		||||
        for (frec = lsquic_pofi_first(&pofi, packet_out); frec;
 | 
			
		||||
                                                frec = lsquic_pofi_next(&pofi))
 | 
			
		||||
        {
 | 
			
		||||
            if (fullcheck)
 | 
			
		||||
            {
 | 
			
		||||
                assert(srec->sr_frame_type == expected_type);
 | 
			
		||||
                assert(frec->fe_frame_type == expected_type);
 | 
			
		||||
                if (packet_out->po_packno != 1)
 | 
			
		||||
                {
 | 
			
		||||
                    /* First packet may contain two stream frames, do not
 | 
			
		||||
                     * check it.
 | 
			
		||||
                     */
 | 
			
		||||
                    assert(!lsquic_posi_next(&posi));
 | 
			
		||||
                    assert(!lsquic_pofi_next(&pofi));
 | 
			
		||||
                    if (TAILQ_NEXT(packet_out, po_next))
 | 
			
		||||
                    {
 | 
			
		||||
                        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 &&
 | 
			
		||||
                                            srec->sr_stream->id == stream_id)
 | 
			
		||||
            if (frec->fe_frame_type == expected_type &&
 | 
			
		||||
                                            frec->fe_stream->id == stream_id)
 | 
			
		||||
            {
 | 
			
		||||
                assert(!fin);
 | 
			
		||||
                if (QUIC_FRAME_STREAM == expected_type)
 | 
			
		||||
                    len = pf_local->pf_parse_stream_frame(packet_out->po_data + srec->sr_off,
 | 
			
		||||
                        packet_out->po_data_sz - srec->sr_off, &frame);
 | 
			
		||||
                    len = pf_local->pf_parse_stream_frame(packet_out->po_data + frec->fe_off,
 | 
			
		||||
                        packet_out->po_data_sz - frec->fe_off, &frame);
 | 
			
		||||
                else
 | 
			
		||||
                    len = pf_local->pf_parse_crypto_frame(packet_out->po_data + srec->sr_off,
 | 
			
		||||
                        packet_out->po_data_sz - srec->sr_off, &frame);
 | 
			
		||||
                    len = pf_local->pf_parse_crypto_frame(packet_out->po_data + frec->fe_off,
 | 
			
		||||
                        packet_out->po_data_sz - frec->fe_off, &frame);
 | 
			
		||||
                assert(len > 0);
 | 
			
		||||
                if (QUIC_FRAME_STREAM == expected_type)
 | 
			
		||||
                    assert(frame.stream_id == srec->sr_stream->id);
 | 
			
		||||
                    assert(frame.stream_id == frec->fe_stream->id);
 | 
			
		||||
                else
 | 
			
		||||
                    assert(frame.stream_id == ~0ULL);
 | 
			
		||||
                /* 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);
 | 
			
		||||
                    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);
 | 
			
		||||
                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
 | 
			
		||||
 * into new packet.
 | 
			
		||||
 */
 | 
			
		||||
/* Test two: large frame in the middle */
 | 
			
		||||
static void
 | 
			
		||||
test_bad_packbits_guess_2 (void)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -2455,8 +2453,8 @@ test_bad_packbits_guess_2 (void)
 | 
			
		|||
    assert(1 == streams[2]->n_unacked);
 | 
			
		||||
    ack_packet(&tobjs.send_ctl, 1);
 | 
			
		||||
    assert(0 == streams[0]->n_unacked);
 | 
			
		||||
    assert(1 == streams[1]->n_unacked);
 | 
			
		||||
    assert(0 == streams[2]->n_unacked);
 | 
			
		||||
    assert(0 == streams[1]->n_unacked);
 | 
			
		||||
    assert(1 == streams[2]->n_unacked);
 | 
			
		||||
    ack_packet(&tobjs.send_ctl, 2);
 | 
			
		||||
    assert(0 == streams[0]->n_unacked);
 | 
			
		||||
    assert(0 == streams[1]->n_unacked);
 | 
			
		||||
| 
						 | 
				
			
			@ -2508,7 +2506,7 @@ test_bad_packbits_guess_3 (void)
 | 
			
		|||
    assert(1 == streams[0]->n_unacked);
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
                                                g_ctl_settings.tcs_bp_type);
 | 
			
		||||
    assert(2 == lsquic_send_ctl_n_scheduled(&tobjs.send_ctl));
 | 
			
		||||
| 
						 | 
				
			
			@ -2522,12 +2520,12 @@ test_bad_packbits_guess_3 (void)
 | 
			
		|||
 | 
			
		||||
    /* Verify packets */
 | 
			
		||||
    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(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
 | 
			
		||||
    lsquic_send_ctl_sent_packet(&tobjs.send_ctl, packet_out);
 | 
			
		||||
    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(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM));
 | 
			
		||||
    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
 | 
			
		||||
{
 | 
			
		||||
    const unsigned char    *buf;
 | 
			
		||||
| 
						 | 
				
			
			@ -3164,6 +3284,9 @@ main (int argc, char **argv)
 | 
			
		|||
    test_bad_packbits_guess_2();
 | 
			
		||||
    test_bad_packbits_guess_3();
 | 
			
		||||
 | 
			
		||||
    test_resize_buffered();
 | 
			
		||||
    test_resize_scheduled();
 | 
			
		||||
 | 
			
		||||
    main_test_packetization();
 | 
			
		||||
 | 
			
		||||
    enum lsquic_version ver;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue