Release 2.20.0

- [FEATURE] QUIC and HTTP/3 Internet Draft 30 support.
- [FEATURE] Unreliable Datagram Extension support.
- [FEATURE] Adaptive congestion controller.
- [BUGFIX] Do not send MAX_STREAM_DATA frames on crypto streams.
- [BUGFIX] Fail with CRYPTO_BUFFER_EXCEEDED when too much CRYPTO
  data comes in.
- [BUFFIX] Spin bit is now strictly per path; value is reset on
  DCID change.
- [BUGFIX] Check that max value of max_streams_uni and
  max_streams_bidi TPs is 2^60.
- [BUGFIX] Close IETF mini conn immediately if crypto session
  cannot be initialized.
- Deprecate ID-28 (no browser uses it): it's no longer in the
  default versions list.
- New programs duck_server and duck_client that implement the
  experimental siduck-00 protocol.  They quack!
- IETF crypto streams: don't limit ourselves from sending.
- Command-line programs: turn off QL loss bits if -G is used, as
  Wireshark cannot decrypt QUIC packets when this extension is used.
- Turn all h3 framing unit tests back on.
- Fix malo initialization when compiled in no-pool mode.
This commit is contained in:
Dmitri Tikhonov 2020-09-15 16:42:13 -04:00
parent c3c69ba3bb
commit b1a7c3f944
53 changed files with 1745 additions and 161 deletions

View file

@ -1,6 +1,7 @@
# Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE.
SET(lsquic_STAT_SRCS
ls-qpack/lsqpack.c
lsquic_adaptive_cc.c
lsquic_alarmset.c
lsquic_arr.c
lsquic_attq.c

View file

@ -0,0 +1,212 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/* lsquic_adaptive_cc.c -- adaptive congestion controller */
#include <inttypes.h>
#include <math.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#ifdef WIN32
#include <vc_compat.h>
#endif
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_hash.h"
#include "lsquic_util.h"
#include "lsquic_cong_ctl.h"
#include "lsquic_sfcw.h"
#include "lsquic_conn_flow.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_stream.h"
#include "lsquic_rtt.h"
#include "lsquic_conn_public.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_out.h"
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_cubic.h"
#include "lsquic_adaptive_cc.h"
#define LSQUIC_LOGGER_MODULE LSQLM_ADAPTIVE_CC
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(acc->acc_cubic.cu_conn)
#include "lsquic_logger.h"
#define CALL_BOTH(method, ...) do { \
lsquic_cong_bbr_if.method(&acc->acc_bbr, __VA_ARGS__); \
lsquic_cong_cubic_if.method(&acc->acc_cubic, __VA_ARGS__); \
} while (0)
#define CALL_BOTH_MAYBE(method, ...) do { \
if (lsquic_cong_bbr_if.method) \
lsquic_cong_bbr_if.method(&acc->acc_bbr, __VA_ARGS__); \
if (lsquic_cong_cubic_if.method) \
lsquic_cong_cubic_if.method(&acc->acc_cubic, __VA_ARGS__); \
} while (0)
#define CALL_BOTH0(method) do { \
lsquic_cong_bbr_if.method(&acc->acc_bbr); \
lsquic_cong_cubic_if.method(&acc->acc_cubic); \
} while (0)
static void
adaptive_cc_init (void *cong_ctl, const struct lsquic_conn_public *conn_pub,
enum quic_ft_bit retx_frames)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH(cci_init, conn_pub, retx_frames);
LSQ_DEBUG("initialized");
}
static void
adaptive_cc_reinit (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH0(cci_reinit);
}
static void
adaptive_cc_ack (void *cong_ctl, struct lsquic_packet_out *packet_out,
unsigned packet_sz, lsquic_time_t now, int app_limited)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH(cci_ack, packet_out, packet_sz, now, app_limited);
}
static void
adaptive_cc_loss (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH0(cci_loss);
}
static void
adaptive_cc_begin_ack (void *cong_ctl, lsquic_time_t ack_time,
uint64_t in_flight)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH_MAYBE(cci_begin_ack, ack_time, in_flight);
}
static void
adaptive_cc_end_ack (void *cong_ctl, uint64_t in_flight)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH_MAYBE(cci_end_ack, in_flight);
}
static void
adaptive_cc_sent (void *cong_ctl, struct lsquic_packet_out *packet_out,
uint64_t in_flight, int app_limited)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH_MAYBE(cci_sent, packet_out, in_flight, app_limited);
}
static void
adaptive_cc_lost (void *cong_ctl, struct lsquic_packet_out *packet_out,
unsigned packet_sz)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH_MAYBE(cci_lost, packet_out, packet_sz);
}
static void
adaptive_cc_timeout (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH0(cci_timeout);
}
static void
adaptive_cc_was_quiet (void *cong_ctl, lsquic_time_t now, uint64_t in_flight)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH(cci_was_quiet, now, in_flight);
}
static uint64_t
adaptive_cc_get_cwnd (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
uint64_t rv[2];
rv[0] = lsquic_cong_cubic_if.cci_get_cwnd(&acc->acc_cubic);
rv[1] = lsquic_cong_bbr_if.cci_get_cwnd(&acc->acc_bbr);
if (acc->acc_flags & ACC_CUBIC)
return rv[0];
else
return rv[1];
}
static uint64_t
adaptive_cc_pacing_rate (void *cong_ctl, int in_recovery)
{
struct adaptive_cc *const acc = cong_ctl;
uint64_t rv[2];
rv[0] = lsquic_cong_cubic_if.cci_pacing_rate(&acc->acc_cubic, in_recovery);
rv[1] = lsquic_cong_bbr_if.cci_pacing_rate(&acc->acc_bbr, in_recovery);
if (acc->acc_flags & ACC_CUBIC)
return rv[0];
else
return rv[1];
}
static void
adaptive_cc_cleanup (void *cong_ctl)
{
struct adaptive_cc *const acc = cong_ctl;
CALL_BOTH0(cci_cleanup);
LSQ_DEBUG("cleanup");
}
const struct cong_ctl_if lsquic_cong_adaptive_if =
{
.cci_ack = adaptive_cc_ack,
.cci_begin_ack = adaptive_cc_begin_ack,
.cci_end_ack = adaptive_cc_end_ack,
.cci_cleanup = adaptive_cc_cleanup,
.cci_get_cwnd = adaptive_cc_get_cwnd,
.cci_init = adaptive_cc_init,
.cci_pacing_rate = adaptive_cc_pacing_rate,
.cci_loss = adaptive_cc_loss,
.cci_lost = adaptive_cc_lost,
.cci_reinit = adaptive_cc_reinit,
.cci_timeout = adaptive_cc_timeout,
.cci_sent = adaptive_cc_sent,
.cci_was_quiet = adaptive_cc_was_quiet,
};

View file

@ -0,0 +1,24 @@
/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_adaptive_cc.h -- Adaptive congestion controller
*
* The controller begins using BBRv1, but keeps Cubic state as well.
* When RTT is known, we pick either Cubic (small RTT) or BBRv1 (large
* RTT).
*/
#ifndef LSQUIC_ADAPTIVE_CC_H
#define LSQUIC_ADAPTIVE_CC_H 1
struct adaptive_cc
{
struct lsquic_cubic acc_cubic;
struct lsquic_bbr acc_bbr;
enum {
ACC_CUBIC, /* If set, use Cubic; otherwise, use BBR */
} acc_flags;
};
extern const struct cong_ctl_if lsquic_cong_adaptive_if;
#endif

View file

@ -58,7 +58,10 @@ lsquic_bw_sampler_cleanup (struct bw_sampler *sampler)
if (sampler->bws_conn)
LSQ_DEBUG("cleanup");
if (sampler->bws_malo)
{
lsquic_malo_destroy(sampler->bws_malo);
sampler->bws_malo = NULL;
}
}

View file

@ -267,3 +267,33 @@ lsquic_conn_log_cid (const struct lsquic_conn *lconn)
return lconn->cn_if->ci_get_log_cid(lconn);
return CN_SCID(lconn);
}
int
lsquic_conn_want_datagram_write (struct lsquic_conn *lconn, int is_want)
{
if (lconn->cn_if && lconn->cn_if->ci_want_datagram_write)
return lconn->cn_if->ci_want_datagram_write(lconn, is_want);
else
return -1;
}
int
lsquic_conn_set_min_datagram_size (struct lsquic_conn *lconn, size_t sz)
{
if (lconn->cn_if && lconn->cn_if->ci_set_min_datagram_size)
return lconn->cn_if->ci_set_min_datagram_size(lconn, sz);
else
return -1;
}
size_t
lsquic_conn_get_min_datagram_size (struct lsquic_conn *lconn)
{
if (lconn->cn_if && lconn->cn_if->ci_get_min_datagram_size)
return lconn->cn_if->ci_get_min_datagram_size(lconn);
else
return 0;
}

View file

@ -91,6 +91,12 @@ struct ack_state
uint32_t arr[6];
};
struct to_coal
{
const struct lsquic_packet_out *prev_packet;
size_t prev_sz_sum;
};
struct conn_iface
{
enum tick_st
@ -108,7 +114,7 @@ struct conn_iface
* for by the congestion controller.
*/
struct lsquic_packet_out *
(*ci_next_packet_to_send) (struct lsquic_conn *, size_t);
(*ci_next_packet_to_send) (struct lsquic_conn *, const struct to_coal *);
void
(*ci_packet_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
@ -270,6 +276,18 @@ struct conn_iface
void
(*ci_ack_rollback) (struct lsquic_conn *, struct ack_state *);
/* Optional method. */
int
(*ci_want_datagram_write) (struct lsquic_conn *, int);
/* Optional method */
int
(*ci_set_min_datagram_size) (struct lsquic_conn *, size_t);
/* Optional method */
size_t
(*ci_get_min_datagram_size) (struct lsquic_conn *);
};
#define LSCONN_CCE_BITS 3

View file

@ -342,6 +342,7 @@ extern const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1;
ver == LSQVER_ID27 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_ID28 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_ID29 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_ID30 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_050 ? &lsquic_enc_session_common_gquic_2 : \
&lsquic_enc_session_common_gquic_1 )

View file

@ -74,7 +74,8 @@ static const struct alpn_map {
{ LSQVER_ID27, (unsigned char *) "\x05h3-27", },
{ LSQVER_ID28, (unsigned char *) "\x05h3-28", },
{ LSQVER_ID29, (unsigned char *) "\x05h3-29", },
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-29", },
{ LSQVER_ID30, (unsigned char *) "\x05h3-30", },
{ LSQVER_VERNEG, (unsigned char *) "\x05h3-30", },
};
struct enc_sess_iquic;
@ -571,7 +572,6 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
#endif
}
#if LSQUIC_TEST_QUANTUM_READINESS
else
{
const char *s = getenv("LSQUIC_TEST_QUANTUM_READINESS");
if (s && atoi(s))
@ -627,6 +627,16 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
params.tp_numerics[TPI_TIMESTAMPS] = TS_GENERATE_THEM;
params.tp_set |= 1 << TPI_TIMESTAMPS;
}
if (settings->es_datagrams)
{
if (params.tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE))
params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE]
= params.tp_max_udp_payload_size;
else
params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE]
= TP_DEF_MAX_UDP_PAYLOAD_SIZE;
params.tp_set |= 1 << TPI_MAX_DATAGRAM_FRAME_SIZE;
}
len = (version == LSQVER_ID27 ? lsquic_tp_encode_27 : lsquic_tp_encode)(
&params, enc_sess->esi_flags & ESI_SERVER, buf, bufsz);
@ -1220,7 +1230,11 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
SSL_CTX *ssl_ctx = NULL;
union {
char errbuf[ERR_ERROR_STRING_BUF_LEN];
unsigned char trans_params[sizeof(struct transport_params)];
unsigned char trans_params[sizeof(struct transport_params)
#if LSQUIC_TEST_QUANTUM_READINESS
+ 4 + lsquic_tp_get_quantum_sz()
#endif
];
} u;
if (enc_sess->esi_enpub->enp_alpn)
@ -1396,7 +1410,7 @@ init_client (struct enc_sess_iquic *const enc_sess)
#define hexbuf errbuf /* This is a dual-purpose buffer */
unsigned char trans_params[0x80
#if LSQUIC_TEST_QUANTUM_READINESS
+ 4 + QUANTUM_READY_SZ
+ 4 + lsquic_tp_get_quantum_sz()
#endif
];
@ -2018,6 +2032,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
packet_out->po_sent_sz = dst_sz;
packet_out->po_flags &= ~PO_IPv6;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ|(ipv6 << POIPv6_SHIFT);
packet_out->po_dcid_len = packet_out->po_path->np_dcid.len;
lsquic_packet_out_set_enc_level(packet_out, enc_level);
lsquic_packet_out_set_kp(packet_out, enc_sess->esi_key_phase);

View file

@ -58,6 +58,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_set.h"
#include "lsquic_conn_flow.h"
#include "lsquic_sfcw.h"
@ -359,6 +360,9 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
settings->es_grease_quic_bit = LSQUIC_DF_GREASE_QUIC_BIT;
settings->es_mtu_probe_timer = LSQUIC_DF_MTU_PROBE_TIMER;
settings->es_dplpmtud = LSQUIC_DF_DPLPMTUD;
settings->es_cc_algo = LSQUIC_DF_CC_ALGO;
settings->es_cc_rtt_thresh = LSQUIC_DF_CC_RTT_THRESH;
settings->es_optimistic_nat = LSQUIC_DF_OPTIMISTIC_NAT;
}
@ -418,7 +422,7 @@ lsquic_engine_check_settings (const struct lsquic_engine_settings *settings,
return -1;
}
if (settings->es_cc_algo > 2)
if (settings->es_cc_algo > 3)
{
if (err_buf)
snprintf(err_buf, err_buf_sz, "Invalid congestion control "
@ -2472,8 +2476,11 @@ send_packets_out (struct lsquic_engine *engine,
#endif
&& iov < batch->iov + sizeof(batch->iov) / sizeof(batch->iov[0]))
{
const size_t size = iov_size(packet_iov, iov);
packet_out = conn->cn_if->ci_next_packet_to_send(conn, size);
const struct to_coal to_coal = {
.prev_packet = packet_out,
.prev_sz_sum = iov_size(packet_iov, iov),
};
packet_out = conn->cn_if->ci_next_packet_to_send(conn, &to_coal);
if (packet_out)
goto next_coa;
}
@ -2829,7 +2836,7 @@ lsquic_engine_packet_in (lsquic_engine_t *engine,
break;
}
/* [draft-ietf-quic-transport-27] Section 12.2:
/* [draft-ietf-quic-transport-30] Section 12.2:
* " Receivers SHOULD ignore any subsequent packets with a different
* " Destination Connection ID than the first packet in the datagram.
*/

View file

@ -42,6 +42,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_set.h"
#include "lsquic_malo.h"
#include "lsquic_chsk_stream.h"
@ -3670,10 +3671,11 @@ full_conn_ci_packet_in (lsquic_conn_t *lconn, lsquic_packet_in_t *packet_in)
static lsquic_packet_out_t *
full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *unused)
{
struct full_conn *conn = (struct full_conn *) lconn;
return lsquic_send_ctl_next_packet_to_send(&conn->fc_send_ctl, 0);
return lsquic_send_ctl_next_packet_to_send(&conn->fc_send_ctl, NULL);
}

View file

@ -42,6 +42,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_send_ctl.h"
#include "lsquic_alarmset.h"
#include "lsquic_ver_neg.h"
@ -132,9 +133,10 @@ enum ifull_conn_flags
IFC_IGNORE_HSK = 1 << 25,
IFC_PROC_CRYPTO = 1 << 26,
IFC_MIGRA = 1 << 27,
IFC_SPIN = 1 << 28, /* Spin bits are enabled */
IFC_UNUSED28 = 1 << 28, /* Unused */
IFC_DELAYED_ACKS = 1 << 29, /* Delayed ACKs are enabled */
IFC_TIMESTAMPS = 1 << 30, /* Timestamps are enabled */
IFC_DATAGRAMS = 1u<< 31, /* Datagrams are enabled */
};
@ -146,6 +148,7 @@ enum more_flags
MF_IGNORE_MISSING = 1 << 3,
MF_CONN_CLOSE_PACK = 1 << 4, /* CONNECTION_CLOSE has been packetized */
MF_SEND_WRONG_COUNTS= 1 << 5, /* Send wrong ECN counts to peer */
MF_WANT_DATAGRAM_WRITE = 1 << 6,
};
@ -305,6 +308,7 @@ struct conn_path
struct network_path cop_path;
uint64_t cop_path_chals[8]; /* Arbitrary number */
uint64_t cop_inc_chal; /* Incoming challenge */
lsquic_packno_t cop_max_packno;
enum {
/* Initialized covers cop_path.np_pack_size and cop_path.np_dcid */
COP_INITIALIZED = 1 << 0,
@ -316,9 +320,12 @@ struct conn_path
* original path.
*/
COP_GOT_NONPROB = 1 << 2,
/* Spin bit is enabled on this path. */
COP_SPIN_BIT = 1 << 3,
} cop_flags;
unsigned char cop_n_chals;
unsigned char cop_cce_idx;
unsigned char cop_spin_bit;
struct dplpmtud_state cop_dplpmtud;
};
@ -336,6 +343,8 @@ struct ietf_full_conn
struct lsquic_conn ifc_conn;
struct conn_cid_elem ifc_cces[MAX_SCID];
struct lsquic_rechist ifc_rechist[N_PNS];
/* App PNS only, used to calculate was_missing: */
lsquic_packno_t ifc_max_ackable_packno_in;
struct lsquic_send_ctl ifc_send_ctl;
struct lsquic_stream *ifc_stream_hcsi; /* HTTP Control Stream Incoming */
struct lsquic_stream *ifc_stream_hcso; /* HTTP Control Stream Outgoing */
@ -359,7 +368,6 @@ struct ietf_full_conn
struct conn_err ifc_error;
unsigned ifc_n_delayed_streams;
unsigned ifc_n_cons_unretx;
int ifc_spin_bit;
const struct lsquic_stream_if
*ifc_stream_if;
void *ifc_stream_ctx;
@ -448,6 +456,8 @@ struct ietf_full_conn
lsquic_time_t ifc_idle_to;
lsquic_time_t ifc_ping_period;
uint64_t ifc_last_max_data_off_sent;
unsigned short ifc_min_dg_sz,
ifc_max_dg_sz;
struct inc_ack_stats ifc_ias;
struct ack_info ifc_ack;
};
@ -459,6 +469,9 @@ struct ietf_full_conn
#define DCES_END(conn_) ((conn_)->ifc_dces + (sizeof((conn_)->ifc_dces) \
/ sizeof((conn_)->ifc_dces[0])))
#define NPATH2CPATH(npath_) ((struct conn_path *) \
((char *) (npath_) - offsetof(struct conn_path, cop_path)))
static const struct ver_neg server_ver_neg;
static const struct conn_iface *ietf_full_conn_iface_ptr;
@ -1098,20 +1111,16 @@ ietf_full_conn_add_scid (struct ietf_full_conn *conn,
* " connection IDs.
*/
static void
maybe_enable_spin (struct ietf_full_conn *conn)
maybe_enable_spin (struct ietf_full_conn *conn, struct conn_path *cpath)
{
uint8_t nyb;
if (!conn->ifc_settings->es_spin)
if (conn->ifc_settings->es_spin
&& lsquic_crand_get_nybble(conn->ifc_enpub->enp_crand))
{
conn->ifc_flags &= ~IFC_SPIN;
LSQ_DEBUG("spin bit disabled via settings");
}
else if (lsquic_crand_get_nybble(conn->ifc_enpub->enp_crand))
{
conn->ifc_flags |= IFC_SPIN;
conn->ifc_spin_bit = 0;
LSQ_DEBUG("spin bit enabled");
cpath->cop_flags |= COP_SPIN_BIT;
cpath->cop_spin_bit = 0;
LSQ_DEBUG("spin bit enabled on path %hhu", cpath->cop_path.np_path_id);
}
else
{
@ -1120,11 +1129,13 @@ maybe_enable_spin (struct ietf_full_conn *conn)
* " independently for each connection ID.
* (ibid.)
*/
conn->ifc_flags &= ~IFC_SPIN;
cpath->cop_flags &= ~COP_SPIN_BIT;
nyb = lsquic_crand_get_nybble(conn->ifc_enpub->enp_crand);
conn->ifc_spin_bit = nyb & 1;
LSQ_DEBUG("spin bit randomly disabled; random spin bit value is %d",
conn->ifc_spin_bit);
cpath->cop_spin_bit = nyb & 1;
LSQ_DEBUG("spin bit disabled %s on path %hhu; random spin bit "
"value is %hhu",
!conn->ifc_settings->es_spin ? "via settings" : "randomly",
cpath->cop_path.np_path_id, cpath->cop_spin_bit);
}
}
@ -1186,6 +1197,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
conn->ifc_max_ack_packno[PNS_INIT] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ack_packno[PNS_HSK] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ack_packno[PNS_APP] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ackable_packno_in = 0;
conn->ifc_paths[0].cop_path.np_path_id = 0;
conn->ifc_paths[1].cop_path.np_path_id = 1;
conn->ifc_paths[2].cop_path.np_path_id = 2;
@ -1194,7 +1206,6 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
conn->ifc_max_req_id = VINT_MAX_VALUE + 1;
conn->ifc_ping_unretx_thresh = 20;
conn->ifc_max_retx_since_last_ack = MAX_RETR_PACKETS_SINCE_LAST_ACK;
maybe_enable_spin(conn);
if (conn->ifc_settings->es_noprogress_timeout)
conn->ifc_mflags |= MF_NOPROG_TIMEOUT;
return 0;
@ -1403,6 +1414,7 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
conn->ifc_paths[0].cop_path = imc->imc_path;
conn->ifc_paths[0].cop_flags = COP_VALIDATED|COP_INITIALIZED;
conn->ifc_used_paths = 1 << 0;
maybe_enable_spin(conn, &conn->ifc_paths[0]);
if (imc->imc_flags & IMC_ADDR_VALIDATED)
lsquic_send_ctl_path_validated(&conn->ifc_send_ctl);
else
@ -2720,6 +2732,28 @@ ietf_full_conn_ci_write_ack (struct lsquic_conn *lconn,
}
static int
ietf_full_conn_ci_want_datagram_write (struct lsquic_conn *lconn, int is_want)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
int old;
if (conn->ifc_flags & IFC_DATAGRAMS)
{
old = !!(conn->ifc_mflags & MF_WANT_DATAGRAM_WRITE);
if (is_want)
conn->ifc_mflags |= MF_WANT_DATAGRAM_WRITE;
else
conn->ifc_mflags &= ~MF_WANT_DATAGRAM_WRITE;
LSQ_DEBUG("turn %s \"want datagram write\" flag",
is_want ? "on" : "off");
return old;
}
else
return -1;
}
static void
ietf_full_conn_ci_client_call_on_new (struct lsquic_conn *lconn)
{
@ -2840,6 +2874,8 @@ ietf_full_conn_ci_retire_cid (struct lsquic_conn *lconn)
* Switch DCID.
*/
*CUR_DCID(conn) = (*dces[0])->de_cid;
if (CUR_CPATH(conn)->cop_flags & COP_SPIN_BIT)
CUR_CPATH(conn)->cop_spin_bit = 0;
LSQ_INFOC("switched DCID to %"CID_FMT, CID_BITS(CUR_DCID(conn)));
/*
* Mark old DCID for retirement.
@ -3348,6 +3384,15 @@ handshake_ok (struct lsquic_conn *lconn)
LSQ_DEBUG("timestamps enabled: will send TIMESTAMP frames");
conn->ifc_flags |= IFC_TIMESTAMPS;
}
if (conn->ifc_settings->es_datagrams
&& (params->tp_set & (1 << TPI_MAX_DATAGRAM_FRAME_SIZE)))
{
LSQ_DEBUG("datagrams enabled");
conn->ifc_flags |= IFC_DATAGRAMS;
conn->ifc_max_dg_sz =
params->tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE] > USHRT_MAX
? USHRT_MAX : params->tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE];
}
conn->ifc_max_peer_ack_usec = params->tp_max_ack_delay * 1000;
@ -3766,6 +3811,11 @@ ietf_full_conn_ci_is_tickable (struct lsquic_conn *lconn)
LSQ_DEBUG("tickable: send DATA_BLOCKED frame");
goto check_can_send;
}
if (conn->ifc_mflags & MF_WANT_DATAGRAM_WRITE)
{
LSQ_DEBUG("tickable: want to write DATAGRAM frame");
goto check_can_send;
}
if (conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE ?
lsquic_send_ctl_has_buffered(&conn->ifc_send_ctl) :
lsquic_send_ctl_has_buffered_high(&conn->ifc_send_ctl))
@ -4375,26 +4425,32 @@ generate_path_resp_3 (struct ietf_full_conn *conn, lsquic_time_t now)
static struct lsquic_packet_out *
ietf_full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
ietf_full_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
struct lsquic_packet_out *packet_out;
const struct conn_path *cpath;
packet_out = lsquic_send_ctl_next_packet_to_send(&conn->ifc_send_ctl, size);
packet_out = lsquic_send_ctl_next_packet_to_send(&conn->ifc_send_ctl,
to_coal);
if (packet_out)
lsquic_packet_out_set_spin_bit(packet_out, conn->ifc_spin_bit);
{
cpath = NPATH2CPATH(packet_out->po_path);
lsquic_packet_out_set_spin_bit(packet_out, cpath->cop_spin_bit);
}
return packet_out;
}
static struct lsquic_packet_out *
ietf_full_conn_ci_next_packet_to_send_pre_hsk (struct lsquic_conn *lconn,
size_t size)
const struct to_coal *to_coal)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
struct lsquic_packet_out *packet_out;
packet_out = ietf_full_conn_ci_next_packet_to_send(lconn, size);
packet_out = ietf_full_conn_ci_next_packet_to_send(lconn, to_coal);
if (packet_out)
++conn->ifc_u.cli.ifcli_packets_out;
return packet_out;
@ -4618,16 +4674,55 @@ maybe_retire_dcid (struct ietf_full_conn *conn, const lsquic_cid_t *dcid)
}
/* Return true if the two paths differ only in peer port */
static int
only_peer_port_changed (const struct network_path *old,
struct network_path *new)
{
const struct sockaddr *old_sa, *new_sa;
if (!lsquic_sockaddr_eq(NP_LOCAL_SA(old), NP_LOCAL_SA(new)))
return 0;
old_sa = NP_PEER_SA(old);
new_sa = NP_PEER_SA(new);
if (old_sa->sa_family == AF_INET)
return old_sa->sa_family == new_sa->sa_family
&& ((struct sockaddr_in *) old_sa)->sin_addr.s_addr
== ((struct sockaddr_in *) new_sa)->sin_addr.s_addr
&& ((struct sockaddr_in *) old_sa)->sin_port
!= /* NE! */((struct sockaddr_in *) new_sa)->sin_port;
else
return old_sa->sa_family == new_sa->sa_family
&& ((struct sockaddr_in6 *) old_sa)->sin6_port != /* NE! */
((struct sockaddr_in6 *) new_sa)->sin6_port
&& 0 == memcmp(&((struct sockaddr_in6 *) old_sa)->sin6_addr,
&((struct sockaddr_in6 *) new_sa)->sin6_addr,
sizeof(((struct sockaddr_in6 *) new_sa)->sin6_addr));
}
static void
switch_path_to (struct ietf_full_conn *conn, unsigned char path_id)
{
const unsigned char old_path_id = conn->ifc_cur_path_id;
const int keep_path_properties = conn->ifc_settings->es_optimistic_nat
&& only_peer_port_changed(CUR_NPATH(conn),
&conn->ifc_paths[path_id].cop_path);
assert(conn->ifc_cur_path_id != path_id);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "switched paths");
if (keep_path_properties)
{
conn->ifc_paths[path_id].cop_path.np_pack_size
= CUR_NPATH(conn)->np_pack_size;
LSQ_DEBUG("keep path properties: set MTU to %hu",
conn->ifc_paths[path_id].cop_path.np_pack_size);
}
lsquic_send_ctl_repath(&conn->ifc_send_ctl,
CUR_NPATH(conn), &conn->ifc_paths[path_id].cop_path);
CUR_NPATH(conn), &conn->ifc_paths[path_id].cop_path,
keep_path_properties);
maybe_retire_dcid(conn, &CUR_NPATH(conn)->np_dcid);
conn->ifc_cur_path_id = path_id;
conn->ifc_pub.path = CUR_NPATH(conn);
@ -5600,7 +5695,11 @@ insert_new_dcid (struct ietf_full_conn *conn, uint64_t seqno,
memcpy((*dce)->de_srst, token, sizeof((*dce)->de_srst));
(*dce)->de_flags |= DE_SRST;
if (update_cur_dcid)
{
*CUR_DCID(conn) = *cid;
if (CUR_CPATH(conn)->cop_flags & COP_SPIN_BIT)
CUR_CPATH(conn)->cop_spin_bit = 0;
}
}
else
LSQ_WARN("cannot allocate dce to insert DCID seqno %"PRIu64, seqno);
@ -5742,7 +5841,7 @@ process_retire_connection_id_frame (struct ietf_full_conn *conn,
{
if (LSQUIC_CIDS_EQ(&cce->cce_cid, &packet_in->pi_dcid))
{
ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR, "cannot retire CID "
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION, "cannot retire CID "
"seqno=%"PRIu64", for it is used as DCID in the packet", seqno);
return 0;
}
@ -6012,6 +6111,35 @@ process_timestamp_frame (struct ietf_full_conn *conn,
}
static unsigned
process_datagram_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{
const void *data;
size_t data_sz;
int parsed_len;
if (!(conn->ifc_flags & IFC_DATAGRAMS))
{
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
"Received unexpected DATAGRAM frame (not negotiated)");
return 0;
}
parsed_len = conn->ifc_conn.cn_pf->pf_parse_datagram_frame(p, len,
&data, &data_sz);
if (parsed_len < 0)
return 0;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "%zd-byte DATAGRAM", data_sz);
LSQ_DEBUG("%zd-byte DATAGRAM", data_sz);
conn->ifc_enpub->enp_stream_if->on_datagram(&conn->ifc_conn, data, data_sz);
return parsed_len;
}
typedef unsigned (*process_frame_f)(
struct ietf_full_conn *, struct lsquic_packet_in *,
const unsigned char *p, size_t);
@ -6041,6 +6169,7 @@ static process_frame_f const process_frames[N_QUIC_FRAMES] =
[QUIC_FRAME_HANDSHAKE_DONE] = process_handshake_done_frame,
[QUIC_FRAME_ACK_FREQUENCY] = process_ack_frequency_frame,
[QUIC_FRAME_TIMESTAMP] = process_timestamp_frame,
[QUIC_FRAME_DATAGRAM] = process_datagram_frame,
};
@ -6254,9 +6383,22 @@ force_queueing_ack_app (struct ietf_full_conn *conn)
}
enum was_missing {
/* Note that particular enum values matter for speed */
WM_NONE = 0,
WM_MAX_GAP = 1, /* Newly arrived ackable packet introduced a gap in incoming
* packet number sequence.
*/
WM_SMALLER = 2, /* Newly arrived ackable packet is smaller than previously
* seen maximum number.
*/
};
static void
try_queueing_ack_app (struct ietf_full_conn *conn,
int was_missing, int ecn, lsquic_time_t now)
enum was_missing was_missing, int ecn, lsquic_time_t now)
{
lsquic_time_t srtt, ack_timeout;
@ -6268,8 +6410,10 @@ try_queueing_ack_app (struct ietf_full_conn *conn,
*/
|| (ecn == ECN_CE
&& lsquic_send_ctl_ecn_turned_on(&conn->ifc_send_ctl))
|| (was_missing == WM_MAX_GAP)
|| ((conn->ifc_flags & IFC_ACK_HAD_MISS)
&& was_missing && conn->ifc_n_slack_akbl[PNS_APP] > 0)
&& was_missing == WM_SMALLER
&& conn->ifc_n_slack_akbl[PNS_APP] > 0)
|| many_in_and_will_write(conn))
{
lsquic_alarmset_unset(&conn->ifc_alset, AL_ACK_APP);
@ -6279,7 +6423,7 @@ try_queueing_ack_app (struct ietf_full_conn *conn,
"was_missing: %d",
lsquic_pns2str[PNS_APP], conn->ifc_n_slack_akbl[PNS_APP],
conn->ifc_n_slack_all,
!!(conn->ifc_flags & IFC_ACK_HAD_MISS), was_missing);
!!(conn->ifc_flags & IFC_ACK_HAD_MISS), (int) was_missing);
}
else if (conn->ifc_n_slack_akbl[PNS_APP] > 0)
{
@ -6310,17 +6454,6 @@ try_queueing_ack_init_or_hsk (struct ietf_full_conn *conn,
}
static void
try_queueing_ack (struct ietf_full_conn *conn, enum packnum_space pns,
int was_missing, int ecn, lsquic_time_t now)
{
if (PNS_APP == pns)
try_queueing_ack_app(conn, was_missing, ecn, now);
else
try_queueing_ack_init_or_hsk(conn, pns);
}
static int
maybe_queue_opp_ack (struct ietf_full_conn *conn)
{
@ -6437,6 +6570,8 @@ process_retry_packet (struct ietf_full_conn *conn,
return -1;
*CUR_DCID(conn) = scid;
if (CUR_CPATH(conn)->cop_flags & COP_SPIN_BIT)
CUR_CPATH(conn)->cop_spin_bit = 0;
lsquic_alarmset_unset(&conn->ifc_alset, AL_RETX_INIT);
lsquic_alarmset_unset(&conn->ifc_alset, AL_RETX_HSK);
lsquic_alarmset_unset(&conn->ifc_alset, AL_RETX_APP);
@ -6509,9 +6644,6 @@ on_dcid_change (struct ietf_full_conn *conn, const lsquic_cid_t *dcid_in)
LSQ_DEBUGC("%s: set SCID to %"CID_FMT, __func__, CID_BITS(CN_SCID(lconn)));
LOG_SCIDS(conn);
/* Reset spin bit, see [draft-ietf-quic-transport-20] Section 17.3.1 */
maybe_enable_spin(conn);
return 0;
}
@ -6568,15 +6700,33 @@ record_dcid (struct ietf_full_conn *conn,
}
static int
holes_after (struct lsquic_rechist *rechist, lsquic_packno_t packno)
{
const struct lsquic_packno_range *first_range;
first_range = lsquic_rechist_peek(rechist);
/* If it's not in the very first range, there is obviously a gap
* between it and the maximum packet number. If the packet number
* in question preceeds the cutoff, we assume that there are no
* holes (as we simply have no information).
*/
return first_range
&& packno < first_range->low
&& packno > lsquic_rechist_cutoff(rechist);
}
static int
process_regular_packet (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in)
{
struct conn_path *cpath;
enum packnum_space pns;
enum received_st st;
enum dec_packin dec_packin;
enum quic_ft_bit frame_types;
int was_missing, packno_increased;
enum was_missing was_missing;
unsigned n_rechist_packets;
unsigned char saved_path_id;
if (HETY_RETRY == packet_in->pi_header_type)
@ -6679,8 +6829,7 @@ process_regular_packet (struct ietf_full_conn *conn,
EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in);
packno_increased = packet_in->pi_packno
> lsquic_rechist_largest_packno(&conn->ifc_rechist[pns]);
n_rechist_packets = lsquic_rechist_n_packets(&conn->ifc_rechist[pns]);
st = lsquic_rechist_received(&conn->ifc_rechist[pns], packet_in->pi_packno,
packet_in->pi_received);
switch (st) {
@ -6704,31 +6853,81 @@ process_regular_packet (struct ietf_full_conn *conn,
if (lsquic_packet_in_non_probing(packet_in)
&& packet_in->pi_packno > conn->ifc_max_non_probing)
conn->ifc_max_non_probing = packet_in->pi_packno;
if (0 == (conn->ifc_flags & (IFC_ACK_QUED_INIT << pns)))
/* From [draft-ietf-quic-transport-30] Section 13.2.1:
*
" In order to assist loss detection at the sender, an endpoint SHOULD
" generate and send an ACK frame without delay when it receives an ack-
" eliciting packet either:
"
" * when the received packet has a packet number less than another
" ack-eliciting packet that has been received, or
"
" * when the packet has a packet number larger than the highest-
" numbered ack-eliciting packet that has been received and there are
" missing packets between that packet and this packet.
*
*/
if (packet_in->pi_frame_types & IQUIC_FRAME_ACKABLE_MASK)
{
frame_types = packet_in->pi_frame_types;
if (frame_types & IQUIC_FRAME_ACKABLE_MASK)
if (PNS_APP == pns /* was_missing is only used in PNS_APP */)
{
was_missing = packet_in->pi_packno !=
lsquic_rechist_largest_packno(&conn->ifc_rechist[pns]);
++conn->ifc_n_slack_akbl[pns];
if (packet_in->pi_packno > conn->ifc_max_ackable_packno_in)
{
was_missing = (enum was_missing) /* WM_MAX_GAP is 1 */
n_rechist_packets /* Don't count very first packno */
&& conn->ifc_max_ackable_packno_in + 1
< packet_in->pi_packno
&& holes_after(&conn->ifc_rechist[PNS_APP],
conn->ifc_max_ackable_packno_in);
conn->ifc_max_ackable_packno_in = packet_in->pi_packno;
}
else
was_missing = (enum was_missing) /* WM_SMALLER is 2 */
/* The check is necessary (rather setting was_missing to
* WM_SMALLER) because we cannot guarantee that peer does
* not have bugs.
*/
((packet_in->pi_packno
< conn->ifc_max_ackable_packno_in) << 1);
}
else
was_missing = 0;
conn->ifc_n_slack_all += PNS_APP == pns;
try_queueing_ack(conn, pns, was_missing,
was_missing = WM_NONE;
++conn->ifc_n_slack_akbl[pns];
}
else
was_missing = WM_NONE;
conn->ifc_n_slack_all += PNS_APP == pns;
if (0 == (conn->ifc_flags & (IFC_ACK_QUED_INIT << pns)))
{
if (PNS_APP == pns)
try_queueing_ack_app(conn, was_missing,
lsquic_packet_in_ecn(packet_in), packet_in->pi_received);
else
try_queueing_ack_init_or_hsk(conn, pns);
}
conn->ifc_incoming_ecn <<= 1;
conn->ifc_incoming_ecn |=
lsquic_packet_in_ecn(packet_in) != ECN_NOT_ECT;
++conn->ifc_ecn_counts_in[pns][ lsquic_packet_in_ecn(packet_in) ];
if (packno_increased && PNS_APP == pns && (conn->ifc_flags & IFC_SPIN))
if (PNS_APP == pns
&& (cpath = &conn->ifc_paths[packet_in->pi_path_id],
cpath->cop_flags & COP_SPIN_BIT)
/* [draft-ietf-quic-transport-30] Section 17.3.1 talks about
* how spin bit value is set.
*/
&& (packet_in->pi_packno > cpath->cop_max_packno
/* Zero means "unset", in which case any incoming packet
* number will do. On receipt of second packet numbered
* zero, the rechist module will dup it and this code path
* won't hit.
*/
|| cpath->cop_max_packno == 0))
{
cpath->cop_max_packno = packet_in->pi_packno;
if (conn->ifc_flags & IFC_SERVER)
conn->ifc_spin_bit = lsquic_packet_in_spin_bit(packet_in);
cpath->cop_spin_bit = lsquic_packet_in_spin_bit(packet_in);
else
conn->ifc_spin_bit = !lsquic_packet_in_spin_bit(packet_in);
cpath->cop_spin_bit = !lsquic_packet_in_spin_bit(packet_in);
}
conn->ifc_pub.bytes_in += packet_in->pi_data_sz;
if ((conn->ifc_mflags & MF_VALIDATE_PATH) &&
@ -7290,6 +7489,88 @@ ietf_full_conn_ci_retx_timeout (struct lsquic_conn *lconn)
}
static size_t
ietf_full_conn_ci_get_min_datagram_size (struct lsquic_conn *lconn)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
return (size_t) conn->ifc_min_dg_sz;
}
static int
ietf_full_conn_ci_set_min_datagram_size (struct lsquic_conn *lconn,
size_t new_size)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
const struct transport_params *const params =
lconn->cn_esf.i->esfi_get_peer_transport_params(lconn->cn_enc_session);
if (!(conn->ifc_flags & IFC_DATAGRAMS))
{
LSQ_WARN("datagrams are not enabled: cannot set minimum size");
return -1;
}
if (new_size > USHRT_MAX)
{
LSQ_DEBUG("min datagram size cannot be larger than %hu", USHRT_MAX);
return -1;
}
if (new_size > params->tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE])
{
LSQ_DEBUG("maximum datagram frame size is %"PRIu64", cannot change it "
"to %zd", params->tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE],
new_size);
return -1;
}
conn->ifc_min_dg_sz = new_size;
LSQ_DEBUG("set minimum datagram size to %zd bytes", new_size);
return 0;
}
/* Return true if datagram was written, false otherwise */
static int
write_datagram (struct ietf_full_conn *conn)
{
struct lsquic_packet_out *packet_out;
size_t need;
int w;
need = conn->ifc_conn.cn_pf->pf_datagram_frame_size(conn->ifc_min_dg_sz);
packet_out = get_writeable_packet(conn, need);
if (!packet_out)
return 0;
w = conn->ifc_conn.cn_pf->pf_gen_datagram_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), conn->ifc_min_dg_sz,
conn->ifc_max_dg_sz,
conn->ifc_enpub->enp_stream_if->on_dg_write, &conn->ifc_conn);
if (w < 0)
{
LSQ_DEBUG("could not generate DATAGRAM frame");
return 0;
}
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_DATAGRAM, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding DATAGRAME frame to packet failed: %d", errno);
return 0;
}
packet_out->po_frame_types |= QUIC_FTBIT_DATAGRAM;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
/* XXX The DATAGRAM frame should really be a regen. Do it when we
* no longer require these frame types to be at the beginning of the
* packet.
*/
return 1;
}
static enum tick_st
ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
{
@ -7454,6 +7735,10 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
if (!write_is_possible(conn))
goto end_write;
while ((conn->ifc_mflags & MF_WANT_DATAGRAM_WRITE) && write_datagram(conn))
if (!write_is_possible(conn))
goto end_write;
if (!TAILQ_EMPTY(&conn->ifc_pub.write_streams))
{
process_streams_write_events(conn, 1);
@ -7850,8 +8135,11 @@ ietf_full_conn_ci_record_addrs (struct lsquic_conn *lconn, void *peer_ctx,
{
record_to_path(conn, first_unused, peer_ctx, local_sa, peer_sa);
if (0 == conn->ifc_used_paths && !(conn->ifc_flags & IFC_SERVER))
{
/* First path is considered valid immediately */
first_unused->cop_flags |= COP_VALIDATED;
maybe_enable_spin(conn, first_unused);
}
LSQ_DEBUG("record new path ID %d",
(int) (first_unused - conn->ifc_paths));
conn->ifc_used_paths |= 1 << (first_unused - conn->ifc_paths);
@ -7905,6 +8193,7 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
.ci_get_ctx = ietf_full_conn_ci_get_ctx, \
.ci_get_engine = ietf_full_conn_ci_get_engine, \
.ci_get_log_cid = ietf_full_conn_ci_get_log_cid, \
.ci_get_min_datagram_size= ietf_full_conn_ci_get_min_datagram_size, \
.ci_get_path = ietf_full_conn_ci_get_path, \
.ci_going_away = ietf_full_conn_ci_going_away, \
.ci_hsk_done = ietf_full_conn_ci_hsk_done, \
@ -7922,10 +8211,12 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
.ci_report_live = ietf_full_conn_ci_report_live, \
.ci_retx_timeout = ietf_full_conn_ci_retx_timeout, \
.ci_set_ctx = ietf_full_conn_ci_set_ctx, \
.ci_set_min_datagram_size= ietf_full_conn_ci_set_min_datagram_size, \
.ci_status = ietf_full_conn_ci_status, \
.ci_stateless_reset = ietf_full_conn_ci_stateless_reset, \
.ci_tick = ietf_full_conn_ci_tick, \
.ci_tls_alert = ietf_full_conn_ci_tls_alert, \
.ci_want_datagram_write = ietf_full_conn_ci_want_datagram_write, \
.ci_write_ack = ietf_full_conn_ci_write_ack
static const struct conn_iface ietf_full_conn_iface = {
@ -8111,6 +8402,14 @@ on_setting (void *ctx, uint64_t setting_id, uint64_t value)
LSQ_DEBUG("received unknown SETTING 0x%"PRIX64"=0x%"PRIX64
"; ignore it", setting_id, value);
break;
case 2: /* HTTP/2 SETTINGS_ENABLE_PUSH */
case 3: /* HTTP/2 SETTINGS_MAX_CONCURRENT_STREAMS */
case 4: /* HTTP/2 SETTINGS_INITIAL_WINDOW_SIZE */
case 5: /* HTTP/2 SETTINGS_MAX_FRAME_SIZE */
/* [draft-ietf-quic-http-30] Section 7.2.4.1 */
ABORT_QUIETLY(1, HEC_SETTINGS_ERROR, "unexpected HTTP/2 setting "
"%"PRIu64, setting_id);
break;
}
}
@ -8333,12 +8632,14 @@ hcsi_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
callbacks = &hcsi_callbacks_server_28;
break;
case (0 << 8) | LSQVER_ID29:
case (0 << 8) | LSQVER_ID30:
callbacks = &hcsi_callbacks_client_29;
break;
default:
assert(0);
/* fallthru */
case (1 << 8) | LSQVER_ID29:
case (1 << 8) | LSQVER_ID30:
callbacks = &hcsi_callbacks_server_29;
break;
}

View file

@ -3768,6 +3768,7 @@ gquic_encrypt_packet (enc_session_t *enc_session_p,
packet_out->po_sent_sz = enc_sz;
packet_out->po_flags &= ~PO_IPv6;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ|(ipv6 << POIPv6_SHIFT);
packet_out->po_dcid_len = GQUIC_CID_LEN;
return ENCPA_OK;
}

View file

@ -72,6 +72,7 @@ enum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_HSK_ADAPTER] = LSQ_LOG_WARN,
[LSQLM_BBR] = LSQ_LOG_WARN,
[LSQLM_CUBIC] = LSQ_LOG_WARN,
[LSQLM_ADAPTIVE_CC] = LSQ_LOG_WARN,
[LSQLM_HEADERS] = LSQ_LOG_WARN,
[LSQLM_FRAME_READER]= LSQ_LOG_WARN,
[LSQLM_FRAME_WRITER]= LSQ_LOG_WARN,
@ -115,6 +116,7 @@ const char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
[LSQLM_HSK_ADAPTER] = "hsk-adapter",
[LSQLM_BBR] = "bbr",
[LSQLM_CUBIC] = "cubic",
[LSQLM_ADAPTIVE_CC] = "adaptive-cc",
[LSQLM_HEADERS] = "headers",
[LSQLM_FRAME_READER]= "frame-reader",
[LSQLM_FRAME_WRITER]= "frame-writer",

View file

@ -63,6 +63,7 @@ enum lsquic_logger_module {
LSQLM_HSK_ADAPTER,
LSQLM_BBR,
LSQLM_CUBIC,
LSQLM_ADAPTIVE_CC,
LSQLM_HEADERS,
LSQLM_FRAME_WRITER,
LSQLM_FRAME_READER,

View file

@ -182,6 +182,7 @@ lsquic_malo_create (size_t obj_size)
{
TAILQ_INIT(&malo->elems);
malo->obj_size = obj_size;
malo->next_iter_elem = NULL;
return malo;
}
else

View file

@ -1924,12 +1924,13 @@ mini_conn_ci_Q050_packet_in (struct lsquic_conn *lconn,
static struct lsquic_packet_out *
mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal_UNUSED)
{
struct mini_conn *mc = (struct mini_conn *) lconn;
lsquic_packet_out_t *packet_out;
assert(0 == size);
assert(NULL == to_coal_UNUSED);
TAILQ_FOREACH(packet_out, &mc->mc_packets_out, po_next)
{
if (packet_out->po_flags & PO_SENT)

View file

@ -448,6 +448,17 @@ is_first_packet_ok (const struct lsquic_packet_in *packet_in,
}
static void
imico_peer_addr_validated (struct ietf_mini_conn *conn, const char *how)
{
if (!(conn->imc_flags & IMC_ADDR_VALIDATED))
{
conn->imc_flags |= IMC_ADDR_VALIDATED;
LSQ_DEBUG("peer address validated (%s)", how);
}
}
struct lsquic_conn *
lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
const struct lsquic_packet_in *packet_in,
@ -527,7 +538,7 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
TAILQ_INIT(&conn->imc_app_packets);
TAILQ_INIT(&conn->imc_crypto_frames);
if (odcid)
conn->imc_flags |= IMC_ADDR_VALIDATED;
imico_peer_addr_validated(conn, "odcid");
LSQ_DEBUG("created mini connection object %p; max packet size=%hu",
conn, conn->imc_path.np_pack_size);
@ -652,7 +663,8 @@ imico_can_send (const struct ietf_mini_conn *conn, size_t size)
static struct lsquic_packet_out *
ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal)
{
struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
struct lsquic_packet_out *packet_out;
@ -663,7 +675,11 @@ ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
if (packet_out->po_flags & PO_SENT)
continue;
packet_size = lsquic_packet_out_total_sz(lconn, packet_out);
if (size == 0 || packet_size + size <= conn->imc_path.np_pack_size)
if (!(to_coal
&& (packet_size + to_coal->prev_sz_sum
> conn->imc_path.np_pack_size
|| !lsquic_packet_out_equal_dcids(to_coal->prev_packet, packet_out))
))
{
if (!imico_can_send(conn, packet_size))
{
@ -674,7 +690,7 @@ ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
}
packet_out->po_flags |= PO_SENT;
conn->imc_bytes_out += packet_size;
if (size == 0)
if (!to_coal)
LSQ_DEBUG("packet_to_send: %"PRIu64, packet_out->po_packno);
else
LSQ_DEBUG("packet_to_send: %"PRIu64" (coalesced)",
@ -847,7 +863,7 @@ imico_process_crypto_frame (IMICO_PROC_FRAME_ARGS)
{
if (0 != conn->imc_conn.cn_esf.i->esfi_init_server(
conn->imc_conn.cn_enc_session))
return -1;
return 0;
conn->imc_flags |= IMC_ENC_SESS_INITED;
}
@ -1210,6 +1226,32 @@ imico_maybe_delay_processing (struct ietf_mini_conn *conn,
}
/* [draft-ietf-quic-transport-30] Section 8.1:
" Additionally, a server MAY consider the client address validated if
" the client uses a connection ID chosen by the server and the
" connection ID contains at least 64 bits of entropy.
*
* We use RAND_bytes() to generate SCIDs, so it's all entropy.
*/
static void
imico_maybe_validate_by_dcid (struct ietf_mini_conn *conn,
const lsquic_cid_t *dcid)
{
unsigned i;
if (dcid->len >= 8)
/* Generic code with unnecessary loop as future-proofing */
for (i = 0; i < conn->imc_conn.cn_n_cces; ++i)
if ((conn->imc_conn.cn_cces_mask & (i << 1))
&& (conn->imc_conn.cn_cces[i].cce_flags & CCE_SEQNO)
&& LSQUIC_CIDS_EQ(&conn->imc_conn.cn_cces[i].cce_cid, dcid))
{
imico_peer_addr_validated(conn, "dcid/scid + entropy");
return;
}
}
/* Only a single packet is supported */
static void
ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
@ -1236,6 +1278,9 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
return;
}
if (!(conn->imc_flags & IMC_ADDR_VALIDATED))
imico_maybe_validate_by_dcid(conn, &packet_in->pi_dcid);
pns = lsquic_hety2pns[ packet_in->pi_header_type ];
if (pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT))
{
@ -1261,7 +1306,7 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
return;
}
else if (pns == PNS_HSK)
conn->imc_flags |= IMC_ADDR_VALIDATED;
imico_peer_addr_validated(conn, "handshake PNS");
if (((conn->imc_flags >> IMCBIT_PNS_BIT_SHIFT) & 3) < pns)
{

View file

@ -36,6 +36,7 @@ enum quic_frame_type
QUIC_FRAME_HANDSHAKE_DONE, /* I */
QUIC_FRAME_ACK_FREQUENCY, /* I */
QUIC_FRAME_TIMESTAMP, /* I */
QUIC_FRAME_DATAGRAM, /* I */
N_QUIC_FRAMES
};
@ -66,6 +67,7 @@ enum quic_ft_bit {
QUIC_FTBIT_HANDSHAKE_DONE = 1 << QUIC_FRAME_HANDSHAKE_DONE,
QUIC_FTBIT_ACK_FREQUENCY = 1 << QUIC_FRAME_ACK_FREQUENCY,
QUIC_FTBIT_TIMESTAMP = 1 << QUIC_FRAME_TIMESTAMP,
QUIC_FTBIT_DATAGRAM = 1 << QUIC_FRAME_DATAGRAM,
};
static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
@ -95,6 +97,7 @@ static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
[QUIC_FRAME_HANDSHAKE_DONE] = "QUIC_FRAME_HANDSHAKE_DONE",
[QUIC_FRAME_ACK_FREQUENCY] = "QUIC_FRAME_ACK_FREQUENCY",
[QUIC_FRAME_TIMESTAMP] = "QUIC_FRAME_TIMESTAMP",
[QUIC_FRAME_DATAGRAM] = "QUIC_FRAME_DATAGRAM",
};
#define QUIC_FRAME_PRELEN (sizeof("QUIC_FRAME_"))
@ -132,6 +135,7 @@ static const char * const frame_type_2_str[N_QUIC_FRAMES] = {
QUIC_FRAME_SLEN(QUIC_FRAME_HANDSHAKE_DONE) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_ACK_FREQUENCY) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_TIMESTAMP) + 1 + \
QUIC_FRAME_SLEN(QUIC_FRAME_DATAGRAM) + 1 + \
0

View file

@ -475,3 +475,55 @@ lsquic_packet_out_turn_on_fin (struct lsquic_packet_out *packet_out,
return -1;
}
static unsigned
offset_to_dcid (const struct lsquic_packet_out *packet_out)
{
if (packet_out->po_header_type == HETY_NOT_SET)
return 1;
else
{
assert(!(packet_out->po_lflags & POL_GQUIC));
return 6;
}
}
/* Return true if DCIDs of the two packets are equal, false otherwise. */
int
lsquic_packet_out_equal_dcids (const struct lsquic_packet_out *a,
const struct lsquic_packet_out *b)
{
const int a_encrypted = !!(a->po_flags & PO_ENCRYPTED);
const int b_encrypted = !!(b->po_flags & PO_ENCRYPTED);
const unsigned char *dcids[2];
size_t sizes[2];
switch ((a_encrypted << 1) | b_encrypted)
{
case (0 << 1) | 0:
return a->po_path == b->po_path;
case (0 << 1) | 1:
dcids[0] = a->po_path->np_dcid.idbuf;
sizes[0] = a->po_path->np_dcid.len;
dcids[1] = b->po_enc_data + offset_to_dcid(b);
sizes[1] = b->po_dcid_len;
break;
case (1 << 1) | 0:
dcids[0] = a->po_enc_data + offset_to_dcid(a);
sizes[0] = a->po_dcid_len;
dcids[1] = b->po_path->np_dcid.idbuf;
sizes[1] = b->po_path->np_dcid.len;
break;
default:
dcids[0] = a->po_enc_data + offset_to_dcid(a);
sizes[0] = a->po_dcid_len;
dcids[1] = b->po_enc_data + offset_to_dcid(b);
sizes[1] = b->po_dcid_len;
break;
}
return sizes[0] == sizes[1]
&& 0 == memcmp(dcids[0], dcids[1], sizes[0]);
}

View file

@ -148,6 +148,7 @@ typedef struct lsquic_packet_out
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
unsigned short po_token_len;
enum header_type po_header_type:8;
unsigned char po_dcid_len; /* If PO_ENCRYPTED is set */
enum {
POL_GQUIC = 1 << 0, /* Used for logging */
#define POLEV_SHIFT 1
@ -345,4 +346,7 @@ int
lsquic_packet_out_turn_on_fin (struct lsquic_packet_out *,
const struct parse_funcs *, const struct lsquic_stream *);
int
lsquic_packet_out_equal_dcids (const struct lsquic_packet_out *,
const struct lsquic_packet_out *);
#endif

View file

@ -323,6 +323,15 @@ struct parse_funcs
(*pf_gen_timestamp_frame) (unsigned char *buf, size_t buf_len, uint64_t);
int
(*pf_parse_timestamp_frame) (const unsigned char *buf, size_t, uint64_t *);
int
(*pf_parse_datagram_frame) (const unsigned char *buf, size_t, const void **,
size_t *);
int
(*pf_gen_datagram_frame) (unsigned char *, size_t bufsz, size_t min_sz,
size_t max_sz, ssize_t (*)(struct lsquic_conn *, void *, size_t),
struct lsquic_conn *);
unsigned
(*pf_datagram_frame_size) (size_t);
};

View file

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

View file

@ -49,6 +49,8 @@
#define FRAME_TYPE_ACK_FREQUENCY 0xAF
#define FRAME_TYPE_TIMESTAMP 0x2F5
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static int
ietf_v1_gen_one_varint (unsigned char *, size_t, unsigned char, uint64_t);
@ -2147,6 +2149,82 @@ ietf_v1_parse_timestamp_frame (const unsigned char *buf, size_t buf_len,
}
static int
ietf_v1_parse_datagram_frame (const unsigned char *buf, size_t buf_len,
const void **data, size_t *data_len)
{
uint64_t len;
int s;
/* Length and frame type have been checked already */
assert(buf_len > 0);
assert(buf[0] == 0x30 || buf[0] == 0x31);
if (buf[0] & 1)
{
s = vint_read(buf + 1, buf + buf_len, &len);
if (s > 0 && 1 + s + len >= buf_len)
{
*data = buf + 1 + s;
*data_len = len;
return 1 + buf_len + len;
}
else
return -1;
}
else
{
*data = buf + 1;
*data_len = buf_len - 1;
return buf_len;
}
}
static unsigned
ietf_v1_datagram_frame_size (size_t sz)
{
return 1u + vint_size(sz) + sz;
}
static int
ietf_v1_gen_datagram_frame (unsigned char *buf, size_t bufsz, size_t min_sz,
size_t max_sz,
ssize_t (*user_callback)(struct lsquic_conn *, void *, size_t),
struct lsquic_conn *lconn)
{
unsigned bits, len_sz;
ssize_t nw;
/* We always generate length. A more efficient implementation would
* complicate the API.
*/
if (min_sz)
bits = vint_val2bits(min_sz);
else
bits = vint_val2bits(bufsz);
len_sz = 1u << bits;
if (1 + len_sz + min_sz > bufsz)
{
errno = ENOBUFS;
return -1;
}
nw = user_callback(lconn, buf + 1 + len_sz, min_sz ? min_sz
: MIN(bufsz - 1 - len_sz, max_sz));
if (nw >= 0)
{
buf[0] = 0x31;
vint_write(&buf[1], (uint64_t) nw, bits, len_sz);
return 1 + len_sz + nw;
}
else
return -1;
}
const struct parse_funcs lsquic_parse_funcs_ietf_v1 =
{
.pf_gen_reg_pkt_header = ietf_v1_gen_reg_pkt_header,
@ -2217,4 +2295,7 @@ const struct parse_funcs lsquic_parse_funcs_ietf_v1 =
.pf_ack_frequency_frame_size = ietf_v1_ack_frequency_frame_size,
.pf_gen_timestamp_frame = ietf_v1_gen_timestamp_frame,
.pf_parse_timestamp_frame = ietf_v1_parse_timestamp_frame,
.pf_parse_datagram_frame = ietf_v1_parse_datagram_frame,
.pf_gen_datagram_frame = ietf_v1_gen_datagram_frame,
.pf_datagram_frame_size = ietf_v1_datagram_frame_size,
};

View file

@ -289,8 +289,8 @@ const enum quic_frame_type lsquic_iquic_byte2type[0x40] =
[0x2D] = QUIC_FRAME_INVALID,
[0x2E] = QUIC_FRAME_INVALID,
[0x2F] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_INVALID,
[0x31] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_DATAGRAM,
[0x31] = QUIC_FRAME_DATAGRAM,
[0x32] = QUIC_FRAME_INVALID,
[0x33] = QUIC_FRAME_INVALID,
[0x34] = QUIC_FRAME_INVALID,

View file

@ -515,10 +515,11 @@ lsquic_prq_have_pending (const struct pr_queue *prq)
static struct lsquic_packet_out *
evanescent_conn_ci_next_packet_to_send (struct lsquic_conn *lconn, size_t size)
evanescent_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal_UNUSED)
{
struct evanescent_conn *const evconn = (struct evanescent_conn *) lconn;
assert(size == 0);
assert(!to_coal_UNUSED);
return &evconn->evc_packet_out;
}

View file

@ -188,3 +188,16 @@ lsquic_rechist_mem_used (const struct lsquic_rechist *rechist)
- sizeof(rechist->rh_pints)
+ lsquic_packints_mem_used(&rechist->rh_pints);
}
const struct lsquic_packno_range *
lsquic_rechist_peek (const struct lsquic_rechist *rechist)
{
const struct packet_interval *pint;
pint = TAILQ_FIRST(&rechist->rh_pints.pk_intervals);
if (pint)
return &pint->range;
else
return NULL;
}

View file

@ -77,4 +77,9 @@ lsquic_rechist_largest_recv (const lsquic_rechist_t *);
size_t
lsquic_rechist_mem_used (const struct lsquic_rechist *);
const struct lsquic_packno_range *
lsquic_rechist_peek (const struct lsquic_rechist *);
#define lsquic_rechist_n_packets(rechist_) (+(rechist_)->rh_n_packets)
#endif

View file

@ -30,6 +30,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_util.h"
#include "lsquic_sfcw.h"
#include "lsquic_varint.h"
@ -63,7 +64,7 @@
#define MIN_RTO_DELAY 1000000 /* Microseconds */
#define N_NACKS_BEFORE_RETX 3
#define CGP(ctl) ((struct cong_ctl *) &(ctl)->sc_cong_u)
#define CGP(ctl) ((struct cong_ctl *) (ctl)->sc_cong_ctl)
#define packet_out_total_sz(p) \
lsquic_packet_out_total_sz(ctl->sc_conn_pub->lconn, p)
@ -323,7 +324,7 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
struct lsquic_engine_public *enpub, const struct ver_neg *ver_neg,
struct lsquic_conn_public *conn_pub, enum send_ctl_flags flags)
{
unsigned i, algo;
unsigned i;
memset(ctl, 0, sizeof(*ctl));
TAILQ_INIT(&ctl->sc_scheduled_packets);
TAILQ_INIT(&ctl->sc_unacked_packets[PNS_INIT]);
@ -351,14 +352,22 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
lsquic_alarmset_init_alarm(alset, AL_RETX_HSK, retx_alarm_rings, ctl);
lsquic_alarmset_init_alarm(alset, AL_RETX_APP, retx_alarm_rings, ctl);
lsquic_senhist_init(&ctl->sc_senhist, ctl->sc_flags & SC_IETF);
if (0 == enpub->enp_settings.es_cc_algo)
algo = LSQUIC_DF_CC_ALGO;
else
algo = enpub->enp_settings.es_cc_algo;
if (algo == 2)
ctl->sc_ci = &lsquic_cong_bbr_if;
else
switch (enpub->enp_settings.es_cc_algo)
{
case 1:
ctl->sc_ci = &lsquic_cong_cubic_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc.acc_cubic;
break;
case 2:
ctl->sc_ci = &lsquic_cong_bbr_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc.acc_bbr;
break;
case 3:
default:
ctl->sc_ci = &lsquic_cong_adaptive_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc;
break;
}
ctl->sc_ci->cci_init(CGP(ctl), conn_pub, ctl->sc_retx_frames);
if (ctl->sc_flags & SC_PACE)
lsquic_pacer_init(&ctl->sc_pacer, conn_pub->lconn,
@ -682,6 +691,33 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
}
static void
send_ctl_select_cc (struct lsquic_send_ctl *ctl)
{
lsquic_time_t srtt;
srtt = lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats);
if (srtt <= ctl->sc_enpub->enp_settings.es_cc_rtt_thresh)
{
LSQ_INFO("srtt is %"PRIu64" usec, which is smaller than or equal to "
"the threshold of %u usec: select Cubic congestion controller",
srtt, ctl->sc_enpub->enp_settings.es_cc_rtt_thresh);
ctl->sc_ci = &lsquic_cong_cubic_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc.acc_cubic;
ctl->sc_flags |= SC_CLEANUP_BBR;
}
else
{
LSQ_INFO("srtt is %"PRIu64" usec, which is greater than the threshold "
"of %u usec: select BBRv1 congestion controller", srtt,
ctl->sc_enpub->enp_settings.es_cc_rtt_thresh);
ctl->sc_ci = &lsquic_cong_bbr_if;
ctl->sc_cong_ctl = &ctl->sc_adaptive_cc.acc_bbr;
}
}
static void
take_rtt_sample (lsquic_send_ctl_t *ctl,
lsquic_time_t now, lsquic_time_t lack_delta)
@ -696,6 +732,8 @@ take_rtt_sample (lsquic_send_ctl_t *ctl,
LSQ_DEBUG("packno %"PRIu64"; rtt: %"PRIu64"; delta: %"PRIu64"; "
"new srtt: %"PRIu64, packno, measured_rtt, lack_delta,
lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats));
if (ctl->sc_ci == &lsquic_cong_adaptive_if)
send_ctl_select_cc(ctl);
}
}
@ -1423,6 +1461,11 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
if (ctl->sc_flags & SC_PACE)
lsquic_pacer_cleanup(&ctl->sc_pacer);
ctl->sc_ci->cci_cleanup(CGP(ctl));
if (ctl->sc_flags & SC_CLEANUP_BBR)
{
assert(ctl->sc_ci == &lsquic_cong_cubic_if);
lsquic_cong_bbr_if.cci_cleanup(&ctl->sc_adaptive_cc.acc_bbr);
}
#if LSQUIC_SEND_STATS
LSQ_NOTICE("stats: n_total_sent: %u; n_resent: %u; n_delayed: %u",
ctl->sc_stats.n_total_sent, ctl->sc_stats.n_resent,
@ -1801,9 +1844,11 @@ lsquic_send_ctl_next_packet_to_send_predict (struct lsquic_send_ctl *ctl)
lsquic_packet_out_t *
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl,
const struct to_coal *to_coal)
{
lsquic_packet_out_t *packet_out;
size_t size;
int dec_limit;
get_packet:
@ -1843,14 +1888,24 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl, size_t size)
}
}
if (UNLIKELY(size))
if (UNLIKELY(to_coal != NULL))
{
if (packet_out_total_sz(packet_out) + size > SC_PACK_SIZE(ctl))
/* From [draft-ietf-quic-transport-30], Section-12.2:
" Senders MUST NOT coalesce QUIC packets with different connection
" IDs into a single UDP datagram.
*/
if (packet_out_total_sz(packet_out) + to_coal->prev_sz_sum
> SC_PACK_SIZE(ctl)
|| !lsquic_packet_out_equal_dcids(to_coal->prev_packet, packet_out))
return NULL;
LSQ_DEBUG("packet %"PRIu64" (%zu bytes) will be tacked on to "
"previous packet(s) (%zu bytes) (coalescing)",
packet_out->po_packno, packet_out_total_sz(packet_out), size);
packet_out->po_packno, packet_out_total_sz(packet_out),
to_coal->prev_sz_sum);
size = to_coal->prev_sz_sum;
}
else
size = 0;
send_ctl_sched_remove(ctl, packet_out);
if (dec_limit)
@ -3336,8 +3391,9 @@ send_ctl_resize_q (struct lsquic_send_ctl *ctl, struct lsquic_packets_tailq *q,
void
lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl, struct network_path *old,
struct network_path *new)
lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl,
const struct network_path *old, const struct network_path *new,
int keep_path_properties)
{
struct lsquic_packet_out *packet_out;
unsigned count;
@ -3367,11 +3423,15 @@ lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl, struct network_path *old,
LSQ_DEBUG("repathed %u packet%.*s", count, count != 1, "s");
lsquic_send_ctl_resize(ctl);
memset(&ctl->sc_conn_pub->rtt_stats, 0,
sizeof(ctl->sc_conn_pub->rtt_stats));
ctl->sc_ci->cci_reinit(CGP(ctl));
if (keep_path_properties)
LSQ_DEBUG("keeping path properties: MTU, RTT, and CC state");
else
{
lsquic_send_ctl_resize(ctl);
memset(&ctl->sc_conn_pub->rtt_stats, 0,
sizeof(ctl->sc_conn_pub->rtt_stats));
ctl->sc_ci->cci_reinit(CGP(ctl));
}
}

View file

@ -20,6 +20,7 @@ struct lsquic_conn_public;
struct network_path;
struct ver_neg;
enum pns;
struct to_coal;
enum buf_packet_type { BPT_HIGHEST_PRIO, BPT_OTHER_PRIO, };
@ -48,6 +49,7 @@ enum send_ctl_flags {
SC_SANITY_CHECK = 1 << 15,
SC_CIDLEN = 1 << 16, /* sc_cidlen is set */
SC_POISON = 1 << 17, /* poisoned packet exists */
SC_CLEANUP_BBR = 1 << 18,
};
typedef struct lsquic_send_ctl {
@ -67,11 +69,9 @@ typedef struct lsquic_send_ctl {
int (*sc_can_send)(struct lsquic_send_ctl *);
unsigned sc_bytes_unacked_retx;
unsigned sc_bytes_scheduled;
union {
struct lsquic_cubic cubic;
struct lsquic_bbr bbr;
} sc_cong_u;
struct adaptive_cc sc_adaptive_cc;
const struct cong_ctl_if *sc_ci;
void *sc_cong_ctl;
struct lsquic_engine_public *sc_enpub;
unsigned sc_bytes_unacked_all;
unsigned sc_n_in_flight_all;
@ -166,7 +166,8 @@ void
lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *, struct lsquic_packet_out *);
struct lsquic_packet_out *
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *, size_t);
lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *,
const struct to_coal *);
int
lsquic_send_ctl_next_packet_to_send_predict (struct lsquic_send_ctl *);
@ -362,8 +363,9 @@ void
lsquic_send_ctl_empty_pns (struct lsquic_send_ctl *, enum packnum_space);
void
lsquic_send_ctl_repath (struct lsquic_send_ctl *, struct network_path *old,
struct network_path *new);
lsquic_send_ctl_repath (struct lsquic_send_ctl *ctl,
const struct network_path *old, const struct network_path *new,
int keep_path_properties);
void
lsquic_send_ctl_resize (struct lsquic_send_ctl *);

View file

@ -61,6 +61,7 @@
#include "lsquic_bw_sampler.h"
#include "lsquic_minmax.h"
#include "lsquic_bbr.h"
#include "lsquic_adaptive_cc.h"
#include "lsquic_send_ctl.h"
#include "lsquic_headers.h"
#include "lsquic_ev_log.h"
@ -504,9 +505,16 @@ lsquic_stream_new_crypto (enum enc_level enc_level,
stream->sm_bflags |= SMBF_CRYPTO|SMBF_IETF;
stream->sm_enc_level = enc_level;
/* TODO: why have limit in crypto stream? Set it to UINT64_MAX? */
/* We allow buffering of up to 16 KB of CRYPTO data (I guess we could
* make this configurable?). The window is opened (without sending
* MAX_STREAM_DATA) as CRYPTO data is consumed. If too much comes in
* at a time, we abort with TEC_CRYPTO_BUFFER_EXCEEDED.
*/
lsquic_sfcw_init(&stream->fc, 16 * 1024, NULL, conn_pub, stream_id);
stream->max_send_off = 16 * 1024;
/* Don't limit ourselves from sending CRYPTO data. We assume that
* the underlying crypto library behaves in a sane manner.
*/
stream->max_send_off = UINT64_MAX;
LSQ_DEBUG("created crypto stream");
SM_HISTORY_APPEND(stream, SHE_CREATED);
stream->sm_frame_header_sz = stream_crypto_frame_header_sz;
@ -970,8 +978,14 @@ lsquic_stream_update_sfcw (lsquic_stream_t *stream, uint64_t max_off)
if (stream->sm_bflags & SMBF_IETF)
{
lconn = stream->conn_pub->lconn;
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FLOW_CONTROL_ERROR,
"flow control violation on stream %"PRIu64, stream->id);
if (lsquic_stream_is_crypto(stream))
lconn->cn_if->ci_abort_error(lconn, 0,
TEC_CRYPTO_BUFFER_EXCEEDED,
"crypto buffer exceeded on in crypto level %"PRIu64,
crypto_level(stream));
else
lconn->cn_if->ci_abort_error(lconn, 0, TEC_FLOW_CONTROL_ERROR,
"flow control violation on stream %"PRIu64, stream->id);
}
return -1;
}
@ -1369,7 +1383,12 @@ static void
stream_consumed_bytes (struct lsquic_stream *stream)
{
lsquic_sfcw_set_read_off(&stream->fc, stream->read_offset);
if (lsquic_sfcw_fc_offsets_changed(&stream->fc))
if (lsquic_sfcw_fc_offsets_changed(&stream->fc)
/* We advance crypto streams' offsets (to control amount of
* buffering we allow), but do not send MAX_STREAM_DATA frames.
*/
&& !((stream->sm_bflags & (SMBF_IETF|SMBF_CRYPTO))
== (SMBF_IETF|SMBF_CRYPTO)))
{
if (!(stream->sm_qflags & SMQF_SENDING_FLAGS))
TAILQ_INSERT_TAIL(&stream->conn_pub->sending_streams, stream,
@ -4534,6 +4553,12 @@ update_type_hist_and_check (const struct lsquic_stream *stream,
case HQFT_MAX_PUSH_ID:
/* [draft-ietf-quic-http-24], Section 7 */
return -1;
case 2: /* HTTP/2 PRIORITY */
case 6: /* HTTP/2 PING */
case 8: /* HTTP/2 WINDOW_UPDATE */
case 9: /* HTTP/2 CONTINUATION */
/* [draft-ietf-quic-http-30], Section 7.2.8 */
return -1;
default:
/* Ignore unknown frames */
return 0;

View file

@ -536,7 +536,7 @@ size_t
lsquic_stream_flush_threshold (const struct lsquic_stream *, unsigned);
#endif
#define crypto_level(stream) (~0ULL - (stream)->id)
#define crypto_level(stream) (UINT64_MAX - (stream)->id)
void
lsquic_stream_set_stream_if (struct lsquic_stream *,

View file

@ -49,7 +49,7 @@ struct tokgen_shm_state
{
uint8_t tgss_version;
uint8_t tgss_magic_top[sizeof(TOKGEN_SHM_MAGIC_TOP) - 1];
uint8_t tgss_crypter_key[N_TOKEN_TYPES][CRYPTER_KEY_SIZE];
uint8_t tgss_padding[2 * CRYPTER_KEY_SIZE];
uint8_t tgss_srst_prk_size;
uint8_t tgss_srst_prk[SRST_MAX_PRK_SIZE];
uint8_t tgss_magic_bottom[sizeof(TOKGEN_SHM_MAGIC_BOTTOM) - 1];
@ -72,8 +72,6 @@ struct crypter
struct token_generator
{
/* We encrypt different token types using different keys. */
struct crypter tg_crypters[N_TOKEN_TYPES];
/* Stateless reset token is generated using HKDF with CID as the
* `info' parameter to HKDF-Expand.

View file

@ -7,8 +7,6 @@ struct sockaddr;
struct lsquic_packet_in;
struct lsquic_cid;
enum token_type { TOKEN_RETRY, TOKEN_RESUME, N_TOKEN_TYPES, };
struct token_generator;
struct token_generator *

View file

@ -54,6 +54,7 @@ tpi_val_2_enum (uint64_t tpi_val)
case 14: return TPI_ACTIVE_CONNECTION_ID_LIMIT;
case 15: return TPI_INITIAL_SOURCE_CID;
case 16: return TPI_RETRY_SOURCE_CID;
case 0x20: return TPI_MAX_DATAGRAM_FRAME_SIZE;
#if LSQUIC_TEST_QUANTUM_READINESS
case 0xC37: return TPI_QUANTUM_READINESS;
#endif
@ -85,6 +86,7 @@ static const unsigned enum_2_tpi_val[LAST_TPI + 1] =
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = 0xE,
[TPI_INITIAL_SOURCE_CID] = 0xF,
[TPI_RETRY_SOURCE_CID] = 0x10,
[TPI_MAX_DATAGRAM_FRAME_SIZE] = 0x20,
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = 0xC37,
#endif
@ -114,6 +116,7 @@ const char * const lsquic_tpi2str[LAST_TPI + 1] =
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = "active_connection_id_limit",
[TPI_INITIAL_SOURCE_CID] = "initial_source_connection_id",
[TPI_RETRY_SOURCE_CID] = "retry_source_connection_id",
[TPI_MAX_DATAGRAM_FRAME_SIZE] = "max_datagram_frame_size",
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = "quantum_readiness",
#endif
@ -148,8 +151,8 @@ static const uint64_t max_vals[MAX_NUMERIC_TPI + 1] =
*/
[TPI_MAX_UDP_PAYLOAD_SIZE] = VINT_MAX_VALUE,
[TPI_ACK_DELAY_EXPONENT] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAMS_UNI] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAMS_BIDI] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAMS_UNI] = 1ull << 60,
[TPI_INIT_MAX_STREAMS_BIDI] = 1ull << 60,
[TPI_INIT_MAX_DATA] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = VINT_MAX_VALUE,
@ -160,6 +163,7 @@ static const uint64_t max_vals[MAX_NUMERIC_TPI + 1] =
[TPI_LOSS_BITS] = 1,
[TPI_MIN_ACK_DELAY] = (1u << 24) - 1u,
[TPI_TIMESTAMPS] = TS_WANT_THEM|TS_GENERATE_THEM,
[TPI_MAX_DATAGRAM_FRAME_SIZE] = VINT_MAX_VALUE,
};
@ -206,6 +210,23 @@ lsquic_tp_has_pref_ipv6 (const struct transport_params *params)
}
#if LSQUIC_TEST_QUANTUM_READINESS
#include <stdlib.h>
size_t
lsquic_tp_get_quantum_sz (void)
{
const char *str;
str = getenv("LSQUIC_QUANTUM_SZ");
if (str)
return atoi(str);
else
/* https://github.com/quicwg/base-drafts/wiki/Quantum-Readiness-test */
return 1200;
}
#endif
static size_t
update_cid_bits (unsigned bits[][3], enum transport_param_id tpi,
const lsquic_cid_t *cid)
@ -238,6 +259,9 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
enum transport_param_id tpi;
unsigned set;
unsigned bits[LAST_TPI + 1][3 /* ID, length, value */];
#if LSQUIC_TEST_QUANTUM_READINESS
const size_t quantum_sz = lsquic_tp_get_quantum_sz();
#endif
need = 0;
set = params->tp_set; /* Will turn bits off for default values */
@ -275,14 +299,14 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
}
}
#if LSQUIC_TEST_QUANTUM_READINESS
else if (set & (1 << TPI_QUANTUM_READINESS))
if (set & (1 << TPI_QUANTUM_READINESS))
{
bits[TPI_QUANTUM_READINESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_QUANTUM_READINESS]);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(QUANTUM_READY_SZ);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(quantum_sz);
need += (1 << bits[TPI_QUANTUM_READINESS][0])
+ (1 << bits[TPI_QUANTUM_READINESS][1])
+ QUANTUM_READY_SZ;
+ quantum_sz;
}
#endif
@ -375,6 +399,7 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
vint_write(p, 1 << bits[tpi][2], bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
@ -420,11 +445,11 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
break;
#if LSQUIC_TEST_QUANTUM_READINESS
case TPI_QUANTUM_READINESS:
vint_write(p, QUANTUM_READY_SZ,
bits[tpi][1], 1 << bits[tpi][1]);
LSQ_DEBUG("encoded %zd bytes of quantum readiness", quantum_sz);
vint_write(p, quantum_sz, bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
memset(p, 'Q', QUANTUM_READY_SZ);
p += QUANTUM_READY_SZ;
memset(p, 'Q', quantum_sz);
p += quantum_sz;
break;
#endif
}
@ -501,6 +526,7 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
switch (len)
{
case 1:
@ -734,6 +760,9 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
enum transport_param_id tpi;
unsigned set;
unsigned bits[LAST_TPI + 1][3 /* ID, length, value */];
#if LSQUIC_TEST_QUANTUM_READINESS
const size_t quantum_sz = lsquic_tp_get_quantum_sz();
#endif
need = 0;
set = params->tp_set; /* Will turn bits off for default values */
@ -788,10 +817,10 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
{
bits[TPI_QUANTUM_READINESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_QUANTUM_READINESS]);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(QUANTUM_READY_SZ);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(quantum_sz);
need += (1 << bits[TPI_QUANTUM_READINESS][0])
+ (1 << bits[TPI_QUANTUM_READINESS][1])
+ QUANTUM_READY_SZ;
+ quantum_sz;
}
#endif
@ -884,6 +913,7 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
vint_write(p, 1 << bits[tpi][2], bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
@ -931,11 +961,11 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
break;
#if LSQUIC_TEST_QUANTUM_READINESS
case TPI_QUANTUM_READINESS:
vint_write(p, QUANTUM_READY_SZ,
bits[tpi][1], 1 << bits[tpi][1]);
LSQ_DEBUG("encoded %zd bytes of quantum readiness", quantum_sz);
vint_write(p, quantum_sz, bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
memset(p, 'Q', QUANTUM_READY_SZ);
p += QUANTUM_READY_SZ;
memset(p, 'Q', quantum_sz);
p += quantum_sz;
break;
#endif
}
@ -1012,6 +1042,7 @@ lsquic_tp_decode_27 (const unsigned char *const buf, size_t bufsz,
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
switch (len)
{
case 1:

View file

@ -34,6 +34,7 @@ enum transport_param_id
*/
TPI_MIN_ACK_DELAY,
TPI_TIMESTAMPS,
TPI_MAX_DATAGRAM_FRAME_SIZE,
TPI_LOSS_BITS, MAX_NUMERIC_TPI = TPI_LOSS_BITS,
/*
@ -53,8 +54,6 @@ enum transport_param_id
#define LAST_TP_CID TPI_RETRY_SOURCE_CID
TPI_RETRY_SOURCE_CID,
#if LSQUIC_TEST_QUANTUM_READINESS
/* https://github.com/quicwg/base-drafts/wiki/Quantum-Readiness-test */
#define QUANTUM_READY_SZ 1200
TPI_QUANTUM_READINESS,
#endif
TPI_STATELESS_RESET_TOKEN, LAST_TPI = TPI_STATELESS_RESET_TOKEN
@ -180,4 +179,9 @@ extern const char * const lsquic_tpi2str[LAST_TPI + 1];
#define TS_WANT_THEM 1
#define TS_GENERATE_THEM 2
#if LSQUIC_TEST_QUANTUM_READINESS
size_t
lsquic_tp_get_quantum_sz (void);
#endif
#endif

View file

@ -21,6 +21,7 @@ static const unsigned char version_tags[N_LSQVER][4] =
[LSQVER_ID27] = { 0xFF, 0, 0, 27, },
[LSQVER_ID28] = { 0xFF, 0, 0, 28, },
[LSQVER_ID29] = { 0xFF, 0, 0, 29, },
[LSQVER_ID30] = { 0xFF, 0, 0, 30, },
[LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, },
};
@ -60,6 +61,7 @@ const char *const lsquic_ver2str[N_LSQVER] = {
[LSQVER_ID27] = "FF00001B",
[LSQVER_ID28] = "FF00001C",
[LSQVER_ID29] = "FF00001D",
[LSQVER_ID30] = "FF00001E",
[LSQVER_VERNEG] = "FAFAFAFA",
};