diff --git a/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h index 1368c368db..0e17c2850d 100644 --- a/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +++ b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -58,6 +58,15 @@ ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, void *tls_native_handle); +/** + * @function + * + * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher + * AEAD_AES_128_GCM for Retry packet integrity protection. + */ +NGTCP2_EXTERN ngtcp2_crypto_aead * +ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); + /** * @function * @@ -507,6 +516,45 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token( uint8_t *token, const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen, const ngtcp2_cid *cid); +/** + * @function + * + * `ngtcp2_crypto_write_connection_close` writes Initial packet + * containing CONNECTION_CLOSE with the given |error_code| to the + * buffer pointed by |dest| of length |destlen|. This function is + * designed for server to close connection without committing the + * state when validating Retry token fails. This function must not be + * used by client. The |dcid| must be the Source Connection ID in + * Initial packet from client. The |scid| must be the Destination + * Connection ID in Initial packet from client. |scid| is used to + * derive initial keying materials. + * + * This function wraps around `ngtcp2_pkt_write_connection_close` for + * easier use. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close( + uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code); + +/** + * @function + * + * `ngtcp2_crypto_write_retry` writes Retry packet to the buffer + * pointed by |dest| of length |destlen|. |odcid| specifies Original + * Destination Connection ID. |token| specifies Retry Token, and + * |tokenlen| specifies its length. + * + * This function wraps around `ngtcp2_pkt_write_retry` for easier use. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize +ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, + const uint8_t *token, size_t tokenlen); + #ifdef __cplusplus } #endif diff --git a/deps/ngtcp2/crypto/openssl/openssl.c b/deps/ngtcp2/crypto/openssl/openssl.c index 95e36c2981..17320ae932 100644 --- a/deps/ngtcp2/crypto/openssl/openssl.c +++ b/deps/ngtcp2/crypto/openssl/openssl.c @@ -42,6 +42,11 @@ ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { return ctx; } +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + aead->native_handle = (void *)EVP_aes_128_gcm(); + return aead; +} + static const EVP_CIPHER *crypto_ssl_get_aead(SSL *ssl) { switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { case TLS1_3_CK_AES_128_GCM_SHA256: diff --git a/deps/ngtcp2/crypto/shared.c b/deps/ngtcp2/crypto/shared.c index f12736ce6f..d2a41d6b32 100644 --- a/deps/ngtcp2/crypto/shared.c +++ b/deps/ngtcp2/crypto/shared.c @@ -264,6 +264,7 @@ int ngtcp2_crypto_derive_and_install_initial_key( uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; ngtcp2_crypto_ctx ctx; + ngtcp2_crypto_aead retry_aead; int rv; ngtcp2_crypto_ctx_initial(&ctx); @@ -323,6 +324,8 @@ int ngtcp2_crypto_derive_and_install_initial_key( return -1; } + ngtcp2_conn_set_retry_aead(conn, ngtcp2_crypto_aead_retry(&retry_aead)); + return 0; } @@ -442,3 +445,59 @@ int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token, return 0; } + +ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, + const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, + uint64_t error_code) { + uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_hp_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + ngtcp2_crypto_ctx ctx; + ngtcp2_ssize spktlen; + + ngtcp2_crypto_ctx_initial(&ctx); + + if (ngtcp2_crypto_derive_initial_secrets(rx_secret, tx_secret, initial_secret, + scid, + NGTCP2_CRYPTO_SIDE_SERVER) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + spktlen = ngtcp2_pkt_write_connection_close( + dest, destlen, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb, + &ctx.aead, tx_key, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, tx_hp_key); + if (spktlen < 0) { + return -1; + } + + return spktlen; +} + +ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, + const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, + const ngtcp2_cid *odcid, + const uint8_t *token, size_t tokenlen) { + ngtcp2_crypto_aead aead; + ngtcp2_ssize spktlen; + + ngtcp2_crypto_aead_retry(&aead); + + spktlen = ngtcp2_pkt_write_retry(dest, destlen, dcid, scid, odcid, token, + tokenlen, ngtcp2_crypto_encrypt_cb, &aead); + if (spktlen < 0) { + return -1; + } + + return spktlen; +} diff --git a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h index 6b1ea084e5..917cbb6f22 100644 --- a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -159,7 +159,7 @@ typedef struct ngtcp2_mem { } ngtcp2_mem; /* NGTCP2_PROTO_VER is the supported QUIC protocol version. */ -#define NGTCP2_PROTO_VER 0xff000018u +#define NGTCP2_PROTO_VER 0xff000019u /* NGTCP2_PROTO_VER_MAX is the highest QUIC version the library supports. */ #define NGTCP2_PROTO_VER_MAX NGTCP2_PROTO_VER @@ -167,7 +167,7 @@ typedef struct ngtcp2_mem { /* NGTCP2_ALPN_H3 is a serialized form of HTTP/3 ALPN protocol identifier this library supports. Notice that the first byte is the length of the following protocol identifier. */ -#define NGTCP2_ALPN_H3 "\x5h3-24" +#define NGTCP2_ALPN_H3 "\x5h3-25" #define NGTCP2_MAX_PKTLEN_IPV4 1252 #define NGTCP2_MAX_PKTLEN_IPV6 1232 @@ -227,6 +227,7 @@ typedef enum ngtcp2_lib_error { NGTCP2_ERR_STREAM_IN_USE = -209, NGTCP2_ERR_STREAM_DATA_BLOCKED = -210, NGTCP2_ERR_FLOW_CONTROL = -211, + NGTCP2_ERR_CONNECTION_ID_LIMIT = -212, NGTCP2_ERR_STREAM_LIMIT = -213, NGTCP2_ERR_FINAL_SIZE = -214, NGTCP2_ERR_CRYPTO = -215, @@ -286,7 +287,9 @@ typedef enum ngtcp2_pkt_type { #define NGTCP2_FINAL_SIZE_ERROR 0x6u #define NGTCP2_FRAME_ENCODING_ERROR 0x7u #define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8u +#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9u #define NGTCP2_PROTOCOL_VIOLATION 0xau +#define NGTCP2_INVALID_TOKEN 0xbu #define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xdu #define NGTCP2_KEY_UPDATE_ERROR 0xeu #define NGTCP2_CRYPTO_ERROR 0x100u @@ -377,10 +380,14 @@ typedef struct ngtcp2_pkt_stateless_reset { size_t randlen; } ngtcp2_pkt_stateless_reset; +/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ +#define NGTCP2_RETRY_TAGLEN 16 + typedef struct ngtcp2_pkt_retry { ngtcp2_cid odcid; const uint8_t *token; size_t tokenlen; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; } ngtcp2_pkt_retry; #if defined(__cplusplus) && __cplusplus >= 201103L @@ -389,7 +396,7 @@ typedef enum ngtcp2_transport_param_id : uint16_t { typedef enum ngtcp2_transport_param_id { #endif NGTCP2_TRANSPORT_PARAM_ORIGINAL_CONNECTION_ID = 0x0000, - NGTCP2_TRANSPORT_PARAM_IDLE_TIMEOUT = 0x0001, + NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, NGTCP2_TRANSPORT_PARAM_MAX_PACKET_SIZE = 0x0003, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, @@ -452,6 +459,14 @@ typedef enum ngtcp2_rand_ctx { */ #define NGTCP2_DEFAULT_MAX_ACK_DELAY (25 * NGTCP2_MILLISECONDS) +/** + * @macro + * + * NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT is the default value of + * active_connection_id_limit transport parameter value if omitted. + */ +#define NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT 2 + /** * @macro * @@ -499,10 +514,12 @@ typedef struct ngtcp2_transport_params { /* initial_max_streams_uni is the number of concurrent unidirectional streams that the remote endpoint can create. */ uint64_t initial_max_streams_uni; - /* idle_timeout is a duration during which endpoint allows + /* max_idle_timeout is a duration during which sender allows quiescent. */ - ngtcp2_duration idle_timeout; + ngtcp2_duration max_idle_timeout; uint64_t max_packet_size; + /* active_connection_id_limit is the maximum number of Connection ID + that sender can store. */ uint64_t active_connection_id_limit; uint64_t ack_delay_exponent; ngtcp2_duration max_ack_delay; @@ -559,13 +576,12 @@ typedef struct ngtcp2_rcvry_stat { ngtcp2_duration smoothed_rtt; ngtcp2_duration rttvar; size_t pto_count; - /* probe_pkt_left is the number of probe packet to sent */ - size_t probe_pkt_left; ngtcp2_tstamp loss_detection_timer; /* last_tx_pkt_ts corresponds to time_of_last_sent_ack_eliciting_packet in - draft-ietf-quic-recovery-23. */ - ngtcp2_tstamp last_tx_pkt_ts; + draft-ietf-quic-recovery-25. It is indexed by + ngtcp2_crypto_level. No last_tx_pkt_ts for 0RTT packet. */ + ngtcp2_tstamp last_tx_pkt_ts[3]; } ngtcp2_rcvry_stat; typedef struct ngtcp2_cc_stat { @@ -835,29 +851,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, * :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN`. */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset( - uint8_t *dest, size_t destlen, uint8_t *stateless_reset_token, - uint8_t *rand, size_t randlen); - -/** - * @function - * - * `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed - * by |dest| whose length is |destlen|. |hd| must be long packet - * header, and its type must be :enum:`NGTCP2_PKT_RETRY`. |odcid| - * specifies Original Destination Connection ID. |token| specifies - * Retry Token, and |tokenlen| specifies its length. - * - * This function returns the number of bytes written to the buffer, or - * one of the following negative error codes: - * - * :enum:`NGTCP2_ERR_NOBUF` - * Buffer is too small. - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry(uint8_t *dest, size_t destlen, - const ngtcp2_pkt_hd *hd, - const ngtcp2_cid *odcid, - const uint8_t *token, - size_t tokenlen); + uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token, + const uint8_t *rand, size_t randlen); /** * @function @@ -1010,6 +1005,19 @@ typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn, */ typedef int (*ngtcp2_handshake_completed)(ngtcp2_conn *conn, void *user_data); +/** + * @functypedef + * + * :type:`ngtcp2_handshake_confirmed` is invoked when QUIC + * cryptographic handshake is confirmed. The handshake confirmation + * means that both endpoints agree that handshake has finished. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_handshake_confirmed)(ngtcp2_conn *conn, void *user_data); + /** * @functypedef * @@ -1608,9 +1616,67 @@ typedef struct ngtcp2_conn_callbacks { * destination Connection ID is now deactivated. */ ngtcp2_connection_id_status dcid_status; + /** + * handshake_confirmed is a callback function which is invoked when + * both endpoints agree that handshake has finished. This field is + * ignored by server because handshake_completed indicates the + * handshake confirmation for server. + */ + ngtcp2_handshake_confirmed handshake_confirmed; } ngtcp2_conn_callbacks; -/* +/** + * @function + * + * `ngtcp2_pkt_write_connection_close` writes Initial packet + * containing CONNECTION_CLOSE frame with the given |error_code| to + * the buffer pointed by |dest| of length |destlen|. All encryption + * parameters are for Initial packet encryption. The packet number is + * always 0. + * + * The primary use case of this function is for server to send + * CONNECTION_CLOSE frame in Initial packet to close connection + * without committing the state when validating Retry token fails. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE + * Callback function failed. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( + uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead, const uint8_t *key, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key); + +/** + * @function + * + * `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed + * by |dest| whose length is |destlen|. |odcid| specifies Original + * Destination Connection ID. |token| specifies Retry Token, and + * |tokenlen| specifies its length. |aead| must be AEAD_AES_128_GCM. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE + * Callback function failed. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( + uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, + size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead); + +/** + * @function + * * `ngtcp2_accept` is used by server implementation, and decides * whether packet |pkt| of length |pktlen| is acceptable for initial * packet from client. @@ -2596,6 +2662,17 @@ ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn); NGTCP2_EXTERN void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, const ngtcp2_crypto_ctx *ctx); +/** + * @function + * + * `ngtcp2_conn_set_retry_aead` sets |aead| for Retry integrity tag + * verification. It must be AEAD_AES_128_GCM. This function must be + * called if |conn| is initialized as client. Server does not verify + * the tag and has no need to call this function. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, + const ngtcp2_crypto_aead *aead); + /** * @function * diff --git a/deps/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/lib/ngtcp2_acktr.h index 760d9520e5..0efd2156ac 100644 --- a/deps/ngtcp2/lib/ngtcp2_acktr.h +++ b/deps/ngtcp2/lib/ngtcp2_acktr.h @@ -101,10 +101,6 @@ typedef enum { /* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending protected packet to be acknowledged. */ NGTCP2_ACKTR_FLAG_ACTIVE_ACK = 0x02, - /* NGTCP2_ACKTR_FLAG_PENDING_ACK_FINISHED is set when server - received TLSv1.3 Finished message, and its acknowledgement is - pending. */ - NGTCP2_ACKTR_FLAG_PENDING_FINISHED_ACK = 0x40, /* NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK is set when server received acknowledgement for ACK which acknowledges the last handshake packet from client (which contains TLSv1.3 Finished message). */ diff --git a/deps/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/lib/ngtcp2_cid.h index cb61a21d52..fe5576ae46 100644 --- a/deps/ngtcp2/lib/ngtcp2_cid.h +++ b/deps/ngtcp2/lib/ngtcp2_cid.h @@ -38,10 +38,6 @@ typedef enum { NGTCP2_SCID_FLAG_NONE, NGTCP2_SCID_FLAG_USED = 0x01, NGTCP2_SCID_FLAG_RETIRED = 0x02, - /* NGTCP2_SCID_FLAG_INITIAL_CID indicates that this Connection ID is - provided during handshake (initial Connection ID or in - preferred_address transport parameter). */ - NGTCP2_SCID_FLAG_INITIAL_CID = 0x04, } ngtcp2_scid_flag; typedef struct { diff --git a/deps/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/lib/ngtcp2_conn.c index 1f8d9dd77f..add4e5d924 100644 --- a/deps/ngtcp2/lib/ngtcp2_conn.c +++ b/deps/ngtcp2/lib/ngtcp2_conn.c @@ -366,6 +366,8 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_crypto_level crypto_level, ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { int rv; + memset(pktns, 0, sizeof(*pktns)); + rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem); if (rv != 0) { return rv; @@ -406,6 +408,24 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_crypto_level crypto_level, return rv; } +static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_crypto_level crypto_level, + ngtcp2_rst *rst, ngtcp2_default_cc *cc, ngtcp2_log *log, + ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + int rv; + + *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); + if (*ppktns == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = pktns_init(*ppktns, crypto_level, rst, cc, log, qlog, mem); + if (rv != 0) { + ngtcp2_mem_free(mem, *ppktns); + } + + return rv; +} + static int cycle_less(const ngtcp2_pq_entry *lhs, const ngtcp2_pq_entry *rhs) { ngtcp2_strm *ls = ngtcp2_struct_of(lhs, ngtcp2_strm, pe); ngtcp2_strm *rs = ngtcp2_struct_of(rhs, ngtcp2_strm, pe); @@ -454,6 +474,16 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { ngtcp2_gaptr_free(&pktns->rx.pngap); } +static void pktns_del(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { + if (pktns == NULL) { + return; + } + + pktns_free(pktns, mem); + + ngtcp2_mem_free(mem, pktns); +} + static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return ngtcp2_cid_less(lhs->ptr, rhs->ptr); } @@ -469,6 +499,8 @@ static int ts_retired_less(const ngtcp2_pq_entry *lhs, static void rcvry_stat_reset(ngtcp2_rcvry_stat *rcs) { memset(rcs, 0, sizeof(*rcs)); rcs->min_rtt = UINT64_MAX; + // Initializes them with UINT64_MAX. + memset(rcs->last_tx_pkt_ts, 0xff, sizeof(rcs->last_tx_pkt_ts)); } static void cc_stat_reset(ngtcp2_cc_stat *ccs) { @@ -498,6 +530,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_transport_params *params = &settings->transport_params; uint8_t *buf; + assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); + if (mem == NULL) { mem = ngtcp2_mem_default(); } @@ -568,16 +602,16 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_default_cc_init(&(*pconn)->cc, &(*pconn)->ccs, &(*pconn)->rst, &(*pconn)->log); - rv = pktns_init(&(*pconn)->in_pktns, NGTCP2_CRYPTO_LEVEL_INITIAL, - &(*pconn)->rst, &(*pconn)->cc, &(*pconn)->log, - &(*pconn)->qlog, mem); + rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_CRYPTO_LEVEL_INITIAL, + &(*pconn)->rst, &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + mem); if (rv != 0) { goto fail_in_pktns_init; } - rv = pktns_init(&(*pconn)->hs_pktns, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, - &(*pconn)->rst, &(*pconn)->cc, &(*pconn)->log, - &(*pconn)->qlog, mem); + rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, + &(*pconn)->rst, &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + mem); if (rv != 0) { goto fail_hs_pktns_init; } @@ -612,7 +646,6 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, params->stateless_reset_token_present ? params->stateless_reset_token : NULL); - scident->flags |= NGTCP2_SCID_FLAG_INITIAL_CID; rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, ngtcp2_ksl_key_ptr(&key, &scident->cid), scident); @@ -622,8 +655,6 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, scident = NULL; - (*pconn)->scid.num_initial_id = 1; - if (server && params->preferred_address_present) { scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); if (scident == NULL) { @@ -633,7 +664,6 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid, params->preferred_address.stateless_reset_token); - scident->flags |= NGTCP2_SCID_FLAG_INITIAL_CID; rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, ngtcp2_ksl_key_ptr(&key, &scident->cid), scident); @@ -644,7 +674,6 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, scident = NULL; (*pconn)->scid.last_seq = 1; - ++(*pconn)->scid.num_initial_id; } ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); @@ -681,9 +710,9 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, fail_token: pktns_free(&(*pconn)->pktns, mem); fail_pktns_init: - pktns_free(&(*pconn)->hs_pktns, mem); + pktns_del((*pconn)->hs_pktns, mem); fail_hs_pktns_init: - pktns_free(&(*pconn)->in_pktns, mem); + pktns_del((*pconn)->in_pktns, mem); fail_in_pktns_init: ngtcp2_default_cc_free(&(*pconn)->cc); ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin); @@ -724,6 +753,7 @@ int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->state = NGTCP2_CS_CLIENT_INITIAL; (*pconn)->local.bidi.next_stream_id = 0; (*pconn)->local.uni.next_stream_id = 2; + return 0; } @@ -734,6 +764,8 @@ int ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_settings *settings, const ngtcp2_mem *mem, void *user_data) { int rv; + ngtcp2_transport_params *params; + rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, mem, user_data, 1); if (rv != 0) { @@ -743,6 +775,13 @@ int ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; (*pconn)->local.bidi.next_stream_id = 1; (*pconn)->local.uni.next_stream_id = 3; + + params = &(*pconn)->local.settings.transport_params; + if (dcid->datalen == 0) { + /* Client uses zero-length Connection ID */ + params->active_connection_id_limit = 0; + } + return 0; } @@ -795,8 +834,8 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); pktns_free(&conn->pktns, conn->mem); - pktns_free(&conn->hs_pktns, conn->mem); - pktns_free(&conn->in_pktns, conn->mem); + pktns_del(conn->hs_pktns, conn->mem); + pktns_del(conn->in_pktns, conn->mem); ngtcp2_default_cc_free(&conn->cc); @@ -1049,9 +1088,9 @@ static int conn_on_pkt_sent(ngtcp2_conn *conn, ngtcp2_rtb *rtb, } if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { - conn->rcs.last_tx_pkt_ts = ent->ts; + conn->rcs.last_tx_pkt_ts[rtb->crypto_level] = ent->ts; } - ngtcp2_conn_set_loss_detection_timer(conn); + ngtcp2_conn_set_loss_detection_timer(conn, ent->ts); return 0; } @@ -1559,17 +1598,18 @@ static ngtcp2_ssize conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest, switch (type) { case NGTCP2_PKT_INITIAL: - if (!conn->in_pktns.crypto.tx.ckm) { + if (!conn->in_pktns) { return 0; } - pktns = &conn->in_pktns; + assert(conn->in_pktns->crypto.tx.ckm); + pktns = conn->in_pktns; cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; break; case NGTCP2_PKT_HANDSHAKE: - if (!conn->hs_pktns.crypto.tx.ckm) { + if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) { return 0; } - pktns = &conn->hs_pktns; + pktns = conn->hs_pktns; cc.aead_overhead = conn->crypto.aead_overhead; break; default: @@ -1657,22 +1697,41 @@ static ngtcp2_ssize conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest, } } - if (!conn->server && !(flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && - !conn->pktns.crypto.tx.ckm && conn->rcs.probe_pkt_left) { - /* Do not send PING frame if client have not got acknowledgement - of first Initial. */ - if (type == NGTCP2_PKT_HANDSHAKE || - (!conn->hs_pktns.crypto.tx.ckm && - ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) { - lfr.type = NGTCP2_FRAME_PING; + /* Don't send any PING frame if client Initial has not been + acknowledged yet. */ + if (!(flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && pktns->rtb.probe_pkt_left && + (type != NGTCP2_PKT_INITIAL || + ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) { + lfr.type = NGTCP2_FRAME_PING; - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); - if (rv != 0) { - assert(rv == NGTCP2_ERR_NOBUF); + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + } else { + flags |= NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_PROBE; + pkt_empty = 0; + } + } + + if (!pkt_empty) { + if (!(flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + /* The intention of smaller limit is get more chance to measure + RTT samples in early phase. */ + if (pktns->rtb.probe_pkt_left || pktns->tx.num_non_ack_pkt >= 1) { + lfr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + } else { + flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pktns->tx.num_non_ack_pkt = 0; + } } else { - flags |= NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_PROBE; - pkt_empty = 0; + ++pktns->tx.num_non_ack_pkt; } + } else { + pktns->tx.num_non_ack_pkt = 0; } } @@ -1705,7 +1764,7 @@ static ngtcp2_ssize conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest, ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)spktlen); - if (*pfrc != frq || padded) { + if ((flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) { rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen, flags, conn->mem); if (rv != 0) { @@ -1726,8 +1785,8 @@ static ngtcp2_ssize conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest, } } - if (conn->rcs.probe_pkt_left) { - --conn->rcs.probe_pkt_left; + if (pktns->rtb.probe_pkt_left && (flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + --pktns->rtb.probe_pkt_left; } ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); @@ -1764,12 +1823,12 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, uint8_t *dest, switch (type) { case NGTCP2_PKT_INITIAL: assert(conn->server); - pktns = &conn->in_pktns; + pktns = conn->in_pktns; ack_delay = 0; ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; break; case NGTCP2_PKT_HANDSHAKE: - pktns = &conn->hs_pktns; + pktns = conn->hs_pktns; ack_delay = 0; ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; break; @@ -1816,7 +1875,7 @@ static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn, /* Actullay client never send ACK for server Initial. This is because once it gets server Initial, it gets Handshake tx key and discards Initial key. */ - if (conn->server) { + if (conn->server && conn->in_pktns) { nwrite = conn_write_ack_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, ts); if (nwrite < 0) { assert(nwrite != NGTCP2_ERR_NOBUF); @@ -1828,7 +1887,7 @@ static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn, destlen -= (size_t)nwrite; } - if (conn->hs_pktns.crypto.tx.ckm) { + if (conn->hs_pktns->crypto.tx.ckm) { nwrite = conn_write_ack_pkt(conn, dest, destlen, NGTCP2_PKT_HANDSHAKE, ts); if (nwrite < 0) { assert(nwrite != NGTCP2_ERR_NOBUF); @@ -1887,7 +1946,6 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, uint8_t *dest, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; ngtcp2_ssize res = 0; - int probe = conn->rcs.probe_pkt_left != 0; nwrite = conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, early_datalen, ts); @@ -1896,7 +1954,7 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, uint8_t *dest, return nwrite; } - if (nwrite && probe) { + if (!conn->server && nwrite) { return nwrite; } @@ -1998,25 +2056,19 @@ static int conn_should_send_max_data(ngtcp2_conn *conn) { * remote endpoint. */ static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) { - size_t n, left; + size_t n, len = ngtcp2_ksl_len(&conn->scid.set); - if (ngtcp2_ksl_len(&conn->scid.set) >= NGTCP2_MAX_SCID_POOL_SIZE) { + if (len >= NGTCP2_MAX_SCID_POOL_SIZE) { return 0; } - left = NGTCP2_MAX_SCID_POOL_SIZE - ngtcp2_ksl_len(&conn->scid.set); - - assert(ngtcp2_ksl_len(&conn->scid.set) >= - conn->scid.num_initial_id + conn->scid.num_retired); + /* len includes retired CID. We don't provide extra CID if doing so + excceds NGTCP2_MAX_SCID_POOL_SIZE. */ - n = ngtcp2_ksl_len(&conn->scid.set) - conn->scid.num_initial_id - + n = conn->remote.transport_params.active_connection_id_limit + conn->scid.num_retired; - assert(conn->remote.transport_params.active_connection_id_limit >= n); - - n = conn->remote.transport_params.active_connection_id_limit - n; - - return ngtcp2_min(n, left); + return ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len; } /* @@ -2267,13 +2319,6 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, } } - if (conn->oscid.datalen) { - rv = conn_enqueue_new_connection_id(conn); - if (rv != 0) { - return rv; - } - } - if (!ppe_pending) { switch (type) { case NGTCP2_PKT_SHORT: @@ -2283,6 +2328,18 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, : NGTCP2_PKT_FLAG_NONE; cc->ckm = pktns->crypto.tx.ckm; cc->hp_key = pktns->crypto.tx.hp_key; + + /* transport parameter is only valid after handshake completion + which means we don't know how many connection ID that remote + peer can accept before handshake completion. */ + if (conn->oscid.datalen && + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + rv = conn_enqueue_new_connection_id(conn); + if (rv != 0) { + return rv; + } + } + break; case NGTCP2_PKT_0RTT: assert(!conn->server); @@ -2734,6 +2791,25 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, } } + if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + if (pktns->rtb.probe_pkt_left || + pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) { + lfr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + } else { + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pktns->tx.num_non_ack_pkt = 0; + } + } else { + ++pktns->tx.num_non_ack_pkt; + } + } else { + pktns->tx.num_non_ack_pkt = 0; + } + /* TODO Push STREAM frame back to ngtcp2_strm if there is an error before ngtcp2_rtb_entry is safely created and added. */ lfr.type = NGTCP2_FRAME_PADDING; @@ -2759,7 +2835,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite); - if (*pfrc != pktns->tx.frq || padded) { + if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) { rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite, rtb_entry_flags, conn->mem); if (rv != 0) { @@ -2814,12 +2890,12 @@ ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest, switch (type) { case NGTCP2_PKT_INITIAL: - pktns = &conn->in_pktns; + pktns = conn->in_pktns; cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; flags = NGTCP2_PKT_FLAG_LONG_FORM; break; case NGTCP2_PKT_HANDSHAKE: - pktns = &conn->hs_pktns; + pktns = conn->hs_pktns; cc.aead_overhead = conn->crypto.aead_overhead; flags = NGTCP2_PKT_FLAG_LONG_FORM; break; @@ -3121,7 +3197,8 @@ static ngtcp2_ssize conn_write_probe_pkt(ngtcp2_conn *conn, uint8_t *dest, ngtcp2_ssize nwrite; ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "transmit probe pkt left=%zu", conn->rcs.probe_pkt_left); + "transmit probe pkt left=%zu", + conn->pktns.rtb.probe_pkt_left); /* a probe packet is not blocked by cwnd. */ nwrite = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, @@ -3133,7 +3210,7 @@ static ngtcp2_ssize conn_write_probe_pkt(ngtcp2_conn *conn, uint8_t *dest, return nwrite; } - --conn->rcs.probe_pkt_left; + --conn->pktns.rtb.probe_pkt_left; ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", nwrite); @@ -3147,11 +3224,14 @@ static ngtcp2_ssize conn_write_probe_pkt(ngtcp2_conn *conn, uint8_t *dest, * packets and lost ones. */ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || - ngtcp2_rtb_num_ack_eliciting(&conn->in_pktns.rtb) || - ngtcp2_rtb_num_ack_eliciting(&conn->hs_pktns.rtb) || - ngtcp2_ksl_len(&conn->in_pktns.crypto.tx.frq) || - ngtcp2_ksl_len(&conn->hs_pktns.crypto.tx.frq); + (in_pktns && (ngtcp2_rtb_num_ack_eliciting(&in_pktns->rtb) || + ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) || + (hs_pktns && (ngtcp2_rtb_num_ack_eliciting(&hs_pktns->rtb) || + ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq))); } /* @@ -3245,6 +3325,8 @@ static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return rv; } +static void conn_reset_congestion_state(ngtcp2_conn *conn); + /* * conn_on_path_validation_failed is called when path validation * fails. This function may delete |pv|. @@ -3275,6 +3357,7 @@ static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv, if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid); + conn_reset_congestion_state(conn); } return conn_stop_pv(conn, ts); @@ -3499,13 +3582,11 @@ static int conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, return 0; } -static void conn_reset_congestion_state(ngtcp2_conn *conn); - /* * conn_on_retry is called when Retry packet is received. The - * function decodes the data in the buffer pointed by |payload| whose - * length is |payloadlen| as Retry packet payload. The packet header - * is given in |hd|. + * function decodes the data in the buffer pointed by |pkt| whose + * length is |pktlen| as Retry packet. The length of long packet + * header is given in |hdpktlen|. |pkt| includes packet header. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -3520,12 +3601,13 @@ static void conn_reset_congestion_state(ngtcp2_conn *conn); * ODCID does not match; or Token is empty. */ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, - const uint8_t *payload, size_t payloadlen) { + size_t hdpktlen, const uint8_t *pkt, size_t pktlen) { int rv; ngtcp2_pkt_retry retry; uint8_t *p; + ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_rtb *rtb = &conn->pktns.rtb; - ngtcp2_rtb *in_rtb = &conn->in_pktns.rtb; + ngtcp2_rtb *in_rtb = &in_pktns->rtb; uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1]; ngtcp2_frame_chain *frc = NULL; @@ -3533,8 +3615,18 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, return 0; } - rv = ngtcp2_pkt_decode_retry(&retry, payload, payloadlen); + rv = ngtcp2_pkt_decode_retry(&retry, pkt + hdpktlen, pktlen - hdpktlen); + if (rv != 0) { + return rv; + } + + retry.odcid = conn->dcid.current.cid; + + rv = ngtcp2_pkt_verify_retry_tag(&retry, pkt, pktlen, conn->callbacks.encrypt, + &conn->crypto.retry_aead); if (rv != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "unable to verify Retry packet integrity"); return rv; } @@ -3546,8 +3638,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, return NGTCP2_ERR_PROTO; } - if (ngtcp2_cid_eq(&conn->dcid.current.cid, &hd->scid) || - !ngtcp2_cid_eq(&conn->dcid.current.cid, &retry.odcid)) { + if (ngtcp2_cid_eq(&conn->dcid.current.cid, &hd->scid)) { return 0; } @@ -3580,7 +3671,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, frc = NULL; ngtcp2_rtb_remove_all(in_rtb, &frc); - rv = conn_resched_frames(conn, &conn->in_pktns, &frc); + rv = conn_resched_frames(conn, in_pktns, &frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); ngtcp2_frame_chain_list_del(frc, conn->mem); @@ -3622,7 +3713,10 @@ int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, } /* - * conn_recv_ack processes received ACK frame |fr|. + * conn_recv_ack processes received ACK frame |fr|. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -3637,7 +3731,7 @@ int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, * User callback failed. */ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, - ngtcp2_tstamp ts) { + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { int rv; ngtcp2_frame_chain *frc = NULL; ngtcp2_rcvry_stat *rcs = &conn->rcs; @@ -3654,7 +3748,7 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, ngtcp2_acktr_recv_ack(&pktns->acktr, fr); - num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, conn, ts); + num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, conn, pkt_ts, ts); if (num_acked < 0) { /* TODO assert this */ assert(ngtcp2_err_is_fatal((int)num_acked)); @@ -3672,9 +3766,9 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, } rcs->pto_count = 0; - rcs->probe_pkt_left = 0; + pktns->rtb.probe_pkt_left = 0; - ngtcp2_conn_set_loss_detection_timer(conn); + ngtcp2_conn_set_loss_detection_timer(conn, ts); return 0; } @@ -4029,14 +4123,13 @@ static void conn_recv_path_challenge(ngtcp2_conn *conn, * conn_reset_congestion_state resets congestion state. */ static void conn_reset_congestion_state(ngtcp2_conn *conn) { - uint64_t bytes_in_flight; - rcvry_stat_reset(&conn->rcs); - /* Keep bytes_in_flight because we have to take care of packets - in flight. */ - bytes_in_flight = conn->ccs.bytes_in_flight; cc_stat_reset(&conn->ccs); - conn->ccs.bytes_in_flight = bytes_in_flight; + + if (conn->hs_pktns) { + conn->hs_pktns->rtb.cc_pkt_num = conn->hs_pktns->tx.last_pkt_num + 1; + } + conn->pktns.rtb.cc_pkt_num = conn->pktns.tx.last_pkt_num + 1; } static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, @@ -4056,8 +4149,6 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, return 0; } - conn_reset_congestion_state(conn); - /* If validation succeeds, we don't have to throw DCID away. */ pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH; @@ -4077,6 +4168,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, goto fail; } ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); + conn_reset_congestion_state(conn); } rv = conn_call_path_validation(conn, &pv->dcid.ps.path, @@ -4160,29 +4252,42 @@ static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num) { return 0; } +static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { + uint64_t bytes_in_flight; + + bytes_in_flight = ngtcp2_rtb_get_bytes_in_flight(&pktns->rtb); + + assert(conn->ccs.bytes_in_flight >= bytes_in_flight); + + conn->ccs.bytes_in_flight -= bytes_in_flight; + + pktns_del(pktns, conn->mem); +} + /* - * conn_discard_initial_key discards Initial packet protection keys. + * conn_discard_initial_state discards state for Initial packet number + * space. */ -static void conn_discard_initial_key(ngtcp2_conn *conn) { - ngtcp2_pktns *pktns = &conn->in_pktns; - - if (conn->flags & NGTCP2_CONN_FLAG_INITIAL_KEY_DISCARDED) { +static void conn_discard_initial_state(ngtcp2_conn *conn) { + if (!conn->in_pktns) { return; } - conn->flags |= NGTCP2_CONN_FLAG_INITIAL_KEY_DISCARDED; - - ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); - ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); - - pktns->crypto.tx.ckm = NULL; - pktns->crypto.rx.ckm = NULL; + conn_discard_pktns(conn, conn->in_pktns); + conn->in_pktns = NULL; +} - delete_buffed_pkts(pktns->rx.buffed_pkts, conn->mem); - pktns->rx.buffed_pkts = NULL; +/* + * conn_discard_handshake_state discards state for Handshake packet + * number space. + */ +static void conn_discard_handshake_state(ngtcp2_conn *conn) { + if (!conn->hs_pktns) { + return; + } - ngtcp2_rtb_clear(&pktns->rtb); - ngtcp2_acktr_commit_ack(&pktns->acktr); + conn_discard_pktns(conn, conn->hs_pktns); + conn->hs_pktns = NULL; } /* @@ -4202,7 +4307,7 @@ static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts); + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts); static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, @@ -4212,7 +4317,9 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, * conn_recv_handshake_pkt processes received packet |pkt| whose * length is |pktlen| during handshake period. The buffer pointed by * |pkt| might contain multiple packets. This function only processes - * one packet. + * one packet. |pkt_ts| is the timestamp when packet is received. + * |ts| should be the current time. Usually they are the same, but + * for buffered packets, |pkt_ts| would be earlier than |ts|. * * This function returns the number of bytes it reads if it succeeds, * or one of the following negative error codes: @@ -4241,6 +4348,7 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { ngtcp2_ssize nread; ngtcp2_pkt_hd hd; @@ -4276,18 +4384,10 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, return (ngtcp2_ssize)pktlen; } - if (!conn->server && conn->pktns.crypto.rx.ckm) { - rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); - if (rv != 0) { - return rv; - } - return conn_recv_pkt(conn, &conn->dcid.current.ps.path, pkt, pktlen, ts); - } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "buffering Short packet len=%zu", pktlen); - rv = conn_buffer_pkt(conn, &conn->hs_pktns, path, pkt, pktlen, ts); + rv = conn_buffer_pkt(conn, &conn->pktns, path, pkt, pktlen, ts); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -4357,7 +4457,7 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, return NGTCP2_ERR_DISCARD_PKT; } - rv = conn_on_retry(conn, &hd, pkt + hdpktlen, pktlen - hdpktlen); + rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen); if (rv != 0) { if (ngtcp2_err_is_fatal(rv)) { return rv; @@ -4396,8 +4496,8 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, if (conn->early.ckm) { ngtcp2_ssize nread2; /* TODO Avoid to parse header twice. */ - nread2 = - conn_recv_pkt(conn, &conn->dcid.current.ps.path, pkt, pktlen, ts); + nread2 = conn_recv_pkt(conn, &conn->dcid.current.ps.path, pkt, pktlen, + pkt_ts, ts); if (nread2 < 0) { return nread2; } @@ -4411,7 +4511,7 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "buffering 0-RTT packet len=%zu", pktlen); - rv = conn_buffer_pkt(conn, &conn->in_pktns, path, pkt, pktlen, ts); + rv = conn_buffer_pkt(conn, conn->in_pktns, path, pkt, pktlen, ts); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -4419,13 +4519,15 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, return (ngtcp2_ssize)pktlen; case NGTCP2_PKT_INITIAL: - if (conn->flags & NGTCP2_CONN_FLAG_INITIAL_KEY_DISCARDED) { + if (!conn->in_pktns) { ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_PKT, "Initial packet is discarded because keys have been discarded"); return (ngtcp2_ssize)pktlen; } + assert(conn->in_pktns); + if (conn->server) { if (conn->local.settings.token.len) { rv = verify_token(&conn->local.settings.token, &hd); @@ -4447,14 +4549,14 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, return NGTCP2_ERR_DISCARD_PKT; } - pktns = &conn->in_pktns; + pktns = conn->in_pktns; aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; - crypto = &conn->in_pktns.crypto.strm; + crypto = &pktns->crypto.strm; crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; break; case NGTCP2_PKT_HANDSHAKE: - if (!conn->hs_pktns.crypto.rx.ckm) { + if (!conn->hs_pktns->crypto.rx.ckm) { if (conn->server) { ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_PKT, @@ -4464,7 +4566,7 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "buffering Handshake packet len=%zu", pktlen); - rv = conn_buffer_pkt(conn, &conn->in_pktns, path, pkt, pktlen, ts); + rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pkt, pktlen, ts); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -4472,9 +4574,9 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, return (ngtcp2_ssize)pktlen; } - pktns = &conn->hs_pktns; + pktns = conn->hs_pktns; aead_overhead = conn->crypto.aead_overhead; - crypto = &conn->hs_pktns.crypto.strm; + crypto = &pktns->crypto.strm; crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; break; @@ -4628,7 +4730,7 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, switch (fr->type) { case NGTCP2_FRAME_ACK: case NGTCP2_FRAME_ACK_ECN: - rv = conn_recv_ack(conn, pktns, &fr->ack, ts); + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); if (rv != 0) { return rv; } @@ -4648,12 +4750,6 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, case NGTCP2_FRAME_CONNECTION_CLOSE: conn_recv_connection_close(conn, &fr->connection_close); break; - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - if (fr->type != NGTCP2_PKT_HANDSHAKE) { - return NGTCP2_ERR_PROTO; - } - conn_recv_connection_close(conn, &fr->connection_close); - break; case NGTCP2_FRAME_PING: require_ack = 1; break; @@ -4681,7 +4777,8 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, ngtcp2_acktr_immediate_ack(&pktns->acktr); } - rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, ts); + rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, + pkt_ts); if (rv != 0) { return rv; } @@ -4711,7 +4808,7 @@ static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, conn->hs_recved += pktlen; while (pktlen) { - nread = conn_recv_handshake_pkt(conn, path, pkt, pktlen, ts); + nread = conn_recv_handshake_pkt(conn, path, pkt, pktlen, ts, ts); if (nread < 0) { if (ngtcp2_err_is_fatal((int)nread)) { return (int)nread; @@ -5547,127 +5644,6 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, return 0; } -/* - * conn_recv_delayed_handshake_pkt processes the received Handshake - * packet which is received after handshake completed. This function - * does the minimal job, and its purpose is send acknowledgement of - * this packet to the peer. We assume that hd->type is one of - * Initial, or Handshake. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_CALLBACK_FAILURE - * User callback failed. - * NGTCP2_ERR_FRAME_ENCODING - * Frame is badly formatted; or frame type is unknown. - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_DISCARD_PKT - * Packet was discarded. - * NGTCP2_ERR_ACK_FRAME - * ACK frame is malformed. - * NGTCP2_ERR_PROTO - * APPLICATION_CLOSE frame is included in Initial packet. - */ -static int -conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, - size_t pktlen, const uint8_t *payload, - size_t payloadlen, ngtcp2_tstamp ts) { - ngtcp2_ssize nread; - ngtcp2_max_frame mfr; - ngtcp2_frame *fr = &mfr.fr; - int rv; - int require_ack = 0; - ngtcp2_pktns *pktns; - - switch (hd->type) { - case NGTCP2_PKT_HANDSHAKE: - pktns = &conn->hs_pktns; - break; - default: - assert(0); - } - - if (payloadlen == 0) { - /* QUIC packet must contain at least one frame */ - return NGTCP2_ERR_DISCARD_PKT; - } - - ngtcp2_qlog_pkt_received_start(&conn->qlog, hd); - - for (; payloadlen;) { - nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); - if (nread < 0) { - return (int)nread; - } - - payload += nread; - payloadlen -= (size_t)nread; - - if (fr->type == NGTCP2_FRAME_ACK) { - fr->ack.ack_delay = 0; - fr->ack.ack_delay_unscaled = 0; - } - - ngtcp2_log_rx_fr(&conn->log, hd, fr); - - switch (fr->type) { - case NGTCP2_FRAME_ACK: - case NGTCP2_FRAME_ACK_ECN: - rv = conn_recv_ack(conn, pktns, &fr->ack, ts); - if (rv != 0) { - return rv; - } - if (!conn->server && hd->type == NGTCP2_PKT_HANDSHAKE) { - conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; - } - break; - case NGTCP2_FRAME_PADDING: - break; - case NGTCP2_FRAME_CONNECTION_CLOSE: - case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - if (hd->type != NGTCP2_PKT_HANDSHAKE) { - break; - } - conn_recv_connection_close(conn, &fr->connection_close); - break; - case NGTCP2_FRAME_CRYPTO: - require_ack = 1; - break; - case NGTCP2_FRAME_PING: - require_ack = 1; - break; - default: - return NGTCP2_ERR_PROTO; - } - - ngtcp2_qlog_write_frame(&conn->qlog, fr); - } - - ngtcp2_qlog_pkt_received_end(&conn->qlog, hd, pktlen); - - rv = pktns_commit_recv_pkt_num(pktns, hd->pkt_num); - if (rv != 0) { - return rv; - } - - if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) { - ngtcp2_acktr_immediate_ack(&pktns->acktr); - } - - rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd->pkt_num, require_ack, ts); - if (rv != 0) { - return rv; - } - - conn_restart_timer_on_read(conn, ts); - - ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); - - return 0; -} - /* * conn_recv_max_streams processes the incoming MAX_STREAMS frame * |fr|. @@ -5750,11 +5726,12 @@ static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb, */ static int conn_recv_new_connection_id(ngtcp2_conn *conn, const ngtcp2_new_connection_id *fr) { - size_t i, len, max; + size_t i, len; ngtcp2_dcid *dcid; ngtcp2_pv *pv = conn->pv; int rv; int found = 0; + size_t extra_dcid = 0; if (conn->dcid.current.cid.datalen == 0) { return NGTCP2_ERR_PROTO; @@ -5822,17 +5799,29 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, } if (!found) { - max = ngtcp2_min( - conn->local.settings.transport_params.active_connection_id_limit, - NGTCP2_MAX_DCID_POOL_SIZE); len = ngtcp2_ringbuf_len(&conn->dcid.unused); - if (len >= max) { + + if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + if (pv) { + if (pv->dcid.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + } + + if (conn->local.settings.transport_params.active_connection_id_limit <= + len + extra_dcid) { + return NGTCP2_ERR_CONNECTION_ID_LIMIT; + } + + if (len >= NGTCP2_MAX_DCID_POOL_SIZE) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "too many connection ID"); - /* If active_connection_id_limit is 0, and no unused DCID is - present, we are going to keep current DCID even if it is under - retire_prior_to. This is violation of peer because we - advertised active_connection_id_limit = 0. */ return 0; } @@ -5966,7 +5955,7 @@ static int conn_recv_retire_connection_id(ngtcp2_conn *conn, ngtcp2_ksl_it it; ngtcp2_scid *scid; - if (conn->oscid.datalen == 0) { + if (conn->oscid.datalen == 0 || conn->scid.last_seq < fr->seq) { return NGTCP2_ERR_PROTO; } @@ -5981,9 +5970,6 @@ static int conn_recv_retire_connection_id(ngtcp2_conn *conn, if (!(scid->flags & NGTCP2_SCID_FLAG_RETIRED)) { scid->flags |= NGTCP2_SCID_FLAG_RETIRED; ++conn->scid.num_retired; - if (scid->flags & NGTCP2_SCID_FLAG_INITIAL_CID) { - --conn->scid.num_initial_id; - } } if (scid->pe.index != NGTCP2_PQ_BAD_INDEX) { @@ -6020,24 +6006,123 @@ static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { } /* - * conn_key_phase_changed returns nonzero if |hd| indicates that the - * key phase has unexpected value. + * conn_select_preferred_addr asks a client application to select a + * server address from preferred addresses received from server. If a + * client chooses the address, path validation will start. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. */ -static int conn_key_phase_changed(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) { - ngtcp2_pktns *pktns = &conn->pktns; +static int conn_select_preferred_addr(ngtcp2_conn *conn) { + uint8_t buf[128]; + ngtcp2_addr addr; + int rv; + ngtcp2_duration timeout; + ngtcp2_pv *pv; + ngtcp2_dcid *dcid; - return !(pktns->crypto.rx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) ^ - !(hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE); -} + ngtcp2_addr_init(&addr, buf, 0, NULL); -/* - * conn_prepare_key_update installs new updated keys. - */ -static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - int rv; - ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; - ngtcp2_duration pto = conn_compute_pto(conn); - ngtcp2_pktns *pktns = &conn->pktns; + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return 0; + } + + rv = conn_call_select_preferred_addr(conn, &addr); + if (rv != 0) { + return rv; + } + + if (addr.addrlen == 0 || + ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) { + return 0; + } + + assert(conn->pv == NULL); + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + timeout = conn_compute_pto(conn); + timeout = + ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_INITIAL_RTT)); + + rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH, + &conn->log, conn->mem); + if (rv != 0) { + /* TODO Call ngtcp2_dcid_free here if it is introduced */ + return rv; + } + + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + conn->pv = pv; + + ngtcp2_addr_copy(&pv->dcid.ps.path.local, &conn->dcid.current.ps.path.local); + ngtcp2_addr_copy(&pv->dcid.ps.path.remote, &addr); + + return conn_call_activate_dcid(conn, &pv->dcid); +} + +/* + * conn_recv_handshake_done processes the incoming HANDSHAKE_DONE + * frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Server received HANDSHAKE_DONE frame. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_recv_handshake_done(ngtcp2_conn *conn) { + int rv; + + if (conn->server) { + return NGTCP2_ERR_PROTO; + } + + if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { + return 0; + } + + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; + + conn_discard_handshake_state(conn); + + if (conn->callbacks.handshake_confirmed) { + rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +/* + * conn_key_phase_changed returns nonzero if |hd| indicates that the + * key phase has unexpected value. + */ +static int conn_key_phase_changed(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) { + ngtcp2_pktns *pktns = &conn->pktns; + + return !(pktns->crypto.rx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) ^ + !(hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE); +} + +/* + * conn_prepare_key_update installs new updated keys. + */ +static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv; + ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; + ngtcp2_duration pto = conn_compute_pto(conn); + ngtcp2_pktns *pktns = &conn->pktns; ngtcp2_crypto_km *rx_ckm = pktns->crypto.rx.ckm; ngtcp2_crypto_km *tx_ckm = pktns->crypto.tx.ckm; ngtcp2_crypto_km *new_rx_ckm, *new_tx_ckm; @@ -6166,6 +6251,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, "path is migrated back to the original path"); ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid); + conn_reset_congestion_state(conn); rv = conn_stop_pv(conn, ts); if (rv != 0) { return rv; @@ -6201,6 +6287,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current); } ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); + conn_reset_congestion_state(conn); if (conn->pv) { ngtcp2_log_info( @@ -6222,7 +6309,10 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, /* * conn_recv_pkt processes a packet contained in the buffer pointed by * |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets. - * This function only processes the first packet. + * This function only processes the first packet. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. * * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: @@ -6254,7 +6344,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, */ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts) { + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { ngtcp2_pkt_hd hd; int rv = 0; size_t hdpktlen; @@ -6307,13 +6397,9 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, "delayed Initial packet was discarded"); return (ngtcp2_ssize)pktlen; case NGTCP2_PKT_HANDSHAKE: - pktns = &conn->hs_pktns; - ckm = pktns->crypto.rx.ckm; - hp_key = pktns->crypto.rx.hp_key; - hp_mask = conn->callbacks.hp_mask; - decrypt = conn->callbacks.decrypt; - aead_overhead = conn->crypto.aead_overhead; - break; + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "delayed Handshake packet was discarded"); + return (ngtcp2_ssize)pktlen; case NGTCP2_PKT_0RTT: if (!conn->server || !conn->early.ckm) { return NGTCP2_ERR_DISCARD_PKT; @@ -6501,20 +6587,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { - if (hd.type == NGTCP2_PKT_HANDSHAKE) { - /* TODO find a way when to ignore incoming handshake packet */ - rv = conn_recv_delayed_handshake_pkt(conn, &hd, pktlen, payload, - payloadlen, ts); - if (rv < 0) { - if (ngtcp2_err_is_fatal(rv)) { - return rv; - } - return (ngtcp2_ssize)rv; - } - return (ngtcp2_ssize)pktlen; - } - } else { + if (!(hd.flags & NGTCP2_PKT_FLAG_LONG_FORM)) { conn->flags |= NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT; } @@ -6556,7 +6629,9 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: case NGTCP2_FRAME_NEW_CONNECTION_ID: + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: case NGTCP2_FRAME_PATH_CHALLENGE: + case NGTCP2_FRAME_PATH_RESPONSE: break; default: return NGTCP2_ERR_PROTO; @@ -6577,7 +6652,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, switch (fr->type) { case NGTCP2_FRAME_ACK: case NGTCP2_FRAME_ACK_ECN: - rv = conn_recv_ack(conn, pktns, &fr->ack, ts); + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); if (rv != 0) { return rv; } @@ -6672,6 +6747,13 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } non_probing_pkt = 1; break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + rv = conn_recv_handshake_done(conn); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; case NGTCP2_FRAME_DATA_BLOCKED: case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: @@ -6725,7 +6807,8 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_acktr_immediate_ack(&pktns->acktr); } - rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, ts); + rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, + pkt_ts); if (rv != 0) { return rv; } @@ -6757,7 +6840,7 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, for (ppc = &pktns->rx.buffed_pkts; *ppc;) { next = (*ppc)->next; nread = conn_recv_pkt(conn, &(*ppc)->path.path, (*ppc)->pkt, (*ppc)->pktlen, - ts); + (*ppc)->ts, ts); if (nread < 0 && !ngtcp2_err_is_fatal((int)nread)) { /* TODO We don't know this is the first QUIC packet in a datagram. */ @@ -6792,7 +6875,7 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, */ static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_pktns *pktns = &conn->in_pktns; + ngtcp2_pktns *pktns = conn->hs_pktns; ngtcp2_ssize nread; ngtcp2_pkt_chain **ppc, *next; @@ -6802,7 +6885,7 @@ static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn, for (ppc = &pktns->rx.buffed_pkts; *ppc;) { next = (*ppc)->next; nread = conn_recv_handshake_pkt(conn, &(*ppc)->path.path, (*ppc)->pkt, - (*ppc)->pktlen, ts); + (*ppc)->pktlen, (*ppc)->ts, ts); ngtcp2_pkt_chain_del(*ppc, conn->mem); *ppc = next; if (nread < 0) { @@ -6878,7 +6961,7 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, size_t origpktlen = pktlen; while (pktlen) { - nread = conn_recv_pkt(conn, path, pkt, pktlen, ts); + nread = conn_recv_pkt(conn, path, pkt, pktlen, ts, ts); if (nread < 0) { if (ngtcp2_err_is_fatal((int)nread)) { return (int)nread; @@ -6982,8 +7065,11 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, * exhausted in at least one of packet number space. */ static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) { - return conn->in_pktns.tx.last_pkt_num == NGTCP2_MAX_PKT_NUM || - conn->hs_pktns.tx.last_pkt_num == NGTCP2_MAX_PKT_NUM || + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + + return (in_pktns && in_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) || + (hs_pktns && hs_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) || conn->pktns.tx.last_pkt_num == NGTCP2_MAX_PKT_NUM; } @@ -7001,11 +7087,33 @@ static size_t conn_server_hs_tx_left(ngtcp2_conn *conn) { return conn->hs_recved * 3 - conn->hs_sent; } +/* + * conn_enqueue_handshake_done enqueues HANDSHAKE_DONE frame for + * transmission. + */ +static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_frame_chain *nfrc; + int rv; + + assert(conn->server); + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_HANDSHAKE_DONE; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + + return 0; +} + int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) { int rv; - ngtcp2_pktns *hs_pktns = &conn->hs_pktns; switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: @@ -7022,13 +7130,15 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, return 0; } - if (hs_pktns->crypto.rx.ckm) { - conn_discard_initial_key(conn); + assert(conn->hs_pktns); + if (conn->hs_pktns->crypto.rx.ckm && conn->in_pktns) { rv = conn_process_buffered_handshake_pkt(conn, ts); if (rv != 0) { return rv; } + + conn_discard_initial_state(conn); } return 0; @@ -7045,15 +7155,15 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, * packet buffered, perform address validation in order to buffer * validated data only. */ - if (ngtcp2_rob_first_gap_offset(&conn->in_pktns.crypto.strm.rx.rob) == 0) { - if (ngtcp2_rob_data_buffered(&conn->in_pktns.crypto.strm.rx.rob)) { + if (ngtcp2_rob_first_gap_offset(&conn->in_pktns->crypto.strm.rx.rob) == 0) { + if (ngtcp2_rob_data_buffered(&conn->in_pktns->crypto.strm.rx.rob)) { /* Address has been validated with token */ if (conn->local.settings.token.len) { return 0; } return NGTCP2_ERR_RETRY; } - if (conn->in_pktns.rx.buffed_pkts) { + if (conn->in_pktns->rx.buffed_pkts) { /* 0RTT is buffered, force retry */ return NGTCP2_ERR_RETRY; } @@ -7062,10 +7172,12 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_PROTO; } - /* Process re-ordered 0-RTT packets which were arrived before - Initial packet. */ + /* Process re-ordered 0-RTT packets which arrived before Initial + packet. */ if (conn->early.ckm) { - rv = conn_process_buffered_protected_pkt(conn, &conn->in_pktns, ts); + assert(conn->in_pktns); + + rv = conn_process_buffered_protected_pkt(conn, conn->in_pktns, ts); if (rv != 0) { return rv; } @@ -7078,24 +7190,15 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, return rv; } - /* Process re-ordered 0-RTT packets which were arrived before - Initial packet. */ - if (conn->early.ckm) { - rv = conn_process_buffered_protected_pkt(conn, &conn->in_pktns, ts); - if (rv != 0) { - return rv; - } - } - - if (hs_pktns->crypto.rx.ckm) { + if (conn->hs_pktns->crypto.rx.ckm) { rv = conn_process_buffered_handshake_pkt(conn, ts); if (rv != 0) { return rv; } } - if (conn->hs_pktns.rx.max_pkt_num != -1) { - conn_discard_initial_key(conn); + if (conn->hs_pktns->rx.max_pkt_num != -1) { + conn_discard_initial_state(conn); } if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { @@ -7106,6 +7209,8 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; } + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; + rv = conn_handshake_completed(conn); if (rv != 0) { return rv; @@ -7117,12 +7222,17 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, return rv; } - rv = conn_process_buffered_protected_pkt(conn, &conn->hs_pktns, ts); + rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); if (rv != 0) { return rv; } - conn->hs_pktns.acktr.flags |= NGTCP2_ACKTR_FLAG_PENDING_FINISHED_ACK; + conn_discard_handshake_state(conn); + + rv = conn_enqueue_handshake_done(conn); + if (rv != 0) { + return rv; + } return 0; case NGTCP2_CS_CLOSING: @@ -7134,66 +7244,6 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, } } -/* - * conn_select_preferred_addr asks a client application to select a - * server address from preferred addresses received from server. If a - * client chooses the address, path validation will start. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - * NGTCP2_ERR_CALLBACK_FAILURE - * User-defined callback function failed. - */ -static int conn_select_preferred_addr(ngtcp2_conn *conn) { - uint8_t buf[128]; - ngtcp2_addr addr; - int rv; - ngtcp2_duration timeout; - ngtcp2_pv *pv; - ngtcp2_dcid *dcid; - - ngtcp2_addr_init(&addr, buf, 0, NULL); - - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { - return 0; - } - - rv = conn_call_select_preferred_addr(conn, &addr); - if (rv != 0) { - return rv; - } - - if (addr.addrlen == 0 || - ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) { - return 0; - } - - assert(conn->pv == NULL); - - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - timeout = conn_compute_pto(conn); - timeout = - ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_INITIAL_RTT)); - - rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH, - &conn->log, conn->mem); - if (rv != 0) { - /* TODO Call ngtcp2_dcid_free here if it is introduced */ - return rv; - } - - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); - conn->pv = pv; - - ngtcp2_addr_copy(&pv->dcid.ps.path.local, &conn->dcid.current.ps.path.local); - ngtcp2_addr_copy(&pv->dcid.ps.path.remote, &addr); - - return conn_call_activate_dcid(conn, &pv->dcid); -} - /* * conn_retransmit_retry_early retransmits 0RTT packet after Retry is * received from server. @@ -7277,7 +7327,8 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, return nwrite + early_spktlen; case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: - if (conn->rcs.probe_pkt_left) { + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); if (nwrite) { return nwrite; @@ -7338,20 +7389,15 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, conn_process_early_rtb(conn); - rv = conn_process_buffered_protected_pkt(conn, &conn->hs_pktns, ts); + rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); if (rv != 0) { return (ngtcp2_ssize)rv; } if (conn->remote.transport_params.preferred_address_present) { - /* TODO Starting path validation against preferred address must - be done after dropping Handshake key which is impossible at - draft-18. */ - /* TODO And client has to send NEW_CONNECTION_ID before starting - path validation */ rv = conn_select_preferred_addr(conn); if (rv != 0) { - return (ngtcp2_ssize)rv; + return rv; } } @@ -7383,7 +7429,8 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, destlen = ngtcp2_min(destlen, server_hs_tx_left); origlen = ngtcp2_min(origlen, server_hs_tx_left); - if (conn->rcs.probe_pkt_left) { + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); if (nwrite < 0) { return nwrite; @@ -7621,9 +7668,11 @@ int ngtcp2_conn_install_initial_key(ngtcp2_conn *conn, const uint8_t *rx_key, const uint8_t *tx_key, const uint8_t *tx_iv, const uint8_t *tx_hp_key, size_t keylen, size_t ivlen) { - ngtcp2_pktns *pktns = &conn->in_pktns; + ngtcp2_pktns *pktns = conn->in_pktns; int rv; + assert(pktns); + if (pktns->crypto.rx.hp_key) { ngtcp2_vec_del(pktns->crypto.rx.hp_key, conn->mem); pktns->crypto.rx.hp_key = NULL; @@ -7665,9 +7714,10 @@ int ngtcp2_conn_install_handshake_key( ngtcp2_conn *conn, const uint8_t *rx_key, const uint8_t *rx_iv, const uint8_t *rx_hp_key, const uint8_t *tx_key, const uint8_t *tx_iv, const uint8_t *tx_hp_key, size_t keylen, size_t ivlen) { - ngtcp2_pktns *pktns = &conn->hs_pktns; + ngtcp2_pktns *pktns = conn->hs_pktns; int rv; + assert(pktns); assert(!pktns->crypto.rx.hp_key); assert(!pktns->crypto.rx.ckm); assert(!pktns->crypto.tx.hp_key); @@ -7769,7 +7819,8 @@ int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { assert(conn->state == NGTCP2_CS_POST_HANDSHAKE); - if ((conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) || + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || !conn->crypto.key_update.new_tx_ckm || !conn->crypto.key_update.new_rx_ckm || (confirmed_ts != UINT64_MAX && confirmed_ts + 3 * pto > ts)) { @@ -7856,8 +7907,12 @@ static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr, void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - acktr_cancel_expired_ack_delay_timer(&conn->in_pktns.acktr, ts); - acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns.acktr, ts); + if (conn->in_pktns) { + acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, ts); + } + if (conn->hs_pktns) { + acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, ts); + } acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts); } @@ -7896,6 +7951,13 @@ int ngtcp2_conn_set_remote_transport_params( assert(!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)); + /* Assume that ngtcp2_decode_transport_params sets default value if + active_connection_id_limit is omitted. */ + if (params->active_connection_id_limit < + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + if (!conn->server) { rv = conn_client_validate_transport_params(conn, params); if (rv != 0) { @@ -8179,7 +8241,8 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, if (res == 0) { if (conn_handshake_remnants_left(conn)) { - if (conn->rcs.probe_pkt_left) { + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); if (nwrite) { return nwrite; @@ -8196,25 +8259,9 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, } } } - if (res == 0) { - nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); - if (nwrite < 0) { - return nwrite; - } - if (nwrite > 0) { - res = nwrite; - dest += nwrite; - if (destlen < (size_t)nwrite) { - destlen = 0; - } else { - destlen -= (size_t)nwrite; - } - origlen -= (size_t)nwrite; - } - } } - if (conn->rcs.probe_pkt_left) { + if (conn->pktns.rtb.probe_pkt_left) { nwrite = conn_write_probe_pkt(conn, dest, origlen, pdatalen, strm, fin, datav, datavcnt, ts); goto fin; @@ -8251,9 +8298,13 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, uint64_t error_code, ngtcp2_tstamp ts) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_ssize res = 0, nwrite; ngtcp2_frame fr; uint8_t pkt_type; + int bundle = 0; + uint8_t bundle_pkt_type; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -8282,9 +8333,9 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { pkt_type = NGTCP2_PKT_SHORT; - } else if (conn->hs_pktns.crypto.tx.ckm) { + } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { pkt_type = NGTCP2_PKT_HANDSHAKE; - } else if (conn->in_pktns.crypto.tx.ckm) { + } else if (in_pktns && in_pktns->crypto.tx.ckm) { pkt_type = NGTCP2_PKT_INITIAL; } else { /* This branch is taken if server has not read any Initial packet @@ -8292,10 +8343,18 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, return NGTCP2_ERR_INVALID_STATE; } - if (conn->server && pkt_type == NGTCP2_PKT_HANDSHAKE && - conn->in_pktns.crypto.tx.ckm) { + if (conn->server && pkt_type == NGTCP2_PKT_HANDSHAKE && in_pktns) { + bundle = 1; + bundle_pkt_type = NGTCP2_PKT_INITIAL; + } else if (pkt_type == NGTCP2_PKT_SHORT && hs_pktns && + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + bundle = 1; + bundle_pkt_type = NGTCP2_PKT_HANDSHAKE; + } + + if (bundle) { nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, dest, destlen, NGTCP2_PKT_INITIAL, &conn->dcid.current.cid, &fr, + conn, dest, destlen, bundle_pkt_type, &conn->dcid.current.cid, &fr, NGTCP2_RTB_FLAG_NONE, ts); if (nwrite < 0) { return nwrite; @@ -8555,7 +8614,9 @@ static int conn_extend_max_stream_offset(ngtcp2_conn *conn, ngtcp2_strm *strm, size_t datalen) { ngtcp2_strm *top; - if (strm->rx.unsent_max_offset <= NGTCP2_MAX_VARINT - datalen) { + if (strm->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { + strm->rx.unsent_max_offset = NGTCP2_MAX_VARINT; + } else { strm->rx.unsent_max_offset += datalen; } @@ -8678,34 +8739,76 @@ const ngtcp2_cc_stat *ngtcp2_conn_get_cc_stat(ngtcp2_conn *conn) { } static ngtcp2_pktns *conn_get_earliest_loss_time_pktns(ngtcp2_conn *conn) { - ngtcp2_pktns *in_pktns = &conn->in_pktns; - ngtcp2_pktns *hs_pktns = &conn->hs_pktns; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_pktns *pktns = &conn->pktns; ngtcp2_pktns *res = in_pktns; - if (res->rtb.loss_time == 0 || - (hs_pktns->rtb.loss_time && - hs_pktns->rtb.loss_time < res->rtb.loss_time)) { + if (hs_pktns && hs_pktns->rtb.loss_time != UINT64_MAX && + (res == NULL || hs_pktns->rtb.loss_time < res->rtb.loss_time)) { res = hs_pktns; } - if (res->rtb.loss_time == 0 || - (pktns->rtb.loss_time && pktns->rtb.loss_time < res->rtb.loss_time)) { + if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + pktns->rtb.loss_time != UINT64_MAX && + (res == NULL || pktns->rtb.loss_time < res->rtb.loss_time)) { res = pktns; } return res; } -void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn) { +static ngtcp2_tstamp conn_get_earliest_last_tx_pkt_ts(ngtcp2_conn *conn) { + ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + ngtcp2_tstamp earliest_ts = rcs->last_tx_pkt_ts[NGTCP2_CRYPTO_LEVEL_INITIAL]; + ngtcp2_pktns *pktns = ns[0]; + size_t i; + + for (i = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; i <= NGTCP2_CRYPTO_LEVEL_APP; ++i) { + if (rcs->last_tx_pkt_ts[i] != UINT64_MAX && + (!pktns || rcs->last_tx_pkt_ts[i] < earliest_ts) && + (i != NGTCP2_CRYPTO_LEVEL_APP || + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED))) { + earliest_ts = rcs->last_tx_pkt_ts[i]; + pktns = ns[i]; + } + } + + return earliest_ts; +} + +static ngtcp2_pktns * +conn_get_earliest_non_null_last_tx_pktns(ngtcp2_conn *conn) { + ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; + ngtcp2_pktns *res = ns[0]; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + size_t i, earliest_ts = rcs->last_tx_pkt_ts[NGTCP2_CRYPTO_LEVEL_INITIAL]; + + for (i = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; i <= NGTCP2_CRYPTO_LEVEL_APP; ++i) { + if (res == NULL || + (rcs->last_tx_pkt_ts[i] != UINT64_MAX && + (earliest_ts == UINT64_MAX || rcs->last_tx_pkt_ts[i] < earliest_ts) && + (i != NGTCP2_CRYPTO_LEVEL_APP || + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)))) { + earliest_ts = rcs->last_tx_pkt_ts[i]; + res = ns[i]; + } + } + + return res; +} + +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_rcvry_stat *rcs = &conn->rcs; ngtcp2_duration timeout; - ngtcp2_pktns *in_pktns = &conn->in_pktns; - ngtcp2_pktns *hs_pktns = &conn->hs_pktns; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_pktns *loss_pktns = conn_get_earliest_loss_time_pktns(conn); + ngtcp2_pktns *earliest_pktns = conn_get_earliest_loss_time_pktns(conn); + ngtcp2_tstamp last_tx_pkt_ts; - if (loss_pktns->rtb.loss_time) { - rcs->loss_detection_timer = loss_pktns->rtb.loss_time; + if (earliest_pktns && earliest_pktns->rtb.loss_time != UINT64_MAX) { + rcs->loss_detection_timer = earliest_pktns->rtb.loss_time; ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "loss_detection_timer=%" PRIu64 " nonzero crypto loss time", @@ -8713,8 +8816,8 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn) { return; } - if (ngtcp2_rtb_num_ack_eliciting(&in_pktns->rtb) == 0 && - ngtcp2_rtb_num_ack_eliciting(&hs_pktns->rtb) == 0 && + if ((!in_pktns || ngtcp2_rtb_num_ack_eliciting(&in_pktns->rtb) == 0) && + (!hs_pktns || ngtcp2_rtb_num_ack_eliciting(&hs_pktns->rtb) == 0) && ngtcp2_rtb_num_ack_eliciting(&pktns->rtb) == 0 && (conn->server || (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) { if (rcs->loss_detection_timer) { @@ -8732,12 +8835,18 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn) { } timeout *= 1ULL << rcs->pto_count; - rcs->loss_detection_timer = rcs->last_tx_pkt_ts + timeout; + last_tx_pkt_ts = conn_get_earliest_last_tx_pkt_ts(conn); + + if (last_tx_pkt_ts == UINT64_MAX) { + last_tx_pkt_ts = ts; + } + + rcs->loss_detection_timer = last_tx_pkt_ts + timeout; ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "loss_detection_timer=%" PRIu64 " last_tx_pkt_ts=%" PRIu64 " timeout=%" PRIu64, - rcs->loss_detection_timer, rcs->last_tx_pkt_ts, + rcs->loss_detection_timer, last_tx_pkt_ts, (uint64_t)(timeout / NGTCP2_MILLISECONDS)); } @@ -8774,9 +8883,10 @@ static int conn_on_crypto_timeout(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_rcvry_stat *rcs = &conn->rcs; int rv; - ngtcp2_pktns *in_pktns = &conn->in_pktns; - ngtcp2_pktns *hs_pktns = &conn->hs_pktns; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_pktns *loss_pktns = conn_get_earliest_loss_time_pktns(conn); + ngtcp2_pktns *earliest_pktns; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -8797,36 +8907,54 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "loss detection timer fired"); - if (loss_pktns->rtb.loss_time) { + if (loss_pktns && loss_pktns->rtb.loss_time != UINT64_MAX) { rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, rcs, ts); if (rv != 0) { return rv; } - ngtcp2_conn_set_loss_detection_timer(conn); + ngtcp2_conn_set_loss_detection_timer(conn, ts); return 0; } - if (ngtcp2_rtb_num_ack_eliciting(&in_pktns->rtb) || - ngtcp2_rtb_num_ack_eliciting(&hs_pktns->rtb)) { - /* TODO close connection if we have too many backlog in rtb. */ - - rv = conn_on_crypto_timeout(conn, in_pktns); - if (rv != 0) { - return rv; - } - rv = conn_on_crypto_timeout(conn, hs_pktns); - if (rv != 0) { - return rv; + if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (hs_pktns->crypto.tx.ckm) { + hs_pktns->rtb.probe_pkt_left = 1; + } else { + conn_on_crypto_timeout(conn, in_pktns); + in_pktns->rtb.probe_pkt_left = 1; } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "handshake packet is still in-flight"); - rcs->probe_pkt_left = 1; - } else if (!conn->server && !conn->pktns.crypto.tx.ckm) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "send anti-deadlock packet"); - rcs->probe_pkt_left = 1; } else { - rcs->probe_pkt_left = 2; + earliest_pktns = conn_get_earliest_non_null_last_tx_pktns(conn); + + assert(earliest_pktns); + + switch (earliest_pktns->rtb.crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + assert(in_pktns); + rv = conn_on_crypto_timeout(conn, in_pktns); + if (rv != 0) { + return rv; + } + in_pktns->rtb.probe_pkt_left = 1; + if (!conn->server) { + break; + } + /* fall through for server so that it can coalesce packets. */ + /* fall through */ + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + assert(hs_pktns); + rv = conn_on_crypto_timeout(conn, hs_pktns); + if (rv != 0) { + return rv; + } + hs_pktns->rtb.probe_pkt_left = 1; + break; + case NGTCP2_CRYPTO_LEVEL_APP: + conn->pktns.rtb.probe_pkt_left = 2; + break; + default: + assert(0); + } } ++rcs->pto_count; @@ -8834,7 +8962,7 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "pto_count=%zu", rcs->pto_count); - ngtcp2_conn_set_loss_detection_timer(conn); + ngtcp2_conn_set_loss_detection_timer(conn, ts); return 0; } @@ -8854,10 +8982,12 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, switch (crypto_level) { case NGTCP2_CRYPTO_LEVEL_INITIAL: - pktns = &conn->in_pktns; + assert(conn->in_pktns); + pktns = conn->in_pktns; break; case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: - pktns = &conn->hs_pktns; + assert(conn->hs_pktns); + pktns = conn->hs_pktns; break; case NGTCP2_CRYPTO_LEVEL_APP: pktns = &conn->pktns; @@ -9019,7 +9149,8 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, conn->log.last_ts = ts; conn->qlog.last_ts = ts; - if (conn->remote.transport_params.disable_active_migration) { + if (conn->remote.transport_params.disable_active_migration || + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { return NGTCP2_ERR_INVALID_STATE; } if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { @@ -9066,8 +9197,19 @@ uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { ngtcp2_duration trpto; - ngtcp2_duration idle_timeout = - conn->local.settings.transport_params.idle_timeout; + ngtcp2_duration idle_timeout; + + /* TODO Remote max_idle_timeout becomes effective after handshake + completion. */ + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || + (conn->local.settings.transport_params.max_idle_timeout && + conn->local.settings.transport_params.max_idle_timeout < + conn->remote.transport_params.max_idle_timeout)) { + idle_timeout = conn->local.settings.transport_params.max_idle_timeout; + } else { + idle_timeout = conn->remote.transport_params.max_idle_timeout; + } if (idle_timeout == 0) { return UINT64_MAX; @@ -9084,16 +9226,24 @@ ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, const ngtcp2_crypto_ctx *ctx) { - conn->in_pktns.crypto.ctx = *ctx; + assert(conn->in_pktns); + conn->in_pktns->crypto.ctx = *ctx; } const ngtcp2_crypto_ctx *ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn) { - return &conn->in_pktns.crypto.ctx; + assert(conn->in_pktns); + return &conn->in_pktns->crypto.ctx; +} + +void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, + const ngtcp2_crypto_aead *aead) { + conn->crypto.retry_aead = *aead; } void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, const ngtcp2_crypto_ctx *ctx) { - conn->hs_pktns.crypto.ctx = *ctx; + assert(conn->hs_pktns); + conn->hs_pktns->crypto.ctx = *ctx; conn->pktns.crypto.ctx = *ctx; } @@ -9118,3 +9268,65 @@ void ngtcp2_settings_default(ngtcp2_settings *settings) { NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; settings->transport_params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; } + +/* The functions prefixed with ngtcp2_pkt_ are usually put inside + ngtcp2_pkt.c. This function uses encryption construct and uses + test data defined only in ngtcp2_conn_test.c, so it is written + here. */ +ngtcp2_ssize ngtcp2_pkt_write_connection_close( + uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead, const uint8_t *key, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key) { + ngtcp2_pkt_hd hd; + ngtcp2_crypto_km ckm; + ngtcp2_crypto_cc cc; + ngtcp2_vec hp_key_vec; + ngtcp2_ppe ppe; + ngtcp2_frame fr = {0}; + int rv; + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid, + scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, + NGTCP2_PROTO_VER, /* len = */ 0); + + ngtcp2_vec_init(&ckm.secret, NULL, 0); + ngtcp2_vec_init(&ckm.key, key, 16); + ngtcp2_vec_init(&ckm.iv, iv, 12); + ngtcp2_vec_init(&hp_key_vec, hp_key, 16); + ckm.pkt_num = 0; + ckm.flags = NGTCP2_CRYPTO_KM_FLAG_NONE; + + cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; + cc.aead = *aead; + cc.hp = *hp; + cc.ckm = &ckm; + cc.hp_key = &hp_key_vec; + cc.encrypt = encrypt; + cc.hp_mask = hp_mask; + cc.user_data = NULL; + + ngtcp2_ppe_init(&ppe, dest, destlen, &cc); + + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return rv; + } + + if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { + return NGTCP2_ERR_NOBUF; + } + + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; + fr.connection_close.error_code = error_code; + + rv = ngtcp2_ppe_encode_frame(&ppe, &fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return rv; + } + + return ngtcp2_ppe_final(&ppe, NULL); +} diff --git a/deps/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/lib/ngtcp2_conn.h index 213e8a9e85..ba3a61a8eb 100644 --- a/deps/ngtcp2/lib/ngtcp2_conn.h +++ b/deps/ngtcp2/lib/ngtcp2_conn.h @@ -98,12 +98,16 @@ typedef enum { kept to catch in-flight packet on retired path. */ #define NGTCP2_MAX_DCID_RETIRED_SIZE 2 /* NGTCP2_MAX_SCID_POOL_SIZE is the maximum number of source - connection ID the local endpoint provides in NEW_CONNECTION_ID to - the remote endpoint. The chosen value was described in old draft. - Now a remote endpoint tells the maximum value. The value can be - quite large, and we have to put the sane limit.*/ + connection ID the local endpoint provides to the remote endpoint. + The chosen value was described in old draft. Now a remote endpoint + tells the maximum value. The value can be quite large, and we have + to put the sane limit.*/ #define NGTCP2_MAX_SCID_POOL_SIZE 8 +/* NGTCP2_MAX_NON_ACK_TX_PKT is the maximum number of continuous non + ACK-eliciting packets. */ +#define NGTCP2_MAX_NON_ACK_TX_PKT 10 + /* * ngtcp2_max_frame is defined so that it covers the largest ACK * frame. @@ -150,12 +154,12 @@ typedef enum { /* NGTCP2_CONN_FLAG_SADDR_VERIFIED is set when source address is verified. */ NGTCP2_CONN_FLAG_SADDR_VERIFIED = 0x40, + /* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint + confirmed completion of handshake. */ + NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED = 0x80, /* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the library transitions its state to "post handshake". */ NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED = 0x0100, - /* NGTCP2_CONN_FLAG_INITIAL_KEY_DISCARDED is set when Initial keys - have been discarded. */ - NGTCP2_CONN_FLAG_INITIAL_KEY_DISCARDED = 0x0400, /* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update is not confirmed by the local endpoint. That is, it has not received ACK frame which acknowledges packet which is encrypted @@ -188,6 +192,9 @@ typedef struct { last time.*/ int64_t last_pkt_num; ngtcp2_frame_chain *frq; + /* num_non_ack_pkt is the number of continuous non ACK-eliciting + packets. */ + size_t num_non_ack_pkt; } tx; struct { @@ -210,10 +217,10 @@ typedef struct { * - 0-RTT packet is only buffered in server Initial encryption * level ngtcp2_pktns. * - * - Handshake packet is only buffered in client Initial encryption - * level ngtcp2_pktns. + * - Handshake packet is only buffered in client Handshake + * encryption level ngtcp2_pktns. * - * - Short packet is only buffered in Handshake encryption level + * - Short packet is only buffered in Short encryption level * ngtcp2_pktns. */ ngtcp2_pkt_chain *buffed_pkts; @@ -265,8 +272,8 @@ struct ngtcp2_conn { during handshake. It is used to receive late handshake packets after handshake completion. */ ngtcp2_cid odcid; - ngtcp2_pktns in_pktns; - ngtcp2_pktns hs_pktns; + ngtcp2_pktns *in_pktns; + ngtcp2_pktns *hs_pktns; ngtcp2_pktns pktns; struct { @@ -295,11 +302,6 @@ struct ngtcp2_conn { ngtcp2_pq used; /* last_seq is the last sequence number of connection ID. */ uint64_t last_seq; - /* num_initial_id is the number of Connection ID initially offered - to the remote endpoint and is not retired yet. It includes the - initial Connection ID used during handshake and the one in - preferred_address transport parameter. */ - size_t num_initial_id; /* num_retired is the number of retired Connection ID still included in set. */ size_t num_retired; @@ -418,6 +420,8 @@ struct ngtcp2_conn { size_t aead_overhead; /* decrypt_buf is a buffer which is used to write decrypted data. */ ngtcp2_vec decrypt_buf; + /* retry_aead is AEAD to verify Retry packet integrity. */ + ngtcp2_crypto_aead retry_aead; } crypto; /* pkt contains the packet intermediate construction data to support @@ -635,7 +639,7 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, ngtcp2_duration ack_delay); -void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn); +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); /* * ngtcp2_conn_detect_lost_pkt detects lost packets. diff --git a/deps/ngtcp2/lib/ngtcp2_crypto.c b/deps/ngtcp2/lib/ngtcp2_crypto.c index f1cb075a0e..73132f8893 100644 --- a/deps/ngtcp2/lib/ngtcp2_crypto.c +++ b/deps/ngtcp2/lib/ngtcp2_crypto.c @@ -182,8 +182,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { len += varint_paramlen(params->max_ack_delay / NGTCP2_MILLISECONDS); } - if (params->idle_timeout) { - len += varint_paramlen(params->idle_timeout / NGTCP2_MILLISECONDS); + if (params->max_idle_timeout) { + len += varint_paramlen(params->max_idle_timeout / NGTCP2_MILLISECONDS); } if (params->active_connection_id_limit) { len += varint_paramlen(params->active_connection_id_limit); @@ -286,9 +286,9 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, params->max_ack_delay / NGTCP2_MILLISECONDS); } - if (params->idle_timeout) { - p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_IDLE_TIMEOUT, - params->idle_timeout / NGTCP2_MILLISECONDS); + if (params->max_idle_timeout) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, + params->max_idle_timeout / NGTCP2_MILLISECONDS); } if (params->active_connection_id_limit) { @@ -372,8 +372,9 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, memset(¶ms->preferred_address, 0, sizeof(params->preferred_address)); params->disable_active_migration = 0; params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; - params->idle_timeout = 0; - params->active_connection_id_limit = 0; + params->max_idle_timeout = 0; + params->active_connection_id_limit = + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; params->original_connection_id_present = 0; memset(scb, 0, sizeof(scb)); @@ -440,12 +441,12 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, } p += nread; break; - case NGTCP2_TRANSPORT_PARAM_IDLE_TIMEOUT: - nread = decode_varint(¶ms->idle_timeout, p, end); + case NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT: + nread = decode_varint(¶ms->max_idle_timeout, p, end); if (nread < 0) { return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; } - params->idle_timeout *= NGTCP2_MILLISECONDS; + params->max_idle_timeout *= NGTCP2_MILLISECONDS; p += nread; break; case NGTCP2_TRANSPORT_PARAM_MAX_PACKET_SIZE: diff --git a/deps/ngtcp2/lib/ngtcp2_err.c b/deps/ngtcp2/lib/ngtcp2_err.c index febc4425f7..10b6ef81d5 100644 --- a/deps/ngtcp2/lib/ngtcp2_err.c +++ b/deps/ngtcp2/lib/ngtcp2_err.c @@ -48,6 +48,8 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_STREAM_DATA_BLOCKED"; case NGTCP2_ERR_FLOW_CONTROL: return "ERR_FLOW_CONTROL"; + case NGTCP2_ERR_CONNECTION_ID_LIMIT: + return "ERR_CONNECTION_ID_LIMIT"; case NGTCP2_ERR_STREAM_LIMIT: return "ERR_STREAM_LIMIT"; case NGTCP2_ERR_FINAL_SIZE: @@ -112,6 +114,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { return NGTCP2_FRAME_ENCODING_ERROR; case NGTCP2_ERR_FLOW_CONTROL: return NGTCP2_FLOW_CONTROL_ERROR; + case NGTCP2_ERR_CONNECTION_ID_LIMIT: + return NGTCP2_CONNECTION_ID_LIMIT_ERROR; case NGTCP2_ERR_STREAM_LIMIT: return NGTCP2_STREAM_LIMIT_ERROR; case NGTCP2_ERR_FINAL_SIZE: diff --git a/deps/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/lib/ngtcp2_log.c index 3f78a0f900..11f335d974 100644 --- a/deps/ngtcp2/lib/ngtcp2_log.c +++ b/deps/ngtcp2/lib/ngtcp2_log.c @@ -129,8 +129,12 @@ static const char *strerrorcode(uint64_t error_code) { return "FRAME_ENCODING_ERROR"; case NGTCP2_TRANSPORT_PARAMETER_ERROR: return "TRANSPORT_PARAMETER_ERROR"; + case NGTCP2_CONNECTION_ID_LIMIT_ERROR: + return "CONNECTION_ID_LIMIT_ERROR"; case NGTCP2_PROTOCOL_VIOLATION: return "PROTOCOL_VIOLATION"; + case NGTCP2_INVALID_TOKEN: + return "INVALID_TOKEN"; case NGTCP2_CRYPTO_BUFFER_EXCEEDED: return "CRYPTO_BUFFER_EXCEEDED"; case NGTCP2_KEY_UPDATE_ERROR: @@ -425,6 +429,13 @@ static void log_fr_retire_connection_id(ngtcp2_log *log, NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq); } +static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_handshake_done *fr, + const char *dir) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " HANDSHAKE_DONE(0x%02x)"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type); +} + static void log_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, const ngtcp2_frame *fr, const char *dir) { switch (fr->type) { @@ -489,6 +500,9 @@ static void log_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, case NGTCP2_FRAME_RETIRE_CONNECTION_ID: log_fr_retire_connection_id(log, hd, &fr->retire_connection_id, dir); break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + log_fr_handshake_done(log, hd, &fr->handshake_done, dir); + break; default: assert(0); } @@ -627,9 +641,9 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi); log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_uni_streams=%u"), NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni); - log->log_printf(log->user_data, (NGTCP2_LOG_TP " idle_timeout=%u"), + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%u"), NGTCP2_LOG_TP_HD_FIELDS, - params->idle_timeout / NGTCP2_MILLISECONDS); + params->max_idle_timeout / NGTCP2_MILLISECONDS); log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_packet_size=%u"), NGTCP2_LOG_TP_HD_FIELDS, params->max_packet_size); log->log_printf(log->user_data, (NGTCP2_LOG_TP " ack_delay_exponent=%u"), diff --git a/deps/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/lib/ngtcp2_pkt.c index 96a43a8e8b..4aca1b05ef 100644 --- a/deps/ngtcp2/lib/ngtcp2_pkt.c +++ b/deps/ngtcp2/lib/ngtcp2_pkt.c @@ -484,6 +484,9 @@ ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload, case NGTCP2_FRAME_RETIRE_CONNECTION_ID: return ngtcp2_pkt_decode_retire_connection_id_frame( &dest->retire_connection_id, payload, payloadlen); + case NGTCP2_FRAME_HANDSHAKE_DONE: + return ngtcp2_pkt_decode_handshake_done_frame(&dest->handshake_done, + payload, payloadlen); default: if (has_mask(type, NGTCP2_FRAME_STREAM)) { return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen); @@ -1349,6 +1352,16 @@ ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest, return (ngtcp2_ssize)len; } +ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, + const uint8_t *payload, + size_t payloadlen) { + (void)payload; + (void)payloadlen; + + dest->type = NGTCP2_FRAME_HANDSHAKE_DONE; + return 1; +} + ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, ngtcp2_frame *fr) { switch (fr->type) { @@ -1401,6 +1414,9 @@ ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, case NGTCP2_FRAME_RETIRE_CONNECTION_ID: return ngtcp2_pkt_encode_retire_connection_id_frame( out, outlen, &fr->retire_connection_id); + case NGTCP2_FRAME_HANDSHAKE_DONE: + return ngtcp2_pkt_encode_handshake_done_frame(out, outlen, + &fr->handshake_done); default: return NGTCP2_ERR_INVALID_ARGUMENT; } @@ -1869,6 +1885,20 @@ ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame( return (ngtcp2_ssize)len; } +ngtcp2_ssize +ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, + const ngtcp2_handshake_done *fr) { + (void)fr; + + if (outlen < 1) { + return NGTCP2_ERR_NOBUF; + } + + *out++ = NGTCP2_FRAME_HANDSHAKE_DONE; + + return 1; +} + ngtcp2_ssize ngtcp2_pkt_write_version_negotiation( uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid, size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv, @@ -1940,35 +1970,15 @@ int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload, size_t payloadlen) { - size_t len = 1; - const uint8_t *p = payload; - size_t odcil; - - if (payloadlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - odcil = p[0]; - if (odcil > NGTCP2_MAX_CIDLEN) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } + size_t len = /* token */ 1 + NGTCP2_RETRY_TAGLEN; - len += odcil; if (payloadlen < len) { return NGTCP2_ERR_INVALID_ARGUMENT; } - ++p; - - ngtcp2_cid_init(&dest->odcid, p, odcil); - p += odcil; - - dest->tokenlen = (size_t)(payload + payloadlen - p); - if (dest->tokenlen) { - dest->token = p; - } else { - dest->token = NULL; - } + dest->token = payload; + dest->tokenlen = (size_t)(payloadlen - NGTCP2_RETRY_TAGLEN); + ngtcp2_cpymem(dest->tag, payload + dest->tokenlen, NGTCP2_RETRY_TAGLEN); return 0; } @@ -1982,9 +1992,10 @@ int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num, int64_t cand = (expected & ~mask) | pkt_num; if (cand <= expected - hwin) { + assert(cand <= (int64_t)NGTCP2_MAX_VARINT - win); return cand + win; } - if (cand > expected + hwin && cand > win) { + if (cand > expected + hwin && cand >= win) { return cand - win; } return cand; @@ -2017,9 +2028,10 @@ int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr) { return 0; } -ngtcp2_ssize ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen, - uint8_t *stateless_reset_token, - uint8_t *rand, size_t randlen) { +ngtcp2_ssize +ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen, + const uint8_t *stateless_reset_token, + const uint8_t *rand, size_t randlen) { uint8_t *p; if (destlen < @@ -2042,16 +2054,26 @@ ngtcp2_ssize ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen, return p - dest; } -ngtcp2_ssize ngtcp2_pkt_write_retry(uint8_t *dest, size_t destlen, - const ngtcp2_pkt_hd *hd, - const ngtcp2_cid *odcid, - const uint8_t *token, size_t tokenlen) { +static const uint8_t retry_key[] = + "\x4d\x32\xec\xdb\x2a\x21\x33\xc8\x41\xe4\x04\x3d\xf2\x7d\x44\x30"; +static const uint8_t retry_nonce[] = + "\x4d\x16\x11\xd0\x55\x13\xa5\x52\xc5\x87\xd5\x75"; + +ngtcp2_ssize +ngtcp2_pkt_write_retry(uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, + const uint8_t *token, size_t tokenlen, + ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead) { + ngtcp2_pkt_hd hd; + uint8_t pseudo_retry[1500]; + ngtcp2_ssize pseudo_retrylen; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; + int rv; uint8_t *p; - ngtcp2_ssize nwrite; + size_t offset; - assert(hd->flags & NGTCP2_PKT_FLAG_LONG_FORM); - assert(hd->type == NGTCP2_PKT_RETRY); assert(tokenlen > 0); + assert(!ngtcp2_cid_eq(scid, odcid)); /* Retry packet is sent at most once per one connection attempt. In the first connection attempt, client has to send random DCID @@ -2060,30 +2082,107 @@ ngtcp2_ssize ngtcp2_pkt_write_retry(uint8_t *dest, size_t destlen, return NGTCP2_ERR_INVALID_ARGUMENT; } - nwrite = ngtcp2_pkt_encode_hd_long(dest, destlen, hd); - if (nwrite < 0) { - return nwrite; + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_RETRY, dcid, + scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, + NGTCP2_PROTO_VER, /* len = */ 0); + + pseudo_retrylen = + ngtcp2_pkt_encode_pseudo_retry(pseudo_retry, sizeof(pseudo_retry), &hd, + /* unused = */ 0, odcid, token, tokenlen); + if (pseudo_retrylen < 0) { + return pseudo_retrylen; } - if (destlen < - (size_t)nwrite + 1 /* ODCID Len */ + odcid->datalen + tokenlen) { + /* OpenSSL does not like NULL plaintext. */ + rv = encrypt(NULL, tag, aead, (const uint8_t *)"", 0, retry_key, retry_nonce, + sizeof(retry_nonce) - 1, pseudo_retry, (size_t)pseudo_retrylen, + NULL); + if (rv != 0) { + return rv; + } + + offset = 1 + odcid->datalen; + if (destlen < (size_t)pseudo_retrylen + sizeof(tag) - offset) { return NGTCP2_ERR_NOBUF; } - dest[0] &= 0xf0; + p = ngtcp2_cpymem(dest, pseudo_retry + offset, + (size_t)pseudo_retrylen - offset); + p = ngtcp2_cpymem(p, tag, sizeof(tag)); + + return p - dest; +} - p = dest + nwrite; +ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry( + uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused, + const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen) { + uint8_t *p = dest; + ngtcp2_ssize nwrite; + + if (destlen < 1 + odcid->datalen) { + return NGTCP2_ERR_NOBUF; + } *p++ = (uint8_t)odcid->datalen; - if (odcid->datalen) { - p = ngtcp2_cpymem(p, odcid->data, odcid->datalen); + p = ngtcp2_cpymem(p, odcid->data, odcid->datalen); + destlen -= (size_t)(p - dest); + + nwrite = ngtcp2_pkt_encode_hd_long(p, destlen, hd); + if (nwrite < 0) { + return nwrite; } + if (destlen < (size_t)nwrite + tokenlen) { + return NGTCP2_ERR_NOBUF; + } + + *p &= 0xf0; + *p |= unused; + + p += nwrite; + p = ngtcp2_cpymem(p, token, tokenlen); return p - dest; } +int ngtcp2_pkt_verify_retry_tag(const ngtcp2_pkt_retry *retry, + const uint8_t *pkt, size_t pktlen, + ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead) { + uint8_t pseudo_retry[1500]; + size_t pseudo_retrylen; + uint8_t *p = pseudo_retry; + int rv; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; + + assert(pktlen >= sizeof(retry->tag)); + + if (sizeof(pseudo_retry) < + 1 + retry->odcid.datalen + pktlen - sizeof(retry->tag)) { + return NGTCP2_ERR_PROTO; + } + + *p++ = (uint8_t)retry->odcid.datalen; + p = ngtcp2_cpymem(p, retry->odcid.data, retry->odcid.datalen); + p = ngtcp2_cpymem(p, pkt, pktlen - sizeof(retry->tag)); + + pseudo_retrylen = (size_t)(p - pseudo_retry); + + /* OpenSSL does not like NULL plaintext. */ + rv = encrypt(NULL, tag, aead, (const uint8_t *)"", 0, retry_key, retry_nonce, + sizeof(retry_nonce) - 1, pseudo_retry, pseudo_retrylen, NULL); + if (rv != 0) { + return rv; + } + + if (0 != memcmp(retry->tag, tag, sizeof(retry->tag))) { + return NGTCP2_ERR_PROTO; + } + + return 0; +} + size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, size_t len, size_t left) { size_t n = 1 /* type */ + ngtcp2_put_varint_len((uint64_t)stream_id) + diff --git a/deps/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/lib/ngtcp2_pkt.h index dfbe808eb5..962218057a 100644 --- a/deps/ngtcp2/lib/ngtcp2_pkt.h +++ b/deps/ngtcp2/lib/ngtcp2_pkt.h @@ -123,6 +123,7 @@ typedef enum { NGTCP2_FRAME_PATH_RESPONSE = 0x1b, NGTCP2_FRAME_CONNECTION_CLOSE = 0x1c, NGTCP2_FRAME_CONNECTION_CLOSE_APP = 0x1d, + NGTCP2_FRAME_HANDSHAKE_DONE = 0x1e, } ngtcp2_frame_type; typedef struct { @@ -271,6 +272,10 @@ typedef struct { uint64_t seq; } ngtcp2_retire_connection_id; +typedef struct { + uint8_t type; +} ngtcp2_handshake_done; + typedef union { uint8_t type; ngtcp2_stream stream; @@ -292,6 +297,7 @@ typedef union { ngtcp2_crypto crypto; ngtcp2_new_token new_token; ngtcp2_retire_connection_id retire_connection_id; + ngtcp2_handshake_done handshake_done; } ngtcp2_frame; struct ngtcp2_pkt_chain; @@ -427,7 +433,7 @@ int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, /* * ngtcp2_pkt_decode_retry decodes Retry packet payload |payload| of - * length |payloadlen|. The |payload| must start with ODCID Len + * length |payloadlen|. The |payload| must start with Retry token * field. * * This function returns 0 if it succeeds, or one of the following @@ -732,6 +738,22 @@ ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest, const uint8_t *payload, size_t payloadlen); +/* + * ngtcp2_pkt_decode_handshake_done_frame decodes HANDSHAKE_DONE frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. HANDSHAKE_DONE frame must start at + * payload[0]. This function finishes when it decodes one + * HANDSHAKE_DONE frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include HANDSHAKE_DONE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, + const uint8_t *payload, + size_t payloadlen); + /* * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the * buffer pointed by |out| of length |outlen|. @@ -997,6 +1019,20 @@ ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen, ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame( uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr); +/* + * ngtcp2_pkt_encode_handshake_done_frame encodes HANDSHAKE_DONE frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, + const ngtcp2_handshake_done *fr); + /* * ngtcp2_pkt_adjust_pkt_num find the full 64 bits packet number for * |pkt_num|, which is expected to be least significant |n| bits. The @@ -1040,12 +1076,43 @@ size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left); * ngtcp2_pkt_verify_reserved_bits verifies that the first byte |c| of * the packet header has the correct reserved bits. * - * This function returns 0 if it succeeds, or the following negative - * error codes: + * This function returns 0 if it succeeds, or one of the following + * negative error codes: * * NGTCP2_ERR_PROTO * Reserved bits has wrong value. */ int ngtcp2_pkt_verify_reserved_bits(uint8_t c); +/* + * ngtcp2_pkt_encode_pseudo_retry encodes Retry pseudo-packet in the + * buffer pointed by |dest| of length |destlen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_BUF + * Buffer is too short. + */ +ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry( + uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused, + const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen); + +/* + * ngtcp2_pkt_verify_retry_tag verifies Retry packet. The buffer + * pointed by |pkt| of length |pktlen| must contain Retry packet + * including packet header. The odcid and tag fields of |retry| must + * be specified. |aead| must be AEAD_AES_128_GCM. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Verification failed. + */ +int ngtcp2_pkt_verify_retry_tag(const ngtcp2_pkt_retry *retry, + const uint8_t *pkt, size_t pktlen, + ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead); + #endif /* NGTCP2_PKT_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/lib/ngtcp2_ppe.c index eaa1746759..b4a23c1e05 100644 --- a/deps/ngtcp2/lib/ngtcp2_ppe.c +++ b/deps/ngtcp2/lib/ngtcp2_ppe.c @@ -106,6 +106,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { ngtcp2_buf *buf = &ppe->buf; ngtcp2_crypto_cc *cc = ppe->cc; ngtcp2_conn *conn = cc->user_data; + void *conn_user_data = conn ? conn->user_data : NULL; uint8_t *payload = buf->begin + ppe->hdlen; size_t payloadlen = ngtcp2_buf_len(buf) - ppe->hdlen; uint8_t mask[NGTCP2_HP_SAMPLELEN]; @@ -127,7 +128,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { rv = cc->encrypt(conn, payload, &cc->aead, payload, payloadlen, cc->ckm->key.base, ppe->nonce, cc->ckm->iv.len, buf->begin, - ppe->hdlen, conn->user_data); + ppe->hdlen, conn_user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -138,7 +139,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { assert(ppe->sample_offset + NGTCP2_HP_SAMPLELEN <= ngtcp2_buf_len(buf)); rv = cc->hp_mask(conn, mask, &cc->hp, cc->hp_key->base, - buf->begin + ppe->sample_offset, conn->user_data); + buf->begin + ppe->sample_offset, conn_user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } diff --git a/deps/ngtcp2/lib/ngtcp2_qlog.c b/deps/ngtcp2/lib/ngtcp2_qlog.c index 881c6e2f42..2f6d2390c9 100644 --- a/deps/ngtcp2/lib/ngtcp2_qlog.c +++ b/deps/ngtcp2/lib/ngtcp2_qlog.c @@ -730,6 +730,24 @@ write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) { return p; } +static uint8_t *write_handshake_done_frame(uint8_t *p, + const ngtcp2_handshake_done *fr) { + ngtcp2_vec name, value; + (void)fr; + + /* + * {"frame_type":"handshake_done"} + */ +#define NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD 31 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "handshake_done")); + *p++ = '}'; + + return p; +} + static void qlog_pkt_write_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, int sent) { uint8_t *p; @@ -955,6 +973,14 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { } p = write_connection_close_frame(p, &fr->connection_close); break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_handshake_done_frame(p, &fr->handshake_done); + break; default: assert(0); } @@ -1020,8 +1046,8 @@ void ngtcp2_qlog_parameters_set_transport_params( p = write_pair_bool(p, ngtcp2_vec_lit(&name, "disable_active_migration"), params->disable_active_migration); *p++ = ','; - p = write_pair_duration(p, ngtcp2_vec_lit(&name, "idle_timeout"), - params->idle_timeout); + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "max_idle_timeout"), + params->max_idle_timeout); *p++ = ','; p = write_pair_number(p, ngtcp2_vec_lit(&name, "max_packet_size"), params->max_packet_size); diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/lib/ngtcp2_rtb.c index 9bf077f3c9..83936f2aa6 100644 --- a/deps/ngtcp2/lib/ngtcp2_rtb.c +++ b/deps/ngtcp2/lib/ngtcp2_rtb.c @@ -160,8 +160,10 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_crypto_level crypto_level, rtb->mem = mem; rtb->largest_acked_tx_pkt_num = -1; rtb->num_ack_eliciting = 0; - rtb->loss_time = 0; + rtb->loss_time = UINT64_MAX; + rtb->probe_pkt_left = 0; rtb->crypto_level = crypto_level; + rtb->cc_pkt_num = 0; } void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { @@ -183,6 +185,8 @@ void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent) { ngtcp2_rst_on_pkt_sent(rtb->rst, ent, rtb->cc->ccs); + assert(rtb->cc_pkt_num <= ent->hd.pkt_num); + rtb->cc->ccs->bytes_in_flight += ent->pktlen; ngtcp2_rst_update_app_limited(rtb->rst, rtb->cc->ccs); @@ -198,8 +202,10 @@ static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent) { --rtb->num_ack_eliciting; } - assert(rtb->cc->ccs->bytes_in_flight >= ent->pktlen); - rtb->cc->ccs->bytes_in_flight -= ent->pktlen; + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { + assert(rtb->cc->ccs->bytes_in_flight >= ent->pktlen); + rtb->cc->ccs->bytes_in_flight -= ent->pktlen; + } } static void rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, @@ -355,7 +361,8 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, } ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, - ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_conn *conn, ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts) { ngtcp2_rtb_entry *ent; int64_t largest_ack = fr->largest_ack, min_ack; size_t i; @@ -403,7 +410,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, if (!rtt_updated && largest_pkt_acked && (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { rtt_updated = 1; - ngtcp2_conn_update_rtt(conn, ts - largest_pkt_sent_ts, + ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, fr->ack_delay_unscaled); } rtb_on_pkt_acked(rtb, ent, ts); @@ -441,7 +448,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, if (!rtt_updated && largest_pkt_acked && (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { rtt_updated = 1; - ngtcp2_conn_update_rtt(conn, ts - largest_pkt_sent_ts, + ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, fr->ack_delay_unscaled); } rtb_on_pkt_acked(rtb, ent, ts); @@ -469,7 +476,7 @@ static int rtb_pkt_lost(ngtcp2_rtb *rtb, const ngtcp2_rtb_entry *ent, return 1; } - if (rtb->loss_time == 0) { + if (rtb->loss_time == UINT64_MAX) { rtb->loss_time = ent->ts + loss_delay; } else { rtb->loss_time = ngtcp2_min(rtb->loss_time, ent->ts + loss_delay); @@ -500,7 +507,7 @@ void ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, int64_t last_lost_pkt_num; ngtcp2_ksl_key key; - rtb->loss_time = 0; + rtb->loss_time = UINT64_MAX; loss_delay = compute_pkt_loss_delay(rcs); lost_send_time = ts - loss_delay; @@ -642,21 +649,20 @@ int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) { return ngtcp2_ksl_len(&rtb->ents) == 0; } -void ngtcp2_rtb_clear(ngtcp2_rtb *rtb) { +uint64_t ngtcp2_rtb_get_bytes_in_flight(ngtcp2_rtb *rtb) { + uint64_t bytes_in_flight = 0; ngtcp2_ksl_it it; ngtcp2_rtb_entry *ent; - it = ngtcp2_ksl_begin(&rtb->ents); - - for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + for (it = ngtcp2_ksl_begin(&rtb->ents); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { ent = ngtcp2_ksl_it_get(&it); - rtb->cc->ccs->bytes_in_flight -= ent->pktlen; - ngtcp2_rtb_entry_del(ent, rtb->mem); + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { + bytes_in_flight += ent->pktlen; + } } - ngtcp2_ksl_clear(&rtb->ents); - rtb->largest_acked_tx_pkt_num = -1; - rtb->num_ack_eliciting = 0; + return bytes_in_flight; } size_t ngtcp2_rtb_num_ack_eliciting(ngtcp2_rtb *rtb) { diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/lib/ngtcp2_rtb.h index ca0aacb263..0e11cea5d4 100644 --- a/deps/ngtcp2/lib/ngtcp2_rtb.h +++ b/deps/ngtcp2/lib/ngtcp2_rtb.h @@ -220,8 +220,13 @@ typedef struct { int64_t largest_acked_tx_pkt_num; size_t num_ack_eliciting; ngtcp2_tstamp loss_time; + /* probe_pkt_left is the number of probe packet to send */ + size_t probe_pkt_left; /* crypto_level is encryption level which |crypto| belongs to. */ ngtcp2_crypto_level crypto_level; + /* cc_pkt_num is the smallest packet number that is contributed to + bytes_in_flight. */ + int64_t cc_pkt_num; } ngtcp2_rtb; /* @@ -257,7 +262,10 @@ ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb); /* * ngtcp2_rtb_recv_ack removes acked ngtcp2_rtb_entry from |rtb|. - * |pkt_num| is a packet number which includes |fr|. + * |pkt_num| is a packet number which includes |fr|. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. * * This function returns the number of newly acknowledged packets if * it succeeds, or one of the following negative error codes: @@ -268,7 +276,8 @@ ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb); * Out of memory */ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, - ngtcp2_conn *conn, ngtcp2_tstamp ts); + ngtcp2_conn *conn, ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts); /* * ngtcp2_rtb_detect_lost_pkt detects lost packets and prepends the @@ -306,11 +315,10 @@ int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc); int ngtcp2_rtb_empty(ngtcp2_rtb *rtb); /* - * ngtcp2_rtb_clear removes all ngtcp2_rtb_entry objects. - * bytes_in_flight, largest_acked_tx_pkt_num, and num_ack_eliciting - * are also reset to their initial value. + * ngtcp2_rtb_get_bytes_in_flight returns the sum of bytes in flight + * for the stored entries. */ -void ngtcp2_rtb_clear(ngtcp2_rtb *rtb); +uint64_t ngtcp2_rtb_get_bytes_in_flight(ngtcp2_rtb *rtb); /* * ngtcp2_rtb_num_ack_eliciting returns the number of ACK eliciting