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 2019-11-20
- 2.6.6 - 2.6.6
- [BUGFIX] Using HTTP/3 to HTTP/1.x converter. - [BUGFIX] Using HTTP/3 to HTTP/1.x converter.

View file

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

View file

@ -243,6 +243,7 @@ struct enc_sess_iquic
ESI_CACHED_INFO = 1 << 9, ESI_CACHED_INFO = 1 << 9,
ESI_1RTT_ACKED = 1 << 10, ESI_1RTT_ACKED = 1 << 10,
ESI_WANT_TICKET = 1 << 11, ESI_WANT_TICKET = 1 << 11,
ESI_QL_BITS = 1 << 12,
} esi_flags; } esi_flags;
enum evp_aead_direction_t enum evp_aead_direction_t
esi_dir[2]; /* client, server */ esi_dir[2]; /* client, server */
@ -333,7 +334,10 @@ apply_hp (struct enc_sess_iquic *enc_sess,
hp->hp_gen_mask(enc_sess, hp, cliser, dst + packno_off + 4, mask); hp->hp_gen_mask(enc_sess, hp, cliser, dst + packno_off + 4, mask);
LSQ_DEBUG("apply header protection using mask %s", LSQ_DEBUG("apply header protection using mask %s",
HEXSTR(mask, 5, mask_str)); HEXSTR(mask, 5, mask_str));
dst[0] ^= (0xF | (((dst[0] & 0x80) == 0) << 4)) & mask[0]; 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) switch (packno_len)
{ {
case 4: case 4:
@ -391,7 +395,10 @@ strip_hp (struct enc_sess_iquic *enc_sess,
hp->hp_gen_mask(enc_sess, hp, cliser, iv, mask); hp->hp_gen_mask(enc_sess, hp, cliser, iv, mask);
LSQ_DEBUG("strip header protection using mask %s", LSQ_DEBUG("strip header protection using mask %s",
HEXSTR(mask, 5, mask_str)); HEXSTR(mask, 5, mask_str));
dst[0] ^= (0xF | (((dst[0] & 0x80) == 0) << 4)) & mask[0]; 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; packno = 0;
shift = 0; shift = 0;
*packno_len = 1 + (dst[0] & 3); *packno_len = 1 + (dst[0] & 3);
@ -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)); - !!(params.tp_flags & (TRAPA_PREFADDR_IPv4|TRAPA_PREFADDR_IPv6));
if (!settings->es_allow_migration) if (!settings->es_allow_migration)
params.tp_disable_active_migration = 1; params.tp_disable_active_migration = 1;
if (settings->es_ql_bits)
params.tp_flags |= TRAPA_QL_BITS;
len = lsquic_tp_encode(&params, buf, bufsz); len = lsquic_tp_encode(&params, buf, bufsz);
if (len >= 0) 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; return 0;
} }
@ -1978,7 +1994,15 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
goto err; 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"); LSQ_DEBUG("reserved bits are not set to zero");
dec_packin = DECPI_VIOLATION; dec_packin = DECPI_VIOLATION;

View file

@ -80,7 +80,7 @@
#include "lsquic_logger.h" #include "lsquic_logger.h"
#ifndef LSQUIC_DEBUG_NEXT_ADV_TICK #ifndef LSQUIC_DEBUG_NEXT_ADV_TICK
#define LSQUIC_DEBUG_NEXT_ADV_TICK 0 #define LSQUIC_DEBUG_NEXT_ADV_TICK 1
#endif #endif
#define MIN(a, b) ((a) < (b) ? (a) : (b)) #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_size = LSQUIC_DF_QPACK_ENC_MAX_SIZE;
settings->es_qpack_enc_max_blocked = LSQUIC_DF_QPACK_ENC_MAX_BLOCKED; settings->es_qpack_enc_max_blocked = LSQUIC_DF_QPACK_ENC_MAX_BLOCKED;
settings->es_allow_migration = LSQUIC_DF_ALLOW_MIGRATION; settings->es_allow_migration = LSQUIC_DF_ALLOW_MIGRATION;
settings->es_ql_bits = LSQUIC_DF_QL_BITS;
} }

View file

@ -66,13 +66,24 @@ lsquic_ev_log_packet_in (const lsquic_cid_t *cid,
(unsigned) (packet_in->pi_data_sz + GQUIC_PACKET_HASH_SZ)); (unsigned) (packet_in->pi_data_sz + GQUIC_PACKET_HASH_SZ));
break; break;
default: default:
LCID("packet in: %"PRIu64", type: %s, size: %u; ecn: %u, spin: %d; " if (packet_in->pi_flags & PI_LOG_QL_BITS)
"path: %hhu", LCID("packet in: %"PRIu64", type: %s, size: %u; ecn: %u, spin: %d; "
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type], "path: %hhu; Q: %d; L: %d",
(unsigned) (packet_in->pi_data_sz + IQUIC_TAG_LEN), packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type],
lsquic_packet_in_ecn(packet_in), (unsigned) (packet_in->pi_data_sz + IQUIC_TAG_LEN),
/* spin bit value is only valid for short packet headers */ lsquic_packet_in_ecn(packet_in),
lsquic_packet_in_spin_bit(packet_in), packet_in->pi_path_id); /* 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],
(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);
break; break;
} }
} }
@ -220,6 +231,27 @@ lsquic_ev_log_packet_sent (const lsquic_cid_t *cid,
*/ */
lsquic_frame_types_to_str(frames, sizeof(frames), lsquic_frame_types_to_str(frames, sizeof(frames),
packet_out->po_frame_types)); 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 else
LCID("sent packet %"PRIu64", type %s, crypto: %s, size %hu, frame " LCID("sent packet %"PRIu64", type %s, crypto: %s, size %hu, frame "
"types: %s, ecn: %u, spin: %d; kp: %u, path: %hhu, flags: %u", "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", LSQ_DEBUG("peer transport parameters: %s",
(lsquic_tp_to_str(params, buf, sizeof(buf)), buf)); (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) if (params->tp_init_max_streams_bidi > (1ull << 60)
|| params->tp_init_max_streams_uni > (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 #define PIBIT_BITS_SHIFT 12
PI_BITS_BIT_0 = (1 <<12), PI_BITS_BIT_0 = (1 <<12),
PI_BITS_BIT_1 = (1 <<13), 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 */ /* pi_token and pi_token_size are set in Initial and Retry packets */
unsigned short pi_token_size; /* Size of the token */ unsigned short pi_token_size; /* Size of the token */
unsigned char pi_token; /* Offset to token */ unsigned char pi_token; /* Offset to token */

View file

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

View file

@ -260,6 +260,8 @@ gen_short_pkt_header (const struct lsquic_conn *lconn,
buf[0] = 0x40 buf[0] = 0x40
| (lsquic_packet_out_spin_bit(packet_out) << 5) | (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) | (lsquic_packet_out_key_phase(packet_out) << 2)
| packno_bits | packno_bits
; ;

View file

@ -384,10 +384,12 @@ qdh_supply_hset_to_stream (struct qpack_dec_hdl *qdh,
struct uncompressed_headers *uh = NULL; struct uncompressed_headers *uh = NULL;
const struct lsqpack_header *header; const struct lsqpack_header *header;
enum lsquic_header_status st; enum lsquic_header_status st;
int push_promise;
unsigned i; unsigned i;
void *hset; 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) if (!hset)
{ {
LSQ_INFO("call to hsi_create_header_set failed"); 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 static enum lsqpack_read_header_status
qdh_header_read_results (struct qpack_dec_hdl *qdh, qdh_header_read_results (struct qpack_dec_hdl *qdh,
struct lsquic_stream *stream, enum lsqpack_read_header_status rhs, 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 (qlist)
{ {
if (0 != qdh_supply_hset_to_stream(qdh, stream, qlist)) if (0 != qdh_process_qlist(qdh, stream, qlist))
return LQRHS_ERROR; return LQRHS_ERROR;
if (qdh->qdh_dec_sm_out) if (qdh->qdh_dec_sm_out)
{ {

View file

@ -21,6 +21,7 @@ struct qpack_dec_hdl
struct lsquic_conn *qdh_conn; struct lsquic_conn *qdh_conn;
enum { enum {
QDH_INITIALIZED = 1 << 0, QDH_INITIALIZED = 1 << 0,
QDH_PUSH_PROMISE = 1 << 1,
} qdh_flags; } qdh_flags;
struct lsqpack_dec qdh_decoder; struct lsqpack_dec qdh_decoder;
struct lsquic_stream *qdh_enc_sm_in; 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); assert(ctl->sc_n_in_flight_all);
packet_sz = packet_out_sent_sz(packet_out); packet_sz = packet_out_sent_sz(packet_out);
++ctl->sc_loss_count;
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK)) if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
{ {
ctl->sc_flags |= SC_LOST_ACK_INIT << lsquic_packet_out_pns(packet_out); 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); 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; return packet_out;
} }
@ -1625,6 +1646,8 @@ lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *ctl,
#if LSQUIC_SEND_STATS #if LSQUIC_SEND_STATS
++ctl->sc_stats.n_delayed; ++ctl->sc_stats.n_delayed;
#endif #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_1RTT_ACKED = 1 << 11,
SC_APP_LIMITED = 1 << 12, SC_APP_LIMITED = 1 << 12,
SC_ECN = 1 << 13, SC_ECN = 1 << 13,
SC_QL_BITS = 1 << 14,
}; };
typedef struct lsquic_send_ctl { typedef struct lsquic_send_ctl {
@ -124,6 +125,8 @@ typedef struct lsquic_send_ctl {
unsigned sc_retry_count; unsigned sc_retry_count;
unsigned sc_rt_count; /* Count round trips */ unsigned sc_rt_count; /* Count round trips */
lsquic_packno_t sc_cur_rt_end; 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; } lsquic_send_ctl_t;
void void
@ -357,4 +360,8 @@ void
lsquic_send_ctl_maybe_app_limited (struct lsquic_send_ctl *, lsquic_send_ctl_maybe_app_limited (struct lsquic_send_ctl *,
const struct network_path *); const struct network_path *);
#define lsquic_send_ctl_do_ql_bits(ctl) do { \
(ctl)->sc_flags |= SC_QL_BITS; \
} while (0)
#endif #endif

View file

@ -3726,8 +3726,9 @@ lsquic_stream_push_info (const lsquic_stream_t *stream,
} }
int static int
lsquic_stream_uh_in (lsquic_stream_t *stream, struct uncompressed_headers *uh) stream_uh_in_gquic (struct lsquic_stream *stream,
struct uncompressed_headers *uh)
{ {
if ((stream->sm_bflags & SMBF_USE_HEADERS) if ((stream->sm_bflags & SMBF_USE_HEADERS)
&& !(stream->stream_flags & STREAM_HAVE_UH)) && !(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 unsigned
lsquic_stream_priority (const lsquic_stream_t *stream) lsquic_stream_priority (const lsquic_stream_t *stream)
{ {
@ -3961,7 +4024,8 @@ lsquic_stream_set_stream_if (struct lsquic_stream *stream,
static int 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: */ /* 3-bit codes: */
enum { enum {
@ -3988,6 +4052,20 @@ update_type_hist_and_check (struct hq_filter *filter)
case HQFT_DATA: case HQFT_DATA:
code = CODE_DATA; code = CODE_DATA;
break; 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: default:
/* Ignore unknown frames */ /* Ignore unknown frames */
return 0; 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 static size_t
hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin) 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) switch (filter->hqfi_state)
{ {
case HQFI_STATE_FRAME_HEADER_BEGIN: 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; filter->hqfi_state = HQFI_STATE_FRAME_HEADER_CONTINUE;
/* fall-through */ /* fall-through */
case HQFI_STATE_FRAME_HEADER_CONTINUE: 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) if (s < 0)
break; break;
filter->hqfi_flags |= HQFI_FLAG_BEGIN; 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, LSQ_DEBUG("HQ frame type 0x%"PRIX64" at offset %"PRIu64", size %"PRIu64,
filter->hqfi_type, stream->read_offset + (unsigned) (p - buf), filter->hqfi_type, stream->read_offset + (unsigned) (p - buf),
filter->hqfi_left); 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; lconn = stream->conn_pub->lconn;
filter->hqfi_flags |= HQFI_FLAG_ERROR; filter->hqfi_flags |= HQFI_FLAG_ERROR;
@ -4060,37 +4153,23 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
stream->id); stream->id);
goto end; 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_left > 0)
{ {
if (filter->hqfi_type == HQFT_DATA) if (filter->hqfi_type == HQFT_DATA)
goto end; goto end;
else if (filter->hqfi_type == HQFT_PUSH_PROMISE) else if (filter->hqfi_type == HQFT_PUSH_PROMISE)
{ {
lconn = stream->conn_pub->lconn;
if (stream->sm_bflags & SMBF_SERVER) if (stream->sm_bflags & SMBF_SERVER)
{
lconn = stream->conn_pub->lconn;
lconn->cn_if->ci_abort_error(lconn, 1, lconn->cn_if->ci_abort_error(lconn, 1,
HEC_FRAME_UNEXPECTED, "Received PUSH_PROMISE frame " HEC_FRAME_UNEXPECTED, "Received PUSH_PROMISE frame "
"on stream %"PRIu64" (clients are not supposed to " "on stream %"PRIu64" (clients are not supposed to "
"send those)", stream->id); "send those)", stream->id);
goto end;
}
else else
/* Our client implementation does not support server filter->hqfi_state = HQFI_STATE_PUSH_ID_BEGIN;
* 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 else
@ -4115,6 +4194,18 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
} }
} }
break; 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: case HQFI_STATE_READING_PAYLOAD:
if (filter->hqfi_type == HQFT_DATA) if (filter->hqfi_type == HQFT_DATA)
goto end; goto end;
@ -4124,6 +4215,7 @@ hq_read (void *ctx, const unsigned char *buf, size_t sz, int fin)
switch (filter->hqfi_type) switch (filter->hqfi_type)
{ {
case HQFT_HEADERS: case HQFT_HEADERS:
case HQFT_PUSH_PROMISE:
prev = p; prev = p;
if (filter->hqfi_flags & HQFI_FLAG_BEGIN) if (filter->hqfi_flags & HQFI_FLAG_BEGIN)
{ {

View file

@ -101,12 +101,14 @@ struct stream_hq_frame
struct hq_filter 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 */ /* No need to copy the values: use it directly */
#define hqfi_left hqfi_vint_state.vr2s_two #define hqfi_left hqfi_vint2_state.vr2s_two
#define hqfi_type hqfi_vint_state.vr2s_one #define hqfi_type hqfi_vint2_state.vr2s_one
struct varint_read_state hqfi_vint1_state;
#define hqfi_push_id hqfi_vint1_state.value
enum { enum {
HQFI_FLAG_GOT_HEADERS = 1 << 0, HQFI_FLAG_UNUSED_0 = 1 << 0,
HQFI_FLAG_ERROR = 1 << 1, HQFI_FLAG_ERROR = 1 << 1,
HQFI_FLAG_BEGIN = 1 << 2, HQFI_FLAG_BEGIN = 1 << 2,
HQFI_FLAG_BLOCKED = 1 << 3, HQFI_FLAG_BLOCKED = 1 << 3,
@ -115,6 +117,8 @@ struct hq_filter
HQFI_STATE_FRAME_HEADER_BEGIN, HQFI_STATE_FRAME_HEADER_BEGIN,
HQFI_STATE_FRAME_HEADER_CONTINUE, HQFI_STATE_FRAME_HEADER_CONTINUE,
HQFI_STATE_READING_PAYLOAD, HQFI_STATE_READING_PAYLOAD,
HQFI_STATE_PUSH_ID_BEGIN,
HQFI_STATE_PUSH_ID_CONTINUE,
} hqfi_state:8; } hqfi_state:8;
unsigned char hqfi_hist_idx; unsigned char hqfi_hist_idx;
#define MAX_HQFI_ENTRIES (sizeof(unsigned) * 8 / 3) #define MAX_HQFI_ENTRIES (sizeof(unsigned) * 8 / 3)
@ -263,7 +267,7 @@ struct lsquic_stream
struct hq_filter sm_hq_filter; struct hq_filter sm_hq_filter;
/* We can safely use 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. */ /** If @ref SMQF_WANT_FLUSH is set, flush until this offset. */
uint64_t sm_flush_to; uint64_t sm_flush_to;
@ -590,4 +594,10 @@ lsquic_stream_push_promise (struct lsquic_stream *, struct push_promise *);
void void
lsquic_stream_force_finish (struct lsquic_stream *); 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 #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) if (params->tp_disable_active_migration != TP_DEF_DISABLE_ACTIVE_MIGRATION)
need += 4 + 0; need += 4 + 0;
if (params->tp_flags & TRAPA_QL_BITS)
need += 4 + 0;
if (need > bufsz || need > UINT16_MAX) if (need > bufsz || need > UINT16_MAX)
{ {
errno = ENOBUFS; errno = ENOBUFS;
@ -256,6 +259,12 @@ lsquic_tp_encode (const struct transport_params *params,
return -1; 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 LSQUIC_TEST_QUANTUM_READINESS
if (params->tp_flags & TRAPA_QUANTUM_READY) 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; set_of_ids |= 1 << param_id;
} }
else else
goto unknown; goto gt32;
if (NUMERIC_TRANS_PARAMS & (1u << param_id)) if (NUMERIC_TRANS_PARAMS & (1u << param_id))
{ {
switch (len) switch (len)
@ -372,7 +381,7 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
} }
else else
{ {
switch (param_id) gt32: switch (param_id)
{ {
case TPI_DISABLE_ACTIVE_MIGRATION: case TPI_DISABLE_ACTIVE_MIGRATION:
EXPECT_LEN(0); 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))) sizeof(params->tp_preferred_address.ipv6_addr)))
params->tp_flags |= TRAPA_PREFADDR_IPv6; params->tp_flags |= TRAPA_PREFADDR_IPv6;
break; break;
case TPI_QL_BITS:
EXPECT_LEN(0);
params->tp_flags |= TRAPA_QL_BITS;
break;
} }
unknown:
p += len; p += len;
} }
} }
@ -536,6 +548,13 @@ lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
return; 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 SEMICOLON
#undef WRITE_ONE_PARAM #undef WRITE_ONE_PARAM

View file

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

View file

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