mirror of
				https://gitea.invidious.io/iv-org/litespeed-quic.git
				synced 2024-08-15 00:53:43 +00:00 
			
		
		
		
	Release 2.16.1
- [FEATURE] Use "no-progress timeout" after which connection is closed. - [BUGFIX] Select new SCID when current SCID is retired. - [BUGFIX] Don't warn about dropped Initial packet sequence gaps during mini/full handoff. - [BUGFIX] Send correct conn error when HTTP/3 frame is truncated. - [BUGFIX] Mini conn: consider amplification when deciding to return TICK_SEND. - [BUGFIX] Don't double-count tag length in amplification logic. - [BUGFIX] Don't squeeze out lone path challenges. - [BUGFIX] Log messages dealing with scheduled packet queue squeezing. - [BUGFIX] don't wipe current path if no path challenge responses come back. - [BUGFIX] When path is reset, don't lose path_id which is used for logging. - Downgrade flow control violations to info log level from warnings. - Fix connection cap extra check, avoid checks in nested calls. - Fix some unit tests when extra checks are enabled. - Use ls-hpack 2.2.1. - Turn off unconditional extra checks for IETF clients. - Extra checks: don't verify sent size of hello packets. Client changes DCID length and this check will fail.
This commit is contained in:
		
							parent
							
								
									6bca16f0d2
								
							
						
					
					
						commit
						8ae5ecb45e
					
				
					 22 changed files with 369 additions and 54 deletions
				
			
		
							
								
								
									
										24
									
								
								CHANGELOG
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								CHANGELOG
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,3 +1,27 @@
 | 
			
		|||
2020-06-09
 | 
			
		||||
    - 2.16.1
 | 
			
		||||
    - [FEATURE] Use "no-progress timeout" after which connection is closed.
 | 
			
		||||
    - [BUGFIX] Select new SCID when current SCID is retired.
 | 
			
		||||
    - [BUGFIX] Don't warn about dropped Initial packet sequence gaps during
 | 
			
		||||
      mini/full handoff.
 | 
			
		||||
    - [BUGFIX] Send correct conn error when HTTP/3 frame is truncated.
 | 
			
		||||
    - [BUGFIX] Mini conn: consider amplification when deciding to return
 | 
			
		||||
      TICK_SEND.
 | 
			
		||||
    - [BUGFIX] Don't double-count tag length in amplification logic.
 | 
			
		||||
    - [BUGFIX] Don't squeeze out lone path challenges.
 | 
			
		||||
    - [BUGFIX] Log messages dealing with scheduled packet queue squeezing.
 | 
			
		||||
    - [BUGFIX] don't wipe current path if no path challenge responses
 | 
			
		||||
      come back.
 | 
			
		||||
    - [BUGFIX] When path is reset, don't lose path_id which is used for
 | 
			
		||||
      logging.
 | 
			
		||||
    - Downgrade flow control violations to info log level from warnings.
 | 
			
		||||
    - Fix connection cap extra check, avoid checks in nested calls.
 | 
			
		||||
    - Fix some unit tests when extra checks are enabled.
 | 
			
		||||
    - Use ls-hpack 2.2.1.
 | 
			
		||||
    - Turn off unconditional extra checks for IETF clients.
 | 
			
		||||
    - Extra checks: don't verify sent size of hello packets.  Client
 | 
			
		||||
      changes DCID length and this check will fail.
 | 
			
		||||
 | 
			
		||||
2020-06-03
 | 
			
		||||
    - 2.16.0
 | 
			
		||||
    - [API] Use lsxpack_header v206.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1939,6 +1939,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
 | 
			
		|||
            settings->es_qpack_dec_max_size = atoi(val);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (0 == strncmp(name, "noprogress_timeout", 18))
 | 
			
		||||
        {
 | 
			
		||||
            settings->es_noprogress_timeout = atoi(val);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 20:
 | 
			
		||||
        if (0 == strncmp(name, "max_header_list_size", 20))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,13 +46,13 @@ developed by the IETF.  Both types are included in a single enum:
 | 
			
		|||
 | 
			
		||||
        Google QUIC version Q050
 | 
			
		||||
 | 
			
		||||
    .. member:: LSQVER_ID25
 | 
			
		||||
 | 
			
		||||
        IETF QUIC version ID (Internet-Draft) 25
 | 
			
		||||
 | 
			
		||||
    .. member:: LSQVER_ID27
 | 
			
		||||
 | 
			
		||||
        IETF QUIC version ID 27
 | 
			
		||||
        IETF QUIC version ID (Internet-Draft) 27
 | 
			
		||||
 | 
			
		||||
    .. member:: LSQVER_ID28
 | 
			
		||||
 | 
			
		||||
        IETF QUIC version ID 28
 | 
			
		||||
 | 
			
		||||
    .. member:: N_LSQVER
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -703,6 +703,19 @@ settings structure:
 | 
			
		|||
 | 
			
		||||
       Default value is :macro:`LSQUIC_DF_MAX_UDP_PAYLOAD_SIZE_RX`
 | 
			
		||||
 | 
			
		||||
    .. member:: unsigned        es_noprogress_timeout
 | 
			
		||||
 | 
			
		||||
       No progress timeout.
 | 
			
		||||
 | 
			
		||||
       If connection does not make progress for this number of seconds, the
 | 
			
		||||
       connection is dropped.  Here, progress is defined as user streams
 | 
			
		||||
       being written to or read from.
 | 
			
		||||
 | 
			
		||||
       If this value is zero, this timeout is disabled.
 | 
			
		||||
 | 
			
		||||
       Default value is :macro:`LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER` in server
 | 
			
		||||
       mode and :macro:`LSQUIC_DF_NOPROGRESS_TIMEOUT_CLIENT` in client mode.
 | 
			
		||||
 | 
			
		||||
To initialize the settings structure to library defaults, use the following
 | 
			
		||||
convenience function:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -881,6 +894,14 @@ out of date.  Please check your :file:`lsquic.h` for actual values.*
 | 
			
		|||
 | 
			
		||||
    By default, incoming packet size is not limited.
 | 
			
		||||
 | 
			
		||||
.. macro:: LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER
 | 
			
		||||
 | 
			
		||||
    By default, drop no-progress connections after 60 seconds on the server.
 | 
			
		||||
 | 
			
		||||
.. macro:: LSQUIC_DF_NOPROGRESS_TIMEOUT_CLIENT
 | 
			
		||||
 | 
			
		||||
    By default, do not use no-progress timeout on the client.
 | 
			
		||||
 | 
			
		||||
Receiving Packets
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ author = u'LiteSpeed Technologies'
 | 
			
		|||
# The short X.Y version
 | 
			
		||||
version = u'2.16'
 | 
			
		||||
# The full version, including alpha/beta/rc tags
 | 
			
		||||
release = u'2.16.0'
 | 
			
		||||
release = u'2.16.1'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- General configuration ---------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ extern "C" {
 | 
			
		|||
 | 
			
		||||
#define LSQUIC_MAJOR_VERSION 2
 | 
			
		||||
#define LSQUIC_MINOR_VERSION 16
 | 
			
		||||
#define LSQUIC_PATCH_VERSION 0
 | 
			
		||||
#define LSQUIC_PATCH_VERSION 1
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Engine flags:
 | 
			
		||||
| 
						 | 
				
			
			@ -343,6 +343,12 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
 | 
			
		|||
/** By default, incoming packet size is not limited. */
 | 
			
		||||
#define LSQUIC_DF_MAX_UDP_PAYLOAD_SIZE_RX 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
 | 
			
		||||
 | 
			
		||||
struct lsquic_engine_settings {
 | 
			
		||||
    /**
 | 
			
		||||
     * This is a bit mask wherein each bit corresponds to a value in
 | 
			
		||||
| 
						 | 
				
			
			@ -752,6 +758,20 @@ struct lsquic_engine_settings {
 | 
			
		|||
     * Default value is @ref LSQUIC_DF_MAX_UDP_PAYLOAD_SIZE_RX
 | 
			
		||||
     */
 | 
			
		||||
    unsigned short  es_max_udp_payload_size_rx;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * No progress timeout.
 | 
			
		||||
     *
 | 
			
		||||
     * If connection does not make progress for this number of seconds, the
 | 
			
		||||
     * connection is dropped.  Here, progress is defined as user streams
 | 
			
		||||
     * being written to or read from.
 | 
			
		||||
     *
 | 
			
		||||
     * If this value is zero, this timeout is disabled.
 | 
			
		||||
     *
 | 
			
		||||
     * Default value is @ref LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER in server
 | 
			
		||||
     * mode and @ref LSQUIC_DF_NOPROGRESS_TIMEOUT_CLIENT in client mode.
 | 
			
		||||
     */
 | 
			
		||||
    unsigned        es_noprogress_timeout;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Initialize `settings' to default values */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,7 +99,7 @@ lsquic_cfcw_incr_max_recv_off (struct lsquic_cfcw *fc, uint64_t incr)
 | 
			
		|||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("flow control violation: received at offset %"PRIu64", while "
 | 
			
		||||
        LSQ_INFO("flow control violation: received at offset %"PRIu64", while "
 | 
			
		||||
            "flow control receive offset is %"PRIu64,
 | 
			
		||||
            fc->cf_max_recv_off + incr, fc->cf_recv_off);
 | 
			
		||||
        return 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,12 +56,15 @@ struct lsquic_conn_public {
 | 
			
		|||
    const struct network_path      *path;
 | 
			
		||||
#if LSQUIC_EXTRA_CHECKS
 | 
			
		||||
    unsigned long                   stream_frame_bytes;
 | 
			
		||||
    unsigned                        wtp_level;  /* wtp: Write To Packets */
 | 
			
		||||
#endif
 | 
			
		||||
    /* "unsigned" is wide enough: these values are only used for amplification
 | 
			
		||||
     * limit before initial path is validated.
 | 
			
		||||
     */
 | 
			
		||||
    unsigned                        bytes_in;   /* successfully processed */
 | 
			
		||||
    unsigned                        bytes_out;
 | 
			
		||||
    /* Used for no-progress timeout */
 | 
			
		||||
    lsquic_time_t                   last_tick, last_prog;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -295,6 +295,8 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
 | 
			
		|||
        settings->es_init_max_streams_uni
 | 
			
		||||
                         = LSQUIC_DF_INIT_MAX_STREAMS_UNI_SERVER;
 | 
			
		||||
        settings->es_ping_period = 0;
 | 
			
		||||
        settings->es_noprogress_timeout
 | 
			
		||||
                         = LSQUIC_DF_NOPROGRESS_TIMEOUT_SERVER;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -311,6 +313,8 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
 | 
			
		|||
        settings->es_init_max_streams_uni
 | 
			
		||||
                         = LSQUIC_DF_INIT_MAX_STREAMS_UNI_CLIENT;
 | 
			
		||||
        settings->es_ping_period = LSQUIC_DF_PING_PERIOD;
 | 
			
		||||
        settings->es_noprogress_timeout
 | 
			
		||||
                         = LSQUIC_DF_NOPROGRESS_TIMEOUT_CLIENT;
 | 
			
		||||
    }
 | 
			
		||||
    settings->es_max_streams_in  = LSQUIC_DF_MAX_STREAMS_IN;
 | 
			
		||||
    settings->es_idle_conn_to    = LSQUIC_DF_IDLE_CONN_TO;
 | 
			
		||||
| 
						 | 
				
			
			@ -601,6 +605,9 @@ lsquic_engine_new (unsigned flags,
 | 
			
		|||
    if (!engine->pub.enp_tokgen)
 | 
			
		||||
        return NULL;
 | 
			
		||||
    engine->pub.enp_crand = &engine->crand;
 | 
			
		||||
    if (engine->pub.enp_settings.es_noprogress_timeout)
 | 
			
		||||
        engine->pub.enp_noprog_timeout
 | 
			
		||||
            = engine->pub.enp_settings.es_noprogress_timeout * 1000000;
 | 
			
		||||
    if (flags & ENG_SERVER)
 | 
			
		||||
    {
 | 
			
		||||
        engine->pr_queue = lsquic_prq_create(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,6 +66,8 @@ struct lsquic_engine_public {
 | 
			
		|||
    struct crand                   *enp_crand;
 | 
			
		||||
    struct evp_aead_ctx_st         *enp_retry_aead_ctx;
 | 
			
		||||
    unsigned char                  *enp_alpn;   /* May be set if not HTTP */
 | 
			
		||||
    /* es_noprogress_timeout converted to microseconds for speed */
 | 
			
		||||
    lsquic_time_t                   enp_noprog_timeout;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Put connection onto the Tickable Queue if it is not already on it.  If
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -112,6 +112,7 @@ enum full_conn_flags {
 | 
			
		|||
    FC_ABORT_COMPLAINED
 | 
			
		||||
                      = (1 <<23),
 | 
			
		||||
    FC_GOT_SREJ       = (1 <<24),   /* Don't schedule ACK alarm */
 | 
			
		||||
    FC_NOPROG_TIMEOUT = (1 <<25),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define FC_IMMEDIATE_CLOSE_FLAGS \
 | 
			
		||||
| 
						 | 
				
			
			@ -686,6 +687,8 @@ new_conn_common (lsquic_cid_t cid, struct lsquic_engine_public *enpub,
 | 
			
		|||
    if (conn->fc_settings->es_support_push)
 | 
			
		||||
        conn->fc_flags |= FC_SUPPORT_PUSH;
 | 
			
		||||
    conn->fc_conn.cn_n_cces = sizeof(conn->fc_cces) / sizeof(conn->fc_cces[0]);
 | 
			
		||||
    if (conn->fc_settings->es_noprogress_timeout)
 | 
			
		||||
        conn->fc_flags |= FC_NOPROG_TIMEOUT;
 | 
			
		||||
    return conn;
 | 
			
		||||
 | 
			
		||||
  cleanup_on_error:
 | 
			
		||||
| 
						 | 
				
			
			@ -2481,9 +2484,22 @@ idle_alarm_expired (enum alarm_id al_id, void *ctx, lsquic_time_t expiry,
 | 
			
		|||
                                                            lsquic_time_t now)
 | 
			
		||||
{
 | 
			
		||||
    struct full_conn *conn = ctx;
 | 
			
		||||
    LSQ_DEBUG("connection timed out");
 | 
			
		||||
    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out");
 | 
			
		||||
    conn->fc_flags |= FC_TIMED_OUT;
 | 
			
		||||
 | 
			
		||||
    if ((conn->fc_flags & FC_NOPROG_TIMEOUT)
 | 
			
		||||
        && conn->fc_pub.last_prog + conn->fc_enpub->enp_noprog_timeout < now)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("connection timed out due to lack of progress");
 | 
			
		||||
        EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out due to "
 | 
			
		||||
                                                            "lack of progress");
 | 
			
		||||
        /* Different flag so that CONNECTION_CLOSE frame is sent */
 | 
			
		||||
        conn->fc_flags |= FC_ABORTED;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("connection timed out");
 | 
			
		||||
        EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out");
 | 
			
		||||
        conn->fc_flags |= FC_TIMED_OUT;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3242,6 +3258,31 @@ full_conn_ci_can_write_ack (struct lsquic_conn *lconn)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* This should be called before lsquic_alarmset_ring_expired() */
 | 
			
		||||
static void
 | 
			
		||||
maybe_set_noprogress_alarm (struct full_conn *conn, lsquic_time_t now)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_time_t exp;
 | 
			
		||||
 | 
			
		||||
    if (conn->fc_flags & FC_NOPROG_TIMEOUT)
 | 
			
		||||
    {
 | 
			
		||||
        if (conn->fc_pub.last_tick)
 | 
			
		||||
        {
 | 
			
		||||
            exp = conn->fc_pub.last_prog + conn->fc_enpub->enp_noprog_timeout;
 | 
			
		||||
            if (!lsquic_alarmset_is_set(&conn->fc_alset, AL_IDLE)
 | 
			
		||||
                                    || exp < conn->fc_alset.as_expiry[AL_IDLE])
 | 
			
		||||
                lsquic_alarmset_set(&conn->fc_alset, AL_IDLE, exp);
 | 
			
		||||
            conn->fc_pub.last_tick = now;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            conn->fc_pub.last_tick = now;
 | 
			
		||||
            conn->fc_pub.last_prog = now;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static enum tick_st
 | 
			
		||||
full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -3296,6 +3337,8 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
 | 
			
		|||
        conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    maybe_set_noprogress_alarm(conn, now);
 | 
			
		||||
 | 
			
		||||
    lsquic_send_ctl_tick_in(&conn->fc_send_ctl, now);
 | 
			
		||||
    lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 1);
 | 
			
		||||
    CLOSE_IF_NECESSARY();
 | 
			
		||||
| 
						 | 
				
			
			@ -3520,6 +3563,20 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
set_earliest_idle_alarm (struct full_conn *conn, lsquic_time_t idle_conn_to)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_time_t exp;
 | 
			
		||||
 | 
			
		||||
    if (conn->fc_pub.last_prog
 | 
			
		||||
        && (assert(conn->fc_flags & FC_NOPROG_TIMEOUT),
 | 
			
		||||
            exp = conn->fc_pub.last_prog + conn->fc_enpub->enp_noprog_timeout,
 | 
			
		||||
            exp < idle_conn_to))
 | 
			
		||||
        idle_conn_to = exp;
 | 
			
		||||
    lsquic_alarmset_set(&conn->fc_alset, AL_IDLE, idle_conn_to);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
full_conn_ci_packet_in (lsquic_conn_t *lconn, lsquic_packet_in_t *packet_in)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -3528,7 +3585,7 @@ full_conn_ci_packet_in (lsquic_conn_t *lconn, lsquic_packet_in_t *packet_in)
 | 
			
		|||
#if LSQUIC_CONN_STATS
 | 
			
		||||
    conn->fc_stats.in.bytes += packet_in->pi_data_sz;
 | 
			
		||||
#endif
 | 
			
		||||
    lsquic_alarmset_set(&conn->fc_alset, AL_IDLE,
 | 
			
		||||
    set_earliest_idle_alarm(conn,
 | 
			
		||||
                packet_in->pi_received + conn->fc_settings->es_idle_conn_to);
 | 
			
		||||
    if (0 == (conn->fc_flags & FC_ERROR))
 | 
			
		||||
        if (0 != process_incoming_packet(conn, packet_in))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -141,6 +141,7 @@ enum ifull_conn_flags
 | 
			
		|||
enum more_flags
 | 
			
		||||
{
 | 
			
		||||
    MF_VALIDATE_PATH    = 1 << 0,
 | 
			
		||||
    MF_NOPROG_TIMEOUT   = 1 << 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -476,6 +477,9 @@ static int
 | 
			
		|||
insert_new_dcid (struct ietf_full_conn *, uint64_t seqno,
 | 
			
		||||
    const lsquic_cid_t *, const unsigned char *token, int update_cur_dcid);
 | 
			
		||||
 | 
			
		||||
static struct conn_cid_elem *
 | 
			
		||||
find_cce_by_cid (struct ietf_full_conn *, const lsquic_cid_t *);
 | 
			
		||||
 | 
			
		||||
static unsigned
 | 
			
		||||
highest_bit_set (unsigned sz)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -533,9 +537,22 @@ idle_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("connection timed out");
 | 
			
		||||
    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out");
 | 
			
		||||
    conn->ifc_flags |= IFC_TIMED_OUT;
 | 
			
		||||
 | 
			
		||||
    if ((conn->ifc_mflags & MF_NOPROG_TIMEOUT)
 | 
			
		||||
        && conn->ifc_pub.last_prog + conn->ifc_enpub->enp_noprog_timeout < now)
 | 
			
		||||
    {
 | 
			
		||||
        EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out due to "
 | 
			
		||||
                                                            "lack of progress");
 | 
			
		||||
        /* Different flag so that CONNECTION_CLOSE frame is sent */
 | 
			
		||||
        ABORT_QUIETLY(0, TEC_APPLICATION_ERROR,
 | 
			
		||||
                                "connection timed out due to lack of progress");
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_DEBUG("connection timed out");
 | 
			
		||||
        EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "connection timed out");
 | 
			
		||||
        conn->ifc_flags |= IFC_TIMED_OUT;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -564,6 +581,14 @@ cid_throt_alarm_expired (enum alarm_id al_id, void *ctx,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
wipe_path (struct ietf_full_conn *conn, unsigned path_id)
 | 
			
		||||
{
 | 
			
		||||
    memset(&conn->ifc_paths[path_id], 0, sizeof(conn->ifc_paths[0]));
 | 
			
		||||
    conn->ifc_paths[path_id].cop_path.np_path_id = path_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
path_chal_alarm_expired (enum alarm_id al_id, void *ctx,
 | 
			
		||||
                    lsquic_time_t expiry, lsquic_time_t now, unsigned path_id)
 | 
			
		||||
| 
						 | 
				
			
			@ -577,12 +602,15 @@ path_chal_alarm_expired (enum alarm_id al_id, void *ctx,
 | 
			
		|||
        LSQ_DEBUG("path #%u challenge expired, schedule another one", path_id);
 | 
			
		||||
        conn->ifc_send_flags |= SF_SEND_PATH_CHAL << path_id;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    else if (conn->ifc_cur_path_id != path_id)
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_INFO("migration to path #%u failed after none of %u path "
 | 
			
		||||
            "challenges received responses", path_id, copath->cop_n_chals);
 | 
			
		||||
        memset(copath, 0, sizeof(*copath));
 | 
			
		||||
        wipe_path(conn, path_id);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        LSQ_INFO("no path challenge responses on current path %u, stop "
 | 
			
		||||
            "sending path challenges", path_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1132,6 +1160,8 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
 | 
			
		|||
    conn->ifc_ping_unretx_thresh = 20;
 | 
			
		||||
    conn->ifc_max_retx_since_last_ack = MAX_RETR_PACKETS_SINCE_LAST_ACK;
 | 
			
		||||
    maybe_enable_spin(conn);
 | 
			
		||||
    if (conn->ifc_settings->es_noprogress_timeout)
 | 
			
		||||
        conn->ifc_mflags |= MF_NOPROG_TIMEOUT;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1422,14 +1452,21 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
 | 
			
		|||
     */
 | 
			
		||||
    have_outgoing_ack = 0;
 | 
			
		||||
    next_packno = ~0ULL;
 | 
			
		||||
    /* mini conn may drop Init packets, making gaps; don't warn about them: */
 | 
			
		||||
    conn->ifc_send_ctl.sc_senhist.sh_flags |= SH_GAP_OK;
 | 
			
		||||
    while ((packet_out = TAILQ_FIRST(&imc->imc_packets_out)))
 | 
			
		||||
    {
 | 
			
		||||
        TAILQ_REMOVE(&imc->imc_packets_out, packet_out, po_next);
 | 
			
		||||
 | 
			
		||||
        /* Holes in the sequence signify ACKed or lost packets */
 | 
			
		||||
        /* Holes in the sequence signify no-longer-relevant Initial packets or
 | 
			
		||||
         * ACKed or lost packets.
 | 
			
		||||
         */
 | 
			
		||||
        ++next_packno;
 | 
			
		||||
        for ( ; next_packno < packet_out->po_packno; ++next_packno)
 | 
			
		||||
        {
 | 
			
		||||
            lsquic_senhist_add(&conn->ifc_send_ctl.sc_senhist, next_packno);
 | 
			
		||||
            conn->ifc_send_ctl.sc_senhist.sh_warn_thresh = next_packno;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        packet_out->po_path = CUR_NPATH(conn);
 | 
			
		||||
        if (imc->imc_sent_packnos & (1ULL << packet_out->po_packno))
 | 
			
		||||
| 
						 | 
				
			
			@ -1453,6 +1490,11 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
 | 
			
		|||
                                                (1 << QUIC_FRAME_ACK);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    conn->ifc_send_ctl.sc_senhist.sh_flags &= ~SH_GAP_OK;
 | 
			
		||||
    /* ...Yes, that's a bunch of little annoying steps to suppress the gap
 | 
			
		||||
     * warnings, but it would have been even more annoying (and expensive)
 | 
			
		||||
     * to add packet renumbering logic to the mini conn.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    for (pns = 0; pns < N_PNS; ++pns)
 | 
			
		||||
        for (i = 0; i < 4; ++i)
 | 
			
		||||
| 
						 | 
				
			
			@ -4307,10 +4349,7 @@ switch_path_to (struct ietf_full_conn *conn, unsigned char path_id)
 | 
			
		|||
    conn->ifc_send_flags &= ~(SF_SEND_PATH_RESP << old_path_id);
 | 
			
		||||
    lsquic_alarmset_unset(&conn->ifc_alset, AL_PATH_CHAL + old_path_id);
 | 
			
		||||
    if (conn->ifc_flags & IFC_SERVER)
 | 
			
		||||
    {
 | 
			
		||||
        memset(&conn->ifc_paths[old_path_id], 0, sizeof(conn->ifc_paths[0]));
 | 
			
		||||
        conn->ifc_paths[old_path_id].cop_path.np_path_id = old_path_id;
 | 
			
		||||
    }
 | 
			
		||||
        wipe_path(conn, old_path_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5417,6 +5456,24 @@ process_retire_connection_id_frame (struct ietf_full_conn *conn,
 | 
			
		|||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        retire_cid(conn, cce, packet_in->pi_received);
 | 
			
		||||
        if (lconn->cn_cur_cce_idx == cce - lconn->cn_cces)
 | 
			
		||||
        {
 | 
			
		||||
            cce = find_cce_by_cid(conn, &packet_in->pi_dcid);
 | 
			
		||||
            if (cce)
 | 
			
		||||
            {
 | 
			
		||||
                LSQ_DEBUGC("current SCID was retired; set current SCID to "
 | 
			
		||||
                    "%"CID_FMT" based on DCID in incoming packet",
 | 
			
		||||
                    CID_BITS(&packet_in->pi_dcid));
 | 
			
		||||
                cce->cce_flags |= CCE_USED;
 | 
			
		||||
                lconn->cn_cur_cce_idx = cce - lconn->cn_cces;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                LSQ_WARN("current SCID was retired; no new SCID candidate");
 | 
			
		||||
                /* This could theoretically happen when zero-length CIDs were
 | 
			
		||||
                 * used.  Currently, there should be no way lsquic could get
 | 
			
		||||
                 * into this situation.
 | 
			
		||||
                 */
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        LSQ_DEBUG("cannot retire CID seqno=%"PRIu64": not found", seqno);
 | 
			
		||||
| 
						 | 
				
			
			@ -6521,15 +6578,29 @@ process_incoming_packet_fast (struct ietf_full_conn *conn,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
set_earliest_idle_alarm (struct ietf_full_conn *conn, lsquic_time_t idle_conn_to)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_time_t exp;
 | 
			
		||||
 | 
			
		||||
    if (conn->ifc_pub.last_prog
 | 
			
		||||
        && (assert(conn->ifc_mflags & MF_NOPROG_TIMEOUT),
 | 
			
		||||
            exp = conn->ifc_pub.last_prog + conn->ifc_enpub->enp_noprog_timeout,
 | 
			
		||||
            exp < idle_conn_to))
 | 
			
		||||
        idle_conn_to = exp;
 | 
			
		||||
    if (idle_conn_to)
 | 
			
		||||
        lsquic_alarmset_set(&conn->ifc_alset, AL_IDLE, idle_conn_to);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ietf_full_conn_ci_packet_in (struct lsquic_conn *lconn,
 | 
			
		||||
                             struct lsquic_packet_in *packet_in)
 | 
			
		||||
{
 | 
			
		||||
    struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
 | 
			
		||||
 | 
			
		||||
    if (conn->ifc_idle_to)
 | 
			
		||||
        lsquic_alarmset_set(&conn->ifc_alset, AL_IDLE,
 | 
			
		||||
                packet_in->pi_received + conn->ifc_idle_to);
 | 
			
		||||
    set_earliest_idle_alarm(conn, conn->ifc_idle_to
 | 
			
		||||
                    ? packet_in->pi_received + conn->ifc_idle_to : 0);
 | 
			
		||||
    if (0 == (conn->ifc_flags & IFC_IMMEDIATE_CLOSE_FLAGS))
 | 
			
		||||
        if (0 != conn->ifc_process_incoming_packet(conn, packet_in))
 | 
			
		||||
            conn->ifc_flags |= IFC_ERROR;
 | 
			
		||||
| 
						 | 
				
			
			@ -6646,6 +6717,32 @@ static void (*const send_funcs[N_SEND])(
 | 
			
		|||
    |SF_SEND_ACK_FREQUENCY\
 | 
			
		||||
    |SF_SEND_STOP_SENDING)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* This should be called before lsquic_alarmset_ring_expired() */
 | 
			
		||||
static void
 | 
			
		||||
maybe_set_noprogress_alarm (struct ietf_full_conn *conn, lsquic_time_t now)
 | 
			
		||||
{
 | 
			
		||||
    lsquic_time_t exp;
 | 
			
		||||
 | 
			
		||||
    if (conn->ifc_mflags & MF_NOPROG_TIMEOUT)
 | 
			
		||||
    {
 | 
			
		||||
        if (conn->ifc_pub.last_tick)
 | 
			
		||||
        {
 | 
			
		||||
            exp = conn->ifc_pub.last_prog + conn->ifc_enpub->enp_noprog_timeout;
 | 
			
		||||
            if (!lsquic_alarmset_is_set(&conn->ifc_alset, AL_IDLE)
 | 
			
		||||
                                    || exp < conn->ifc_alset.as_expiry[AL_IDLE])
 | 
			
		||||
                lsquic_alarmset_set(&conn->ifc_alset, AL_IDLE, exp);
 | 
			
		||||
            conn->ifc_pub.last_tick = now;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            conn->ifc_pub.last_tick = now;
 | 
			
		||||
            conn->ifc_pub.last_prog = now;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static enum tick_st
 | 
			
		||||
ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -6688,6 +6785,8 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
 | 
			
		|||
        conn->ifc_flags &= ~IFC_HAVE_SAVED_ACK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    maybe_set_noprogress_alarm(conn, now);
 | 
			
		||||
 | 
			
		||||
    lsquic_send_ctl_tick_in(&conn->ifc_send_ctl, now);
 | 
			
		||||
    lsquic_send_ctl_set_buffer_stream_packets(&conn->ifc_send_ctl, 1);
 | 
			
		||||
    CLOSE_IF_NECESSARY();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -635,7 +635,7 @@ ietf_mini_conn_ci_is_tickable (struct lsquic_conn *lconn)
 | 
			
		|||
            if (!(packet_out->po_flags & PO_SENT))
 | 
			
		||||
            {
 | 
			
		||||
                packet_size = lsquic_packet_out_total_sz(lconn, packet_out);
 | 
			
		||||
                return imico_can_send(conn, packet_size + IQUIC_TAG_LEN);
 | 
			
		||||
                return imico_can_send(conn, packet_size);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -665,15 +665,15 @@ ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
 | 
			
		|||
        packet_size = lsquic_packet_out_total_sz(lconn, packet_out);
 | 
			
		||||
        if (size == 0 || packet_size + size <= conn->imc_path.np_pack_size)
 | 
			
		||||
        {
 | 
			
		||||
            if (!imico_can_send(conn, packet_size + IQUIC_TAG_LEN))
 | 
			
		||||
            if (!imico_can_send(conn, packet_size))
 | 
			
		||||
            {
 | 
			
		||||
                LSQ_DEBUG("cannot send packet %"PRIu64" of size %zu: client "
 | 
			
		||||
                    "address has not been validated", packet_out->po_packno,
 | 
			
		||||
                    packet_size + IQUIC_TAG_LEN);
 | 
			
		||||
                    packet_size);
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
            packet_out->po_flags |= PO_SENT;
 | 
			
		||||
            conn->imc_bytes_out += packet_size + IQUIC_TAG_LEN;
 | 
			
		||||
            conn->imc_bytes_out += packet_size;
 | 
			
		||||
            if (size == 0)
 | 
			
		||||
                LSQ_DEBUG("packet_to_send: %"PRIu64, packet_out->po_packno);
 | 
			
		||||
            else
 | 
			
		||||
| 
						 | 
				
			
			@ -1310,7 +1310,7 @@ ietf_mini_conn_ci_packet_not_sent (struct lsquic_conn *lconn,
 | 
			
		|||
 | 
			
		||||
    packet_out->po_flags &= ~PO_SENT;
 | 
			
		||||
    packet_size = lsquic_packet_out_total_sz(lconn, packet_out);
 | 
			
		||||
    conn->imc_bytes_out -= packet_size + IQUIC_TAG_LEN;
 | 
			
		||||
    conn->imc_bytes_out -= packet_size;
 | 
			
		||||
    LSQ_DEBUG("%s: packet %"PRIu64" not sent", __func__, packet_out->po_packno);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1356,9 +1356,11 @@ imico_handle_losses_and_have_unsent (struct ietf_mini_conn *conn,
 | 
			
		|||
{
 | 
			
		||||
    TAILQ_HEAD(, lsquic_packet_out) lost_packets =
 | 
			
		||||
                                    TAILQ_HEAD_INITIALIZER(lost_packets);
 | 
			
		||||
    const struct lsquic_conn *const lconn = &conn->imc_conn;
 | 
			
		||||
    lsquic_packet_out_t *packet_out, *next;
 | 
			
		||||
    lsquic_time_t retx_to = 0;
 | 
			
		||||
    unsigned n_to_send = 0;
 | 
			
		||||
    size_t packet_size;
 | 
			
		||||
 | 
			
		||||
    for (packet_out = TAILQ_FIRST(&conn->imc_packets_out); packet_out;
 | 
			
		||||
                                                        packet_out = next)
 | 
			
		||||
| 
						 | 
				
			
			@ -1376,8 +1378,11 @@ imico_handle_losses_and_have_unsent (struct ietf_mini_conn *conn,
 | 
			
		|||
                TAILQ_INSERT_TAIL(&lost_packets, packet_out, po_next);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        else if (packet_size = lsquic_packet_out_total_sz(lconn, packet_out),
 | 
			
		||||
                                                imico_can_send(conn, packet_size))
 | 
			
		||||
            ++n_to_send;
 | 
			
		||||
        else
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    conn->imc_hsk_count += !TAILQ_EMPTY(&lost_packets);
 | 
			
		||||
| 
						 | 
				
			
			@ -1387,7 +1392,11 @@ imico_handle_losses_and_have_unsent (struct ietf_mini_conn *conn,
 | 
			
		|||
        TAILQ_REMOVE(&lost_packets, packet_out, po_next);
 | 
			
		||||
        if ((packet_out->po_frame_types & IQUIC_FRAME_RETX_MASK)
 | 
			
		||||
                            && 0 == imico_repackage_packet(conn, packet_out))
 | 
			
		||||
            ++n_to_send;
 | 
			
		||||
        {
 | 
			
		||||
            packet_size = lsquic_packet_out_total_sz(lconn, packet_out);
 | 
			
		||||
            if (imico_can_send(conn, packet_size))
 | 
			
		||||
                ++n_to_send;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            imico_destroy_packet(conn, packet_out);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -220,7 +220,8 @@ typedef struct lsquic_packet_out
 | 
			
		|||
#if LSQUIC_EXTRA_CHECKS
 | 
			
		||||
#define lsquic_packet_out_sent_sz(lconn, p) (                               \
 | 
			
		||||
        __builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ?                 \
 | 
			
		||||
        (assert((p)->po_sent_sz == lsquic_packet_out_total_sz(lconn, p)),   \
 | 
			
		||||
        (assert(((p)->po_flags & PO_HELLO /* Avoid client DCID change */)   \
 | 
			
		||||
            || (p)->po_sent_sz == lsquic_packet_out_total_sz(lconn, p)),    \
 | 
			
		||||
            (p)->po_sent_sz) : lsquic_packet_out_total_sz(lconn, p))
 | 
			
		||||
#   else
 | 
			
		||||
#define lsquic_packet_out_sent_sz(lconn, p) (                               \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -363,10 +363,7 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
 | 
			
		|||
    ctl->sc_cached_bpt.stream_id = UINT64_MAX;
 | 
			
		||||
#if LSQUIC_EXTRA_CHECKS
 | 
			
		||||
    ctl->sc_flags |= SC_SANITY_CHECK;
 | 
			
		||||
#else
 | 
			
		||||
    if ((ctl->sc_conn_pub->lconn->cn_flags & (LSCONN_IETF|LSCONN_SERVER))
 | 
			
		||||
                                                                == LSCONN_IETF)
 | 
			
		||||
        ctl->sc_flags |= SC_SANITY_CHECK;
 | 
			
		||||
    LSQ_DEBUG("sanity checks enabled");
 | 
			
		||||
#endif
 | 
			
		||||
    ctl->sc_gap = UINT64_MAX - 1 /* Can't have +1 == 0 */;
 | 
			
		||||
    if ((ctl->sc_conn_pub->lconn->cn_flags & (LSCONN_IETF|LSCONN_SERVER))
 | 
			
		||||
| 
						 | 
				
			
			@ -2227,7 +2224,8 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
 | 
			
		|||
                                                            packet_out = next)
 | 
			
		||||
    {
 | 
			
		||||
        next = TAILQ_NEXT(packet_out, po_next);
 | 
			
		||||
        if (packet_out->po_regen_sz < packet_out->po_data_sz)
 | 
			
		||||
        if (packet_out->po_regen_sz < packet_out->po_data_sz
 | 
			
		||||
                || packet_out->po_frame_types == QUIC_FTBIT_PATH_CHALLENGE)
 | 
			
		||||
        {
 | 
			
		||||
            if (packet_out->po_flags & PO_ENCRYPTED)
 | 
			
		||||
                send_ctl_return_enc_data(ctl, packet_out);
 | 
			
		||||
| 
						 | 
				
			
			@ -2238,7 +2236,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
 | 
			
		|||
            /* Log the whole list before we squeeze for the first time */
 | 
			
		||||
            if (!pre_squeeze_logged++)
 | 
			
		||||
                LOG_PACKET_Q(&ctl->sc_scheduled_packets,
 | 
			
		||||
                                        "unacked packets before squeezing");
 | 
			
		||||
                                        "scheduled packets before squeezing");
 | 
			
		||||
#endif
 | 
			
		||||
            send_ctl_sched_remove(ctl, packet_out);
 | 
			
		||||
            LSQ_DEBUG("Dropping packet %"PRIu64" from scheduled queue",
 | 
			
		||||
| 
						 | 
				
			
			@ -2255,7 +2253,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
 | 
			
		|||
#ifndef NDEBUG
 | 
			
		||||
    if (pre_squeeze_logged)
 | 
			
		||||
        LOG_PACKET_Q(&ctl->sc_scheduled_packets,
 | 
			
		||||
                                        "unacked packets after squeezing");
 | 
			
		||||
                                        "scheduled packets after squeezing");
 | 
			
		||||
    else if (ctl->sc_n_scheduled > 0)
 | 
			
		||||
        LOG_PACKET_Q(&ctl->sc_scheduled_packets, "delayed packets");
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,11 +16,14 @@
 | 
			
		|||
 | 
			
		||||
typedef struct lsquic_senhist {
 | 
			
		||||
    lsquic_packno_t             sh_last_sent;
 | 
			
		||||
    lsquic_packno_t             sh_warn_thresh;
 | 
			
		||||
    enum {
 | 
			
		||||
#if !LSQUIC_SENHIST_FATAL
 | 
			
		||||
        SH_WARNED   = 1 << 0,   /* Warn once */
 | 
			
		||||
#endif
 | 
			
		||||
        SH_GAP_OK   = 1 << 1,   /* Before connection is just about to close */
 | 
			
		||||
        SH_GAP_OK   = 1 << 1,   /* Before connection is just about to close or
 | 
			
		||||
                                 * during mini/full packet handoff.
 | 
			
		||||
                                 */
 | 
			
		||||
    }                           sh_flags;
 | 
			
		||||
} lsquic_senhist_t;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +38,8 @@ typedef struct lsquic_senhist {
 | 
			
		|||
 | 
			
		||||
#if LSQUIC_SENHIST_FATAL
 | 
			
		||||
#define lsquic_senhist_add(hist, packno) do {                           \
 | 
			
		||||
    if (!((hist)->sh_flags & SH_GAP_OK))                                \
 | 
			
		||||
    if (!((hist)->sh_flags & SH_GAP_OK)                                 \
 | 
			
		||||
                        && (packno) > (hist)->sh_warn_thresh)           \
 | 
			
		||||
        assert((hist)->sh_last_sent == packno - 1);                     \
 | 
			
		||||
    if ((int64_t) (packno) > (int64_t) (hist)->sh_last_sent)            \
 | 
			
		||||
        (hist)->sh_last_sent = packno;                                  \
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +48,8 @@ typedef struct lsquic_senhist {
 | 
			
		|||
#define lsquic_senhist_add(hist, packno) do {                           \
 | 
			
		||||
    if ((hist)->sh_last_sent != packno - 1)                             \
 | 
			
		||||
    {                                                                   \
 | 
			
		||||
        if (!((hist)->sh_flags & (SH_WARNED|SH_GAP_OK)))                \
 | 
			
		||||
        if (!((hist)->sh_flags & (SH_WARNED|SH_GAP_OK))                 \
 | 
			
		||||
                        && (packno) > (hist)->sh_warn_thresh)           \
 | 
			
		||||
        {                                                               \
 | 
			
		||||
            LSQ_WARN("send history gap %"PRIu64" - %"PRIu64,            \
 | 
			
		||||
                (hist)->sh_last_sent, packno);                          \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -137,7 +137,7 @@ lsquic_sfcw_set_max_recv_off (struct lsquic_sfcw *fc, uint64_t max_recv_off)
 | 
			
		|||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        LSQ_WARN("flow control violation: received at offset %"PRIu64", "
 | 
			
		||||
        LSQ_INFO("flow control violation: received at offset %"PRIu64", "
 | 
			
		||||
            "while flow control receive offset is %"PRIu64,
 | 
			
		||||
            max_recv_off, fc->sf_recv_off);
 | 
			
		||||
        return 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -259,6 +259,26 @@ stream_inside_callback (const lsquic_stream_t *stream)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* This is an approximation.  If data is written or read outside of the
 | 
			
		||||
 * event loop, last_prog will be somewhat out of date, but it's close
 | 
			
		||||
 * enough for our purposes.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
maybe_update_last_progress (struct lsquic_stream *stream)
 | 
			
		||||
{
 | 
			
		||||
    if (stream->conn_pub && !lsquic_stream_is_critical(stream))
 | 
			
		||||
    {
 | 
			
		||||
        if (stream->conn_pub->last_prog != stream->conn_pub->last_tick)
 | 
			
		||||
            LSQ_DEBUG("update last progress to %"PRIu64,
 | 
			
		||||
                                            stream->conn_pub->last_tick);
 | 
			
		||||
        stream->conn_pub->last_prog = stream->conn_pub->last_tick;
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
        stream->sm_last_prog = stream->conn_pub->last_tick;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
maybe_conn_to_tickable (lsquic_stream_t *stream)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1460,6 +1480,8 @@ ssize_t
 | 
			
		|||
lsquic_stream_readf (struct lsquic_stream *stream,
 | 
			
		||||
        size_t (*readf)(void *, const unsigned char *, size_t, int), void *ctx)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t nread;
 | 
			
		||||
 | 
			
		||||
    SM_HISTORY_APPEND(stream, SHE_USER_READ);
 | 
			
		||||
 | 
			
		||||
    if (lsquic_stream_is_reset(stream))
 | 
			
		||||
| 
						 | 
				
			
			@ -1485,7 +1507,11 @@ lsquic_stream_readf (struct lsquic_stream *stream,
 | 
			
		|||
           return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return stream_readf(stream, readf, ctx);
 | 
			
		||||
    nread = stream_readf(stream, readf, ctx);
 | 
			
		||||
    if (nread >= 0)
 | 
			
		||||
        maybe_update_last_progress(stream);
 | 
			
		||||
 | 
			
		||||
    return nread;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2745,6 +2771,9 @@ verify_conn_cap (const struct lsquic_conn_public *conn_pub)
 | 
			
		|||
    struct lsquic_hash_elem *el;
 | 
			
		||||
    unsigned n_buffered;
 | 
			
		||||
 | 
			
		||||
    if (conn_pub->wtp_level > 1)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (!conn_pub->all_streams)
 | 
			
		||||
        /* TODO: enable this check for unit tests as well */
 | 
			
		||||
        return;
 | 
			
		||||
| 
						 | 
				
			
			@ -2760,6 +2789,7 @@ verify_conn_cap (const struct lsquic_conn_public *conn_pub)
 | 
			
		|||
 | 
			
		||||
    assert(n_buffered + conn_pub->stream_frame_bytes
 | 
			
		||||
                                            == conn_pub->conn_cap.cc_sent);
 | 
			
		||||
    LSQ_DEBUG("%s: cc_sent: %"PRIu64, __func__, conn_pub->conn_cap.cc_sent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3056,6 +3086,10 @@ stream_write_to_packets (lsquic_stream_t *stream, struct lsquic_reader *reader,
 | 
			
		|||
        .fgc_nread_from_reader = 0,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
#if LSQUIC_EXTRA_CHECKS
 | 
			
		||||
    if (stream->conn_pub)
 | 
			
		||||
        ++stream->conn_pub->wtp_level;
 | 
			
		||||
#endif
 | 
			
		||||
    use_framing = (stream->sm_bflags & (SMBF_IETF|SMBF_USE_HEADERS))
 | 
			
		||||
                                       == (SMBF_IETF|SMBF_USE_HEADERS);
 | 
			
		||||
    if (use_framing)
 | 
			
		||||
| 
						 | 
				
			
			@ -3079,7 +3113,10 @@ stream_write_to_packets (lsquic_stream_t *stream, struct lsquic_reader *reader,
 | 
			
		|||
        {
 | 
			
		||||
        case SWTP_OK:
 | 
			
		||||
            if (!seen_ok++)
 | 
			
		||||
            {
 | 
			
		||||
                maybe_conn_to_tickable_if_writeable(stream, 0);
 | 
			
		||||
                maybe_update_last_progress(stream);
 | 
			
		||||
            }
 | 
			
		||||
            if (fg_ctx.fgc_fin(&fg_ctx))
 | 
			
		||||
            {
 | 
			
		||||
                if (use_framing && seen_ok)
 | 
			
		||||
| 
						 | 
				
			
			@ -3097,7 +3134,7 @@ stream_write_to_packets (lsquic_stream_t *stream, struct lsquic_reader *reader,
 | 
			
		|||
        default:
 | 
			
		||||
            abort_connection(stream);
 | 
			
		||||
            stream->stream_flags &= ~STREAM_LAST_WRITE_OK;
 | 
			
		||||
            return -1;
 | 
			
		||||
            goto err;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3113,7 +3150,7 @@ stream_write_to_packets (lsquic_stream_t *stream, struct lsquic_reader *reader,
 | 
			
		|||
        {
 | 
			
		||||
            nw = save_to_buffer(stream, reader, size);
 | 
			
		||||
            if (nw < 0)
 | 
			
		||||
                return -1;
 | 
			
		||||
                goto err;
 | 
			
		||||
            fg_ctx.fgc_nread_from_reader += nw; /* Make this cleaner? */
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -3129,7 +3166,18 @@ stream_write_to_packets (lsquic_stream_t *stream, struct lsquic_reader *reader,
 | 
			
		|||
    maybe_mark_as_blocked(stream);
 | 
			
		||||
 | 
			
		||||
  end:
 | 
			
		||||
#if LSQUIC_EXTRA_CHECKS
 | 
			
		||||
    if (stream->conn_pub)
 | 
			
		||||
        --stream->conn_pub->wtp_level;
 | 
			
		||||
#endif
 | 
			
		||||
    return fg_ctx.fgc_nread_from_reader;
 | 
			
		||||
 | 
			
		||||
  err:
 | 
			
		||||
#if LSQUIC_EXTRA_CHECKS
 | 
			
		||||
    if (stream->conn_pub)
 | 
			
		||||
        --stream->conn_pub->wtp_level;
 | 
			
		||||
#endif
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4056,6 +4104,7 @@ lsquic_stream_get_hset (struct lsquic_stream *stream)
 | 
			
		|||
        stream->stream_flags |= STREAM_FIN_REACHED;
 | 
			
		||||
        SM_HISTORY_APPEND(stream, SHE_REACH_FIN);
 | 
			
		||||
    }
 | 
			
		||||
    maybe_update_last_progress(stream);
 | 
			
		||||
    LSQ_DEBUG("return header set");
 | 
			
		||||
    return hset;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4347,12 +4396,15 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
 | 
			
		|||
        LSQ_INFO("FIN at unexpected place in filter; state: %u",
 | 
			
		||||
                                                        filter->hqfi_state);
 | 
			
		||||
        filter->hqfi_flags |= HQFI_FLAG_ERROR;
 | 
			
		||||
/* From [draft-ietf-quic-http-16] Section 3.1:
 | 
			
		||||
 *               When a stream terminates cleanly, if the last frame on
 | 
			
		||||
 * the stream was truncated, this MUST be treated as a connection error
 | 
			
		||||
 * (see HTTP_MALFORMED_FRAME in Section 8.1).
 | 
			
		||||
/* From [draft-ietf-quic-http-28] Section 7.1:
 | 
			
		||||
 " When a stream terminates cleanly, if the last frame on the stream was
 | 
			
		||||
 " truncated, this MUST be treated as a connection error (Section 8) of
 | 
			
		||||
 " type H3_FRAME_ERROR.  Streams which terminate abruptly may be reset
 | 
			
		||||
 " at any point in a frame.
 | 
			
		||||
 */
 | 
			
		||||
        abort_connection(stream);
 | 
			
		||||
        lconn = stream->conn_pub->lconn;
 | 
			
		||||
        lconn->cn_if->ci_abort_error(lconn, 1, HEC_FRAME_ERROR,
 | 
			
		||||
            "last HTTP/3 frame on stream %"PRIu64" was truncated", stream->id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return p - buf;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -317,6 +317,11 @@ struct lsquic_stream
 | 
			
		|||
 | 
			
		||||
    uint64_t                        sm_last_frame_off;
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
    /* Last time stream made progress */
 | 
			
		||||
    lsquic_time_t                   sm_last_prog;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Content length specified in incoming `content-length' header field.
 | 
			
		||||
     * Used to verify size of DATA frames.
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1 +1 @@
 | 
			
		|||
Subproject commit b577d56964e443972adb38ec56706181846cc47f
 | 
			
		||||
Subproject commit bd5d58987e0e88d7c1b676f83624528926fb423a
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +79,7 @@ sampler_test_send_packet (struct sampler_test *stest, lsquic_packno_t packno,
 | 
			
		|||
    memset(packet_out, 0, sizeof(*packet_out));
 | 
			
		||||
    packet_out->po_packno = packno;
 | 
			
		||||
    packet_out->po_flags |= PO_SENT_SZ;
 | 
			
		||||
    packet_out->po_flags |= PO_HELLO;   /* Bypass sanity check */
 | 
			
		||||
    packet_out->po_sent_sz = kRegularPacketSize;
 | 
			
		||||
    packet_out->po_sent = stest->time;
 | 
			
		||||
    if (retx)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -143,11 +143,17 @@ get_network_path (struct lsquic_conn *lconn, const struct sockaddr *sa)
 | 
			
		|||
    return &network_path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
abort_error (struct lsquic_conn *lconn, int is_app,
 | 
			
		||||
                                unsigned error_code, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct conn_iface our_conn_if =
 | 
			
		||||
{
 | 
			
		||||
    .ci_can_write_ack = unit_test_doesnt_write_ack,
 | 
			
		||||
    .ci_get_path      = get_network_path,
 | 
			
		||||
    .ci_abort_error   = abort_error,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
int
 | 
			
		||||
main (void)
 | 
			
		||||
{
 | 
			
		||||
    struct lsquic_senhist hist = { 0
 | 
			
		||||
    struct lsquic_senhist hist = { 0, 0
 | 
			
		||||
#if !LSQUIC_SENHIST_FATAL
 | 
			
		||||
        , 0
 | 
			
		||||
#endif        
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue