From 615bdd9da4202c60a61adfe72ef66e856482f6e7 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 9 Jan 2020 09:45:26 -0800 Subject: [PATCH] deps: update ngtcp2 --- deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h | 80 ++++- deps/ngtcp2/lib/includes/ngtcp2/version.h | 90 +++--- deps/ngtcp2/lib/ngtcp2_cid.c | 8 + deps/ngtcp2/lib/ngtcp2_cid.h | 6 + deps/ngtcp2/lib/ngtcp2_conn.c | 361 +++++++++++++++++++--- deps/ngtcp2/lib/ngtcp2_conn.h | 6 + deps/ngtcp2/lib/ngtcp2_str.c | 22 +- deps/ngtcp2/lib/ngtcp2_str.h | 7 + 8 files changed, 481 insertions(+), 99 deletions(-) diff --git a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h index c63f7b3d44..6b1ea084e5 100644 --- a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -345,8 +345,8 @@ typedef struct ngtcp2_vec { * * `ngtcp2_cid_init` initializes Connection ID |cid| with the byte * string pointed by |data| and its length is |datalen|. |datalen| - * must be at least :enum:`NGTCP2_MIN_CDLEN`, and at most - * :enum:`NGTCP2_MAX_CDLEN`. + * must be at least :enum:`NGTCP2_MIN_CIDLEN`, and at most + * :enum:`NGTCP2_MAX_CIDLEN`. */ NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen); @@ -1403,6 +1403,37 @@ typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn, const ngtcp2_preferred_addr *paddr, void *user_data); +typedef enum ngtcp2_connection_id_status_type { + /* NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE indicates that a local + endpoint starts using new destination Connection ID. */ + NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, + /* NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE indicates that a + local endpoint stops using a given destination Connection ID. */ + NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE +} ngtcp2_connection_id_status_type; + +/** + * @functypedef + * + * :type:`ngtcp2_connection_id_status` is a callback function which is + * called when the status of Connection ID changes. + * + * |token| is the associated stateless reset token and it is NULL if + * no token is present. + * + * |type| is the one of the value defined in + * :enum:`ngtcp2_connection_id_status_type`. The new value might be + * added in the future release. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_connection_id_status)(ngtcp2_conn *conn, int type, + uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token, + void *user_data); + typedef struct ngtcp2_conn_callbacks { /** * client_initial is a callback function which is invoked when @@ -1571,6 +1602,12 @@ typedef struct ngtcp2_conn_callbacks { * is increased. This callback function is optional. */ ngtcp2_extend_max_stream_data extend_max_stream_data; + /** + * dcid_status is a callback function which is invoked when the new + * destination Connection ID is activated or the activated + * destination Connection ID is now deactivated. + */ + ngtcp2_connection_id_status dcid_status; } ngtcp2_conn_callbacks; /* @@ -2350,6 +2387,45 @@ NGTCP2_EXTERN size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn); */ NGTCP2_EXTERN size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest); +/** + * @function + * + * `ngtcp2_conn_get_num_active_dcid` returns the number of the active + * destination connection ID. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn); + +/** + * @struct + * + * :type:`ngtcp2_cid_token` is the convenient struct to store + * Connection ID, its associated path, and stateless reset token. + */ +typedef struct ngtcp2_cid_token { + /* seq is the sequence number of this Connection ID. */ + uint64_t seq; + /* cid is Connection ID. */ + ngtcp2_cid cid; + /* ps is the path which is associated to this Connection ID. */ + ngtcp2_path_storage ps; + /* token is the stateless reset token for this Connection ID. */ + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; + /* token_resent is nonzero if token contains stateless reset + token. */ + uint8_t token_present; +} ngtcp2_cid_token; + +/** + * @function + * + * `ngtcp2_conn_get_active_dcid` writes the all active destination + * connection IDs and tokens to |dest|. The buffer pointed by |dest| + * must have ``sizeof(ngtcp2_cid_token) * n`` bytes available, where n + * is the return value of `ngtcp2_conn_get_num_active_dcid()`. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, + ngtcp2_cid_token *dest); + /** * @function * diff --git a/deps/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/lib/includes/ngtcp2/version.h index 85850725bf..af5877d9d2 100644 --- a/deps/ngtcp2/lib/includes/ngtcp2/version.h +++ b/deps/ngtcp2/lib/includes/ngtcp2/version.h @@ -1,45 +1,45 @@ -/* - * ngtcp2 - * - * Copyright (c) 2016 ngtcp2 contributors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef VERSION_H -#define VERSION_H - -/** - * @macro - * - * Version number of the ngtcp2 library release. - */ -#define NGTCP2_VERSION "0.1.90" - -/** - * @macro - * - * Numerical representation of the version number of the ngtcp2 - * library release. This is a 24 bit number with 8 bits for major - * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 - * becomes 0x010203. - */ -#define NGTCP2_VERSION_NUM 0x00015a - -#endif /* VERSION_H */ +/* + * ngtcp2 + * + * Copyright (c) 2016 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VERSION_H +#define VERSION_H + +/** + * @macro + * + * Version number of the ngtcp2 library release. + */ +#define NGTCP2_VERSION "0.1.90" + +/** + * @macro + * + * Numerical representation of the version number of the ngtcp2 + * library release. This is a 24 bit number with 8 bits for major + * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 + * becomes 0x010203. + */ +#define NGTCP2_VERSION_NUM 0x00015a + +#endif /* VERSION_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/lib/ngtcp2_cid.c index c53a61f99a..b0670a9c6f 100644 --- a/deps/ngtcp2/lib/ngtcp2_cid.c +++ b/deps/ngtcp2/lib/ngtcp2_cid.c @@ -95,6 +95,14 @@ void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { dest->ts_retired = src->ts_retired; } +void ngtcp2_dcid_copy_no_path(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { + dest->seq = src->seq; + dest->cid = src->cid; + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + + dest->ts_retired = src->ts_retired; +} + int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token) { diff --git a/deps/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/lib/ngtcp2_cid.h index 4598ee1cc7..cb61a21d52 100644 --- a/deps/ngtcp2/lib/ngtcp2_cid.h +++ b/deps/ngtcp2/lib/ngtcp2_cid.h @@ -125,6 +125,12 @@ void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, */ void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src); +/* + * ngtcp2_dcid_copy_no_path behaves like ngtcp2_dcid_copy, but it does + * not copy path. + */ +void ngtcp2_dcid_copy_no_path(ngtcp2_dcid *dest, const ngtcp2_dcid *src); + /* * ngtcp2_dcid_verify_uniqueness verifies uniqueness of (|seq|, |cid|, * |token|) tuple against |dcid|. diff --git a/deps/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/lib/ngtcp2_conn.c index f3d7482d63..f850f80511 100644 --- a/deps/ngtcp2/lib/ngtcp2_conn.c +++ b/deps/ngtcp2/lib/ngtcp2_conn.c @@ -324,6 +324,38 @@ static int conn_call_extend_max_stream_data(ngtcp2_conn *conn, return 0; } +static int conn_call_dcid_status(ngtcp2_conn *conn, + ngtcp2_connection_id_status_type type, + const ngtcp2_dcid *dcid) { + int rv; + + if (!conn->callbacks.dcid_status) { + return 0; + } + + rv = conn->callbacks.dcid_status( + conn, (int)type, dcid->seq, &dcid->cid, + ngtcp2_check_invalid_stateless_reset_token(dcid->token) ? NULL + : dcid->token, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_activate_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid) { + return conn_call_dcid_status(conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, + dcid); +} + +static int conn_call_deactivate_dcid(ngtcp2_conn *conn, + const ngtcp2_dcid *dcid) { + return conn_call_dcid_status( + conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid); +} + static int crypto_offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return *lhs->i < *rhs->i; @@ -2103,7 +2135,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); if (scid->ts_retired == UINT64_MAX || scid->ts_retired + timeout >= ts) { - return 0; + break; } assert(scid->flags & NGTCP2_SCID_FLAG_RETIRED); @@ -2127,6 +2159,12 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, if (dcid->ts_retired + timeout >= ts) { break; } + + rv = conn_call_deactivate_dcid(conn, dcid); + if (rv != 0) { + return rv; + } + ngtcp2_ringbuf_pop_front(&conn->dcid.retired); } @@ -2403,6 +2441,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, break; } + if ((*pfrc)->fr.type == NGTCP2_FRAME_RETIRE_CONNECTION_ID) { + assert(conn->dcid.num_retire_queued); + --conn->dcid.num_retire_queued; + } + pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; pfrc = &(*pfrc)->next; @@ -3111,6 +3154,36 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { ngtcp2_ksl_len(&conn->hs_pktns.crypto.tx.frq); } +/* + * conn_retire_dcid_seq retires destination connection ID denoted by + * |seq|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_frame_chain *nfrc; + int rv; + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + nfrc->fr.retire_connection_id.seq = seq; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + + ++conn->dcid.num_retire_queued; + + return 0; +} + /* * conn_retire_dcid retires |dcid|. * @@ -3122,13 +3195,17 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { */ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, ngtcp2_tstamp ts) { - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_frame_chain *nfrc; ngtcp2_ringbuf *rb = &conn->dcid.retired; - ngtcp2_dcid *dest; + ngtcp2_dcid *dest, *stale_dcid; int rv; if (ngtcp2_ringbuf_full(rb)) { + stale_dcid = ngtcp2_ringbuf_get(rb, 0); + rv = conn_call_deactivate_dcid(conn, stale_dcid); + if (rv != 0) { + return rv; + } + ngtcp2_ringbuf_pop_front(rb); } @@ -3136,17 +3213,7 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, ngtcp2_dcid_copy(dest, dcid); dest->ts_retired = ts; - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); - if (rv != 0) { - return rv; - } - - nfrc->fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; - nfrc->fr.retire_connection_id.seq = dcid->seq; - nfrc->next = pktns->tx.frq; - pktns->tx.frq = nfrc; - - return 0; + return conn_retire_dcid_seq(conn, dcid->seq); } /* @@ -5395,6 +5462,18 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); } +/* + * check_stateless_reset returns nonzero if Stateless Reset |sr| + * coming via |path| is valid against |dcid|. + */ +static int check_stateless_reset(const ngtcp2_dcid *dcid, + const ngtcp2_path *path, + const ngtcp2_pkt_stateless_reset *sr) { + return ngtcp2_path_eq(&dcid->ps.path, path) && + ngtcp2_verify_stateless_reset_token(dcid->token, + sr->stateless_reset_token) == 0; +} + /* * conn_on_stateless_reset decodes Stateless Reset from the buffer * pointed by |payload| whose length is |payloadlen|. |payload| @@ -5415,21 +5494,31 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, const uint8_t *payload, size_t payloadlen) { int rv = 1; + ngtcp2_pv *pv = conn->pv; + ngtcp2_dcid *dcid; ngtcp2_pkt_stateless_reset sr; + size_t len, i; rv = ngtcp2_pkt_decode_stateless_reset(&sr, payload, payloadlen); if (rv != 0) { return rv; } - if ((!ngtcp2_path_eq(&conn->dcid.current.ps.path, path) || - ngtcp2_verify_stateless_reset_token(conn->dcid.current.token, - sr.stateless_reset_token) != 0) && - (!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || - !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path) || - ngtcp2_verify_stateless_reset_token(conn->pv->fallback_dcid.token, - sr.stateless_reset_token) != 0)) { - return NGTCP2_ERR_INVALID_ARGUMENT; + if (!check_stateless_reset(&conn->dcid.current, path, &sr) && + (!pv || (!check_stateless_reset(&pv->dcid, path, &sr) && + (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || + !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) { + len = ngtcp2_ringbuf_len(&conn->dcid.retired); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + if (check_stateless_reset(dcid, path, &sr)) { + break; + } + } + + if (i == len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } } conn->state = NGTCP2_CS_DRAINING; @@ -5657,6 +5746,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, ngtcp2_dcid *dcid; ngtcp2_pv *pv = conn->pv; int rv; + int found = 0; if (conn->dcid.current.cid.datalen == 0) { return NGTCP2_ERR_PROTO; @@ -5672,7 +5762,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, return rv; } if (ngtcp2_cid_eq(&conn->dcid.current.cid, &fr->cid)) { - return 0; + found = 1; } if (pv) { @@ -5682,7 +5772,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, return rv; } if (ngtcp2_cid_eq(&pv->dcid.cid, &fr->cid)) { - return 0; + found = 1; } } @@ -5696,19 +5786,37 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, return NGTCP2_ERR_PROTO; } if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) { - return 0; + found = 1; } } - rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused, fr->retire_prior_to, - ts); - if (rv != 0) { - return rv; + if (conn->dcid.retire_prior_to < fr->retire_prior_to) { + conn->dcid.retire_prior_to = fr->retire_prior_to; + + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused, + conn->dcid.retire_prior_to, ts); + if (rv != 0) { + return rv; + } + } else if (fr->seq < conn->dcid.retire_prior_to) { + /* If packets are reordered, we might have retire_prior_to which + is larger than fr->seq. + + A malicious peer might send crafted NEW_CONNECTION_ID to force + local endpoint to create lots of RETIRE_CONNECTION_ID frames. + For example, a peer might send seq = 50000 and retire_prior_to + = 50000. Then send NEW_CONNECTION_ID frames with seq < + 50000. */ + if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) { + return conn_retire_dcid_seq(conn, fr->seq); + } + return 0; } 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) { 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 @@ -5718,18 +5826,93 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, return 0; } - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); - ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); + if (!found) { + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + return 0; + } - if (conn->dcid.current.seq < fr->retire_prior_to) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return 0; + } + + if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { rv = conn_retire_dcid(conn, &conn->dcid.current, ts); if (rv != 0) { return rv; } dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - ngtcp2_dcid_copy(&conn->dcid.current, dcid); + if (pv && conn->dcid.current.seq == pv->dcid.seq) { + ngtcp2_dcid_copy_no_path(&pv->dcid, dcid); + } + + assert(!pv || !(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || + conn->dcid.current.seq != pv->fallback_dcid.seq); + + ngtcp2_dcid_copy_no_path(&conn->dcid.current, dcid); ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + } + + if (pv) { + if (pv->dcid.seq < conn->dcid.retire_prior_to) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + rv = conn_retire_dcid(conn, &pv->dcid, ts); + if (rv != 0) { + return rv; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + ngtcp2_dcid_copy_no_path(&pv->dcid, dcid); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &pv->dcid); + if (rv != 0) { + return rv; + } + } else { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, + "path migration is aborted because connection ID is" + "retired and no unused connection ID is available"); + + assert(!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)); + + return conn_stop_pv(conn, ts); + } + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq < conn->dcid.retire_prior_to) { + rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); + if (rv != 0) { + return rv; + } + + if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + ngtcp2_dcid_copy_no_path(&pv->fallback_dcid, dcid); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); + if (rv != 0) { + return rv; + } + } else { + /* Now we have no fallback dcid. */ + assert(pv->dcid.seq == conn->dcid.current.seq); + + pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_RETIRE_DCID_ON_FINISH; + + return conn_stop_pv(conn, ts); + } + } } return 0; @@ -5999,7 +6182,6 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, "path migration is aborted because new migration has started"); rv = conn_stop_pv(conn, ts); if (rv != 0) { - ngtcp2_pv_del(pv); return rv; } } @@ -6008,7 +6190,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, ngtcp2_ringbuf_pop_front(&conn->dcid.unused); - return 0; + return conn_call_activate_dcid(conn, &pv->dcid); } /* @@ -6686,7 +6868,7 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, } /* - * conn_is_retired_path returns nonzero if |path| is inclued in + * conn_is_retired_path returns nonzero if |path| is included in * retired path list. */ static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) { @@ -6891,6 +7073,11 @@ int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, } conn->state = NGTCP2_CS_POST_HANDSHAKE; + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + rv = conn_process_buffered_protected_pkt(conn, &conn->hs_pktns, ts); if (rv != 0) { return rv; @@ -6963,7 +7150,7 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { ngtcp2_addr_copy(&pv->dcid.ps.path.local, &conn->dcid.current.ps.path.local); ngtcp2_addr_copy(&pv->dcid.ps.path.remote, &addr); - return 0; + return conn_call_activate_dcid(conn, &pv->dcid); } /* @@ -7010,6 +7197,7 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, size_t server_hs_tx_left; ngtcp2_rcvry_stat *rcs = &conn->rcs; size_t pending_early_datalen; + const ngtcp2_dcid *dcid; cwnd = conn_cwnd_left(conn); destlen = ngtcp2_min(destlen, cwnd); @@ -7107,6 +7295,24 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, sizeof(conn->dcid.current.token)); } + if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + if (rv != 0) { + return rv; + } + + assert(ngtcp2_ringbuf_len(&conn->dcid.unused)); + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + ngtcp2_dcid_copy_no_path(&conn->dcid.current, dcid); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + } + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + conn_process_early_rtb(conn); rv = conn_process_buffered_protected_pkt(conn, &conn->hs_pktns, ts); @@ -7551,6 +7757,7 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { ngtcp2_tstamp res = UINT64_MAX; ngtcp2_duration pto = conn_compute_pto(conn); ngtcp2_scid *scid; + ngtcp2_dcid *dcid; if (conn->pv) { res = ngtcp2_pv_next_expiry(conn->pv); @@ -7563,6 +7770,11 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { } } + if (ngtcp2_ringbuf_len(&conn->dcid.retired)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); + res = ngtcp2_min(res, dcid->ts_retired + pto); + } + return res; } @@ -7634,6 +7846,8 @@ conn_client_validate_transport_params(ngtcp2_conn *conn, if (!ngtcp2_cid_eq(&conn->rcid, ¶ms->original_connection_id)) { return NGTCP2_ERR_TRANSPORT_PARAM; } + } else if (params->original_connection_id_present) { + return NGTCP2_ERR_TRANSPORT_PARAM; } return 0; @@ -8660,6 +8874,72 @@ size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { return ngtcp2_ksl_len(&conn->scid.set); } +size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { + size_t n = 1; /* for conn->dcid.current */ + ngtcp2_pv *pv = conn->pv; + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + return 0; + } + + if (pv) { + if (pv->dcid.seq != conn->dcid.current.seq) { + ++n; + } + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { + ++n; + } + } + + n += ngtcp2_ringbuf_len(&conn->dcid.retired); + + return n; +} + +static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest, + const ngtcp2_dcid *src) { + dest->seq = src->seq; + dest->cid = src->cid; + ngtcp2_path_storage_init2(&dest->ps, &src->ps.path); + dest->token_present = + (uint8_t)!ngtcp2_check_invalid_stateless_reset_token(src->token); + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { + ngtcp2_pv *pv = conn->pv; + ngtcp2_cid_token *orig = dest; + ngtcp2_dcid *dcid; + size_t len, i; + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + return 0; + } + + copy_dcid_to_cid_token(dest, &conn->dcid.current); + ++dest; + + if (pv) { + if (pv->dcid.seq != conn->dcid.current.seq) { + copy_dcid_to_cid_token(dest, &pv->dcid); + ++dest; + } + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { + copy_dcid_to_cid_token(dest, &pv->fallback_dcid); + ++dest; + } + } + + len = ngtcp2_ringbuf_len(&conn->dcid.retired); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + copy_dcid_to_cid_token(dest, dcid); + ++dest; + } + + return (size_t)(dest - orig); +} + void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { ngtcp2_addr *dest = &conn->dcid.current.ps.path.local; @@ -8715,6 +8995,11 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_path_copy(&conn->dcid.current.ps.path, path); ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + conn_reset_congestion_state(conn); return 0; diff --git a/deps/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/lib/ngtcp2_conn.h index 55ef4658bc..213e8a9e85 100644 --- a/deps/ngtcp2/lib/ngtcp2_conn.h +++ b/deps/ngtcp2/lib/ngtcp2_conn.h @@ -277,6 +277,12 @@ struct ngtcp2_conn { /* retired is a set of CID retired by local endpoint. Keep them in 3*PTO to catch packets in flight along the old path. */ ngtcp2_ringbuf retired; + /* retire_prior_to is the larget retire_prior_to received so + far. */ + uint64_t retire_prior_to; + /* num_retire_queued is the number of RETIRE_CONNECTION_ID frames + queued for transmission. */ + size_t num_retire_queued; } dcid; struct { diff --git a/deps/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/lib/ngtcp2_str.c index 2e7d971533..b7502e3bd0 100644 --- a/deps/ngtcp2/lib/ngtcp2_str.c +++ b/deps/ngtcp2/lib/ngtcp2_str.c @@ -74,24 +74,18 @@ char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, int ngtcp2_verify_stateless_reset_token(const uint8_t *want, const uint8_t *got) { - size_t i; - - /* We consider that token with all bits not set is invalid. */ - for (i = 0; i < NGTCP2_STATELESS_RESET_TOKENLEN; ++i) { - if (got[i] != 0) { - break; - } - } - - if (i == NGTCP2_STATELESS_RESET_TOKENLEN) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - return ngtcp2_cmemeq(want, got, NGTCP2_STATELESS_RESET_TOKENLEN) + return !ngtcp2_check_invalid_stateless_reset_token(got) && + ngtcp2_cmemeq(want, got, NGTCP2_STATELESS_RESET_TOKENLEN) ? 0 : NGTCP2_ERR_INVALID_ARGUMENT; } +int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token) { + static uint8_t invalid_token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0}; + + return 0 == memcmp(invalid_token, token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n) { size_t i; int rv = 0; diff --git a/deps/ngtcp2/lib/ngtcp2_str.h b/deps/ngtcp2/lib/ngtcp2_str.h index 637e17ea6a..104b1f1a03 100644 --- a/deps/ngtcp2/lib/ngtcp2_str.h +++ b/deps/ngtcp2/lib/ngtcp2_str.h @@ -68,6 +68,13 @@ char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, int ngtcp2_verify_stateless_reset_token(const uint8_t *want, const uint8_t *got); +/* + * ngtcp2_check_invalid_stateless_reset_token returns nonzero if + * |token| is invalid stateless reset token. Currently, token which + * consists of all zeros is considered invalid. + */ +int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token); + /* * ngtcp2_cmemeq returns nonzero if the first |n| bytes of the buffers * pointed by |a| and |b| are equal. The comparison is done in a