Release 4.0.0

This commit is contained in:
George Wang 2023-03-14 13:29:13 -04:00
parent b373fe5220
commit 79880b469a
47 changed files with 2459 additions and 606 deletions

View file

@ -1,3 +1,11 @@
2023-03-14
- 4.0.0
- Add support for QUICv2 (RFC9369).
- Add support for version negotiation (RFC9368).
- Retry packet for address validation.
- Improve handshake under high packet loss/corruption.
- Improve QUIC interop results
2023-01-26
- 3.3.1
- Fix blocked header encoding stream due to connection flow control congestion.

View file

@ -14,9 +14,31 @@ and HTTP/3 functionality for servers and clients. Most of the code in this
distribution is used in our own products: LiteSpeed Web Server, LiteSpeed ADC,
and OpenLiteSpeed.
Currently supported QUIC versions are v1, Internet-Draft versions 29, and 27;
Currently supported QUIC versions are v1, v2, Internet-Draft versions 29, and 27;
and the older "Google" QUIC versions Q043, Q046, an Q050.
Standard Compliance
-------------------
LiteSpeed QUIC is mostly compliant to the follow RFCs:
- [RFC 9000](https://www.rfc-editor.org/rfc/rfc9000)
- [RFC 9001](https://www.rfc-editor.org/rfc/rfc9001)
- [RFC 9002](https://www.rfc-editor.org/rfc/rfc9002)
- [RFC 9114](https://www.rfc-editor.org/rfc/rfc9114)
- [RFC 9204](https://www.rfc-editor.org/rfc/rfc9204)
QUIC protocol extensions
------------------------
The following QUIC protocol extensions are implemented:
- [QUIC Version 2](https://www.rfc-editor.org/authors/rfc9369.html)
- [Compatible Version Negotiation](https://datatracker.ietf.org/doc/draft-ietf-quic-version-negotiation/)
- [Datagrams](https://datatracker.ietf.org/doc/html/rfc9221)`
- [ACK Frequency](https://datatracker.ietf.org/doc/draft-ietf-quic-ack-frequency/)
- [Greasing the QUIC Bit](https://datatracker.ietf.org/doc/html/rfc9287)
Documentation
-------------
@ -136,7 +158,7 @@ docker build -t lsquic .
Then you can use the examples from the command line. For example:
```
sudo docker run -it --rm lsquic http_client -s www.google.com -p / -o version=h3-29
sudo docker run -it --rm lsquic http_client -s www.google.com -p / -o version=h3
sudo docker run -p 12345:12345/udp -v /path/to/certs:/mnt/certs -it --rm lsquic http_server -c www.example.com,/mnt/certs/chain,/mnt/certs/key
```

View file

@ -804,7 +804,7 @@ sport_init_server (struct service_port *sport, struct lsquic_engine *engine,
#ifndef WIN32
int flags;
#endif
SOCKOPT_VAL on;
SOCKOPT_VAL on = 1;
socklen_t socklen;
char addr_str[0x20];
@ -828,6 +828,14 @@ sport_init_server (struct service_port *sport, struct lsquic_engine *engine,
if (-1 == sockfd)
return -1;
if (AF_INET6 == sa_local->sa_family
&& setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
CHAR_CAST &on, sizeof(on)) == -1)
{
close(sockfd);
return -1;
}
if (0 != bind(sockfd, sa_local, socklen)) {
saved_errno = errno;
LSQ_WARN("bind failed: %s", strerror(errno));
@ -977,9 +985,21 @@ sport_init_server (struct service_port *sport, struct lsquic_engine *engine,
#if ECN_SUPPORTED
on = 1;
if (AF_INET == sa_local->sa_family)
s = setsockopt(sockfd, IPPROTO_IP, IP_RECVTOS, CHAR_CAST &on, sizeof(on));
{
s = setsockopt(sockfd, IPPROTO_IP, IP_RECVTOS,
CHAR_CAST &on, sizeof(on));
if (!s)
s = setsockopt(sockfd, IPPROTO_IP, IP_TOS,
CHAR_CAST &on, sizeof(on));
}
else
s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVTCLASS, CHAR_CAST &on, sizeof(on));
{
s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVTCLASS,
CHAR_CAST &on, sizeof(on));
if (!s)
s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_TCLASS,
CHAR_CAST &on, sizeof(on));
}
if (0 != s)
{
saved_errno = errno;
@ -987,6 +1007,8 @@ sport_init_server (struct service_port *sport, struct lsquic_engine *engine,
errno = saved_errno;
return -1;
}
LSQ_DEBUG("server ECN support is enabled.");
#endif
if (sport->sp_flags & SPORT_SET_SNDBUF)
@ -1177,11 +1199,21 @@ sport_init_client (struct service_port *sport, struct lsquic_engine *engine,
{
int on = 1;
if (AF_INET == sa_local->sa_family)
{
s = setsockopt(sockfd, IPPROTO_IP, IP_RECVTOS,
CHAR_CAST &on, sizeof(on));
CHAR_CAST &on, sizeof(on));
if (!s)
s = setsockopt(sockfd, IPPROTO_IP, IP_TOS,
CHAR_CAST &on, sizeof(on));
}
else
{
s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVTCLASS,
CHAR_CAST &on, sizeof(on));
CHAR_CAST &on, sizeof(on));
if (!s)
s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_TCLASS,
CHAR_CAST &on, sizeof(on));
}
if (0 != s)
{
saved_errno = errno;
@ -1189,6 +1221,7 @@ sport_init_client (struct service_port *sport, struct lsquic_engine *engine,
errno = saved_errno;
return -1;
}
LSQ_DEBUG("client ECN support is enabled.");
}
#endif
@ -1796,6 +1829,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
settings->es_spin = atoi(val);
return 0;
}
if (0 == strncmp(name, "srej", 4))
{
settings->es_support_srej = atoi(val);
return 0;
}
break;
case 7:
if (0 == strncmp(name, "version", 7))
@ -1933,6 +1971,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
settings->es_handshake_to = atoi(val);
return 0;
}
if (0 == strncmp(name, "support_srej", 12))
{
settings->es_support_srej = atoi(val);
return 0;
}
if (0 == strncmp(name, "delayed_acks", 12))
{
settings->es_delayed_acks = atoi(val);

View file

@ -24,9 +24,9 @@ copyright = u'2023, LiteSpeed Technologies'
author = u'LiteSpeed Technologies'
# The short X.Y version
version = u'3.3'
version = u'4.0'
# The full version, including alpha/beta/rc tags
release = u'3.3.1'
release = u'4.0.0'
# -- General configuration ---------------------------------------------------

View file

@ -25,9 +25,9 @@ struct sockaddr;
extern "C" {
#endif
#define LSQUIC_MAJOR_VERSION 3
#define LSQUIC_MINOR_VERSION 3
#define LSQUIC_PATCH_VERSION 1
#define LSQUIC_MAJOR_VERSION 4
#define LSQUIC_MINOR_VERSION 0
#define LSQUIC_PATCH_VERSION 0
/**
* Engine flags:
@ -82,12 +82,23 @@ enum lsquic_version
LSQVER_I001,
/**
* Special version to trigger version negotiation.
* [draft-ietf-quic-transport-11], Section 3.
* IETF QUIC v2.
*/
LSQVER_VERNEG,
LSQVER_I002,
N_LSQVER
/**
* Reserved version to trigger version negotiation.
* [rfc9000], Section 15.
*/
LSQVER_RESVED,
N_LSQVER,
/**
* The version 0x00000000 is reserved to represent version negotiation.
* [rfc9000], Section 15.
*/
LSQVER_VERNEG
};
/**
@ -103,19 +114,19 @@ enum lsquic_version
#define LSQUIC_FORCED_TCID0_VERSIONS ((1 << LSQVER_046)|(1 << LSQVER_050))
#define LSQUIC_EXPERIMENTAL_VERSIONS ( \
(1 << LSQVER_VERNEG))
(1 << LSQVER_RESVED))
#define LSQUIC_DEPRECATED_VERSIONS ((1 << LSQVER_ID27))
#define LSQUIC_GQUIC_HEADER_VERSIONS (1 << LSQVER_043)
#define LSQUIC_IETF_VERSIONS ((1 << LSQVER_ID27) \
| (1 << LSQVER_ID29) \
| (1 << LSQVER_I001) | (1 << LSQVER_VERNEG))
| (1 << LSQVER_ID29) | (1 << LSQVER_I001) \
| (1 << LSQVER_I002) | (1 << LSQVER_RESVED))
#define LSQUIC_IETF_DRAFT_VERSIONS ((1 << LSQVER_ID27) \
| (1 << LSQVER_ID29) \
| (1 << LSQVER_VERNEG))
| (1 << LSQVER_RESVED))
enum lsquic_hsk_status
{
@ -306,6 +317,10 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
#define LSQUIC_DF_STTL 86400
#define LSQUIC_DF_MAX_INCHOATE (1 * 1000 * 1000)
#define LSQUIC_DF_SUPPORT_SREJ_SERVER 0
#define LSQUIC_DF_SUPPORT_SREJ_CLIENT 0
/** Do not use NSTP by default */
#define LSQUIC_DF_SUPPORT_NSTP 0
/** TODO: IETF QUIC clients do not support push */
@ -355,6 +370,9 @@ typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)(
/** Allow migration by default */
#define LSQUIC_DF_ALLOW_MIGRATION 1
/** Default retry token duration. */ /* Do not set this value to zero. */
#define LSQUIC_DF_RETRY_TOKEN_DURATION 10
/** Use QL loss bits by default */
#define LSQUIC_DF_QL_BITS 2
@ -544,6 +562,17 @@ struct lsquic_engine_settings {
*/
unsigned es_max_inchoate;
/**
* Support SREJ: for client side, this means supporting server's SREJ
* responses (this does not work yet) and for server side, this means
* generating SREJ instead of REJ when appropriate.
*
* For IETF QUIC, this sending stateless retries when appropriate.
* The IETF client always supports stateless retries and knows how to
* handle them.
*/
int es_support_srej;
/**
* Setting this value to 0 means that
*
@ -863,6 +892,13 @@ struct lsquic_engine_settings {
*/
int es_allow_migration;
/**
* Amount of time, in seconds, after which the server token included in
* a stateless retry expires. If set to zero, the default value is
* used, which is @ref LSQUIC_DF_RETRY_TOKEN_DURATION
*/
unsigned es_retry_token_duration;
/**
* Use QL loss bits. Allowed values are:
* 0: Do not use loss bits

View file

@ -40,7 +40,7 @@ enum lsquic_conn_flags {
LSCONN_HASHED = (1 << 2),
LSCONN_MINI = (1 << 3), /* This is a mini connection */
LSCONN_IMMED_CLOSE = (1 << 4),
LSCONN_UNUSED_5 = (1 << 5),
LSCONN_PROMOTE_FAIL = (1 << 5),
LSCONN_HANDSHAKE_DONE = (1 << 6),
LSCONN_CLOSING = (1 << 7),
LSCONN_PEER_GOING_AWAY= (1 << 8),
@ -60,6 +60,8 @@ enum lsquic_conn_flags {
LSCONN_SERVER = (1 <<22),
LSCONN_IETF = (1 <<23),
LSCONN_RETRY_CONN = (1 <<24), /* This is a retry connection */
LSCONN_VER_UPDATED = (1 <<25),
LSCONN_NO_BL = (1 <<26),
};
/* A connection may have things to send and be closed at the same time.
@ -68,6 +70,7 @@ enum tick_st {
TICK_SEND = (1 << 0),
TICK_CLOSE = (1 << 1),
TICK_PROMOTE = (1 << 2), /* Promote mini connection to full connection */
TICK_RETRY = (1 << 3), /* Send retry packet -- used by mini conns */
};
#define TICK_QUIET 0

View file

@ -39,10 +39,10 @@ enum lsquic_version;
/* This enum maps to the list above */
enum enc_level
{
ENC_LEV_CLEAR,
ENC_LEV_EARLY,
ENC_LEV_INIT,
ENC_LEV_FORW,
ENC_LEV_0RTT,
ENC_LEV_HSK,
ENC_LEV_APP,
N_ENC_LEVS
};
@ -300,7 +300,9 @@ struct enc_session_funcs_iquic
void *(crypto_streams)[4],
const struct crypto_stream_if *,
const struct lsquic_cid *odcid,
const struct lsquic_cid *iscid);
const struct lsquic_cid *iscid,
const struct lsquic_cid *rscid
);
void
(*esfi_shake_stream)(enc_session_t *, struct lsquic_stream *,
@ -340,10 +342,7 @@ struct enc_session_funcs_gquic lsquic_enc_session_gquic_gquic_1;
LSQUIC_EXTERN const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1;
#define select_esf_common_by_ver(ver) ( \
ver == LSQVER_ID27 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_ID29 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_I001 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \
ver > LSQVER_050 ? &lsquic_enc_session_common_ietf_v1 : \
ver == LSQVER_050 ? &lsquic_enc_session_common_gquic_2 : \
&lsquic_enc_session_common_gquic_1 )
@ -376,4 +375,16 @@ lsquic_enc_sess_ietf_gen_quic_ctx (
const struct lsquic_engine_settings *settings,
enum lsquic_version version, unsigned char *buf, size_t bufsz);
struct enc_sess_iquic;
int
iquic_esfi_init_server_tp (struct enc_sess_iquic *const enc_sess);
int
iquic_esfi_switch_version (enc_session_t *enc_session_p, lsquic_cid_t *dcid,
int backup_keys);
int
iquic_esf_is_enc_level_ready (enc_session_t *enc_session_p,
enum enc_level level);
#endif

View file

@ -12,10 +12,10 @@
const char *const lsquic_enclev2str[] =
{
[ENC_LEV_EARLY] = "early",
[ENC_LEV_CLEAR] = "clear",
[ENC_LEV_INIT] = "initial",
[ENC_LEV_FORW] = "forw-secure",
[ENC_LEV_0RTT] = "0RTT",
[ENC_LEV_INIT] = "INIT",
[ENC_LEV_HSK] = "HSK",
[ENC_LEV_APP] = "APP",
};

View file

@ -58,13 +58,6 @@
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(enc_sess->esi_conn)
#include "lsquic_logger.h"
#define KEY_LABEL "quic key"
#define KEY_LABEL_SZ (sizeof(KEY_LABEL) - 1)
#define IV_LABEL "quic iv"
#define IV_LABEL_SZ (sizeof(IV_LABEL) - 1)
#define PN_LABEL "quic hp"
#define PN_LABEL_SZ (sizeof(PN_LABEL) - 1)
#define N_HSK_PAIRS (N_ENC_LEVS - 1)
static const struct alpn_map {
@ -74,7 +67,8 @@ static const struct alpn_map {
{ LSQVER_ID27, (unsigned char *) "\x05h3-27", },
{ LSQVER_ID29, (unsigned char *) "\x05h3-29", },
{ LSQVER_I001, (unsigned char *) "\x02h3", },
{ LSQVER_VERNEG, (unsigned char *) "\x02h3", },
{ LSQVER_I002, (unsigned char *) "\x02h3", },
{ LSQVER_RESVED, (unsigned char *) "\x02h3", },
};
struct enc_sess_iquic;
@ -159,11 +153,36 @@ struct crypto_ctx_pair
};
struct hsk_crypto
{
struct crypto_ctx_pair pair;
struct header_prot hp;
};
struct label_set
{
const char *key;
const char *iv;
const char *hp;
int key_len;
int iv_len;
int hp_len;
};
static struct label_set hkdf_labels[2] =
{
{ "quic key", "quic iv", "quic hp", 8, 7, 7 },
{ "quicv2 key", "quicv2 iv", "quicv2 hp", 10, 9, 9 }
};
/* [draft-ietf-quic-tls-12] Section 5.3.6 */
static int
init_crypto_ctx (struct crypto_ctx *crypto_ctx, const EVP_MD *md,
const EVP_AEAD *aead, const unsigned char *secret,
size_t secret_sz, enum evp_aead_direction_t dir)
size_t secret_sz, enum evp_aead_direction_t dir,
struct label_set *key_iv)
{
crypto_ctx->yk_key_sz = EVP_AEAD_key_length(aead);
crypto_ctx->yk_iv_sz = EVP_AEAD_nonce_length(aead);
@ -174,9 +193,9 @@ init_crypto_ctx (struct crypto_ctx *crypto_ctx, const EVP_MD *md,
return -1;
}
lsquic_qhkdf_expand(md, secret, secret_sz, KEY_LABEL, KEY_LABEL_SZ,
lsquic_qhkdf_expand(md, secret, secret_sz, key_iv->key, key_iv->key_len,
crypto_ctx->yk_key_buf, crypto_ctx->yk_key_sz);
lsquic_qhkdf_expand(md, secret, secret_sz, IV_LABEL, IV_LABEL_SZ,
lsquic_qhkdf_expand(md, secret, secret_sz, key_iv->iv, key_iv->iv_len,
crypto_ctx->yk_iv_buf, crypto_ctx->yk_iv_sz);
if (!EVP_AEAD_CTX_init_with_direction(&crypto_ctx->yk_aead_ctx, aead,
crypto_ctx->yk_key_buf, crypto_ctx->yk_key_sz, IQUIC_TAG_LEN, dir))
@ -216,13 +235,9 @@ struct enc_sess_iquic
struct header_prot esi_hp;
struct crypto_ctx_pair
esi_pairs[2];
/* These are used during handshake. There are three of them.
* esi_hsk_pairs and esi_hsk_hps are allocated and freed
* together.
*/
struct crypto_ctx_pair *
esi_hsk_pairs;
struct header_prot *esi_hsk_hps;
/* These are used during handshake. There are three of them. */
struct hsk_crypto *esi_hsk_crypto;
struct hsk_crypto *esi_vn_save;
lsquic_packno_t esi_max_packno[N_PNS];
lsquic_cid_t esi_odcid;
lsquic_cid_t esi_rscid; /* Retry SCID */
@ -250,6 +265,7 @@ struct enc_sess_iquic
ESI_MAX_PACKNO_HSK = ESI_MAX_PACKNO_INIT << PNS_HSK,
ESI_MAX_PACKNO_APP = ESI_MAX_PACKNO_INIT << PNS_APP,
ESI_HAVE_0RTT_TP = 1 << 20,
ESI_SWITCH_VER = 1 << 21,
} esi_flags;
enum enc_level esi_last_w;
unsigned esi_trasec_sz;
@ -496,6 +512,20 @@ strip_hp (struct enc_sess_iquic *enc_sess,
}
static void
set_tp_version_info (struct transport_params *params,
unsigned versions, enum lsquic_version ver)
{
assert(params->tp_version_cnt == 0);
params->tp_version_info[params->tp_version_cnt++] = ver;
if (versions & (1 << LSQVER_I002))
params->tp_version_info[params->tp_version_cnt++] = LSQVER_I002;
if (versions & (1 << LSQVER_I001))
params->tp_version_info[params->tp_version_cnt++] = LSQVER_I001;
params->tp_set |= 1 << TPI_VERSION_INFORMATION;
}
static int
gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
size_t bufsz)
@ -525,6 +555,11 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
params.tp_original_dest_cid = enc_sess->esi_odcid;
params.tp_set |= 1 << TPI_ORIGINAL_DEST_CID;
}
if (enc_sess->esi_flags & ESI_RSCID)
{
params.tp_retry_source_cid = enc_sess->esi_rscid;
params.tp_set |= 1 << TPI_RETRY_SOURCE_CID;
}
#if LSQUIC_PREFERRED_ADDR
char addr_buf[INET6_ADDRSTRLEN + 6 /* port */ + 1];
const char *s, *colon;
@ -661,6 +696,14 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
params.tp_set |= 1 << TPI_MAX_DATAGRAM_FRAME_SIZE;
}
if (enc_sess->esi_ver_neg && enc_sess->esi_ver_neg->vn_supp
!= (1u << enc_sess->esi_ver_neg->vn_ver))
set_tp_version_info(&params, enc_sess->esi_ver_neg->vn_supp,
enc_sess->esi_ver_neg->vn_ver);
else if (enc_sess->esi_conn->cn_flags & LSCONN_VER_UPDATED)
set_tp_version_info(&params, settings->es_versions,
enc_sess->esi_conn->cn_version);
len = (version == LSQVER_ID27 ? lsquic_tp_encode_27 : lsquic_tp_encode)(
&params, enc_sess->esi_flags & ESI_SERVER, buf, bufsz);
if (len >= 0)
@ -1018,7 +1061,8 @@ iquic_esfi_create_server (struct lsquic_engine_public *enpub,
void *(crypto_streams)[4],
const struct crypto_stream_if *cryst_if,
const struct lsquic_cid *odcid,
const struct lsquic_cid *iscid)
const struct lsquic_cid *iscid,
const struct lsquic_cid *rscid)
{
struct enc_sess_iquic *enc_sess;
@ -1042,6 +1086,11 @@ iquic_esfi_create_server (struct lsquic_engine_public *enpub,
enc_sess->esi_odcid = *odcid;
enc_sess->esi_flags |= ESI_ODCID;
}
if (rscid)
{
enc_sess->esi_rscid = *rscid;
enc_sess->esi_flags |= ESI_RSCID;
}
enc_sess->esi_iscid = *iscid;
enc_sess->esi_flags |= ESI_ISCID;
@ -1099,6 +1148,18 @@ log_crypto_pair (const struct enc_sess_iquic *enc_sess,
}
static const unsigned char *const lsquic_ver2salt[N_LSQVER] = {
[LSQVER_043] = HSK_SALT_PRE29,
[LSQVER_046] = HSK_SALT_PRE29,
[LSQVER_050] = HSK_SALT_PRE29,
[LSQVER_ID27] = HSK_SALT_PRE29,
[LSQVER_ID29] = HSK_SALT_PRE33,
[LSQVER_I001] = HSK_SALT,
[LSQVER_I002] = HSK_SALT_V2,
[LSQVER_RESVED] = HSK_SALT,
};
/* [draft-ietf-quic-tls-12] Section 5.3.2 */
static int
setup_handshake_keys (struct enc_sess_iquic *enc_sess, const lsquic_cid_t *cid)
@ -1118,30 +1179,20 @@ setup_handshake_keys (struct enc_sess_iquic *enc_sess, const lsquic_cid_t *cid)
unsigned char secret[2][SHA256_DIGEST_LENGTH]; /* client, server */
unsigned char key[2][EVP_MAX_KEY_LENGTH];
char hexbuf[EVP_MAX_MD_SIZE * 2 + 1];
struct label_set *labels;
if (!enc_sess->esi_hsk_pairs)
if (!enc_sess->esi_hsk_crypto)
{
enc_sess->esi_hsk_pairs = calloc(N_HSK_PAIRS,
sizeof(enc_sess->esi_hsk_pairs[0]));
enc_sess->esi_hsk_hps = calloc(N_HSK_PAIRS,
sizeof(enc_sess->esi_hsk_hps[0]));
if (!(enc_sess->esi_hsk_pairs && enc_sess->esi_hsk_hps))
{
free(enc_sess->esi_hsk_pairs);
free(enc_sess->esi_hsk_hps);
enc_sess->esi_hsk_crypto = calloc(N_HSK_PAIRS,
sizeof(enc_sess->esi_hsk_crypto[0]));
if (!enc_sess->esi_hsk_crypto)
return -1;
}
}
pair = &enc_sess->esi_hsk_pairs[ENC_LEV_CLEAR];
pair = &enc_sess->esi_hsk_crypto[ENC_LEV_INIT].pair;
pair->ykp_thresh = IQUIC_INVALID_PACKNO;
hp = &enc_sess->esi_hsk_hps[ENC_LEV_CLEAR];
hp = &enc_sess->esi_hsk_crypto[ENC_LEV_INIT].hp;
if (enc_sess->esi_conn->cn_version < LSQVER_ID29)
salt = HSK_SALT_PRE29;
else if (enc_sess->esi_conn->cn_version < LSQVER_I001)
salt = HSK_SALT_PRE33;
else
salt = HSK_SALT;
salt = lsquic_ver2salt[enc_sess->esi_conn->cn_version];
HKDF_extract(hsk_secret, &hsk_secret_sz, md, cid->idbuf, cid->len,
salt, HSK_SALT_SZ);
if (enc_sess->esi_flags & ESI_LOG_SECRETS)
@ -1163,21 +1214,22 @@ setup_handshake_keys (struct enc_sess_iquic *enc_sess, const lsquic_cid_t *cid)
HEXSTR(secret[1], sizeof(secret[1]), hexbuf));
}
labels = &hkdf_labels[enc_sess->esi_conn->cn_version == LSQVER_I002];
cliser = !!(enc_sess->esi_flags & ESI_SERVER);
if (0 != init_crypto_ctx(&pair->ykp_ctx[!cliser], md, aead, secret[0],
sizeof(secret[0]), rw2dir(!cliser)))
sizeof(secret[0]), rw2dir(!cliser), labels))
goto err;
if (0 != init_crypto_ctx(&pair->ykp_ctx[cliser], md, aead, secret[1],
sizeof(secret[1]), rw2dir(cliser)))
sizeof(secret[1]), rw2dir(cliser), labels))
goto err;
hp->hp_gen_mask = gen_hp_mask_aes;
hp->hp_enc_level = ENC_LEV_CLEAR;
hp->hp_enc_level = ENC_LEV_INIT;
key_len = EVP_AEAD_key_length(aead);
lsquic_qhkdf_expand(md, secret[!cliser], sizeof(secret[0]), PN_LABEL,
PN_LABEL_SZ, key[0], key_len);
lsquic_qhkdf_expand(md, secret[cliser], sizeof(secret[0]), PN_LABEL,
PN_LABEL_SZ, key[1], key_len);
lsquic_qhkdf_expand(md, secret[!cliser], sizeof(secret[0]), labels->hp,
labels->hp_len, key[0], key_len);
lsquic_qhkdf_expand(md, secret[cliser], sizeof(secret[0]), labels->hp,
labels->hp_len, key[1], key_len);
if (enc_sess->esi_flags & ESI_LOG_SECRETS)
{
log_crypto_pair(enc_sess, pair, "handshake");
@ -1217,30 +1269,43 @@ cleanup_hp (struct header_prot *hp)
}
static void
cleanup_hsk_crypto (struct hsk_crypto *c)
{
cleanup_crypto_ctx(&c->pair.ykp_ctx[0]);
cleanup_crypto_ctx(&c->pair.ykp_ctx[1]);
cleanup_hp(&c->hp);
}
static void
free_vn_save (struct enc_sess_iquic *enc_sess)
{
if (enc_sess->esi_vn_save)
{
cleanup_hsk_crypto(enc_sess->esi_vn_save);
free(enc_sess->esi_vn_save);
enc_sess->esi_vn_save = NULL;
}
}
static void
free_handshake_keys (struct enc_sess_iquic *enc_sess)
{
struct crypto_ctx_pair *pair;
unsigned i;
struct hsk_crypto *c;
if (enc_sess->esi_hsk_pairs)
if (enc_sess->esi_hsk_crypto)
{
assert(enc_sess->esi_hsk_hps);
for (pair = enc_sess->esi_hsk_pairs; pair <
enc_sess->esi_hsk_pairs + N_HSK_PAIRS; ++pair)
for (c = enc_sess->esi_hsk_crypto; c <
enc_sess->esi_hsk_crypto + N_HSK_PAIRS; ++c)
{
cleanup_crypto_ctx(&pair->ykp_ctx[0]);
cleanup_crypto_ctx(&pair->ykp_ctx[1]);
cleanup_hsk_crypto(c);
}
free(enc_sess->esi_hsk_pairs);
enc_sess->esi_hsk_pairs = NULL;
for (i = 0; i < N_HSK_PAIRS; ++i)
cleanup_hp(&enc_sess->esi_hsk_hps[i]);
free(enc_sess->esi_hsk_hps);
enc_sess->esi_hsk_hps = NULL;
free(enc_sess->esi_hsk_crypto);
enc_sess->esi_hsk_crypto = NULL;
}
else
assert(!enc_sess->esi_hsk_hps);
free_vn_save(enc_sess);
}
@ -1342,6 +1407,33 @@ iquic_esf_set_conn (enc_session_t *enc_session_p, struct lsquic_conn *lconn)
}
int
iquic_esfi_init_server_tp (struct enc_sess_iquic *const enc_sess)
{
unsigned char trans_params[sizeof(struct transport_params)
#if LSQUIC_TEST_QUANTUM_READINESS
+ 4 + lsquic_tp_get_quantum_sz()
#endif
];
int transpa_len;
char errbuf[ERR_ERROR_STRING_BUF_LEN];
transpa_len = gen_trans_params(enc_sess, trans_params,
sizeof(trans_params));
if (transpa_len < 0)
return -1;
if (1 != SSL_set_quic_transport_params(enc_sess->esi_ssl, trans_params,
transpa_len))
{
LSQ_ERROR("cannot set QUIC transport params: %s",
ERR_error_string(ERR_get_error(), errbuf));
return -1;
}
return 0;
}
static int
iquic_esfi_init_server (enc_session_t *enc_session_p)
{
@ -1349,16 +1441,7 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
struct network_path *path;
const struct alpn_map *am;
unsigned quic_ctx_idx;
int transpa_len;
SSL_CTX *ssl_ctx = NULL;
union {
char errbuf[ERR_ERROR_STRING_BUF_LEN];
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)
enc_sess->esi_alpn = enc_sess->esi_enpub->enp_alpn;
@ -1388,8 +1471,9 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
enc_sess->esi_ssl = SSL_new(ssl_ctx);
if (!enc_sess->esi_ssl)
{
char errbuf[ERR_ERROR_STRING_BUF_LEN];
LSQ_ERROR("cannot create SSL object: %s",
ERR_error_string(ERR_get_error(), u.errbuf));
ERR_error_string(ERR_get_error(), errbuf));
return -1;
}
#if BORINGSSL_API_VERSION >= 13
@ -1410,18 +1494,8 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
return -1;
}
transpa_len = gen_trans_params(enc_sess, u.trans_params,
sizeof(u.trans_params));
if (transpa_len < 0)
return -1;
if (1 != SSL_set_quic_transport_params(enc_sess->esi_ssl, u.trans_params,
transpa_len))
{
LSQ_ERROR("cannot set QUIC transport params: %s",
ERR_error_string(ERR_get_error(), u.errbuf));
return -1;
}
// if (iquic_esfi_init_server_tp(enc_sess) == -1)
// return -1;
SSL_clear_options(enc_sess->esi_ssl, SSL_OP_NO_TLSv1_3);
if (enc_sess->esi_enpub->enp_lookup_cert)
@ -1786,6 +1860,7 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
cidbuf[0]),
CID_BITS_B(cids[TP_CID_IDX(tpi)], cidbuf[1]));
}
enc_sess->esi_conn->cn_flags |= LSCONN_NO_BL;
return -1;
}
}
@ -2026,30 +2101,46 @@ iquic_esfi_destroy (enc_session_t *enc_session_p)
/* See [draft-ietf-quic-tls-14], Section 4 */
static const enum enc_level hety2el[] =
{
[HETY_NOT_SET] = ENC_LEV_FORW,
[HETY_SHORT] = ENC_LEV_APP,
[HETY_VERNEG] = 0,
[HETY_INITIAL] = ENC_LEV_CLEAR,
[HETY_INITIAL] = ENC_LEV_INIT,
[HETY_RETRY] = 0,
[HETY_HANDSHAKE] = ENC_LEV_INIT,
[HETY_0RTT] = ENC_LEV_EARLY,
[HETY_HANDSHAKE] = ENC_LEV_HSK,
[HETY_0RTT] = ENC_LEV_0RTT,
};
static const enum enc_level pns2enc_level[2][N_PNS] =
{
[0] = {
[PNS_INIT] = ENC_LEV_CLEAR,
[PNS_HSK] = ENC_LEV_INIT,
[PNS_APP] = ENC_LEV_EARLY,
[PNS_INIT] = ENC_LEV_INIT,
[PNS_HSK] = ENC_LEV_HSK,
[PNS_APP] = ENC_LEV_0RTT,
},
[1] = {
[PNS_INIT] = ENC_LEV_CLEAR,
[PNS_HSK] = ENC_LEV_INIT,
[PNS_APP] = ENC_LEV_FORW,
[PNS_INIT] = ENC_LEV_INIT,
[PNS_HSK] = ENC_LEV_HSK,
[PNS_APP] = ENC_LEV_APP,
},
};
int
iquic_esf_is_enc_level_ready (enc_session_t *enc_session_p,
enum enc_level level)
{
const struct enc_sess_iquic *enc_sess = enc_session_p;
const struct header_prot *hp;
if (level == ENC_LEV_APP)
hp = &enc_sess->esi_hp;
else if (enc_sess->esi_hsk_crypto)
hp = &enc_sess->esi_hsk_crypto[level].hp;
else
return 0;
return header_prot_inited(hp, 0);
}
static enum enc_packout
iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
const struct lsquic_engine_public *enpub, struct lsquic_conn *lconn_UNUSED,
@ -2075,17 +2166,17 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
pns = lsquic_packet_out_pns(packet_out);
enc_level = pns2enc_level[ enc_sess->esi_have_forw ][ pns ];
if (enc_level == ENC_LEV_FORW)
if (enc_level == ENC_LEV_APP)
{
pair = &enc_sess->esi_pairs[ enc_sess->esi_key_phase ];
crypto_ctx = &pair->ykp_ctx[ 1 ];
hp = &enc_sess->esi_hp;
}
else if (enc_sess->esi_hsk_pairs)
else if (enc_sess->esi_hsk_crypto)
{
pair = &enc_sess->esi_hsk_pairs[ enc_level ];
pair = &enc_sess->esi_hsk_crypto[ enc_level ].pair;
crypto_ctx = &pair->ykp_ctx[ 1 ];
hp = &enc_sess->esi_hsk_hps[ enc_level ];
hp = &enc_sess->esi_hsk_crypto[ enc_level ].hp;
}
else
{
@ -2146,7 +2237,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
dst_sz, &packno_off, &packno_len);
if (header_sz < 0)
goto err;
if (enc_level == ENC_LEV_FORW)
if (enc_level == ENC_LEV_APP)
dst[0] |= enc_sess->esi_key_phase << 2;
dst[0] &= enc_sess->esi_grease | packet_out->po_path->np_dcid.idbuf[0];
@ -2183,7 +2274,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
lsquic_packet_out_set_enc_level(packet_out, enc_level);
lsquic_packet_out_set_kp(packet_out, enc_sess->esi_key_phase);
if (enc_level == ENC_LEV_FORW && hp->hp_gen_mask != gen_hp_mask_chacha20)
if (enc_level == ENC_LEV_APP && hp->hp_gen_mask != gen_hp_mask_chacha20)
apply_hp_batch(enc_sess, hp, packet_out, packno_off, packno_len);
else
apply_hp_immediately(enc_sess, hp, packet_out, packno_off, packno_len);
@ -2218,9 +2309,14 @@ static struct ku_label
}
select_ku_label (const struct enc_sess_iquic *enc_sess)
select_ku_label (enum lsquic_version version)
{
return (struct ku_label) { "quic ku", 7, };
static struct ku_label labels[2] =
{
{ "quic ku", 7, },
{ "quicv2 ku", 9, }
};
return labels[version == LSQVER_I002];
}
@ -2267,10 +2363,10 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
}
enc_level = hety2el[packet_in->pi_header_type];
if (enc_level == ENC_LEV_FORW)
if (enc_level == ENC_LEV_APP)
hp = &enc_sess->esi_hp;
else if (enc_sess->esi_hsk_pairs)
hp = &enc_sess->esi_hsk_hps[ enc_level ];
else if (enc_sess->esi_hsk_crypto)
hp = &enc_sess->esi_hsk_crypto[ enc_level ].hp;
else
hp = NULL;
@ -2299,7 +2395,7 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
packet_in->pi_data + sample_off,
dst, packet_in->pi_header_sz, &packno_len);
if (enc_level == ENC_LEV_FORW)
if (enc_level == ENC_LEV_APP)
{
key_phase = (dst[0] & 0x04) > 0;
pair = &enc_sess->esi_pairs[ key_phase ];
@ -2314,7 +2410,7 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
|| packet_in->pi_packno
> enc_sess->esi_pairs[enc_sess->esi_key_phase].ykp_thresh)
{
const struct ku_label kl = select_ku_label(enc_sess);
const struct ku_label kl = select_ku_label(enc_sess->esi_conn->cn_version);
lsquic_qhkdf_expand(enc_sess->esi_md,
enc_sess->esi_traffic_secrets[0], enc_sess->esi_trasec_sz,
kl.str, kl.len, new_secret, enc_sess->esi_trasec_sz);
@ -2329,7 +2425,9 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
crypto_ctx->yk_flags = 0;
s = init_crypto_ctx(crypto_ctx, enc_sess->esi_md,
enc_sess->esi_aead, new_secret, enc_sess->esi_trasec_sz,
evp_aead_open);
evp_aead_open,
&hkdf_labels[enc_sess->esi_conn->cn_version == LSQVER_I002]
);
if (s != 0)
{
LSQ_ERROR("could not init open crypto ctx (key phase)");
@ -2352,8 +2450,8 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
else
{
key_phase = 0;
assert(enc_sess->esi_hsk_pairs);
pair = &enc_sess->esi_hsk_pairs[ enc_level ];
assert(enc_sess->esi_hsk_crypto);
pair = &enc_sess->esi_hsk_crypto[ enc_level ].pair;
crypto_ctx = &pair->ykp_ctx[ 0 ];
if (UNLIKELY(0 == (crypto_ctx->yk_flags & YK_INITED)))
{
@ -2411,7 +2509,7 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
if (dst[0] & 0x08)
packet_in->pi_flags |= PI_LOSS_BIT;
}
else if (dst[0] & (0x0C << (packet_in->pi_header_type == HETY_NOT_SET)))
else if (dst[0] & (0x0C << (packet_in->pi_header_type == HETY_SHORT)))
{
LSQ_DEBUG("reserved bits are not set to zero");
dec_packin = DECPI_VIOLATION;
@ -2422,7 +2520,7 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
{
LSQ_DEBUG("decryption in the new key phase %u successful, rotate "
"keys", key_phase);
const struct ku_label kl = select_ku_label(enc_sess);
const struct ku_label kl = select_ku_label(enc_sess->esi_conn->cn_version);
pair->ykp_thresh = packet_in->pi_packno;
pair->ykp_ctx[ 0 ] = crypto_ctx_buf;
memcpy(enc_sess->esi_traffic_secrets[ 0 ], new_secret,
@ -2434,7 +2532,8 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
enc_sess->esi_trasec_sz);
s = init_crypto_ctx(&pair->ykp_ctx[1], enc_sess->esi_md,
enc_sess->esi_aead, new_secret, enc_sess->esi_trasec_sz,
evp_aead_seal);
evp_aead_seal,
&hkdf_labels[enc_sess->esi_conn->cn_version == LSQVER_I002]);
if (s != 0)
{
LSQ_ERROR("could not init seal crypto ctx (key phase)");
@ -2457,8 +2556,6 @@ iquic_esf_decrypt_packet (enc_session_t *enc_session_p,
packet_in->pi_data = dst;
packet_in->pi_flags |= PI_OWN_DATA | PI_DECRYPTED
| (enc_level << PIBIT_ENC_LEV_SHIFT);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "decrypted packet %"PRIu64,
packet_in->pi_packno);
pns = lsquic_enclev2pns[enc_level];
if (packet_in->pi_packno > enc_sess->esi_max_packno[pns]
|| !(enc_sess->esi_flags & (ESI_MAX_PACKNO_INIT << pns)))
@ -2622,23 +2719,58 @@ iquic_esfi_set_iscid (enc_session_t *enc_session_p,
}
int
iquic_esfi_switch_version (enc_session_t *enc_session_p, lsquic_cid_t *dcid,
int backup_keys)
{
struct enc_sess_iquic *const enc_sess = enc_session_p;
enc_sess->esi_flags |= ESI_SWITCH_VER;
/* Free previous handshake keys */
assert(enc_sess->esi_hsk_crypto);
if (backup_keys)
{
if (!enc_sess->esi_vn_save)
{
enc_sess->esi_vn_save = calloc(1, sizeof(*enc_sess->esi_vn_save));
//skip error, non fatal
}
else
cleanup_hsk_crypto(enc_sess->esi_vn_save);
if (enc_sess->esi_vn_save)
*enc_sess->esi_vn_save = enc_sess->esi_hsk_crypto[ENC_LEV_INIT];
}
if (!enc_sess->esi_vn_save)
cleanup_hsk_crypto(&enc_sess->esi_hsk_crypto[ENC_LEV_INIT]);
else
memset(&enc_sess->esi_hsk_crypto[ENC_LEV_INIT], 0,
sizeof(enc_sess->esi_hsk_crypto[ENC_LEV_INIT]));
if (0 == setup_handshake_keys(enc_sess, dcid ? dcid : &enc_sess->esi_odcid))
{
LSQ_INFO("update handshake keys to version %s",
lsquic_ver2str[enc_sess->esi_conn->cn_version]);
return 0;
}
else
return -1;
}
static int
iquic_esfi_reset_dcid (enc_session_t *enc_session_p,
const lsquic_cid_t *old_dcid, const lsquic_cid_t *new_dcid)
{
struct enc_sess_iquic *const enc_sess = enc_session_p;
struct crypto_ctx_pair *pair;
enc_sess->esi_odcid = *old_dcid;
enc_sess->esi_rscid = *new_dcid;
enc_sess->esi_flags |= ESI_ODCID|ESI_RSCID|ESI_RETRY;
/* Free previous handshake keys */
assert(enc_sess->esi_hsk_pairs);
pair = &enc_sess->esi_hsk_pairs[ENC_LEV_CLEAR];
cleanup_crypto_ctx(&pair->ykp_ctx[0]);
cleanup_crypto_ctx(&pair->ykp_ctx[1]);
cleanup_hp(&enc_sess->esi_hsk_hps[ENC_LEV_CLEAR]);
assert(enc_sess->esi_hsk_crypto);
cleanup_hsk_crypto(&enc_sess->esi_hsk_crypto[ENC_LEV_INIT]);
if (0 == setup_handshake_keys(enc_sess, new_dcid))
{
@ -2812,7 +2944,7 @@ maybe_drop_SSL (struct enc_sess_iquic *enc_sess)
if ((enc_sess->esi_flags & (ESI_HSK_CONFIRMED|ESI_HANDSHAKE_OK))
== (ESI_HSK_CONFIRMED|ESI_HANDSHAKE_OK)
&& enc_sess->esi_ssl
&& lsquic_frab_list_empty(&enc_sess->esi_frals[ENC_LEV_FORW]))
&& lsquic_frab_list_empty(&enc_sess->esi_frals[ENC_LEV_APP]))
{
if ((enc_sess->esi_flags & (ESI_SERVER|ESI_WANT_TICKET))
!= ESI_WANT_TICKET)
@ -2841,10 +2973,10 @@ no_sess_ticket (enum alarm_id alarm_id, void *ctx,
typedef char enums_have_the_same_value[
(int) ssl_encryption_initial == (int) ENC_LEV_CLEAR &&
(int) ssl_encryption_early_data == (int) ENC_LEV_EARLY &&
(int) ssl_encryption_handshake == (int) ENC_LEV_INIT &&
(int) ssl_encryption_application == (int) ENC_LEV_FORW ? 1 : -1];
(int) ssl_encryption_initial == (int) ENC_LEV_INIT &&
(int) ssl_encryption_early_data == (int) ENC_LEV_0RTT &&
(int) ssl_encryption_handshake == (int) ENC_LEV_HSK &&
(int) ssl_encryption_application == (int) ENC_LEV_APP ? 1 : -1];
static int
set_secret (SSL *ssl, enum ssl_encryption_level_t level,
@ -2862,6 +2994,7 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level,
unsigned char key[EVP_MAX_KEY_LENGTH];
char errbuf[ERR_ERROR_STRING_BUF_LEN];
#define hexbuf errbuf
struct label_set *labels;
enc_sess = SSL_get_ex_data(ssl, s_idx);
if (!enc_sess)
@ -2894,11 +3027,11 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level,
secrets[0] = write_secret, secrets[1] = read_secret;
*/
if (enc_level < ENC_LEV_FORW)
if (enc_level < ENC_LEV_APP)
{
assert(enc_sess->esi_hsk_pairs);
pair = &enc_sess->esi_hsk_pairs[enc_level];
hp = &enc_sess->esi_hsk_hps[enc_level];
assert(enc_sess->esi_hsk_crypto);
pair = &enc_sess->esi_hsk_crypto[enc_level].pair;
hp = &enc_sess->esi_hsk_crypto[enc_level].hp;
}
else
{
@ -2924,8 +3057,9 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level,
else
LSQ_DEBUG("set %s for level %u", rw2str[rw], enc_level);
labels = &hkdf_labels[enc_sess->esi_conn->cn_version == LSQVER_I002];
if (0 != init_crypto_ctx(&pair->ykp_ctx[rw], crypa.md,
crypa.aead, secret, secret_len, rw2dir(rw)))
crypa.aead, secret, secret_len, rw2dir(rw), labels))
goto err;
if (pair->ykp_ctx[!rw].yk_flags & YK_INITED)
@ -2943,8 +3077,8 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level,
key_len = EVP_AEAD_key_length(crypa.aead);
if (hp->hp_gen_mask == gen_hp_mask_aes)
{
lsquic_qhkdf_expand(crypa.md, secret, secret_len, PN_LABEL, PN_LABEL_SZ,
key, key_len);
lsquic_qhkdf_expand(crypa.md, secret, secret_len, labels->hp,
labels->hp_len, key, key_len);
EVP_CIPHER_CTX_init(&hp->hp_u.cipher_ctx[rw]);
if (!EVP_EncryptInit_ex(&hp->hp_u.cipher_ctx[rw], crypa.hp, NULL, key, 0))
{
@ -2953,8 +3087,8 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level,
}
}
else
lsquic_qhkdf_expand(crypa.md, secret, secret_len, PN_LABEL, PN_LABEL_SZ,
hp->hp_u.buf[rw], key_len);
lsquic_qhkdf_expand(crypa.md, secret, secret_len, labels->hp,
labels->hp_len, hp->hp_u.buf[rw], key_len);
hp->hp_flags |= 1 << rw;
if (enc_sess->esi_flags & ESI_LOG_SECRETS)
@ -2965,7 +3099,7 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level,
key_len, hexbuf));
}
if (rw && enc_level == ENC_LEV_FORW)
if (rw && enc_level == ENC_LEV_APP)
enc_sess->esi_have_forw = 1;
return 1;
@ -3106,7 +3240,7 @@ chsk_ietf_on_new_stream (void *stream_if_ctx, struct lsquic_stream *stream)
enum enc_level enc_level;
enc_level = enc_sess->esi_cryst_if->csi_enc_level(stream);
if (enc_level == ENC_LEV_CLEAR)
if (enc_level == ENC_LEV_INIT)
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
LSQ_DEBUG("handshake stream created successfully");
@ -3344,6 +3478,9 @@ const unsigned char *const lsquic_retry_key_buf[N_IETF_RETRY_VERSIONS] =
/* [draft-ietf-quic-tls-33] Section 5.8 */
(unsigned char *)
"\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e",
/* [draft-draft-ietf-quic-v2] Section 3.3.3 */
(unsigned char *)
"\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92",
};
@ -3355,6 +3492,8 @@ const unsigned char *const lsquic_retry_nonce_buf[N_IETF_RETRY_VERSIONS] =
(unsigned char *) "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c",
/* [draft-ietf-quic-tls-33] Section 5.8 */
(unsigned char *) "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb",
/* [draft-draft-ietf-quic-v2] Section 3.3.3 */
(unsigned char *) "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a",
};

View file

@ -226,6 +226,7 @@ struct lsquic_engine
*/
ENG_CONNS_BY_ADDR
= (1 << 9), /* Connections are hashed by address */
ENG_FORCE_RETRY = (1 << 10), /* Will force retry packets to be sent */
#ifndef NDEBUG
ENG_COALESCE = (1 << 24), /* Packet coalescing is enabled */
#endif
@ -307,6 +308,7 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
{
settings->es_cfcw = LSQUIC_DF_CFCW_SERVER;
settings->es_sfcw = LSQUIC_DF_SFCW_SERVER;
settings->es_support_srej= LSQUIC_DF_SUPPORT_SREJ_SERVER;
settings->es_init_max_data
= LSQUIC_DF_INIT_MAX_DATA_SERVER;
settings->es_init_max_stream_data_bidi_remote
@ -325,6 +327,7 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
{
settings->es_cfcw = LSQUIC_DF_CFCW_CLIENT;
settings->es_sfcw = LSQUIC_DF_SFCW_CLIENT;
settings->es_support_srej= LSQUIC_DF_SUPPORT_SREJ_CLIENT;
settings->es_init_max_data
= LSQUIC_DF_INIT_MAX_DATA_CLIENT;
settings->es_init_max_stream_data_bidi_remote
@ -373,6 +376,7 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
settings->es_qpack_enc_max_size = LSQUIC_DF_QPACK_ENC_MAX_SIZE;
settings->es_qpack_enc_max_blocked = LSQUIC_DF_QPACK_ENC_MAX_BLOCKED;
settings->es_allow_migration = LSQUIC_DF_ALLOW_MIGRATION;
settings->es_retry_token_duration = LSQUIC_DF_RETRY_TOKEN_DURATION;
settings->es_ql_bits = LSQUIC_DF_QL_BITS;
settings->es_spin = LSQUIC_DF_SPIN;
settings->es_delayed_acks = LSQUIC_DF_DELAYED_ACKS;
@ -756,7 +760,7 @@ lsquic_engine_new (unsigned flags,
if (flags & LSENG_HTTP)
engine->pub.enp_flags |= ENPUB_HTTP;
#ifndef NDEBUG
#if !defined(NDEBUG) || LSQUIC_QIR
{
const char *env;
env = getenv("LSQUIC_LOSE_PACKETS_RE");
@ -775,6 +779,15 @@ lsquic_engine_new (unsigned flags,
env);
}
#endif
env = getenv("LSQUIC_FORCE_RETRY");
if (env)
{
if (atoi(env))
{
engine->flags |= ENG_FORCE_RETRY;
LSQ_WARN("will force retry");
}
}
env = getenv("LSQUIC_COALESCE");
if (env)
{
@ -908,7 +921,7 @@ destroy_conn (struct lsquic_engine *engine, struct lsquic_conn *conn,
struct purga_el *puel;
engine->mini_conns_count -= !!(conn->cn_flags & LSCONN_MINI);
if (engine->purga
if (engine->purga && !(conn->cn_flags & LSCONN_NO_BL)
/* Blacklist all CIDs except for promoted mini connections */
&& (conn->cn_flags & (LSCONN_MINI|LSCONN_PROMOTED))
!= (LSCONN_MINI|LSCONN_PROMOTED))
@ -1145,6 +1158,44 @@ new_full_conn_server (lsquic_engine_t *engine, lsquic_conn_t *mini_conn,
}
static void
remove_conn_from_hash (lsquic_engine_t *engine, lsquic_conn_t *conn);
static int
promote_mini_conn (lsquic_engine_t *engine, lsquic_conn_t *mini_conn,
lsquic_time_t now)
{
lsquic_conn_t *new_conn;
EV_LOG_CONN_EVENT(lsquic_conn_log_cid( mini_conn ),
"promote to full conn");
assert( mini_conn->cn_flags & LSCONN_MINI);
new_conn = new_full_conn_server(engine, mini_conn, now);
if (new_conn)
{
new_conn->cn_last_sent = engine->last_sent;
eng_hist_inc(&engine->history, now, sl_new_full_conns);
mini_conn->cn_flags |= LSCONN_PROMOTED;
assert(engine->curr_conn == mini_conn);
engine->curr_conn = new_conn;
if (mini_conn->cn_flags & LSCONN_ATTQ)
{
lsquic_attq_remove(engine->attq, mini_conn);
(void) engine_decref_conn(engine, mini_conn, LSCONN_ATTQ);
}
if (mini_conn->cn_flags & LSCONN_HASHED)
remove_conn_from_hash(engine, mini_conn);
lsquic_mh_insert(&engine->conns_tickable, new_conn,
new_conn->cn_last_ticked);
engine_incref_conn(new_conn, LSCONN_TICKABLE);
return 0;
}
return -1;
}
static enum
{
VER_NOT_SPECIFIED,
@ -1203,6 +1254,38 @@ schedule_req_packet (struct lsquic_engine *engine, enum packet_req_type type,
}
static void
schedule_mini_retry (struct lsquic_engine *engine, struct lsquic_conn *conn,
lsquic_time_t now)
{
const struct network_path *path;
assert(engine->pr_queue);
path = conn->cn_if->ci_get_path(conn, NULL);
if (!path)
{
LSQ_WARN("cannot fetch default path");
return;
}
assert(conn->cn_flags & LSCONN_IETF);
if (0 == lsquic_prq_new_req_ext(engine->pr_queue, PACKET_REQ_RETRY,
0 /* Only supporting retry on IETF mini conns for now */,
conn->cn_version, path->np_pack_size, &conn->cn_cid,
&path->np_dcid, path->np_peer_ctx, NP_LOCAL_SA(path),
NP_PEER_SA(path)
))
LSQ_DEBUGC("scheduled %s packet for mini conn %"CID_FMT,
lsquic_preqt2str[PACKET_REQ_RETRY],
CID_BITS(lsquic_conn_log_cid(conn)));
else
LSQ_DEBUGC("could not schedule %s packet for mini conn %"CID_FMT,
lsquic_preqt2str[PACKET_REQ_RETRY],
CID_BITS(lsquic_conn_log_cid(conn)));
}
static unsigned short
sa2port (const struct sockaddr *sa)
{
@ -1423,8 +1506,60 @@ find_or_create_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
if ((1 << version) & LSQUIC_IETF_VERSIONS)
{
lsquic_cid_t odcid;
if (engine->pub.enp_settings.es_support_srej
&& HETY_INITIAL == packet_in->pi_header_type)
{
/* XXX Need to handle condition when packets are reordered? */
const int has_token
= packet_in->pi_token && packet_in->pi_token_size;
const int need_retry =
!!(engine->flags & ENG_FORCE_RETRY);
switch ((need_retry << 1) | has_token)
{
case (0 << 1) | 0:
odcid.len = 0;
goto create_ietf_mini_conn;
case (1 << 1) | 1:
case (0 << 1) | 1:
odcid.len = 0;
if (0 == lsquic_tg_validate_token(engine->pub.enp_tokgen,
packet_in, sa_peer, &odcid))
goto create_ietf_mini_conn;
/* From [draft-ietf-quic-transport-30] Section 8.1.2:
" In response to processing an Initial containing a token that was
" provided in a Retry packet, a server cannot send another Retry
" packet; it can only refuse the connection or permit it to proceed.
*/
if (TOKEN_RETRY == packet_in->pi_data[packet_in->pi_token])
{
LSQ_DEBUGC("CID %"CID_FMT" has invalid Retry token",
CID_BITS(&packet_in->pi_conn_id));
return NULL;
}
/* According to the spec, we SHOULD send CONNECTION_CLOSE
* when receiving an invalid Retry token. We don't do it
* because it's a lot of code change for an event that is
* not likely to happen: a major browser copying the token
* incorrectly.
*/
break;
default:
assert(0);
/* fall-through */
case (1 << 1) | 0:
break;
}
schedule_req_packet(engine, PACKET_REQ_RETRY, packet_in, sa_local,
sa_peer, peer_ctx);
return NULL;
}
else
odcid.len = 0;
create_ietf_mini_conn:
conn = lsquic_mini_conn_ietf_new(&engine->pub, packet_in, version,
sa_peer->sa_family == AF_INET, NULL, packet_in_size);
sa_peer->sa_family == AF_INET, odcid.len ? &odcid : NULL,
packet_in_size);
}
else
{
@ -1621,6 +1756,12 @@ process_packet_in (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
#endif
QLOG_PACKET_RX(lsquic_conn_log_cid(conn), packet_in, packet_in_data, packet_in_size);
lsquic_packet_in_put(&engine->pub.enp_mm, packet_in);
if ((conn->cn_flags & (LSCONN_MINI | LSCONN_HANDSHAKE_DONE))
== (LSCONN_MINI | LSCONN_HANDSHAKE_DONE))
{
if (promote_mini_conn(engine, conn, lsquic_time_now()) == -1)
conn->cn_flags |= LSCONN_PROMOTE_FAIL;
}
return 0;
}
@ -1900,7 +2041,8 @@ engine_incref_conn (lsquic_conn_t *conn, enum lsquic_conn_flags flag)
assert(flag & CONN_REF_FLAGS);
assert(!(conn->cn_flags & flag));
conn->cn_flags |= flag;
LSQ_DEBUGC("incref conn %"CID_FMT", '%s' -> '%s'",
LSQ_DEBUGC("incref %sconn %"CID_FMT", '%s' -> '%s'",
(conn->cn_flags &LSCONN_MINI) ? "mini-" : "",
CID_BITS(lsquic_conn_log_cid(conn)),
(refflags2str(conn->cn_flags & ~flag, str[0]), str[0]),
(refflags2str(conn->cn_flags, str[1]), str[1]));
@ -1920,7 +2062,8 @@ engine_decref_conn (lsquic_engine_t *engine, lsquic_conn_t *conn,
assert(0 == (conn->cn_flags & LSCONN_HASHED));
#endif
conn->cn_flags &= ~flags;
LSQ_DEBUGC("decref conn %"CID_FMT", '%s' -> '%s'",
LSQ_DEBUGC("decref %sconn %"CID_FMT", '%s' -> '%s'",
(conn->cn_flags &LSCONN_MINI) ? "mini-" : "",
CID_BITS(lsquic_conn_log_cid(conn)),
(refflags2str(conn->cn_flags | flags, str[0]), str[0]),
(refflags2str(conn->cn_flags, str[1]), str[1]));
@ -2296,7 +2439,7 @@ lose_matching_packets (const lsquic_engine_t *engine, struct out_batch *batch,
for (i = 0; i < n; ++i)
{
snprintf(packno_str, sizeof(packno_str), "%"PRIu64,
snprintf(packno_str, sizeof(packno_str), "#%"PRIu64,
batch->packets[i]->po_packno);
if (0 == regexec(&engine->lose_packets_re, packno_str, 0, NULL, 0))
{
@ -2662,7 +2805,7 @@ send_packets_out (struct lsquic_engine *engine,
goto end_for;
}
}
LSQ_DEBUGC("batched packet %"PRIu64" for connection %"CID_FMT,
LSQ_DEBUGC("batched packet #%"PRIu64" for connection %"CID_FMT,
packet_out->po_packno, CID_BITS(lsquic_conn_log_cid(conn)));
if (packet_out->po_flags & PO_ENCRYPTED)
{
@ -2928,6 +3071,11 @@ process_connections (lsquic_engine_t *engine, conn_iter_f next_conn,
}
tick_st |= TICK_CLOSE; /* Destroy mini connection */
}
if (tick_st & TICK_RETRY)
{
assert(conn->cn_flags & LSCONN_MINI);
schedule_mini_retry(engine, conn, now);
}
if (tick_st & TICK_SEND)
{
if (!(conn->cn_flags & LSCONN_HAS_OUTGOING))

View file

@ -57,23 +57,23 @@ lsquic_ev_log_packet_in (const lsquic_cid_t *cid,
switch (packet_in->pi_flags & (PI_FROM_MINI|PI_GQUIC))
{
case PI_FROM_MINI|PI_GQUIC:
LCID("packet in: %"PRIu64" (from mini)", packet_in->pi_packno);
LCID("RX packet #%"PRIu64" (mini)", packet_in->pi_packno);
break;
case PI_FROM_MINI:
LCID("packet in: %"PRIu64" (from mini), type: %s, ecn: %u",
LCID("RX packet #%"PRIu64" %s (mini), ecn: %u",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type],
lsquic_packet_in_ecn(packet_in));
break;
case PI_GQUIC:
packet_sz = packet_in->pi_data_sz
+ (packet_in->pi_flags & PI_DECRYPTED ? GQUIC_PACKET_HASH_SZ : 0);
LCID("packet in: %"PRIu64", size: %u", packet_in->pi_packno, packet_sz);
LCID("RX packet #%"PRIu64", size: %u", packet_in->pi_packno, packet_sz);
break;
default:
packet_sz = packet_in->pi_data_sz
+ (packet_in->pi_flags & PI_DECRYPTED ? IQUIC_TAG_LEN : 0);
if (packet_in->pi_flags & PI_LOG_QL_BITS)
LCID("packet in: %"PRIu64", type: %s, size: %u; ecn: %u, spin: %d; "
LCID("RX packet #%"PRIu64" %s, size: %u; ecn: %u, spin: %d; "
"path: %hhu; Q: %d; L: %d",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type],
packet_sz,
@ -83,7 +83,7 @@ lsquic_ev_log_packet_in (const lsquic_cid_t *cid,
((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; "
LCID("RX packet #%"PRIu64" %s, size: %u; ecn: %u, spin: %d; "
"path: %hhu",
packet_in->pi_packno, lsquic_hety2str[packet_in->pi_header_type],
packet_sz,
@ -102,7 +102,7 @@ lsquic_ev_log_ack_frame_in (const lsquic_cid_t *cid,
char buf[MAX_ACKI_STR_SZ];
lsquic_acki2str(acki, buf, sizeof(buf));
LCID("ACK frame in: %s", buf);
LCID("RX ACK frame: %s", buf);
}
@ -110,7 +110,7 @@ void
lsquic_ev_log_stream_frame_in (const lsquic_cid_t *cid,
const struct stream_frame *frame)
{
LCID("STREAM frame in: stream %"PRIu64"; offset %"PRIu64"; size %"PRIu16
LCID("RX STREAM frame: stream %"PRIu64"; offset %"PRIu64"; size %"PRIu16
"; fin: %d", frame->stream_id, frame->data_frame.df_offset,
frame->data_frame.df_size, (int) frame->data_frame.df_fin);
}
@ -120,7 +120,7 @@ void
lsquic_ev_log_crypto_frame_in (const lsquic_cid_t *cid,
const struct stream_frame *frame, unsigned enc_level)
{
LCID("CRYPTO frame in: level %u; offset %"PRIu64"; size %"PRIu16,
LCID("RX CRYPTO frame: level %u; offset %"PRIu64"; size %"PRIu16,
enc_level, frame->data_frame.df_offset, frame->data_frame.df_size);
}
@ -129,7 +129,7 @@ void
lsquic_ev_log_stop_waiting_frame_in (const lsquic_cid_t *cid,
lsquic_packno_t least)
{
LCID("STOP_WAITING frame in: least unacked packno %"PRIu64, least);
LCID("RX STOP_WAITING frame: least unacked packno %"PRIu64, least);
}
@ -137,7 +137,7 @@ void
lsquic_ev_log_window_update_frame_in (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id, uint64_t offset)
{
LCID("WINDOW_UPDATE frame in: stream %"PRIu64"; offset %"PRIu64,
LCID("RX WINDOW_UPDATE frame: stream %"PRIu64"; offset %"PRIu64,
stream_id, offset);
}
@ -146,7 +146,7 @@ void
lsquic_ev_log_blocked_frame_in (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id)
{
LCID("BLOCKED frame in: stream %"PRIu64, stream_id);
LCID("RX BLOCKED frame: stream %"PRIu64, stream_id);
}
@ -154,7 +154,7 @@ void
lsquic_ev_log_connection_close_frame_in (const lsquic_cid_t *cid,
uint64_t error_code, int reason_len, const char *reason)
{
LCID("CONNECTION_CLOSE frame in: error code %"PRIu64", reason: %.*s",
LCID("RX CONNECTION_CLOSE frame: error code %"PRIu64", reason: %.*s",
error_code, reason_len, reason);
}
@ -163,7 +163,7 @@ void
lsquic_ev_log_goaway_frame_in (const lsquic_cid_t *cid, uint32_t error_code,
lsquic_stream_id_t stream_id, int reason_len, const char *reason)
{
LCID("GOAWAY frame in: error code %"PRIu32", stream %"PRIu64
LCID("RX GOAWAY frame: error code %"PRIu32", stream %"PRIu64
", reason: %.*s", error_code, stream_id, reason_len, reason);
}
@ -172,7 +172,7 @@ void
lsquic_ev_log_rst_stream_frame_in (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id, uint64_t offset, uint64_t error_code)
{
LCID("RST_STREAM frame in: error code %"PRIu64", stream %"PRIu64
LCID("RX RST_STREAM frame: error code %"PRIu64", stream %"PRIu64
", offset: %"PRIu64, error_code, stream_id, offset);
}
@ -181,7 +181,7 @@ void
lsquic_ev_log_stop_sending_frame_in (const lsquic_cid_t *cid,
lsquic_stream_id_t stream_id, uint64_t error_code)
{
LCID("STOP_SENDING frame in: error code %"PRIu64", stream %"PRIu64,
LCID("RX STOP_SENDING frame: error code %"PRIu64", stream %"PRIu64,
error_code, stream_id);
}
@ -189,14 +189,14 @@ lsquic_ev_log_stop_sending_frame_in (const lsquic_cid_t *cid,
void
lsquic_ev_log_padding_frame_in (const lsquic_cid_t *cid, size_t len)
{
LCID("PADDING frame in of %zd bytes", len);
LCID("RX PADDING frame of %zd bytes", len);
}
void
lsquic_ev_log_ping_frame_in (const lsquic_cid_t *cid)
{
LCID("PING frame in");
LCID("RX PING frame");
}
@ -204,8 +204,9 @@ void
lsquic_ev_log_packet_created (const lsquic_cid_t *cid,
const struct lsquic_packet_out *packet_out)
{
LCID("created packet %"PRIu64"; flags: version=%d, nonce=%d, conn_id=%d",
LCID("created packet #%"PRIu64" %s; flags: version=%d, nonce=%d, conn_id=%d",
packet_out->po_packno,
lsquic_hety2str[packet_out->po_header_type],
!!(packet_out->po_flags & PO_VERSION),
!!(packet_out->po_flags & PO_NONCE),
!!(packet_out->po_flags & PO_CONN_ID));
@ -218,34 +219,34 @@ lsquic_ev_log_packet_sent (const lsquic_cid_t *cid,
{
char frames[lsquic_frame_types_str_sz];
if (lsquic_packet_out_verneg(packet_out))
LCID("sent version negotiation packet, size %hu",
LCID("TX version negotiation packet, size %hu",
packet_out->po_data_sz);
else if (lsquic_packet_out_retry(packet_out))
LCID("sent stateless retry packet, size %hu", packet_out->po_data_sz);
LCID("TX stateless retry packet, size %hu", packet_out->po_data_sz);
else if (lsquic_packet_out_pubres(packet_out))
LCID("sent public reset packet, size %hu", packet_out->po_data_sz);
LCID("TX public reset packet, size %hu", packet_out->po_data_sz);
else if (packet_out->po_lflags & POL_GQUIC)
LCID("sent packet %"PRIu64", size %hu, frame types: %s",
packet_out->po_packno, packet_out->po_enc_data_sz,
LCID("TX packet #%"PRIu64" (%s), size %hu",
packet_out->po_packno,
/* 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));
packet_out->po_frame_types),
packet_out->po_enc_data_sz);
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; "
LCID("TX packet #%"PRIu64" %s (%s), size %hu, "
"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),
packet_out->po_enc_data_sz,
lsquic_packet_out_ecn(packet_out),
/* spin bit value is only valid for short packet headers */
lsquic_packet_out_spin_bit(packet_out),
@ -255,17 +256,16 @@ lsquic_ev_log_packet_sent (const lsquic_cid_t *cid,
lsquic_packet_out_square_bit(packet_out),
lsquic_packet_out_loss_bit(packet_out));
else
LCID("sent packet %"PRIu64", type %s, crypto: %s, size %hu, frame "
"types: %s, ecn: %u, spin: %d; kp: %u, path: %hhu, flags: %u",
LCID("TX packet #%"PRIu64" %s (%s), size %hu, "
"ecn: %u, spin: %d; kp: %u, path: %hhu, flags: %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),
packet_out->po_enc_data_sz,
lsquic_packet_out_ecn(packet_out),
/* spin bit value is only valid for short packet headers */
lsquic_packet_out_spin_bit(packet_out),
@ -280,13 +280,14 @@ lsquic_ev_log_packet_not_sent (const lsquic_cid_t *cid,
const struct lsquic_packet_out *packet_out)
{
char frames[lsquic_frame_types_str_sz];
LCID("unsent packet %"PRIu64", size %hu, frame types: %s",
packet_out->po_packno, packet_out->po_enc_data_sz,
LCID("unsent packet #%"PRIu64" %s, size %hu",
packet_out->po_packno,
/* 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));
packet_out->po_frame_types),
packet_out->po_enc_data_sz);
}

View file

@ -1608,7 +1608,7 @@ process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
enc_level = lsquic_packet_in_enc_level(packet_in);
if (!is_handshake_stream_id(conn, stream_frame->stream_id)
&& enc_level == ENC_LEV_CLEAR)
&& enc_level == ENC_LEV_INIT)
{
lsquic_malo_put(stream_frame);
ABORT_ERROR("received unencrypted data for stream %"PRIu64,

View file

@ -88,6 +88,7 @@
#define MAX_ANY_PACKETS_SINCE_LAST_ACK 20
#define ACK_TIMEOUT (TP_DEF_MAX_ACK_DELAY * 1000)
#define INITIAL_CHAL_TIMEOUT 250000
#define HSK_PING_TIMEOUT 200000
/* Retire original CID after this much time has elapsed: */
#define RET_CID_TIMEOUT 2000000
@ -191,6 +192,7 @@ enum send
SEND_MAX_STREAMS_BIDI = SEND_MAX_STREAMS + SD_BIDI,
SEND_MAX_STREAMS_UNI = SEND_MAX_STREAMS + SD_UNI,
SEND_STOP_SENDING,
SEND_NEW_TOKEN,
SEND_HANDSHAKE_DONE,
SEND_ACK_FREQUENCY,
N_SEND
@ -220,6 +222,7 @@ enum send_flags
SF_SEND_MAX_STREAMS_BIDI = 1 << SEND_MAX_STREAMS_BIDI,
SF_SEND_MAX_STREAMS_UNI = 1 << SEND_MAX_STREAMS_UNI,
SF_SEND_STOP_SENDING = 1 << SEND_STOP_SENDING,
SF_SEND_NEW_TOKEN = 1 << SEND_NEW_TOKEN,
SF_SEND_HANDSHAKE_DONE = 1 << SEND_HANDSHAKE_DONE,
SF_SEND_ACK_FREQUENCY = 1 << SEND_ACK_FREQUENCY,
};
@ -1356,7 +1359,10 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
assert(versions);
versions &= LSQUIC_IETF_VERSIONS;
ver = highest_bit_set(versions);
if (versions & (1 << LSQVER_I001))
ver = LSQVER_I001;
else
ver = highest_bit_set(versions);
if (sess_resume)
{
sess_resume_version = lsquic_sess_resume_version(sess_resume, sess_resume_sz);
@ -1432,13 +1438,13 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
if (!conn->ifc_conn.cn_enc_session)
goto err2;
conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR] = lsquic_stream_new_crypto(
ENC_LEV_CLEAR, &conn->ifc_pub, &lsquic_cry_sm_if,
conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT] = lsquic_stream_new_crypto(
ENC_LEV_INIT, &conn->ifc_pub, &lsquic_cry_sm_if,
conn->ifc_conn.cn_enc_session,
SCF_IETF|SCF_DI_AUTOSWITCH|SCF_CALL_ON_NEW|SCF_CRITICAL);
if (!conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR])
if (!conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT])
goto err3;
if (!lsquic_stream_get_ctx(conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR]))
if (!lsquic_stream_get_ctx(conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT]))
goto err4;
conn->ifc_pub.packet_out_malo =
lsquic_malo_create(sizeof(struct lsquic_packet_out));
@ -1467,7 +1473,7 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
return &conn->ifc_conn;
err4:
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR]);
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT]);
err3:
conn->ifc_conn.cn_esf.i->esfi_destroy(conn->ifc_conn.cn_enc_session);
err2:
@ -1544,6 +1550,8 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
goto err1;
if (imc->imc_flags & IMC_IGNORE_INIT)
conn->ifc_flags |= IFC_IGNORE_INIT;
if (enpub->enp_settings.es_support_srej)
conn->ifc_send_flags |= SF_SEND_NEW_TOKEN;
conn->ifc_paths[0].cop_path = imc->imc_path;
conn->ifc_paths[0].cop_flags = COP_VALIDATED|COP_INITIALIZED|COP_ALLOW_MTU_PADDING;
@ -1603,6 +1611,11 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
conn->ifc_process_incoming_packet = process_incoming_packet_fast;
conn->ifc_send_ctl.sc_cur_packno = imc->imc_next_packno - 1;
conn->ifc_incoming_ecn = imc->imc_incoming_ecn;
conn->ifc_pub.rtt_stats = imc->imc_rtt_stats;
conn->ifc_last_live_update = now;
lsquic_send_ctl_begin_optack_detection(&conn->ifc_send_ctl);
for (pns = 0; pns < IMICO_N_PNS; ++pns)
@ -1675,15 +1688,6 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
{
conn->ifc_ecn_counts_in[pns][i] = imc->imc_ecn_counts_in[pns][i];
}
conn->ifc_incoming_ecn = imc->imc_incoming_ecn;
conn->ifc_pub.rtt_stats = imc->imc_rtt_stats;
lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_RET_CIDS,
ret_cids_alarm_expired, conn);
lsquic_alarmset_set(&conn->ifc_alset, AL_RET_CIDS,
now + RET_CID_TIMEOUT);
conn->ifc_last_live_update = now;
if (0 != handshake_ok(&conn->ifc_conn))
goto err3;
@ -1693,10 +1697,10 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
conn->ifc_enpub->enp_stream_if_ctx, &conn->ifc_conn);
conn->ifc_idle_to = conn->ifc_settings->es_idle_timeout * 1000000;
conn->ifc_created = imc->imc_created;
conn->ifc_created = now;
if (conn->ifc_idle_to)
lsquic_alarmset_set(&conn->ifc_alset, AL_IDLE,
imc->imc_created + conn->ifc_idle_to);
now + conn->ifc_idle_to);
while ((packet_in = TAILQ_FIRST(&imc->imc_app_packets)))
{
TAILQ_REMOVE(&imc->imc_app_packets, packet_in, pi_next);
@ -1901,6 +1905,16 @@ generate_ack_frame_for_pns (struct ietf_full_conn *conn,
packet_out->po_data + packet_out->po_data_sz, w);
lsquic_send_ctl_scheduled_ack(&conn->ifc_send_ctl, pns,
packet_out->po_ack2ed);
// NOTE: Add a PING frame after ACK frame before HANDSHAKE_DONE, in a hacky way
if (!(conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
&& packet_out->po_data_sz + w < packet_out->po_n_alloc)
{
LSQ_DEBUG("add a PING frame before HANDSHAKE_DONE");
*(packet_out->po_data + packet_out->po_data_sz + w) = '\x01';
++w;
}
packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK;
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_ACK, packet_out->po_data_sz, w))
@ -1914,7 +1928,8 @@ generate_ack_frame_for_pns (struct ietf_full_conn *conn,
conn->ifc_flags |= IFC_ACK_HAD_MISS;
else
conn->ifc_flags &= ~IFC_ACK_HAD_MISS;
LSQ_DEBUG("Put %d bytes of ACK frame into packet on outgoing queue", w);
LSQ_DEBUG("Put %d bytes of ACK frame into packet %" PRIu64
" on outgoing queue", w, packet_out->po_packno);
if (conn->ifc_n_cons_unretx >= conn->ifc_ping_unretx_thresh &&
!lsquic_send_ctl_have_outgoing_retx_frames(&conn->ifc_send_ctl))
{
@ -2034,6 +2049,58 @@ generate_max_data_frame (struct ietf_full_conn *conn)
}
static void
generate_new_token_frame (struct ietf_full_conn *conn, lsquic_time_t now)
{
struct lsquic_packet_out *packet_out;
const struct network_path *path;
ssize_t token_sz;
size_t need;
int w;
unsigned char token_buf[MAX_RETRY_TOKEN_LEN];
path = &conn->ifc_paths[conn->ifc_cur_path_id].cop_path;
token_sz = lsquic_tg_token_size(conn->ifc_enpub->enp_tokgen, TOKEN_RESUME,
NP_PEER_SA(path));
need = conn->ifc_conn.cn_pf->pf_new_token_frame_size(token_sz);
packet_out = get_writeable_packet(conn, need);
if (!packet_out)
return;
token_sz = lsquic_tg_generate_resume(conn->ifc_enpub->enp_tokgen, token_buf,
sizeof(token_buf), NP_PEER_SA(path));
if (token_sz < 0)
{
LSQ_WARN("could not generate resume token");
conn->ifc_send_flags &= ~SF_SEND_NEW_TOKEN; /* Let's not try again */
return;
}
w = conn->ifc_conn.cn_pf->pf_gen_new_token_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), token_buf, token_sz);
if (w < 0)
{
ABORT_ERROR("generating NEW_TOKEN frame failed: %d", errno);
return;
}
LSQ_DEBUG("generated %d-byte NEW_TOKEN frame", w);
EV_LOG_GENERATED_NEW_TOKEN_FRAME(LSQUIC_LOG_CONN_ID, conn->ifc_conn.cn_pf,
packet_out->po_data + packet_out->po_data_sz, w);
if (0 != lsquic_packet_out_add_frame(packet_out, conn->ifc_pub.mm, 0,
QUIC_FRAME_NEW_TOKEN, packet_out->po_data_sz, w))
{
ABORT_ERROR("adding frame to packet failed: %d", errno);
return;
}
packet_out->po_frame_types |= QUIC_FTBIT_NEW_TOKEN;
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, w);
conn->ifc_send_flags &= ~SF_SEND_NEW_TOKEN;
(void) token_sz;
}
static int
can_issue_cids (const struct ietf_full_conn *conn)
{
@ -3799,6 +3866,29 @@ handshake_ok (struct lsquic_conn *lconn)
else
dce->de_flags = DE_ASSIGNED;
if (!(conn->ifc_flags & IFC_SERVER)
&& (params->tp_set & (1 << TPI_VERSION_INFORMATION)))
{
LSQ_DEBUG("server chosen version %s",
lsquic_ver2str[params->tp_chosen_version]);
if (((1 << params->tp_chosen_version)
& conn->ifc_settings->es_versions) == 0)
{
ABORT_QUIETLY(0, TEC_VERSION_NEGOTIATION_ERROR,
"server chosen version %s is not supported",
lsquic_ver2str[params->tp_chosen_version]
);
return -1;
}
// if (conn->ifc_conn.cn_version != params->tp_chosen_version)
// {
// LSQ_DEBUG("version negociation: switch version from %s to %s",
// lsquic_ver2str[conn->ifc_conn.cn_version],
// lsquic_ver2str[params->tp_chosen_version]);
// conn->ifc_conn.cn_version = params->tp_chosen_version;
// }
}
LSQ_INFO("applied peer transport parameters");
if ((conn->ifc_flags & (IFC_HTTP|IFC_HTTP_INITED)) == IFC_HTTP)
@ -4413,7 +4503,8 @@ generate_connection_close_packet (struct ietf_full_conn *conn)
lsquic_send_ctl_incr_pack_sz(&conn->ifc_send_ctl, packet_out, sz);
packet_out->po_frame_types |= 1 << QUIC_FRAME_CONNECTION_CLOSE;
conn->ifc_mflags |= MF_CONN_CLOSE_PACK;
LSQ_DEBUG("generated CONNECTION_CLOSE frame in its own packet");
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID,
"generated CONNECTION_CLOSE frame in its own packet");
conn->ifc_send_flags &= ~SF_SEND_CONN_CLOSE;
}
@ -4431,12 +4522,30 @@ log_conn_flow_control (struct ietf_full_conn *conn)
static void
generate_ping_frame (struct ietf_full_conn *conn, lsquic_time_t unused)
generate_ping_frame (struct ietf_full_conn *conn, lsquic_time_t now)
{
struct lsquic_packet_out *packet_out;
int pns;
int sz;
packet_out = get_writeable_packet(conn, 1);
if (conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
packet_out = get_writeable_packet(conn, 1);
else
{
conn->ifc_ping_period += HSK_PING_TIMEOUT;
lsquic_alarmset_set(&conn->ifc_alset, AL_PING,
now + conn->ifc_ping_period);
if (iquic_esf_is_enc_level_ready(conn->ifc_conn.cn_enc_session,
ENC_LEV_HSK))
pns = PNS_HSK;
else
pns = PNS_INIT;
packet_out = lsquic_send_ctl_new_packet_out(&conn->ifc_send_ctl, 0, pns,
CUR_NPATH(conn));
if (packet_out)
lsquic_send_ctl_scheduled_one(&conn->ifc_send_ctl, packet_out);
}
if (!packet_out)
{
LSQ_DEBUG("cannot get writeable packet for PING frame");
@ -4839,7 +4948,9 @@ static unsigned
process_padding_frame (struct ietf_full_conn *conn,
struct lsquic_packet_in *packet_in, const unsigned char *p, size_t len)
{
return (unsigned) count_zero_bytes(p, len);
unsigned sz = (unsigned) count_zero_bytes(p, len);
EV_LOG_PADDING_FRAME_IN(LSQUIC_LOG_CONN_ID, sz);
return sz;
}
@ -5591,7 +5702,7 @@ process_crypto_frame_server (struct ietf_full_conn *conn,
parsed_len);
return (unsigned) parsed_len;
}
if (enc_level < ENC_LEV_INIT)
if (enc_level < ENC_LEV_HSK)
{ /* Must be dup */
LSQ_DEBUG("discard %d-byte CRYPTO frame on level %s", parsed_len,
lsquic_enclev2str[enc_level]);
@ -5613,6 +5724,11 @@ process_crypto_frame_server (struct ietf_full_conn *conn,
LSQ_DEBUG("handshake confirmed: send HANDSHAKE_DONE");
conn->ifc_flags &= ~IFC_PROC_CRYPTO;
conn->ifc_send_flags |= SF_SEND_HANDSHAKE_DONE;
lsquic_alarmset_init_alarm(&conn->ifc_alset, AL_RET_CIDS,
ret_cids_alarm_expired, conn);
lsquic_alarmset_set(&conn->ifc_alset, AL_RET_CIDS,
lsquic_time_now() + RET_CID_TIMEOUT);
}
return (unsigned) parsed_len;
@ -5653,7 +5769,7 @@ process_crypto_frame_client (struct ietf_full_conn *conn,
EV_LOG_CRYPTO_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_frame, enc_level);
LSQ_DEBUG("Got CRYPTO frame for enc level #%u", enc_level);
if ((conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
&& enc_level != ENC_LEV_FORW)
&& enc_level != ENC_LEV_APP)
{
LSQ_DEBUG("handshake complete: ignore CRYPTO frames in "
"non-forward-secure packets");
@ -5743,7 +5859,6 @@ process_stream_frame (struct ietf_full_conn *conn,
return 0;
}
EV_LOG_STREAM_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_frame);
LSQ_DEBUG("Got stream frame for stream #%"PRIu64, stream_frame->stream_id);
CONN_STATS(in.stream_frames, 1);
CONN_STATS(in.stream_data_sz, stream_frame->data_frame.df_size);
@ -5941,7 +6056,6 @@ process_ping_frame (struct ietf_full_conn *conn,
* return the length of this frame.
*/
EV_LOG_PING_FRAME_IN(LSQUIC_LOG_CONN_ID);
LSQ_DEBUG("received PING");
if (conn->ifc_flags & IFC_SERVER)
log_conn_flow_control(conn);
@ -6037,7 +6151,7 @@ process_max_data_frame (struct ietf_full_conn *conn,
if (parsed_len < 0)
return 0;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "MAX_DATA frame in; offset: %"PRIu64,
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "RX MAX_DATA frame; offset: %"PRIu64,
max_data);
if (max_data > conn->ifc_pub.conn_cap.cc_max)
{
@ -6067,7 +6181,7 @@ process_max_stream_data_frame (struct ietf_full_conn *conn,
if (parsed_len < 0)
return 0;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "MAX_STREAM_DATA frame in; "
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "RX MAX_STREAM_DATA frame; "
"stream_id: %"PRIu64"; offset: %"PRIu64, stream_id, max_data);
if (conn_is_receive_only_stream(conn, stream_id))
{
@ -6480,7 +6594,7 @@ process_stream_blocked_frame (struct ietf_full_conn *conn,
if (parsed_len < 0)
return 0;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "STREAM_BLOCKED frame in: stream "
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "RX STREAM_BLOCKED frame: stream "
"%"PRIu64"; offset %"PRIu64, stream_id, peer_off);
LSQ_DEBUG("received STREAM_BLOCKED frame: stream %"PRIu64
"; offset %"PRIu64, stream_id, peer_off);
@ -6546,7 +6660,7 @@ process_blocked_frame (struct ietf_full_conn *conn,
if (parsed_len < 0)
return 0;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "BLOCKED frame in: offset %"PRIu64,
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "RX BLOCKED frame: offset %"PRIu64,
peer_off);
LSQ_DEBUG("received BLOCKED frame: offset %"PRIu64, peer_off);
@ -6576,7 +6690,7 @@ process_handshake_done_frame (struct ietf_full_conn *conn,
if (parsed_len < 0)
return 0;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "HANDSHAKE_DONE frame in");
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "RX HANDSHAKE_DONE frame");
LSQ_DEBUG("received HANDSHAKE_DONE frame");
if (conn->ifc_flags & IFC_SERVER)
@ -6612,11 +6726,11 @@ process_ack_frequency_frame (struct ietf_full_conn *conn,
if (parsed_len < 0)
return 0;
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "ACK_FREQUENCY(seqno: %"PRIu64"; "
"pack_tol: %"PRIu64"; upd: %"PRIu64"; ignore: %d) frame in", seqno,
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "RX ACK_FREQUENCY frame: (seqno: %"PRIu64"; "
"pack_tol: %"PRIu64"; upd: %"PRIu64"; ignore: %d)", seqno,
pack_tol, upd_mad, ignore);
LSQ_DEBUG("ACK_FREQUENCY(seqno: %"PRIu64"; pack_tol: %"PRIu64"; "
"upd: %"PRIu64"; ignore: %d) frame in", seqno, pack_tol, upd_mad,
LSQ_DEBUG("RX ACK_FREQUENCY frame: (seqno: %"PRIu64"; pack_tol: %"PRIu64"; "
"upd: %"PRIu64"; ignore: %d)", seqno, pack_tol, upd_mad,
ignore);
if (pack_tol == 0)
@ -6764,6 +6878,7 @@ process_packet_frame (struct ietf_full_conn *conn,
{
LSQ_DEBUG("invalid frame %u (bytes: %s) at encryption level %s",
type, HEXSTR(p, MIN(len, 8), str), lsquic_enclev2str[enc_level]);
ABORT_QUIETLY(0, TEC_FRAME_ENCODING_ERROR, "invalid frame");
return 0;
}
}
@ -7246,10 +7361,10 @@ ignore_init (struct ietf_full_conn *conn)
lsquic_rechist_cleanup(&conn->ifc_rechist[PNS_INIT]);
if (!(conn->ifc_flags & IFC_SERVER))
{
if (conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR])
if (conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT])
{
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR]);
conn->ifc_u.cli.crypto_streams[ENC_LEV_CLEAR] = NULL;
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT]);
conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT] = NULL;
}
conn->ifc_conn.cn_if = ietf_full_conn_iface_ptr;
}
@ -7265,10 +7380,10 @@ ignore_hsk (struct ietf_full_conn *conn)
lsquic_send_ctl_empty_pns(&conn->ifc_send_ctl, PNS_HSK);
lsquic_rechist_cleanup(&conn->ifc_rechist[PNS_HSK]);
if (!(conn->ifc_flags & IFC_SERVER))
if (conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT])
if (conn->ifc_u.cli.crypto_streams[ENC_LEV_HSK])
{
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT]);
conn->ifc_u.cli.crypto_streams[ENC_LEV_INIT] = NULL;
lsquic_stream_destroy(conn->ifc_u.cli.crypto_streams[ENC_LEV_HSK]);
conn->ifc_u.cli.crypto_streams[ENC_LEV_HSK] = NULL;
}
}
@ -7323,11 +7438,6 @@ process_regular_packet (struct ietf_full_conn *conn,
CONN_STATS(in.packets, 1);
pns = lsquic_hety2pns[ packet_in->pi_header_type ];
if (pns == PNS_INIT)
conn->ifc_conn.cn_esf.i->esfi_set_iscid(conn->ifc_conn.cn_enc_session,
packet_in);
else if (pns == PNS_HSK)
lsquic_send_ctl_maybe_calc_rough_rtt(&conn->ifc_send_ctl, pns - 1);
if ((pns == PNS_INIT && (conn->ifc_flags & IFC_IGNORE_INIT))
|| (pns == PNS_HSK && (conn->ifc_flags & IFC_IGNORE_HSK)))
{
@ -7423,6 +7533,12 @@ process_regular_packet (struct ietf_full_conn *conn,
}
}
if (pns == PNS_INIT)
conn->ifc_conn.cn_esf.i->esfi_set_iscid(conn->ifc_conn.cn_enc_session,
packet_in);
else if (pns == PNS_HSK)
lsquic_send_ctl_maybe_calc_rough_rtt(&conn->ifc_send_ctl, pns - 1);
EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in);
is_rechist_empty = lsquic_rechist_is_empty(&conn->ifc_rechist[pns]);
@ -7534,7 +7650,7 @@ process_regular_packet (struct ietf_full_conn *conn,
}
conn->ifc_pub.bytes_in += packet_in->pi_data_sz;
if ((conn->ifc_mflags & MF_VALIDATE_PATH) &&
(packet_in->pi_header_type == HETY_NOT_SET
(packet_in->pi_header_type == HETY_SHORT
|| packet_in->pi_header_type == HETY_HANDSHAKE))
{
conn->ifc_mflags &= ~MF_VALIDATE_PATH;
@ -7562,7 +7678,24 @@ verneg_ok (const struct ietf_full_conn *conn)
enum lsquic_version ver;
ver = highest_bit_set(conn->ifc_u.cli.ifcli_ver_neg.vn_supp);
return (1 << ver) & LSQUIC_IETF_DRAFT_VERSIONS;
return (1 << ver) & LSQUIC_IETF_VERSIONS;
}
static void
enable_ping_alarm_for_handshake (struct ietf_full_conn *conn)
{
conn->ifc_ping_period = HSK_PING_TIMEOUT;
lsquic_alarmset_set(&conn->ifc_alset, AL_PING,
lsquic_time_now() + conn->ifc_ping_period);
}
static int
switch_version (struct ietf_full_conn *conn, enum lsquic_version version)
{
conn->ifc_conn.cn_version = version;
return iquic_esfi_switch_version(conn->ifc_conn.cn_enc_session, NULL, 0);
}
@ -7650,19 +7783,39 @@ process_incoming_packet_verneg (struct ietf_full_conn *conn,
return 0;
}
if (packet_in->pi_version != conn->ifc_u.cli.ifcli_ver_neg.vn_ver)
{
if (!((1 << packet_in->pi_version)
& conn->ifc_u.cli.ifcli_ver_neg.vn_supp))
{
LSQ_DEBUG("server version doesn't match versions "
"supported: ignore");
return 0;
}
LSQ_DEBUG("version negociation: server switched version from %s to %s",
lsquic_ver2str[conn->ifc_u.cli.ifcli_ver_neg.vn_ver],
lsquic_ver2str[packet_in->pi_version]);
switch_version(conn, packet_in->pi_version);
}
else
conn->ifc_conn.cn_version = conn->ifc_u.cli.ifcli_ver_neg.vn_ver;
assert(conn->ifc_u.cli.ifcli_ver_neg.vn_tag);
assert(conn->ifc_u.cli.ifcli_ver_neg.vn_state != VN_END);
conn->ifc_u.cli.ifcli_ver_neg.vn_state = VN_END;
conn->ifc_u.cli.ifcli_ver_neg.vn_tag = NULL;
conn->ifc_conn.cn_version = conn->ifc_u.cli.ifcli_ver_neg.vn_ver;
conn->ifc_conn.cn_flags |= LSCONN_VER_SET;
LSQ_DEBUG("end of version negotiation: agreed upon %s",
lsquic_ver2str[conn->ifc_u.cli.ifcli_ver_neg.vn_ver]);
lsquic_ver2str[conn->ifc_conn.cn_version]);
EV_LOG_VER_NEG(LSQUIC_LOG_CONN_ID,
"agreed", lsquic_ver2str[conn->ifc_u.cli.ifcli_ver_neg.vn_ver]);
conn->ifc_process_incoming_packet = process_incoming_packet_fast;
"agreed", lsquic_ver2str[conn->ifc_conn.cn_version]);
conn->ifc_process_incoming_packet = process_regular_packet;
return process_regular_packet(conn, packet_in);
if (process_regular_packet(conn, packet_in) == 0)
{
enable_ping_alarm_for_handshake(conn);
return 0;
}
return -1;
}
@ -7825,6 +7978,7 @@ static void (*const send_funcs[N_SEND])(
[SEND_MAX_STREAMS_UNI] = generate_max_streams_uni_frame,
[SEND_MAX_STREAMS_BIDI] = generate_max_streams_bidi_frame,
[SEND_STOP_SENDING] = generate_stop_sending_frames,
[SEND_NEW_TOKEN] = generate_new_token_frame,
[SEND_PATH_CHAL_PATH_0] = generate_path_chal_0,
[SEND_PATH_CHAL_PATH_1] = generate_path_chal_1,
[SEND_PATH_CHAL_PATH_2] = generate_path_chal_2,
@ -7849,7 +8003,7 @@ static void (*const send_funcs[N_SEND])(
|SF_SEND_PATH_RESP_PATH_2|SF_SEND_PATH_RESP_PATH_3\
|SF_SEND_PING|SF_SEND_HANDSHAKE_DONE\
|SF_SEND_ACK_FREQUENCY\
|SF_SEND_STOP_SENDING)
|SF_SEND_STOP_SENDING|SF_SEND_NEW_TOKEN)
/* This should be called before lsquic_alarmset_ring_expired() */
@ -8442,7 +8596,8 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
goto end;
}
}
else if (conn->ifc_ping_period)
else if (conn->ifc_ping_period
&& (conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE))
{
lsquic_alarmset_unset(&conn->ifc_alset, AL_PING);
lsquic_send_ctl_sanity_check(&conn->ifc_send_ctl);
@ -8797,7 +8952,7 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
conn->ifc_pub.bytes_in = garbage_sz;
conn->ifc_pub.bytes_in += garbage_sz;
LSQ_DEBUG("count %zd bytes of garbage, new value: %u bytes", garbage_sz,
conn->ifc_pub.bytes_in);
}
@ -9481,6 +9636,7 @@ hcsi_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
break;
case (0 << 8) | LSQVER_ID29:
case (0 << 8) | LSQVER_I001:
case (0 << 8) | LSQVER_I002:
callbacks = &hcsi_callbacks_client_29;
break;
default:
@ -9488,6 +9644,7 @@ hcsi_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
/* fallthru */
case (1 << 8) | LSQVER_ID29:
case (1 << 8) | LSQVER_I001:
case (1 << 8) | LSQVER_I002:
callbacks = &hcsi_callbacks_server_29;
break;
}

View file

@ -767,7 +767,7 @@ gquic2_setup_handshake_keys (struct lsquic_enc_session *enc_session)
secret, sizeof(secret)))
goto err;
if (enc_session->es_flags & ES_LOG_SECRETS)
log_crypto_ctx(enc_session, ENC_LEV_CLEAR, i);
log_crypto_ctx(enc_session, ENC_LEV_INIT, i);
}
return 0;
@ -3069,14 +3069,14 @@ decrypt_packet (struct lsquic_enc_session *enc_session, uint8_t path_id,
key = enc_session->dec_ctx_f;
memcpy(nonce, enc_session->dec_key_nonce_f, 4);
LSQ_DEBUG("decrypt_packet using 'F' key...");
enc_level = ENC_LEV_FORW;
enc_level = ENC_LEV_APP;
}
else
{
key = enc_session->dec_ctx_i;
memcpy(nonce, enc_session->dec_key_nonce_i, 4);
LSQ_DEBUG("decrypt_packet using 'I' key...");
enc_level = ENC_LEV_INIT;
enc_level = ENC_LEV_HSK;
}
memcpy(nonce + 4, &path_id_packet_number,
sizeof(path_id_packet_number));
@ -3150,7 +3150,7 @@ lsquic_enc_session_decrypt (struct lsquic_enc_session *enc_session,
header_len, data_len, buf_out, max_out_len, out_len);
else if (0 == verify_packet_hash(enc_session, version, buf, header_len,
data_len, buf_out, max_out_len, out_len))
return ENC_LEV_CLEAR;
return ENC_LEV_INIT;
else
return -1;
}
@ -3247,7 +3247,7 @@ gquic_encrypt_buf (struct lsquic_enc_session *enc_session,
memcpy(buf_out, header, header_len);
memcpy(buf_out + header_len, md, HS_PKT_HASH_LENGTH);
memcpy(buf_out + header_len + HS_PKT_HASH_LENGTH, data, data_len);
return ENC_LEV_CLEAR;
return ENC_LEV_INIT;
}
else
{
@ -3262,14 +3262,14 @@ gquic_encrypt_buf (struct lsquic_enc_session *enc_session,
{
enc_session->server_start_use_final_key = 1;
}
enc_level = ENC_LEV_INIT;
enc_level = ENC_LEV_HSK;
}
else
{
LSQ_DEBUG("lsquic_enc_session_encrypt using 'F' key...");
key = enc_session->enc_ctx_f;
memcpy(nonce, enc_session->enc_key_nonce_f, 4);
enc_level = ENC_LEV_FORW;
enc_level = ENC_LEV_APP;
}
path_id_packet_number = combine_path_id_pack_num(path_id, pack_num);
memcpy(nonce + 4, &path_id_packet_number,
@ -3964,9 +3964,9 @@ static const char *const gel2str[] =
static const enum enc_level gel2el[] =
{
[GEL_CLEAR] = ENC_LEV_CLEAR,
[GEL_EARLY] = ENC_LEV_EARLY,
[GEL_FORW] = ENC_LEV_FORW,
[GEL_CLEAR] = ENC_LEV_INIT,
[GEL_EARLY] = ENC_LEV_0RTT,
[GEL_FORW] = ENC_LEV_APP,
};

View file

@ -14,6 +14,10 @@
#define HSK_SALT ((unsigned char *) \
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17" \
"\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a")
/* [draft-ietf-quic-v2] Section 3.3.1 */
#define HSK_SALT_V2 ((unsigned char *) \
"\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93" \
"\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd\x2e\xd9")
#define HSK_SALT_SZ (sizeof(HSK_SALT_BUF) - 1)
#define CLIENT_LABEL "client in"

View file

@ -24,6 +24,7 @@ enum trans_error_code
TEC_KEY_UPDATE_ERROR = 0xE,
TEC_AEAD_LIMIT_REACHED = 0xF,
TEC_NO_VIABLE_PATH = 0x10,
TEC_VERSION_NEGOTIATION_ERROR = 0x11,
};
/* Must be at least two */
@ -33,12 +34,12 @@ enum trans_error_code
#define IETF_RETRY_KEY_SZ 16
#define IETF_RETRY_NONCE_SZ 12
#define N_IETF_RETRY_VERSIONS 3
#define N_IETF_RETRY_VERSIONS 4
extern const unsigned char *const lsquic_retry_key_buf[N_IETF_RETRY_VERSIONS];
extern const unsigned char *const lsquic_retry_nonce_buf[N_IETF_RETRY_VERSIONS];
#define lsquic_version_2_retryver(ver_) ( \
(ver_) <= LSQVER_ID27 ? 0 : \
(ver_) < LSQVER_I001 ? 1 : \
2)
(ver_) == LSQVER_I002 ? 3 : 2)
#endif

View file

@ -58,10 +58,10 @@ ietf_mini_conn_ci_abort_error (struct lsquic_conn *lconn, int is_app,
static const enum header_type el2hety[] =
{
[ENC_LEV_INIT] = HETY_HANDSHAKE,
[ENC_LEV_CLEAR] = HETY_INITIAL,
[ENC_LEV_FORW] = HETY_NOT_SET,
[ENC_LEV_EARLY] = 0, /* Invalid */
[ENC_LEV_HSK] = HETY_HANDSHAKE,
[ENC_LEV_INIT] = HETY_INITIAL,
[ENC_LEV_APP] = HETY_SHORT,
[ENC_LEV_0RTT] = 0, /* Invalid */
};
@ -87,19 +87,13 @@ lsquic_mini_conn_ietf_ecn_ok (const struct ietf_mini_conn *conn)
}
#define imico_ecn_ok lsquic_mini_conn_ietf_ecn_ok
static enum ecn
imico_get_ecn (struct ietf_mini_conn *conn)
{
if (!conn->imc_enpub->enp_settings.es_ecn)
return ECN_NOT_ECT;
else if (!conn->imc_sent_packnos /* We set ECT0 in first flight */
|| imico_ecn_ok(conn))
return ECN_ECT0;
else
return ECN_NOT_ECT;
return ECN_ECT0;
}
@ -175,8 +169,54 @@ read_from_msg_ctx (void *ctx, void *buf, size_t len, int *fin)
static int
imico_chlo_has_been_consumed (const struct ietf_mini_conn *conn)
{
return conn->imc_streams[ENC_LEV_CLEAR].mcs_read_off > 3
&& conn->imc_streams[ENC_LEV_CLEAR].mcs_read_off >= conn->imc_ch_len;
return conn->imc_streams[ENC_LEV_INIT].mcs_read_off > 3
&& conn->imc_streams[ENC_LEV_INIT].mcs_read_off >= conn->imc_ch_len;
}
static int
imico_process_version_negociation (struct ietf_mini_conn *conn,
const struct transport_params *params)
{
unsigned common_versions;
unsigned ver;
int i;
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
{
LSQ_DEBUG("client chosen version %s",
lsquic_ver2str[params->tp_chosen_version]);
for (i = 1; i < params->tp_version_cnt; ++i)
{
LSQ_DEBUG("client available version %s",
lsquic_ver2str[params->tp_version_info[i]]);
//EV_LOG_VER_NEG(LSQUIC_LOG_CONN_ID, "supports",
// lsquic_ver2str[params->tp_version_info[i]]);
}
}
if (params->tp_chosen_version != conn->imc_conn.cn_version)
{
LSQ_DEBUG("client chosen version does not match the version in use %s",
lsquic_ver2str[conn->imc_conn.cn_version]);
ietf_mini_conn_ci_abort_error(&conn->imc_conn, 0,
TEC_VERSION_NEGOTIATION_ERROR,
"chosen version mismatch");
return -1;
}
common_versions = conn->imc_enpub->enp_settings.es_versions
& params->tp_versions;
ver = highest_bit_set(common_versions);
if (conn->imc_conn.cn_version != ver)
{
LSQ_DEBUG("version negociation: switch version from %s to %s",
lsquic_ver2str[conn->imc_conn.cn_version],
lsquic_ver2str[ver]);
conn->imc_conn.cn_version = ver;
conn->imc_conn.cn_flags |= LSCONN_VER_UPDATED;
iquic_esfi_switch_version(conn->imc_conn.cn_enc_session,
&conn->imc_cces[0].cce_cid, 1);
}
return 0;
}
@ -204,6 +244,15 @@ imico_maybe_process_params (struct ietf_mini_conn *conn)
}
LSQ_DEBUG("read transport params, packet size is set to %hu bytes",
conn->imc_path.np_pack_size);
if (params->tp_set & (1 << TPI_VERSION_INFORMATION))
{
if (imico_process_version_negociation(conn, params) == -1)
{
conn->imc_flags |= IMC_VER_NEG_FAILED;
return -1;
}
}
iquic_esfi_init_server_tp(conn->imc_conn.cn_enc_session);
}
else
{
@ -216,6 +265,10 @@ imico_maybe_process_params (struct ietf_mini_conn *conn)
}
static int
imico_generate_ack (struct ietf_mini_conn *conn, enum packnum_space pns,
lsquic_time_t now);
static ssize_t
imico_stream_write (void *stream, const void *bufp, size_t bufsz)
{
@ -239,19 +292,32 @@ imico_stream_write (void *stream, const void *bufp, size_t bufsz)
return bufsz;
}
if (cryst->mcs_enc_level == ENC_LEV_INIT
&& (conn->imc_flags & IMC_QUEUED_ACK_INIT))
{
imico_generate_ack(conn, PNS_INIT, lsquic_time_now());
}
while (msg_ctx.buf < msg_ctx.end)
{
header_sz = lconn->cn_pf->pf_calc_crypto_frame_header_sz(
cryst->mcs_write_off, msg_ctx.end - msg_ctx.buf);
need = header_sz + 1;
need = header_sz + 500;
packet_out = imico_get_packet_out(conn,
el2hety[ cryst->mcs_enc_level ], need);
if (!packet_out)
return -1;
// NOTE: reduce the size of first crypto frame to combine packets
int avail = lsquic_packet_out_avail(packet_out);
if (cryst->mcs_enc_level == ENC_LEV_HSK
&& cryst->mcs_write_off == 0
&& avail > conn->imc_hello_pkt_remain - conn->imc_long_header_sz)
{
avail = conn->imc_hello_pkt_remain - conn->imc_long_header_sz;
}
p = msg_ctx.buf;
len = pf->pf_gen_crypto_frame(packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), 0, cryst->mcs_write_off, 0,
avail, 0, cryst->mcs_write_off, 0,
msg_ctx.end - msg_ctx.buf, read_from_msg_ctx, &msg_ctx);
if (len < 0)
return len;
@ -261,6 +327,8 @@ imico_stream_write (void *stream, const void *bufp, size_t bufsz)
packet_out->po_frame_types |= 1 << QUIC_FRAME_CRYPTO;
packet_out->po_flags |= PO_HELLO;
cryst->mcs_write_off += msg_ctx.buf - p;
if (cryst->mcs_enc_level == ENC_LEV_INIT)
conn->imc_hello_pkt_remain = avail - len;
}
assert(msg_ctx.buf == msg_ctx.end);
@ -299,8 +367,8 @@ imico_read_chlo_size (struct ietf_mini_conn *conn, const unsigned char *buf,
{
const unsigned char *const end = buf + sz;
assert(conn->imc_streams[ENC_LEV_CLEAR].mcs_read_off < 4);
switch (conn->imc_streams[ENC_LEV_CLEAR].mcs_read_off)
assert(conn->imc_streams[ENC_LEV_INIT].mcs_read_off < 4);
switch (conn->imc_streams[ENC_LEV_INIT].mcs_read_off)
{
case 0:
if (buf == end)
@ -356,7 +424,7 @@ imico_stream_readf (void *stream,
avail = DF_SIZE(frame) - frame->data_frame.df_read_off;
buf = frame->data_frame.df_data + frame->data_frame.df_read_off;
nread = readf(ctx, buf, avail, DF_FIN(frame));
if (cryst->mcs_enc_level == ENC_LEV_CLEAR && cryst->mcs_read_off < 4)
if (cryst->mcs_enc_level == ENC_LEV_INIT && cryst->mcs_read_off < 4)
imico_read_chlo_size(conn, buf, nread);
total_read += nread;
cryst->mcs_read_off += nread;
@ -468,7 +536,7 @@ imico_peer_addr_validated (struct ietf_mini_conn *conn, const char *how)
struct lsquic_conn *
lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
const struct lsquic_packet_in *packet_in,
struct lsquic_packet_in *packet_in,
enum lsquic_version version, int is_ipv4, const lsquic_cid_t *odcid,
size_t udp_payload_size)
{
@ -480,7 +548,7 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
if (!is_first_packet_ok(packet_in, udp_payload_size))
return NULL;
packet_in->pi_flags |= PI_FIRST_INIT;
conn = lsquic_malo_get(enpub->enp_mm.malo.mini_conn_ietf);
if (!conn)
{
@ -539,9 +607,16 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
}
esfi = select_esf_iquic_by_ver(version);
enc_sess = esfi->esfi_create_server(enpub, &conn->imc_conn,
&packet_in->pi_dcid, conn->imc_stream_ps, &crypto_stream_if,
&conn->imc_cces[0].cce_cid, &conn->imc_path.np_dcid);
if (version > LSQVER_ID27)
enc_sess = esfi->esfi_create_server(enpub, &conn->imc_conn,
&packet_in->pi_dcid, conn->imc_stream_ps, &crypto_stream_if,
odcid ? odcid : &conn->imc_cces[0].cce_cid,
&conn->imc_path.np_dcid,
odcid ? &conn->imc_cces[0].cce_cid : NULL);
else
enc_sess = esfi->esfi_create_server(enpub, &conn->imc_conn,
&packet_in->pi_dcid, conn->imc_stream_ps, &crypto_stream_if,
odcid, &conn->imc_path.np_dcid, NULL);
if (!enc_sess)
{
lsquic_malo_put(conn);
@ -549,7 +624,8 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
}
conn->imc_enpub = enpub;
conn->imc_created = packet_in->pi_received;
conn->imc_expire = packet_in->pi_received +
enpub->enp_settings.es_handshake_to;
if (enpub->enp_settings.es_base_plpmtu)
conn->imc_path.np_pack_size = enpub->enp_settings.es_base_plpmtu;
else if (is_ipv4)
@ -576,8 +652,22 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
}
#endif
conn->imc_long_header_sz = 1 /* Type */
+ 4 /* Version */
+ 1 /* DCIL */
+ conn->imc_path.np_dcid.len
+ 1 /* SCIL */
+ CN_SCID(&conn->imc_conn)->len
+ 0 /* HSK packet by server has no token */
+ 2 /* Always use two bytes to encode payload length */
+ 1 /* mini conn packet number < 256 */
+ IQUIC_TAG_LEN
;
conn->imc_hello_pkt_remain = conn->imc_path.np_pack_size
- conn->imc_long_header_sz - 1 /* token len */;
LSQ_DEBUG("created mini connection object %p; max packet size=%hu",
conn, conn->imc_path.np_pack_size);
conn, conn->imc_path.np_pack_size);
return &conn->imc_conn;
}
@ -679,7 +769,8 @@ ietf_mini_conn_ci_is_tickable (struct lsquic_conn *lconn)
const struct lsquic_packet_out *packet_out;
size_t packet_size;
if (conn->imc_enpub->enp_flags & ENPUB_CAN_SEND)
if ((conn->imc_enpub->enp_flags & ENPUB_CAN_SEND)
&& !(conn->imc_flags & IMC_AMP_CAPPED))
TAILQ_FOREACH(packet_out, &conn->imc_packets_out, po_next)
if (!(packet_out->po_flags & PO_SENT))
{
@ -700,18 +791,117 @@ imico_can_send (const struct ietf_mini_conn *conn, size_t size)
}
static void
imico_zero_pad (struct lsquic_packet_out *packet_out)
{
size_t pad_size;
// static void
// imico_zero_pad (struct lsquic_packet_out *packet_out)
// {
// size_t pad_size;
//
// pad_size = lsquic_packet_out_avail(packet_out);
// memset(packet_out->po_data + packet_out->po_data_sz, 0, pad_size);
// packet_out->po_padding_sz = pad_size;
// packet_out->po_data_sz += pad_size;
// packet_out->po_frame_types |= QUIC_FTBIT_PADDING;
// }
pad_size = lsquic_packet_out_avail(packet_out);
memset(packet_out->po_data + packet_out->po_data_sz, 0, pad_size);
packet_out->po_data_sz += pad_size;
packet_out->po_frame_types |= QUIC_FTBIT_PADDING;
static lsquic_time_t
imico_rechist_largest_recv (void *rechist_ctx);
static int
imico_build_ack_frame (struct ietf_mini_conn *conn, enum packnum_space pns,
lsquic_time_t now, unsigned char *outbuf,
size_t outbuf_sz, lsquic_packno_t *ack2ed)
{
struct ietf_mini_rechist rechist;
uint64_t ecn_counts_buf[4];
const uint64_t *ecn_counts;
int not_used_has_missing, len;
if (conn->imc_incoming_ecn)
{
ecn_counts_buf[0] = conn->imc_ecn_counts_in[pns][0];
ecn_counts_buf[1] = conn->imc_ecn_counts_in[pns][1];
ecn_counts_buf[2] = conn->imc_ecn_counts_in[pns][2];
ecn_counts_buf[3] = conn->imc_ecn_counts_in[pns][3];
ecn_counts = ecn_counts_buf;
}
else
ecn_counts = NULL;
lsquic_imico_rechist_init(&rechist, conn, pns);
len = conn->imc_conn.cn_pf->pf_gen_ack_frame(outbuf, outbuf_sz,
lsquic_imico_rechist_first,
lsquic_imico_rechist_next, imico_rechist_largest_recv, &rechist,
now, &not_used_has_missing, ack2ed, ecn_counts);
if (len < 0)
{
LSQ_WARN("could not generate ACK frame");
return -1;
}
EV_LOG_GENERATED_ACK_FRAME(LSQUIC_LOG_CONN_ID, conn->imc_conn.cn_pf,
outbuf, len);
return len;
}
static void
remove_ack_frame (struct ietf_mini_conn *conn,
struct lsquic_packet_out *packet_out)
{
if (packet_out->po_regen_sz == 0)
return;
int l = packet_out->po_data_sz - packet_out->po_regen_sz
- packet_out->po_padding_sz;
memmove(packet_out->po_data ,
packet_out->po_data + packet_out->po_regen_sz, l);
memset(packet_out->po_data + l, 0, packet_out->po_regen_sz);
packet_out->po_padding_sz += packet_out->po_regen_sz;
packet_out->po_regen_sz = 0;
}
// static void
// update_packet_ack (struct ietf_mini_conn *conn, enum packnum_space pns,
// struct lsquic_packet_out *packet_out)
// {
// int len, need;
// unsigned char outbuf[1024];
// size_t outbuf_sz = sizeof(outbuf);
// lsquic_packno_t ack2ed;
//
// len = imico_build_ack_frame(conn, pns, lsquic_time_now(), outbuf,
// outbuf_sz, &ack2ed);
// if (len > packet_out->po_regen_sz + packet_out->po_padding_sz)
// return;
// need = len + !!(packet_out->po_frame_types & (1 << QUIC_FRAME_PING));
// LSQ_DEBUG("update leading ACK frame for packet %"PRIu64
// ", regen_sz: %hd, padding_sz: %hd, need: %d, len: %d",
// packet_out->po_packno, packet_out->po_regen_sz,
// packet_out->po_padding_sz, need, len);
//
// if (need != packet_out->po_regen_sz)
// {
// int l = packet_out->po_data_sz - packet_out->po_regen_sz
// - packet_out->po_padding_sz;
// memmove(packet_out->po_data + need,
// packet_out->po_data + packet_out->po_regen_sz, l);
// if (packet_out->po_padding_sz)
// {
// if (need < packet_out->po_regen_sz)
// memset(packet_out->po_data + need + l, 0,
// packet_out->po_regen_sz - need);
// packet_out->po_padding_sz += packet_out->po_regen_sz - need;
// }
// else
// packet_out->po_data_sz -= packet_out->po_regen_sz - need;
// packet_out->po_regen_sz = need;
// }
// memmove(packet_out->po_data, outbuf, len);
// packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK;
// packet_out->po_ack2ed = ack2ed;
// }
static struct lsquic_packet_out *
ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
const struct to_coal *to_coal)
@ -729,11 +919,31 @@ ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
" ack-eliciting Initial packets to at least the smallest allowed
" maximum datagram size of 1200 bytes.
*/
if (packet_out->po_header_type == HETY_INITIAL
&& !(packet_out->po_frame_types & (1 << QUIC_FRAME_PADDING))
&& (packet_out->po_frame_types & IQUIC_FRAME_ACKABLE_MASK)
&& lsquic_packet_out_avail(packet_out) > 0)
imico_zero_pad(packet_out);
if (packet_out->po_header_type == HETY_INITIAL)
{
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK)
&& packet_out->po_retx_cnt > 0)
{
remove_ack_frame(conn, packet_out);
}
if (packet_out->po_retx_cnt > 0
&& !imico_can_send(conn, IQUIC_MAX_IPv4_PACKET_SZ))
{
conn->imc_flags |= IMC_AMP_CAPPED;
LSQ_DEBUG("cannot send INIT packet #%"PRIu64" without "
"enough quota", packet_out->po_packno);
return NULL;
}
// if (!(packet_out->po_frame_types & (1 << QUIC_FRAME_PADDING))
// && (packet_out->po_frame_types & IQUIC_FRAME_ACKABLE_MASK)
// && lsquic_packet_out_avail(packet_out) > 0)
// {
// LSQ_DEBUG("generated PADDING frame: %hd bytes for packet %"PRIu64,
// lsquic_packet_out_avail(packet_out), packet_out->po_packno);
// imico_zero_pad(packet_out);
// }
}
packet_size = lsquic_packet_out_total_sz(lconn, packet_out);
if (!(to_coal
&& (packet_size + to_coal->prev_sz_sum
@ -743,6 +953,7 @@ ietf_mini_conn_ci_next_packet_to_send (struct lsquic_conn *lconn,
{
if (!imico_can_send(conn, packet_size))
{
conn->imc_flags |= IMC_AMP_CAPPED;
LSQ_DEBUG("cannot send packet %"PRIu64" of size %zu: client "
"address has not been validated", packet_out->po_packno,
packet_size);
@ -778,7 +989,7 @@ imico_calc_retx_timeout (const struct ietf_mini_conn *conn)
}
else
to = 300000;
return to << conn->imc_hsk_count;
return to;
}
@ -789,23 +1000,20 @@ ietf_mini_conn_ci_next_tick_time (struct lsquic_conn *lconn, unsigned *why)
const struct lsquic_packet_out *packet_out;
lsquic_time_t exp_time, retx_time;
exp_time = conn->imc_created +
conn->imc_enpub->enp_settings.es_handshake_to;
exp_time = conn->imc_expire;
TAILQ_FOREACH(packet_out, &conn->imc_packets_out, po_next)
if (packet_out->po_flags & PO_SENT)
{
retx_time = packet_out->po_sent + imico_calc_retx_timeout(conn);
retx_time = packet_out->po_sent + (imico_calc_retx_timeout(conn)
<< (packet_out->po_retx_cnt >> 1));
if (retx_time < exp_time)
{
*why = N_AEWS + AL_RETX_HSK;
return retx_time;
}
else
{
*why = AEW_MINI_EXPIRE;
return exp_time;
}
break;
}
*why = AEW_MINI_EXPIRE;
@ -1092,6 +1300,7 @@ static unsigned
imico_process_ping_frame (IMICO_PROC_FRAME_ARGS)
{
LSQ_DEBUG("got a PING frame, do nothing");
EV_LOG_PING_FRAME_IN(LSQUIC_LOG_CONN_ID);
return 1;
}
@ -1136,6 +1345,17 @@ imico_process_invalid_frame (IMICO_PROC_FRAME_ARGS)
}
static unsigned
imico_process_invalid_frame_pv (IMICO_PROC_FRAME_ARGS)
{
LSQ_DEBUG("invalid frame %u (%s)", p[0],
frame_type_2_str[ conn->imc_conn.cn_pf->pf_parse_frame_type(p, len) ]);
ietf_mini_conn_ci_abort_error(&conn->imc_conn, 0, TEC_PROTOCOL_VIOLATION,
"protocol violation detected while processing frame");
return 0;
}
static unsigned (*const imico_process_frames[N_QUIC_FRAMES])
(IMICO_PROC_FRAME_ARGS) =
{
@ -1154,13 +1374,15 @@ static unsigned (*const imico_process_frames[N_QUIC_FRAMES])
[QUIC_FRAME_BLOCKED] = imico_process_invalid_frame,
[QUIC_FRAME_STREAM_BLOCKED] = imico_process_invalid_frame,
[QUIC_FRAME_STREAMS_BLOCKED] = imico_process_invalid_frame,
[QUIC_FRAME_NEW_CONNECTION_ID] = imico_process_invalid_frame,
[QUIC_FRAME_NEW_CONNECTION_ID] = imico_process_invalid_frame_pv,
[QUIC_FRAME_STOP_SENDING] = imico_process_invalid_frame,
[QUIC_FRAME_PATH_CHALLENGE] = imico_process_invalid_frame,
[QUIC_FRAME_PATH_RESPONSE] = imico_process_invalid_frame,
/* STREAM frame can only come in the App PNS and we delay those packets: */
[QUIC_FRAME_STREAM] = imico_process_invalid_frame,
[QUIC_FRAME_HANDSHAKE_DONE] = imico_process_invalid_frame,
[QUIC_FRAME_RETIRE_CONNECTION_ID] = imico_process_invalid_frame_pv,
[QUIC_FRAME_NEW_TOKEN] = imico_process_invalid_frame_pv,
[QUIC_FRAME_HANDSHAKE_DONE] = imico_process_invalid_frame_pv,
[QUIC_FRAME_ACK_FREQUENCY] = imico_process_invalid_frame,
[QUIC_FRAME_TIMESTAMP] = imico_process_invalid_frame,
};
@ -1185,6 +1407,16 @@ imico_process_packet_frame (struct ietf_mini_conn *conn,
{
LSQ_DEBUG("invalid frame %u at encryption level %s", type,
lsquic_enclev2str[enc_level]);
if (type == QUIC_FRAME_INVALID)
ietf_mini_conn_ci_abort_error(&conn->imc_conn, 0,
TEC_FRAME_ENCODING_ERROR,
"invalid frame %u at encryption level %s",
type, lsquic_enclev2str[enc_level]);
else
ietf_mini_conn_ci_abort_error(&conn->imc_conn, 0,
TEC_PROTOCOL_VIOLATION,
"protcol violation for invalid frame %u at encryption level %s",
type, lsquic_enclev2str[enc_level]);
return 0;
}
}
@ -1258,6 +1490,15 @@ ignore_init (struct ietf_mini_conn *conn)
LSQ_DEBUG("henceforth, no Initial packets shall be sent or received; "
"destroyed %u packet%.*s", count, count != 1, "s");
int ext_to = conn->imc_enpub->enp_settings.es_idle_timeout * 1000000
- conn->imc_enpub->enp_settings.es_handshake_to;
if (ext_to > 0)
{
conn->imc_expire += ext_to;
LSQ_DEBUG("extend mini-conn expire time to %d seconds",
conn->imc_enpub->enp_settings.es_idle_timeout);
}
}
@ -1397,6 +1638,10 @@ imico_switch_to_trechist (struct ietf_mini_conn *conn)
}
static int
imico_generate_handshake_done (struct ietf_mini_conn *conn);
/* Only a single packet is supported */
static void
ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
@ -1416,6 +1661,7 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
" that contain packets that are all discarded.
*/
conn->imc_bytes_in += packet_in->pi_data_sz;
conn->imc_flags &= ~IMC_AMP_CAPPED;
if (conn->imc_flags & IMC_ERROR)
{
@ -1446,6 +1692,15 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
case DECPI_NOT_YET:
imico_maybe_delay_processing(conn, packet_in);
return;
case DECPI_BADCRYPT:
if (packet_in->pi_flags & PI_FIRST_INIT)
{
LSQ_DEBUG("possible packet corruption, destroy mini-conn.");
conn->imc_flags |= IMC_ERROR | IMC_CLOSE_RECVD;
/* avoid adding CID to black list */
conn->imc_conn.cn_flags |= LSCONN_NO_BL;
}
//fall through
default:
LSQ_DEBUG("could not decrypt packet");
return;
@ -1491,19 +1746,28 @@ ietf_mini_conn_ci_packet_in (struct lsquic_conn *lconn,
conn->imc_largest_recvd[pns] = packet_in->pi_received;
imico_record_recvd_packno(conn, pns, packet_in->pi_packno);
if (!(conn->imc_flags & (IMC_QUEUED_ACK_INIT << pns)))
{
LSQ_DEBUG("queued ACK in %s", lsquic_pns2str[pns]);
conn->imc_flags |= IMC_QUEUED_ACK_INIT << pns;
}
++conn->imc_ecn_counts_in[pns][ lsquic_packet_in_ecn(packet_in) ];
conn->imc_incoming_ecn <<= 1;
conn->imc_incoming_ecn |= lsquic_packet_in_ecn(packet_in) != ECN_NOT_ECT;
if (0 != imico_parse_regular_packet(conn, packet_in))
{
LSQ_DEBUG("connection is now in error state");
conn->imc_flags |= IMC_ERROR;
return;
}
if (!(conn->imc_flags & (IMC_QUEUED_ACK_INIT << pns)))
LSQ_DEBUG("queued ACK in %s", lsquic_pns2str[pns]);
conn->imc_flags |= IMC_QUEUED_ACK_INIT << pns;
++conn->imc_ecn_counts_in[pns][ lsquic_packet_in_ecn(packet_in) ];
conn->imc_incoming_ecn <<= 1;
conn->imc_incoming_ecn |= lsquic_packet_in_ecn(packet_in) != ECN_NOT_ECT;
if (conn->imc_flags & IMC_HSK_OK)
{
if (lconn->cn_esf.i->esfi_in_init(lconn->cn_enc_session))
LSQ_DEBUG("still in init, defer HANDSHAKE_DONE");
else if (0 != imico_generate_handshake_done(conn))
conn->imc_flags |= IMC_ERROR;
}
}
@ -1563,12 +1827,11 @@ imico_repackage_packet (struct ietf_mini_conn *conn,
if (packno > MAX_PACKETS)
return -1;
LSQ_DEBUG("Packet %"PRIu64" repackaged for resending as packet %"PRIu64,
oldno, packno);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "packet %"PRIu64" repackaged for "
"resending as packet %"PRIu64, oldno, packno);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "schedule resend, repackage packet "
"#%"PRIu64" -> #%"PRIu64, oldno, packno);
packet_out->po_packno = packno;
packet_out->po_flags &= ~PO_SENT;
++packet_out->po_retx_cnt;
lsquic_packet_out_set_ecn(packet_out, imico_get_ecn(conn));
if (packet_out->po_flags & PO_ENCRYPTED)
imico_return_enc_data(conn, packet_out);
@ -1595,14 +1858,26 @@ imico_handle_losses_and_have_unsent (struct ietf_mini_conn *conn,
next = TAILQ_NEXT(packet_out, po_next);
if (packet_out->po_flags & PO_SENT)
{
if (0 == retx_to)
retx_to = imico_calc_retx_timeout(conn);
if (packet_out->po_sent + retx_to < now)
if (packet_out->po_frame_types & IQUIC_FRAME_RETX_MASK)
{
if (0 == retx_to)
retx_to = imico_calc_retx_timeout(conn);
if (conn->imc_hsk_count == 0)
packet_out->po_retx_cnt = 0;
if (packet_out->po_sent
+ (retx_to << (packet_out->po_retx_cnt >> 1)) < now)
{
LSQ_DEBUG("packet %"PRIu64" has been lost (rto: %"PRIu64")",
packet_out->po_packno,
retx_to << (packet_out->po_retx_cnt >> 1));
TAILQ_REMOVE(&conn->imc_packets_out, packet_out, po_next);
TAILQ_INSERT_TAIL(&lost_packets, packet_out, po_next);
}
}
else
{
LSQ_DEBUG("packet %"PRIu64" has been lost (rto: %"PRIu64")",
packet_out->po_packno, retx_to);
TAILQ_REMOVE(&conn->imc_packets_out, packet_out, po_next);
TAILQ_INSERT_TAIL(&lost_packets, packet_out, po_next);
imico_destroy_packet(conn, packet_out);
}
}
else if (packet_size = lsquic_packet_out_total_sz(lconn, packet_out),
@ -1617,8 +1892,7 @@ imico_handle_losses_and_have_unsent (struct ietf_mini_conn *conn,
while ((packet_out = TAILQ_FIRST(&lost_packets)))
{
TAILQ_REMOVE(&lost_packets, packet_out, po_next);
if ((packet_out->po_frame_types & IQUIC_FRAME_RETX_MASK)
&& 0 == imico_repackage_packet(conn, packet_out))
if (0 == imico_repackage_packet(conn, packet_out))
{
packet_size = lsquic_packet_out_total_sz(lconn, packet_out);
if (imico_can_send(conn, packet_size))
@ -1738,57 +2012,58 @@ static const enum header_type pns2hety[] =
{
[PNS_INIT] = HETY_INITIAL,
[PNS_HSK] = HETY_HANDSHAKE,
[PNS_APP] = HETY_NOT_SET,
[PNS_APP] = HETY_SHORT,
};
static int
imico_generate_ping (struct ietf_mini_conn *conn,
struct lsquic_packet_out *packet_out)
{
int len;
len = conn->imc_conn.cn_pf->pf_gen_ping_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out));
if (len > 0)
{
packet_out->po_frame_types |= 1 << QUIC_FRAME_PING;
packet_out->po_data_sz += len;
packet_out->po_regen_sz += len;
LSQ_DEBUG("wrote PING frame of size %d to packet %" PRIu64,
len, packet_out->po_packno);
}
return 0;
}
static int
imico_generate_ack (struct ietf_mini_conn *conn, enum packnum_space pns,
lsquic_time_t now)
{
struct lsquic_packet_out *packet_out;
enum header_type header_type;
struct ietf_mini_rechist rechist;
int not_used_has_missing, len;
uint64_t ecn_counts_buf[4];
const uint64_t *ecn_counts;
int len;
header_type = pns2hety[pns];
if (conn->imc_incoming_ecn)
{
ecn_counts_buf[0] = conn->imc_ecn_counts_in[pns][0];
ecn_counts_buf[1] = conn->imc_ecn_counts_in[pns][1];
ecn_counts_buf[2] = conn->imc_ecn_counts_in[pns][2];
ecn_counts_buf[3] = conn->imc_ecn_counts_in[pns][3];
ecn_counts = ecn_counts_buf;
}
else
ecn_counts = NULL;
packet_out = imico_get_packet_out(conn, header_type, 0);
if (!packet_out)
return -1;
/* Generate ACK frame */
lsquic_imico_rechist_init(&rechist, conn, pns);
len = conn->imc_conn.cn_pf->pf_gen_ack_frame(
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out), lsquic_imico_rechist_first,
lsquic_imico_rechist_next, imico_rechist_largest_recv, &rechist,
now, &not_used_has_missing, &packet_out->po_ack2ed, ecn_counts);
len = imico_build_ack_frame(conn, pns, now,
packet_out->po_data + packet_out->po_data_sz,
lsquic_packet_out_avail(packet_out),
&packet_out->po_ack2ed);
if (len < 0)
{
LSQ_WARN("could not generate ACK frame");
return -1;
}
EV_LOG_GENERATED_ACK_FRAME(LSQUIC_LOG_CONN_ID, conn->imc_conn.cn_pf,
packet_out->po_data + packet_out->po_data_sz, len);
packet_out->po_frame_types |= 1 << QUIC_FRAME_ACK;
packet_out->po_data_sz += len;
packet_out->po_regen_sz += len;
conn->imc_flags &= ~(IMC_QUEUED_ACK_INIT << pns);
LSQ_DEBUG("wrote ACK frame of size %d in %s", len, lsquic_pns2str[pns]);
LSQ_DEBUG("wrote ACK frame of size %d in %s to packet %" PRIu64,
len, lsquic_pns2str[pns], packet_out->po_packno);
if (pns == PNS_INIT && conn->imc_hsk_count > 0)
imico_generate_ping(conn, packet_out);
return 0;
}
@ -1801,9 +2076,10 @@ imico_generate_acks (struct ietf_mini_conn *conn, lsquic_time_t now)
for (pns = PNS_INIT; pns < IMICO_N_PNS; ++pns)
if (conn->imc_flags & (IMC_QUEUED_ACK_INIT << pns)
&& !(pns == PNS_INIT && (conn->imc_flags & IMC_IGNORE_INIT)))
{
if (0 != imico_generate_ack(conn, pns, now))
return -1;
}
return 0;
}
@ -1848,6 +2124,13 @@ imico_generate_conn_close (struct ietf_mini_conn *conn)
reason = "bad transport parameters";
rlen = 24;
}
else if (conn->imc_flags & IMC_VER_NEG_FAILED)
{
is_app = 0;
error_code = TEC_VERSION_NEGOTIATION_ERROR;
reason = "version negociation failed";
rlen = 26;
}
else if (conn->imc_flags & IMC_HSK_FAILED)
{
is_app = 0;
@ -1953,7 +2236,7 @@ imico_generate_handshake_done (struct ietf_mini_conn *conn)
int sz;
need = conn->imc_conn.cn_pf->pf_handshake_done_frame_size();
packet_out = imico_get_packet_out(conn, HETY_NOT_SET, need);
packet_out = imico_get_packet_out(conn, HETY_SHORT, need);
if (!packet_out)
return -1;
sz = conn->imc_conn.cn_pf->pf_gen_handshake_done_frame(
@ -1980,12 +2263,20 @@ ietf_mini_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
enum tick_st tick;
if (conn->imc_created + conn->imc_enpub->enp_settings.es_handshake_to < now)
if (conn->imc_expire < now)
{
LSQ_DEBUG("connection expired: closing");
return TICK_CLOSE;
}
if (!(conn->imc_flags &
(IMC_HAVE_TP|IMC_ADDR_VALIDATED|IMC_BAD_TRANS_PARAMS))
&& conn->imc_enpub->enp_settings.es_support_srej)
{
LSQ_DEBUG("Peer not validated and do not have transport parameters "
"on the first tick: retry");
return TICK_RETRY|TICK_CLOSE;
}
if (conn->imc_flags & (IMC_QUEUED_ACK_INIT|IMC_QUEUED_ACK_HSK))
{
@ -1997,6 +2288,9 @@ ietf_mini_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
}
if (lconn->cn_flags & (LSCONN_PROMOTED|LSCONN_PROMOTE_FAIL))
return TICK_CLOSE;
tick = 0;
if (conn->imc_flags & IMC_ERROR)
@ -2131,6 +2425,7 @@ ietf_mini_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
struct ietf_mini_conn *conn = (struct ietf_mini_conn *) lconn;
conn->imc_bytes_in += garbage_sz;
conn->imc_flags &= ~IMC_AMP_CAPPED;
LSQ_DEBUG("count %zd bytes of garbage, new value: %u bytes", garbage_sz,
conn->imc_bytes_in);
}

View file

@ -6,6 +6,8 @@
#ifndef LSQUIC_MINI_CONN_IETF_H
#define LSQUIC_MINI_CONN_IETF_H 1
#include "lsquic_ietf.h"
struct lsquic_conn;
struct lsquic_engine_public;
struct lsquic_packet_in;
@ -32,14 +34,13 @@ typedef uint64_t packno_set_t;
* connection is promoted. This means we do not have to have data
* structures to track the App PNS.
*/
#define IMICO_N_PNS (N_PNS - 1)
struct ietf_mini_conn
{
struct lsquic_conn imc_conn;
struct conn_cid_elem imc_cces[3];
struct lsquic_engine_public *imc_enpub;
lsquic_time_t imc_created;
lsquic_time_t imc_expire;
enum {
IMC_ENC_SESS_INITED = 1 << 0,
IMC_QUEUED_ACK_INIT = 1 << 1,
@ -66,6 +67,8 @@ struct ietf_mini_conn
IMC_PATH_CHANGED = 1 << 21,
IMC_HSK_DONE_SENT = 1 << 22,
IMC_TRECHIST = 1 << 23,
IMC_VER_NEG_FAILED = 1 << 24,
IMC_AMP_CAPPED = 1 << 25,
} imc_flags;
struct mini_crypto_stream imc_streams[N_ENC_LEVS];
void *imc_stream_ps[N_ENC_LEVS];
@ -87,7 +90,7 @@ struct ietf_mini_conn
packno_set_t imc_acked_packnos[IMICO_N_PNS];
lsquic_time_t imc_largest_recvd[IMICO_N_PNS];
struct lsquic_rtt_stats imc_rtt_stats;
unsigned imc_error_code;
enum trans_error_code imc_error_code;
unsigned imc_bytes_in;
unsigned imc_bytes_out;
unsigned short imc_crypto_frames_sz;
@ -112,6 +115,8 @@ struct ietf_mini_conn
unsigned char imc_delayed_packets_count;
#define IMICO_MAX_STASHED_FRAMES 10u
unsigned char imc_n_crypto_frames;
unsigned short imc_hello_pkt_remain;
unsigned char imc_long_header_sz;
struct network_path imc_path;
};
@ -128,7 +133,7 @@ struct ietf_mini_conn
struct lsquic_conn *
lsquic_mini_conn_ietf_new (struct lsquic_engine_public *,
const struct lsquic_packet_in *,
struct lsquic_packet_in *,
enum lsquic_version, int is_ipv4, const struct lsquic_cid *,
size_t udp_payload_size);

View file

@ -49,11 +49,11 @@ lsquic_frame_types_to_str (char *buf, size_t bufsz,
const char *const lsquic_hety2str[] =
{
[HETY_NOT_SET] = "Short",
[HETY_VERNEG] = "Version Negotiation",
[HETY_INITIAL] = "Initial",
[HETY_RETRY] = "Retry",
[HETY_HANDSHAKE] = "Handshake",
[HETY_SHORT] = "SHORT",
[HETY_VERNEG] = "VERNEG",
[HETY_INITIAL] = "INIT",
[HETY_RETRY] = "RETRY",
[HETY_HANDSHAKE] = "HSK",
[HETY_0RTT] = "0-RTT",
};
@ -61,7 +61,7 @@ const char *const lsquic_hety2str[] =
/* [draft-ietf-quic-tls-14], Section 4 */
const enum packnum_space lsquic_hety2pns[] =
{
[HETY_NOT_SET] = PNS_APP,
[HETY_SHORT] = PNS_APP,
[HETY_VERNEG] = 0,
[HETY_INITIAL] = PNS_INIT,
[HETY_RETRY] = 0,
@ -73,16 +73,16 @@ const enum packnum_space lsquic_hety2pns[] =
/* [draft-ietf-quic-tls-14], Section 4 */
const enum packnum_space lsquic_enclev2pns[] =
{
[ENC_LEV_CLEAR] = PNS_INIT,
[ENC_LEV_INIT] = PNS_HSK,
[ENC_LEV_EARLY] = PNS_APP,
[ENC_LEV_FORW] = PNS_APP,
[ENC_LEV_INIT] = PNS_INIT,
[ENC_LEV_HSK] = PNS_HSK,
[ENC_LEV_0RTT] = PNS_APP,
[ENC_LEV_APP] = PNS_APP,
};
const char *const lsquic_pns2str[] =
{
[PNS_INIT] = "Init PNS",
[PNS_HSK] = "Handshake PNS",
[PNS_APP] = "App PNS",
[PNS_INIT] = "INIT pns",
[PNS_HSK] = "HSK pns",
[PNS_APP] = "APP pns",
};

View file

@ -176,7 +176,8 @@ enum
enum header_type
{
HETY_NOT_SET, /* This value must be zero */
HETY_NOT_SET, /* This value must be zero */
HETY_SHORT = HETY_NOT_SET, /* This value must be zero */
HETY_VERNEG,
HETY_INITIAL,
HETY_RETRY,
@ -197,6 +198,7 @@ enum packnum_space
PNS_INIT,
PNS_HSK,
PNS_APP,
IMICO_N_PNS = PNS_APP,
N_PNS
};
@ -240,8 +242,9 @@ extern const char *const lsquic_pns2str[];
* regenerating them. This keeps the code simple(r).
*/
#define IQUIC_FRAME_RETX_MASK ( \
ALL_IQUIC_FRAMES & ~(QUIC_FTBIT_PADDING|QUIC_FTBIT_PATH_RESPONSE \
|QUIC_FTBIT_PATH_CHALLENGE|QUIC_FTBIT_ACK|QUIC_FTBIT_TIMESTAMP))
ALL_IQUIC_FRAMES & ~(QUIC_FTBIT_PADDING | QUIC_FTBIT_PATH_RESPONSE \
| QUIC_FTBIT_PATH_CHALLENGE | QUIC_FTBIT_ACK \
| QUIC_FTBIT_TIMESTAMP | QUIC_FTBIT_PING))
extern const enum quic_ft_bit lsquic_legal_frames_by_level[][4];

View file

@ -88,6 +88,8 @@ typedef struct lsquic_packet_in
PI_LOG_QL_BITS = (1 <<14),
PI_SQUARE_BIT = (1 <<15),
PI_LOSS_BIT = (1 <<16),
PI_VER_PARSED = (1 <<17),
PI_FIRST_INIT = (1 <<18),
} pi_flags;
/* pi_token and pi_token_size are set in Initial and Retry packets */
unsigned short pi_token_size; /* Size of the token */
@ -101,6 +103,7 @@ typedef struct lsquic_packet_in
unsigned char pi_nonce; /* Offset to nonce */
enum header_type pi_header_type:8;
unsigned char pi_path_id;
unsigned char pi_version; /* parsed enum lsquic_version */
/* If PI_OWN_DATA flag is not set, `pi_data' points to user-supplied
* packet data, which is NOT TO BE MODIFIED.
*/

View file

@ -382,7 +382,7 @@ lsquic_packet_out_chop_regen (lsquic_packet_out_t *packet_out)
}
}
assert(adj); /* Otherwise why are we called? */
//assert(adj); /* Otherwise why are we called? */
packet_out->po_regen_sz = 0;
packet_out->po_frame_types &= ~BQUIC_FRAME_REGEN_MASK;
}
@ -479,7 +479,7 @@ lsquic_packet_out_turn_on_fin (struct lsquic_packet_out *packet_out,
static unsigned
offset_to_dcid (const struct lsquic_packet_out *packet_out)
{
if (packet_out->po_header_type == HETY_NOT_SET)
if (packet_out->po_header_type == HETY_SHORT)
return 1;
else
{

View file

@ -111,6 +111,8 @@ typedef struct lsquic_packet_out
PO_SCHED = (1 <<14), /* On scheduled queue */
PO_SENT_SZ = (1 <<15),
PO_LONGHEAD = (1 <<16),
PO_ACKED_LOSS_CHAIN = (1<<17),
#define POIPv6_SHIFT 20
PO_IPv6 = (1 <<20), /* Set if pmi_allocate was passed is_ipv6=1,
* otherwise unset.
@ -185,6 +187,8 @@ typedef struct lsquic_packet_out
unsigned char *po_enc_data;
lsquic_ver_tag_t po_ver_tag; /* Set if PO_VERSION is set */
unsigned short po_retx_cnt;
unsigned short po_padding_sz;
unsigned char *po_nonce; /* Use to generate header if PO_NONCE is set */
const struct network_path
*po_path;

View file

@ -486,7 +486,7 @@ gquic_Q050_packout_size (const struct lsquic_conn *lconn,
size_t sz;
if ((lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
&& packet_out->po_header_type == HETY_NOT_SET)
&& packet_out->po_header_type == HETY_SHORT)
sz = gquic_Q050_packout_header_size_short(lconn, packet_out->po_flags);
else
sz = gquic_Q050_packout_header_size_long_by_packet(lconn, packet_out);

View file

@ -21,26 +21,27 @@ parse_ietf_v1_or_Q046plus_long_begin (struct lsquic_packet_in *packet_in,
size_t length, int is_server, unsigned cid_len,
struct packin_parse_state *state)
{
lsquic_ver_tag_t tag;
if (length >= 5)
{
memcpy(&tag, packet_in->pi_data + 1, 4);
switch (tag)
{
case TAG('Q', '0', '4', '6'):
return lsquic_Q046_parse_packet_in_long_begin(packet_in, length,
is_server, cid_len, state);
case TAG('Q', '0', '5', '0'):
return lsquic_Q050_parse_packet_in_long_begin(packet_in, length,
is_server, cid_len, state);
default:
return lsquic_ietf_v1_parse_packet_in_long_begin(packet_in, length,
is_server, cid_len, state);
}
}
else
enum lsquic_version version;
if (length < 6)
return -1;
version = lsquic_tag2ver_fast(packet_in->pi_data + 1);
if (version != N_LSQVER)
{
packet_in->pi_version = version;
packet_in->pi_flags |= PI_VER_PARSED;
}
switch (version)
{
case LSQVER_046:
return lsquic_Q046_parse_packet_in_long_begin(packet_in, length,
is_server, cid_len, state);
case LSQVER_050:
return lsquic_Q050_parse_packet_in_long_begin(packet_in, length,
is_server, cid_len, state);
default:
return lsquic_ietf_v1_parse_packet_in_long_begin(packet_in, length,
is_server, cid_len, state);
}
}
@ -51,10 +52,10 @@ static int (* const parse_begin_funcs[32]) (struct lsquic_packet_in *,
/* Xs vary, Gs are iGnored: */
#define PBEL(mask) [(mask) >> 3]
/* 1X11 XGGG: */
PBEL(0x80|0x40|0x20|0x10|0x08) = lsquic_Q046_parse_packet_in_long_begin,
PBEL(0x80|0x00|0x20|0x10|0x08) = lsquic_Q046_parse_packet_in_long_begin,
PBEL(0x80|0x40|0x20|0x10|0x00) = lsquic_Q046_parse_packet_in_long_begin,
PBEL(0x80|0x00|0x20|0x10|0x00) = lsquic_Q046_parse_packet_in_long_begin,
PBEL(0x80|0x40|0x20|0x10|0x08) = parse_ietf_v1_or_Q046plus_long_begin,
PBEL(0x80|0x00|0x20|0x10|0x08) = parse_ietf_v1_or_Q046plus_long_begin,
PBEL(0x80|0x40|0x20|0x10|0x00) = parse_ietf_v1_or_Q046plus_long_begin,
PBEL(0x80|0x00|0x20|0x10|0x00) = parse_ietf_v1_or_Q046plus_long_begin,
/* 1X00 XGGG: */
PBEL(0x80|0x40|0x00|0x00|0x08) = parse_ietf_v1_or_Q046plus_long_begin,
PBEL(0x80|0x00|0x00|0x00|0x08) = parse_ietf_v1_or_Q046plus_long_begin,
@ -336,10 +337,10 @@ lsquic_dcid_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_I001] = {
[ENC_LEV_CLEAR] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
[LSQVER_I002] = {
[ENC_LEV_INIT] = 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
[ENC_LEV_0RTT] = 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
@ -349,9 +350,39 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
| QUIC_FTBIT_PATH_CHALLENGE
| QUIC_FTBIT_DATAGRAM
| QUIC_FTBIT_RETIRE_CONNECTION_ID,
[ENC_LEV_INIT] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
[ENC_LEV_HSK] = 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
[ENC_LEV_APP] = 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_I001] = {
[ENC_LEV_INIT] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
| QUIC_FTBIT_ACK | QUIC_FTBIT_CONNECTION_CLOSE,
[ENC_LEV_0RTT] = 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_DATAGRAM
| QUIC_FTBIT_RETIRE_CONNECTION_ID,
[ENC_LEV_HSK] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
| QUIC_FTBIT_ACK| QUIC_FTBIT_CONNECTION_CLOSE,
[ENC_LEV_APP] = 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
@ -367,9 +398,9 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
,
},
[LSQVER_ID29] = {
[ENC_LEV_CLEAR] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
[ENC_LEV_INIT] = 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
[ENC_LEV_0RTT] = 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
@ -379,9 +410,9 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
| 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
[ENC_LEV_HSK] = 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
[ENC_LEV_APP] = 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
@ -397,9 +428,9 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
,
},
[LSQVER_ID27] = {
[ENC_LEV_CLEAR] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
[ENC_LEV_INIT] = 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
[ENC_LEV_0RTT] = QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
| QUIC_FTBIT_STREAM | QUIC_FTBIT_RST_STREAM
| QUIC_FTBIT_BLOCKED
| QUIC_FTBIT_MAX_DATA | QUIC_FTBIT_MAX_STREAM_DATA
@ -410,9 +441,9 @@ const enum quic_ft_bit lsquic_legal_frames_by_level[N_LSQVER][N_ENC_LEVS] =
| QUIC_FTBIT_RETIRE_CONNECTION_ID
| QUIC_FTBIT_DATAGRAM
,
[ENC_LEV_INIT] = QUIC_FTBIT_CRYPTO | QUIC_FTBIT_PADDING | QUIC_FTBIT_PING
[ENC_LEV_HSK] = 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
[ENC_LEV_APP] = 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

View file

@ -68,6 +68,11 @@ int
lsquic_ietf_v1_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions,
uint8_t);
int
lsquic_iquic_gen_retry_pkt (unsigned char *buf, size_t bufsz,
const struct lsquic_engine_public *, const lsquic_cid_t *scid,
const lsquic_cid_t *dcid, enum lsquic_version, const struct sockaddr *,
uint8_t random_nybble);
#define GQUIC_RESET_SZ 33
ssize_t

View file

@ -142,7 +142,7 @@ ietf_v1_packout_max_header_size (const struct lsquic_conn *lconn,
enum packet_out_flags flags, size_t dcid_len, enum header_type header_type)
{
if ((lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
&& header_type == HETY_NOT_SET)
&& header_type == HETY_SHORT)
return ietf_v1_packout_header_size_short(flags, dcid_len);
else
return ietf_v1_packout_header_size_long_by_flags(lconn, header_type,
@ -159,6 +159,15 @@ static const unsigned char header_type_to_bin[] = {
};
/* [draft-ietf-quic-v2] Section-3.2 */
static const unsigned char header_type_to_bin_v2[] = {
[HETY_INITIAL] = 0x1,
[HETY_0RTT] = 0x2,
[HETY_HANDSHAKE] = 0x3,
[HETY_RETRY] = 0x0,
};
static unsigned
write_packno (unsigned char *p, lsquic_packno_t packno,
enum packno_bits bits)
@ -205,10 +214,16 @@ gen_long_pkt_header (const struct lsquic_conn *lconn,
packno_bits = lsquic_packet_out_packno_bits(packet_out);
p = buf;
*p++ = 0xC0
| ( header_type_to_bin[ packet_out->po_header_type ] << 4)
| packno_bits
;
if (lconn->cn_version == LSQVER_I002)
*p++ = 0xC0
| ( header_type_to_bin_v2[ packet_out->po_header_type ] << 4)
| packno_bits
;
else
*p++ = 0xC0
| ( header_type_to_bin[ packet_out->po_header_type ] << 4)
| packno_bits
;
ver_tag = lsquic_ver2tag(lconn->cn_version);
memcpy(p, &ver_tag, sizeof(ver_tag));
p += sizeof(ver_tag);
@ -285,7 +300,7 @@ ietf_v1_gen_reg_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz, unsigned *packno_off, unsigned *packno_len)
{
if (packet_out->po_header_type == HETY_NOT_SET)
if (packet_out->po_header_type == HETY_SHORT)
return gen_short_pkt_header(lconn, packet_out, buf, bufsz, packno_off,
packno_len);
else
@ -301,7 +316,7 @@ ietf_v1_packout_size (const struct lsquic_conn *lconn,
size_t sz;
if ((lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
&& packet_out->po_header_type == HETY_NOT_SET)
&& packet_out->po_header_type == HETY_SHORT)
sz = ietf_v1_packout_header_size_short(packet_out->po_flags,
packet_out->po_path->np_dcid.len);
else
@ -1700,16 +1715,21 @@ static const enum header_type bits2ht[4] =
};
static const enum header_type bits2ht_v2[4] =
{
[0] = HETY_RETRY,
[1] = HETY_INITIAL,
[2] = HETY_0RTT,
[3] = HETY_HANDSHAKE,
};
#if LSQUIC_QIR
/* Return true if the parsing function is to enforce the minimum DCID
* length requirement as specified in IETF v1 and the I-Ds.
*/
static int
enforce_initial_dcil (lsquic_ver_tag_t tag)
enforce_initial_dcil (enum lsquic_version version)
{
enum lsquic_version version;
version = lsquic_tag2ver(tag);
return version != (enum lsquic_version) -1
&& ((1 << version) & LSQUIC_IETF_VERSIONS);
}
@ -1723,22 +1743,28 @@ lsquic_ietf_v1_parse_packet_in_long_begin (struct lsquic_packet_in *packet_in,
{
const unsigned char *p = packet_in->pi_data;
const unsigned char *const end = p + length;
lsquic_ver_tag_t tag;
enum header_type header_type;
unsigned dcil, scil;
int verneg, r;
int r;
unsigned char first_byte;
uint64_t payload_len, token_len;
if (length < 6)
return -1;
first_byte = *p++;
memcpy(&tag, p, 4);
if ((packet_in->pi_flags & PI_VER_PARSED) == 0)
{
packet_in->pi_version = lsquic_tag2ver_fast(p);
packet_in->pi_flags |= PI_VER_PARSED;
}
p += 4;
verneg = 0 == tag;
if (!verneg)
header_type = bits2ht[ (first_byte >> 4) & 3 ];
if (packet_in->pi_version != LSQVER_VERNEG)
{
if (packet_in->pi_version == LSQVER_I002)
header_type = bits2ht_v2[ (first_byte >> 4) & 3 ];
else
header_type = bits2ht[ (first_byte >> 4) & 3 ];
}
else
header_type = HETY_VERNEG;
@ -1769,7 +1795,7 @@ lsquic_ietf_v1_parse_packet_in_long_begin (struct lsquic_packet_in *packet_in,
{
case HETY_INITIAL:
#if LSQUIC_QIR
if (!enforce_initial_dcil(tag))
if (!enforce_initial_dcil(packet_in->pi_version))
{
/* Count even zero-length DCID as having DCID */
packet_in->pi_flags |= PI_CONN_ID;
@ -1893,7 +1919,10 @@ lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet (const unsigned char *buf,
return 0;
first_byte = *p++;
header_type = bits2ht[ (first_byte >> 4) & 3 ];
if (*p != 0x6B)
header_type = bits2ht[ (first_byte >> 4) & 3 ];
else
header_type = bits2ht_v2[ (first_byte >> 4) & 3 ];
if (header_type != HETY_INITIAL)
return 0;

View file

@ -11,6 +11,7 @@
#include <sys/types.h>
#include <openssl/rand.h>
#include <openssl/aead.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
@ -181,7 +182,7 @@ lsquic_Q046_parse_packet_in_short_begin (lsquic_packet_in_t *packet_in,
packet_in->pi_packno = packno;
p += packet_len;
packet_in->pi_header_type = HETY_NOT_SET;
packet_in->pi_header_type = HETY_SHORT;
packet_in->pi_quic_ver = 0;
packet_in->pi_nonce = 0;
packet_in->pi_header_sz = p - packet_in->pi_data;
@ -320,8 +321,6 @@ popcount (unsigned v)
++count;
return count;
}
#endif
@ -379,3 +378,76 @@ lsquic_Q046_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
}
int
lsquic_iquic_gen_retry_pkt (unsigned char *buf, size_t bufsz,
const struct lsquic_engine_public *enpub,
const lsquic_cid_t *scid, const lsquic_cid_t *dcid,
enum lsquic_version version, const struct sockaddr *sockaddr,
uint8_t random_nybble)
{
struct token_generator *const tokgen = enpub->enp_tokgen;
const unsigned our_scid_len = enpub->enp_settings.es_scid_len;
unsigned char *const end = buf + bufsz;
unsigned char *p = buf;
lsquic_ver_tag_t ver_tag;
size_t ad_len, out_len;
unsigned ret_ver;
ssize_t sz;
const size_t INTEGRITY_TAG_LEN = 16;
/* [draft-ietf-quic-tls-25] Section 5.8 specifies the layout of the
* Retry Pseudo-Packet:
*/
unsigned char ad_buf[
1 + MAX_CID_LEN /* ODCID */
+ 1 + 4 /* Type and version */
+ 1 + MAX_CID_LEN /* DCID */
+ 1 + MAX_CID_LEN /* SCID */
+ MAX_RETRY_TOKEN_LEN /* Retry token */
];
unsigned char tag[INTEGRITY_TAG_LEN];
/* See [draft-ietf-quic-transport-25], Section 17.2.5 */
if (bufsz < 1 + sizeof(ver_tag) + 1 + our_scid_len + 1 + dcid->len
+ MAX_RETRY_TOKEN_LEN + INTEGRITY_TAG_LEN)
return -1;
p = ad_buf;
*p++ = dcid->len;
memcpy(p, dcid->idbuf, dcid->len);
p += dcid->len;
*p++ = 0xC0
| (3 << 4)
| random_nybble
;
ver_tag = lsquic_ver2tag(version);
memcpy(p, &ver_tag, sizeof(ver_tag));
p += sizeof(ver_tag);
*p++ = scid->len;
memcpy(p, scid->idbuf, scid->len);
p += scid->len;
*p++ = our_scid_len;
RAND_bytes(p, our_scid_len);
p += our_scid_len;
sz = lsquic_tg_generate_retry(tokgen, p, end - p,
p - our_scid_len, our_scid_len, sockaddr, dcid);
if (sz < 0)
return -1;
p += sz;
ad_len = p - ad_buf;
ret_ver = lsquic_version_2_retryver(version);
out_len = sizeof(tag);
if (!(1 == EVP_AEAD_CTX_seal(&enpub->enp_retry_aead_ctx[ret_ver], tag,
&out_len, out_len, lsquic_retry_nonce_buf[ret_ver],
IETF_RETRY_NONCE_SZ,
NULL, 0, ad_buf, ad_len) && out_len == sizeof(tag)))
return -1;
memcpy(buf, ad_buf + 1 + dcid->len, ad_len - 1 - dcid->len);
memcpy(buf + ad_len - 1 - dcid->len, tag, sizeof(tag));
return ad_len - 1 - dcid->len + sizeof(tag);
}

View file

@ -84,6 +84,13 @@ struct evanescent_conn
+ 1 /* DCIL */ + MAX_CID_LEN + 1 /* SCIL */ + MAX_CID_LEN + \
4 * N_LSQVER)
/* [draft-ietf-quic-transport-22], Section 17.2.5 */
#define IQUIC_RETRY_SIZE (1 /* Type */ + 4 /* Version */ + \
+ 1 /* DCIL */ + MAX_CID_LEN + 1 /* SCIL */ + MAX_CID_LEN + \
+ 1 /* ODCIL */ + MAX_CID_LEN + MAX_RETRY_TOKEN_LEN)
/* GQUIC retry is dynamically size, this is a reasonable high bound */
#define GQUIC_RETRY_SIZE 512
struct pr_queue
{
@ -262,7 +269,7 @@ put_req (struct pr_queue *prq, struct packet_req *req)
}
static int
int
lsquic_prq_new_req_ext (struct pr_queue *prq, enum packet_req_type type,
unsigned flags, enum lsquic_version version, unsigned short data_sz,
const lsquic_cid_t *dcid, const lsquic_cid_t *scid, void *peer_ctx,
@ -370,10 +377,11 @@ lsquic_prq_new_req (struct pr_queue *prq, enum packet_req_type type,
static size_t
max_bufsz (const struct pr_queue *prq)
{
return MAX(MAX(MAX(IQUIC_VERNEG_SIZE,
IQUIC_MIN_SRST_SIZE),
sizeof(prq->prq_verneg_g_buf)),
sizeof(prq->prq_pubres_g_buf));
return MAX(MAX(MAX(MAX(IQUIC_VERNEG_SIZE,
IQUIC_RETRY_SIZE),
IQUIC_MAX_SRST_SIZE),
sizeof(prq->prq_verneg_g_buf)),
sizeof(prq->prq_pubres_g_buf));
}
@ -484,6 +492,17 @@ lsquic_prq_next_conn (struct pr_queue *prq)
else
packet_out->po_data_sz = 0;
break;
case (PACKET_REQ_RETRY << 29) | 0:
packet_out->po_flags |= PO_RETRY;
len = lsquic_iquic_gen_retry_pkt(packet_out->po_data, max_bufsz(prq),
prq->prq_enpub, &req->pr_scid, &req->pr_dcid,
req->pr_version, NP_PEER_SA(&req->pr_path),
lsquic_crand_get_nybble(prq->prq_enpub->enp_crand));
if (len > 0)
packet_out->po_data_sz = len;
else
packet_out->po_data_sz = 0;
break;
default:
packet_out->po_flags &= ~PO_VERNEG;
packet_out->po_data_sz = req->pr_rst_sz;

View file

@ -51,6 +51,7 @@ struct sockaddr;
enum packet_req_type {
PACKET_REQ_VERNEG,
PACKET_REQ_PUBRES,
PACKET_REQ_RETRY,
N_PREQ_TYPES,
};
@ -75,6 +76,12 @@ lsquic_prq_next_conn (struct pr_queue *);
int
lsquic_prq_have_pending (const struct pr_queue *);
int
lsquic_prq_new_req_ext (struct pr_queue *prq, enum packet_req_type type,
unsigned flags, enum lsquic_version version, unsigned short data_sz,
const lsquic_cid_t *dcid, const lsquic_cid_t *scid, void *peer_ctx,
const struct sockaddr *local_addr, const struct sockaddr *peer_addr);
void
lsquic_prq_drop (struct lsquic_conn *);

View file

@ -68,6 +68,7 @@
#define DEFAULT_RETX_DELAY 500000 /* Microseconds */
#define MAX_RTO_DELAY 60000000 /* Microseconds */
#define MIN_RTO_DELAY 200000 /* Microseconds */
#define INITIAL_RTT 333333 /* Microseconds */
#define N_NACKS_BEFORE_RETX 3
#define CGP(ctl) ((struct cong_ctl *) (ctl)->sc_cong_ctl)
@ -260,6 +261,10 @@ get_retx_delay (const struct lsquic_rtt_stats *rtt_stats)
}
static lsquic_time_t
calculate_packet_rto (lsquic_send_ctl_t *ctl);
static void
retx_alarm_rings (enum alarm_id al_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now)
{
@ -275,7 +280,7 @@ retx_alarm_rings (enum alarm_id al_id, void *ctx, lsquic_time_t expiry, lsquic_t
assert(!lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX_INIT + pns));
rm = get_retx_mode(ctl);
LSQ_INFO("retx timeout, mode %s", retx2str[rm]);
LSQ_INFO("%s timeout, mode %s", lsquic_alid2str[al_id], retx2str[rm]);
switch (rm)
{
@ -287,18 +292,23 @@ retx_alarm_rings (enum alarm_id al_id, void *ctx, lsquic_time_t expiry, lsquic_t
send_ctl_detect_losses(ctl, pns, now);
break;
case RETX_MODE_TLP:
ctl->sc_last_rto_time = now;
++ctl->sc_n_tlp;
send_ctl_expire(ctl, pns, EXFI_LAST);
break;
case RETX_MODE_RTO:
ctl->sc_last_rto_time = now;
++ctl->sc_n_consec_rtos;
ctl->sc_next_limit = 2;
LSQ_DEBUG("packet RTO is %"PRIu64" usec", expiry);
if ( now - ctl->sc_last_rto_time >= calculate_packet_rto(ctl))
{
ctl->sc_last_rto_time = now;
++ctl->sc_n_consec_rtos;
ctl->sc_next_limit = 2;
ctl->sc_ci->cci_timeout(CGP(ctl));
if (lconn->cn_if->ci_retx_timeout)
lconn->cn_if->ci_retx_timeout(lconn);
}
LSQ_DEBUG("packet RTO is %"PRIu64" (+%"PRIu64") usec, consec RTOs: %d",
expiry, now - expiry, ctl->sc_n_consec_rtos);
send_ctl_expire(ctl, pns, EXFI_ALL);
ctl->sc_ci->cci_timeout(CGP(ctl));
if (lconn->cn_if->ci_retx_timeout)
lconn->cn_if->ci_retx_timeout(lconn);
break;
}
@ -450,18 +460,14 @@ calculate_tlp_delay (lsquic_send_ctl_t *ctl)
lsquic_time_t srtt, delay;
srtt = lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats);
if (!srtt)
srtt = INITIAL_RTT;
if (ctl->sc_n_in_flight_all > 1)
{
delay = 10000; /* 10 ms is the minimum tail loss probe delay */
if (delay < 2 * srtt)
delay = 2 * srtt;
}
else
{
delay = srtt + srtt / 2 + ctl->sc_conn_pub->max_peer_ack_usec;
if (delay < 2 * srtt)
delay = 2 * srtt;
}
if (delay < 2 * srtt)
delay = 2 * srtt;
return delay;
}
@ -519,8 +525,9 @@ set_retx_alarm (struct lsquic_send_ctl *ctl, enum packnum_space pns,
if (delay > MAX_RTO_DELAY)
delay = MAX_RTO_DELAY;
LSQ_DEBUG("set retx alarm to %"PRIu64", which is %"PRIu64
" usec from now, mode %s", now + delay, delay, retx2str[rm]);
LSQ_DEBUG("set RETX_%s alarm to %"PRIu64" (%"PRIu64
"), mode %s", lsquic_pns2str[pns],
now + delay, delay, retx2str[rm]);
lsquic_alarmset_set(ctl->sc_alset, AL_RETX_INIT + pns, now + delay);
if (PNS_APP == pns
@ -656,7 +663,7 @@ send_ctl_add_poison (struct lsquic_send_ctl *ctl)
poison->po_packno = ctl->sc_gap;
poison->po_loss_chain = poison; /* Won't be used, but just in case */
TAILQ_INSERT_TAIL(&ctl->sc_unacked_packets[PNS_APP], poison, po_next);
LSQ_DEBUG("insert poisoned packet %"PRIu64, poison->po_packno);
LSQ_DEBUG("insert poisoned packet #%"PRIu64, poison->po_packno);
ctl->sc_flags |= SC_POISON;
return 0;
}
@ -672,7 +679,7 @@ send_ctl_reschedule_poison (struct lsquic_send_ctl *ctl)
TAILQ_FOREACH(poison, &ctl->sc_unacked_packets[PNS_APP], po_next)
if (poison->po_flags & PO_POISON)
{
LSQ_DEBUG("remove poisoned packet %"PRIu64, poison->po_packno);
LSQ_DEBUG("remove poisoned packet #%"PRIu64, poison->po_packno);
TAILQ_REMOVE(&ctl->sc_unacked_packets[PNS_APP], poison, po_next);
lsquic_malo_put(poison);
lsquic_send_ctl_begin_optack_detection(ctl);
@ -690,7 +697,7 @@ send_ctl_reschedule_poison (struct lsquic_send_ctl *ctl)
}
else
log_level = LSQ_LOG_DEBUG;
LSQ_LOG(log_level, "odd: poisoned packet %"PRIu64" not found during "
LSQ_LOG(log_level, "odd: poisoned packet #%"PRIu64" not found during "
"reschedule, flag: %d", ctl->sc_gap, !!(ctl->sc_flags & SC_POISON));
}
@ -734,7 +741,7 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
pns = lsquic_packet_out_pns(packet_out);
if (0 != send_ctl_update_poison_hist(ctl, packet_out->po_packno))
return -1;
LSQ_DEBUG("packet %"PRIu64" has been sent (frame types: %s)",
LSQ_DEBUG("sent packet #%"PRIu64" (%s)",
packet_out->po_packno, lsquic_frame_types_to_str(frames,
sizeof(frames), packet_out->po_frame_types));
lsquic_senhist_add(&ctl->sc_senhist, packet_out->po_packno);
@ -795,7 +802,7 @@ take_rtt_sample (lsquic_send_ctl_t *ctl,
const lsquic_packno_t packno = ctl->sc_largest_acked_packno;
const lsquic_time_t sent = ctl->sc_largest_acked_sent_time;
const lsquic_time_t measured_rtt = now - sent;
if (packno > ctl->sc_max_rtt_packno && lack_delta < measured_rtt)
if ((!packno || packno > ctl->sc_max_rtt_packno) && lack_delta < measured_rtt)
{
if (UNLIKELY(ctl->sc_flags & SC_ROUGH_RTT))
{
@ -860,6 +867,88 @@ send_ctl_maybe_renumber_sched_to_right (struct lsquic_send_ctl *ctl,
}
static void
send_ctl_process_loss_chain_pkt (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *const chain_cur,
struct lsquic_packet_out **next)
{
unsigned packet_sz;
const char *state;
enum packnum_space pns;
switch (chain_cur->po_flags & (PO_SCHED|PO_UNACKED|PO_LOST))
{
case PO_SCHED:
send_ctl_maybe_renumber_sched_to_right(ctl, chain_cur);
send_ctl_sched_remove(ctl, chain_cur);
state = "scheduled";
break;
case PO_UNACKED:
if (chain_cur->po_flags & PO_LOSS_REC)
{
pns = lsquic_packet_out_pns(chain_cur);
TAILQ_REMOVE(&ctl->sc_unacked_packets[pns], chain_cur, po_next);
state = "loss record";
}
else
{
packet_sz = packet_out_sent_sz(chain_cur);
send_ctl_unacked_remove(ctl, chain_cur, packet_sz);
state = "unacked";
}
break;
case PO_LOST:
TAILQ_REMOVE(&ctl->sc_lost_packets, chain_cur, po_next);
state = "lost";
break;
case 0:
/* This is also weird, but let it pass */
state = "unknown";
break;
default:
assert(0);
break;
}
if (next && *next == chain_cur)
*next = TAILQ_NEXT(*next, po_next);
if (0 == (chain_cur->po_flags & PO_LOSS_REC))
lsquic_packet_out_ack_streams(chain_cur);
LSQ_DEBUG("loss chain, destroy %s packet #%"PRIu64, state,
chain_cur->po_packno);
send_ctl_destroy_packet(ctl, chain_cur);
}
static void
send_ctl_acked_loss_chain (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *const packet_out,
struct lsquic_packet_out **next,
lsquic_packno_t largest_acked)
{
struct lsquic_packet_out *chain_cur, *chain_next;
unsigned count;
count = 0;
for (chain_cur = packet_out->po_loss_chain; chain_cur != packet_out;
chain_cur = chain_next)
{
chain_next = chain_cur->po_loss_chain;
if (chain_cur->po_packno > packet_out->po_packno
&& chain_cur->po_packno <= largest_acked
&& (chain_cur->po_flags & PO_LOST) == 0)
{
chain_cur->po_flags |= PO_ACKED_LOSS_CHAIN;
continue;
}
send_ctl_process_loss_chain_pkt(ctl, chain_cur, next);
++count;
}
packet_out->po_loss_chain = packet_out;
if (count)
LSQ_DEBUG("destroyed %u packet%.*s in chain of packet #%"PRIu64,
count, count != 1, "s", packet_out->po_packno);
}
/* The third argument to advance `next' pointer when modifying the unacked
* queue. This is because the unacked queue may contain several elements
* of the same chain. This is not true of the lost and scheduled packet
@ -871,50 +960,19 @@ send_ctl_destroy_chain (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out **next)
{
struct lsquic_packet_out *chain_cur, *chain_next;
unsigned packet_sz, count;
enum packnum_space pns = lsquic_packet_out_pns(packet_out);
unsigned count;
count = 0;
for (chain_cur = packet_out->po_loss_chain; chain_cur != packet_out;
chain_cur = chain_next)
{
chain_next = chain_cur->po_loss_chain;
switch (chain_cur->po_flags & (PO_SCHED|PO_UNACKED|PO_LOST))
{
case PO_SCHED:
send_ctl_maybe_renumber_sched_to_right(ctl, chain_cur);
send_ctl_sched_remove(ctl, chain_cur);
break;
case PO_UNACKED:
if (chain_cur->po_flags & PO_LOSS_REC)
TAILQ_REMOVE(&ctl->sc_unacked_packets[pns], chain_cur, po_next);
else
{
packet_sz = packet_out_sent_sz(chain_cur);
send_ctl_unacked_remove(ctl, chain_cur, packet_sz);
}
break;
case PO_LOST:
TAILQ_REMOVE(&ctl->sc_lost_packets, chain_cur, po_next);
break;
case 0:
/* This is also weird, but let it pass */
break;
default:
assert(0);
break;
}
if (next && *next == chain_cur)
*next = TAILQ_NEXT(*next, po_next);
if (0 == (chain_cur->po_flags & PO_LOSS_REC))
lsquic_packet_out_ack_streams(chain_cur);
send_ctl_destroy_packet(ctl, chain_cur);
send_ctl_process_loss_chain_pkt(ctl, chain_cur, next);
++count;
}
packet_out->po_loss_chain = packet_out;
if (count)
LSQ_DEBUG("destroyed %u packet%.*s in chain of packet %"PRIu64,
LSQ_DEBUG("destroyed %u packet%.*s in chain of packet #%"PRIu64,
count, count != 1, "s", packet_out->po_packno);
}
@ -972,7 +1030,7 @@ send_ctl_handle_regular_lost_packet (struct lsquic_send_ctl *ctl,
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
{
ctl->sc_flags |= SC_LOST_ACK_INIT << lsquic_packet_out_pns(packet_out);
LSQ_DEBUG("lost ACK in packet %"PRIu64, packet_out->po_packno);
LSQ_DEBUG("lost ACK in packet #%"PRIu64, packet_out->po_packno);
}
if (ctl->sc_ci->cci_lost)
@ -990,7 +1048,7 @@ send_ctl_handle_regular_lost_packet (struct lsquic_send_ctl *ctl,
if (packet_out->po_frame_types & ctl->sc_retx_frames)
{
LSQ_DEBUG("lost retransmittable packet %"PRIu64,
LSQ_DEBUG("lost retransmittable packet #%"PRIu64,
packet_out->po_packno);
loss_record = send_ctl_record_loss(ctl, packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
@ -1000,7 +1058,7 @@ send_ctl_handle_regular_lost_packet (struct lsquic_send_ctl *ctl,
}
else
{
LSQ_DEBUG("lost unretransmittable packet %"PRIu64,
LSQ_DEBUG("lost unretransmittable packet #%"PRIu64,
packet_out->po_packno);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
send_ctl_destroy_chain(ctl, packet_out, next);
@ -1016,7 +1074,7 @@ send_ctl_handle_lost_mtu_probe (struct lsquic_send_ctl *ctl,
{
unsigned packet_sz;
LSQ_DEBUG("lost MTU probe in packet %"PRIu64, packet_out->po_packno);
LSQ_DEBUG("lost MTU probe in packet #%"PRIu64, packet_out->po_packno);
packet_sz = packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
assert(packet_out->po_loss_chain == packet_out);
@ -1090,7 +1148,7 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
if (packet_out->po_packno + ctl->sc_reord_thresh <
ctl->sc_largest_acked_packno)
{
LSQ_DEBUG("loss by FACK detected (dist: %"PRIu64"), packet %"PRIu64,
LSQ_DEBUG("loss by FACK detected (dist: %"PRIu64"), packet #%"PRIu64,
ctl->sc_largest_acked_packno - packet_out->po_packno,
packet_out->po_packno);
if (0 == (packet_out->po_flags & PO_MTU_PROBE))
@ -1111,12 +1169,12 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
&& 0 == (packet_out->po_flags & PO_MTU_PROBE)
&& largest_retx_packno <= ctl->sc_largest_acked_packno)
{
LSQ_DEBUG("loss by early retransmit detected, packet %"PRIu64,
LSQ_DEBUG("loss by early retransmit detected, packet #%"PRIu64,
packet_out->po_packno);
largest_lost_packno = packet_out->po_packno;
ctl->sc_loss_to =
lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats) / 4;
LSQ_DEBUG("set sc_loss_to to %"PRIu64", packet %"PRIu64,
LSQ_DEBUG("set sc_loss_to to %"PRIu64", packet #%"PRIu64,
ctl->sc_loss_to, packet_out->po_packno);
(void) send_ctl_handle_lost_packet(ctl, packet_out, &next);
continue;
@ -1125,7 +1183,7 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
if (ctl->sc_largest_acked_sent_time > packet_out->po_sent +
lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats))
{
LSQ_DEBUG("loss by sent time detected: packet %"PRIu64,
LSQ_DEBUG("loss by sent time detected: packet #%"PRIu64,
packet_out->po_packno);
if ((packet_out->po_frame_types & ctl->sc_retx_frames)
&& 0 == (packet_out->po_flags & PO_MTU_PROBE))
@ -1138,7 +1196,7 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
if (largest_lost_packno > ctl->sc_largest_sent_at_cutback)
{
LSQ_DEBUG("detected new loss: packet %"PRIu64"; new lsac: "
LSQ_DEBUG("detected new loss: packet #%"PRIu64"; new lsac: "
"%"PRIu64, largest_lost_packno, ctl->sc_largest_sent_at_cutback);
send_ctl_loss_event(ctl);
}
@ -1147,7 +1205,7 @@ send_ctl_detect_losses (struct lsquic_send_ctl *ctl, enum packnum_space pns,
* number sent at the time of the last loss event indicate the same
* loss event. This follows NewReno logic, see RFC 6582.
*/
LSQ_DEBUG("ignore loss of packet %"PRIu64" smaller than lsac "
LSQ_DEBUG("ignore loss of packet #%"PRIu64" smaller than lsac "
"%"PRIu64, largest_lost_packno, ctl->sc_largest_sent_at_cutback);
return largest_lost_packno > ctl->sc_largest_sent_at_cutback;
@ -1160,7 +1218,7 @@ send_ctl_mtu_probe_acked (struct lsquic_send_ctl *ctl,
{
struct lsquic_conn *const lconn = ctl->sc_conn_pub->lconn;
LSQ_DEBUG("MTU probe in packet %"PRIu64" has been ACKed",
LSQ_DEBUG("MTU probe in packet #%"PRIu64" has been ACKed",
packet_out->po_packno);
assert(lconn->cn_if->ci_mtu_probe_acked);
if (lconn->cn_if->ci_mtu_probe_acked)
@ -1181,7 +1239,7 @@ send_ctl_maybe_increase_reord_thresh (struct lsquic_send_ctl *ctl,
< prev_largest_acked)
{
ctl->sc_reord_thresh = prev_largest_acked - loss_record->po_packno;
LSQ_DEBUG("packet %"PRIu64" was a spurious loss by FACK, increase "
LSQ_DEBUG("packet #%"PRIu64" was a spurious loss by FACK, increase "
"reordering threshold to %u", loss_record->po_packno,
ctl->sc_reord_thresh);
}
@ -1294,14 +1352,14 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
ctl->sc_largest_acked_sent_time = packet_out->po_sent;
ecn_total_acked += lsquic_packet_out_ecn(packet_out) != ECN_NOT_ECT;
ecn_ce_cnt += lsquic_packet_out_ecn(packet_out) == ECN_CE;
one_rtt_cnt += lsquic_packet_out_enc_level(packet_out) == ENC_LEV_FORW;
one_rtt_cnt += lsquic_packet_out_enc_level(packet_out) == ENC_LEV_APP;
if (0 == (packet_out->po_flags
& (PO_LOSS_REC|PO_POISON|PO_MTU_PROBE)))
{
packet_sz = packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
lsquic_packet_out_ack_streams(packet_out);
LSQ_DEBUG("acking via regular record %"PRIu64,
LSQ_DEBUG("acking via regular record #%"PRIu64,
packet_out->po_packno);
}
else if (packet_out->po_flags & PO_LOSS_REC)
@ -1309,7 +1367,7 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
packet_sz = packet_out->po_sent_sz;
TAILQ_REMOVE(&ctl->sc_unacked_packets[pns], packet_out,
po_next);
LSQ_DEBUG("acking via loss record %"PRIu64,
LSQ_DEBUG("acking via loss record #%"PRIu64,
packet_out->po_packno);
send_ctl_maybe_increase_reord_thresh(ctl, packet_out,
prev_largest_acked);
@ -1325,7 +1383,7 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
}
else
{
LSQ_WARN("poisoned packet %"PRIu64" acked",
LSQ_WARN("poisoned packet #%"PRIu64" acked",
packet_out->po_packno);
return -1;
}
@ -1334,9 +1392,15 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
do_rtt |= packet_out->po_packno == largest_acked(acki);
ctl->sc_ci->cci_ack(CGP(ctl), packet_out, packet_sz, now,
app_limited);
send_ctl_destroy_chain(ctl, packet_out, &next);
if (!(packet_out->po_flags & PO_ACKED_LOSS_CHAIN))
send_ctl_acked_loss_chain(ctl, packet_out, &next,
largest_acked(acki));
send_ctl_destroy_packet(ctl, packet_out);
}
else if (packet_out->po_flags & PO_ACKED_LOSS_CHAIN)
{
send_ctl_process_loss_chain_pkt(ctl, packet_out, &next);
}
packet_out = next;
}
while (packet_out && packet_out->po_packno <= largest_acked(acki));
@ -1344,6 +1408,7 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
if (do_rtt)
{
take_rtt_sample(ctl, ack_recv_time, acki->lack_delta);
LSQ_DEBUG("clear sc_n_consec_rtos, sc_n_hsk, sc_ntlp");
ctl->sc_n_consec_rtos = 0;
ctl->sc_n_hsk = 0;
ctl->sc_n_tlp = 0;
@ -1352,7 +1417,10 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
detect_losses:
losses_detected = send_ctl_detect_losses(ctl, pns, ack_recv_time);
if (send_ctl_first_unacked_retx_packet(ctl, pns))
set_retx_alarm(ctl, pns, now);
{
if (!lsquic_alarmset_is_set(ctl->sc_alset, pns) && losses_detected)
set_retx_alarm(ctl, pns, now);
}
else
{
LSQ_DEBUG("No retransmittable packets: clear alarm");
@ -1469,7 +1537,7 @@ send_ctl_next_lost (lsquic_send_ctl_t *ctl)
UINT64_MAX);
if (lost_packet->po_regen_sz >= lost_packet->po_data_sz)
{
LSQ_DEBUG("Dropping packet %"PRIu64" from lost queue",
LSQ_DEBUG("Dropping packet #%"PRIu64" from lost queue",
lost_packet->po_packno);
TAILQ_REMOVE(&ctl->sc_lost_packets, lost_packet, po_next);
lost_packet->po_flags &= ~PO_LOST;
@ -1927,7 +1995,7 @@ send_ctl_maybe_zero_pad (struct lsquic_send_ctl *ctl,
if (cum_size + size > limit)
break;
cum_size += size;
if (HETY_NOT_SET == packet_out->po_header_type)
if (HETY_SHORT == packet_out->po_header_type)
break;
}
@ -1942,7 +2010,7 @@ send_ctl_maybe_zero_pad (struct lsquic_send_ctl *ctl,
initial_packet->po_data_sz += size;
initial_packet->po_frame_types |= QUIC_FTBIT_PADDING;
}
LSQ_DEBUG("Added %zu bytes of PADDING to packet %"PRIu64, size,
LSQ_DEBUG("Added %zu bytes of PADDING to packet #%"PRIu64, size,
initial_packet->po_packno);
}
@ -1960,7 +2028,8 @@ lsquic_send_ctl_next_packet_to_send_predict (struct lsquic_send_ctl *ctl)
n_rtos = ~0u;
TAILQ_FOREACH(packet_out, &ctl->sc_scheduled_packets, po_next)
{
if (!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
if (!(packet_out->po_frame_types
& ((1 << QUIC_FRAME_ACK) | (1 << QUIC_FRAME_CRYPTO)))
&& 0 == ctl->sc_next_limit
&& 0 != (n_rtos == ~0u ? /* Initialize once */
(n_rtos = send_ctl_get_n_consec_rtos(ctl)) : n_rtos))
@ -1972,11 +2041,11 @@ lsquic_send_ctl_next_packet_to_send_predict (struct lsquic_send_ctl *ctl)
&& packet_out->po_regen_sz == packet_out->po_data_sz
&& packet_out->po_frame_types != QUIC_FTBIT_PATH_CHALLENGE)
{
LSQ_DEBUG("send prediction: packet %"PRIu64" would be dropped, "
LSQ_DEBUG("send prediction: packet #%"PRIu64" would be dropped, "
"continue", packet_out->po_packno);
continue;
}
LSQ_DEBUG("send prediction: yes, packet %"PRIu64", flags %u, frames 0x%X",
LSQ_DEBUG("send prediction: yes, packet #%"PRIu64", flags %u, frames 0x%X",
packet_out->po_packno, (unsigned) packet_out->po_flags,
(unsigned) packet_out->po_frame_types);
return 1;
@ -1998,18 +2067,26 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl,
get_packet:
packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets);
if (!packet_out)
{
LSQ_DEBUG("no more scheduled packets");
return NULL;
}
/* Note: keep logic in this function and in
* lsquic_send_ctl_next_packet_to_send_predict() in synch.
*/
if (!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
&& send_ctl_get_n_consec_rtos(ctl))
if (!(packet_out->po_frame_types
& ((1 << QUIC_FRAME_ACK) | (1 << QUIC_FRAME_CRYPTO)))
&& send_ctl_get_n_consec_rtos(ctl))
{
if (ctl->sc_next_limit)
dec_limit = 1;
else
{
LSQ_DEBUG("sc_n_consec_rtos: %d, sc_next_limit is 0",
ctl->sc_n_consec_rtos);
return NULL;
}
}
else
dec_limit = 0;
@ -2024,7 +2101,7 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl,
}
else
{
LSQ_DEBUG("Dropping packet %"PRIu64" from scheduled queue",
LSQ_DEBUG("Dropping packet #%"PRIu64" from scheduled queue",
packet_out->po_packno);
send_ctl_sched_remove(ctl, packet_out);
send_ctl_destroy_chain(ctl, packet_out, NULL);
@ -2043,7 +2120,7 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl,
> 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 "
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),
to_coal->prev_sz_sum);
@ -2061,7 +2138,7 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl,
else
packet_out->po_lflags &= ~POL_LIMITED;
if (UNLIKELY(packet_out->po_header_type == HETY_INITIAL)
if (UNLIKELY(!(ctl->sc_conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE))
&& (!(ctl->sc_conn_pub->lconn->cn_flags & LSCONN_SERVER)
|| (packet_out->po_frame_types
& IQUIC_FRAME_ACKABLE_MASK))
@ -2080,7 +2157,7 @@ lsquic_send_ctl_next_packet_to_send (struct lsquic_send_ctl *ctl,
}
else
packet_out->po_lflags &= ~POL_LOSS_BIT;
if (packet_out->po_header_type == HETY_NOT_SET)
if (packet_out->po_header_type == HETY_SHORT)
{
if (ctl->sc_gap + 1 == packet_out->po_packno)
++ctl->sc_square_count;
@ -2102,14 +2179,14 @@ lsquic_send_ctl_delayed_one (lsquic_send_ctl_t *ctl,
send_ctl_sched_prepend(ctl, packet_out);
if (packet_out->po_lflags & POL_LIMITED)
++ctl->sc_next_limit;
LSQ_DEBUG("packet %"PRIu64" has been delayed", packet_out->po_packno);
LSQ_DEBUG("packet #%"PRIu64" has been delayed", packet_out->po_packno);
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_delayed;
#endif
if (packet_out->po_lflags & POL_LOSS_BIT)
++ctl->sc_loss_count;
if ((ctl->sc_flags & SC_QL_BITS)
&& packet_out->po_header_type == HETY_NOT_SET)
&& packet_out->po_header_type == HETY_SHORT)
ctl->sc_square_count -= 1 + (ctl->sc_gap + 1 == packet_out->po_packno);
}
@ -2168,7 +2245,7 @@ send_ctl_allocate_packet (struct lsquic_send_ctl *ctl, enum packno_bits bits,
{
[PNS_INIT] = HETY_INITIAL,
[PNS_HSK] = HETY_HANDSHAKE,
[PNS_APP] = HETY_NOT_SET,
[PNS_APP] = HETY_SHORT,
};
lsquic_packet_out_t *packet_out;
@ -2232,7 +2309,7 @@ lsquic_send_ctl_new_packet_out (lsquic_send_ctl_t *ctl, unsigned need_at_least,
return NULL;
packet_out->po_packno = send_ctl_next_packno(ctl);
LSQ_DEBUG("created packet %"PRIu64, packet_out->po_packno);
LSQ_DEBUG("created packet #%"PRIu64, packet_out->po_packno);
EV_LOG_PACKET_CREATED(LSQUIC_LOG_CONN_ID, packet_out);
return packet_out;
}
@ -2367,10 +2444,8 @@ update_for_resending (lsquic_send_ctl_t *ctl, lsquic_packet_out_t *packet_out)
ctl->sc_bytes_scheduled -= packet_out->po_regen_sz;
lsquic_packet_out_chop_regen(packet_out);
}
LSQ_DEBUG("Packet %"PRIu64" repackaged for resending as packet %"PRIu64,
oldno, packno);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "packet %"PRIu64" repackaged for "
"resending as packet %"PRIu64, oldno, packno);
EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "schedule resend, repackage packet "
"#%"PRIu64" -> #%"PRIu64, oldno, packno);
}
@ -2447,7 +2522,7 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl,
ctl->sc_bytes_scheduled -= adj;
if (0 == packet_out->po_frame_types)
{
LSQ_DEBUG("cancel packet %"PRIu64" after eliding frames for "
LSQ_DEBUG("cancel packet #%"PRIu64" after eliding frames for "
"stream %"PRIu64, packet_out->po_packno, stream_id);
send_ctl_sched_remove(ctl, packet_out);
send_ctl_destroy_chain(ctl, packet_out, NULL);
@ -2587,7 +2662,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
"scheduled packets before squeezing");
#endif
send_ctl_sched_remove(ctl, packet_out);
LSQ_DEBUG("Dropping packet %"PRIu64" from scheduled queue",
LSQ_DEBUG("Dropping packet #%"PRIu64" from scheduled queue",
packet_out->po_packno);
send_ctl_destroy_chain(ctl, packet_out, NULL);
send_ctl_destroy_packet(ctl, packet_out);
@ -3107,7 +3182,7 @@ lsquic_send_ctl_schedule_buffered (lsquic_send_ctl_t *ctl,
--packet_q->bpq_count;
packet_out->po_packno = send_ctl_next_packno(ctl);
LSQ_DEBUG("Remove packet from buffered queue #%u; count: %u. "
"It becomes packet %"PRIu64, packet_type, packet_q->bpq_count,
"It becomes packet #%"PRIu64, packet_type, packet_q->bpq_count,
packet_out->po_packno);
lsquic_send_ctl_scheduled_one(ctl, packet_out);
}
@ -3360,6 +3435,8 @@ lsquic_send_ctl_maybe_calc_rough_rtt (struct lsquic_send_ctl *ctl,
if (min_sent < UINT64_MAX)
{
rtt = lsquic_time_now() - min_sent;
if (rtt > 500000)
rtt = 500000;
lsquic_rtt_stats_update(&ctl->sc_conn_pub->rtt_stats, rtt, 0);
ctl->sc_flags |= SC_ROUGH_RTT;
LSQ_DEBUG("set rough RTT to %"PRIu64" usec", rtt);
@ -3557,7 +3634,7 @@ send_ctl_resize_q (struct lsquic_send_ctl *ctl, struct lsquic_packets_tailq *q,
++count_dst;
packet_out->po_packno = send_ctl_next_packno(ctl);
send_ctl_sched_append(ctl, packet_out);
LSQ_DEBUG("created packet %"PRIu64, packet_out->po_packno);
LSQ_DEBUG("created packet #%"PRIu64, packet_out->po_packno);
EV_LOG_PACKET_CREATED(LSQUIC_LOG_CONN_ID, packet_out);
}
else
@ -4040,13 +4117,14 @@ lsquic_send_ctl_0rtt_to_1rtt (struct lsquic_send_ctl *ctl)
if (packet_out->po_header_type == HETY_0RTT)
{
++count;
packet_out->po_header_type = HETY_NOT_SET;
packet_out->po_header_type = HETY_SHORT;
if (packet_out->po_flags & PO_ENCRYPTED)
send_ctl_return_enc_data(ctl, packet_out);
}
LSQ_DEBUG("handshake ok: changed %u packet%.*s from 0-RTT to 1-RTT",
count, count != 1, "s");
ctl->sc_n_consec_rtos >>= 1;
}

View file

@ -2561,7 +2561,7 @@ lsquic_stream_flush_threshold (const struct lsquic_stream *stream,
flags |= PO_LONGHEAD;
packet_header_sz = lsquic_po_header_length(stream->conn_pub->lconn, flags,
stream->conn_pub->path->np_dcid.len, HETY_NOT_SET);
stream->conn_pub->path->np_dcid.len, HETY_SHORT);
stream_header_sz = stream->sm_frame_header_sz(stream, data_sz);
tag_len = stream->conn_pub->lconn->cn_esf_c->esf_tag_len;

View file

@ -1,5 +1,6 @@
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <arpa/inet.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
@ -49,13 +50,22 @@ struct tokgen_shm_state
{
uint8_t tgss_version;
uint8_t tgss_magic_top[sizeof(TOKGEN_SHM_MAGIC_TOP) - 1];
uint8_t tgss_padding[2 * CRYPTER_KEY_SIZE];
uint8_t tgss_crypter_key[N_TOKEN_TYPES][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];
};
/* The various salt values below were obtained by reading from /dev/random
* when the code was first written.
*/
static const uint64_t salts[N_TOKEN_TYPES] =
{
[TOKEN_RETRY] = 0xa49c3ef763a6243f,
[TOKEN_RESUME] = 0x0b3664549086b8ca,
};
static const uint8_t srst_salt[8] = "\x28\x6e\x81\x02\x40\x5b\x2c\x2b";
@ -68,19 +78,56 @@ struct crypter
};
/* Bloom filter of Resume tokens. See below. */
struct resumed_token_page
{
TAILQ_ENTRY(resumed_token_page) next;
time_t begin, /* Oldest entry */
end; /* Newest entry */
unsigned count; /* Number of entries */
uintptr_t masks[];
};
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.
*/
size_t tg_srst_prk_sz;
uint8_t tg_srst_prk_buf[SRST_MAX_PRK_SIZE];
unsigned tg_retry_token_duration;
TAILQ_HEAD(resumed_token_pages_head, resumed_token_page)
tg_resume_token_pages;
};
static int
setup_nonce_prk (unsigned char *nonce_prk_buf, size_t *nonce_prk_sz,
unsigned i, time_t now)
{
struct {
time_t now;
enum token_type tt;
uint8_t buf[16];
} ikm;
ikm.now = now;
ikm.tt = i;
RAND_bytes(ikm.buf, sizeof(ikm.buf));
if (HKDF_extract(nonce_prk_buf, nonce_prk_sz,
EVP_sha256(), (uint8_t *) &ikm, sizeof(ikm),
(void *) &salts[i], sizeof(salts[i])))
return 0;
else
{
LSQ_ERROR("HKDF_extract failed");
return -1;
}
}
static int
@ -149,10 +196,14 @@ get_or_generate_state (struct lsquic_engine_public *enpub, time_t now,
if (getenv("LSQUIC_NULL_TOKGEN"))
{
LSQ_NOTICE("using NULL tokgen");
memset(shm_state->tgss_crypter_key, 0,
sizeof(shm_state->tgss_crypter_key));
memset(&srst_ikm, 0, sizeof(srst_ikm));
}
else
{
RAND_bytes((void *) shm_state->tgss_crypter_key,
sizeof(shm_state->tgss_crypter_key));
srst_ikm.now = now;
RAND_bytes(srst_ikm.buf, sizeof(srst_ikm.buf));
}
@ -205,6 +256,26 @@ lsquic_tg_new (struct lsquic_engine_public *enpub)
if (0 != get_or_generate_state(enpub, now, &shm_state))
goto err;
TAILQ_INIT(&tokgen->tg_resume_token_pages);
unsigned i;
for (i = 0; i < sizeof(tokgen->tg_crypters)
/ sizeof(tokgen->tg_crypters[0]); ++i)
{
struct crypter *crypter;
crypter = tokgen->tg_crypters + i;
if (0 != setup_nonce_prk(crypter->nonce_prk_buf,
&crypter->nonce_prk_sz, i, now))
goto err;
if (1 != EVP_AEAD_CTX_init(&crypter->ctx, EVP_aead_aes_128_gcm(),
shm_state.tgss_crypter_key[i],
sizeof(shm_state.tgss_crypter_key[i]), RETRY_TAG_LEN, 0))
goto err;
}
tokgen->tg_retry_token_duration
= enpub->enp_settings.es_retry_token_duration;
if (tokgen->tg_retry_token_duration == 0)
tokgen->tg_retry_token_duration = LSQUIC_DF_RETRY_TOKEN_DURATION;
tokgen->tg_srst_prk_sz = shm_state.tgss_srst_prk_size;
if (tokgen->tg_srst_prk_sz > sizeof(tokgen->tg_srst_prk_buf))
@ -228,11 +299,513 @@ lsquic_tg_new (struct lsquic_engine_public *enpub)
void
lsquic_tg_destroy (struct token_generator *tokgen)
{
struct resumed_token_page *page;
struct crypter *crypter;
unsigned i;
while ((page = TAILQ_FIRST(&tokgen->tg_resume_token_pages)))
{
TAILQ_REMOVE(&tokgen->tg_resume_token_pages, page, next);
free(page);
}
for (i = 0; i < sizeof(tokgen->tg_crypters)
/ sizeof(tokgen->tg_crypters[0]); ++i)
{
crypter = tokgen->tg_crypters + i;
EVP_AEAD_CTX_cleanup(&crypter->ctx);
}
free(tokgen);
LSQ_DEBUG("destroyed");
}
/* To limit reuse of Resume tokens, used Resume tokens are inserted into a
* list of Bloom filters with very low false positive rate. Before a Resume
* token is used, we check in the Bloom filter. If this token has already
* been used, it fails validation.
*
* There are three ways when this check will fail:
* 1. Bloom filter false positive. In this case, Resume token fails
* validation, which may cause the server may issue a Retry. This
* should happen very infrequently (see below).
* 2. Server restart. Because the Bloom filter is stored in process
* memory, this will result in false negative and a Resume token can be
* reused.
* 3. Different working process. Similar to (2).
*
* Bloom filters are on a linked list. Each filter is used up to MAX_PER_PAGE
* values or RESUME_MAX_SECS seconds, after which a new Bloom filter is inserted.
* Bloom filters are removed once the most recent element is older than
* RESUME_MAX_HOURS hours.
*/
#define RESUME_MAX_SECS (24 * 3600)
#define N_BLOOM_FUNCS 10
/* We need 30 bytes to generate 10 24-bit Bloom filter values */
typedef char enough_blooms[MIN_RESUME_TOKEN_LEN >= 3 * N_BLOOM_FUNCS ? 1 : -1];
typedef uint32_t bloom_vals_t[N_BLOOM_FUNCS];
#define RESUME_TOKEN_PAGE_SIZE (1u << 21)
/* For memory efficiency, we allocate 2MB chunks of memory,
* not 2MB + 28 bytes. Thus, we can't use the whole 24-bit range.
*/
#define MAX_BLOOM_VALUE ((RESUME_TOKEN_PAGE_SIZE - \
sizeof(struct resumed_token_page)) * 8 - 1)
#define MAX_PER_PAGE 500000
/* This works out to 0.00012924% false positive rate:
* perl -E '$k=10;$m=1<<24;$n=500000;printf("%.10lf",(1-exp(1)**-($k*$n/$m))**$k)'
*/
static int
tokgen_seen_resumed_token (struct token_generator *tokgen,
const unsigned char *token, size_t token_sz, bloom_vals_t bloom_vals)
{
const struct resumed_token_page *page;
unsigned n, idx;
uintptr_t slot;
if (1 + N_BLOOM_FUNCS * 3 > token_sz)
return 0;
++token;
for (n = 0; n < N_BLOOM_FUNCS; ++n)
{
bloom_vals[n] = *token++;
bloom_vals[n] |= *token++ << 8;
bloom_vals[n] |= *token++ << 16;
if (bloom_vals[n] > MAX_BLOOM_VALUE)
bloom_vals[n] = MAX_BLOOM_VALUE;
}
page = TAILQ_FIRST(&tokgen->tg_resume_token_pages);
while (page)
{
for (n = 0; n < N_BLOOM_FUNCS; ++n)
{
idx = bloom_vals[n] / (sizeof(page->masks[0]) * 8);
slot = 1;
slot <<= bloom_vals[n] % (sizeof(page->masks[0]) * 8);
if (!(page->masks[idx] & slot))
goto next_page;
}
return 1;
next_page:
page = TAILQ_NEXT(page, next);
}
return 0;
}
static void /* void: if it fails, there is nothing to do */
tokgen_record_resumed_token (struct token_generator *tokgen, time_t now,
bloom_vals_t bloom_vals)
{
struct resumed_token_page *page;
unsigned n, idx;
uintptr_t slot;
/* Expunge old pages at insertion time only to save on time() syscall */
while ((page = TAILQ_FIRST(&tokgen->tg_resume_token_pages)))
if (page->end + RESUME_MAX_SECS < now)
{
LSQ_DEBUG("drop resumed cache page");
TAILQ_REMOVE(&tokgen->tg_resume_token_pages, page, next);
free(page);
}
else
break;
page = TAILQ_LAST(&tokgen->tg_resume_token_pages, resumed_token_pages_head);
if (!(page && page->count < MAX_PER_PAGE && now
< page->begin + RESUME_MAX_SECS))
{
page = calloc(1, RESUME_TOKEN_PAGE_SIZE);
if (!page)
{
LSQ_WARN("cannot allocate resumed cache page");
return;
}
LSQ_DEBUG("allocate resumed cache page");
TAILQ_INSERT_TAIL(&tokgen->tg_resume_token_pages, page, next);
page->begin = now;
page->end = now;
page->count = 0;
}
page->end = now;
++page->count;
for (n = 0; n < N_BLOOM_FUNCS; ++n)
{
idx = bloom_vals[n] / (sizeof(page->masks[0]) * 8);
slot = 1;
slot <<= bloom_vals[n] % (sizeof(page->masks[0]) * 8);
page->masks[idx] |= slot;
}
}
static const char *const tt2str[N_TOKEN_TYPES] = {
[TOKEN_RESUME] = "resume",
[TOKEN_RETRY] = "retry",
};
int
lsquic_tg_validate_token (struct token_generator *tokgen,
const struct lsquic_packet_in *packet_in, const struct sockaddr *sa_peer,
lsquic_cid_t *odcid)
{
size_t decr_token_len, encr_token_len, ad_len;
const unsigned char *nonce, *encr_token, *p, *end, *ad;
struct crypter *crypter;
enum token_type token_type;
time_t issued_at, ttl, now;
int is_ipv6;
unsigned version;
bloom_vals_t bloom_vals;
unsigned char decr_token[MAX_RETRY_TOKEN_LEN - RETRY_TAG_LEN
- RETRY_NONCE_LEN];
char token_str[MAX_RETRY_TOKEN_LEN * 2 + 1];
char addr_str[2][INET6_ADDRSTRLEN];
if (!(packet_in->pi_token && packet_in->pi_token_size))
{
LSQ_DEBUGC("packet for connection %"CID_FMT" has no token: "
"validation failed", CID_BITS(&packet_in->pi_dcid));
return -1;
}
if (packet_in->pi_token_size < RETRY_TAG_LEN + RETRY_NONCE_LEN)
{
LSQ_DEBUGC("packet for connection %"CID_FMT" has too-short token "
"(%hu bytes): validation failed", CID_BITS(&packet_in->pi_dcid),
packet_in->pi_token_size);
return -1;
}
token_type = packet_in->pi_data[packet_in->pi_token];
switch (token_type)
{
case TOKEN_RETRY:
ttl = tokgen->tg_retry_token_duration;
ad = packet_in->pi_dcid.idbuf;
ad_len = packet_in->pi_dcid.len;
break;
case TOKEN_RESUME:
if (tokgen_seen_resumed_token(tokgen, packet_in->pi_data
+ packet_in->pi_token, packet_in->pi_token_size, bloom_vals))
{
LSQ_DEBUGC("%s token for connection %"CID_FMT" has already "
"been used: validation failed", tt2str[token_type],
CID_BITS(&packet_in->pi_dcid));
return -1;
}
ttl = RESUME_MAX_SECS;
ad = NULL;
ad_len = 0;
break;
default:
LSQ_DEBUGC("packet for connection %"CID_FMT" has unknown token "
"type (%u): validation failed", CID_BITS(&packet_in->pi_dcid),
token_type);
return -1;
}
crypter = &tokgen->tg_crypters[ token_type ];
nonce = packet_in->pi_data + packet_in->pi_token;
encr_token = nonce + RETRY_NONCE_LEN;
encr_token_len = packet_in->pi_token_size - RETRY_NONCE_LEN;
decr_token_len = sizeof(decr_token);
if (!EVP_AEAD_CTX_open(&crypter->ctx, decr_token, &decr_token_len,
decr_token_len, nonce, RETRY_NONCE_LEN,
encr_token, encr_token_len, ad, ad_len))
{
LSQ_DEBUGC("packet for connection %"CID_FMT" has undecryptable %s "
"token %s: validation failed", CID_BITS(&packet_in->pi_dcid),
tt2str[token_type],
HEXSTR(packet_in->pi_data + packet_in->pi_token,
packet_in->pi_token_size, token_str));
return -1;
}
/* From here on, we begin to warn: this is because we were able to
* decrypt it, so this is our token. We should be able to parse it.
*/
p = decr_token;
end = p + decr_token_len;
if (p + 1 > end)
goto too_short;
version = *p++;
if (version != TOKGEN_VERSION)
{
LSQ_DEBUGC("packet for connection %"CID_FMT" has %s token with "
"wrong version %u (expected %u): validation failed",
CID_BITS(&packet_in->pi_dcid), tt2str[token_type],
version, TOKGEN_VERSION);
return -1;
}
if (p + sizeof(issued_at) > end)
goto too_short;
memcpy(&issued_at, p, sizeof(issued_at));
now = time(NULL);
if (issued_at + ttl < now)
{
LSQ_DEBUGC("%s token for connection %"CID_FMT" expired %lu "
"seconds ago", tt2str[token_type], CID_BITS(&packet_in->pi_dcid),
(unsigned long) (now - issued_at - ttl));
return -1;
}
p += sizeof(issued_at);
if (p + 1 > end)
goto too_short;
is_ipv6 = *p++;
if (is_ipv6)
{
if (p + 16 > end)
goto too_short;
if (!(AF_INET6 == sa_peer->sa_family &&
0 == memcmp(p, &((struct sockaddr_in6 *) sa_peer)->sin6_addr, 16)))
goto ip_mismatch;
p += 16;
}
else
{
if (p + 4 > end)
goto too_short;
if (!(AF_INET == sa_peer->sa_family &&
0 == memcmp(p, &((struct sockaddr_in *)
sa_peer)->sin_addr.s_addr, 4)))
goto ip_mismatch;
p += 4;
}
if (TOKEN_RETRY == token_type)
{
if (p + 2 >= end)
goto too_short;
if (AF_INET == sa_peer->sa_family)
{
if (memcmp(p, &((struct sockaddr_in *) sa_peer)->sin_port, 2))
goto port_mismatch;
}
else if (memcmp(p, &((struct sockaddr_in6 *) sa_peer)->sin6_port, 2))
goto port_mismatch;
if (0 && LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
{
uint16_t port;
memcpy(&port, p, sizeof(port));
port = ntohs(port);
LSQ_DEBUG("port %hu in Retry token matches", port);
}
p += 2;
if (end - p > MAX_CID_LEN)
goto too_long;
if (odcid)
{
memcpy(odcid->idbuf, p, end - p);
odcid->len = end - p;
LSQ_DEBUGC("ODCID: %"CID_FMT, CID_BITS(odcid));
}
}
else
{
if (p != end)
{
assert(p < end);
goto too_long;
}
tokgen_record_resumed_token(tokgen, now, bloom_vals);
}
LSQ_DEBUGC("validated %lu-second-old %s token %s for connection "
"%"CID_FMT, (unsigned long) (now - issued_at), tt2str[token_type],
HEXSTR(packet_in->pi_data + packet_in->pi_token,
packet_in->pi_token_size, token_str),
CID_BITS(&packet_in->pi_dcid));
return 0;
too_short:
LSQ_INFOC("decrypted %s token for connection %"CID_FMT" is too short "
"(%zu bytes): validation failed", tt2str[token_type],
CID_BITS(&packet_in->pi_dcid), decr_token_len);
return -1;
ip_mismatch:
addr_str[0][0] = '\0';
addr_str[1][0] = '\0';
(void) inet_ntop(is_ipv6 ? AF_INET6 : AF_INET, p, addr_str[0],
sizeof(addr_str[0]));
if (AF_INET6 == sa_peer->sa_family)
(void) inet_ntop(AF_INET6, &((struct sockaddr_in6 *) sa_peer
)->sin6_addr, addr_str[1], sizeof(addr_str[1]));
else
(void) inet_ntop(AF_INET, &((struct sockaddr_in *) sa_peer
)->sin_addr.s_addr, addr_str[1], sizeof(addr_str[1]));
LSQ_INFOC("IP address %s in %s token for connection %"CID_FMT" does not "
"match peer IP address %s: validation failed", addr_str[0],
tt2str[token_type], CID_BITS(&packet_in->pi_dcid), addr_str[1]);
return -1;
too_long:
LSQ_INFOC("decrypted %s token for connection %"CID_FMT" is too long "
"(%zu bytes): validation failed", tt2str[token_type],
CID_BITS(&packet_in->pi_dcid), decr_token_len);
return -1;
port_mismatch:
{
uint16_t ports[2];
ports[0] = AF_INET6 == sa_peer->sa_family
? ((struct sockaddr_in6 *) sa_peer)->sin6_port
: ((struct sockaddr_in *) sa_peer)->sin_port;
ports[0] = ntohs(ports[0]);
memcpy(&ports[1], p, sizeof(ports[1]));
ports[1] = ntohs(ports[1]);
LSQ_INFOC("port %hu in %s token for connection %"CID_FMT" does not "
"match peer port %hu: validation failed", ports[1], tt2str[token_type],
CID_BITS(&packet_in->pi_dcid), ports[0]);
return -1;
}
}
#define LABEL_PREFIX_SZ 8
static const uint8_t *labels[N_TOKEN_TYPES] =
{
[TOKEN_RETRY] = (uint8_t *) "retry me",
[TOKEN_RESUME] = (uint8_t *) "resume m",
};
static ssize_t
tokgen_generate_token (struct token_generator *tokgen,
enum token_type token_type, unsigned char *buf, size_t bufsz,
const unsigned char *ad_buf, size_t ad_len,
const struct sockaddr *sa_peer, const lsquic_cid_t *odcid)
{
struct crypter *crypter;
unsigned char *p, *in;
time_t now;
size_t len, in_len;
unsigned char label[ LABEL_PREFIX_SZ + sizeof(crypter->nonce_counter) ];
char in_str[(MAX_RETRY_TOKEN_LEN - RETRY_NONCE_LEN
- RETRY_TAG_LEN) * 2 + 1],
ad_str[ad_len * 2 + 1],
token_str[MAX_RETRY_TOKEN_LEN * 2 + 1];
if (bufsz < MAX_RETRY_TOKEN_LEN)
return -1;
crypter = &tokgen->tg_crypters[ token_type ];
p = buf;
*p = token_type;
memcpy(label, labels[token_type], LABEL_PREFIX_SZ);
memcpy(label + LABEL_PREFIX_SZ, &crypter->nonce_counter,
sizeof(crypter->nonce_counter));
(void) HKDF_expand(p + 1, RETRY_NONCE_LEN - 1, EVP_sha256(),
crypter->nonce_prk_buf, crypter->nonce_prk_sz, label, sizeof(label));
p += RETRY_NONCE_LEN;
*p++ = TOKGEN_VERSION;
now = time(NULL);
memcpy(p, &now, sizeof(now));
p += sizeof(now);
if (AF_INET == sa_peer->sa_family)
{
*p++ = 0;
memcpy(p, &((struct sockaddr_in *) sa_peer)->sin_addr.s_addr, 4);
p += 4;
}
else
{
*p++ = 1;
memcpy(p, &((struct sockaddr_in6 *) sa_peer)->sin6_addr, 16);
p += 16;
}
if (token_type == TOKEN_RETRY)
{
if (AF_INET == sa_peer->sa_family)
memcpy(p, &((struct sockaddr_in *) sa_peer)->sin_port, 2);
else
memcpy(p, &((struct sockaddr_in6 *) sa_peer)->sin6_port, 2);
p += 2;
}
if (odcid)
{
assert(odcid->len <= MAX_CID_LEN);
memcpy(p, odcid->idbuf, odcid->len);
p += odcid->len;
}
len = bufsz - RETRY_NONCE_LEN;
in = buf + RETRY_NONCE_LEN;
in_len = p - buf - RETRY_NONCE_LEN;
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG))
lsquic_hexstr(in, in_len, in_str, sizeof(in_str));
if (EVP_AEAD_CTX_seal(&crypter->ctx, in, &len, len,
buf, RETRY_NONCE_LEN, in, in_len, ad_buf, ad_len))
{
++crypter->nonce_counter;
LSQ_DEBUG("in: %s, ad: %s -> %s token: %s (%zu bytes)",
in_str,
HEXSTR(ad_buf, ad_len, ad_str),
tt2str[token_type],
HEXSTR(buf, RETRY_NONCE_LEN + len, token_str),
RETRY_NONCE_LEN + len);
return RETRY_NONCE_LEN + len;
}
else
{
LSQ_WARN("could not seal retry token");
return -1;
}
}
ssize_t
lsquic_tg_generate_retry (struct token_generator *tokgen,
unsigned char *buf, size_t bufsz, const unsigned char *scid_buf,
size_t scid_len, const struct sockaddr *sa_peer,
const lsquic_cid_t *odcid)
{
return tokgen_generate_token(tokgen, TOKEN_RETRY, buf, bufsz, scid_buf,
scid_len, sa_peer, odcid);
}
ssize_t
lsquic_tg_generate_resume (struct token_generator *tokgen,
unsigned char *buf, size_t bufsz, const struct sockaddr *sa_peer)
{
return tokgen_generate_token(tokgen, TOKEN_RESUME, buf, bufsz, NULL,
0, sa_peer, NULL);
}
size_t
lsquic_tg_token_size (const struct token_generator *tokgen,
enum token_type token_type, const struct sockaddr *sa_peer)
{
return MAX_RETRY_TOKEN_LEN - 16
+ (AF_INET == sa_peer->sa_family ? 4 : 16);
}
void
lsquic_tg_generate_sreset (struct token_generator *tokgen,
const struct lsquic_cid *cid, unsigned char *reset_token)

View file

@ -7,6 +7,8 @@ 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 *
@ -20,4 +22,44 @@ void
lsquic_tg_generate_sreset (struct token_generator *,
const struct lsquic_cid *cid, unsigned char *reset_token);
/* Retry and Resume tokens have identical sizes. Use *RETRY* macros
* for both.
*/
#define RETRY_TAG_LEN 16
/* Type is encoded in the nonce */
#define RETRY_NONCE_LEN 12
#define MAX_RETRY_TOKEN_LEN (RETRY_NONCE_LEN + 1 /* version */ + \
sizeof(time_t) /* time */ + 1 /* IPv4 or IPv6 */ + \
16 /* IPv6 or IPv4 address */ + 2 /* Port number */ + MAX_CID_LEN + \
RETRY_TAG_LEN)
/* Need this to make sure we have enough bytes for Bloom filter functions */
#define MIN_RESUME_TOKEN_LEN (RETRY_NONCE_LEN + 1 /* version */ + \
sizeof(time_t) /* time */ + 1 /* IPv4 or IPv6 */ + \
4 /* IPv4 address */ + 0 /* No port number */ + 0 /* No CID */ + \
RETRY_TAG_LEN)
ssize_t
lsquic_tg_generate_retry (struct token_generator *,
unsigned char *buf, size_t bufsz,
const unsigned char *scid_buf, size_t scid_len,
const struct sockaddr *sa_peer, const struct lsquic_cid *odcid);
ssize_t
lsquic_tg_generate_resume (struct token_generator *,
unsigned char *buf, size_t bufsz,
const struct sockaddr *sa_peer);
int
lsquic_tg_validate_token (struct token_generator *,
const struct lsquic_packet_in *, const struct sockaddr *,
struct lsquic_cid *);
size_t
lsquic_tg_token_size (const struct token_generator *tokgen,
enum token_type token_type, const struct sockaddr *sa_peer);
#endif

View file

@ -19,6 +19,7 @@
#include "Ws2tcpip.h"
#endif
#include "lsquic.h"
#include "lsquic_byteswap.h"
#include "lsquic_int_types.h"
#include "lsquic_types.h"
@ -54,6 +55,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 17: return TPI_VERSION_INFORMATION;
case 0x20: return TPI_MAX_DATAGRAM_FRAME_SIZE;
#if LSQUIC_TEST_QUANTUM_READINESS
case 0xC37: return TPI_QUANTUM_READINESS;
@ -87,6 +89,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_VERSION_INFORMATION] = 0x11,
[TPI_MAX_DATAGRAM_FRAME_SIZE] = 0x20,
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = 0xC37,
@ -267,6 +270,8 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
#if LSQUIC_TEST_QUANTUM_READINESS
const size_t quantum_sz = lsquic_tp_get_quantum_sz();
#endif
lsquic_ver_tag_t tag;
int i;
need = 0;
set = params->tp_set; /* Will turn bits off for default values */
@ -357,6 +362,16 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
need += (1 << bits[tpi][0]) + 1 /* Zero length byte */;
}
if (set & (1 << TPI_VERSION_INFORMATION))
{
tpi = TPI_VERSION_INFORMATION;
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
bits[tpi][1] = vint_val2bits(params->tp_version_cnt << 2);
need += (1 << bits[tpi][0])
+ (1 << bits[tpi][1])
+ (params->tp_version_cnt << 2);
}
if (need > bufsz || need > UINT16_MAX)
{
errno = ENOBUFS;
@ -458,6 +473,17 @@ lsquic_tp_encode (const struct transport_params *params, int is_server,
p += quantum_sz;
break;
#endif
case TPI_VERSION_INFORMATION:
//FIXME: generate supported version info.
vint_write(p, params->tp_version_cnt << 2,
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
for(i = 0; i < params->tp_version_cnt; ++i)
{
tag = lsquic_ver2tag(params->tp_version_info[i]);
WRITE_TO_P(&tag, 4);
}
break;
}
}
@ -480,6 +506,7 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
enum transport_param_id tpi;
unsigned set_of_ids;
int s;
lsquic_ver_tag_t tag;
p = buf;
end = buf + bufsz;
@ -643,6 +670,31 @@ lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
if (q != p + len)
return -1;
break;
case TPI_VERSION_INFORMATION:
if (len & 0x3)
return -1;
q = p;
while(q < p + len)
{
memmove(&tag, q, 4);
if (tag == 0)
return -1;
int ver = lsquic_tag2ver(tag);
if (ver != -1)
{
if (params->tp_version_cnt > 0)
params->tp_versions |= 1 << ver;
if (params->tp_version_cnt < sizeof(params->tp_version_info))
params->tp_version_info[params->tp_version_cnt++] = ver;
}
else if (params->tp_version_cnt == 0)
return -1;
q += 4;
}
if (!is_server && (params->tp_versions
& (1 << params->tp_chosen_version)) == 0)
return -1;
break;
default:
/* Do nothing: skip this transport parameter */
break;
@ -693,6 +745,7 @@ lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
enum transport_param_id tpi;
char tok_str[sizeof(params->tp_stateless_reset_token) * 2 + 1];
char addr_str[INET6_ADDRSTRLEN];
int i;
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if (params->tp_set & (1 << tpi))
@ -766,6 +819,23 @@ lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
return;
}
}
if (params->tp_set & (1 << TPI_VERSION_INFORMATION))
{
nw = snprintf(buf, end - buf, "; version information: chosen: %s, available:",
lsquic_ver2str[params->tp_chosen_version]);
buf += nw;
if (buf >= end)
return;
for(i = 1; i < params->tp_version_cnt; ++i)
{
nw = snprintf(buf, end - buf, " %s",
lsquic_ver2str[params->tp_version_info[i]]);
buf += nw;
if (buf >= end)
return;
}
}
}
@ -988,6 +1058,8 @@ lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
p += quantum_sz;
break;
#endif
case TPI_VERSION_INFORMATION:
break;
}
}

View file

@ -64,6 +64,7 @@ enum transport_param_id
#if LSQUIC_TEST_QUANTUM_READINESS
TPI_QUANTUM_READINESS,
#endif
TPI_VERSION_INFORMATION,
TPI_STATELESS_RESET_TOKEN, LAST_TPI = TPI_STATELESS_RESET_TOKEN
};
@ -106,6 +107,10 @@ struct transport_params
#define tp_original_dest_cid tp_cids[TP_CID_IDX(TPI_ORIGINAL_DEST_CID)]
#define tp_initial_source_cid tp_cids[TP_CID_IDX(TPI_INITIAL_SOURCE_CID)]
#define tp_retry_source_cid tp_cids[TP_CID_IDX(TPI_RETRY_SOURCE_CID)]
unsigned tp_versions;
uint8_t tp_version_cnt;
uint8_t tp_version_info[7];
#define tp_chosen_version tp_version_info[0]
};
#define MAX_TP_STR_SZ ((LAST_TPI + 1) * \

View file

@ -18,7 +18,8 @@ static const unsigned char version_tags[N_LSQVER][4] =
[LSQVER_ID27] = { 0xFF, 0, 0, 27, },
[LSQVER_ID29] = { 0xFF, 0, 0, 29, },
[LSQVER_I001] = { 0, 0, 0, 1, },
[LSQVER_VERNEG] = { 0xFA, 0xFA, 0xFA, 0xFA, },
[LSQVER_I002] = { 0x6B, 0x33, 0x43, 0xCF },
[LSQVER_RESVED] = { 0xFA, 0xFA, 0xFA, 0xFA, },
};
@ -47,6 +48,37 @@ lsquic_tag2ver (uint32_t ver_tag)
}
enum lsquic_version
lsquic_tag2ver_fast (const unsigned char *tag)
{
unsigned char ch;
ch = *tag;
if (ch == 'Q' && *(tag + 1) == '0')
{
if (*(tag + 2) == '5' && *(tag + 3) == '0')
return LSQVER_050;
else if (*(tag + 2) == '4' && *(tag + 3) == '6')
return LSQVER_046;
}
else if (ch == 0x6b && *(tag + 1) == 0x33
&& *(tag + 2) == 0x43 && *(tag + 3) == 0xcf)
{
return LSQVER_I002;
}
else if (ch == '\0' && *(tag + 1) == 0 && *(tag + 2) == 0)
{
if (*(tag + 3) == 0x01)
return LSQVER_I001;
else if (*(tag + 3) == 0x00)
return LSQVER_VERNEG;
}
else if ((ch & 0xf) == 0xa && (*(tag + 1) & 0xf) == 0xa
&& (*(tag + 2) & 0xf) == 0xa && (*(tag + 3) & 0xf) == 0xa)
return LSQVER_RESVED;
return N_LSQVER;
}
const char *const lsquic_ver2str[N_LSQVER] = {
[LSQVER_043] = "Q043",
[LSQVER_046] = "Q046",
@ -54,7 +86,8 @@ const char *const lsquic_ver2str[N_LSQVER] = {
[LSQVER_ID27] = "FF00001B",
[LSQVER_ID29] = "FF00001D",
[LSQVER_I001] = "00000001",
[LSQVER_VERNEG] = "FAFAFAFA",
[LSQVER_I002] = "6B3343CF",
[LSQVER_RESVED] = "FAFAFAFA",
};
@ -65,7 +98,8 @@ const char *const lsquic_ver2altstr[N_LSQVER] = {
[LSQVER_ID27] = "h3-27",
[LSQVER_ID29] = "h3-29",
[LSQVER_I001] = "h3",
[LSQVER_VERNEG] = "VERNEG",
[LSQVER_I002] = "h3-v2",
[LSQVER_RESVED] = "RESERVED",
};

View file

@ -14,6 +14,9 @@ lsquic_ver2tag (unsigned version);
enum lsquic_version
lsquic_tag2ver (uint32_t ver_tag);
enum lsquic_version
lsquic_tag2ver_fast (const unsigned char * tag);
extern const char *const lsquic_ver2str[];
int

View file

@ -179,7 +179,7 @@ run_test (const struct test *test)
size_t sz;
unsigned char buf[0x1000];
pf = select_pf_by_ver(LSQVER_ID27);
pf = select_pf_by_ver(LSQVER_I001);
if (!test->skip_gen)
{
rechist.acki = &test->acki;

View file

@ -20,8 +20,8 @@
static struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0);
//static const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_ID27); // will not work on MSVC
#define pf ((const struct parse_funcs *const)select_pf_by_ver(LSQVER_ID27))
//static const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_I001); // will not work on MSVC
#define pf ((const struct parse_funcs *const)select_pf_by_ver(LSQVER_I001))
static void

View file

@ -103,7 +103,7 @@ main (void)
const struct test tests[] = {
{ .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID27),
.pf = select_pf_by_ver(LSQVER_I001),
.offset = 0,
.data_sz = 10,
.data = "0123456789",
@ -119,7 +119,7 @@ main (void)
},
{ .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID27),
.pf = select_pf_by_ver(LSQVER_I001),
.offset = 500,
.data_sz = 10,
.data = "0123456789",

View file

@ -63,7 +63,7 @@
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID27);
static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_I001);
struct test_ctl_settings
{
@ -343,7 +343,7 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
memset(tobjs, 0, sizeof(*tobjs));
LSCONN_INITIALIZE(&tobjs->lconn);
tobjs->lconn.cn_pf = g_pf;
tobjs->lconn.cn_version = LSQVER_ID27;
tobjs->lconn.cn_version = LSQVER_I001;
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1;
network_path.np_pack_size = packet_sz;
tobjs->lconn.cn_if = &our_conn_if;

View file

@ -214,7 +214,7 @@ new_packet (struct test_ctx *ctx)
*/
packet_out = lsquic_packet_out_new(&ctx->enpub.enp_mm, ctx->enpub.enp_mm.malo.packet_out, 1,
&ctx->lconn, PACKNO_BITS_0, 0, NULL, &ctx->path, HETY_NOT_SET);
&ctx->lconn, PACKNO_BITS_0, 0, NULL, &ctx->path, HETY_SHORT);
if (packet_out)
packet_out->po_packno = packno++;

View file

@ -185,8 +185,8 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
int s;
memset(tobjs, 0, sizeof(*tobjs));
LSCONN_INITIALIZE(&tobjs->lconn);
tobjs->lconn.cn_pf = select_pf_by_ver(LSQVER_ID27);
tobjs->lconn.cn_version = LSQVER_ID27;
tobjs->lconn.cn_pf = select_pf_by_ver(LSQVER_I001);
tobjs->lconn.cn_version = LSQVER_I001;
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1;
network_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ;
tobjs->lconn.cn_if = &our_conn_if;

View file

@ -363,7 +363,7 @@ init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
LSCONN_INITIALIZE(&tobjs->lconn);
tobjs->lconn.cn_pf = pf ? pf : g_pf;
tobjs->lconn.cn_version = tobjs->lconn.cn_pf == &lsquic_parse_funcs_ietf_v1 ?
LSQVER_ID27 : LSQVER_043;
LSQVER_I001 : LSQVER_043;
tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_gquic_1;
network_path.np_pack_size = 1370;
tobjs->lconn.cn_if = &our_conn_if;
@ -1620,7 +1620,7 @@ test_termination (void)
{
init_test_ctl_settings(&g_ctl_settings);
g_ctl_settings.tcs_schedule_stream_packets_immediately = 1;
init_test_objs(&tobjs, 0x4000, 0x4000, select_pf_by_ver(LSQVER_ID27));
init_test_objs(&tobjs, 0x4000, 0x4000, select_pf_by_ver(LSQVER_I001));
tf->func(&tobjs);
deinit_test_objs(&tobjs);
}
@ -2619,7 +2619,7 @@ test_changing_pack_size (void)
enum lsquic_version versions_to_test[3] =
{
LSQVER_046,
LSQVER_ID27,
LSQVER_I001,
};
for (i = 0; i < 3; i++)
@ -3005,7 +3005,7 @@ test_resize_buffered (void)
ssize_t nw;
struct test_objs tobjs;
struct lsquic_stream *streams[1];
const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_ID27);
const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_I001);
char buf[0x10000];
unsigned char buf_out[0x10000];
int s, fin;
@ -3068,7 +3068,7 @@ test_resize_scheduled (void)
ssize_t nw;
struct test_objs tobjs;
struct lsquic_stream *streams[1];
const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_ID27);
const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_I001);
char buf[0x10000];
unsigned char buf_out[0x10000];
int s, fin;
@ -3260,8 +3260,8 @@ test_packetization (int schedule_stream_packets_immediately, int dispatch_once,
if (g_use_crypto_ctor)
{
stream_ids[0] = ENC_LEV_CLEAR;
stream_ids[1] = ENC_LEV_INIT;
stream_ids[0] = ENC_LEV_INIT;
stream_ids[1] = ENC_LEV_HSK;
}
else
{
@ -3754,7 +3754,7 @@ main (int argc, char **argv)
/* Redo some tests using crypto streams and frames */
g_use_crypto_ctor = 1;
g_pf = select_pf_by_ver(LSQVER_ID27);
g_pf = select_pf_by_ver(LSQVER_I001);
main_test_packetization();
return 0;

View file

@ -371,7 +371,7 @@ main (void)
*/
{ .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID27),
.pf = select_pf_by_ver(LSQVER_I001),
.fin = { 0, 1, },
.offset = 0x0807060504030201UL,
.stream_id = 0x210,
@ -391,7 +391,7 @@ main (void)
},
{ .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID27),
.pf = select_pf_by_ver(LSQVER_I001),
.fin = { 0, 0, },
.offset = 0,
.stream_id = 0x210,
@ -410,7 +410,7 @@ main (void)
},
{ .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID27),
.pf = select_pf_by_ver(LSQVER_I001),
.fin = { 0, 0, },
.offset = 0,
.stream_id = 0x21,
@ -428,7 +428,7 @@ main (void)
},
{ .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID27),
.pf = select_pf_by_ver(LSQVER_I001),
.fin = { 0, 0, },
.offset = 0x0807060504030201UL,
.stream_id = 0x210,
@ -448,7 +448,7 @@ main (void)
},
{ .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID27),
.pf = select_pf_by_ver(LSQVER_I001),
.fin = { 1, 0, },
.offset = 0x0807060504030201UL,
.stream_id = 0x210,
@ -466,7 +466,7 @@ main (void)
},
{ .lineno = __LINE__,
.pf = select_pf_by_ver(LSQVER_ID27),
.pf = select_pf_by_ver(LSQVER_I001),
.fin = { 1, 0, },
.offset = 0x0807060504030201UL,
.stream_id = 0x210,

View file

@ -274,7 +274,7 @@ main (void)
{ "Balls to the wall: every possible bit is set",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 1<<0,
0x41, 0x23, /* Stream ID */
@ -293,7 +293,7 @@ main (void)
{ "Balls to the wall #2: every possible bit is set except FIN",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -312,7 +312,7 @@ main (void)
{ "Data length is zero",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 0<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -330,7 +330,7 @@ main (void)
{ "Sanity check: what happens when data length is zero #1",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -349,7 +349,7 @@ main (void)
{ "Sanity check: what happens when data length is zero #2",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -368,7 +368,7 @@ main (void)
{ "Sanity check: what happens when data length is zero #3",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 0<<2 | 1<<1 | 0<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -386,7 +386,7 @@ main (void)
{ "Sanity check: what happens when data length is zero #3",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 1<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -405,7 +405,7 @@ main (void)
{ "Check data bounds #1",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 1<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */
@ -424,7 +424,7 @@ main (void)
{ "Check data bounds #2",
__LINE__,
select_pf_by_ver(LSQVER_ID27),
select_pf_by_ver(LSQVER_I001),
/* TYPE OFF DLEN FIN */
{ 0x10 | 1<<2 | 1<<1 | 1<<0,
0x81, 0x23, 0x00, 0xE4, /* Stream ID */