Release 2.6.7

- [FEATURE] Implement the QL extension (offered by default).
- [BUGFIX] Abort when encountering unexpected HTTP/3 frames.
- [BUGFIX] Acknowledge (QPACK) HTTP/3 trailers correctly.
- [DEBUG] Turn on debug message for next advisory tick.
This commit is contained in:
Dmitri Tikhonov 2019-11-22 00:40:05 -05:00
parent 55f8042d41
commit 02b6086dba
18 changed files with 317 additions and 49 deletions

View file

@ -1,3 +1,10 @@
2019-11-22
- 2.6.7
- [FEATURE] Implement the QL extension (offered by default).
- [BUGFIX] Abort when encountering unexpected HTTP/3 frames.
- [BUGFIX] Acknowledge (QPACK) HTTP/3 trailers correctly.
- [DEBUG] Turn on debug message for next advisory tick.
2019-11-20
- 2.6.6
- [BUGFIX] Using HTTP/3 to HTTP/1.x converter.

View file

@ -25,7 +25,7 @@ extern "C" {
#define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 6
#define LSQUIC_PATCH_VERSION 6
#define LSQUIC_PATCH_VERSION 7
/**
* Engine flags:
@ -369,6 +369,9 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
/** Allow migration by default */
#define LSQUIC_DF_ALLOW_MIGRATION 1
/** Use QL loss bits by default */
#define LSQUIC_DF_QL_BITS 1
/* 1: Cubic; 2: BBR */
#define LSQUIC_DF_CC_ALGO 2
@ -736,6 +739,13 @@ struct lsquic_engine_settings {
* 2: BBR
*/
unsigned es_cc_algo;
/**
* Use QL loss bits.
*
* Default value is @ref LSQUIC_DF_QL_BITS
*/
int es_ql_bits;
};
/* Initialize `settings' to default values */

View file

@ -243,6 +243,7 @@ struct enc_sess_iquic
ESI_CACHED_INFO = 1 << 9,
ESI_1RTT_ACKED = 1 << 10,
ESI_WANT_TICKET = 1 << 11,
ESI_QL_BITS = 1 << 12,
} esi_flags;
enum evp_aead_direction_t
esi_dir[2]; /* client, server */
@ -333,6 +334,9 @@ apply_hp (struct enc_sess_iquic *enc_sess,
hp->hp_gen_mask(enc_sess, hp, cliser, dst + packno_off + 4, mask);
LSQ_DEBUG("apply header protection using mask %s",
HEXSTR(mask, 5, mask_str));
if (enc_sess->esi_flags & ESI_QL_BITS)
dst[0] ^= (0x7 | ((dst[0] >> 7) << 3)) & mask[0];
else
dst[0] ^= (0xF | (((dst[0] & 0x80) == 0) << 4)) & mask[0];
switch (packno_len)
{
@ -391,6 +395,9 @@ strip_hp (struct enc_sess_iquic *enc_sess,
hp->hp_gen_mask(enc_sess, hp, cliser, iv, mask);
LSQ_DEBUG("strip header protection using mask %s",
HEXSTR(mask, 5, mask_str));
if (enc_sess->esi_flags & ESI_QL_BITS)
dst[0] ^= (0x7 | ((dst[0] >> 7) << 3)) & mask[0];
else
dst[0] ^= (0xF | (((dst[0] & 0x80) == 0) << 4)) & mask[0];
packno = 0;
shift = 0;
@ -539,6 +546,8 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
- !!(params.tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6));
if (!settings->es_allow_migration)
params.tp_disable_active_migration = 1;
if (settings->es_ql_bits)
params.tp_flags |= TRAPA_QL_BITS;
len = lsquic_tp_encode(&params, buf, bufsz);
if (len >= 0)
@ -1467,6 +1476,13 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
}
}
if ((trans_params->tp_flags & TRAPA_QL_BITS)
&& enc_sess->esi_enpub->enp_settings.es_ql_bits)
{
LSQ_DEBUG("enable QL loss bits");
enc_sess->esi_flags |= ESI_QL_BITS;
}
return 0;
}
@ -1978,7 +1994,15 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
goto err;
}
if (dst[0] & (0x0C << (packet_in->pi_header_type == HETY_NOT_SET)))
if (enc_sess->esi_flags & ESI_QL_BITS)
{
packet_in->pi_flags |= PI_LOG_QL_BITS;
if (dst[0] & 0x10)
packet_in->pi_flags |= PI_SQUARE_BIT;
if (dst[0] & 0x08)
packet_in->pi_flags |= PI_LOSS_BIT;
}
else if (dst[0] & (0x0C << (packet_in->pi_header_type == HETY_NOT_SET)))
{
LSQ_DEBUG("reserved bits are not set to zero");
dec_packin = DECPI_VIOLATION;

View file

@ -80,7 +80,7 @@
#include "lsquic_logger.h"
#ifndef LSQUIC_DEBUG_NEXT_ADV_TICK
#define LSQUIC_DEBUG_NEXT_ADV_TICK 0
#define LSQUIC_DEBUG_NEXT_ADV_TICK 1
#endif
#define MIN(a, b) ((a) < (b) ? (a) : (b))
@ -322,6 +322,7 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
settings->es_qpack_enc_max_size = LSQUIC_DF_QPACK_ENC_MAX_SIZE;
settings->es_qpack_enc_max_blocked = LSQUIC_DF_QPACK_ENC_MAX_BLOCKED;
settings->es_allow_migration = LSQUIC_DF_ALLOW_MIGRATION;
settings->es_ql_bits = LSQUIC_DF_QL_BITS;
}

View file

@ -66,6 +66,17 @@ lsquic_ev_log_packet_in (const lsquic_cid_t *cid,
(unsigned) (packet_in->pi_data_sz + GQUIC_PACKET_HASH_SZ));
break;
default:
if (packet_in->pi_flags & PI_LOG_QL_BITS)
LCID("packet in: %"PRIu64", type: %s, size: %u; ecn: %u, spin: %d; "
"path: %hhu; Q: %d; L: %d",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type],
(unsigned) (packet_in->pi_data_sz + IQUIC_TAG_LEN),
lsquic_packet_in_ecn(packet_in),
/* spin bit value is only valid for short packet headers */
lsquic_packet_in_spin_bit(packet_in), packet_in->pi_path_id,
((packet_in->pi_flags & PI_SQUARE_BIT) > 0),
((packet_in->pi_flags & PI_LOSS_BIT) > 0));
else
LCID("packet in: %"PRIu64", type: %s, size: %u; ecn: %u, spin: %d; "
"path: %hhu",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type],
@ -220,6 +231,27 @@ lsquic_ev_log_packet_sent (const lsquic_cid_t *cid,
*/
lsquic_frame_types_to_str(frames, sizeof(frames),
packet_out->po_frame_types));
else if (packet_out->po_lflags & POL_LOG_QL_BITS)
LCID("sent packet %"PRIu64", type %s, crypto: %s, size %hu, frame "
"types: %s, ecn: %u, spin: %d; kp: %u, path: %hhu, flags: %u; "
"Q: %u; L: %u",
packet_out->po_packno, lsquic_hety2str[packet_out->po_header_type],
lsquic_enclev2str[ lsquic_packet_out_enc_level(packet_out) ],
packet_out->po_enc_data_sz,
/* Frame types is a list of different frames types contained
* in the packet, no more. Count and order of frames is not
* printed.
*/
lsquic_frame_types_to_str(frames, sizeof(frames),
packet_out->po_frame_types),
lsquic_packet_out_ecn(packet_out),
/* spin bit value is only valid for short packet headers */
lsquic_packet_out_spin_bit(packet_out),
lsquic_packet_out_kp(packet_out),
packet_out->po_path->np_path_id,
(unsigned) packet_out->po_flags,
lsquic_packet_out_square_bit(packet_out),
lsquic_packet_out_loss_bit(packet_out));
else
LCID("sent packet %"PRIu64", type %s, crypto: %s, size %hu, frame "
"types: %s, ecn: %u, spin: %d; kp: %u, path: %hhu, flags: %u",

View file

@ -2690,6 +2690,13 @@ handshake_ok (struct lsquic_conn *lconn)
LSQ_DEBUG("peer transport parameters: %s",
(lsquic_tp_to_str(params, buf, sizeof(buf)), buf));
if ((params->tp_flags & TRAPA_QL_BITS)
&& conn->ifc_settings->es_ql_bits)
{
LSQ_DEBUG("turn on QL loss bits");
lsquic_send_ctl_do_ql_bits(&conn->ifc_send_ctl);
}
if (params->tp_init_max_streams_bidi > (1ull << 60)
|| params->tp_init_max_streams_uni > (1ull << 60))
{

View file

@ -83,7 +83,11 @@ typedef struct lsquic_packet_in
#define PIBIT_BITS_SHIFT 12
PI_BITS_BIT_0 = (1 <<12),
PI_BITS_BIT_1 = (1 <<13),
} pi_flags:16;
/* Square bit and loss bit flags are used for logging */
PI_LOG_QL_BITS = (1 <<14),
PI_SQUARE_BIT = (1 <<15),
PI_LOSS_BIT = (1 <<16),
} pi_flags;
/* pi_token and pi_token_size are set in Initial and Retry packets */
unsigned short pi_token_size; /* Size of the token */
unsigned char pi_token; /* Offset to token */

View file

@ -143,7 +143,10 @@ typedef struct lsquic_packet_out
#define POECN_SHIFT 4
POL_ECNBIT_0 = 1 << 4,
POL_ECNBIT_1 = 1 << 5,
} po_lflags:8;
POL_LOG_QL_BITS = 1 << 6,
POL_SQUARE_BIT = 1 << 7,
POL_LOSS_BIT = 1 << 8,
} po_lflags:16;
unsigned char *po_data;
/* A lot of packets contain data belonging to only one stream. Thus,
@ -199,6 +202,8 @@ typedef struct lsquic_packet_out
} while (0)
#define lsquic_packet_out_spin_bit(p) (((p)->po_flags & PO_SPIN_BIT) > 0)
#define lsquic_packet_out_square_bit(p) (((p)->po_lflags & POL_SQUARE_BIT) > 0)
#define lsquic_packet_out_loss_bit(p) (((p)->po_lflags & POL_LOSS_BIT) > 0)
#define lsquic_packet_out_set_spin_bit(p, b) do { \
(p)->po_flags &= ~PO_SPIN_BIT; \

View file

@ -260,6 +260,8 @@ gen_short_pkt_header (const struct lsquic_conn *lconn,
buf[0] = 0x40
| (lsquic_packet_out_spin_bit(packet_out) << 5)
| (lsquic_packet_out_square_bit(packet_out) << 4)
| (lsquic_packet_out_loss_bit(packet_out) << 3)
| (lsquic_packet_out_key_phase(packet_out) << 2)
| packno_bits
;

View file

@ -384,10 +384,12 @@ qdh_supply_hset_to_stream (struct qpack_dec_hdl *qdh,
struct uncompressed_headers *uh = NULL;
const struct lsqpack_header *header;
enum lsquic_header_status st;
int push_promise;
unsigned i;
void *hset;
hset = hset_if->hsi_create_header_set(qdh->qdh_hsi_ctx, 0);
push_promise = lsquic_stream_header_is_pp(stream);
hset = hset_if->hsi_create_header_set(qdh->qdh_hsi_ctx, push_promise);
if (!hset)
{
LSQ_INFO("call to hsi_create_header_set failed");
@ -440,6 +442,21 @@ qdh_supply_hset_to_stream (struct qpack_dec_hdl *qdh,
}
static int
qdh_process_qlist (struct qpack_dec_hdl *qdh,
struct lsquic_stream *stream, struct lsqpack_header_list *qlist)
{
if (!lsquic_stream_header_is_trailer(stream))
return qdh_supply_hset_to_stream(qdh, stream, qlist);
else
{
LSQ_DEBUG("discard trailer header set");
lsqpack_dec_destroy_header_list(qlist);
return 0;
}
}
static enum lsqpack_read_header_status
qdh_header_read_results (struct qpack_dec_hdl *qdh,
struct lsquic_stream *stream, enum lsqpack_read_header_status rhs,
@ -452,7 +469,7 @@ qdh_header_read_results (struct qpack_dec_hdl *qdh,
{
if (qlist)
{
if (0 != qdh_supply_hset_to_stream(qdh, stream, qlist))
if (0 != qdh_process_qlist(qdh, stream, qlist))
return LQRHS_ERROR;
if (qdh->qdh_dec_sm_out)
{

View file

@ -21,6 +21,7 @@ struct qpack_dec_hdl
struct lsquic_conn *qdh_conn;
enum {
QDH_INITIALIZED = 1 << 0,
QDH_PUSH_PROMISE = 1 << 1,
} qdh_flags;
struct lsqpack_dec qdh_decoder;
struct lsquic_stream *qdh_enc_sm_in;

View file

@ -765,6 +765,8 @@ send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
assert(ctl->sc_n_in_flight_all);
packet_sz = packet_out_sent_sz(packet_out);
++ctl->sc_loss_count;
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
{
ctl->sc_flags |= SC_LOST_ACK_INIT << lsquic_packet_out_pns(packet_out);
@ -1610,6 +1612,25 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
send_ctl_maybe_zero_pad(ctl, packet_out, size ? size : 1200);
}
if (ctl->sc_flags & SC_QL_BITS)
{
packet_out->po_lflags |= POL_LOG_QL_BITS;
if (ctl->sc_loss_count)
{
--ctl->sc_loss_count;
packet_out->po_lflags |= POL_LOSS_BIT;
}
else
packet_out->po_lflags &= ~POL_LOSS_BIT;
if (packet_out->po_header_type == HETY_NOT_SET)
{
if (ctl->sc_square_count++ & 128)
packet_out->po_lflags |= POL_SQUARE_BIT;
else
packet_out->po_lflags &= ~POL_SQUARE_BIT;
}
}
return packet_out;
}
@ -1625,6 +1646,8 @@ lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *ctl,
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_delayed;
#endif
if (packet_out->po_lflags & POL_LOSS_BIT)
++ctl->sc_loss_count;
}

View file

@ -44,6 +44,7 @@ enum send_ctl_flags {
SC_1RTT_ACKED = 1 << 11,
SC_APP_LIMITED = 1 << 12,
SC_ECN = 1 << 13,
SC_QL_BITS = 1 << 14,
};
typedef struct lsquic_send_ctl {
@ -124,6 +125,8 @@ typedef struct lsquic_send_ctl {
unsigned sc_retry_count;
unsigned sc_rt_count; /* Count round trips */
lsquic_packno_t sc_cur_rt_end;
unsigned sc_loss_count; /* Used to set loss bit */
unsigned sc_square_count;/* Used to set square bit */
} lsquic_send_ctl_t;
void
@ -357,4 +360,8 @@ void
lsquic_send_ctl_maybe_app_limited (struct lsquic_send_ctl *,
const struct network_path *);
#define lsquic_send_ctl_do_ql_bits(ctl) do { \
(ctl)->sc_flags |= SC_QL_BITS; \
} while (0)
#endif

View file

@ -3726,8 +3726,9 @@ lsquic_stream_push_info (const lsquic_stream_t *stream,
}
int
lsquic_stream_uh_in (lsquic_stream_t *stream, struct uncompressed_headers *uh)
static int
stream_uh_in_gquic (struct lsquic_stream *stream,
struct uncompressed_headers *uh)
{
if ((stream->sm_bflags & SMBF_USE_HEADERS)
&& !(stream->stream_flags & STREAM_HAVE_UH))
@ -3764,6 +3765,68 @@ lsquic_stream_uh_in (lsquic_stream_t *stream, struct uncompressed_headers *uh)
}
static int
stream_uh_in_ietf (struct lsquic_stream *stream,
struct uncompressed_headers *uh)
{
int push_promise;
push_promise = lsquic_stream_header_is_pp(stream);
if (!(stream->stream_flags & STREAM_HAVE_UH) && !push_promise)
{
SM_HISTORY_APPEND(stream, SHE_HEADERS_IN);
LSQ_DEBUG("received uncompressed headers");
stream->stream_flags |= STREAM_HAVE_UH;
if (uh->uh_flags & UH_FIN)
{
/* IETF QUIC only sets UH_FIN for a pushed stream on the server to
* mark request as done:
*/
if (stream->sm_bflags & SMBF_IETF)
assert((stream->sm_bflags & SMBF_SERVER)
&& lsquic_stream_is_pushed(stream));
stream->stream_flags |= STREAM_FIN_RECVD|STREAM_HEAD_IN_FIN;
}
stream->uh = uh;
if (uh->uh_oth_stream_id == 0)
{
if (uh->uh_weight)
lsquic_stream_set_priority_internal(stream, uh->uh_weight);
}
else
LSQ_NOTICE("don't know how to depend on stream %"PRIu64,
uh->uh_oth_stream_id);
}
else
{
/* Trailer should never make here, as we discard it in qdh */
LSQ_DEBUG("discard %s header set",
push_promise ? "push promise" : "trailer");
if (uh->uh_hset)
stream->conn_pub->enpub->enp_hsi_if
->hsi_discard_header_set(uh->uh_hset);
free(uh);
}
return 0;
}
int
lsquic_stream_uh_in (lsquic_stream_t *stream, struct uncompressed_headers *uh)
{
if (stream->sm_bflags & SMBF_USE_HEADERS)
{
if (stream->sm_bflags & SMBF_IETF)
return stream_uh_in_ietf(stream, uh);
else
return stream_uh_in_gquic(stream, uh);
}
else
return -1;
}
unsigned
lsquic_stream_priority (const lsquic_stream_t *stream)
{
@ -3961,7 +4024,8 @@ lsquic_stream_set_stream_if (struct lsquic_stream *stream,
static int
update_type_hist_and_check (struct hq_filter *filter)
update_type_hist_and_check (const struct lsquic_stream *stream,
struct hq_filter *filter)
{
/* 3-bit codes: */
enum {
@ -3988,6 +4052,20 @@ update_type_hist_and_check (struct hq_filter *filter)
case HQFT_DATA:
code = CODE_DATA;
break;
case HQFT_PUSH_PROMISE:
case HQFT_DUPLICATE_PUSH:
/* [draft-ietf-quic-http-24], Section 7 */
if ((stream->id & SIT_MASK) == SIT_BIDI_CLIENT
&& !(stream->sm_bflags & SMBF_SERVER))
return 0;
else
return -1;
case HQFT_CANCEL_PUSH:
case HQFT_SETTINGS:
case HQFT_GOAWAY:
case HQFT_MAX_PUSH_ID:
/* [draft-ietf-quic-http-24], Section 7 */
return -1;
default:
/* Ignore unknown frames */
return 0;
@ -4021,6 +4099,21 @@ update_type_hist_and_check (struct hq_filter *filter)
}
int
lsquic_stream_header_is_pp (const struct lsquic_stream *stream)
{
return stream->sm_hq_filter.hqfi_type == HQFT_PUSH_PROMISE;
}
int
lsquic_stream_header_is_trailer (const struct lsquic_stream *stream)
{
return (stream->stream_flags & STREAM_HAVE_UH)
&& stream->sm_hq_filter.hqfi_type == HQFT_HEADERS;
}
static size_t
hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
{
@ -4037,11 +4130,11 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
switch (filter->hqfi_state)
{
case HQFI_STATE_FRAME_HEADER_BEGIN:
filter->hqfi_vint_state.vr2s_state = 0;
filter->hqfi_vint2_state.vr2s_state = 0;
filter->hqfi_state = HQFI_STATE_FRAME_HEADER_CONTINUE;
/* fall-through */
case HQFI_STATE_FRAME_HEADER_CONTINUE:
s = lsquic_varint_read_two(&p, end, &filter->hqfi_vint_state);
s = lsquic_varint_read_two(&p, end, &filter->hqfi_vint2_state);
if (s < 0)
break;
filter->hqfi_flags |= HQFI_FLAG_BEGIN;
@ -4049,7 +4142,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
LSQ_DEBUG("HQ frame type 0x%"PRIX64" at offset %"PRIu64", size %"PRIu64,
filter->hqfi_type, stream->read_offset + (unsigned) (p - buf),
filter->hqfi_left);
if (0 != update_type_hist_and_check(filter))
if (0 != update_type_hist_and_check(stream, filter))
{
lconn = stream->conn_pub->lconn;
filter->hqfi_flags |= HQFI_FLAG_ERROR;
@ -4060,38 +4153,24 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
stream->id);
goto end;
}
if (filter->hqfi_type == HQFT_HEADERS)
{
if (0 == (filter->hqfi_flags & HQFI_FLAG_GOT_HEADERS))
filter->hqfi_flags |= HQFI_FLAG_GOT_HEADERS;
else
{
filter->hqfi_type = (1ull << 62) - 1;
LSQ_DEBUG("Ignoring HEADERS frame");
}
}
if (filter->hqfi_left > 0)
{
if (filter->hqfi_type == HQFT_DATA)
goto end;
else if (filter->hqfi_type == HQFT_PUSH_PROMISE)
{
lconn = stream->conn_pub->lconn;
if (stream->sm_bflags & SMBF_SERVER)
{
lconn = stream->conn_pub->lconn;
lconn->cn_if->ci_abort_error(lconn, 1,
HEC_FRAME_UNEXPECTED, "Received PUSH_PROMISE frame "
"on stream %"PRIu64" (clients are not supposed to "
"send those)", stream->id);
else
/* Our client implementation does not support server
* push.
*/
lconn->cn_if->ci_abort_error(lconn, 1,
HEC_FRAME_UNEXPECTED,
"Received PUSH_PROMISE frame (not supported)"
"on stream %"PRIu64, stream->id);
goto end;
}
else
filter->hqfi_state = HQFI_STATE_PUSH_ID_BEGIN;
}
}
else
{
@ -4115,6 +4194,18 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
}
}
break;
case HQFI_STATE_PUSH_ID_BEGIN:
filter->hqfi_vint1_state.pos = 0;
filter->hqfi_state = HQFI_STATE_PUSH_ID_CONTINUE;
/* Fall-through */
case HQFI_STATE_PUSH_ID_CONTINUE:
prev = p;
s = lsquic_varint_read_nb(&p, end, &filter->hqfi_vint1_state);
filter->hqfi_left -= p - prev;
if (s == 0)
filter->hqfi_state = HQFI_STATE_READING_PAYLOAD;
/* A bit of a white lie here */
break;
case HQFI_STATE_READING_PAYLOAD:
if (filter->hqfi_type == HQFT_DATA)
goto end;
@ -4124,6 +4215,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
switch (filter->hqfi_type)
{
case HQFT_HEADERS:
case HQFT_PUSH_PROMISE:
prev = p;
if (filter->hqfi_flags & HQFI_FLAG_BEGIN)
{

View file

@ -101,12 +101,14 @@ struct stream_hq_frame
struct hq_filter
{
struct varint_read2_state hqfi_vint_state;
struct varint_read2_state hqfi_vint2_state;
/* No need to copy the values: use it directly */
#define hqfi_left hqfi_vint_state.vr2s_two
#define hqfi_type hqfi_vint_state.vr2s_one
#define hqfi_left hqfi_vint2_state.vr2s_two
#define hqfi_type hqfi_vint2_state.vr2s_one
struct varint_read_state hqfi_vint1_state;
#define hqfi_push_id hqfi_vint1_state.value
enum {
HQFI_FLAG_GOT_HEADERS = 1 << 0,
HQFI_FLAG_UNUSED_0 = 1 << 0,
HQFI_FLAG_ERROR = 1 << 1,
HQFI_FLAG_BEGIN = 1 << 2,
HQFI_FLAG_BLOCKED = 1 << 3,
@ -115,6 +117,8 @@ struct hq_filter
HQFI_STATE_FRAME_HEADER_BEGIN,
HQFI_STATE_FRAME_HEADER_CONTINUE,
HQFI_STATE_READING_PAYLOAD,
HQFI_STATE_PUSH_ID_BEGIN,
HQFI_STATE_PUSH_ID_CONTINUE,
} hqfi_state:8;
unsigned char hqfi_hist_idx;
#define MAX_HQFI_ENTRIES (sizeof(unsigned) * 8 / 3)
@ -263,7 +267,7 @@ struct lsquic_stream
struct hq_filter sm_hq_filter;
/* We can safely use sm_hq_filter */
#define sm_uni_type_state sm_hq_filter.hqfi_vint_state.vr2s_varint_state
#define sm_uni_type_state sm_hq_filter.hqfi_vint2_state.vr2s_varint_state
/** If @ref SMQF_WANT_FLUSH is set, flush until this offset. */
uint64_t sm_flush_to;
@ -590,4 +594,10 @@ lsquic_stream_push_promise (struct lsquic_stream *, struct push_promise *);
void
lsquic_stream_force_finish (struct lsquic_stream *);
int
lsquic_stream_header_is_pp (const struct lsquic_stream *);
int
lsquic_stream_header_is_trailer (const struct lsquic_stream *);
#endif

View file

@ -137,6 +137,9 @@ lsquic_tp_encode (const struct transport_params *params,
if (params->tp_disable_active_migration != TP_DEF_DISABLE_ACTIVE_MIGRATION)
need += 4 + 0;
if (params->tp_flags & TRAPA_QL_BITS)
need += 4 + 0;
if (need > bufsz || need > UINT16_MAX)
{
errno = ENOBUFS;
@ -256,6 +259,12 @@ lsquic_tp_encode (const struct transport_params *params,
return -1;
}
if (params->tp_flags & TRAPA_QL_BITS)
{
WRITE_UINT_TO_P(TPI_QL_BITS, 16);
WRITE_UINT_TO_P(0, 16);
}
#if LSQUIC_TEST_QUANTUM_READINESS
if (params->tp_flags & TRAPA_QUANTUM_READY)
{
@ -332,7 +341,7 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
set_of_ids |= 1 << param_id;
}
else
goto unknown;
goto gt32;
if (NUMERIC_TRANS_PARAMS & (1u << param_id))
{
switch (len)
@ -372,7 +381,7 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
}
else
{
switch (param_id)
gt32: switch (param_id)
{
case TPI_DISABLE_ACTIVE_MIGRATION:
EXPECT_LEN(0);
@ -450,8 +459,11 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
sizeof(params->tp_preferred_address.ipv6_addr)))
params->tp_flags |= TRAPA_PREFADDR_IPv6;
break;
case TPI_QL_BITS:
EXPECT_LEN(0);
params->tp_flags |= TRAPA_QL_BITS;
break;
}
unknown:
p += len;
}
}
@ -536,6 +548,13 @@ lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
return;
}
}
if (params->tp_flags & TRAPA_QL_BITS)
{
nw = snprintf(buf, end - buf, "; QL loss bits");
buf += nw;
if (buf >= end)
return;
}
#undef SEMICOLON
#undef WRITE_ONE_PARAM

View file

@ -57,6 +57,8 @@ enum trapa_flags
#define TPI_QUANTUM_READINESS 3127
TRAPA_QUANTUM_READY = 1 << 5, /* Include "Quantum Readiness" TP */
#endif
#define TPI_QL_BITS 0x1055 /* 1055 is 133t for "loss" */
TRAPA_QL_BITS = 1 << 6,
};
struct transport_params

View file

@ -1703,6 +1703,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
settings->es_cc_algo = atoi(val);
return 0;
}
else if (0 == strncmp(name, "ql_bits", 7))
{
settings->es_ql_bits = atoi(val);
return 0;
}
break;
case 8:
if (0 == strncmp(name, "max_cfcw", 8))