Release 2.6.0

- [FEATURE] QUIC and HTTP/3 Internet Draft 24 support
This commit is contained in:
Dmitri Tikhonov 2019-11-07 16:19:03 -05:00
parent 75a7a2a36a
commit 03e6b668ec
20 changed files with 514 additions and 124 deletions

View file

@ -1,3 +1,7 @@
2019-11-07
- 2.6.0
- [FEATURE] QUIC and HTTP/3 Internet Draft 24 support
2019-11-07 2019-11-07
- 2.5.2 - 2.5.2
- [BUGFIX] argument order to gQUIC client constructor. Regression - [BUGFIX] argument order to gQUIC client constructor. Regression

View file

@ -49,7 +49,8 @@ IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O0 -g3") SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -O0 -g3")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Werror") SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -Werror")
IF(CMAKE_C_COMPILER MATCHES "clang" AND IF(CMAKE_C_COMPILER MATCHES "clang" AND
NOT "$ENV{TRAVIS}" MATCHES "^true$") NOT "$ENV{TRAVIS}" MATCHES "^true$" AND
NOT "$ENV{EXTRA_CFLAGS}" MATCHES "-fsanitize")
SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fsanitize=address") SET(MY_CMAKE_FLAGS "${MY_CMAKE_FLAGS} -fsanitize=address")
ENDIF() ENDIF()
# Uncomment to enable cleartext protocol mode (no crypto): # Uncomment to enable cleartext protocol mode (no crypto):

View file

@ -24,8 +24,8 @@ extern "C" {
#endif #endif
#define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 5 #define LSQUIC_MINOR_VERSION 6
#define LSQUIC_PATCH_VERSION 2 #define LSQUIC_PATCH_VERSION 0
/** /**
* Engine flags: * Engine flags:
@ -129,6 +129,11 @@ enum lsquic_version
*/ */
LSQVER_ID23, LSQVER_ID23,
/**
* IETF QUIC Draft-24
*/
LSQVER_ID24,
/** /**
* 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.
@ -156,9 +161,11 @@ enum lsquic_version
#define LSQUIC_GQUIC_HEADER_VERSIONS ((1 << LSQVER_039) | (1 << LSQVER_043)) #define LSQUIC_GQUIC_HEADER_VERSIONS ((1 << LSQVER_039) | (1 << LSQVER_043))
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID23) | (1 << LSQVER_VERNEG)) #define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID23) | (1 << LSQVER_ID24) \
| (1 << LSQVER_VERNEG))
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID23) | (1 << LSQVER_VERNEG)) #define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID23) | (1 << LSQVER_ID24) \
| (1 << LSQVER_VERNEG))
enum lsquic_hsk_status enum lsquic_hsk_status
{ {

View file

@ -321,6 +321,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_ID23 ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_ID23 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_ID24 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
&lsquic_enc_session_common_gquic_1 ) &lsquic_enc_session_common_gquic_1 )

View file

@ -79,6 +79,7 @@ static const struct alpn_map {
enum lsquic_version version; enum lsquic_version version;
const unsigned char *alpn; const unsigned char *alpn;
} s_alpns[] = { } s_alpns[] = {
{ LSQVER_ID24, (unsigned char *) "\x05h3-24", },
{ LSQVER_ID23, (unsigned char *) "\x05h3-23", }, { LSQVER_ID23, (unsigned char *) "\x05h3-23", },
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-23", }, { LSQVER_VERNEG, (unsigned char *) "\x05h3-23", },
}; };
@ -509,6 +510,14 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
} }
#endif #endif
} }
#if LSQUIC_TEST_QUANTUM_READINESS
else
{
const char *s = getenv("LSQUIC_TEST_QUANTUM_READINESS");
if (s && atoi(s))
params.tp_flags |= TRAPA_QUANTUM_READY;
}
#endif
params.tp_init_max_data = settings->es_init_max_data; params.tp_init_max_data = settings->es_init_max_data;
params.tp_init_max_stream_data_bidi_local params.tp_init_max_stream_data_bidi_local
= settings->es_init_max_stream_data_bidi_local; = settings->es_init_max_stream_data_bidi_local;
@ -1224,7 +1233,11 @@ init_client (struct enc_sess_iquic *const enc_sess)
int transpa_len; int transpa_len;
char errbuf[ERR_ERROR_STRING_BUF_LEN]; char errbuf[ERR_ERROR_STRING_BUF_LEN];
#define hexbuf errbuf /* This is a dual-purpose buffer */ #define hexbuf errbuf /* This is a dual-purpose buffer */
unsigned char trans_params[0x80]; unsigned char trans_params[0x80
#if LSQUIC_TEST_QUANTUM_READINESS
+ 4 + QUANTUM_READY_SZ
#endif
];
for (am = s_alpns; am < s_alpns + sizeof(s_alpns) for (am = s_alpns; am < s_alpns + sizeof(s_alpns)
/ sizeof(s_alpns[0]); ++am) / sizeof(s_alpns[0]); ++am)
@ -1779,6 +1792,22 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
} }
static struct ku_label
{
const char *str;
uint8_t len;
}
select_ku_label (const struct enc_sess_iquic *enc_sess)
{
if (enc_sess->esi_conn->cn_version == LSQVER_ID23)
return (struct ku_label) { "traffic upd", 11, };
else
return (struct ku_label) { "quic ku", 7, };
}
static enum dec_packin static enum dec_packin
iquic_esf_decrypt_packet (enc_session_t *enc_session_p, iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
struct lsquic_engine_public *enpub, const struct lsquic_conn *lconn, struct lsquic_engine_public *enpub, const struct lsquic_conn *lconn,
@ -1859,9 +1888,10 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
|| packet_in->pi_packno || packet_in->pi_packno
> enc_sess->esi_pairs[enc_sess->esi_key_phase].ykp_thresh) > enc_sess->esi_pairs[enc_sess->esi_key_phase].ykp_thresh)
{ {
const struct ku_label kl = select_ku_label(enc_sess);
lsquic_qhkdf_expand(enc_sess->esi_md, lsquic_qhkdf_expand(enc_sess->esi_md,
enc_sess->esi_traffic_secrets[cliser], enc_sess->esi_trasec_sz, enc_sess->esi_traffic_secrets[cliser], enc_sess->esi_trasec_sz,
"traffic upd", 11, new_secret, enc_sess->esi_trasec_sz); kl.str, kl.len, new_secret, enc_sess->esi_trasec_sz);
if (enc_sess->esi_flags & ESI_LOG_SECRETS) if (enc_sess->esi_flags & ESI_LOG_SECRETS)
LSQ_DEBUG("key phase changed to %u, will try decrypting using " LSQ_DEBUG("key phase changed to %u, will try decrypting using "
"new secret %s", key_phase, HEXSTR(new_secret, "new secret %s", key_phase, HEXSTR(new_secret,
@ -1959,6 +1989,7 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
{ {
LSQ_DEBUG("decryption in the new key phase %u successful, rotate " LSQ_DEBUG("decryption in the new key phase %u successful, rotate "
"keys", key_phase); "keys", key_phase);
const struct ku_label kl = select_ku_label(enc_sess);
pair = &enc_sess->esi_pairs[ key_phase ]; pair = &enc_sess->esi_pairs[ key_phase ];
pair->ykp_thresh = packet_in->pi_packno; pair->ykp_thresh = packet_in->pi_packno;
pair->ykp_ctx[ cliser ] = crypto_ctx_buf; pair->ykp_ctx[ cliser ] = crypto_ctx_buf;
@ -1966,7 +1997,7 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
enc_sess->esi_trasec_sz); enc_sess->esi_trasec_sz);
lsquic_qhkdf_expand(enc_sess->esi_md, lsquic_qhkdf_expand(enc_sess->esi_md,
enc_sess->esi_traffic_secrets[!cliser], enc_sess->esi_trasec_sz, enc_sess->esi_traffic_secrets[!cliser], enc_sess->esi_trasec_sz,
"traffic upd", 11, new_secret, enc_sess->esi_trasec_sz); kl.str, kl.len, new_secret, enc_sess->esi_trasec_sz);
memcpy(enc_sess->esi_traffic_secrets[ !cliser ], new_secret, memcpy(enc_sess->esi_traffic_secrets[ !cliser ], new_secret,
enc_sess->esi_trasec_sz); enc_sess->esi_trasec_sz);
s = init_crypto_ctx(&pair->ykp_ctx[ !cliser ], enc_sess->esi_md, s = init_crypto_ctx(&pair->ykp_ctx[ !cliser ], enc_sess->esi_md,

View file

@ -1234,7 +1234,7 @@ find_conn_by_srst (struct lsquic_engine *engine,
static int static int
process_packet_in (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in, process_packet_in (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
struct packin_parse_state *ppstate, const struct sockaddr *sa_local, struct packin_parse_state *ppstate, const struct sockaddr *sa_local,
const struct sockaddr *sa_peer, void *peer_ctx) const struct sockaddr *sa_peer, void *peer_ctx, int full_udp_packet)
{ {
lsquic_conn_t *conn; lsquic_conn_t *conn;
const unsigned char *packet_in_data; const unsigned char *packet_in_data;
@ -1257,6 +1257,7 @@ process_packet_in (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
if (!conn) if (!conn)
{ {
if (engine->pub.enp_settings.es_honor_prst if (engine->pub.enp_settings.es_honor_prst
&& full_udp_packet
&& !(packet_in->pi_flags & PI_GQUIC) && !(packet_in->pi_flags & PI_GQUIC)
&& engine->pub.enp_srst_hash && engine->pub.enp_srst_hash
&& (conn = find_conn_by_srst(engine, packet_in))) && (conn = find_conn_by_srst(engine, packet_in)))
@ -2562,7 +2563,7 @@ lsquic_engine_packet_in (lsquic_engine_t *engine,
packet_in->pi_flags |= (3 & ecn) << PIBIT_ECN_SHIFT; packet_in->pi_flags |= (3 & ecn) << PIBIT_ECN_SHIFT;
eng_hist_inc(&engine->history, packet_in->pi_received, sl_packets_in); eng_hist_inc(&engine->history, packet_in->pi_received, sl_packets_in);
s = process_packet_in(engine, packet_in, &ppstate, sa_local, sa_peer, s = process_packet_in(engine, packet_in, &ppstate, sa_local, sa_peer,
peer_ctx); peer_ctx, packet_in->pi_data_sz == packet_in_size);
n_zeroes += s == 0; n_zeroes += s == 0;
} }
while (0 == s && packet_in_data < packet_end); while (0 == s && packet_in_data < packet_end);

View file

@ -4792,7 +4792,7 @@ process_max_streams_frame (struct ietf_full_conn *conn,
if (max_stream_id > VINT_MAX_VALUE) if (max_stream_id > VINT_MAX_VALUE)
{ {
ABORT_QUIETLY(0, TEC_STREAM_LIMIT_ERROR, ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR,
"MAX_STREAMS: max %s stream ID of %"PRIu64" exceeds maximum " "MAX_STREAMS: max %s stream ID of %"PRIu64" exceeds maximum "
"stream ID", sd == SD_BIDI ? "bidi" : "uni", max_stream_id); "stream ID", sd == SD_BIDI ? "bidi" : "uni", max_stream_id);
return 0; return 0;
@ -4880,7 +4880,12 @@ process_new_connection_id_frame (struct ietf_full_conn *conn,
parsed_len = conn->ifc_conn.cn_pf->pf_parse_new_conn_id(p, len, parsed_len = conn->ifc_conn.cn_pf->pf_parse_new_conn_id(p, len,
&seqno, &retire_prior_to, &cid, &token); &seqno, &retire_prior_to, &cid, &token);
if (parsed_len < 0) if (parsed_len < 0)
{
if (parsed_len == -2)
ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR,
"NEW_CONNECTION_ID contains invalid CID length");
return 0; return 0;
}
if (seqno > UINT32_MAX || retire_prior_to > UINT32_MAX) if (seqno > UINT32_MAX || retire_prior_to > UINT32_MAX)
{ /* It is wasteful to use 8-byte integers for these counters, so this { /* It is wasteful to use 8-byte integers for these counters, so this
@ -4893,7 +4898,7 @@ process_new_connection_id_frame (struct ietf_full_conn *conn,
if (retire_prior_to > seqno) if (retire_prior_to > seqno)
{ {
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION, ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR,
"NEW_CONNECTION_ID: Retire Prior To=%"PRIu64" is larger then the " "NEW_CONNECTION_ID: Retire Prior To=%"PRIu64" is larger then the "
"Sequence Number=%"PRIu64, retire_prior_to, seqno); "Sequence Number=%"PRIu64, retire_prior_to, seqno);
return 0; return 0;
@ -5044,7 +5049,7 @@ process_retire_connection_id_frame (struct ietf_full_conn *conn,
{ {
if (LSQUIC_CIDS_EQ(&cce->cce_cid, &packet_in->pi_dcid)) if (LSQUIC_CIDS_EQ(&cce->cce_cid, &packet_in->pi_dcid))
{ {
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION, "cannot retire CID " ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR, "cannot retire CID "
"seqno=%"PRIu64", for it is used as DCID in the packet", seqno); "seqno=%"PRIu64", for it is used as DCID in the packet", seqno);
return 0; return 0;
} }
@ -5071,6 +5076,13 @@ process_new_token_frame (struct ietf_full_conn *conn,
if (parsed_len < 0) if (parsed_len < 0)
return 0; return 0;
if (0 == token_sz)
{
ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR, "received an empty "
"NEW_TOKEN frame");
return 0;
}
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG) if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)) || LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT))
{ {
@ -5129,6 +5141,7 @@ static unsigned
process_streams_blocked_frame (struct ietf_full_conn *conn, process_streams_blocked_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len) struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{ {
lsquic_stream_id_t max_stream_id;
uint64_t stream_limit; uint64_t stream_limit;
enum stream_dir sd; enum stream_dir sd;
int parsed_len; int parsed_len;
@ -5138,6 +5151,15 @@ process_streams_blocked_frame (struct ietf_full_conn *conn,
if (parsed_len < 0) if (parsed_len < 0)
return 0; return 0;
max_stream_id = stream_limit << SIT_SHIFT;
if (max_stream_id > VINT_MAX_VALUE)
{
ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR,
"STREAMS_BLOCKED: max %s stream ID of %"PRIu64" exceeds maximum "
"stream ID", sd == SD_BIDI ? "bidi" : "uni", max_stream_id);
return 0;
}
LSQ_DEBUG("received STREAMS_BLOCKED frame: limited to %"PRIu64 LSQ_DEBUG("received STREAMS_BLOCKED frame: limited to %"PRIu64
" %sdirectional stream%.*s", stream_limit, sd == SD_UNI ? "uni" : "bi", " %sdirectional stream%.*s", stream_limit, sd == SD_UNI ? "uni" : "bi",
stream_limit != 1, "s"); stream_limit != 1, "s");
@ -5257,6 +5279,9 @@ init_new_path (struct ietf_full_conn *conn, struct conn_path *path,
} }
else if (!dcid_changed) else if (!dcid_changed)
{ {
/* It is OK to reuse DCID if the peer did not use a new DCID when its
* address changed. See [draft-ietf-quic-transport-24] Section 9.5.
*/
path->cop_path.np_dcid = CUR_NPATH(conn)->np_dcid; path->cop_path.np_dcid = CUR_NPATH(conn)->np_dcid;
LSQ_DEBUGC("assigned already-used DCID %"CID_FMT" to new path %u, " LSQ_DEBUGC("assigned already-used DCID %"CID_FMT" to new path %u, "
"as incoming DCID did not change", "as incoming DCID did not change",
@ -5422,7 +5447,6 @@ process_retry_packet (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in) struct lsquic_packet_in *packet_in)
{ {
lsquic_cid_t scid; lsquic_cid_t scid;
int cidlen_diff;
if (conn->ifc_flags & (IFC_SERVER|IFC_RETRIED)) if (conn->ifc_flags & (IFC_SERVER|IFC_RETRIED))
{ {
@ -5439,10 +5463,9 @@ process_retry_packet (struct ietf_full_conn *conn,
return 0; return 0;
} }
cidlen_diff = (int) CUR_DCID(conn)->len - (int) packet_in->pi_scid_len;
if (0 != lsquic_send_ctl_retry(&conn->ifc_send_ctl, if (0 != lsquic_send_ctl_retry(&conn->ifc_send_ctl,
packet_in->pi_data + packet_in->pi_token, packet_in->pi_data + packet_in->pi_token,
packet_in->pi_token_size, cidlen_diff)) packet_in->pi_token_size))
return -1; return -1;
lsquic_scid_from_packet_in(packet_in, &scid); lsquic_scid_from_packet_in(packet_in, &scid);
@ -5700,11 +5723,23 @@ process_regular_packet (struct ietf_full_conn *conn,
packet_in->pi_received); packet_in->pi_received);
switch (st) { switch (st) {
case REC_ST_OK: case REC_ST_OK:
if (!(conn->ifc_flags & ( if (!(conn->ifc_flags & (IFC_SERVER|IFC_DCID_SET))
IFC_SERVER|
IFC_DCID_SET))
&& (packet_in->pi_scid_len)) && (packet_in->pi_scid_len))
{ {
if (CUR_DCID(conn)->len == packet_in->pi_scid_len
&& 0 == memcmp(CUR_DCID(conn)->idbuf,
packet_in->pi_data + packet_in->pi_scid_off,
packet_in->pi_scid_len))
{
/*
* [draft-ietf-quic-transport-24] Section 17.2.5:
" A client MUST discard a Retry packet that contains a Source
" Connection ID field that is identical to the Destination
" Connection ID field of its Initial packet.
*/
LSQ_DEBUG("server provided same SCID as ODCID: discard packet");
return 0;
}
conn->ifc_flags |= IFC_DCID_SET; conn->ifc_flags |= IFC_DCID_SET;
lsquic_scid_from_packet_in(packet_in, CUR_DCID(conn)); lsquic_scid_from_packet_in(packet_in, CUR_DCID(conn));
LSQ_DEBUGC("set DCID to %"CID_FMT, LSQ_DEBUGC("set DCID to %"CID_FMT,
@ -5740,10 +5775,14 @@ process_regular_packet (struct ietf_full_conn *conn,
frame_types &= ~(1 << QUIC_FRAME_PING); frame_types &= ~(1 << QUIC_FRAME_PING);
} }
#endif #endif
was_missing = packet_in->pi_packno != if (frame_types & IQUIC_FRAME_ACKABLE_MASK)
{
was_missing = packet_in->pi_packno !=
lsquic_rechist_largest_packno(&conn->ifc_rechist[pns]); lsquic_rechist_largest_packno(&conn->ifc_rechist[pns]);
conn->ifc_n_slack_akbl[pns] ++conn->ifc_n_slack_akbl[pns];
+= !!(frame_types & IQUIC_FRAME_ACKABLE_MASK); }
else
was_missing = 0;
try_queueing_ack(conn, pns, was_missing, packet_in->pi_received); try_queueing_ack(conn, pns, was_missing, packet_in->pi_received);
} }
conn->ifc_incoming_ecn <<= 1; conn->ifc_incoming_ecn <<= 1;

View file

@ -218,29 +218,127 @@ imico_stream_flush (void *stream)
} }
static struct stream_frame *
imico_find_stream_frame (const struct ietf_mini_conn *conn,
enum enc_level enc_level, unsigned read_off)
{
struct stream_frame *frame;
if (conn->imc_last_in.frame && enc_level == conn->imc_last_in.enc_level
&& read_off == DF_ROFF(conn->imc_last_in.frame))
return conn->imc_last_in.frame;
TAILQ_FOREACH(frame, &conn->imc_crypto_frames, next_frame)
if (enc_level == frame->stream_id && read_off == DF_ROFF(frame))
return frame;
return NULL;
}
static void
imico_read_chlo_size (struct ietf_mini_conn *conn, const unsigned char *buf,
size_t sz)
{
const unsigned char *const end = buf + sz;
assert(conn->imc_streams[ENC_LEV_CLEAR].mcs_read_off < 4);
switch (conn->imc_streams[ENC_LEV_CLEAR].mcs_read_off)
{
case 0:
if (buf == end)
return;
if (*buf != 1)
{
LSQ_DEBUG("Does not begin with ClientHello");
conn->imc_flags |= IMC_ERROR;
return;
}
++buf;
/* fall-through */
case 1:
if (buf == end)
return;
if (*buf != 0)
{
LSQ_DEBUG("ClientHello larger than 16K");
conn->imc_flags |= IMC_ERROR;
return;
}
++buf;
/* fall-through */
case 2:
if (buf == end)
return;
conn->imc_ch_len = *buf << 8;
++buf;
/* fall-through */
default:
if (buf == end)
return;
conn->imc_ch_len |= *buf;
}
}
static int
imico_chlo_has_been_consumed (const struct ietf_mini_conn *conn)
{
return conn->imc_streams[ENC_LEV_CLEAR].mcs_read_off > 3
&& conn->imc_streams[ENC_LEV_CLEAR].mcs_read_off >= conn->imc_ch_len;
}
static ssize_t static ssize_t
imico_stream_readf (void *stream, imico_stream_readf (void *stream,
size_t (*readf)(void *, const unsigned char *, size_t, int), void *ctx) size_t (*readf)(void *, const unsigned char *, size_t, int), void *ctx)
{ {
struct mini_crypto_stream *const cryst = stream; struct mini_crypto_stream *const cryst = stream;
struct ietf_mini_conn *const conn = cryst_get_conn(cryst); struct ietf_mini_conn *const conn = cryst_get_conn(cryst);
struct stream_frame *frame = conn->imc_last_in.frame; struct stream_frame *frame;
size_t nread; const unsigned char *buf;
size_t nread, total_read;
unsigned avail;
if (cryst->mcs_enc_level == conn->imc_last_in.enc_level total_read = 0;
&& frame && cryst->mcs_read_off == DF_ROFF(frame)) while ((frame = imico_find_stream_frame(conn, cryst->mcs_enc_level,
cryst->mcs_read_off)))
{ {
nread = readf(ctx, frame->data_frame.df_data avail = DF_SIZE(frame) - frame->data_frame.df_read_off;
+ frame->data_frame.df_read_off, DF_SIZE(frame) buf = frame->data_frame.df_data + frame->data_frame.df_read_off;
- frame->data_frame.df_read_off, DF_FIN(frame)); nread = readf(ctx, buf, avail, DF_FIN(frame));
if (cryst->mcs_enc_level == ENC_LEV_CLEAR && cryst->mcs_read_off < 4)
imico_read_chlo_size(conn, buf, nread);
total_read += nread;
cryst->mcs_read_off += nread; cryst->mcs_read_off += nread;
frame->data_frame.df_read_off += nread; frame->data_frame.df_read_off += nread;
LSQ_DEBUG("read %zu bytes at offset %"PRIu64" on enc level %u", nread, LSQ_DEBUG("read %zu bytes at offset %"PRIu64" on enc level %u", nread,
DF_ROFF(frame), cryst->mcs_enc_level); DF_ROFF(frame), cryst->mcs_enc_level);
return nread; if (DF_END(frame) == DF_ROFF(frame))
{
if (frame == conn->imc_last_in.frame)
conn->imc_last_in.frame = NULL;
else
{
TAILQ_REMOVE(&conn->imc_crypto_frames, frame, next_frame);
--conn->imc_n_crypto_frames;
conn->imc_crypto_frames_sz -= DF_SIZE(frame);
lsquic_packet_in_put(&conn->imc_enpub->enp_mm,
frame->packet_in);
lsquic_malo_put(frame);
}
}
if (nread < avail)
break;
} }
if (total_read > 0)
return total_read;
else else
{ {
/* CRYPTO streams never end, so zero bytes read always means
* EWOULDBLOCK
*/
errno = EWOULDBLOCK; errno = EWOULDBLOCK;
return -1; return -1;
} }
@ -376,6 +474,9 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
conn->imc_conn.cn_esf_c = select_esf_common_by_ver(version); conn->imc_conn.cn_esf_c = select_esf_common_by_ver(version);
TAILQ_INIT(&conn->imc_packets_out); TAILQ_INIT(&conn->imc_packets_out);
TAILQ_INIT(&conn->imc_app_packets); TAILQ_INIT(&conn->imc_app_packets);
TAILQ_INIT(&conn->imc_crypto_frames);
if (odcid)
conn->imc_flags |= IMC_ADDR_VALIDATED;
LSQ_DEBUG("created mini connection object %p; max packet size=%hu", LSQ_DEBUG("created mini connection object %p; max packet size=%hu",
conn, conn->imc_path.np_pack_size); conn, conn->imc_path.np_pack_size);
@ -396,6 +497,7 @@ ietf_mini_conn_ci_destroy (struct lsquic_conn *lconn)
struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn; struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
struct lsquic_packet_out *packet_out; struct lsquic_packet_out *packet_out;
struct lsquic_packet_in *packet_in; struct lsquic_packet_in *packet_in;
struct stream_frame *frame;
while ((packet_out = TAILQ_FIRST(&conn->imc_packets_out))) while ((packet_out = TAILQ_FIRST(&conn->imc_packets_out)))
{ {
@ -407,6 +509,12 @@ ietf_mini_conn_ci_destroy (struct lsquic_conn *lconn)
TAILQ_REMOVE(&conn->imc_app_packets, packet_in, pi_next); TAILQ_REMOVE(&conn->imc_app_packets, packet_in, pi_next);
lsquic_packet_in_put(&conn->imc_enpub->enp_mm, packet_in); lsquic_packet_in_put(&conn->imc_enpub->enp_mm, packet_in);
} }
while ((frame = TAILQ_FIRST(&conn->imc_crypto_frames)))
{
TAILQ_REMOVE(&conn->imc_crypto_frames, frame, next_frame);
lsquic_packet_in_put(&conn->imc_enpub->enp_mm, frame->packet_in);
lsquic_malo_put(frame);
}
if (lconn->cn_enc_session) if (lconn->cn_enc_session)
lconn->cn_esf.i->esfi_destroy(lconn->cn_enc_session); lconn->cn_esf.i->esfi_destroy(lconn->cn_enc_session);
LSQ_DEBUG("ietf_mini_conn_ci_destroyed"); LSQ_DEBUG("ietf_mini_conn_ci_destroyed");
@ -602,6 +710,44 @@ imico_process_stream_frame (IMICO_PROC_FRAME_ARGS)
} }
static int
imico_stash_stream_frame (struct ietf_mini_conn *conn,
enum enc_level enc_level, struct lsquic_packet_in *packet_in,
const struct stream_frame *frame)
{
struct stream_frame *copy;
if (conn->imc_n_crypto_frames >= IMICO_MAX_STASHED_FRAMES)
{
LSQ_INFO("cannot stash more CRYPTO frames, at %hhu already, while max "
"is %u", conn->imc_n_crypto_frames, IMICO_MAX_STASHED_FRAMES);
return -1;
}
if (conn->imc_crypto_frames_sz + DF_SIZE(frame) > IMICO_MAX_BUFFERED_CRYPTO)
{
LSQ_INFO("cannot stash more than %u bytes of CRYPTO frames",
IMICO_MAX_BUFFERED_CRYPTO);
return -1;
}
copy = lsquic_malo_get(conn->imc_enpub->enp_mm.malo.stream_frame);
if (!copy)
{
LSQ_INFO("could not allocate stream frame for stashing");
return -1;
}
*copy = *frame;
copy->packet_in = lsquic_packet_in_get(packet_in);
copy->stream_id = enc_level;
TAILQ_INSERT_TAIL(&conn->imc_crypto_frames, copy, next_frame);
++conn->imc_n_crypto_frames;
conn->imc_crypto_frames_sz += DF_SIZE(frame);
return 0;
}
static unsigned static unsigned
imico_process_crypto_frame (IMICO_PROC_FRAME_ARGS) imico_process_crypto_frame (IMICO_PROC_FRAME_ARGS)
{ {
@ -618,11 +764,19 @@ imico_process_crypto_frame (IMICO_PROC_FRAME_ARGS)
enc_level = lsquic_packet_in_enc_level(packet_in); enc_level = lsquic_packet_in_enc_level(packet_in);
EV_LOG_CRYPTO_FRAME_IN(LSQUIC_LOG_CONN_ID, &stream_frame, enc_level); EV_LOG_CRYPTO_FRAME_IN(LSQUIC_LOG_CONN_ID, &stream_frame, enc_level);
if (!(conn->imc_streams[enc_level].mcs_flags & MCS_CREATED) if (conn->imc_streams[enc_level].mcs_read_off >= DF_OFF(&stream_frame)
|| conn->imc_streams[enc_level].mcs_read_off < && conn->imc_streams[enc_level].mcs_read_off < DF_END(&stream_frame))
stream_frame.data_frame.df_offset
+ stream_frame.data_frame.df_size)
LSQ_DEBUG("Got CRYPTO frame for enc level #%u", enc_level); LSQ_DEBUG("Got CRYPTO frame for enc level #%u", enc_level);
else if (conn->imc_streams[enc_level].mcs_read_off < DF_OFF(&stream_frame))
{
LSQ_DEBUG("Can't read CRYPTO frame on enc level #%u at offset %"PRIu64
" yet -- stash", enc_level, DF_OFF(&stream_frame));
if (0 == imico_stash_stream_frame(conn, enc_level, packet_in,
&stream_frame))
return parsed_len;
else
return 0;
}
else else
{ {
LSQ_DEBUG("Got duplicate CRYPTO frame for enc level #%u -- ignore", LSQ_DEBUG("Got duplicate CRYPTO frame for enc level #%u -- ignore",
@ -657,11 +811,19 @@ imico_process_crypto_frame (IMICO_PROC_FRAME_ARGS)
imico_dispatch_stream_events(conn); imico_dispatch_stream_events(conn);
conn->imc_last_in.frame = NULL; conn->imc_last_in.frame = NULL;
if (DF_ROFF(&stream_frame) < DF_END(&stream_frame))
{
/* This is an odd condition, but let's handle it just in case */
LSQ_DEBUG("New CRYPTO frame on enc level #%u not fully read -- stash",
enc_level);
if (0 != imico_stash_stream_frame(conn, enc_level, packet_in,
&stream_frame))
return 0;
}
if (enc_level == ENC_LEV_CLEAR && stream_frame.data_frame.df_offset == 0
/* Assume that we have ClientHello at offset zero and that it has if (enc_level == ENC_LEV_CLEAR
* transport parameters. && imico_chlo_has_been_consumed(conn)
*/
&& (conn->imc_flags & (IMC_ENC_SESS_INITED|IMC_HAVE_TP)) && (conn->imc_flags & (IMC_ENC_SESS_INITED|IMC_HAVE_TP))
== IMC_ENC_SESS_INITED) == IMC_ENC_SESS_INITED)
{ {
@ -1452,6 +1614,7 @@ ietf_mini_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
return TICK_CLOSE; return TICK_CLOSE;
} }
if (conn->imc_flags & if (conn->imc_flags &
(IMC_QUEUED_ACK_INIT|IMC_QUEUED_ACK_HSK|IMC_QUEUED_ACK_APP)) (IMC_QUEUED_ACK_INIT|IMC_QUEUED_ACK_HSK|IMC_QUEUED_ACK_APP))
{ {

View file

@ -64,6 +64,7 @@ struct ietf_mini_conn
} imc_last_in; } imc_last_in;
TAILQ_HEAD(, lsquic_packet_in) imc_app_packets; TAILQ_HEAD(, lsquic_packet_in) imc_app_packets;
TAILQ_HEAD(, lsquic_packet_out) imc_packets_out; TAILQ_HEAD(, lsquic_packet_out) imc_packets_out;
TAILQ_HEAD(, stream_frame) imc_crypto_frames;
packno_set_t imc_sent_packnos; packno_set_t imc_sent_packnos;
packno_set_t imc_recvd_packnos[N_PNS]; packno_set_t imc_recvd_packnos[N_PNS];
packno_set_t imc_acked_packnos[N_PNS]; packno_set_t imc_acked_packnos[N_PNS];
@ -72,6 +73,11 @@ struct ietf_mini_conn
unsigned imc_error_code; unsigned imc_error_code;
unsigned imc_bytes_in; unsigned imc_bytes_in;
unsigned imc_bytes_out; unsigned imc_bytes_out;
unsigned short imc_crypto_frames_sz;
/* We need to read in the length of ClientHello to check when we have fed
* it to the crypto layer.
*/
unsigned short imc_ch_len;
unsigned char imc_next_packno; unsigned char imc_next_packno;
unsigned char imc_hsk_count; unsigned char imc_hsk_count;
/* We don't send more than eight in the first flight, and so it's OK to /* We don't send more than eight in the first flight, and so it's OK to
@ -85,9 +91,22 @@ struct ietf_mini_conn
uint8_t imc_ecn_counts_out[N_PNS][4]; uint8_t imc_ecn_counts_out[N_PNS][4];
uint8_t imc_incoming_ecn; uint8_t imc_incoming_ecn;
uint8_t imc_tls_alert; uint8_t imc_tls_alert;
#define IMICO_MAX_STASHED_FRAMES 10u
unsigned char imc_n_crypto_frames;
struct network_path imc_path; struct network_path imc_path;
}; };
/* [draft-ietf-quic-transport-24] Section 7.4
*
" Implementations MUST support buffering at least 4096 bytes of data
" received in CRYPTO frames out of order. Endpoints MAY choose to
" allow more data to be buffered during the handshake. A larger limit
" during the handshake could allow for larger keys or credentials to be
" exchanged. An endpoint's buffer size does not need to remain
" constant during the life of the connection.
*/
#define IMICO_MAX_BUFFERED_CRYPTO (6u * 1024u)
struct lsquic_conn * struct lsquic_conn *
lsquic_mini_conn_ietf_new (struct lsquic_engine_public *, lsquic_mini_conn_ietf_new (struct lsquic_engine_public *,
const struct lsquic_packet_in *, const struct lsquic_packet_in *,

View file

@ -209,9 +209,10 @@ extern const char *const lsquic_pns2str[];
| QUIC_FTBIT_NEW_TOKEN \ | QUIC_FTBIT_NEW_TOKEN \
| QUIC_FTBIT_CRYPTO ) | QUIC_FTBIT_CRYPTO )
/* [draft-ietf-quic-transport-20] Section 13.1.1 */ /* [draft-ietf-quic-transport-24] Section 1.2 */
#define IQUIC_FRAME_ACKABLE_MASK ( \ #define IQUIC_FRAME_ACKABLE_MASK ( \
ALL_IQUIC_FRAMES & ~(QUIC_FTBIT_ACK|QUIC_FTBIT_PADDING)) ALL_IQUIC_FRAMES & ~(QUIC_FTBIT_ACK|QUIC_FTBIT_PADDING\
|QUIC_FTBIT_CONNECTION_CLOSE))
/* [draft-ietf-quic-transport-20], Section 13.2 */ /* [draft-ietf-quic-transport-20], Section 13.2 */
/* We bend some rules and retransmit BLOCKED, MAX_DATA, MAX_STREAM_DATA, /* We bend some rules and retransmit BLOCKED, MAX_DATA, MAX_STREAM_DATA,

View file

@ -366,7 +366,7 @@ lsquic_packet_out_ack_streams (lsquic_packet_out_t *packet_out)
static int static int
split_off_last_frames (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out, 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, lsquic_packet_out_t *new_packet_out, struct stream_rec **srecs,
unsigned n_srecs) unsigned n_srecs, enum quic_frame_type frame_type)
{ {
unsigned n; unsigned n;
@ -376,7 +376,7 @@ split_off_last_frames (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
memcpy(new_packet_out->po_data + new_packet_out->po_data_sz, memcpy(new_packet_out->po_data + new_packet_out->po_data_sz,
packet_out->po_data + srec->sr_off, srec->sr_len); packet_out->po_data + srec->sr_off, srec->sr_len);
if (0 != lsquic_packet_out_add_stream(new_packet_out, mm, if (0 != lsquic_packet_out_add_stream(new_packet_out, mm,
srec->sr_stream, QUIC_FRAME_STREAM, srec->sr_stream, frame_type,
new_packet_out->po_data_sz, srec->sr_len)) new_packet_out->po_data_sz, srec->sr_len))
return -1; return -1;
srec->sr_frame_type = 0; srec->sr_frame_type = 0;
@ -394,7 +394,7 @@ split_off_last_frames (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
static int static int
move_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out, move_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
lsquic_packet_out_t *new_packet_out, struct stream_rec **srecs, lsquic_packet_out_t *new_packet_out, struct stream_rec **srecs,
unsigned n_srecs, unsigned max_idx) unsigned n_srecs, unsigned max_idx, enum quic_frame_type frame_type)
{ {
unsigned n; unsigned n;
struct stream_rec *const max_srec = srecs[max_idx]; struct stream_rec *const max_srec = srecs[max_idx];
@ -405,7 +405,7 @@ move_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
packet_out->po_data + max_srec->sr_off + max_srec->sr_len, 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); packet_out->po_data_sz - max_srec->sr_off - max_srec->sr_len);
if (0 != lsquic_packet_out_add_stream(new_packet_out, mm, if (0 != lsquic_packet_out_add_stream(new_packet_out, mm,
max_srec->sr_stream, QUIC_FRAME_STREAM, max_srec->sr_stream, frame_type,
new_packet_out->po_data_sz, max_srec->sr_len)) new_packet_out->po_data_sz, max_srec->sr_len))
return -1; return -1;
@ -448,7 +448,7 @@ split_reader_size (void *ctx)
static size_t static size_t
split_reader_read (void *ctx, void *buf, size_t len, int *fin) split_stream_reader_read (void *ctx, void *buf, size_t len, int *fin)
{ {
struct split_reader_ctx *const reader_ctx = ctx; struct split_reader_ctx *const reader_ctx = ctx;
if (len > reader_ctx->len - reader_ctx->off) if (len > reader_ctx->len - reader_ctx->off)
@ -460,10 +460,23 @@ split_reader_read (void *ctx, void *buf, size_t len, int *fin)
} }
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 static int
split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out, 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, lsquic_packet_out_t *new_packet_out, const struct parse_funcs *pf,
struct stream_rec **srecs, unsigned n_srecs, unsigned max_idx) 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_rec *const max_srec = srecs[max_idx];
struct stream_frame frame; struct stream_frame frame;
@ -471,8 +484,12 @@ split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
unsigned n; unsigned n;
struct split_reader_ctx reader_ctx; struct split_reader_ctx reader_ctx;
len = pf->pf_parse_stream_frame(packet_out->po_data + max_srec->sr_off, if (frame_type == QUIC_FRAME_STREAM)
max_srec->sr_len, &frame); 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) if (len < 0)
{ {
LSQ_ERROR("could not parse own frame"); LSQ_ERROR("could not parse own frame");
@ -490,19 +507,27 @@ split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
reader_ctx.len = frame.data_frame.df_size - frame.data_frame.df_size / 2; reader_ctx.len = frame.data_frame.df_size - frame.data_frame.df_size / 2;
reader_ctx.fin = frame.data_frame.df_fin; reader_ctx.fin = frame.data_frame.df_fin;
len = pf->pf_gen_stream_frame( if (frame_type == QUIC_FRAME_STREAM)
len = pf->pf_gen_stream_frame(
new_packet_out->po_data + new_packet_out->po_data_sz, new_packet_out->po_data + new_packet_out->po_data_sz,
lsquic_packet_out_avail(new_packet_out), frame.stream_id, lsquic_packet_out_avail(new_packet_out), frame.stream_id,
frame.data_frame.df_offset + frame.data_frame.df_size / 2, frame.data_frame.df_offset + frame.data_frame.df_size / 2,
split_reader_fin(&reader_ctx), split_reader_size(&reader_ctx), split_reader_fin(&reader_ctx), split_reader_size(&reader_ctx),
split_reader_read, &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) if (len < 0)
{ {
LSQ_ERROR("could not generate new frame 1"); LSQ_ERROR("could not generate new frame 1");
return -1; return -1;
} }
if (0 != lsquic_packet_out_add_stream(new_packet_out, mm, if (0 != lsquic_packet_out_add_stream(new_packet_out, mm,
max_srec->sr_stream, QUIC_FRAME_STREAM, max_srec->sr_stream, max_srec->sr_frame_type,
new_packet_out->po_data_sz, len)) new_packet_out->po_data_sz, len))
return -1; return -1;
new_packet_out->po_data_sz += len; new_packet_out->po_data_sz += len;
@ -517,11 +542,18 @@ split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
reader_ctx.off = 0; reader_ctx.off = 0;
reader_ctx.len = frame.data_frame.df_size / 2; reader_ctx.len = frame.data_frame.df_size / 2;
reader_ctx.fin = 0; reader_ctx.fin = 0;
len = pf->pf_gen_stream_frame( if (frame_type == QUIC_FRAME_STREAM)
len = pf->pf_gen_stream_frame(
packet_out->po_data + max_srec->sr_off, max_srec->sr_len, packet_out->po_data + max_srec->sr_off, max_srec->sr_len,
frame.stream_id, frame.data_frame.df_offset, frame.stream_id, frame.data_frame.df_offset,
split_reader_fin(&reader_ctx), split_reader_size(&reader_ctx), split_reader_fin(&reader_ctx), split_reader_size(&reader_ctx),
split_reader_read, &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) if (len < 0)
{ {
LSQ_ERROR("could not generate new frame 2"); LSQ_ERROR("could not generate new frame 2");
@ -540,7 +572,7 @@ split_largest_frame (struct lsquic_mm *mm, lsquic_packet_out_t *packet_out,
#ifndef NDEBUG #ifndef NDEBUG
static void static void
verify_srecs (lsquic_packet_out_t *packet_out) verify_srecs (lsquic_packet_out_t *packet_out, enum quic_frame_type frame_type)
{ {
struct packet_out_srec_iter posi; struct packet_out_srec_iter posi;
const struct stream_rec *srec; const struct stream_rec *srec;
@ -553,7 +585,7 @@ verify_srecs (lsquic_packet_out_t *packet_out)
for ( ; srec; srec = posi_next(&posi)) for ( ; srec; srec = posi_next(&posi))
{ {
assert(srec->sr_off == off); assert(srec->sr_off == off);
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); assert(srec->sr_frame_type == frame_type);
off += srec->sr_len; off += srec->sr_len;
} }
@ -573,15 +605,21 @@ lsquic_packet_out_split_in_two (struct lsquic_mm *mm,
struct stream_rec *srec; struct stream_rec *srec;
unsigned n_srecs_alloced = sizeof(local_arr) / sizeof(local_arr[0]); unsigned n_srecs_alloced = sizeof(local_arr) / sizeof(local_arr[0]);
unsigned n_srecs, max_idx, n, nbytes; unsigned n_srecs, max_idx, n, nbytes;
enum quic_frame_type frame_type;
#ifndef NDEBUG #ifndef NDEBUG
unsigned short frame_sum = 0; unsigned short frame_sum = 0;
#endif #endif
int rv; int rv;
/* We only split buffered packets; buffered packets contain only STREAM /* We only split buffered packets or initial packets with CRYPTO frames.
* frames: * Either contain just one frame type: STREAM or CRYPTO.
*/ */
assert(packet_out->po_frame_types == (1 << QUIC_FRAME_STREAM)); 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; n_srecs = 0;
#ifdef WIN32 #ifdef WIN32
@ -589,8 +627,8 @@ lsquic_packet_out_split_in_two (struct lsquic_mm *mm,
#endif #endif
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi)) for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
{ {
/* We only expect references to STREAM frames (buffered packets): */ assert(srec->sr_frame_type == QUIC_FRAME_STREAM
assert(srec->sr_frame_type == QUIC_FRAME_STREAM); || srec->sr_frame_type == QUIC_FRAME_CRYPTO);
if (n_srecs >= n_srecs_alloced) if (n_srecs >= n_srecs_alloced)
{ {
n_srecs_alloced *= 2; n_srecs_alloced *= 2;
@ -636,7 +674,7 @@ lsquic_packet_out_split_in_two (struct lsquic_mm *mm,
if (nbytes >= excess_bytes) if (nbytes >= excess_bytes)
{ {
rv = split_off_last_frames(mm, packet_out, new_packet_out, rv = split_off_last_frames(mm, packet_out, new_packet_out,
srecs + n + 1, n_srecs - n - 1); srecs + n + 1, n_srecs - n - 1, frame_type);
goto end; goto end;
} }
@ -648,7 +686,7 @@ lsquic_packet_out_split_in_two (struct lsquic_mm *mm,
if (nbytes >= excess_bytes) if (nbytes >= excess_bytes)
{ {
rv = move_largest_frame(mm, packet_out, new_packet_out, srecs, rv = move_largest_frame(mm, packet_out, new_packet_out, srecs,
n_srecs, max_idx); n_srecs, max_idx, frame_type);
goto end; goto end;
} }
@ -657,17 +695,17 @@ lsquic_packet_out_split_in_two (struct lsquic_mm *mm,
* the only frame) in two. * the only frame) in two.
*/ */
rv = split_largest_frame(mm, packet_out, new_packet_out, pf, srecs, rv = split_largest_frame(mm, packet_out, new_packet_out, pf, srecs,
n_srecs, max_idx); n_srecs, max_idx, frame_type);
end: end:
if (srecs != local_arr) if (srecs != local_arr)
free(srecs); free(srecs);
if (0 == rv) if (0 == rv)
{ {
new_packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM; new_packet_out->po_frame_types |= 1 << frame_type;
#ifndef NDEBUG #ifndef NDEBUG
verify_srecs(packet_out); verify_srecs(packet_out, frame_type);
verify_srecs(new_packet_out); verify_srecs(new_packet_out, frame_type);
#endif #endif
} }
return rv; return rv;

View file

@ -245,6 +245,11 @@ struct parse_funcs
(*pf_gen_max_data_frame) (unsigned char *, size_t, uint64_t); (*pf_gen_max_data_frame) (unsigned char *, size_t, uint64_t);
unsigned unsigned
(*pf_max_data_frame_size) (uint64_t); (*pf_max_data_frame_size) (uint64_t);
/*
* Returns number of bytes parsed on success or negative value on error:
* -1 Out of input buffer
* -2 Invalid CID length value
*/
int int
(*pf_parse_new_conn_id) (const unsigned char *, size_t, uint64_t *, (*pf_parse_new_conn_id) (const unsigned char *, size_t, uint64_t *,
uint64_t *, lsquic_cid_t *, const unsigned char **); uint64_t *, lsquic_cid_t *, const unsigned char **);

View file

@ -1272,6 +1272,8 @@ ietf_v1_parse_new_conn_id (const unsigned char *buf, size_t len,
return -1; return -1;
cid_len = *p++; cid_len = *p++;
if (cid_len == 0 || cid_len > MAX_CID_LEN)
return -2;
if ((unsigned) (end - p) < cid_len + IQUIC_SRESET_TOKEN_SZ) if ((unsigned) (end - p) < cid_len + IQUIC_SRESET_TOKEN_SZ)
return -1; return -1;

View file

@ -267,41 +267,24 @@ put_req (struct pr_queue *prq, struct packet_req *req)
int int
prq_new_req (struct pr_queue *prq, enum packet_req_type type, lsquic_prq_new_req (struct pr_queue *prq, enum packet_req_type type,
const struct lsquic_packet_in *packet_in, void *peer_ctx, unsigned flags, enum lsquic_version version, unsigned short data_sz,
const struct sockaddr *local_addr, const struct sockaddr *peer_addr) const lsquic_cid_t *dcid, const lsquic_cid_t *scid, void *peer_ctx,
const struct sockaddr *local_addr, const struct sockaddr *peer_addr)
{ {
struct packet_req *req; struct packet_req *req;
lsquic_ver_tag_t ver_tag;
enum lsquic_version version;
enum pr_flags flags;
unsigned max, size, rand; unsigned max, size, rand;
if (packet_in->pi_flags & PI_GQUIC)
flags = PR_GQUIC;
else
flags = 0;
if (packet_in->pi_quic_ver)
{
memcpy(&ver_tag, packet_in->pi_data + packet_in->pi_quic_ver,
sizeof(ver_tag));
version = lsquic_tag2ver(ver_tag);
}
else /* Got to set it to something sensible... */
version = LSQVER_ID23;
if (type == PACKET_REQ_PUBRES && !(flags & PR_GQUIC)) if (type == PACKET_REQ_PUBRES && !(flags & PR_GQUIC))
{ {
if (packet_in->pi_data_sz <= IQUIC_MIN_SRST_SIZE) if (data_sz <= IQUIC_MIN_SRST_SIZE)
{ {
LSQ_DEBUGC("not scheduling public reset: incoming packet for CID " LSQ_DEBUGC("not scheduling public reset: incoming packet for CID "
"%"CID_FMT" too small: %hu bytes", "%"CID_FMT" too small: %hu bytes", CID_BITS(dcid), data_sz);
CID_BITS(&packet_in->pi_dcid), packet_in->pi_data_sz);
return -1; return -1;
} }
/* Use a random stateless reset size */ /* Use a random stateless reset size */
max = MIN(IQUIC_MAX_SRST_SIZE, packet_in->pi_data_sz - 1u); max = MIN(IQUIC_MAX_SRST_SIZE, data_sz - 1u);
if (max > IQUIC_MIN_SRST_SIZE) if (max > IQUIC_MIN_SRST_SIZE)
{ {
rand = get_rand_byte(prq); rand = get_rand_byte(prq);
@ -310,7 +293,7 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
else else
size = IQUIC_MIN_SRST_SIZE; size = IQUIC_MIN_SRST_SIZE;
LSQ_DEBUGC("selected %u-byte reset size for CID %"CID_FMT LSQ_DEBUGC("selected %u-byte reset size for CID %"CID_FMT
" (range is [%u, %u])", size, CID_BITS(&packet_in->pi_dcid), " (range is [%u, %u])", size, CID_BITS(dcid),
IQUIC_MIN_SRST_SIZE, max); IQUIC_MIN_SRST_SIZE, max);
} }
else else
@ -324,7 +307,7 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
} }
req->pr_type = type; req->pr_type = type;
req->pr_dcid = packet_in->pi_dcid; req->pr_dcid = *dcid;
if (lsquic_hash_find(prq->prq_reqs_hash, req, sizeof(req))) if (lsquic_hash_find(prq->prq_reqs_hash, req, sizeof(req)))
{ {
LSQ_DEBUG("request for this DCID and type already exists"); LSQ_DEBUG("request for this DCID and type already exists");
@ -344,7 +327,7 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
req->pr_flags = flags; req->pr_flags = flags;
req->pr_rst_sz = size; req->pr_rst_sz = size;
req->pr_version = version; req->pr_version = version;
lsquic_scid_from_packet_in(packet_in, &req->pr_scid); req->pr_scid = *scid;
req->pr_path.np_peer_ctx = peer_ctx; req->pr_path.np_peer_ctx = peer_ctx;
memcpy(NP_LOCAL_SA(&req->pr_path), local_addr, memcpy(NP_LOCAL_SA(&req->pr_path), local_addr,
sizeof(req->pr_path.np_local_addr)); sizeof(req->pr_path.np_local_addr));
@ -357,6 +340,36 @@ prq_new_req (struct pr_queue *prq, enum packet_req_type type,
} }
int
prq_new_req (struct pr_queue *prq, enum packet_req_type type,
const struct lsquic_packet_in *packet_in, void *peer_ctx,
const struct sockaddr *local_addr, const struct sockaddr *peer_addr)
{
lsquic_ver_tag_t ver_tag;
enum lsquic_version version;
enum pr_flags flags;
lsquic_cid_t scid;
if (packet_in->pi_flags & PI_GQUIC)
flags = PR_GQUIC;
else
flags = 0;
if (packet_in->pi_quic_ver)
{
memcpy(&ver_tag, packet_in->pi_data + packet_in->pi_quic_ver,
sizeof(ver_tag));
version = lsquic_tag2ver(ver_tag);
}
else /* Got to set it to something sensible... */
version = LSQVER_ID24;
lsquic_scid_from_packet_in(packet_in, &scid);
return lsquic_prq_new_req(prq, type, flags, version, packet_in->pi_data_sz,
&packet_in->pi_dcid, &scid, peer_ctx, local_addr, peer_addr);
}
static size_t static size_t
max_bufsz (const struct pr_queue *prq) max_bufsz (const struct pr_queue *prq)
{ {

View file

@ -1654,7 +1654,7 @@ lsquic_send_ctl_have_outgoing_retx_frames (const lsquic_send_ctl_t *ctl)
} }
static void static int
send_ctl_set_packet_out_token (const struct lsquic_send_ctl *ctl, send_ctl_set_packet_out_token (const struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out) struct lsquic_packet_out *packet_out)
{ {
@ -1664,7 +1664,7 @@ send_ctl_set_packet_out_token (const struct lsquic_send_ctl *ctl,
if (!token) if (!token)
{ {
LSQ_WARN("malloc failed: cannot set initial token"); LSQ_WARN("malloc failed: cannot set initial token");
return; return -1;
} }
memcpy(token, ctl->sc_token, ctl->sc_token_sz); memcpy(token, ctl->sc_token, ctl->sc_token_sz);
@ -1672,6 +1672,7 @@ send_ctl_set_packet_out_token (const struct lsquic_send_ctl *ctl,
packet_out->po_token_len = ctl->sc_token_sz; packet_out->po_token_len = ctl->sc_token_sz;
packet_out->po_flags |= PO_NONCE; packet_out->po_flags |= PO_NONCE;
LSQ_DEBUG("set initial token on packet"); LSQ_DEBUG("set initial token on packet");
return 0;
} }
@ -1706,7 +1707,7 @@ send_ctl_allocate_packet (struct lsquic_send_ctl *ctl, enum packno_bits bits,
{ {
packet_out->po_header_type = HETY_INITIAL; packet_out->po_header_type = HETY_INITIAL;
if (ctl->sc_token) if (ctl->sc_token)
send_ctl_set_packet_out_token(ctl, packet_out); (void) send_ctl_set_packet_out_token(ctl, packet_out);
} }
else else
packet_out->po_header_type = HETY_HANDSHAKE; packet_out->po_header_type = HETY_HANDSHAKE;
@ -2553,11 +2554,31 @@ 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;
unsigned off;
off = 0;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
off = srec->sr_off + srec->sr_len;
assert(off);
packet_out->po_data_sz = off;
packet_out->po_frame_types &= ~QUIC_FTBIT_PADDING;
}
int int
lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl, lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl,
const unsigned char *token, size_t token_sz, int cidlen_diff) const unsigned char *token, size_t token_sz)
{ {
struct lsquic_packet_out *packet_out; struct lsquic_packet_out *packet_out, *next, *new_packet_out;
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
size_t sz;
if (token_sz >= 1ull << (sizeof(packet_out->po_token_len) * 8)) if (token_sz >= 1ull << (sizeof(packet_out->po_token_len) * 8))
{ {
@ -2565,14 +2586,6 @@ lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl,
return -1; return -1;
} }
send_ctl_expire(ctl, PNS_INIT, EXFI_ALL);
packet_out = TAILQ_FIRST(&ctl->sc_lost_packets);
if (!(packet_out && HETY_INITIAL == packet_out->po_header_type))
{
LSQ_INFO("cannot find initial packet to add token to");
return -1;
}
++ctl->sc_retry_count; ++ctl->sc_retry_count;
if (ctl->sc_retry_count > 3) if (ctl->sc_retry_count > 3)
{ {
@ -2580,26 +2593,57 @@ lsquic_send_ctl_retry (struct lsquic_send_ctl *ctl,
return -1; return -1;
} }
send_ctl_expire(ctl, PNS_INIT, EXFI_ALL);
if (0 != lsquic_send_ctl_set_token(ctl, token, token_sz)) if (0 != lsquic_send_ctl_set_token(ctl, token, token_sz))
return -1; return -1;
if (packet_out->po_nonce) for (packet_out = TAILQ_FIRST(&ctl->sc_lost_packets); packet_out; packet_out = next)
free(packet_out->po_nonce);
packet_out->po_nonce = malloc(token_sz);
if (!packet_out->po_nonce)
{ {
LSQ_WARN("%s: malloc failed", __func__); next = TAILQ_NEXT(packet_out, po_next);
return -1; if (HETY_INITIAL != packet_out->po_header_type)
continue;
if (packet_out->po_nonce)
free(packet_out->po_nonce);
if (0 != send_ctl_set_packet_out_token(ctl, packet_out))
{
LSQ_INFO("cannot set out token on packet");
return -1;
}
if (packet_out->po_frame_types & QUIC_FTBIT_PADDING)
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 == 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);
return 0;
}
else
{
LSQ_DEBUG("could not split lost packet into two");
send_ctl_destroy_packet(ctl, new_packet_out);
return -1;
}
}
} }
memcpy(packet_out->po_nonce, token, token_sz);
packet_out->po_flags |= PO_NONCE;
packet_out->po_token_len = token_sz;
packet_out->po_data_sz -= token_sz;
if (cidlen_diff > 0)
packet_out->po_data_sz += cidlen_diff;
else if (cidlen_diff < 0)
packet_out->po_data_sz -= -cidlen_diff;
return 0; return 0;
} }

View file

@ -335,8 +335,7 @@ void
lsquic_send_ctl_verneg_done (struct lsquic_send_ctl *); lsquic_send_ctl_verneg_done (struct lsquic_send_ctl *);
int int
lsquic_send_ctl_retry (struct lsquic_send_ctl *, const unsigned char *, lsquic_send_ctl_retry (struct lsquic_send_ctl *, const unsigned char *, size_t);
size_t, int);
int int
lsquic_send_ctl_set_token (struct lsquic_send_ctl *, lsquic_send_ctl_set_token (struct lsquic_send_ctl *,

View file

@ -111,6 +111,10 @@ lsquic_tp_encode (const struct transport_params *params,
if (params->tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6)) if (params->tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6))
need += 4 + preferred_address_size(params); need += 4 + preferred_address_size(params);
} }
#if LSQUIC_TEST_QUANTUM_READINESS
else if (params->tp_flags & TRAPA_QUANTUM_READY)
need += 4 + QUANTUM_READY_SZ;
#endif
for (tpi = 0; tpi <= MAX_TPI; ++tpi) for (tpi = 0; tpi <= MAX_TPI; ++tpi)
if ((NUMERIC_TRANS_PARAMS & (1 << tpi)) if ((NUMERIC_TRANS_PARAMS & (1 << tpi))
@ -252,6 +256,16 @@ lsquic_tp_encode (const struct transport_params *params,
return -1; return -1;
} }
#if LSQUIC_TEST_QUANTUM_READINESS
if (params->tp_flags & TRAPA_QUANTUM_READY)
{
WRITE_UINT_TO_P(TPI_QUANTUM_READINESS, 16);
WRITE_UINT_TO_P(QUANTUM_READY_SZ, 16);
memset(p, 'Q', QUANTUM_READY_SZ);
p += QUANTUM_READY_SZ;
}
#endif
assert(buf + need == p); assert(buf + need == p);
return (int) (p - buf); return (int) (p - buf);

View file

@ -51,6 +51,12 @@ enum trapa_flags
TRAPA_PREFADDR_IPv4 = 1 << 2, /* Preferred IPv4 address is set */ TRAPA_PREFADDR_IPv4 = 1 << 2, /* Preferred IPv4 address is set */
TRAPA_PREFADDR_IPv6 = 1 << 3, /* Preferred IPv6 address is set */ TRAPA_PREFADDR_IPv6 = 1 << 3, /* Preferred IPv6 address is set */
TRAPA_ORIGINAL_CID = 1 << 4, /* Original CID is set */ TRAPA_ORIGINAL_CID = 1 << 4, /* Original CID is set */
#if LSQUIC_TEST_QUANTUM_READINESS
#define QUANTUM_READY_SZ 1200
/* https://github.com/quicwg/base-drafts/wiki/Quantum-Readiness-test */
#define TPI_QUANTUM_READINESS 3127
TRAPA_QUANTUM_READY = 1 << 5, /* Include "Quantum Readiness" TP */
#endif
}; };
struct transport_params struct transport_params

View file

@ -15,6 +15,7 @@ static const unsigned char version_tags[N_LSQVER][4] =
[LSQVER_098] = { 'Q', '0', '9', '8', }, [LSQVER_098] = { 'Q', '0', '9', '8', },
#endif #endif
[LSQVER_ID23] = { 0xFF, 0, 0, 23, }, [LSQVER_ID23] = { 0xFF, 0, 0, 23, },
[LSQVER_ID24] = { 0xFF, 0, 0, 24, },
[LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, }, [LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, },
}; };
@ -52,6 +53,7 @@ const char *const lsquic_ver2str[N_LSQVER] = {
[LSQVER_098] = "Q098", [LSQVER_098] = "Q098",
#endif #endif
[LSQVER_ID23] = "FF000017", [LSQVER_ID23] = "FF000017",
[LSQVER_ID24] = "FF000018",
[LSQVER_VERNEG] = "FAFAFAFA", [LSQVER_VERNEG] = "FAFAFAFA",
}; };

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-23"; const unsigned char alpn[] = "\x5h3-23\x5h3-24";
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,