Release 2.17.0

- [FEATURE] QUIC and HTTP/3 Internet Draft 29 support.
- [BUGFIX] Check that scheduled packets are also sendable when
  calculating a connection's "tickable" property.
- [BUGFIX] Don't count scheduled packets as in-flight when pacer is
  checked on tick.
- gQUIC: delay calling on_new for pushed stream until headers are
  available.
- Allow nested calls to lsquic_engine_connect().
This commit is contained in:
Dmitri Tikhonov 2020-06-18 09:45:44 -04:00
parent 307ca7fe50
commit 4051ae3a1a
19 changed files with 343 additions and 56 deletions

View file

@ -1,3 +1,14 @@
2020-06-18
- 2.17.0
- [FEATURE] QUIC and HTTP/3 Internet Draft 29 support.
- [BUGFIX] Check that scheduled packets are also sendable when
calculating a connection's "tickable" property.
- [BUGFIX] Don't count scheduled packets as in-flight when pacer is
checked on tick.
- gQUIC: delay calling on_new for pushed stream until headers are
available.
- Allow nested calls to lsquic_engine_connect().
2020-06-15 2020-06-15
- 2.16.3 - 2.16.3
- [OPTIMIZATION] Stash up to two reordered packets in IETF mini conn - [OPTIMIZATION] Stash up to two reordered packets in IETF mini conn

View file

@ -21,7 +21,7 @@ static int
select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen, select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg) const unsigned char *in, unsigned int inlen, void *arg)
{ {
const unsigned char alpn[] = "\x5h3-27\x5h3-28"; const unsigned char alpn[] = "\x5h3-27\x5h3-28\x5h3-29";
int r; int r;
r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen,

View file

@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
author = u'LiteSpeed Technologies' author = u'LiteSpeed Technologies'
# The short X.Y version # The short X.Y version
version = u'2.16' version = u'2.17'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = u'2.16.3' release = u'2.17.0'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------

View file

@ -24,8 +24,8 @@ extern "C" {
#endif #endif
#define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 16 #define LSQUIC_MINOR_VERSION 17
#define LSQUIC_PATCH_VERSION 3 #define LSQUIC_PATCH_VERSION 0
/** /**
* Engine flags: * Engine flags:
@ -86,6 +86,11 @@ enum lsquic_version
*/ */
LSQVER_ID28, LSQVER_ID28,
/**
* IETF QUIC Draft-29
*/
LSQVER_ID29,
/** /**
* Special version to trigger version negotiation. * Special version to trigger version negotiation.
* [draft-ietf-quic-transport-11], Section 3. * [draft-ietf-quic-transport-11], Section 3.
@ -114,10 +119,10 @@ enum lsquic_version
#define LSQUIC_GQUIC_HEADER_VERSIONS (1 << LSQVER_043) #define LSQUIC_GQUIC_HEADER_VERSIONS (1 << LSQVER_043)
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \ #define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \
| (1 << LSQVER_VERNEG)) | (1 << LSQVER_ID29) | (1 << LSQVER_VERNEG))
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \ #define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID27) | (1 << LSQVER_ID28) \
| (1 << LSQVER_VERNEG)) | (1 << LSQVER_ID29) | (1 << LSQVER_VERNEG))
enum lsquic_hsk_status enum lsquic_hsk_status
{ {

View file

@ -334,6 +334,7 @@ extern const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1;
#define select_esf_common_by_ver(ver) ( \ #define select_esf_common_by_ver(ver) ( \
ver == LSQVER_ID27 ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_ID27 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_ID28 ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_ID28 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_ID29 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_050 ? &lsquic_enc_session_common_gquic_2 : \ ver == LSQVER_050 ? &lsquic_enc_session_common_gquic_2 : \
&lsquic_enc_session_common_gquic_1 ) &lsquic_enc_session_common_gquic_1 )

View file

@ -73,7 +73,8 @@ static const struct alpn_map {
} s_h3_alpns[] = { } s_h3_alpns[] = {
{ LSQVER_ID27, (unsigned char *) "\x05h3-27", }, { LSQVER_ID27, (unsigned char *) "\x05h3-27", },
{ LSQVER_ID28, (unsigned char *) "\x05h3-28", }, { LSQVER_ID28, (unsigned char *) "\x05h3-28", },
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-28", }, { LSQVER_ID29, (unsigned char *) "\x05h3-29", },
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-29", },
}; };
struct enc_sess_iquic; struct enc_sess_iquic;
@ -935,7 +936,8 @@ setup_handshake_keys (struct enc_sess_iquic *enc_sess, const lsquic_cid_t *cid)
hp = &enc_sess->esi_hsk_hps[ENC_LEV_CLEAR]; hp = &enc_sess->esi_hsk_hps[ENC_LEV_CLEAR];
HKDF_extract(hsk_secret, &hsk_secret_sz, md, cid->idbuf, cid->len, HKDF_extract(hsk_secret, &hsk_secret_sz, md, cid->idbuf, cid->len,
HSK_SALT, HSK_SALT_SZ); enc_sess->esi_conn->cn_version < LSQVER_ID29
? HSK_SALT_PRE29 : HSK_SALT, HSK_SALT_SZ);
if (enc_sess->esi_flags & ESI_LOG_SECRETS) if (enc_sess->esi_flags & ESI_LOG_SECRETS)
{ {
LSQ_DEBUG("handshake salt: %s", HEXSTR(HSK_SALT, HSK_SALT_SZ, hexbuf)); LSQ_DEBUG("handshake salt: %s", HEXSTR(HSK_SALT, HSK_SALT_SZ, hexbuf));
@ -1177,6 +1179,13 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
LSQ_INFO("could not set stream method"); LSQ_INFO("could not set stream method");
return -1; return -1;
} }
/* TODO: set to transport parameter string instead of the constant string */
if (!SSL_set_quic_early_data_context(enc_sess->esi_ssl,
(unsigned char *) "lsquic", 6))
{
LSQ_INFO("could not set early data context");
return -1;
}
maybe_setup_key_logging(enc_sess); maybe_setup_key_logging(enc_sess);
transpa_len = gen_trans_params(enc_sess, u.trans_params, transpa_len = gen_trans_params(enc_sess, u.trans_params,
@ -3004,3 +3013,23 @@ const struct lsquic_stream_if lsquic_mini_cry_sm_if =
}; };
const unsigned char *const lsquic_retry_key_buf[N_IETF_RETRY_VERSIONS] =
{
/* [draft-ietf-quic-tls-25] Section 5.8 */
(unsigned char *)
"\x4d\x32\xec\xdb\x2a\x21\x33\xc8\x41\xe4\x04\x3d\xf2\x7d\x44\x30",
/* [draft-ietf-quic-tls-29] Section 5.8 */
(unsigned char *)
"\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1",
};
const unsigned char *const lsquic_retry_nonce_buf[N_IETF_RETRY_VERSIONS] =
{
/* [draft-ietf-quic-tls-25] Section 5.8 */
(unsigned char *) "\x4d\x16\x11\xd0\x55\x13\xa5\x52\xc5\x87\xd5\x75",
/* [draft-ietf-quic-tls-29] Section 5.8 */
(unsigned char *) "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c",
};

View file

@ -135,7 +135,9 @@ force_close_conn (lsquic_engine_t *engine, lsquic_conn_t *conn);
#define ENGINE_CALLS_INCR(e) #define ENGINE_CALLS_INCR(e)
#endif #endif
/* Nested calls to LSQUIC are not supported */ /* Nested calls to some LSQUIC functions are not supported. Functions that
* iterate over connections cannot be nested.
*/
#define ENGINE_IN(e) do { \ #define ENGINE_IN(e) do { \
assert(!((e)->pub.enp_flags & ENPUB_PROC)); \ assert(!((e)->pub.enp_flags & ENPUB_PROC)); \
(e)->pub.enp_flags |= ENPUB_PROC; \ (e)->pub.enp_flags |= ENPUB_PROC; \
@ -270,7 +272,7 @@ struct lsquic_engine
int last_tick_diff; int last_tick_diff;
#endif #endif
struct crand crand; struct crand crand;
EVP_AEAD_CTX retry_aead_ctx; EVP_AEAD_CTX retry_aead_ctx[N_IETF_RETRY_VERSIONS];
}; };
@ -483,6 +485,7 @@ lsquic_engine_new (unsigned flags,
{ {
lsquic_engine_t *engine; lsquic_engine_t *engine;
size_t alpn_len; size_t alpn_len;
unsigned i;
char err_buf[100]; char err_buf[100];
if (!api->ea_packets_out) if (!api->ea_packets_out)
@ -691,14 +694,17 @@ lsquic_engine_new (unsigned flags,
#if LSQUIC_CONN_STATS #if LSQUIC_CONN_STATS
engine->stats_fh = api->ea_stats_fh; engine->stats_fh = api->ea_stats_fh;
#endif #endif
if (1 != EVP_AEAD_CTX_init(&engine->retry_aead_ctx, EVP_aead_aes_128_gcm(), for (i = 0; i < sizeof(engine->retry_aead_ctx)
IETF_RETRY_KEY_BUF, IETF_RETRY_KEY_SZ, 16, NULL)) / sizeof(engine->retry_aead_ctx[0]); ++i)
{ if (1 != EVP_AEAD_CTX_init(&engine->retry_aead_ctx[i],
LSQ_ERROR("could not initialize retry AEAD ctx"); EVP_aead_aes_128_gcm(), lsquic_retry_key_buf[i],
lsquic_engine_destroy(engine); IETF_RETRY_KEY_SZ, 16, NULL))
return NULL; {
} LSQ_ERROR("could not initialize retry AEAD ctx #%u", i);
engine->pub.enp_retry_aead_ctx = &engine->retry_aead_ctx; lsquic_engine_destroy(engine);
return NULL;
}
engine->pub.enp_retry_aead_ctx = engine->retry_aead_ctx;
LSQ_INFO("instantiated engine"); LSQ_INFO("instantiated engine");
return engine; return engine;
@ -1421,6 +1427,7 @@ lsquic_engine_destroy (lsquic_engine_t *engine)
{ {
struct lsquic_hash_elem *el; struct lsquic_hash_elem *el;
lsquic_conn_t *conn; lsquic_conn_t *conn;
unsigned i;
LSQ_DEBUG("destroying engine"); LSQ_DEBUG("destroying engine");
#ifndef NDEBUG #ifndef NDEBUG
@ -1515,8 +1522,9 @@ lsquic_engine_destroy (lsquic_engine_t *engine)
#if LSQUIC_COUNT_ENGINE_CALLS #if LSQUIC_COUNT_ENGINE_CALLS
LSQ_NOTICE("number of calls into the engine: %lu", engine->n_engine_calls); LSQ_NOTICE("number of calls into the engine: %lu", engine->n_engine_calls);
#endif #endif
if (engine->pub.enp_retry_aead_ctx) for (i = 0; i < sizeof(engine->retry_aead_ctx)
EVP_AEAD_CTX_cleanup(engine->pub.enp_retry_aead_ctx); / sizeof(engine->retry_aead_ctx[0]); ++i)
EVP_AEAD_CTX_cleanup(&engine->pub.enp_retry_aead_ctx[i]);
free(engine->pub.enp_alpn); free(engine->pub.enp_alpn);
free(engine); free(engine);
} }
@ -1579,7 +1587,7 @@ lsquic_engine_connect (lsquic_engine_t *engine, enum lsquic_version version,
unsigned flags, versions; unsigned flags, versions;
int is_ipv4; int is_ipv4;
ENGINE_IN(engine); ENGINE_CALLS_INCR(engine);
if (engine->flags & ENG_SERVER) if (engine->flags & ENG_SERVER)
{ {
@ -1650,7 +1658,6 @@ lsquic_engine_connect (lsquic_engine_t *engine, enum lsquic_version version,
lsquic_conn_set_ctx(conn, conn_ctx); lsquic_conn_set_ctx(conn, conn_ctx);
conn->cn_if->ci_client_call_on_new(conn); conn->cn_if->ci_client_call_on_new(conn);
end: end:
ENGINE_OUT(engine);
return conn; return conn;
err: err:
conn = NULL; conn = NULL;

View file

@ -4060,7 +4060,7 @@ full_conn_ci_push_stream (struct lsquic_conn *lconn, void *hset,
return -1; return -1;
} }
pushed_stream = new_stream(conn, stream_id, SCF_CALL_ON_NEW); pushed_stream = new_stream(conn, stream_id, 0);
if (!pushed_stream) if (!pushed_stream)
{ {
LSQ_WARN("cannot create stream: %s", strerror(errno)); LSQ_WARN("cannot create stream: %s", strerror(errno));
@ -4085,6 +4085,7 @@ full_conn_ci_push_stream (struct lsquic_conn *lconn, void *hset,
return -1; return -1;
} }
lsquic_stream_call_on_new(pushed_stream);
return 0; return 0;
} }
@ -4187,10 +4188,10 @@ full_conn_ci_is_tickable (lsquic_conn_t *lconn)
LSQ_DEBUG("tickable: flags: 0x%X", conn->fc_flags & send_flags); LSQ_DEBUG("tickable: flags: 0x%X", conn->fc_flags & send_flags);
goto check_can_send; goto check_can_send;
} }
if (lsquic_send_ctl_n_scheduled(&conn->fc_send_ctl) > 0) if (lsquic_send_ctl_has_sendable(&conn->fc_send_ctl))
{ {
LSQ_DEBUG("tickable: has scheduled packets"); LSQ_DEBUG("tickable: has sendable packets");
return 1; /* Don't check can_send */ return 1; /* Don't check can_send: already on scheduled queue */
} }
if ((conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) if ((conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
&& lsquic_send_ctl_has_buffered(&conn->fc_send_ctl)) && lsquic_send_ctl_has_buffered(&conn->fc_send_ctl))

View file

@ -410,6 +410,7 @@ struct ietf_full_conn
struct ver_neg struct ver_neg
ifcli_ver_neg; ifcli_ver_neg;
uint64_t ifcli_max_push_id; uint64_t ifcli_max_push_id;
uint64_t ifcli_min_goaway_stream_id;
enum { enum {
IFCLI_PUSH_ENABLED = 1 << 0, IFCLI_PUSH_ENABLED = 1 << 0,
IFCLI_HSK_SENT_OR_DEL = 1 << 1, IFCLI_HSK_SENT_OR_DEL = 1 << 1,
@ -2889,13 +2890,19 @@ try_to_begin_migration (struct ietf_full_conn *conn,
struct sockaddr_in6 v6; struct sockaddr_in6 v6;
} sockaddr; } sockaddr;
if ((params->tp_set & (1 << TPI_DISABLE_ACTIVE_MIGRATION)) if (!conn->ifc_settings->es_allow_migration)
|| !conn->ifc_settings->es_allow_migration)
{ {
if (params->tp_set & (1 << TPI_DISABLE_ACTIVE_MIGRATION)) LSQ_DEBUG("Migration not allowed: retire PreferredAddress CID");
LSQ_DEBUG("TP disables migration: retire PreferredAddress CID"); return BM_NOT_MIGRATING;
else }
LSQ_DEBUG("Migration not allowed: retire PreferredAddress CID");
if (conn->ifc_conn.cn_version <= LSQVER_ID28 /* Starting with ID-29,
disable_active_migration TP applies only to the time period during
the handshake. Our client does not migrate during the handshake:
this code runs only after handshake has succeeded. */
&& (params->tp_set & (1 << TPI_DISABLE_ACTIVE_MIGRATION)))
{
LSQ_DEBUG("TP disables migration: retire PreferredAddress CID");
return BM_NOT_MIGRATING; return BM_NOT_MIGRATING;
} }
@ -3547,10 +3554,10 @@ ietf_full_conn_ci_is_tickable (struct lsquic_conn *lconn)
LSQ_DEBUG("tickable: send flags: 0x%X", conn->ifc_send_flags); LSQ_DEBUG("tickable: send flags: 0x%X", conn->ifc_send_flags);
goto check_can_send; goto check_can_send;
} }
if (lsquic_send_ctl_n_scheduled(&conn->ifc_send_ctl) > 0) if (lsquic_send_ctl_has_sendable(&conn->ifc_send_ctl))
{ {
LSQ_DEBUG("tickable: has scheduled packets"); LSQ_DEBUG("tickable: has sendable packets");
return 1; /* Don't check can_send */ return 1; /* Don't check can_send: already on scheduled queue */
} }
if (conn->ifc_conn.cn_flags & LSCONN_SEND_BLOCKED) if (conn->ifc_conn.cn_flags & LSCONN_SEND_BLOCKED)
{ {
@ -6046,6 +6053,7 @@ verify_retry_packet (struct ietf_full_conn *conn,
{ {
unsigned char *pseudo_packet; unsigned char *pseudo_packet;
size_t out_len, ad_len; size_t out_len, ad_len;
unsigned ret_ver;
int verified; int verified;
if (1 + CUR_DCID(conn)->len + packet_in->pi_data_sz > 0x1000) if (1 + CUR_DCID(conn)->len + packet_in->pi_data_sz > 0x1000)
@ -6070,11 +6078,13 @@ verify_retry_packet (struct ietf_full_conn *conn,
memcpy(pseudo_packet + 1 + CUR_DCID(conn)->len, packet_in->pi_data, memcpy(pseudo_packet + 1 + CUR_DCID(conn)->len, packet_in->pi_data,
packet_in->pi_data_sz); packet_in->pi_data_sz);
ret_ver = lsquic_version_2_retryver(conn->ifc_conn.cn_version);
out_len = 0; out_len = 0;
ad_len = 1 + CUR_DCID(conn)->len + packet_in->pi_data_sz - 16; ad_len = 1 + CUR_DCID(conn)->len + packet_in->pi_data_sz - 16;
verified = 1 == EVP_AEAD_CTX_open(conn->ifc_enpub->enp_retry_aead_ctx, verified = 1 == EVP_AEAD_CTX_open(
&conn->ifc_enpub->enp_retry_aead_ctx[ret_ver],
pseudo_packet + ad_len, &out_len, out_len, pseudo_packet + ad_len, &out_len, out_len,
IETF_RETRY_NONCE_BUF, IETF_RETRY_NONCE_SZ, lsquic_retry_nonce_buf[ret_ver], IETF_RETRY_NONCE_SZ,
pseudo_packet + ad_len, 16, pseudo_packet, ad_len) pseudo_packet + ad_len, 16, pseudo_packet, ad_len)
&& out_len == 0; && out_len == 0;
@ -7472,7 +7482,7 @@ on_setting (void *ctx, uint64_t setting_id, uint64_t value)
static void static void
on_goaway_server (void *ctx, uint64_t stream_id) on_goaway_server_27 (void *ctx, uint64_t stream_id)
{ {
struct ietf_full_conn *const conn = ctx; struct ietf_full_conn *const conn = ctx;
ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED,
@ -7481,7 +7491,7 @@ on_goaway_server (void *ctx, uint64_t stream_id)
static void static void
on_goaway (void *ctx, uint64_t stream_id) on_goaway_client_28 (void *ctx, uint64_t stream_id)
{ {
struct ietf_full_conn *const conn = ctx; struct ietf_full_conn *const conn = ctx;
struct lsquic_stream *stream; struct lsquic_stream *stream;
@ -7520,6 +7530,68 @@ on_goaway (void *ctx, uint64_t stream_id)
} }
static void
on_goaway_client (void *ctx, uint64_t stream_id)
{
struct ietf_full_conn *const conn = ctx;
struct lsquic_stream *stream;
struct lsquic_hash_elem *el;
enum stream_id_type sit;
sit = stream_id & SIT_MASK;
if (sit != SIT_BIDI_CLIENT)
{
ABORT_QUIETLY(1, HEC_ID_ERROR,
"stream ID %"PRIu64" in GOAWAY frame", stream_id);
return;
}
LSQ_DEBUG("received GOAWAY frame, last good stream ID: %"PRIu64, stream_id);
if (conn->ifc_conn.cn_flags & LSCONN_PEER_GOING_AWAY)
{
if (stream_id == conn->ifc_u.cli.ifcli_min_goaway_stream_id)
{
LSQ_DEBUG("ignore duplicate GOAWAY frame");
return;
}
if (stream_id > conn->ifc_u.cli.ifcli_min_goaway_stream_id)
{
ABORT_QUIETLY(1, HEC_ID_ERROR,
"stream ID %"PRIu64" is larger than one already seen in a "
"previous GOAWAY frame, %"PRIu64, stream_id,
conn->ifc_u.cli.ifcli_min_goaway_stream_id);
return;
}
}
else
{
conn->ifc_u.cli.ifcli_min_goaway_stream_id = stream_id;
conn->ifc_conn.cn_flags |= LSCONN_PEER_GOING_AWAY;
if (conn->ifc_enpub->enp_stream_if->on_goaway_received)
conn->ifc_enpub->enp_stream_if->on_goaway_received(&conn->ifc_conn);
}
for (el = lsquic_hash_first(conn->ifc_pub.all_streams); el;
el = lsquic_hash_next(conn->ifc_pub.all_streams))
{
stream = lsquic_hashelem_getdata(el);
if (stream->id >= stream_id
&& (stream->id & SIT_MASK) == SIT_BIDI_CLIENT)
{
lsquic_stream_received_goaway(stream);
}
}
}
static void
on_goaway_server (void *ctx, uint64_t max_push_id)
{
/* TODO: cancel pushes? */
}
static void static void
on_unexpected_frame (void *ctx, uint64_t frame_type) on_unexpected_frame (void *ctx, uint64_t frame_type)
{ {
@ -7529,7 +7601,49 @@ on_unexpected_frame (void *ctx, uint64_t frame_type)
} }
static const struct hcsi_callbacks hcsi_callbacks_server = static const struct hcsi_callbacks hcsi_callbacks_server_27 =
{
.on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id,
.on_settings_frame = on_settings_frame,
.on_setting = on_setting,
.on_goaway = on_goaway_server_27,
.on_unexpected_frame = on_unexpected_frame,
};
static const struct hcsi_callbacks hcsi_callbacks_client_27 =
{
.on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id_client,
.on_settings_frame = on_settings_frame,
.on_setting = on_setting,
.on_goaway = on_goaway_client_28 /* sic */,
.on_unexpected_frame = on_unexpected_frame,
};
static const struct hcsi_callbacks hcsi_callbacks_server_28 =
{
.on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id,
.on_settings_frame = on_settings_frame,
.on_setting = on_setting,
.on_goaway = on_goaway_server /* sic */,
.on_unexpected_frame = on_unexpected_frame,
};
static const struct hcsi_callbacks hcsi_callbacks_client_28 =
{
.on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id_client,
.on_settings_frame = on_settings_frame,
.on_setting = on_setting,
.on_goaway = on_goaway_client_28,
.on_unexpected_frame = on_unexpected_frame,
};
static const struct hcsi_callbacks hcsi_callbacks_server_29 =
{ {
.on_cancel_push = on_cancel_push, .on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id, .on_max_push_id = on_max_push_id,
@ -7539,13 +7653,13 @@ static const struct hcsi_callbacks hcsi_callbacks_server =
.on_unexpected_frame = on_unexpected_frame, .on_unexpected_frame = on_unexpected_frame,
}; };
static const struct hcsi_callbacks hcsi_callbacks = static const struct hcsi_callbacks hcsi_callbacks_client_29 =
{ {
.on_cancel_push = on_cancel_push, .on_cancel_push = on_cancel_push,
.on_max_push_id = on_max_push_id_client, .on_max_push_id = on_max_push_id_client,
.on_settings_frame = on_settings_frame, .on_settings_frame = on_settings_frame,
.on_setting = on_setting, .on_setting = on_setting,
.on_goaway = on_goaway, .on_goaway = on_goaway_client,
.on_unexpected_frame = on_unexpected_frame, .on_unexpected_frame = on_unexpected_frame,
}; };
@ -7554,10 +7668,36 @@ static lsquic_stream_ctx_t *
hcsi_on_new (void *stream_if_ctx, struct lsquic_stream *stream) hcsi_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
{ {
struct ietf_full_conn *const conn = (void *) stream_if_ctx; struct ietf_full_conn *const conn = (void *) stream_if_ctx;
const struct hcsi_callbacks *callbacks;
conn->ifc_stream_hcsi = stream; conn->ifc_stream_hcsi = stream;
switch ((!!(conn->ifc_flags & IFC_SERVER) << 8) | conn->ifc_conn.cn_version)
{
case (0 << 8) | LSQVER_ID27:
callbacks = &hcsi_callbacks_client_27;
break;
case (1 << 8) | LSQVER_ID27:
callbacks = &hcsi_callbacks_server_27;
break;
case (0 << 8) | LSQVER_ID28:
callbacks = &hcsi_callbacks_client_28;
break;
case (1 << 8) | LSQVER_ID28:
callbacks = &hcsi_callbacks_server_28;
break;
case (0 << 8) | LSQVER_ID29:
callbacks = &hcsi_callbacks_client_29;
break;
default:
assert(0);
/* fallthru */
case (1 << 8) | LSQVER_ID29:
callbacks = &hcsi_callbacks_server_29;
break;
}
lsquic_hcsi_reader_init(&conn->ifc_hcsi.reader, &conn->ifc_conn, lsquic_hcsi_reader_init(&conn->ifc_hcsi.reader, &conn->ifc_conn,
conn->ifc_flags & IFC_SERVER ? &hcsi_callbacks_server : &hcsi_callbacks, callbacks, conn);
conn);
lsquic_stream_wantread(stream, 1); lsquic_stream_wantread(stream, 1);
return stream_if_ctx; return stream_if_ctx;
} }

View file

@ -5,7 +5,11 @@
/* [draft-ietf-quic-tls-23] Section 5.2 */ /* [draft-ietf-quic-tls-23] Section 5.2 */
#define HSK_SALT_BUF "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" \ #define HSK_SALT_BUF "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7" \
"\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02" "\xd2\x43\x2b\xb4\x63\x65\xbe\xf9\xf5\x02"
#define HSK_SALT ((unsigned char *) HSK_SALT_BUF) #define HSK_SALT_PRE29 ((unsigned char *) HSK_SALT_BUF)
/* [draft-ietf-quic-tls-29] Section 5.2 */
#define HSK_SALT ((unsigned char *) \
"\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97" \
"\x86\xf1\x9c\x61\x11\xe0\x43\x90\xa8\x99")
#define HSK_SALT_SZ (sizeof(HSK_SALT_BUF) - 1) #define HSK_SALT_SZ (sizeof(HSK_SALT_BUF) - 1)
#define CLIENT_LABEL "client in" #define CLIENT_LABEL "client in"

View file

@ -9,7 +9,7 @@ enum trans_error_code
{ {
TEC_NO_ERROR = 0x0, TEC_NO_ERROR = 0x0,
TEC_INTERNAL_ERROR = 0x1, TEC_INTERNAL_ERROR = 0x1,
TEC_SERVER_BUSY = 0x2, TEC_CONNECTION_REFUSED = 0x2,
TEC_FLOW_CONTROL_ERROR = 0x3, TEC_FLOW_CONTROL_ERROR = 0x3,
TEC_STREAM_LIMIT_ERROR = 0x4, TEC_STREAM_LIMIT_ERROR = 0x4,
TEC_STREAM_STATE_ERROR = 0x5, TEC_STREAM_STATE_ERROR = 0x5,
@ -27,11 +27,12 @@ enum trans_error_code
#define MAX_IETF_CONN_DCIDS 8 #define MAX_IETF_CONN_DCIDS 8
/* [draft-ietf-quic-tls-25] Section 5.8 */ /* [draft-ietf-quic-tls-25] Section 5.8 */
#define IETF_RETRY_KEY_BUF ((unsigned char *) \
"\x4d\x32\xec\xdb\x2a\x21\x33\xc8\x41\xe4\x04\x3d\xf2\x7d\x44\x30")
#define IETF_RETRY_KEY_SZ 16 #define IETF_RETRY_KEY_SZ 16
#define IETF_RETRY_NONCE_BUF ((unsigned char *) \
"\x4d\x16\x11\xd0\x55\x13\xa5\x52\xc5\x87\xd5\x75")
#define IETF_RETRY_NONCE_SZ 12 #define IETF_RETRY_NONCE_SZ 12
#define N_IETF_RETRY_VERSIONS 2
extern const unsigned char *const lsquic_retry_key_buf[N_IETF_RETRY_VERSIONS];
extern const unsigned char *const lsquic_retry_nonce_buf[N_IETF_RETRY_VERSIONS];
#define lsquic_version_2_retryver(ver_) ((ver_) > LSQVER_ID28)
#endif #endif

View file

@ -1623,7 +1623,7 @@ imico_generate_conn_close (struct ietf_mini_conn *conn)
else if (conn->imc_flags & IMC_BAD_TRANS_PARAMS) else if (conn->imc_flags & IMC_BAD_TRANS_PARAMS)
{ {
is_app = 0; is_app = 0;
error_code = TEC_PROTOCOL_VIOLATION; error_code = TEC_TRANSPORT_PARAMETER_ERROR;
reason = "bad transport parameters"; reason = "bad transport parameters";
rlen = 24; rlen = 24;
} }

View file

@ -220,6 +220,34 @@ lsquic_cid_from_packet (const unsigned char *buf, size_t bufsz,
/* See [draft-ietf-quic-transport-28], Section 12.4 (Table 3) */ /* See [draft-ietf-quic-transport-28], Section 12.4 (Table 3) */
const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] = const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
{ {
[LSQVER_ID29] = {
[ENC_LEV_CLEAR] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
| QUIC_FTBIT_ACK | QUIC_FTBIT_CONNECTION_CLOSE,
[ENC_LEV_EARLY] = QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
| QUIC_FTBIT_STREAM | QUIC_FTBIT_RST_STREAM
| QUIC_FTBIT_BLOCKED | QUIC_FTBIT_CONNECTION_CLOSE
| QUIC_FTBIT_MAX_DATA | QUIC_FTBIT_MAX_STREAM_DATA
| QUIC_FTBIT_MAX_STREAMS | QUIC_FTBIT_STREAM_BLOCKED
| QUIC_FTBIT_STREAMS_BLOCKED
| QUIC_FTBIT_NEW_CONNECTION_ID | QUIC_FTBIT_STOP_SENDING
| QUIC_FTBIT_PATH_CHALLENGE | QUIC_FTBIT_PATH_RESPONSE
| QUIC_FTBIT_RETIRE_CONNECTION_ID,
[ENC_LEV_INIT] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
| QUIC_FTBIT_ACK| QUIC_FTBIT_CONNECTION_CLOSE,
[ENC_LEV_FORW] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
| QUIC_FTBIT_ACK | QUIC_FTBIT_CONNECTION_CLOSE
| QUIC_FTBIT_STREAM | QUIC_FTBIT_RST_STREAM
| QUIC_FTBIT_BLOCKED
| QUIC_FTBIT_MAX_DATA | QUIC_FTBIT_MAX_STREAM_DATA
| QUIC_FTBIT_MAX_STREAMS | QUIC_FTBIT_STREAM_BLOCKED
| QUIC_FTBIT_STREAMS_BLOCKED
| QUIC_FTBIT_NEW_CONNECTION_ID | QUIC_FTBIT_STOP_SENDING
| QUIC_FTBIT_PATH_CHALLENGE | QUIC_FTBIT_PATH_RESPONSE
| QUIC_FTBIT_HANDSHAKE_DONE | QUIC_FTBIT_ACK_FREQUENCY
| QUIC_FTBIT_RETIRE_CONNECTION_ID | QUIC_FTBIT_NEW_TOKEN
| QUIC_FTBIT_TIMESTAMP
,
},
[LSQVER_ID28] = { [LSQVER_ID28] = {
[ENC_LEV_CLEAR] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING [ENC_LEV_CLEAR] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
| QUIC_FTBIT_ACK | QUIC_FTBIT_CONNECTION_CLOSE, | QUIC_FTBIT_ACK | QUIC_FTBIT_CONNECTION_CLOSE,

View file

@ -1369,7 +1369,7 @@ lsquic_send_ctl_pacer_blocked (struct lsquic_send_ctl *ctl)
{ {
return (ctl->sc_flags & SC_PACE) return (ctl->sc_flags & SC_PACE)
&& !lsquic_pacer_can_schedule(&ctl->sc_pacer, && !lsquic_pacer_can_schedule(&ctl->sc_pacer,
ctl->sc_n_scheduled + ctl->sc_n_in_flight_all); ctl->sc_n_in_flight_all);
} }
@ -1674,6 +1674,45 @@ send_ctl_maybe_zero_pad (struct lsquic_send_ctl *ctl,
} }
/* Predict whether lsquic_send_ctl_next_packet_to_send() will return a
* packet by mimicking its logic. Returns true if packet will be returned,
* false otherwise.
*/
int
lsquic_send_ctl_next_packet_to_send_predict (struct lsquic_send_ctl *ctl)
{
const struct lsquic_packet_out *packet_out;
unsigned n_rtos;
n_rtos = ~0u;
TAILQ_FOREACH(packet_out, &ctl->sc_scheduled_packets, po_next)
{
if (!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
&& 0 == ctl->sc_next_limit
&& 0 != (n_rtos == ~0u ? /* Initialize once */
(n_rtos = send_ctl_get_n_consec_rtos(ctl)) : n_rtos))
{
LSQ_DEBUG("send prediction: no, n_rtos: %u", n_rtos);
return 0;
}
if ((packet_out->po_flags & PO_REPACKNO)
&& packet_out->po_regen_sz == packet_out->po_data_sz)
{
LSQ_DEBUG("send prediction: packet %"PRIu64" would be dropped, "
"continue", packet_out->po_packno);
continue;
}
LSQ_DEBUG("send prediction: yes, packet %"PRIu64", flags %u, frames 0x%X",
packet_out->po_packno, (unsigned) packet_out->po_flags,
(unsigned) packet_out->po_frame_types);
return 1;
}
LSQ_DEBUG("send prediction: no, no matching scheduled packets");
return 0;
}
lsquic_packet_out_t * lsquic_packet_out_t *
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size) lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
{ {
@ -1685,6 +1724,9 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
if (!packet_out) if (!packet_out)
return NULL; return NULL;
/* Note: keep logic in this function and in
* lsquic_send_ctl_next_packet_to_send_predict() in synch.
*/
if (!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK)) if (!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
&& send_ctl_get_n_consec_rtos(ctl)) && send_ctl_get_n_consec_rtos(ctl))
{ {

View file

@ -168,6 +168,9 @@ lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *, struct lsquic_packet_out *);
struct lsquic_packet_out * struct lsquic_packet_out *
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *, size_t); lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *, size_t);
int
lsquic_send_ctl_next_packet_to_send_predict (struct lsquic_send_ctl *);
void void
lsquic_send_ctl_expire_all (lsquic_send_ctl_t *ctl); lsquic_send_ctl_expire_all (lsquic_send_ctl_t *ctl);
@ -387,4 +390,9 @@ lsquic_send_ctl_begin_optack_detection (struct lsquic_send_ctl *);
void void
lsquic_send_ctl_path_validated (struct lsquic_send_ctl *); lsquic_send_ctl_path_validated (struct lsquic_send_ctl *);
/* Has immediately sendable packets */
#define lsquic_send_ctl_has_sendable(ctl_) \
(lsquic_send_ctl_n_scheduled(ctl_) > 0 \
&& lsquic_send_ctl_next_packet_to_send_predict(ctl_))
#endif #endif

View file

@ -1747,6 +1747,14 @@ void
lsquic_stream_received_goaway (lsquic_stream_t *stream) lsquic_stream_received_goaway (lsquic_stream_t *stream)
{ {
SM_HISTORY_APPEND(stream, SHE_GOAWAY_IN); SM_HISTORY_APPEND(stream, SHE_GOAWAY_IN);
if (stream->stream_flags & STREAM_GOAWAY_IN)
{
LSQ_DEBUG("ignore duplicate GOAWAY");
return;
}
stream->stream_flags |= STREAM_GOAWAY_IN;
if (0 == stream->read_offset && if (0 == stream->read_offset &&
stream->data_in->di_if->di_empty(stream->data_in)) stream->data_in->di_if->di_empty(stream->data_in))
fake_reset_unused_stream(stream); /* Normal condition */ fake_reset_unused_stream(stream); /* Normal condition */

View file

@ -208,7 +208,7 @@ enum stream_flags {
STREAM_ONNEW_DONE = 1 << 17, /* on_new_stream has been called */ STREAM_ONNEW_DONE = 1 << 17, /* on_new_stream has been called */
STREAM_PUSHING = 1 << 18, STREAM_PUSHING = 1 << 18,
STREAM_NOPUSH = 1 << 19, /* Disallow further push promises */ STREAM_NOPUSH = 1 << 19, /* Disallow further push promises */
STREAM_UNUSED20 = 1 << 20, /* Unused */ STREAM_GOAWAY_IN = 1 << 20, /* Incoming GOAWAY has been processed */
STREAM_UNUSED21 = 1 << 21, /* Unused */ STREAM_UNUSED21 = 1 << 21, /* Unused */
STREAM_RST_ACKED = 1 << 22, /* Packet containing RST has been acked */ STREAM_RST_ACKED = 1 << 22, /* Packet containing RST has been acked */
STREAM_BLOCKED_SENT = 1 << 23, /* Stays set once a STREAM_BLOCKED frame is sent */ STREAM_BLOCKED_SENT = 1 << 23, /* Stays set once a STREAM_BLOCKED frame is sent */

View file

@ -20,6 +20,7 @@ static const unsigned char version_tags[N_LSQVER][4] =
#endif #endif
[LSQVER_ID27] = { 0xFF, 0, 0, 27, }, [LSQVER_ID27] = { 0xFF, 0, 0, 27, },
[LSQVER_ID28] = { 0xFF, 0, 0, 28, }, [LSQVER_ID28] = { 0xFF, 0, 0, 28, },
[LSQVER_ID29] = { 0xFF, 0, 0, 29, },
[LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, }, [LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, },
}; };
@ -58,6 +59,7 @@ const char *const lsquic_ver2str[N_LSQVER] = {
#endif #endif
[LSQVER_ID27] = "FF00001B", [LSQVER_ID27] = "FF00001B",
[LSQVER_ID28] = "FF00001C", [LSQVER_ID28] = "FF00001C",
[LSQVER_ID29] = "FF00001D",
[LSQVER_VERNEG] = "FAFAFAFA", [LSQVER_VERNEG] = "FAFAFAFA",
}; };

View file

@ -33,7 +33,7 @@ main (void)
}; };
HKDF_extract(secret, &secret_len, md, dcid.idbuf, dcid.len, HKDF_extract(secret, &secret_len, md, dcid.idbuf, dcid.len,
HSK_SALT, HSK_SALT_SZ); HSK_SALT_PRE29, HSK_SALT_SZ);
assert(sizeof(expected_secret) == secret_len); assert(sizeof(expected_secret) == secret_len);
assert(0 == memcmp(secret, expected_secret, sizeof(expected_secret))); assert(0 == memcmp(secret, expected_secret, sizeof(expected_secret)));