diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h index bc66c40ce211e4..cd10e4def7019b 100644 --- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h @@ -125,13 +125,6 @@ typedef ptrdiff_t nghttp3_ssize; * already in use. */ #define NGHTTP3_ERR_STREAM_IN_USE -105 -/** - * @macro - * - * :macro:`NGHTTP3_ERR_PUSH_ID_BLOCKED` indicates that there are no - * spare push ID available. - */ -#define NGHTTP3_ERR_PUSH_ID_BLOCKED -106 /** * @macro * @@ -167,13 +160,6 @@ typedef ptrdiff_t nghttp3_ssize; * field is too large to process. */ #define NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE -112 -/** - * @macro - * - * :macro:`NGHTTP3_ERR_IGNORE_STREAM` indicates that a stream should - * be ignored. - */ -#define NGHTTP3_ERR_IGNORE_STREAM -113 /** * @macro * @@ -184,17 +170,17 @@ typedef ptrdiff_t nghttp3_ssize; /** * @macro * - * :macro:`NGHTTP3_ERR_IGNORE_PUSH_PROMISE` indicates that a push - * promise should be ignored. + * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is + * closing state. */ -#define NGHTTP3_ERR_IGNORE_PUSH_PROMISE -115 +#define NGHTTP3_ERR_CONN_CLOSING -116 /** * @macro * - * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is - * closing state. + * :macro:`NGHTTP3_ERR_STREAM_DATA_OVERFLOW` indicates that the length + * of stream data is too long and causes overflow. */ -#define NGHTTP3_ERR_CONN_CLOSING -116 +#define NGHTTP3_ERR_STREAM_DATA_OVERFLOW -117 /** * @macro * @@ -451,59 +437,69 @@ typedef ptrdiff_t nghttp3_ssize; /** * @functypedef * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. + * :type:`nghttp3_malloc` is a custom memory allocator to replace + * :manpage:`malloc(3)`. The |user_data| is the + * :member:`nghttp3_mem.user_data`. */ -typedef void *(*nghttp3_malloc)(size_t size, void *mem_user_data); +typedef void *(*nghttp3_malloc)(size_t size, void *user_data); /** * @functypedef * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`nghttp3_mem` structure. + * :type:`nghttp3_free` is a custom memory allocator to replace + * :manpage:`free(3)`. The |user_data| is the + * :member:`nghttp3_mem.user_data`. */ -typedef void (*nghttp3_free)(void *ptr, void *mem_user_data); +typedef void (*nghttp3_free)(void *ptr, void *user_data); /** * @functypedef * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. + * :type:`nghttp3_calloc` is a custom memory allocator to replace + * :manpage:`calloc(3)`. The |user_data| is the + * :member:`nghttp3_mem.user_data`. */ -typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *mem_user_data); +typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *user_data); /** * @functypedef * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp3_mem` structure. + * :type:`nghttp3_realloc` is a custom memory allocator to replace + * :manpage:`realloc(3)`. The |user_data| is the + * :member:`nghttp3_mem.user_data`. */ -typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data); +typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *user_data); /** * @struct * * :type:`nghttp3_mem` is a custom memory allocator functions and user - * defined pointer. The |mem_user_data| member is passed to each + * defined pointer. The :member:`user_data` field is passed to each * allocator function. This can be used, for example, to achieve * per-session memory pool. * * In the following example code, ``my_malloc``, ``my_free``, * ``my_calloc`` and ``my_realloc`` are the replacement of the - * standard allocators ``malloc``, ``free``, ``calloc`` and - * ``realloc`` respectively:: + * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`, + * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively:: * - * void *my_malloc_cb(size_t size, void *mem_user_data) { + * void *my_malloc_cb(size_t size, void *user_data) { + * (void)user_data; * return my_malloc(size); * } * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * void my_free_cb(void *ptr, void *user_data) { + * (void)user_data; + * my_free(ptr); + * } * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) { + * (void)user_data; * return my_calloc(nmemb, size); * } * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * void *my_realloc_cb(void *ptr, size_t size, void *user_data) { + * (void)user_data; * return my_realloc(ptr, size); * } * @@ -516,27 +512,28 @@ typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data); */ typedef struct nghttp3_mem { /** - * :member:`mem_user_data` is an arbitrary user supplied data. This + * :member:`user_data` is an arbitrary user supplied data. This * is passed to each allocator function. */ - void *mem_user_data; + void *user_data; /** * :member:`malloc` is a custom allocator function to replace - * malloc(). + * :manpage:`malloc(3)`. */ nghttp3_malloc malloc; /** - * :member:`free` is a custom allocator function to replace free(). + * :member:`free` is a custom allocator function to replace + * :manpage:`free(3)`. */ nghttp3_free free; /** * :member:`calloc` is a custom allocator function to replace - * calloc(). + * :manpage:`calloc(3)`. */ nghttp3_calloc calloc; /** * :member:`realloc` is a custom allocator function to replace - * realloc(). + * :manpage:`realloc(3)`. */ nghttp3_realloc realloc; } nghttp3_mem; @@ -652,8 +649,8 @@ NGHTTP3_EXTERN void nghttp3_buf_init(nghttp3_buf *buf); * @function * * `nghttp3_buf_free` frees resources allocated for |buf| using |mem| - * as memory allocator. buf->begin must be a heap buffer allocated by - * |mem|. + * as memory allocator. :member:`buf->begin ` must + * be a heap buffer allocated by |mem|. */ NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem); @@ -662,7 +659,8 @@ NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem); * * `nghttp3_buf_left` returns the number of additional bytes which can * be written to the underlying buffer. In other words, it returns - * buf->end - buf->last. + * :member:`buf->end ` - :member:`buf->last + * `. */ NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf); @@ -670,14 +668,17 @@ NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf); * @function * * `nghttp3_buf_len` returns the number of bytes left to read. In - * other words, it returns buf->last - buf->pos. + * other words, it returns :member:`buf->last ` - + * :member:`buf->pos `. */ NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf); /** * @function * - * `nghttp3_buf_reset` sets buf->pos and buf->last to buf->begin. + * `nghttp3_buf_reset` sets :member:`buf->pos ` and + * :member:`buf->last ` to :member:`buf->begin + * `. */ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); @@ -692,7 +693,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); * * :macro:`NGHTTP3_NV_FLAG_NONE` indicates no flag set. */ -#define NGHTTP3_NV_FLAG_NONE 0 +#define NGHTTP3_NV_FLAG_NONE 0x00u /** * @macro @@ -701,7 +702,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); * pair must not be indexed. Other implementation calls this bit as * "sensitive". */ -#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01 +#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01u /** * @macro @@ -710,7 +711,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); * If this flag is set, the library does not make a copy of header * field name. This could improve performance. */ -#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02 +#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02u /** * @macro @@ -719,7 +720,7 @@ NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); * application. If this flag is set, the library does not make a copy * of header field value. This could improve performance. */ -#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04 +#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04u /** * @struct @@ -748,7 +749,7 @@ typedef struct nghttp3_nv { size_t valuelen; /** * :member:`flags` is bitwise OR of one or more of - * NGHTTP3_NV_FLAG_*. + * :macro:`NGHTTP3_NV_FLAG_* `. */ uint8_t flags; } nghttp3_nv; @@ -1057,7 +1058,7 @@ typedef enum nghttp3_qpack_token { * :type:`nghttp3_nv` and has reference counted buffers and tokens * which might be useful for applications. */ -typedef struct { +typedef struct nghttp3_qpack_nv { /** * :member:`name` is the buffer containing header field name. * NULL-termination is guaranteed. @@ -1076,7 +1077,7 @@ typedef struct { int32_t token; /** * :member:`flags` is a bitwise OR of one or more of - * NGHTTP3_NV_FLAG_*. See :macro:`NGHTTP3_NV_FLAG_NONE`. + * :macro:`NGHTTP3_NV_FLAG_* `. */ uint8_t flags; } nghttp3_qpack_nv; @@ -1092,11 +1093,14 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; * @function * * `nghttp3_qpack_encoder_new` initializes QPACK encoder. |pencoder| - * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic - * table size. |max_blocked| is the maximum number of streams which - * can be blocked. |mem| is a memory allocator. This function - * allocates memory for :type:`nghttp3_qpack_encoder` itself and - * assigns its pointer to |*pencoder| if it succeeds. + * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper + * bound of the dynamic table capacity. |mem| is a memory allocator. + * This function allocates memory for :type:`nghttp3_qpack_encoder` + * itself and assigns its pointer to |*pencoder| if it succeeds. + * + * The maximum dynamic table capacity is still 0. In order to change + * the maximum dynamic table capacity, call + * `nghttp3_qpack_encoder_set_max_dtable_capacity`. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -1105,8 +1109,7 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; * Out of memory. */ NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, - size_t max_dtable_size, - size_t max_blocked, + size_t hard_max_dtable_capacity, const nghttp3_mem *mem); /** @@ -1174,82 +1177,24 @@ NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder( /** * @function * - * `nghttp3_qpack_encoder_set_max_dtable_size` sets max dynamic table - * size to |max_dtable_size|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` - * |max_dtable_size| exceeds the hard limit that decoder specifies. - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size); - -/** - * @function - * - * `nghttp3_qpack_encoder_set_hard_max_dtable_size` sets hard maximum - * dynamic table size to |hard_max_dtable_size|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * TBD - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_hard_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t hard_max_dtable_size); - -/** - * @function - * - * `nghttp3_qpack_encoder_set_max_blocked` sets the number of streams - * which can be blocked to |max_blocked|. - * - * This function returns the number of bytes read, or one of the - * following negative error codes: - * - * TBD - */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, - size_t max_blocked); - -/** - * @function - * - * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header - * block for a stream denoted by |stream_id| was acknowledged by - * decoder. This function is provided for debugging purpose only. In - * HTTP/3, |encoder| knows acknowledgement of header block by reading - * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + * `nghttp3_qpack_encoder_set_max_dtable_capacity` sets max dynamic + * table capacity to |max_dtable_capacity|. If |max_dtable_capacity| is + * larger than ``hard_max_dtable_capacity`` parameter of + * `nghttp3_qpack_encoder_new`, it is truncated to the latter. */ NGHTTP3_EXTERN void -nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, - int64_t stream_id); +nghttp3_qpack_encoder_set_max_dtable_capacity(nghttp3_qpack_encoder *encoder, + size_t max_dtable_capacity); /** * @function * - * `nghttp3_qpack_encoder_add_insert_count` increments known received - * count of |encoder| by |n|. This function is provided for debugging - * purpose only. In HTTP/3, |encoder| increments known received count - * by reading decoder stream with - * `nghttp3_qpack_encoder_read_decoder()`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :macro:`NGHTTP3_ERR_NOMEM` - * Out of memory. - * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` - * |n| is too large. + * `nghttp3_qpack_encoder_set_max_blocked_streams` sets the number of + * streams which can be blocked to |max_blocked_streams|. */ -NGHTTP3_EXTERN int -nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, - uint64_t n); +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_set_max_blocked_streams(nghttp3_qpack_encoder *encoder, + size_t max_blocked_streams); /** * @function @@ -1265,23 +1210,11 @@ nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder); /** * @function * - * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream - * denoted by |stream_id| is cancelled. This function is provided for - * debugging purpose only. In HTTP/3, |encoder| knows this by reading - * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. - */ -NGHTTP3_EXTERN void -nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_qpack_encoder_get_num_blocked` returns the number of - * streams which are potentially blocked at decoder side. + * `nghttp3_qpack_encoder_get_num_blocked_streams` returns the number + * of streams which are potentially blocked at decoder side. */ NGHTTP3_EXTERN size_t -nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder); +nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder); /** * @struct @@ -1351,11 +1284,12 @@ typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder; * @function * * `nghttp3_qpack_decoder_new` initializes QPACK decoder. |pdecoder| - * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic - * table size. |max_blocked| is the maximum number of streams which - * can be blocked. |mem| is a memory allocator. This function - * allocates memory for :type:`nghttp3_qpack_decoder` itself and - * assigns its pointer to |*pdecoder| if it succeeds. + * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper + * bound of the dynamic table capacity. |max_blocked_streams| is the + * maximum number of streams which can be blocked. |mem| is a memory + * allocator. This function allocates memory for + * :type:`nghttp3_qpack_decoder` itself and assigns its pointer to + * |*pdecoder| if it succeeds. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -1364,8 +1298,8 @@ typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder; * Out of memory. */ NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, - size_t max_dtable_size, - size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem); /** @@ -1415,7 +1349,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * * :macro:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag set. */ -#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0 +#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0x00u /** * @macro @@ -1423,7 +1357,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header * field is successfully decoded. */ -#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01 +#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01u /** * @macro @@ -1431,7 +1365,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header * fields have been decoded. */ -#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02 +#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02u /** * @macro @@ -1439,7 +1373,7 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding * has been blocked. */ -#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04 +#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04u /** * @function @@ -1459,12 +1393,19 @@ nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); * due to required insert count. * * When a header field is decoded, an application receives it in |nv|. - * nv->name and nv->value are reference counted buffer, and their + * :member:`nv->name ` and :member:`nv->value + * ` are reference counted buffer, and their * reference counts are already incremented for application use. * Therefore, when application finishes processing the header field, * it must call `nghttp3_rcbuf_decref(nv->name) * ` and `nghttp3_rcbuf_decref(nv->value) - * ` or memory leak might occur. + * ` or memory leak might occur. These + * :type:`nghttp3_rcbuf` objects hold the pointer to + * :type:`nghttp3_mem` that is passed to `nghttp3_qpack_decoder_new` + * (or either `nghttp3_conn_client_new` or `nghttp3_conn_server_new` + * if it is used indirectly). As long as these objects are alive, the + * pointed :type:`nghttp3_mem` object must be available. Otherwise, + * `nghttp3_rcbuf_decref` will cause undefined behavior. * * This function returns the number of bytes read, or one of the * following negative error codes: @@ -1529,14 +1470,24 @@ nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, /** * @function * - * `nghttp3_qpack_decoder_set_dtable_cap` sets |cap| as maximum - * dynamic table size. Normally, the maximum capacity is communicated - * in encoder stream. This function is provided for debugging and - * testing purpose. + * `nghttp3_qpack_decoder_set_max_dtable_capacity` sets + * |max_dtable_capacity| as maximum dynamic table size. + * |max_dtable_capacity| must be equal to or smaller than + * ``hard_max_dtable_capacity`` parameter of + * `nghttp3_qpack_decoder_new`. Normally, the maximum capacity is + * communicated in encoder stream. This function is provided for + * debugging and testing purpose. + * + * This function returns 0 if it succeeds, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + * |max_dtable_capacity| exceeds the upper bound of the dynamic + * table capacity. */ -NGHTTP3_EXTERN void -nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, - size_t cap); +NGHTTP3_EXTERN int +nghttp3_qpack_decoder_set_max_dtable_capacity(nghttp3_qpack_decoder *decoder, + size_t max_dtable_capacity); /** * @function @@ -1571,10 +1522,10 @@ NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr); * * :type:`nghttp3_debug_vprintf_callback` is a callback function * invoked when the library outputs debug logging. The function is - * called with arguments suitable for ``vfprintf(3)`` + * called with arguments suitable for :manpage:`vfprintf(3)`. * * The debug output is only enabled if the library is built with - * ``DEBUGBUILD`` macro defined. + * :macro:`DEBUGBUILD` macro defined. */ typedef void (*nghttp3_debug_vprintf_callback)(const char *format, va_list args); @@ -1583,27 +1534,51 @@ typedef void (*nghttp3_debug_vprintf_callback)(const char *format, * @function * * `nghttp3_set_debug_vprintf_callback` sets a debug output callback - * called by the library when built with ``DEBUGBUILD`` macro defined. - * If this option is not used, debug log is written into standard - * error output. + * called by the library when built with :macro:`DEBUGBUILD` macro + * defined. If this option is not used, debug log is written into + * standard error output. * - * For builds without ``DEBUGBUILD`` macro defined, this function is - * noop. + * For builds without :macro:`DEBUGBUILD` macro defined, this function + * is noop. * - * Note that building with ``DEBUGBUILD`` may cause significant + * Note that building with :macro:`DEBUGBUILD` may cause significant * performance penalty to libnghttp3 because of extra processing. It * should be used for debugging purpose only. * * .. Warning:: * - * Building with ``DEBUGBUILD`` may cause significant performance - * penalty to libnghttp3 because of extra processing. It should be - * used for debugging purpose only. We write this two times because - * this is important. + * Building with :macro:`DEBUGBUILD` may cause significant + * performance penalty to libnghttp3 because of extra processing. + * It should be used for debugging purpose only. We write this two + * times because this is important. */ NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback( nghttp3_debug_vprintf_callback debug_vprintf_callback); +/** + * @macrosection + * + * Shutdown related constants + */ + +/** + * @macro + * + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` specifies stream id sent + * by a server when it initiates graceful shutdown of the connection + * via `nghttp3_conn_submit_shutdown_notice`. + */ +#define NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID ((1ull << 62) - 4) + +/** + * @macro + * + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID` specifies push id sent + * by a client when it initiates graceful shutdown of the connection + * via `nghttp3_conn_submit_shutdown_notice`. + */ +#define NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID ((1ull << 62) - 1) + /** * @struct * @@ -1625,7 +1600,7 @@ typedef struct nghttp3_conn nghttp3_conn; * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id, - size_t datalen, void *conn_user_data, + uint64_t datalen, void *conn_user_data, void *stream_user_data); /** @@ -1711,7 +1686,7 @@ typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id, * |name| contains a field name and |value| contains a field value. * |token| is one of token defined in :type:`nghttp3_qpack_token` or * -1 if no token is defined for |name|. |flags| is bitwise OR of - * zero or more of NGHTTP3_NV_FLAG_*. + * zero or more of :macro:`NGHTTP3_NV_FLAG_* `. * * The buffers for |name| and |value| are reference counted. If * application needs to keep them, increment the reference count with @@ -1735,78 +1710,17 @@ typedef int (*nghttp3_recv_header)(nghttp3_conn *conn, int64_t stream_id, * :type:`nghttp3_end_headers` is a callback function which is invoked * when an incoming header block has ended. * + * If the stream ends with this header block, |fin| is set to nonzero. + * * The implementation of this callback must return 0 if it succeeds. * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the * caller immediately. Any values other than 0 is treated as * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id, - void *conn_user_data, + int fin, void *conn_user_data, void *stream_user_data); -/** - * @functypedef - * - * :type:`nghttp3_begin_push_promise` is a callback function which is - * invoked when an incoming header block section in PUSH_PROMISE is - * started on a stream denoted by |stream_id|. |push_id| identifies a - * push promise. Each header field is passed to application by - * :type:`nghttp3_recv_push_promise` callback. And then - * :type:`nghttp3_end_push_promise` is called when a whole header - * block is processed. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_begin_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_recv_push_promise` is a callback function which is - * invoked when a header field in PUSH_PROMISE is received on a stream - * denoted by |stream_id|. |push_id| identifies a push promise. - * |name| contains a field name and |value| contains a field value. - * |token| is one of token defined in :type:`nghttp3_qpack_token` or - * -1 if no token is defined for |name|. |flags| is bitwise OR of - * zero or more of NGHTTP3_NV_FLAG_*. - * - * The buffers for |name| and |value| are reference counted. If - * application needs to keep them, increment the reference count with - * `nghttp3_rcbuf_incref`. When they are no longer used, call - * `nghttp3_rcbuf_decref`. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_recv_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, int32_t token, - nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_end_push_promise` is a callback function which is - * invoked when an incoming header block in PUSH_PROMISE has ended. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_end_push_promise)(nghttp3_conn *conn, int64_t stream_id, - int64_t push_id, void *conn_user_data, - void *stream_user_data); - /** * @functypedef * @@ -1827,26 +1741,7 @@ typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id, /** * @functypedef * - * :type:`nghttp3_cancel_push` is a callback function which is invoked - * when the push identified by |push_id| is cancelled by remote - * endpoint. If a stream has been bound to the push ID, |stream_id| - * contains the stream ID and |stream_user_data| points to the stream - * user data. Otherwise, |stream_id| is -1 and |stream_user_data| is - * NULL. - * - * The implementation of this callback must return 0 if it succeeds. - * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the - * caller immediately. Any values other than 0 is treated as - * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id, void *conn_user_data, - void *stream_user_data); - -/** - * @functypedef - * - * :type:`nghttp3_send_stop_sending` is a callback function which is + * :type:`nghttp3_stop_sending` is a callback function which is * invoked when the library asks application to send STOP_SENDING to * the stream identified by |stream_id|. |app_error_code| indicates * the reason for this action. @@ -1856,43 +1751,61 @@ typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id, * caller immediately. Any values other than 0 is treated as * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ -typedef int (*nghttp3_send_stop_sending)(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, - void *conn_user_data, - void *stream_user_data); +typedef int (*nghttp3_stop_sending)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); /** * @functypedef * - * :type:`nghttp3_push_stream` is a callback function which is invoked - * when a push stream identified by |stream_id| is opened with - * |push_id|. + * :type:`nghttp3_reset_stream` is a callback function which is + * invoked when the library asks application to reset stream + * identified by |stream_id|. |app_error_code| indicates the reason + * for this action. * * The implementation of this callback must return 0 if it succeeds. * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the * caller immediately. Any values other than 0 is treated as * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ -typedef int (*nghttp3_push_stream)(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id, void *conn_user_data); +typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); /** * @functypedef * - * :type:`nghttp3_reset_stream` is a callback function which is - * invoked when the library asks application to reset stream - * identified by |stream_id|. |app_error_code| indicates the reason - * for this action. + * :type:`nghttp3_shutdown` is a callback function which is invoked + * when a shutdown is initiated by the remote endpoint. For client, + * |id| contains a stream id of a client initiated stream, for server, + * it contains a push id. All client streams with stream id or pushes + * with push id equal to or larger than |id| are guaranteed to not be + * processed by the remote endpoint. + * + * Parameter |id| for client can contain a special value + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` and for server it can + * contain special value + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID`. These values signal + * request for graceful shutdown of the connection, triggered by + * remote endpoint's invocation of + * `nghttp3_conn_submit_shutdown_notice`. + * + * It is possible that this callback is invoked multiple times on a + * single connection, however the |id| can only stay the same or + * decrease, never increase. * * The implementation of this callback must return 0 if it succeeds. * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the * caller immediately. Any values other than 0 is treated as * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. */ -typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, - void *conn_user_data, - void *stream_user_data); +typedef int (*nghttp3_shutdown)(nghttp3_conn *conn, int64_t id, + void *conn_user_data); + +#define NGHTTP3_CALLBACKS_VERSION_V1 1 +#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_VERSION_V1 /** * @struct @@ -1953,37 +1866,11 @@ typedef struct nghttp3_callbacks { */ nghttp3_end_headers end_trailers; /** - * :member:`begin_push_promise` is a callback function which is - * invoked when a push promise has started on a particular stream. - */ - nghttp3_begin_push_promise begin_push_promise; - /** - * :member:`recv_push_promise` is a callback function which is - * invoked when a single header field in a push promise is received - * on a particular stream. - */ - nghttp3_recv_push_promise recv_push_promise; - /** - * :member:`end_push_promise` is a callback function which is - * invoked when a push promise has ended on a particular stream. - */ - nghttp3_end_push_promise end_push_promise; - /** - * :member:`cancel_push` is a callback function which is invoked - * when a push promise has been cancelled by a remote endpoint. - */ - nghttp3_cancel_push cancel_push; - /** - * :member:`send_stop_sending` is a callback function which is - * invoked when the library asks application to send STOP_SENDING to - * a particular stream. - */ - nghttp3_send_stop_sending send_stop_sending; - /** - * :member:`push_stream` is a callback function which is invoked - * when a push stream has opened. + * :member:`stop_sending` is a callback function which is invoked + * when the library asks application to send STOP_SENDING to a + * particular stream. */ - nghttp3_push_stream push_stream; + nghttp3_stop_sending stop_sending; /** * :member:`end_stream` is a callback function which is invoked when * a receiving side of stream has been closed. @@ -1995,34 +1882,53 @@ typedef struct nghttp3_callbacks { * RESET_STREAM). */ nghttp3_reset_stream reset_stream; + /** + * :member:`shutdown` is a callback function which is invoked when + * the remote endpoint has signalled initiation of connection shutdown. + */ + nghttp3_shutdown shutdown; } nghttp3_callbacks; +#define NGHTTP3_SETTINGS_VERSION_V1 1 +#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_VERSION_V1 + /** * @struct * * :type:`nghttp3_settings` defines HTTP/3 settings. */ -typedef struct { +typedef struct nghttp3_settings { /** * :member:`max_field_section_size` specifies the maximum header * section (block) size. */ uint64_t max_field_section_size; /** - * :member:`max_pushes` specifies the maximum number of concurrent - * pushes it accepts from a remote endpoint. + * :member:`qpack_max_dtable_capacity` is the maximum size of QPACK + * dynamic table. */ - uint64_t max_pushes; + size_t qpack_max_dtable_capacity; /** - * :member:`qpack_max_table_capacity` is the maximum size of QPACK - * dynamic table. + * :member:`qpack_encoder_max_dtable_capacity` is the upper bound of + * QPACK dynamic table capacity that the QPACK encoder is willing to + * use. The effective maximum dynamic table capacity is the minimum + * of this field and the value of the received + * SETTINGS_QPACK_MAX_TABLE_CAPACITY. If this field is set to 0, + * the encoder does not use the dynamic table. */ - size_t qpack_max_table_capacity; + size_t qpack_encoder_max_dtable_capacity; /** * :member:`qpack_blocked_streams` is the maximum number of streams * which can be blocked while they are being decoded. */ size_t qpack_blocked_streams; + /** + * :member:`enable_connect_protocol`, if set to nonzero, enables + * Extended CONNECT Method (see + * https://www.ietf.org/archive/id/draft-ietf-httpbis-h3-websockets-00.html). + * Client ignores this field. + */ + int enable_connect_protocol; } nghttp3_settings; /** @@ -2030,34 +1936,51 @@ typedef struct { * * `nghttp3_settings_default` fills |settings| with the default * values. + * + * - :member:`max_field_section_size + * ` = :expr:`((1ull << 62) - 1)` + * - :member:`qpack_max_dtable_capacity + * ` = 0 + * - :member:`qpack_encoder_max_dtable_capacity + * ` = 4096 + * - :member:`qpack_blocked_streams + * ` = 0 + * - :member:`enable_connect_protocol + * ` = 0 */ -NGHTTP3_EXTERN void nghttp3_settings_default(nghttp3_settings *settings); +NGHTTP3_EXTERN void +nghttp3_settings_default_versioned(int settings_version, + nghttp3_settings *settings); /** * @function * * `nghttp3_conn_client_new` creates :type:`nghttp3_conn` and * initializes it for client use. The pointer to the object is stored - * in |*pconn|. + * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned + * by `nghttp3_mem_default` is used. */ -NGHTTP3_EXTERN int nghttp3_conn_client_new(nghttp3_conn **pconn, - const nghttp3_callbacks *callbacks, - const nghttp3_settings *settings, - const nghttp3_mem *mem, - void *conn_user_data); +NGHTTP3_EXTERN int +nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, int callbacks_version, + const nghttp3_callbacks *callbacks, + int settings_version, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *conn_user_data); /** * @function * * `nghttp3_conn_server_new` creates :type:`nghttp3_conn` and * initializes it for server use. The pointer to the object is stored - * in |*pconn|. + * in |*pconn|. If |mem| is ``NULL``, the memory allocator returned + * by `nghttp3_mem_default` is used. */ -NGHTTP3_EXTERN int nghttp3_conn_server_new(nghttp3_conn **pconn, - const nghttp3_callbacks *callbacks, - const nghttp3_settings *settings, - const nghttp3_mem *mem, - void *conn_user_data); +NGHTTP3_EXTERN int +nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, int callbacks_version, + const nghttp3_callbacks *callbacks, + int settings_version, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *conn_user_data); /** * @function @@ -2177,8 +2100,8 @@ NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, * `nghttp3_conn_block_stream` tells the library that stream * identified by |stream_id| is blocked due to QUIC flow control. */ -NGHTTP3_EXTERN int nghttp3_conn_block_stream(nghttp3_conn *conn, - int64_t stream_id); +NGHTTP3_EXTERN void nghttp3_conn_block_stream(nghttp3_conn *conn, + int64_t stream_id); /** * @function @@ -2190,6 +2113,26 @@ NGHTTP3_EXTERN int nghttp3_conn_block_stream(nghttp3_conn *conn, NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id); +/** + * @function + * + * `nghttp3_conn_is_stream_writable` returns nonzero if a stream + * identified by |stream_id| is writable. It is not writable if: + * + * - the stream does not exist; or, + * - the stream is closed (e.g., `nghttp3_conn_close_stream` is + * called); or, + * - the stream is QUIC flow control blocked (e.g., + * `nghttp3_conn_block_stream` is called); or, + * - the stream is input data blocked (e.g., + * :macro:`NGHTTP3_ERR_WOULDBLOCK` is returned from + * :type:`nghttp3_read_data_callback`); or, + * - the stream is half-closed local (e.g., + * `nghttp3_conn_shutdown_stream_write` is called). + */ +NGHTTP3_EXTERN int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, + int64_t stream_id); + /** * @function * @@ -2198,8 +2141,19 @@ NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn, * prohibited. This works like `nghttp3_conn_block_stream`, but it * cannot be unblocked by `nghttp3_conn_unblock_stream`. */ -NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, - int64_t stream_id); +NGHTTP3_EXTERN void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_shutdown_stream_read` tells the library that + * read-side of stream denoted by |stream_id| is abruptly closed and + * any further incoming data and pending stream data should be + * discarded. + */ +NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, + int64_t stream_id); /** * @function @@ -2215,21 +2169,21 @@ NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn, * * `nghttp3_conn_close_stream` closes stream identified by * |stream_id|. |app_error_code| is the reason of the closure. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + * Stream not found. + * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM` + * A critical stream is closed. + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` + * User callback failed */ NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, uint64_t app_error_code); -/** - * @function - * - * `nghttp3_conn_reset_stream` must be called if stream identified by - * |stream_id| is reset by a remote endpoint. This is required in - * order to cancel QPACK stream. - */ -NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, - int64_t stream_id); - /** * @macrosection * @@ -2237,19 +2191,25 @@ NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, */ /** + * @macro + * * :macro:`NGHTTP3_DATA_FLAG_NONE` indicates no flag set. */ -#define NGHTTP3_DATA_FLAG_NONE 0x00 +#define NGHTTP3_DATA_FLAG_NONE 0x00u /** + * @macro + * * :macro:`NGHTTP3_DATA_FLAG_EOF` indicates that all request or * response body has been provided to the library. It also indicates * that sending side of stream is closed unless * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same time. */ -#define NGHTTP3_DATA_FLAG_EOF 0x01 +#define NGHTTP3_DATA_FLAG_EOF 0x01u /** + * @macro + * * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` indicates that sending * side of stream is not closed even if :macro:`NGHTTP3_DATA_FLAG_EOF` * is set. Usually this flag is used to send trailer fields with @@ -2257,7 +2217,7 @@ NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, * `nghttp3_conn_submit_trailers()` has been called, regardless of * this flag, the submitted trailer fields are sent. */ -#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02 +#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02u /** * @function @@ -2320,7 +2280,7 @@ typedef nghttp3_ssize (*nghttp3_read_data_callback)( * :type:`nghttp3_data_reader` specifies the way how to generate * request or response body. */ -typedef struct { +typedef struct nghttp3_data_reader { /** * :member:`read_data` is a callback function to generate body. */ @@ -2343,24 +2303,6 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_request( nghttp3_conn *conn, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen, const nghttp3_data_reader *dr, void *stream_user_data); -/** - * @function - * - * `nghttp3_conn_submit_push_promise` submits push promise on the - * stream identified by |stream_id|. |stream_id| must be a client - * initiated bidirectional stream. Only server can submit push - * promise. On success, a push ID is assigned to |*ppush_id|. |nva| - * of length |nvlen| specifies HTTP request header fields. In order - * to submit HTTP response, first call - * `nghttp3_conn_bind_push_stream()` and then - * `nghttp3_conn_submit_response()`. - */ -NGHTTP3_EXTERN int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, - int64_t *ppush_id, - int64_t stream_id, - const nghttp3_nv *nva, - size_t nvlen); - /** * @function * @@ -2401,35 +2343,12 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn, const nghttp3_nv *nva, size_t nvlen); -/** - * @function - * - * `nghttp3_conn_bind_push_stream` binds the stream identified by - * |stream_id| to the push identified by |push_id|. |stream_id| must - * be a server initiated unidirectional stream. |push_id| must be - * obtained from `nghttp3_conn_submit_push_promise()`. To send - * response to this push, call `nghttp3_conn_submit_response()`. - */ -NGHTTP3_EXTERN int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, - int64_t push_id, - int64_t stream_id); - -/** - * @function - * - * `nghttp3_conn_cancel_push` cancels the push identified by - * |push_id|. - */ -NGHTTP3_EXTERN int nghttp3_conn_cancel_push(nghttp3_conn *conn, - int64_t push_id); - /** * @function * * `nghttp3_conn_submit_shutdown_notice` notifies the other endpoint - * to stop creating new stream (for server) or push (for client). - * After a couple of RTTs later, call `nghttp3_conn_shutdown` to start - * graceful shutdown. + * to stop creating new stream. After a couple of RTTs later, call + * `nghttp3_conn_shutdown` to start graceful shutdown. */ NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn); @@ -2439,8 +2358,8 @@ NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn); * `nghttp3_conn_shutdown` starts graceful shutdown. It should be * called after `nghttp3_conn_submit_shutdown_notice` and a couple of * RTT. After calling this function, the local endpoint starts - * rejecting new incoming streams (for server) or pushes (for client). - * The existing streams or pushes are processed normally. + * rejecting new incoming streams. The existing streams are processed + * normally. */ NGHTTP3_EXTERN int nghttp3_conn_shutdown(nghttp3_conn *conn); @@ -2459,18 +2378,23 @@ NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, * * `nghttp3_conn_get_frame_payload_left` returns the number of bytes * left to read current frame payload for a stream denoted by - * |stream_id|. If no such stream is found, it returns - * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`. + * |stream_id|. If no such stream is found, it returns 0. + */ +NGHTTP3_EXTERN uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @macrosection + * + * HTTP stream priority flags */ -NGHTTP3_EXTERN int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, - int64_t stream_id); /** * @macro * * :macro:`NGHTTP3_DEFAULT_URGENCY` is the default urgency level. */ -#define NGHTTP3_DEFAULT_URGENCY 1 +#define NGHTTP3_DEFAULT_URGENCY 3 /** * @macro @@ -2518,8 +2442,9 @@ typedef struct nghttp3_pri { * @function * * `nghttp3_conn_get_stream_priority` stores stream priority of a - * stream denoted by |stream_id| into |*dest|. Only server can use - * this function. + * stream denoted by |stream_id| into |*dest|. |stream_id| must + * identify client initiated bidirectional stream. Only server can + * use this function. * * This function must not be called if |conn| is initialized as * client. @@ -2537,15 +2462,22 @@ NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, /** * @function * - * `nghttp3_conn_set_stream_priority` updates stream priority of a - * stream denoted by |stream_id| by the value pointed by |pri|. + * `nghttp3_conn_set_stream_priority` updates priority of a stream + * denoted by |stream_id| with the value pointed by |pri|. + * |stream_id| must identify client initiated bidirectional stream. * - * This function must not be called if |conn| is initialized as - * client. + * Both client and server can update stream priority with this + * function. + * + * If server updates stream priority with this function, it completely + * overrides stream priority set by client and the attempts to update + * priority by client are ignored. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + * |stream_id| is not a client initiated bidirectional stream ID. * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` * Stream not found. * :macro:`NGHTTP3_ERR_NOMEM` @@ -2572,14 +2504,14 @@ nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, * `nghttp3_vec_len` returns the sum of length in |vec| of |cnt| * elements. */ -NGHTTP3_EXTERN size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt); +NGHTTP3_EXTERN uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt); /** * @function * * `nghttp3_check_header_name` returns nonzero if HTTP header field * name |name| of length |len| is valid according to - * http://tools.ietf.org/html/rfc7230#section-3.2 + * :rfc:`7230#section-3.2`. * * Because this is a header field name in HTTP/3, the upper cased * alphabet is treated as error. @@ -2591,7 +2523,7 @@ NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len); * * `nghttp3_check_header_value` returns nonzero if HTTP header field * value |value| of length |len| is valid according to - * http://tools.ietf.org/html/rfc7230#section-3.2 + * :rfc:`7230#section-3.2`. */ NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len); @@ -2614,6 +2546,12 @@ NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, size_t len); +/** + * @macrosection + * + * nghttp3_info flags + */ + /** * @macro * @@ -2627,7 +2565,7 @@ NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest, * :type:`nghttp3_info` is what `nghttp3_version()` returns. It holds * information about the particular nghttp3 version. */ -typedef struct { +typedef struct nghttp3_info { /** * :member:`age` is the age of this struct. This instance of * nghttp3 sets it to :macro:`NGHTTP3_VERSION_AGE` but a future @@ -2657,7 +2595,49 @@ typedef struct { * met, this function will return a ``NULL``. Pass in 0 to skip the * version checking. */ -NGHTTP3_EXTERN nghttp3_info *nghttp3_version(int least_version); +NGHTTP3_EXTERN const nghttp3_info *nghttp3_version(int least_version); + +/** + * @function + * + * `nghttp3_err_is_fatal` returns nonzero if |liberr| is a fatal + * error. |liberr| must be one of nghttp3 library error codes (which + * is defined as NGHTTP3_ERR_* macro, such as + * :macro:`NGHTTP3_ERR_NOMEM`). + */ +NGHTTP3_EXTERN int nghttp3_err_is_fatal(int liberr); + +/* + * Versioned function wrappers + */ + +/* + * `nghttp3_settings_default` is a wrapper around + * `nghttp3_settings_default_versioned` to set the correct struct + * version. + */ +#define nghttp3_settings_default(SETTINGS) \ + nghttp3_settings_default_versioned(NGHTTP3_SETTINGS_VERSION, (SETTINGS)) + +/* + * `nghttp3_conn_client_new` is a wrapper around + * `nghttp3_conn_client_new_versioned` to set the correct struct + * version. + */ +#define nghttp3_conn_client_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \ + nghttp3_conn_client_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \ + (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \ + (SETTINGS), (MEM), (USER_DATA)) + +/* + * `nghttp3_conn_server_new` is a wrapper around + * `nghttp3_conn_server_new_versioned` to set the correct struct + * version. + */ +#define nghttp3_conn_server_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA) \ + nghttp3_conn_server_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION, \ + (CALLBACKS), NGHTTP3_SETTINGS_VERSION, \ + (SETTINGS), (MEM), (USER_DATA)) #ifdef __cplusplus } diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h index 69d29e9f140c33..bc57eb2cfcf2d6 100644 --- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h @@ -31,7 +31,7 @@ * * Version number of the nghttp3 library release. */ -#define NGHTTP3_VERSION "0.1.0-DEV" +#define NGHTTP3_VERSION "0.7.0" /** * @macro @@ -41,6 +41,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGHTTP3_VERSION_NUM 0x000100 +#define NGHTTP3_VERSION_NUM 0x000700 #endif /* NGHTTP3_VERSION_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c new file mode 100644 index 00000000000000..e134d0f4dceb75 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.c @@ -0,0 +1,91 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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. + */ +#include "nghttp3_balloc.h" + +#include + +#include "nghttp3_mem.h" + +void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen, + const nghttp3_mem *mem) { + assert((blklen & 0xfu) == 0); + + balloc->mem = mem; + balloc->blklen = blklen; + balloc->head = NULL; + nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0); +} + +void nghttp3_balloc_free(nghttp3_balloc *balloc) { + if (balloc == NULL) { + return; + } + + nghttp3_balloc_clear(balloc); +} + +void nghttp3_balloc_clear(nghttp3_balloc *balloc) { + nghttp3_memblock_hd *p, *next; + + for (p = balloc->head; p; p = next) { + next = p->next; + nghttp3_mem_free(balloc->mem, p); + } + + balloc->head = NULL; + nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0); +} + +int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n) { + uint8_t *p; + nghttp3_memblock_hd *hd; + + assert(n <= balloc->blklen); + + if (nghttp3_buf_left(&balloc->buf) < n) { + p = nghttp3_mem_malloc(balloc->mem, sizeof(nghttp3_memblock_hd) + 0x10u + + balloc->blklen); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + hd = (nghttp3_memblock_hd *)(void *)p; + hd->next = balloc->head; + balloc->head = hd; + nghttp3_buf_wrap_init( + &balloc->buf, + (uint8_t *)(((uintptr_t)p + sizeof(nghttp3_memblock_hd) + 0xfu) & + ~(uintptr_t)0xfu), + balloc->blklen); + } + + assert(((uintptr_t)balloc->buf.last & 0xfu) == 0); + + *pbuf = balloc->buf.last; + balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu; + + return 0; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h new file mode 100644 index 00000000000000..e02f61d16b5763 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_balloc.h @@ -0,0 +1,92 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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 NGHTTP3_BALLOC_H +#define NGHTTP3_BALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_buf.h" + +typedef struct nghttp3_memblock_hd nghttp3_memblock_hd; + +/* + * nghttp3_memblock_hd is the header of memory block. + */ +struct nghttp3_memblock_hd { + nghttp3_memblock_hd *next; +}; + +/* + * nghttp3_balloc is a custom memory allocator. It allocates |blklen| + * bytes of memory at once on demand, and returns its slice when the + * allocation is requested. + */ +typedef struct nghttp3_balloc { + /* mem is the underlying memory allocator. */ + const nghttp3_mem *mem; + /* blklen is the size of memory block. */ + size_t blklen; + /* head points to the list of memory block allocated so far. */ + nghttp3_memblock_hd *head; + /* buf wraps the current memory block for allocation requests. */ + nghttp3_buf buf; +} nghttp3_balloc; + +/* + * nghttp3_balloc_init initializes |balloc| with |blklen| which is the + * size of memory block. + */ +void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen, + const nghttp3_mem *mem); + +/* + * nghttp3_balloc_free releases all allocated memory blocks. + */ +void nghttp3_balloc_free(nghttp3_balloc *balloc); + +/* + * nghttp3_balloc_get allocates |n| bytes of memory and assigns its + * pointer to |*pbuf|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n); + +/* + * nghttp3_balloc_clear releases all allocated memory blocks and + * initializes its state. + */ +void nghttp3_balloc_clear(nghttp3_balloc *balloc); + +#endif /* NGHTTP3_BALLOC_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c index 2f9ce7b10ca89e..1fbb72c98af2f2 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c @@ -34,6 +34,10 @@ #include "nghttp3_conv.h" #include "nghttp3_http.h" +/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the + dynamic table capacity that QPACK encoder is willing to use. */ +#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096 + /* * conn_remote_stream_uni returns nonzero if |stream_id| is remote * unidirectional stream ID. @@ -62,15 +66,16 @@ static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) { return 0; } -static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream) { +static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream, + int fin) { int rv; if (!conn->callbacks.end_headers) { return 0; } - rv = conn->callbacks.end_headers(conn, stream->node.nid.id, conn->user_data, - stream->user_data); + rv = conn->callbacks.end_headers(conn, stream->node.nid.id, fin, + conn->user_data, stream->user_data); if (rv != 0) { /* TODO Allow ignore headers */ return NGHTTP3_ERR_CALLBACK_FAILURE; @@ -97,52 +102,16 @@ static int conn_call_begin_trailers(nghttp3_conn *conn, return 0; } -static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream) { +static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream, + int fin) { int rv; if (!conn->callbacks.end_trailers) { return 0; } - rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, conn->user_data, - stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_begin_push_promise(nghttp3_conn *conn, - nghttp3_stream *stream, - int64_t push_id) { - int rv; - - if (!conn->callbacks.begin_push_promise) { - return 0; - } - - rv = conn->callbacks.begin_push_promise(conn, stream->node.nid.id, push_id, - conn->user_data, stream->user_data); - if (rv != 0) { - /* TODO Allow ignore headers */ - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_end_push_promise(nghttp3_conn *conn, - nghttp3_stream *stream, int64_t push_id) { - int rv; - - if (!conn->callbacks.end_push_promise) { - return 0; - } - - rv = conn->callbacks.end_push_promise(conn, stream->node.nid.id, push_id, - conn->user_data, stream->user_data); + rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, fin, + conn->user_data, stream->user_data); if (rv != 0) { /* TODO Allow ignore headers */ return NGHTTP3_ERR_CALLBACK_FAILURE; @@ -167,36 +136,16 @@ static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) { return 0; } -static int conn_call_cancel_push(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.cancel_push) { - return 0; - } - - rv = conn->callbacks.cancel_push( - conn, push_id, stream ? stream->node.nid.id : -1, conn->user_data, - stream ? stream->user_data : NULL); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int conn_call_send_stop_sending(nghttp3_conn *conn, - nghttp3_stream *stream, - uint64_t app_error_code) { +static int conn_call_stop_sending(nghttp3_conn *conn, nghttp3_stream *stream, + uint64_t app_error_code) { int rv; - if (!conn->callbacks.send_stop_sending) { + if (!conn->callbacks.stop_sending) { return 0; } - rv = conn->callbacks.send_stop_sending(conn, stream->node.nid.id, - app_error_code, conn->user_data, - stream->user_data); + rv = conn->callbacks.stop_sending(conn, stream->node.nid.id, app_error_code, + conn->user_data, stream->user_data); if (rv != 0) { return NGHTTP3_ERR_CALLBACK_FAILURE; } @@ -221,23 +170,6 @@ static int conn_call_reset_stream(nghttp3_conn *conn, nghttp3_stream *stream, return 0; } -static int conn_call_push_stream(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - - if (!conn->callbacks.push_stream) { - return 0; - } - - rv = conn->callbacks.push_stream(conn, push_id, stream->node.nid.id, - conn->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - - return 0; -} - static int conn_call_deferred_consume(nghttp3_conn *conn, nghttp3_stream *stream, size_t nconsumed) { @@ -278,37 +210,40 @@ static int cycle_less(const nghttp3_pq_entry *lhsx, return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP; } -static int conn_new(nghttp3_conn **pconn, int server, - const nghttp3_callbacks *callbacks, +static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, + const nghttp3_callbacks *callbacks, int settings_version, const nghttp3_settings *settings, const nghttp3_mem *mem, void *user_data) { int rv; nghttp3_conn *conn; size_t i; + (void)callbacks_version; + (void)settings_version; + + if (mem == NULL) { + mem = nghttp3_mem_default(); + } conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn)); if (conn == NULL) { return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_map_init(&conn->streams, mem); - if (rv != 0) { - goto streams_init_fail; - } + nghttp3_objalloc_init(&conn->out_chunk_objalloc, + NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem); + nghttp3_objalloc_stream_init(&conn->stream_objalloc, 64, mem); - rv = nghttp3_map_init(&conn->pushes, mem); - if (rv != 0) { - goto pushes_init_fail; - } + nghttp3_map_init(&conn->streams, mem); rv = nghttp3_qpack_decoder_init(&conn->qdec, - settings->qpack_max_table_capacity, + settings->qpack_max_dtable_capacity, settings->qpack_blocked_streams, mem); if (rv != 0) { goto qdec_init_fail; } - rv = nghttp3_qpack_encoder_init(&conn->qenc, 0, 0, mem); + rv = nghttp3_qpack_encoder_init( + &conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem); if (rv != 0) { goto qenc_init_fail; } @@ -319,18 +254,13 @@ static int conn_new(nghttp3_conn **pconn, int server, nghttp3_pq_init(&conn->sched[i].spq, cycle_less, mem); } - rv = nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem); - if (rv != 0) { - goto remote_bidi_idtr_init_fail; - } - - rv = nghttp3_gaptr_init(&conn->remote.uni.push_idtr, mem); - if (rv != 0) { - goto push_idtr_init_fail; - } + nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem); conn->callbacks = *callbacks; conn->local.settings = *settings; + if (!server) { + conn->local.settings.enable_connect_protocol = 0; + } nghttp3_settings_default(&conn->remote.settings); conn->mem = mem; conn->user_data = user_data; @@ -338,52 +268,50 @@ static int conn_new(nghttp3_conn **pconn, int server, conn->server = server; conn->rx.goaway_id = NGHTTP3_VARINT_MAX + 1; conn->tx.goaway_id = NGHTTP3_VARINT_MAX + 1; - conn->rx.max_stream_id_bidi = 0; - conn->rx.max_push_id = 0; + conn->rx.max_stream_id_bidi = -4; *pconn = conn; return 0; -push_idtr_init_fail: - nghttp3_idtr_free(&conn->remote.bidi.idtr); -remote_bidi_idtr_init_fail: - nghttp3_qpack_encoder_free(&conn->qenc); qenc_init_fail: nghttp3_qpack_decoder_free(&conn->qdec); qdec_init_fail: - nghttp3_map_free(&conn->pushes); -pushes_init_fail: nghttp3_map_free(&conn->streams); -streams_init_fail: + nghttp3_objalloc_free(&conn->stream_objalloc); + nghttp3_objalloc_free(&conn->out_chunk_objalloc); nghttp3_mem_free(mem, conn); return rv; } -int nghttp3_conn_client_new(nghttp3_conn **pconn, - const nghttp3_callbacks *callbacks, - const nghttp3_settings *settings, - const nghttp3_mem *mem, void *user_data) { +int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, + int callbacks_version, + const nghttp3_callbacks *callbacks, + int settings_version, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, /* server = */ 0, callbacks, settings, mem, user_data); + rv = conn_new(pconn, /* server = */ 0, callbacks_version, callbacks, + settings_version, settings, mem, user_data); if (rv != 0) { return rv; } - (*pconn)->remote.uni.unsent_max_pushes = settings->max_pushes; - return 0; } -int nghttp3_conn_server_new(nghttp3_conn **pconn, - const nghttp3_callbacks *callbacks, - const nghttp3_settings *settings, - const nghttp3_mem *mem, void *user_data) { +int nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, + int callbacks_version, + const nghttp3_callbacks *callbacks, + int settings_version, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, /* server = */ 1, callbacks, settings, mem, user_data); + rv = conn_new(pconn, /* server = */ 1, callbacks_version, callbacks, + settings_version, settings, mem, user_data); if (rv != 0) { return rv; } @@ -391,17 +319,8 @@ int nghttp3_conn_server_new(nghttp3_conn **pconn, return 0; } -static int free_push_promise(nghttp3_map_entry *ent, void *ptr) { - nghttp3_push_promise *pp = nghttp3_struct_of(ent, nghttp3_push_promise, me); - const nghttp3_mem *mem = ptr; - - nghttp3_push_promise_del(pp, mem); - - return 0; -} - -static int free_stream(nghttp3_map_entry *ent, void *ptr) { - nghttp3_stream *stream = nghttp3_struct_of(ent, nghttp3_stream, me); +static int free_stream(void *data, void *ptr) { + nghttp3_stream *stream = data; (void)ptr; @@ -420,8 +339,6 @@ void nghttp3_conn_del(nghttp3_conn *conn) { nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem); nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem); - nghttp3_gaptr_free(&conn->remote.uni.push_idtr); - nghttp3_idtr_free(&conn->remote.bidi.idtr); for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { @@ -433,15 +350,30 @@ void nghttp3_conn_del(nghttp3_conn *conn) { nghttp3_qpack_encoder_free(&conn->qenc); nghttp3_qpack_decoder_free(&conn->qdec); - nghttp3_map_each_free(&conn->pushes, free_push_promise, (void *)conn->mem); - nghttp3_map_free(&conn->pushes); - nghttp3_map_each_free(&conn->streams, free_stream, NULL); nghttp3_map_free(&conn->streams); + nghttp3_objalloc_free(&conn->stream_objalloc); + nghttp3_objalloc_free(&conn->out_chunk_objalloc); + nghttp3_mem_free(conn->mem, conn); } +static int conn_bidi_idtr_open(nghttp3_conn *conn, int64_t stream_id) { + int rv; + + rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id); + if (rv != 0) { + return rv; + } + + if (nghttp3_ksl_len(&conn->remote.bidi.idtr.gap.gap) > 32) { + nghttp3_gaptr_drop_first_gap(&conn->remote.bidi.idtr.gap); + } + + return 0; +} + nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, const uint8_t *src, size_t srclen, int fin) { @@ -455,8 +387,18 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, /* QUIC transport ensures that this is new stream. */ if (conn->server) { if (nghttp3_client_stream_bidi(stream_id)) { - rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id); - assert(rv == 0); + rv = conn_bidi_idtr_open(conn, stream_id); + if (rv != 0) { + if (nghttp3_err_is_fatal(rv)) { + return rv; + } + + /* Ignore return value. We might drop the first gap if there + are many gaps if QUIC stack allows too many holes in stream + ID space. idtr is used to decide whether PRIORITY_UPDATE + frame should be ignored or not and the frame is optional. + Ignoring them causes no harm. */ + } conn->rx.max_stream_id_bidi = nghttp3_max(conn->rx.max_stream_id_bidi, stream_id); @@ -566,12 +508,7 @@ static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream, rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE; break; case NGHTTP3_STREAM_TYPE_PUSH: - if (conn->server) { - return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; - } - stream->type = NGHTTP3_STREAM_TYPE_PUSH; - rstate->state = NGHTTP3_PUSH_STREAM_STATE_PUSH_ID; - break; + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) { return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; @@ -603,7 +540,6 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, int fin) { nghttp3_ssize nread = 0; nghttp3_ssize nconsumed = 0; - size_t push_nproc; int rv; assert(srclen || fin); @@ -646,13 +582,6 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, } nconsumed = nghttp3_conn_read_control(conn, stream, src, srclen); break; - case NGHTTP3_STREAM_TYPE_PUSH: - if (fin) { - stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF; - } - nconsumed = - nghttp3_conn_read_push(conn, &push_nproc, stream, src, srclen, fin); - break; case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: if (fin) { return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; @@ -668,8 +597,7 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, case NGHTTP3_STREAM_TYPE_UNKNOWN: nconsumed = (nghttp3_ssize)srclen; - rv = conn_call_send_stop_sending(conn, stream, - NGHTTP3_H3_STREAM_CREATION_ERROR); + rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR); if (rv != 0) { return rv; } @@ -701,6 +629,8 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, size_t nconsumed = 0; int busy = 0; size_t len; + const uint8_t *pri_field_value = NULL; + size_t pri_field_valuelen = 0; assert(srclen); @@ -753,12 +683,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, } switch (rstate->fr.hd.type) { - case NGHTTP3_FRAME_CANCEL_PUSH: - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - rstate->state = NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH; - break; case NGHTTP3_FRAME_SETTINGS: /* SETTINGS frame might be empty. */ if (rstate->left == 0) { @@ -782,6 +706,19 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, } rstate->state = NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID; break; + case NGHTTP3_FRAME_PRIORITY_UPDATE: + if (!conn->server) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID; + break; + case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID: + /* We do not support push */ + return NGHTTP3_ERR_H3_ID_ERROR; + case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */ case NGHTTP3_FRAME_DATA: case NGHTTP3_FRAME_HEADERS: case NGHTTP3_FRAME_PUSH_PROMISE: @@ -797,34 +734,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, break; } break; - case NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - assert(len > 0); - nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - return (nghttp3_ssize)nconsumed; - } - rstate->fr.cancel_push.push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - if (conn->server) { - rv = nghttp3_conn_on_server_cancel_push(conn, &rstate->fr.cancel_push); - } else { - rv = nghttp3_conn_on_client_cancel_push(conn, &rstate->fr.cancel_push); - } - if (rv != 0) { - return rv; - } - - nghttp3_stream_read_state_reset(rstate); - break; case NGHTTP3_CTRL_STREAM_STATE_SETTINGS: for (; p != end;) { if (rstate->left == 0) { @@ -953,7 +862,7 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } - if (conn->server && !nghttp3_client_stream_bidi(rvint->acc)) { + if (!conn->server && !nghttp3_client_stream_bidi(rvint->acc)) { return NGHTTP3_ERR_H3_ID_ERROR; } if (conn->rx.goaway_id < rvint->acc) { @@ -964,6 +873,14 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, conn->rx.goaway_id = rvint->acc; nghttp3_varint_read_state_reset(rvint); + if (conn->callbacks.shutdown) { + rv = + conn->callbacks.shutdown(conn, conn->rx.goaway_id, conn->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + nghttp3_stream_read_state_reset(rstate); break; case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID: @@ -982,13 +899,105 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } - if (conn->local.uni.max_pushes > (uint64_t)rvint->acc) { + if (conn->local.uni.max_pushes > (uint64_t)rvint->acc + 1) { return NGHTTP3_ERR_H3_FRAME_ERROR; } - conn->local.uni.max_pushes = (uint64_t)rvint->acc; + conn->local.uni.max_pushes = (uint64_t)rvint->acc + 1; + nghttp3_varint_read_state_reset(rvint); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID: + /* server side only */ + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + rstate->fr.priority_update.pri_elem_id = rvint->acc; nghttp3_varint_read_state_reset(rvint); + if (rstate->left == 0) { + rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY; + rstate->fr.priority_update.pri.inc = 0; + + rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update); + if (rv != 0) { + return rv; + } + + nghttp3_stream_read_state_reset(rstate); + break; + } + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE; + + /* Fall through */ + case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE: + /* We need to buffer Priority Field Value because it might be + fragmented. */ + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + if (conn->rx.pri_fieldbuflen == 0 && rstate->left == (int64_t)len) { + /* Everything is in the input buffer. Apply same length + limit we impose when buffering the field. */ + if (len > sizeof(conn->rx.pri_fieldbuf)) { + busy = 1; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + } + + pri_field_value = p; + pri_field_valuelen = len; + } else if (len + conn->rx.pri_fieldbuflen > + sizeof(conn->rx.pri_fieldbuf)) { + busy = 1; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + } else { + memcpy(conn->rx.pri_fieldbuf + conn->rx.pri_fieldbuflen, p, len); + conn->rx.pri_fieldbuflen += len; + + if (rstate->left == (int64_t)len) { + pri_field_value = conn->rx.pri_fieldbuf; + pri_field_valuelen = conn->rx.pri_fieldbuflen; + } + } + + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + return (nghttp3_ssize)nconsumed; + } + + rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY; + rstate->fr.priority_update.pri.inc = 0; + + if (nghttp3_http_parse_priority(&rstate->fr.priority_update.pri, + pri_field_value, + pri_field_valuelen) != 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update); + if (rv != 0) { + return rv; + } + + conn->rx.pri_fieldbuflen = 0; + nghttp3_stream_read_state_reset(rstate); break; case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME: @@ -1012,365 +1021,34 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } -nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, - nghttp3_stream *stream, const uint8_t *src, - size_t srclen, int fin) { - const uint8_t *p = src, *end = src ? src + srclen : src; +static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) { + int bidi = nghttp3_client_stream_bidi(stream->node.nid.id); int rv; - nghttp3_stream_read_state *rstate = &stream->rstate; - nghttp3_varint_read_state *rvint = &rstate->rvint; - nghttp3_ssize nread; - size_t nconsumed = 0; - int busy = 0; - size_t len; - int64_t push_id; - - if (stream->flags & (NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED | - NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED)) { - *pnproc = 0; - if (srclen == 0) { - return 0; - } + rv = conn_call_deferred_consume(conn, stream, + nghttp3_stream_get_buffered_datalen(stream)); + if (rv != 0) { + return rv; + } - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (bidi && conn->callbacks.stream_close) { + rv = conn->callbacks.stream_close(conn, stream->node.nid.id, + stream->error_code, conn->user_data, + stream->user_data); if (rv != 0) { - return rv; + return NGHTTP3_ERR_CALLBACK_FAILURE; } - return 0; } - for (; p != end || busy;) { - busy = 0; - switch (rstate->state) { - case NGHTTP3_PUSH_STREAM_STATE_PUSH_ID: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } + rv = nghttp3_map_remove(&conn->streams, + (nghttp3_map_key_type)stream->node.nid.id); - push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); + assert(0 == rv); - rv = nghttp3_conn_on_stream_push_id(conn, stream, push_id); - if (rv != 0) { - if (rv == NGHTTP3_ERR_IGNORE_STREAM) { - rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_REST; - break; - } - return (nghttp3_ssize)rv; - } + nghttp3_stream_del(stream); - rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE; - - if (stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED) { - if (p != end) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (end == p) { - goto almost_done; - } - - /* Fall through */ - case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - rstate->fr.hd.type = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH; - if (p == end) { - goto almost_done; - } - /* Fall through */ - case NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH: - assert(end - p > 0); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - if (rvint->left) { - goto almost_done; - } - - rstate->left = rstate->fr.hd.length = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - switch (rstate->fr.hd.type) { - case NGHTTP3_FRAME_DATA: - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); - if (rv != 0) { - return rv; - } - /* DATA frame might be empty. */ - if (rstate->left == 0) { - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_DATA_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - } - rstate->state = NGHTTP3_PUSH_STREAM_STATE_DATA; - break; - case NGHTTP3_FRAME_HEADERS: - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN); - if (rv != 0) { - return rv; - } - if (rstate->left == 0) { - rv = nghttp3_stream_empty_headers_allowed(stream); - if (rv != 0) { - return rv; - } - - rv = nghttp3_stream_transit_rx_http_state( - stream, NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = conn_call_begin_headers(conn, stream); - break; - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_begin_trailers(conn, stream); - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - rstate->state = NGHTTP3_PUSH_STREAM_STATE_HEADERS; - break; - case NGHTTP3_FRAME_PUSH_PROMISE: - case NGHTTP3_FRAME_CANCEL_PUSH: - case NGHTTP3_FRAME_SETTINGS: - case NGHTTP3_FRAME_GOAWAY: - case NGHTTP3_FRAME_MAX_PUSH_ID: - case NGHTTP3_H2_FRAME_PRIORITY: - case NGHTTP3_H2_FRAME_PING: - case NGHTTP3_H2_FRAME_WINDOW_UPDATE: - case NGHTTP3_H2_FRAME_CONTINUATION: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - default: - /* TODO Handle reserved frame type */ - busy = 1; - rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME; - break; - } - break; - case NGHTTP3_PUSH_STREAM_STATE_DATA: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - rv = nghttp3_conn_on_data(conn, stream, p, len); - if (rv != 0) { - return rv; - } - - p += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - goto almost_done; - } - - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_DATA_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_HEADERS: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = nghttp3_http_on_response_headers(&stream->rx.http); - if (rv != 0) { - return rv; - } - - rv = conn_call_end_headers(conn, stream); - break; - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_end_trailers(conn, stream); - break; - default: - /* Unreachable */ - assert(0); - } - - if (rv != 0) { - return rv; - } - - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - p += len; - nconsumed += len; - rstate->left -= (int64_t)len; - - if (rstate->left) { - goto almost_done; - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: - nconsumed += (size_t)(end - p); - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - } - -almost_done: - if (fin) { - switch (rstate->state) { - case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: - if (rvint->left) { - return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; - } - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_MSG_END); - if (rv != 0) { - return rv; - } - rv = conn_call_end_stream(conn, stream); - if (rv != 0) { - return rv; - } - break; - case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: - break; - default: - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - } - - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; -} - -static void conn_delete_push_promise(nghttp3_conn *conn, - nghttp3_push_promise *pp) { - int rv; - - rv = nghttp3_map_remove(&conn->pushes, (key_type)pp->node.nid.id); - assert(0 == rv); - - if (!conn->server && - !(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED)) { - ++conn->remote.uni.unsent_max_pushes; - } - - nghttp3_push_promise_del(pp, conn->mem); -} - -static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) { - int bidi_or_push = nghttp3_stream_bidi_or_push(stream); - int rv; - - if (bidi_or_push) { - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } - } - - rv = conn_call_deferred_consume(conn, stream, - nghttp3_stream_get_buffered_datalen(stream)); - if (rv != 0) { - return rv; - } - - if (bidi_or_push && conn->callbacks.stream_close) { - rv = conn->callbacks.stream_close(conn, stream->node.nid.id, - stream->error_code, conn->user_data, - stream->user_data); - if (rv != 0) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - } - - rv = nghttp3_map_remove(&conn->streams, (key_type)stream->node.nid.id); - - assert(0 == rv); - - if (stream->pp) { - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - - conn_delete_push_promise(conn, stream->pp); - } - - nghttp3_stream_del(stream); - - return 0; -} + return 0; +} static int conn_process_blocked_stream_data(nghttp3_conn *conn, nghttp3_stream *stream) { @@ -1380,21 +1058,19 @@ static int conn_process_blocked_stream_data(nghttp3_conn *conn, int rv; size_t len; + assert(nghttp3_client_stream_bidi(stream->node.nid.id)); + for (;;) { len = nghttp3_ringbuf_len(&stream->inq); if (len == 0) { break; } + buf = nghttp3_ringbuf_get(&stream->inq, 0); - if (nghttp3_stream_uni(stream->node.nid.id)) { - nconsumed = nghttp3_conn_read_push( - conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), - len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); - } else { - nconsumed = nghttp3_conn_read_bidi( - conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), - len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); - } + + nconsumed = nghttp3_conn_read_bidi( + conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), + len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); if (nconsumed < 0) { return (int)nconsumed; } @@ -1468,8 +1144,14 @@ nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen); } +static nghttp3_tnode *stream_get_sched_node(nghttp3_stream *stream) { + return &stream->node; +} + static int conn_update_stream_priority(nghttp3_conn *conn, nghttp3_stream *stream, uint8_t pri) { + assert(nghttp3_client_stream_bidi(stream->node.nid.id)); + if (stream->node.pri == pri) { return 0; } @@ -1478,8 +1160,6 @@ static int conn_update_stream_priority(nghttp3_conn *conn, stream->node.pri = pri; - assert(nghttp3_stream_bidi_or_push(stream)); - if (nghttp3_stream_require_schedule(stream)) { return nghttp3_conn_schedule_stream(conn, stream); } @@ -1498,10 +1178,12 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, size_t nconsumed = 0; int busy = 0; size_t len; - nghttp3_push_promise *pp; - nghttp3_push_promise fake_pp = {{0}, {{0}, 0, {0}, 0, 0, 0}, {0}, NULL, -1, - 0}; - nghttp3_frame_entry frent; + + if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) { + *pnproc = srclen; + + return (nghttp3_ssize)srclen; + } if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { *pnproc = 0; @@ -1614,23 +1296,13 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS; break; - case NGHTTP3_FRAME_PUSH_PROMISE: - if (conn->server) { - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; - } - - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - /* No stream->rx.hstate change with PUSH_PROMISE */ - - rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID; - break; + case NGHTTP3_FRAME_PUSH_PROMISE: /* We do not support push */ case NGHTTP3_FRAME_CANCEL_PUSH: case NGHTTP3_FRAME_SETTINGS: case NGHTTP3_FRAME_GOAWAY: case NGHTTP3_FRAME_MAX_PUSH_ID: + case NGHTTP3_FRAME_PRIORITY_UPDATE: + case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID: case NGHTTP3_H2_FRAME_PRIORITY: case NGHTTP3_H2_FRAME_PING: case NGHTTP3_H2_FRAME_WINDOW_UPDATE: @@ -1662,199 +1334,15 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, nghttp3_stream_read_state_reset(rstate); break; - case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), - (int64_t)len == rstate->left); - if (nread < 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - if (rvint->left) { - goto almost_done; - } - - rstate->fr.push_promise.push_id = rvint->acc; - nghttp3_varint_read_state_reset(rvint); - - if (rstate->left == 0) { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - rv = nghttp3_conn_on_push_promise_push_id( - conn, rstate->fr.push_promise.push_id, stream); - if (rv != 0) { - if (rv == NGHTTP3_ERR_IGNORE_PUSH_PROMISE) { - rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE; - if (p == end) { - goto almost_done; - } - break; - } - - return rv; - } - - rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE; - - if (p == end) { - goto almost_done; - } - /* Fall through */ - case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE: - pp = - nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); - - assert(pp); - - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, pp, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - rv = nghttp3_http_on_request_headers(&pp->http); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED; - - rv = conn_call_end_push_promise(conn, stream, pp->node.nid.id); - if (rv != 0) { - return rv; - } - - /* Find pp again because application might call - nghttp3_conn_cancel_push and it may delete pp. */ - pp = - nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); - if (!pp) { - nghttp3_stream_read_state_reset(rstate); - break; - } - - if (!pp->stream && (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED)) { - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL) { - rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - } - - conn_delete_push_promise(conn, pp); - - nghttp3_stream_read_state_reset(rstate); - break; - } - - if (pp->stream) { - ++conn->remote.uni.unsent_max_pushes; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; - - if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { - assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); - - rv = conn_call_push_stream(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - - pp->stream->flags &= - (uint16_t)~NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - rv = conn_process_blocked_stream_data(conn, pp->stream); - if (rv != 0) { - return rv; - } - } - } - - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE: + case NGHTTP3_REQ_STREAM_STATE_HEADERS: len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, &fake_pp, p, len, + nread = nghttp3_conn_on_headers(conn, stream, p, len, (int64_t)len == rstate->left); if (nread < 0) { - return nread; - } - - p += nread; - nconsumed += (size_t)nread; - rstate->left -= nread; - - if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { - if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { - rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); - if (rv != 0) { - return rv; - } - } - *pnproc = (size_t)(p - src); - return (nghttp3_ssize)nconsumed; - } - - if (rstate->left) { - goto almost_done; - } - - pp = - nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); - if (pp) { - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED; - - if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && - conn->tx.goaway_id <= pp->node.nid.id) { - if (pp->stream) { - rv = nghttp3_conn_reject_push_stream(conn, pp->stream); - if (rv != 0) { - return rv; - } - } else { - frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; - frent.fr.cancel_push.push_id = pp->node.nid.id; - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - conn_delete_push_promise(conn, pp); - } + if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { + goto http_header_error; } - } - nghttp3_stream_read_state_reset(rstate); - break; - case NGHTTP3_REQ_STREAM_STATE_HEADERS: - len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); - nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, - (int64_t)len == rstate->left); - if (nread < 0) { return nread; } @@ -1891,9 +1379,14 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, default: /* Unreachable */ assert(0); + abort(); } if (rv != 0) { + if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { + goto http_header_error; + } + return rv; } @@ -1901,7 +1394,10 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: /* Only server utilizes priority information to schedule streams. */ - if (conn->server) { + if (conn->server && + (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_PRIORITY) && + !(stream->flags & NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED) && + !(stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET)) { rv = conn_update_stream_priority(conn, stream, stream->rx.http.pri); if (rv != 0) { return rv; @@ -1909,11 +1405,11 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, } /* fall through */ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - rv = conn_call_end_headers(conn, stream); + rv = conn_call_end_headers(conn, stream, p == end && fin); break; case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - rv = conn_call_end_trailers(conn, stream); + rv = conn_call_end_trailers(conn, stream, p == end && fin); break; default: /* Unreachable */ @@ -1924,11 +1420,30 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, return rv; } - rv = nghttp3_stream_transit_rx_http_state(stream, - NGHTTP3_HTTP_EVENT_HEADERS_END); - assert(0 == rv); + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + + break; + + http_header_error: + stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR; + + busy = 1; + rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST; + + rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); + if (rv != 0) { + return rv; + } + + rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); + if (rv != 0) { + return rv; + } - nghttp3_stream_read_state_reset(rstate); break; case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME: len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); @@ -1944,7 +1459,7 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, break; case NGHTTP3_REQ_STREAM_STATE_IGN_REST: nconsumed += (size_t)(end - p); - *pnproc = (size_t)(p - src); + *pnproc = (size_t)(end - src); return (nghttp3_ssize)nconsumed; } } @@ -1999,144 +1514,6 @@ int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, return 0; } -static int push_idtr_push(nghttp3_gaptr *push_idtr, int64_t push_id) { - int rv; - - rv = nghttp3_gaptr_push(push_idtr, (uint64_t)push_id, 1); - if (rv != 0) { - return rv; - } - - /* Server SHOULD use push ID sequentially, but even if it does, we - might see gaps in push IDs because we might stop reading stream - which ignores PUSH_PROMISE. In order to limit the number of - gaps, drop earlier gaps if certain limit is reached. This makes - otherwise valid push ignored.*/ - if (nghttp3_ksl_len(&push_idtr->gap) > 100) { - nghttp3_gaptr_drop_first_gap(push_idtr); - } - - return 0; -} - -int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream) { - int rv; - nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; - nghttp3_push_promise *pp; - - if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp) { - if ((pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_BOUND) || - (pp->stream_id != -1 && pp->stream_id != stream->node.nid.id)) { - return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; - } - if (pp->stream) { - assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); - /* Push unidirectional stream has already been received and - blocked */ - } else if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED) { - /* We will call begin_push_promise callback even if push is - cancelled */ - } else { - return NGHTTP3_ERR_H3_FRAME_ERROR; - } - - assert(pp->stream_id == -1); - - pp->stream_id = stream->node.nid.id; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; - } else if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)push_id, 1)) { - return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; - } else { - rv = push_idtr_push(push_idtr, push_id); - if (rv != 0) { - return rv; - } - - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node); - if (rv != 0) { - return rv; - } - } - - conn->rx.max_push_id = nghttp3_max(conn->rx.max_push_id, push_id); - - if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && - conn->tx.goaway_id <= push_id) { - return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; - } - - rv = conn_call_begin_push_promise(conn, stream, push_id); - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr) { - nghttp3_push_promise *pp; - nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; - int rv; - - if (conn->remote.uni.max_pushes <= (uint64_t)fr->push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, fr->push_id); - if (pp == NULL) { - if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)fr->push_id, 1)) { - /* push is already cancelled or server is misbehaving */ - return 0; - } - - /* We have not received PUSH_PROMISE yet */ - rv = push_idtr_push(push_idtr, fr->push_id); - if (rv != 0) { - return rv; - } - - conn->rx.max_push_id = nghttp3_max(conn->rx.max_push_id, fr->push_id); - - rv = nghttp3_conn_create_push_promise(conn, &pp, fr->push_id, NULL); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; - - /* cancel_push callback will be called after PUSH_PROMISE frame is - completely received. */ - - return 0; - } - - if (pp->stream) { - return 0; - } - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { - rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); - if (rv != 0) { - return rv; - } - - conn_delete_push_promise(conn, pp); - - return 0; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; - - return 0; -} - static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) { uint32_t urgency = nghttp3_pri_uint8_urgency(tnode->pri); @@ -2145,121 +1522,8 @@ static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) { return &conn->sched[urgency].spq; } -int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr) { - nghttp3_push_promise *pp; - nghttp3_stream *stream; - int rv; - - if (conn->local.uni.next_push_id <= fr->push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - pp = nghttp3_conn_find_push_promise(conn, fr->push_id); - if (pp == NULL) { - return 0; - } - - stream = pp->stream; - - rv = conn_call_cancel_push(conn, fr->push_id, stream); - if (rv != 0) { - return rv; - } - - if (stream) { - rv = nghttp3_conn_close_stream(conn, stream->node.nid.id, - NGHTTP3_H3_REQUEST_CANCELLED); - if (rv != 0) { - assert(NGHTTP3_ERR_STREAM_NOT_FOUND != rv); - return rv; - } - return 0; - } - - nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node)); - - conn_delete_push_promise(conn, pp); - - return 0; -} - -int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, - int64_t push_id) { - nghttp3_push_promise *pp; - int rv; - - if (nghttp3_gaptr_is_pushed(&conn->remote.uni.push_idtr, (uint64_t)push_id, - 1)) { - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp) { - if (pp->stream) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - pp->stream = stream; - stream->pp = pp; - - assert(!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)); - - if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && - conn->tx.goaway_id <= push_id) { - rv = nghttp3_conn_reject_push_stream(conn, stream); - if (rv != 0) { - return rv; - } - return NGHTTP3_ERR_IGNORE_STREAM; - } - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { - ++conn->remote.uni.unsent_max_pushes; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; - - return conn_call_push_stream(conn, push_id, stream); - } - - stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - return 0; - } - - /* Push ID has been received, but pp is gone. This means that - push is cancelled or server is misbehaving. We have no - information to distinguish the two, so just cancel QPACK stream - just in case, and ask application to send STOP_SENDING and - ignore all frames in this stream. */ - rv = nghttp3_conn_cancel_push_stream(conn, stream); - if (rv != 0) { - return rv; - } - return NGHTTP3_ERR_IGNORE_STREAM; - } - - if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { - return NGHTTP3_ERR_H3_ID_ERROR; - } - - rv = push_idtr_push(&conn->remote.uni.push_idtr, push_id); - if (rv != 0) { - return rv; - } - - /* Don't know the associated stream of PUSH_PROMISE. It doesn't - matter because client sends nothing to this stream. */ - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, NULL); - if (rv != 0) { - return rv; - } - - pp->stream = stream; - stream->pp = pp; - stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; - - return 0; -} - static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, nghttp3_stream *stream, - nghttp3_push_promise *pp, const uint8_t *src, size_t srclen, int fin) { nghttp3_ssize nread; @@ -2272,37 +1536,26 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, nghttp3_http_state *http; int request = 0; int trailers = 0; - int ignore_pp = 0; - if (pp) { + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: request = 1; - ignore_pp = pp->stream_id != stream->node.nid.id; - if (ignore_pp) { - http = NULL; - } else { - http = &pp->http; - } - } else { - switch (stream->rx.hstate) { - case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - request = 1; - /* Fall through */ - case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - recv_header = conn->callbacks.recv_header; - break; - case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - request = 1; - /* Fall through */ - case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - trailers = 1; - recv_header = conn->callbacks.recv_trailer; - break; - default: - /* Unreachable */ - assert(0); - } - http = &stream->rx.http; + /* Fall through */ + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + recv_header = conn->callbacks.recv_header; + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + request = 1; + /* Fall through */ + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + trailers = 1; + recv_header = conn->callbacks.recv_trailer; + break; + default: + /* Unreachable */ + assert(0); } + http = &stream->rx.http; nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen); buf.last = buf.end; @@ -2342,17 +1595,9 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, } if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) { - if (ignore_pp) { - nghttp3_rcbuf_decref(nv.name); - nghttp3_rcbuf_decref(nv.value); - - continue; - } - - assert(http); - - rv = nghttp3_http_on_header(http, stream->rstate.fr.hd.type, &nv, request, - trailers); + rv = nghttp3_http_on_header( + http, &nv, request, trailers, + conn->server && conn->local.settings.enable_connect_protocol); switch (rv) { case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: break; @@ -2360,18 +1605,13 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, rv = 0; break; case 0: - if (pp) { - if (conn->callbacks.recv_push_promise) { - rv = conn->callbacks.recv_push_promise( - conn, stream->node.nid.id, pp->node.nid.id, nv.token, nv.name, - nv.value, nv.flags, conn->user_data, stream->user_data); - } - break; - } if (recv_header) { rv = recv_header(conn, stream->node.nid.id, nv.token, nv.name, nv.value, nv.flags, conn->user_data, stream->user_data); + if (rv != 0) { + rv = NGHTTP3_ERR_CALLBACK_FAILURE; + } } break; default: @@ -2393,23 +1633,19 @@ static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, nghttp3_stream *stream, - nghttp3_push_promise *pp, const uint8_t *src, size_t srclen, int fin) { if (srclen == 0 && !fin) { return 0; } - return conn_decode_headers(conn, stream, pp, src, srclen, fin); + return conn_decode_headers(conn, stream, src, srclen, fin); } int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, const nghttp3_frame_settings *fr) { const nghttp3_settings_entry *ent = &fr->iv[0]; nghttp3_settings *dest = &conn->remote.settings; - int rv; - size_t max_table_capacity = SIZE_MAX; - size_t max_blocked_streams = SIZE_MAX; /* TODO Check for duplicates */ switch (ent->id) { @@ -2417,29 +1653,44 @@ int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, dest->max_field_section_size = ent->value; break; case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY: - dest->qpack_max_table_capacity = (size_t)ent->value; - max_table_capacity = - nghttp3_min(max_table_capacity, dest->qpack_max_table_capacity); - rv = nghttp3_qpack_encoder_set_hard_max_dtable_size(&conn->qenc, - max_table_capacity); - if (rv != 0) { - return rv; + if (dest->qpack_max_dtable_capacity != 0) { + return NGHTTP3_ERR_H3_SETTINGS_ERROR; } - rv = nghttp3_qpack_encoder_set_max_dtable_size(&conn->qenc, - max_table_capacity); - if (rv != 0) { - return rv; + + if (ent->value == 0) { + break; } + + dest->qpack_max_dtable_capacity = (size_t)ent->value; + + nghttp3_qpack_encoder_set_max_dtable_capacity(&conn->qenc, + (size_t)ent->value); break; case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS: + if (dest->qpack_blocked_streams != 0) { + return NGHTTP3_ERR_H3_SETTINGS_ERROR; + } + + if (ent->value == 0) { + break; + } + dest->qpack_blocked_streams = (size_t)ent->value; - max_blocked_streams = - nghttp3_min(max_blocked_streams, dest->qpack_blocked_streams); - rv = - nghttp3_qpack_encoder_set_max_blocked(&conn->qenc, max_blocked_streams); - if (rv != 0) { - return rv; + + nghttp3_qpack_encoder_set_max_blocked_streams( + &conn->qenc, (size_t)nghttp3_min(100, ent->value)); + break; + case NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL: + if (!conn->server) { + break; + } + if (ent->value != 0 && ent->value != 1) { + return NGHTTP3_ERR_H3_SETTINGS_ERROR; + } + if (ent->value == 0 && dest->enable_connect_protocol) { + return NGHTTP3_ERR_H3_SETTINGS_ERROR; } + dest->enable_connect_protocol = (int)ent->value; break; case NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH: case NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS: @@ -2454,8 +1705,71 @@ int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, return 0; } +static int +conn_on_priority_update_stream(nghttp3_conn *conn, + const nghttp3_frame_priority_update *fr) { + int64_t stream_id = fr->pri_elem_id; + nghttp3_stream *stream; + int rv; + + if (!nghttp3_client_stream_bidi(stream_id) || + nghttp3_ord_stream_id(stream_id) > conn->remote.bidi.max_client_streams) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && + conn->tx.goaway_id <= stream_id) { + /* Connection is going down. Ignore priority signal. */ + return 0; + } + + rv = conn_bidi_idtr_open(conn, stream_id); + if (rv != 0) { + if (nghttp3_err_is_fatal(rv)) { + return rv; + } + + assert(rv == NGHTTP3_ERR_STREAM_IN_USE); + + /* The stream is gone. Just ignore. */ + return 0; + } + + conn->rx.max_stream_id_bidi = + nghttp3_max(conn->rx.max_stream_id_bidi, stream_id); + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + + stream->node.pri = nghttp3_pri_to_uint8(&fr->pri); + stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED; + + return 0; + } + + if (stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET) { + return 0; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED; + + return conn_update_stream_priority(conn, stream, + nghttp3_pri_to_uint8(&fr->pri)); +} + +int nghttp3_conn_on_priority_update(nghttp3_conn *conn, + const nghttp3_frame_priority_update *fr) { + assert(conn->server); + assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE); + + return conn_on_priority_update_stream(conn, fr); +} + static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id, - size_t datalen, void *user_data) { + uint64_t datalen, void *user_data) { nghttp3_conn *conn = stream->conn; int rv; @@ -2477,76 +1791,34 @@ int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, nghttp3_stream *stream; int rv; nghttp3_stream_callbacks callbacks = { - conn_stream_acked_data, - }; - - rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, &callbacks, - conn->mem); - if (rv != 0) { - return rv; - } - - stream->conn = conn; - - rv = nghttp3_map_insert(&conn->streams, &stream->me); - if (rv != 0) { - nghttp3_stream_del(stream); - return rv; - } - - ++conn->next_seq; - *pstream = stream; - - return 0; -} - -int nghttp3_conn_create_push_promise(nghttp3_conn *conn, - nghttp3_push_promise **ppp, - int64_t push_id, - nghttp3_tnode *assoc_tnode) { - nghttp3_push_promise *pp; - int rv; + conn_stream_acked_data, + }; - rv = nghttp3_push_promise_new(&pp, push_id, conn->next_seq, assoc_tnode, - conn->mem); + rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, &callbacks, + &conn->out_chunk_objalloc, &conn->stream_objalloc, + conn->mem); if (rv != 0) { return rv; } - rv = nghttp3_map_insert(&conn->pushes, &pp->me); + stream->conn = conn; + + rv = nghttp3_map_insert(&conn->streams, + (nghttp3_map_key_type)stream->node.nid.id, stream); if (rv != 0) { - nghttp3_push_promise_del(pp, conn->mem); + nghttp3_stream_del(stream); return rv; } ++conn->next_seq; - *ppp = pp; + *pstream = stream; return 0; } nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id) { - nghttp3_map_entry *me; - - me = nghttp3_map_find(&conn->streams, (key_type)stream_id); - if (me == NULL) { - return NULL; - } - - return nghttp3_struct_of(me, nghttp3_stream, me); -} - -nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, - int64_t push_id) { - nghttp3_map_entry *me; - - me = nghttp3_map_find(&conn->pushes, (key_type)push_id); - if (me == NULL) { - return NULL; - } - - return nghttp3_struct_of(me, nghttp3_push_promise, me); + return nghttp3_map_find(&conn->streams, (nghttp3_map_key_type)stream_id); } int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { @@ -2680,15 +1952,6 @@ nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, } if (conn->tx.ctrl && !nghttp3_stream_is_blocked(conn->tx.ctrl)) { - if (!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED) && - !(conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && - conn->remote.uni.unsent_max_pushes > conn->remote.uni.max_pushes) { - rv = nghttp3_conn_submit_max_push_id(conn); - if (rv != 0) { - return rv; - } - } - ncnt = conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.ctrl); if (ncnt) { @@ -2727,7 +1990,7 @@ nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, return ncnt; } - if (nghttp3_stream_bidi_or_push(stream) && + if (nghttp3_client_stream_bidi(stream->node.nid.id) && !nghttp3_stream_require_schedule(stream)) { nghttp3_conn_unschedule_stream(conn, stream); } @@ -2748,10 +2011,6 @@ nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) { tnode = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe); - if (tnode->nid.type == NGHTTP3_NODE_ID_TYPE_PUSH) { - return nghttp3_struct_of(tnode, nghttp3_push_promise, node)->stream; - } - return nghttp3_struct_of(tnode, nghttp3_stream, node); } @@ -2764,7 +2023,7 @@ int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id, int rv; if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } rv = nghttp3_stream_add_outq_offset(stream, n); @@ -2774,7 +2033,7 @@ int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id, stream->unscheduled_nwrite += n; - if (!nghttp3_stream_bidi_or_push(stream)) { + if (!nghttp3_client_stream_bidi(stream->node.nid.id)) { return 0; } @@ -2795,7 +2054,7 @@ int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id, nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } return nghttp3_stream_add_ack_offset(stream, n); @@ -2840,21 +2099,12 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, return 0; } -static nghttp3_tnode *stream_get_dependency_node(nghttp3_stream *stream) { - if (stream->pp) { - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - return &stream->pp->node; - } - - return &stream->node; -} - int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { /* Assume that stream stays on the same urgency level */ + nghttp3_tnode *node = stream_get_sched_node(stream); int rv; - rv = nghttp3_tnode_schedule(stream_get_dependency_node(stream), - conn_get_sched_pq(conn, &stream->node), + rv = nghttp3_tnode_schedule(node, conn_get_sched_pq(conn, node), stream->unscheduled_nwrite); if (rv != 0) { return rv; @@ -2867,7 +2117,7 @@ int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, nghttp3_stream *stream) { - if (nghttp3_tnode_is_scheduled(stream_get_dependency_node(stream))) { + if (nghttp3_tnode_is_scheduled(stream_get_sched_node(stream))) { return 0; } @@ -2876,8 +2126,9 @@ int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { - nghttp3_tnode_unschedule(stream_get_dependency_node(stream), - conn_get_sched_pq(conn, &stream->node)); + nghttp3_tnode *node = stream_get_sched_node(stream); + + nghttp3_tnode_unschedule(node, conn_get_sched_pq(conn, node)); } int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id, @@ -2983,184 +2234,6 @@ int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id, return conn_submit_headers_data(conn, stream, nva, nvlen, NULL); } -int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, int64_t *ppush_id, - int64_t stream_id, const nghttp3_nv *nva, - size_t nvlen) { - nghttp3_stream *stream; - int rv; - nghttp3_nv *nnva; - nghttp3_frame_entry frent; - int64_t push_id; - nghttp3_push_promise *pp; - - assert(conn->server); - assert(conn->tx.qenc); - assert(nghttp3_client_stream_bidi(stream_id)); - - if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) { - return NGHTTP3_ERR_CONN_CLOSING; - } - - stream = nghttp3_conn_find_stream(conn, stream_id); - if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; - } - - if (conn->local.uni.max_pushes <= (uint64_t)conn->local.uni.next_push_id) { - return NGHTTP3_ERR_PUSH_ID_BLOCKED; - } - - push_id = conn->local.uni.next_push_id; - - rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node); - if (rv != 0) { - return rv; - } - - ++conn->local.uni.next_push_id; - - rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); - if (rv != 0) { - return rv; - } - - frent.fr.hd.type = NGHTTP3_FRAME_PUSH_PROMISE; - frent.fr.push_promise.push_id = push_id; - frent.fr.push_promise.nva = nnva; - frent.fr.push_promise.nvlen = nvlen; - - rv = nghttp3_stream_frq_add(stream, &frent); - if (rv != 0) { - nghttp3_nva_del(nnva, conn->mem); - return rv; - } - - *ppush_id = push_id; - - if (nghttp3_stream_require_schedule(stream)) { - return nghttp3_conn_schedule_stream(conn, stream); - } - - return 0; -} - -int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, int64_t push_id, - int64_t stream_id) { - nghttp3_push_promise *pp; - nghttp3_stream *stream; - int rv; - - assert(conn->server); - assert(nghttp3_server_stream_uni(stream_id)); - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - assert(NULL == nghttp3_conn_find_stream(conn, stream_id)); - - rv = nghttp3_conn_create_stream(conn, &stream, stream_id); - if (rv != 0) { - return rv; - } - - stream->type = NGHTTP3_STREAM_TYPE_PUSH; - stream->pp = pp; - - pp->stream = stream; - - rv = nghttp3_stream_write_stream_type_push_id(stream); - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp3_conn_cancel_push(nghttp3_conn *conn, int64_t push_id) { - if (conn->server) { - return nghttp3_conn_server_cancel_push(conn, push_id); - } - return nghttp3_conn_client_cancel_push(conn, push_id); -} - -int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id) { - nghttp3_push_promise *pp; - nghttp3_frame_entry frent; - int rv; - - assert(conn->tx.ctrl); - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { - frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; - frent.fr.cancel_push.push_id = push_id; - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; - } - - if (pp->stream) { - /* CANCEL_PUSH will be sent, but it does not affect pushed stream. - Stream should be explicitly cancelled. */ - rv = conn_call_reset_stream(conn, pp->stream, NGHTTP3_H3_REQUEST_CANCELLED); - if (rv != 0) { - return rv; - } - } - - nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node)); - - conn_delete_push_promise(conn, pp); - - return 0; -} - -int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id) { - nghttp3_push_promise *pp; - nghttp3_frame_entry frent; - int rv; - - pp = nghttp3_conn_find_push_promise(conn, push_id); - if (pp == NULL) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } - - if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL) { - return 0; - } - - if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED)) { - return NGHTTP3_ERR_INVALID_STATE; - } - - if (pp->stream) { - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; - return nghttp3_conn_cancel_push_stream(conn, pp->stream); - } - - frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; - frent.fr.cancel_push.push_id = push_id; - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - conn_delete_push_promise(conn, pp); - - return 0; -} - int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { nghttp3_frame_entry frent; int rv; @@ -3168,7 +2241,8 @@ int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { assert(conn->tx.ctrl); frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; - frent.fr.goaway.id = conn->server ? (1ull << 62) - 4 : (1ull << 62) - 1; + frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID + : NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID; assert(frent.fr.goaway.id <= conn->tx.goaway_id); @@ -3194,7 +2268,7 @@ int nghttp3_conn_shutdown(nghttp3_conn *conn) { frent.fr.goaway.id = nghttp3_min((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4); } else { - frent.fr.goaway.id = nghttp3_min((1ll << 62) - 1, conn->rx.max_push_id + 1); + frent.fr.goaway.id = 0; } assert(frent.fr.goaway.id <= conn->tx.goaway_id); @@ -3213,12 +2287,7 @@ int nghttp3_conn_shutdown(nghttp3_conn *conn) { int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) { int rv; - rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id); - if (rv != 0) { - return rv; - } - - rv = conn_call_send_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); + rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); if (rv != 0) { return rv; } @@ -3226,74 +2295,46 @@ int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) { return conn_call_reset_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); } -static int conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream, - uint64_t app_error_code) { - int rv; - - /* TODO Send Stream Cancellation if we have not processed all - incoming stream data up to fin */ - rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id); - if (rv != 0) { - return rv; - } - - return conn_call_send_stop_sending(conn, stream, app_error_code); -} - -int nghttp3_conn_reject_push_stream(nghttp3_conn *conn, - nghttp3_stream *stream) { - return conn_reject_push_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); -} - -int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn, - nghttp3_stream *stream) { - return conn_reject_push_stream(conn, stream, NGHTTP3_H3_REQUEST_CANCELLED); -} - -int nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) { +void nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return; } stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED; stream->unscheduled_nwrite = 0; - if (nghttp3_stream_bidi_or_push(stream)) { + if (nghttp3_client_stream_bidi(stream->node.nid.id)) { nghttp3_conn_unschedule_stream(conn, stream); } - - return 0; } -int nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) { +void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return; } stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_WR; stream->unscheduled_nwrite = 0; - if (nghttp3_stream_bidi_or_push(stream)) { + if (nghttp3_client_stream_bidi(stream->node.nid.id)) { nghttp3_conn_unschedule_stream(conn, stream); } - - return 0; } int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED; - if (nghttp3_stream_bidi_or_push(stream) && + if (nghttp3_client_stream_bidi(stream->node.nid.id) && nghttp3_stream_require_schedule(stream)) { return nghttp3_conn_ensure_stream_scheduled(conn, stream); } @@ -3301,16 +2342,29 @@ int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) { return 0; } +int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return 0; + } + + return (stream->flags & + (NGHTTP3_STREAM_FLAG_FC_BLOCKED | + NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED | NGHTTP3_STREAM_FLAG_SHUT_WR | + NGHTTP3_STREAM_FLAG_CLOSED)) == 0; +} + int nghttp3_conn_resume_stream(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; - if (nghttp3_stream_bidi_or_push(stream) && + if (nghttp3_client_stream_bidi(stream->node.nid.id) && nghttp3_stream_require_schedule(stream)) { return nghttp3_conn_ensure_stream_scheduled(conn, stream); } @@ -3336,9 +2390,7 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, nghttp3_conn_unschedule_stream(conn, stream); - if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX && - (conn->server || !stream->pp || - (stream->pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED))) { + if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX) { return conn_delete_stream(conn, stream); } @@ -3346,13 +2398,22 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, return 0; } -int nghttp3_conn_reset_stream(nghttp3_conn *conn, int64_t stream_id) { +int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream; + if (!nghttp3_client_stream_bidi(stream_id)) { + return 0; + } + stream = nghttp3_conn_find_stream(conn, stream_id); if (stream) { - stream->flags |= NGHTTP3_STREAM_FLAG_RESET; + if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) { + return 0; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_RD; } + return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id); } @@ -3383,26 +2444,6 @@ void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn, max_concurrent_streams); } -int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn) { - nghttp3_frame_entry frent; - int rv; - - assert(conn->tx.ctrl); - assert(!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED)); - - frent.fr.hd.type = NGHTTP3_FRAME_MAX_PUSH_ID; - /* The acutal push_id is set when frame is serialized */ - - rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); - if (rv != 0) { - return rv; - } - - conn->flags |= NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; - - return 0; -} - int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id, void *stream_user_data) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); @@ -3416,48 +2457,66 @@ int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id, return 0; } -int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, - int64_t stream_id) { +uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, + int64_t stream_id) { nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { - return NGHTTP3_ERR_STREAM_NOT_FOUND; + return 0; } - return stream->rstate.left; + return (uint64_t)stream->rstate.left; } int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, nghttp3_pri *dest, int64_t stream_id) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + nghttp3_stream *stream; assert(conn->server); + if (!nghttp3_client_stream_bidi(stream_id)) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { return NGHTTP3_ERR_STREAM_NOT_FOUND; } - dest->urgency = nghttp3_pri_uint8_urgency(stream->rx.http.pri); - dest->inc = nghttp3_pri_uint8_inc(stream->rx.http.pri); + dest->urgency = nghttp3_pri_uint8_urgency(stream->node.pri); + dest->inc = nghttp3_pri_uint8_inc(stream->node.pri); return 0; } int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, int64_t stream_id, const nghttp3_pri *pri) { - nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + nghttp3_stream *stream; + nghttp3_frame_entry frent; - assert(conn->server); assert(pri->urgency < NGHTTP3_URGENCY_LEVELS); assert(pri->inc == 0 || pri->inc == 1); + if (!nghttp3_client_stream_bidi(stream_id)) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { return NGHTTP3_ERR_STREAM_NOT_FOUND; } - stream->rx.http.pri = nghttp3_pri_to_uint8(pri); + if (conn->server) { + stream->flags |= NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET; + + return conn_update_stream_priority(conn, stream, nghttp3_pri_to_uint8(pri)); + } + + frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE; + frent.fr.priority_update.pri_elem_id = stream_id; + frent.fr.priority_update.pri = *pri; - return conn_update_stream_priority(conn, stream, stream->rx.http.pri); + return nghttp3_stream_frq_add(conn->tx.ctrl, &frent); } int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, @@ -3472,52 +2531,12 @@ int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER; } -void nghttp3_settings_default(nghttp3_settings *settings) { +void nghttp3_settings_default_versioned(int settings_version, + nghttp3_settings *settings) { + (void)settings_version; + memset(settings, 0, sizeof(nghttp3_settings)); settings->max_field_section_size = NGHTTP3_VARINT_MAX; -} - -int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, - uint64_t seq, nghttp3_tnode *assoc_tnode, - const nghttp3_mem *mem) { - nghttp3_push_promise *pp; - nghttp3_node_id nid; - - pp = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_push_promise)); - if (pp == NULL) { - return NGHTTP3_ERR_NOMEM; - } - - nghttp3_tnode_init( - &pp->node, nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_PUSH, push_id), - seq, NGHTTP3_DEFAULT_URGENCY); - - pp->me.key = (key_type)push_id; - pp->node.nid.id = push_id; - pp->http.status_code = -1; - pp->http.content_length = -1; - - if (assoc_tnode) { - assert(assoc_tnode->nid.type == NGHTTP3_NODE_ID_TYPE_STREAM); - - pp->stream_id = assoc_tnode->nid.id; - pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; - } else { - pp->stream_id = -1; - } - - *ppp = pp; - - return 0; -} - -void nghttp3_push_promise_del(nghttp3_push_promise *pp, - const nghttp3_mem *mem) { - if (pp == NULL) { - return; - } - - nghttp3_tnode_free(&pp->node); - - nghttp3_mem_free(mem, pp); + settings->qpack_encoder_max_dtable_capacity = + NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h index f0f012e177001d..fa7071e4b1ddb7 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h @@ -48,80 +48,38 @@ blocked streams for QPACK encoder. */ #define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100 -/* NGHTTP3_PUSH_PROMISE_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_NONE 0x00 -/* NGHTTP3_PUSH_PROMISE_FLAG_RECVED is set when PUSH_PROMISE is - completely received. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_RECVED 0x01 -/* NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL is set when push is - cancelled by server before receiving PUSH_PROMISE completely. - This flag should have no effect if push stream has already - opened. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL 0x02 -/* NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL is set when push is - canceled by the local endpoint. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL 0x04 -#define NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED \ - (NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL | \ - NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL) -/* NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED indicates that - unsent_max_pushes has been updated for this push ID. */ -#define NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED 0x08 -/* NGHTTP3_PUSH_PROMISE_FLAG_BOUND is set if nghttp3_push_promise - object is bound to a stream where PUSH_PROMISE frame is received - first. nghttp3_push_promise object might be created before - receiving any PUSH_PROMISE when pushed stream is received before - it.*/ -#define NGHTTP3_PUSH_PROMISE_FLAG_BOUND 0x10 - -typedef struct nghttp3_push_promise { - nghttp3_map_entry me; - nghttp3_tnode node; - nghttp3_http_state http; - /* stream is server initiated unidirectional stream which fulfils - the push promise. */ - nghttp3_stream *stream; - /* stream_id is the stream ID where this PUSH_PROMISE is first - received. PUSH_PROMISE with same push ID is allowed to be sent - in the multiple streams. We ignore those duplicated PUSH_PROMISE - entirely because we don't see any value to add extra cost of - processing for it. Even ignoring those frame is not yet easy - because we have to decode the header blocks. Server push is - overly complex and there is no good use case after all. */ - int64_t stream_id; - /* flags is bitwise OR of zero or more of - NGHTTP3_PUSH_PROMISE_FLAG_*. */ - uint16_t flags; -} nghttp3_push_promise; - /* NGHTTP3_CONN_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_CONN_FLAG_NONE 0x0000 +#define NGHTTP3_CONN_FLAG_NONE 0x0000u /* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has been received. */ -#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001 +#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001u /* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has opened. */ -#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002 +#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002u /* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder stream has opened. */ -#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004 +#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004u /* NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED is set when a QPACK decoder stream has opened. */ -#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008 -/* NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED indicates that MAX_PUSH_ID has - been queued to control stream. */ -#define NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED 0x0010 +#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008u /* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has received. */ -#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020 +#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020u /* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has been submitted for transmission. */ -#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040 +#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040u + +typedef struct nghttp3_chunk { + nghttp3_opl_entry oplent; +} nghttp3_chunk; + +nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent); struct nghttp3_conn { + nghttp3_objalloc out_chunk_objalloc; + nghttp3_objalloc stream_objalloc; nghttp3_callbacks callbacks; nghttp3_map streams; - nghttp3_map pushes; nghttp3_qpack_decoder qdec; nghttp3_qpack_encoder qenc; nghttp3_pq qpack_blocked_streams; @@ -138,11 +96,9 @@ struct nghttp3_conn { nghttp3_settings settings; struct { /* max_pushes is the number of push IDs that local endpoint can - issue. This field is used by server only. */ + issue. This field is used by server only and used just for + validation */ uint64_t max_pushes; - /* next_push_id is the next push ID server uses. This field is - used by server only. */ - int64_t next_push_id; } uni; } local; @@ -154,18 +110,6 @@ struct nghttp3_conn { issue. This field is used on server side only. */ uint64_t max_client_streams; } bidi; - struct { - /* push_idtr tracks which push ID has been used by remote - server. This field is used by client only. */ - nghttp3_gaptr push_idtr; - /* unsent_max_pushes is the maximum number of push which the local - endpoint can accept. This limit is not yet notified to the - remote endpoint. This field is used by client only. */ - uint64_t unsent_max_pushes; - /* max_push is the maximum number of push which the local - endpoint can accept. This field is used by client only. */ - uint64_t max_pushes; - } uni; nghttp3_settings settings; } remote; @@ -174,7 +118,13 @@ struct nghttp3_conn { int64_t goaway_id; int64_t max_stream_id_bidi; - int64_t max_push_id; + + /* pri_fieldbuf is a buffer to store incoming Priority Field Value + in PRIORITY_UPDATE frame. */ + uint8_t pri_fieldbuf[8]; + /* pri_fieldlen is the number of bytes written into + pri_fieldbuf. */ + size_t pri_fieldbuflen; } rx; struct { @@ -192,17 +142,9 @@ struct nghttp3_conn { nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id); -nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, - int64_t push_id); - int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, int64_t stream_id); -int nghttp3_conn_create_push_promise(nghttp3_conn *conn, - nghttp3_push_promise **ppp, - int64_t push_id, - nghttp3_tnode *assoc_tnode); - nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, nghttp3_stream *stream, const uint8_t *src, size_t srclen, int fin); @@ -214,10 +156,6 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, nghttp3_stream *stream, const uint8_t *src, size_t srclen); -nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, - nghttp3_stream *stream, const uint8_t *src, - size_t srclen, int fin); - nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, const uint8_t *src, size_t srclen); @@ -226,24 +164,14 @@ nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, const uint8_t *src, size_t srclen); -int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, - nghttp3_stream *stream); - -int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr); - -int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, - const nghttp3_frame_cancel_push *fr); - -int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, - int64_t push_id); - int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, const uint8_t *data, size_t datalen); +int nghttp3_conn_on_priority_update(nghttp3_conn *conn, + const nghttp3_frame_priority_update *fr); + nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, nghttp3_stream *stream, - nghttp3_push_promise *pp, const uint8_t *data, size_t datalen, int fin); @@ -255,12 +183,6 @@ int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn, void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn); -int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id); - -int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id); - -int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn); - int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, @@ -270,20 +192,10 @@ void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream); -int nghttp3_conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream); - -int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn, nghttp3_stream *stream); - /* * nghttp3_conn_get_next_tx_stream returns next stream to send. It * returns NULL if there is no such stream. */ nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn); -int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, - uint64_t seq, nghttp3_tnode *assoc_tnode, - const nghttp3_mem *mem); - -void nghttp3_push_promise_del(nghttp3_push_promise *pp, const nghttp3_mem *mem); - #endif /* NGHTTP3_CONN_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c index 04bf6a0f298b3b..cb340ab5a11363 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c @@ -58,6 +58,7 @@ int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) { } assert(0); + abort(); } int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h index 860326385ba434..23555be7cac027 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h @@ -88,7 +88,7 @@ STIN uint32_t htonl(uint32_t hostlong) { uint32_t res; unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; + *p++ = (unsigned char)(hostlong >> 24); *p++ = (hostlong >> 16) & 0xffu; *p++ = (hostlong >> 8) & 0xffu; *p = hostlong & 0xffu; @@ -98,7 +98,7 @@ STIN uint32_t htonl(uint32_t hostlong) { STIN uint16_t htons(uint16_t hostshort) { uint16_t res; unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; + *p++ = (unsigned char)(hostshort >> 8); *p = hostshort & 0xffu; return res; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_err.c b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c index fb1353ea8a45c5..5cf94db852f71f 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_err.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c @@ -36,8 +36,6 @@ const char *nghttp3_strerror(int liberr) { return "ERR_WOULDBLOCK"; case NGHTTP3_ERR_STREAM_IN_USE: return "ERR_STREAM_IN_USE"; - case NGHTTP3_ERR_PUSH_ID_BLOCKED: - return "ERR_PUSH_ID_BLOCKED"; case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: return "ERR_MALFORMED_HTTP_HEADER"; case NGHTTP3_ERR_REMOVE_HTTP_HEADER: @@ -48,14 +46,12 @@ const char *nghttp3_strerror(int liberr) { return "ERR_QPACK_FATAL"; case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE: return "ERR_QPACK_HEADER_TOO_LARGE"; - case NGHTTP3_ERR_IGNORE_STREAM: - return "ERR_IGNORE_STREAM"; case NGHTTP3_ERR_STREAM_NOT_FOUND: return "ERR_STREAM_NOT_FOUND"; - case NGHTTP3_ERR_IGNORE_PUSH_PROMISE: - return "ERR_IGNORE_PUSH_PROMISE"; case NGHTTP3_ERR_CONN_CLOSING: return "ERR_CONN_CLOSING"; + case NGHTTP3_ERR_STREAM_DATA_OVERFLOW: + return "ERR_STREAM_DATA_OVERFLOW"; case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED: return "ERR_QPACK_DECOMPRESSION_FAILED"; case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR: @@ -106,6 +102,8 @@ uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) { case NGHTTP3_ERR_H3_MISSING_SETTINGS: return NGHTTP3_H3_MISSING_SETTINGS; case NGHTTP3_ERR_H3_INTERNAL_ERROR: + case NGHTTP3_ERR_NOMEM: + case NGHTTP3_ERR_CALLBACK_FAILURE: return NGHTTP3_H3_INTERNAL_ERROR; case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM: return NGHTTP3_H3_CLOSED_CRITICAL_STREAM; @@ -124,3 +122,5 @@ uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) { return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; } } + +int nghttp3_err_is_fatal(int liberr) { return liberr < NGHTTP3_ERR_FATAL; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c index 6be82c6edaf492..38c395ebe16162 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c @@ -26,6 +26,7 @@ #include "nghttp3_frame.h" #include +#include #include "nghttp3_conv.h" #include "nghttp3_str.h" @@ -70,59 +71,53 @@ size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen, nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; } -uint8_t *nghttp3_frame_write_cancel_push(uint8_t *p, - const nghttp3_frame_cancel_push *fr) { +uint8_t *nghttp3_frame_write_goaway(uint8_t *p, + const nghttp3_frame_goaway *fr) { p = nghttp3_frame_write_hd(p, &fr->hd); - p = nghttp3_put_varint(p, fr->push_id); + p = nghttp3_put_varint(p, fr->id); return p; } -size_t -nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, - const nghttp3_frame_cancel_push *fr) { - size_t payloadlen = nghttp3_put_varint_len(fr->push_id); +size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, + const nghttp3_frame_goaway *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->id); *ppayloadlen = (int64_t)payloadlen; - return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) + + return nghttp3_put_varint_len(NGHTTP3_FRAME_GOAWAY) + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; } -uint8_t *nghttp3_frame_write_max_push_id(uint8_t *p, - const nghttp3_frame_max_push_id *fr) { +uint8_t * +nghttp3_frame_write_priority_update(uint8_t *p, + const nghttp3_frame_priority_update *fr) { p = nghttp3_frame_write_hd(p, &fr->hd); - p = nghttp3_put_varint(p, fr->push_id); + p = nghttp3_put_varint(p, fr->pri_elem_id); - return p; -} - -size_t -nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, - const nghttp3_frame_max_push_id *fr) { - size_t payloadlen = nghttp3_put_varint_len(fr->push_id); + assert(fr->pri.urgency <= NGHTTP3_URGENCY_LOW); - *ppayloadlen = (int64_t)payloadlen; + *p++ = 'u'; + *p++ = '='; + *p++ = (uint8_t)('0' + fr->pri.urgency); - return nghttp3_put_varint_len(NGHTTP3_FRAME_MAX_PUSH_ID) + - nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; -} - -uint8_t *nghttp3_frame_write_goaway(uint8_t *p, - const nghttp3_frame_goaway *fr) { - p = nghttp3_frame_write_hd(p, &fr->hd); - p = nghttp3_put_varint(p, fr->id); + if (fr->pri.inc) { +#define NGHTTP3_PRIORITY_INCREMENTAL ", i" + p = nghttp3_cpymem(p, (const uint8_t *)NGHTTP3_PRIORITY_INCREMENTAL, + sizeof(NGHTTP3_PRIORITY_INCREMENTAL) - 1); + } return p; } -size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, - const nghttp3_frame_goaway *fr) { - size_t payloadlen = nghttp3_put_varint_len(fr->id); +size_t nghttp3_frame_write_priority_update_len( + int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->pri_elem_id) + sizeof("u=U") - + 1 + (fr->pri.inc ? sizeof(", i") - 1 : 0); *ppayloadlen = (int64_t)payloadlen; - return nghttp3_put_varint_len(NGHTTP3_FRAME_GOAWAY) + + return nghttp3_put_varint_len(fr->hd.type) + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; } @@ -207,12 +202,3 @@ void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, nghttp3_nva_del(fr->nva, mem); } - -void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, - const nghttp3_mem *mem) { - if (fr == NULL) { - return; - } - - nghttp3_nva_del(fr->nva, mem); -} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h index 9bd59a9660d85b..b64bbc4ecb9667 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h @@ -42,6 +42,10 @@ typedef enum nghttp3_frame_type { NGHTTP3_FRAME_PUSH_PROMISE = 0x05, NGHTTP3_FRAME_GOAWAY = 0x07, NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d, + /* PRIORITY_UPDATE: + https://tools.ietf.org/html/draft-ietf-httpbis-priority-03 */ + NGHTTP3_FRAME_PRIORITY_UPDATE = 0x0f0700, + NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID = 0x0f0701, } nghttp3_frame_type; typedef enum nghttp3_h2_reserved_type { @@ -66,14 +70,10 @@ typedef struct nghttp3_frame_headers { size_t nvlen; } nghttp3_frame_headers; -typedef struct nghttp3_frame_cancel_push { - nghttp3_frame_hd hd; - int64_t push_id; -} nghttp3_frame_cancel_push; - #define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06 #define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01 #define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07 +#define NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL 0x08 #define NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH 0x2 #define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3 @@ -91,32 +91,28 @@ typedef struct nghttp3_frame_settings { nghttp3_settings_entry iv[1]; } nghttp3_frame_settings; -typedef struct nghttp3_frame_push_promise { - nghttp3_frame_hd hd; - nghttp3_nv *nva; - size_t nvlen; - int64_t push_id; -} nghttp3_frame_push_promise; - typedef struct nghttp3_frame_goaway { nghttp3_frame_hd hd; int64_t id; } nghttp3_frame_goaway; -typedef struct nghttp3_frame_max_push_id { +typedef struct nghttp3_frame_priority_update { nghttp3_frame_hd hd; - int64_t push_id; -} nghttp3_frame_max_push_id; + /* pri_elem_id is stream ID if hd.type == + NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type == + NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined + otherwise. */ + int64_t pri_elem_id; + nghttp3_pri pri; +} nghttp3_frame_priority_update; typedef union nghttp3_frame { nghttp3_frame_hd hd; nghttp3_frame_data data; nghttp3_frame_headers headers; - nghttp3_frame_cancel_push cancel_push; nghttp3_frame_settings settings; - nghttp3_frame_push_promise push_promise; nghttp3_frame_goaway goaway; - nghttp3_frame_max_push_id max_push_id; + nghttp3_frame_priority_update priority_update; } nghttp3_frame; /* @@ -150,42 +146,6 @@ uint8_t *nghttp3_frame_write_settings(uint8_t *dest, size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, const nghttp3_frame_settings *fr); -/* - * nghttp3_frame_write_cancel_push writes CANCEL_PUSH frame |fr| to - * |dest|. This function assumes that |dest| has enough space to - * write |fr|. - * - * This function returns |dest| plus the number of bytes written. - */ -uint8_t *nghttp3_frame_write_cancel_push(uint8_t *dest, - const nghttp3_frame_cancel_push *fr); - -/* - * nghttp3_frame_write_cancel_push_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. - */ -size_t nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, - const nghttp3_frame_cancel_push *fr); - -/* - * nghttp3_frame_write_max_push_id writes MAX_PUSH_ID frame |fr| to - * |dest|. This function assumes that |dest| has enough space to - * write |fr|. - * - * This function returns |dest| plus the number of bytes written. - */ -uint8_t *nghttp3_frame_write_max_push_id(uint8_t *dest, - const nghttp3_frame_max_push_id *fr); - -/* - * nghttp3_frame_write_max_push_id_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. - */ -size_t nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, - const nghttp3_frame_max_push_id *fr); - /* * nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|. * This function assumes that |dest| has enough space to write |fr|. @@ -203,6 +163,25 @@ uint8_t *nghttp3_frame_write_goaway(uint8_t *dest, size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, const nghttp3_frame_goaway *fr); +/* + * nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame + * |fr| to |dest|. This function assumes that |dest| has enough space + * to write |fr|. + * + * This function returns |dest| plus the number of bytes written; + */ +uint8_t * +nghttp3_frame_write_priority_update(uint8_t *dest, + const nghttp3_frame_priority_update *fr); + +/* + * nghttp3_frame_write_priority_update_len returns the number of bytes + * required to write |fr|. fr->hd.length is ignored. This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_priority_update_len( + int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr); + /* * nghttp3_nva_copy copies name/value pairs from |nva|, which contains * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so @@ -233,11 +212,4 @@ void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem); void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, const nghttp3_mem *mem); -/* - * nghttp3_frame_push_promise_free frees memory allocated for |fr|. - * It assumes that fr->nva is created by nghttp3_nva_copy() or NULL. - */ -void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, - const nghttp3_mem *mem); - #endif /* NGHTTP3_FRAME_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c index c60887d1ec4db7..88cb49a02f892f 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c @@ -24,29 +24,26 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp3_gaptr.h" -#include "nghttp3_range.h" #include #include -int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { - int rv; - nghttp3_range range = {0, UINT64_MAX}; +void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { + nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range), + mem); - rv = nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, - sizeof(nghttp3_range), mem); - if (rv != 0) { - return rv; - } + gaptr->mem = mem; +} + +static int gaptr_gap_init(nghttp3_gaptr *gaptr) { + nghttp3_range range = {0, UINT64_MAX}; + int rv; rv = nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL); if (rv != 0) { - nghttp3_ksl_free(&gaptr->gap); return rv; } - gaptr->mem = mem; - return 0; } @@ -58,11 +55,19 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) { nghttp3_ksl_free(&gaptr->gap); } -int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { +int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, + uint64_t datalen) { int rv; nghttp3_range k, m, l, r, q = {offset, offset + datalen}; nghttp3_ksl_it it; + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + rv = gaptr_gap_init(gaptr); + if (rv != 0) { + return rv; + } + } + it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, nghttp3_ksl_range_exclusive_compar); @@ -74,7 +79,7 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { } if (nghttp3_range_eq(&k, &m)) { - nghttp3_ksl_remove(&gaptr->gap, &it, &k); + nghttp3_ksl_remove_hint(&gaptr->gap, &it, &it, &k); continue; } nghttp3_range_cut(&l, &r, &k, &m); @@ -96,35 +101,69 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { } uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) { - nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap); - nghttp3_range r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + nghttp3_ksl_it it; + nghttp3_range r; + + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = nghttp3_ksl_begin(&gaptr->gap); + r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + return r.begin; } -nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, - uint64_t offset) { +nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset) { nghttp3_range q = {offset, offset + 1}; - return nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); + nghttp3_ksl_it it; + + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + nghttp3_range r = {0, UINT64_MAX}; + return r; + } + + it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_compar); + + assert(!nghttp3_ksl_it_end(&it)); + + return *(nghttp3_range *)nghttp3_ksl_it_key(&it); } int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, - size_t datalen) { + uint64_t datalen) { nghttp3_range q = {offset, offset + datalen}; - nghttp3_ksl_it it = nghttp3_ksl_lower_bound_compar( - &gaptr->gap, &q, nghttp3_ksl_range_exclusive_compar); - nghttp3_range k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); - nghttp3_range m = nghttp3_range_intersect(&q, &k); + nghttp3_ksl_it it; + nghttp3_range k; + nghttp3_range m; + + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_compar); + k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + m = nghttp3_range_intersect(&q, &k); + return nghttp3_range_len(&m) == 0; } void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr) { - nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap); + nghttp3_ksl_it it; nghttp3_range r; + if (nghttp3_ksl_len(&gaptr->gap) == 0) { + return; + } + + it = nghttp3_ksl_begin(&gaptr->gap); + assert(!nghttp3_ksl_it_end(&it)); r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); - nghttp3_ksl_remove(&gaptr->gap, NULL, &r); + nghttp3_ksl_remove_hint(&gaptr->gap, NULL, &it, &r); } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h index b1713a048bb3c7..7c83c847c9fe29 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h @@ -34,6 +34,7 @@ #include "nghttp3_mem.h" #include "nghttp3_ksl.h" +#include "nghttp3_range.h" /* * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX). @@ -48,14 +49,8 @@ typedef struct nghttp3_gaptr { /* * nghttp3_gaptr_init initializes |gaptr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem); +void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem); /* * nghttp3_gaptr_free frees resources allocated for |gaptr|. @@ -72,7 +67,7 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr); * NGHTTP3_ERR_NOMEM * Out of memory */ -int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen); +int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen); /* * nghttp3_gaptr_first_gap_offset returns the offset to the first gap. @@ -81,18 +76,18 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen); uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr); /* - * nghttp3_gaptr_get_first_gap_after returns the iterator pointing to - * the first gap which overlaps or comes after |offset|. + * nghttp3_gaptr_get_first_gap_after returns the first gap which + * overlaps or comes after |offset|. */ -nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, - uint64_t offset); +nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset); /* * nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset + * datalen) is completely pushed into this object. */ int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, - size_t datalen); + uint64_t datalen); /* * nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c index 465c36ab2cbdb0..5e06d8c47658e1 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c @@ -36,12 +36,17 @@ static uint8_t downcase(uint8_t c) { return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; } +/* + * memieq returns 1 if the data pointed by |a| of length |n| equals to + * |b| of the same length in case-insensitive manner. The data + * pointed by |a| must not include upper cased letters (A-Z). + */ static int memieq(const void *a, const void *b, size_t n) { size_t i; const uint8_t *aa = a, *bb = b; for (i = 0; i < n; ++i) { - if (downcase(aa[i]) != downcase(bb[i])) { + if (aa[i] != downcase(bb[i])) { return 0; } } @@ -73,25 +78,12 @@ static int64_t parse_uint(const uint8_t *s, size_t len) { return n; } -static int lws(const uint8_t *s, size_t n) { - size_t i; - for (i = 0; i < n; ++i) { - if (s[i] != ' ' && s[i] != '\t') { - return 0; - } - } - return 1; -} - static int check_pseudo_header(nghttp3_http_state *http, - const nghttp3_qpack_nv *nv, int flag) { - if (http->flags & flag) { - return 0; - } - if (lws(nv->value->base, nv->value->len)) { + const nghttp3_qpack_nv *nv, uint32_t flag) { + if ((http->flags & flag) || nv->value->len == 0) { return 0; } - http->flags = (uint16_t)(http->flags | flag); + http->flags |= flag; return 1; } @@ -106,98 +98,730 @@ static int expect_response_body(nghttp3_http_state *http) { :path header field value must start with "/". This function must be called after ":method" header field was received. This function returns nonzero if path is valid.*/ -static int check_path(nghttp3_http_state *http) { +static int check_path_flags(nghttp3_http_state *http) { return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 || ((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) || ((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) && (http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK))); } -int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, - size_t len) { - nghttp3_pri pri = *dest; - const uint8_t *p = value, *end = value + len; +/* Generated by genchartbl.py */ +static const int SF_KEY_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */, + 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 0 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, + 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, + 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, + 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, + 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */, + 1 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, + 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; - for (;;) { - for (; p != end && (*p == ' ' || *p == '\t'); ++p) - ; +static nghttp3_ssize sf_parse_key(const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; - if (p == end) { - break; + if ((*p < 'a' || 'z' < *p) && *p != '*') { + return -1; + } + + for (; p != end && SF_KEY_CHARS[*p]; ++p) + ; + + return p - begin; +} + +static nghttp3_ssize sf_parse_integer_or_decimal(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + int sign = 1; + int64_t value = 0; + int type = NGHTTP3_SF_VALUE_TYPE_INTEGER; + size_t len = 0; + size_t fpos = 0; + size_t i; + + if (*p == '-') { + if (++p == end) { + return -1; } + sign = -1; + } + + if (*p < '0' || '9' < *p) { + return -1; + } + + for (; p != end; ++p) { switch (*p) { - case 'u': - ++p; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + value *= 10; + value += *p - '0'; + + if (++len > 15) { + return -1; + } - if (p + 2 > end || *p++ != '=') { - return NGHTTP3_ERR_INVALID_ARGUMENT; + break; + case '.': + if (type != NGHTTP3_SF_VALUE_TYPE_INTEGER) { + goto fin; } - if (!('0' <= *p && *p <= '7')) { - return NGHTTP3_ERR_INVALID_ARGUMENT; + if (len > 12) { + return -1; } + fpos = len; + type = NGHTTP3_SF_VALUE_TYPE_DECIMAL; - pri.urgency = (uint32_t)(*p++ - '0'); + break; + default: + goto fin; + }; + } - if (p == end) { - goto fin; +fin: + switch (type) { + case NGHTTP3_SF_VALUE_TYPE_INTEGER: + if (dest) { + dest->type = (uint8_t)type; + dest->i = value * sign; + } + + return p - begin; + case NGHTTP3_SF_VALUE_TYPE_DECIMAL: + if (fpos == len || len - fpos > 3) { + return -1; + } + + if (dest) { + dest->type = (uint8_t)type; + dest->d = (double)value; + for (i = len - fpos; i > 0; --i) { + dest->d /= (double)10; + } + dest->d *= sign; + } + + return p - begin; + default: + assert(0); + abort(); + } +} + +/* Generated by genchartbl.py */ +static const int SF_DQUOTE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 1 /* [ */, 0 /* \ */, 1 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, + 1 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static nghttp3_ssize sf_parse_string(nghttp3_sf_value *dest, + const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + + if (*p++ != '"') { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case '\\': + if (++p == end) { + return -1; } - if (*p++ != ',') { - return NGHTTP3_ERR_INVALID_ARGUMENT; + switch (*p) { + case '"': + case '\\': + break; + default: + return -1; } break; - case 'i': + case '"': + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_STRING; + dest->s.base = begin + 1; + dest->s.len = (size_t)(p - dest->s.base); + } + ++p; - if (p == end) { - pri.inc = 1; - goto fin; + return p - begin; + default: + if (!SF_DQUOTE_CHARS[*p]) { + return -1; } + } + } - if (*p == ',') { - pri.inc = 1; - ++p; - break; + return -1; +} + +/* Generated by genchartbl.py */ +static const int SF_TOKEN_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, + 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static nghttp3_ssize sf_parse_token(nghttp3_sf_value *dest, + const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + + if ((*p < 'A' || 'Z' < *p) && (*p < 'a' || 'z' < *p) && *p != '*') { + return -1; + } + + for (; p != end && SF_TOKEN_CHARS[*p]; ++p) + ; + + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_TOKEN; + dest->s.base = begin; + dest->s.len = (size_t)(p - begin); + } + + return p - begin; +} + +/* Generated by genchartbl.py */ +static const int SF_BYTESEQ_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 0 /* ! */, 0 /* " */, + 0 /* # */, 0 /* $ */, 0 /* % */, 0 /* & */, 0 /* ' */, + 0 /* ( */, 0 /* ) */, 0 /* * */, 1 /* + */, 0 /* , */, + 0 /* - */, 0 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 0 /* ^ */, + 0 /* _ */, 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 0 /* | */, + 0 /* } */, 0 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */, +}; + +static nghttp3_ssize sf_parse_byteseq(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + + if (*p++ != ':') { + return -1; + } + + for (; p != end; ++p) { + switch (*p) { + case ':': + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_BYTESEQ; + dest->s.base = begin + 1; + dest->s.len = (size_t)(p - dest->s.base); } - if (p + 3 > end || *p != '=' || *(p + 1) != '?' || - (*(p + 2) != '0' && *(p + 2) != '1')) { - return NGHTTP3_ERR_INVALID_ARGUMENT; + ++p; + + return p - begin; + default: + if (!SF_BYTESEQ_CHARS[*p]) { + return -1; } + } + } - pri.inc = *(p + 2) == '1'; + return -1; +} - p += 3; +static nghttp3_ssize sf_parse_boolean(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + int b; - if (p == end) { - goto fin; + if (*p++ != '?') { + return -1; + } + + if (p == end) { + return -1; + } + + switch (*p++) { + case '0': + b = 0; + break; + case '1': + b = 1; + break; + default: + return -1; + } + + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN; + dest->b = b; + } + + return p - begin; +} + +static nghttp3_ssize sf_parse_bare_item(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + switch (*begin) { + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return sf_parse_integer_or_decimal(dest, begin, end); + case '"': + return sf_parse_string(dest, begin, end); + case '*': + return sf_parse_token(dest, begin, end); + case ':': + return sf_parse_byteseq(dest, begin, end); + case '?': + return sf_parse_boolean(dest, begin, end); + default: + if (('A' <= *begin && *begin <= 'Z') || ('a' <= *begin && *begin <= 'z')) { + return sf_parse_token(dest, begin, end); + } + return -1; + } +} + +#define sf_discard_sp_end_err(BEGIN, END, ERR) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + return (ERR); \ + } \ + if (*(BEGIN) != ' ') { \ + break; \ + } \ + } + +static nghttp3_ssize sf_parse_params(const uint8_t *begin, const uint8_t *end) { + const uint8_t *p = begin; + nghttp3_ssize slen; + + for (; p != end && *p == ';';) { + ++p; + + sf_discard_sp_end_err(p, end, -1); + + slen = sf_parse_key(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (p == end || *p != '=') { + /* Boolean true */ + } else if (++p == end) { + return -1; + } else { + slen = sf_parse_bare_item(NULL, p, end); + if (slen < 0) { + return -1; } - if (*p++ != ',') { - return NGHTTP3_ERR_INVALID_ARGUMENT; + p += slen; + } + } + + return p - begin; +} + +static nghttp3_ssize sf_parse_item(nghttp3_sf_value *dest, const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + nghttp3_ssize slen; + + slen = sf_parse_bare_item(dest, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + return p - begin; +} + +nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest, + const uint8_t *begin, const uint8_t *end) { + return sf_parse_item(dest, begin, end); +} + +static nghttp3_ssize sf_parse_inner_list(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + const uint8_t *p = begin; + nghttp3_ssize slen; + + if (*p++ != '(') { + return -1; + } + + for (;;) { + sf_discard_sp_end_err(p, end, -1); + + if (*p == ')') { + ++p; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return -1; } - break; - default: + p += slen; + + if (dest) { + dest->type = NGHTTP3_SF_VALUE_TYPE_INNER_LIST; + } + + return p - begin; + } + + slen = sf_parse_item(NULL, p, end); + if (slen < 0) { + return -1; + } + + p += slen; + + if (p == end || (*p != ' ' && *p != ')')) { + return -1; + } + } +} + +nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + return sf_parse_inner_list(dest, begin, end); +} + +static nghttp3_ssize sf_parse_item_or_inner_list(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end) { + if (*begin == '(') { + return sf_parse_inner_list(dest, begin, end); + } + + return sf_parse_item(dest, begin, end); +} + +#define sf_discard_ows(BEGIN, END) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + goto fin; \ + } \ + if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \ + break; \ + } \ + } + +#define sf_discard_ows_end_err(BEGIN, END, ERR) \ + for (;; ++(BEGIN)) { \ + if ((BEGIN) == (END)) { \ + return (ERR); \ + } \ + if (*(BEGIN) != ' ' && *(BEGIN) != '\t') { \ + break; \ + } \ + } + +int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, + size_t valuelen) { + const uint8_t *p = value, *end = value + valuelen; + nghttp3_ssize slen; + nghttp3_sf_value val; + nghttp3_pri pri = *dest; + const uint8_t *key; + size_t keylen; + + for (; p != end && *p == ' '; ++p) + ; + + for (; p != end;) { + slen = sf_parse_key(p, end); + if (slen < 0) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + key = p; + keylen = (size_t)slen; + + p += slen; + + if (p == end || *p != '=') { + /* Boolean true */ + val.type = NGHTTP3_SF_VALUE_TYPE_BOOLEAN; + val.b = 1; + + slen = sf_parse_params(p, end); + if (slen < 0) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + } else if (++p == end) { return NGHTTP3_ERR_INVALID_ARGUMENT; + } else { + slen = sf_parse_item_or_inner_list(&val, p, end); + if (slen < 0) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + } + + p += slen; + + if (keylen == 1) { + switch (key[0]) { + case 'i': + if (val.type != NGHTTP3_SF_VALUE_TYPE_BOOLEAN) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + pri.inc = val.b; + + break; + case 'u': + if (val.type != NGHTTP3_SF_VALUE_TYPE_INTEGER || + val.i < NGHTTP3_URGENCY_HIGH || NGHTTP3_URGENCY_LOW < val.i) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + pri.urgency = (uint32_t)val.i; + + break; + } } - if (p == end) { + sf_discard_ows(p, end); + + if (*p++ != ',') { return NGHTTP3_ERR_INVALID_ARGUMENT; } + + sf_discard_ows_end_err(p, end, NGHTTP3_ERR_INVALID_ARGUMENT); } fin: - *dest = pri; return 0; } -static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, +static int http_request_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv, int trailers, int connect_protocol) { nghttp3_pri pri; @@ -229,10 +853,6 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, switch (nv->value->base[6]) { case 'T': if (lstreq("CONNECT", nv->value->base, nv->value->len)) { - if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { - /* we won't allow CONNECT for push */ - return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; - } http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT; } break; @@ -259,8 +879,10 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) { return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; } - if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || - (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { + /* scheme is case-insensitive: + https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 */ + if (lstrieq("http", nv->value->base, nv->value->len) || + lstrieq("https", nv->value->base, nv->value->len)) { http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP; } break; @@ -308,11 +930,17 @@ static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, } break; case NGHTTP3_QPACK_TOKEN_PRIORITY: - pri.urgency = nghttp3_pri_uint8_urgency(http->pri); - pri.inc = nghttp3_pri_uint8_inc(http->pri); - if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) == - 0) { - http->pri = nghttp3_pri_to_uint8(&pri); + if (!trailers && !(http->flags & NGHTTP3_HTTP_FLAG_BAD_PRIORITY)) { + pri.urgency = nghttp3_pri_uint8_urgency(http->pri); + pri.inc = nghttp3_pri_uint8_inc(http->pri); + if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) == + 0) { + http->pri = nghttp3_pri_to_uint8(&pri); + http->flags |= NGHTTP3_HTTP_FLAG_PRIORITY; + } else { + http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY; + http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY; + } } break; default: @@ -509,8 +1137,167 @@ static int check_scheme(const uint8_t *value, size_t len) { return 1; } -int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, - nghttp3_qpack_nv *nv, int request, int trailers) { +/* Generated by genmethodchartbl.py */ +static char VALID_METHOD_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, + 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, + 0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 0 /* [ */, + 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ +}; + +static int check_method(const uint8_t *value, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + for (last = value + len; value != last; ++value) { + if (!VALID_METHOD_CHARS[*value]) { + return 0; + } + } + return 1; +} + +/* Generated by genpathchartbl.py */ +static char VALID_PATH_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, + 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, + 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, + 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, + 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, + 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, + 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, + 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, + 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, + 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, + 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, + 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, + 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, + 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, + 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, + 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, + 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, + 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, + 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, + 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, + 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, + 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, + 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, + 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, + 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, + 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, + 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, + 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, + 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, + 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, + 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, + 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, + 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, + 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ +}; + +static int check_path(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_PATH_CHARS[*value]) { + return 0; + } + } + return 1; +} + +int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv, + int request, int trailers, int connect_protocol) { int rv; size_t i; uint8_t c; @@ -535,12 +1322,27 @@ int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, assert(nv->name->len > 0); - if (nv->token == NGHTTP3_QPACK_TOKEN__AUTHORITY || - nv->token == NGHTTP3_QPACK_TOKEN_HOST) { - rv = check_authority(nv->value->base, nv->value->len); - } else if (nv->token == NGHTTP3_QPACK_TOKEN__SCHEME) { + switch (nv->token) { + case NGHTTP3_QPACK_TOKEN__METHOD: + rv = check_method(nv->value->base, nv->value->len); + break; + case NGHTTP3_QPACK_TOKEN__SCHEME: rv = check_scheme(nv->value->base, nv->value->len); - } else { + break; + case NGHTTP3_QPACK_TOKEN__AUTHORITY: + case NGHTTP3_QPACK_TOKEN_HOST: + if (request) { + rv = check_authority(nv->value->base, nv->value->len); + } else { + /* The use of host field in response field section is + undefined. */ + rv = nghttp3_check_header_value(nv->value->base, nv->value->len); + } + break; + case NGHTTP3_QPACK_TOKEN__PATH: + rv = check_path(nv->value->base, nv->value->len); + break; + default: rv = nghttp3_check_header_value(nv->value->base, nv->value->len); } @@ -556,8 +1358,7 @@ int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, } if (request) { - rv = http_request_on_header(http, frame_type, nv, trailers, - /* connect_protocol = */ 0); + rv = http_request_on_header(http, nv, trailers, connect_protocol); } else { rv = http_response_on_header(http, nv, trailers); } @@ -594,7 +1395,7 @@ int nghttp3_http_on_request_headers(nghttp3_http_state *http) { (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) { return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; } - if (!check_path(http)) { + if (!check_path_flags(http)) { return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; } } @@ -609,15 +1410,14 @@ int nghttp3_http_on_response_headers(nghttp3_http_state *http) { if (http->status_code / 100 == 1) { /* non-final response */ - http->flags = (uint16_t)((http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) | - NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + http->flags = (http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) | + NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE; http->content_length = -1; http->status_code = -1; return 0; } - http->flags = - (uint16_t)(http->flags & ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + http->flags &= ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE; if (!expect_response_body(http)) { http->content_length = 0; @@ -629,9 +1429,6 @@ int nghttp3_http_on_response_headers(nghttp3_http_state *http) { } int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) { - if (stream->flags & NGHTTP3_STREAM_FLAG_RESET) { - return 0; - } if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || (stream->rx.http.content_length != -1 && stream->rx.http.content_length != stream->rx.http.recv_content_length)) { @@ -835,6 +1632,19 @@ static const int VALID_HD_VALUE_CHARS[] = { int nghttp3_check_header_value(const uint8_t *value, size_t len) { const uint8_t *last; + + switch (len) { + case 0: + return 1; + case 1: + return !(*value == ' ' || *value == '\t'); + default: + if (*value == ' ' || *value == '\t' || *(value + len - 1) == ' ' || + *(value + len - 1) == '\t') { + return 0; + } + } + for (last = value + len; value != last; ++value) { if (!VALID_HD_VALUE_CHARS[*value]) { return 0; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.h b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h index 65ec91759babdc..1617348ad14d78 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_http.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h @@ -39,48 +39,55 @@ typedef struct nghttp3_http_state nghttp3_http_state; /* HTTP related flags to enforce HTTP semantics */ /* NGHTTP3_HTTP_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_HTTP_FLAG_NONE 0x00 +#define NGHTTP3_HTTP_FLAG_NONE 0x00u /* header field seen so far */ -#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01 -#define NGHTTP3_HTTP_FLAG__PATH 0x02 -#define NGHTTP3_HTTP_FLAG__METHOD 0x04 -#define NGHTTP3_HTTP_FLAG__SCHEME 0x08 +#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01u +#define NGHTTP3_HTTP_FLAG__PATH 0x02u +#define NGHTTP3_HTTP_FLAG__METHOD 0x04u +#define NGHTTP3_HTTP_FLAG__SCHEME 0x08u /* host is not pseudo header, but we require either host or :authority */ -#define NGHTTP3_HTTP_FLAG_HOST 0x10 -#define NGHTTP3_HTTP_FLAG__STATUS 0x20 +#define NGHTTP3_HTTP_FLAG_HOST 0x10u +#define NGHTTP3_HTTP_FLAG__STATUS 0x20u /* required header fields for HTTP request except for CONNECT method. */ #define NGHTTP3_HTTP_FLAG_REQ_HEADERS \ (NGHTTP3_HTTP_FLAG__METHOD | NGHTTP3_HTTP_FLAG__PATH | \ NGHTTP3_HTTP_FLAG__SCHEME) -#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40 +#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40u /* HTTP method flags */ -#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80 -#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100 -#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200 +#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80u +#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100u +#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200u #define NGHTTP3_HTTP_FLAG_METH_ALL \ (NGHTTP3_HTTP_FLAG_METH_CONNECT | NGHTTP3_HTTP_FLAG_METH_HEAD | \ NGHTTP3_HTTP_FLAG_METH_OPTIONS) /* :path category */ /* path starts with "/" */ -#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400 +#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400u /* path "*" */ -#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800 +#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800u /* scheme */ /* "http" or "https" scheme */ -#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000 +#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000u /* set if final response is expected */ -#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000 +#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000u /* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header field is seen. */ -#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000 +#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000u +/* NGHTTP3_HTTP_FLAG_PRIORITY is set when priority header field is + processed. */ +#define NGHTTP3_HTTP_FLAG_PRIORITY 0x8000u +/* NGHTTP3_HTTP_FLAG_BAD_PRIORITY is set when an error is encountered + while parsing priority header field. */ +#define NGHTTP3_HTTP_FLAG_BAD_PRIORITY 0x010000u /* - * This function is called when HTTP header field |nv| in a frame of type - * |frame_type| is received for |http|. This function will validate |nv| - * against the current state of stream. Pass nonzero if this is request - * headers. Pass nonzero to |trailers| if |nv| is included in trailers. + * This function is called when HTTP header field |nv| received for + * |http|. This function will validate |nv| against the current state + * of stream. Pass nonzero if this is request headers. Pass nonzero + * to |trailers| if |nv| is included in trailers. |connect_protocol| + * is nonzero if Extended CONNECT Method is enabled. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -91,8 +98,8 @@ typedef struct nghttp3_http_state nghttp3_http_state; * Invalid HTTP header field was received but it can be treated as * if it was not received because of compatibility reasons. */ -int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, - nghttp3_qpack_nv *nv, int request, int trailers); +int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv, + int request, int trailers, int connect_protocol); /* * This function is called when request header is received. This @@ -143,4 +150,53 @@ int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n); void nghttp3_http_record_request_method(nghttp3_stream *stream, const nghttp3_nv *nva, size_t nvlen); +/* + * RFC 8941 Structured Field Values. + */ +typedef enum nghttp3_sf_value_type { + NGHTTP3_SF_VALUE_TYPE_BOOLEAN, + NGHTTP3_SF_VALUE_TYPE_INTEGER, + NGHTTP3_SF_VALUE_TYPE_DECIMAL, + NGHTTP3_SF_VALUE_TYPE_STRING, + NGHTTP3_SF_VALUE_TYPE_TOKEN, + NGHTTP3_SF_VALUE_TYPE_BYTESEQ, + NGHTTP3_SF_VALUE_TYPE_INNER_LIST, +} nghttp3_sf_value_type; + +/* + * nghttp3_sf_value stores Structured Field Values item. For Inner + * List, only type is set to NGHTTP3_SF_VALUE_TYPE_INNER_LIST. + */ +typedef struct nghttp3_sf_value { + uint8_t type; + union { + int b; + int64_t i; + double d; + struct { + const uint8_t *base; + size_t len; + } s; + }; +} nghttp3_sf_value; + +/* + * nghttp3_sf_parse_item parses the input sequence [|begin|, |end|) + * and stores the parsed an Item in |dest|. It returns the number of + * bytes consumed if it succeeds, or -1. This function is declared + * here for unit tests. + */ +nghttp3_ssize nghttp3_sf_parse_item(nghttp3_sf_value *dest, + const uint8_t *begin, const uint8_t *end); + +/* + * nghttp3_sf_parse_inner_list parses the input sequence [|begin|, |end|) + * and stores the parsed an Inner List in |dest|. It returns the number of + * bytes consumed if it succeeds, or -1. This function is declared + * here for unit tests. + */ +nghttp3_ssize nghttp3_sf_parse_inner_list(nghttp3_sf_value *dest, + const uint8_t *begin, + const uint8_t *end); + #endif /* NGHTTP3_HTTP_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c index cd8fd82e6b2bb5..dc34841fe0f8ef 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c @@ -27,18 +27,10 @@ #include -int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) { - int rv; - - rv = nghttp3_gaptr_init(&idtr->gap, mem); - if (rv != 0) { - return rv; - } +void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) { + nghttp3_gaptr_init(&idtr->gap, mem); idtr->server = server; - idtr->mem = mem; - - return 0; } void nghttp3_idtr_free(nghttp3_idtr *idtr) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h index c4d60861ab167c..ea3346c9a964c4 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h @@ -45,24 +45,15 @@ typedef struct nghttp3_idtr { /* server is nonzero if this object records server initiated stream ID. */ int server; - /* mem is custom memory allocator */ - const nghttp3_mem *mem; } nghttp3_idtr; /* - * nghttp3_idtr_init initializes |idtr|. |chunk| is the size of buffer - * per chunk. + * nghttp3_idtr_init initializes |idtr|. * * If this object records server initiated ID (even number), set * |server| to nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem); +void nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem); /* * nghttp3_idtr_free frees resources allocated for |idtr|. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c index 0896cb693f9989..adea677abe1f1c 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c @@ -34,9 +34,11 @@ #include "nghttp3_mem.h" #include "nghttp3_range.h" +static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; + static size_t ksl_nodelen(size_t keylen) { - return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & - (size_t)~0xf; + return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) & + ~(uintptr_t)0xfu; } static size_t ksl_blklen(size_t nodelen) { @@ -52,31 +54,48 @@ static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node, memcpy(node->key, key, ksl->keylen); } -int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, - const nghttp3_mem *mem) { +void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, + size_t keylen, const nghttp3_mem *mem) { size_t nodelen = ksl_nodelen(keylen); - size_t blklen = ksl_blklen(nodelen); - nghttp3_ksl_blk *head; - ksl->head = nghttp3_mem_malloc(mem, blklen); - if (!ksl->head) { - return NGHTTP3_ERR_NOMEM; - } - ksl->front = ksl->back = ksl->head; + nghttp3_objalloc_init(&ksl->blkalloc, + ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8, + mem); + + ksl->head = NULL; + ksl->front = ksl->back = NULL; ksl->compar = compar; ksl->keylen = keylen; ksl->nodelen = nodelen; ksl->n = 0; - ksl->mem = mem; +} + +static nghttp3_ksl_blk *ksl_blk_objalloc_new(nghttp3_ksl *ksl) { + return nghttp3_objalloc_ksl_blk_len_get(&ksl->blkalloc, + ksl_blklen(ksl->nodelen)); +} + +static void ksl_blk_objalloc_del(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { + nghttp3_objalloc_ksl_blk_release(&ksl->blkalloc, blk); +} + +static int ksl_head_init(nghttp3_ksl *ksl) { + nghttp3_ksl_blk *head = ksl_blk_objalloc_new(ksl); + if (!head) { + return NGHTTP3_ERR_NOMEM; + } - head = ksl->head; head->next = head->prev = NULL; head->n = 0; head->leaf = 1; + ksl->head = head; + ksl->front = ksl->back = head; + return 0; } +#ifdef NOMEMPOOL /* * ksl_free_blk frees |blk| recursively. */ @@ -89,15 +108,20 @@ static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { } } - nghttp3_mem_free(ksl->mem, blk); + ksl_blk_objalloc_del(ksl, blk); } +#endif /* NOMEMPOOL */ void nghttp3_ksl_free(nghttp3_ksl *ksl) { - if (!ksl) { + if (!ksl || !ksl->head) { return; } +#ifdef NOMEMPOOL ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ + + nghttp3_objalloc_free(&ksl->blkalloc); } /* @@ -111,7 +135,7 @@ void nghttp3_ksl_free(nghttp3_ksl *ksl) { static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { nghttp3_ksl_blk *rblk; - rblk = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + rblk = ksl_blk_objalloc_new(ksl); if (rblk == NULL) { return NULL; } @@ -197,9 +221,9 @@ static int ksl_split_head(nghttp3_ksl *ksl) { lblk = ksl->head; - nhead = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + nhead = ksl_blk_objalloc_new(ksl); if (nhead == NULL) { - nghttp3_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); return NGHTTP3_ERR_NOMEM; } nhead->next = nhead->prev = NULL; @@ -246,29 +270,33 @@ static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i, static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, const nghttp3_ksl_key *key, nghttp3_ksl_compar compar) { - nghttp3_ssize left = -1, right = (nghttp3_ssize)blk->n, mid; + size_t i; nghttp3_ksl_node *node; - while (right - left > 1) { - mid = (left + right) >> 1; - node = nghttp3_ksl_nth_node(ksl, blk, (size_t)mid); - if (compar((nghttp3_ksl_key *)node->key, key)) { - left = mid; - } else { - right = mid; - } - } + for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes; + i < blk->n && compar((nghttp3_ksl_key *)node->key, key); + ++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) + ; - return (size_t)right; + return i; } int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, const nghttp3_ksl_key *key, void *data) { - nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_blk *blk; nghttp3_ksl_node *node; size_t i; int rv; + if (!ksl->head) { + rv = ksl_head_init(ksl); + if (rv != 0) { + return rv; + } + } + + blk = ksl->head; + if (blk->n == NGHTTP3_KSL_MAX_NBLK) { rv = ksl_split_head(ksl); if (rv != 0) { @@ -380,10 +408,10 @@ static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, ksl->back = lblk; } - nghttp3_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); if (ksl->head == blk && blk->n == 2) { - nghttp3_mem_free(ksl->mem, ksl->head); + ksl_blk_objalloc_del(ksl, ksl->head); ksl->head = lblk; } else { ksl_remove_node(ksl, blk, i + 1); @@ -477,12 +505,42 @@ static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs, return !compar(lhs, rhs) && !compar(rhs, lhs); } +int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_it *hint, + const nghttp3_ksl_key *key) { + nghttp3_ksl_blk *blk = hint->blk; + + assert(ksl->head); + + if (blk->n <= NGHTTP3_KSL_MIN_NBLK) { + return nghttp3_ksl_remove(ksl, it, key); + } + + ksl_remove_node(ksl, blk, hint->i); + + --ksl->n; + + if (it) { + if (hint->i == blk->n && blk->next) { + nghttp3_ksl_it_init(it, ksl, blk->next, 0); + } else { + nghttp3_ksl_it_init(it, ksl, blk, hint->i); + } + } + + return 0; +} + int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, const nghttp3_ksl_key *key) { nghttp3_ksl_blk *blk = ksl->head; nghttp3_ksl_node *node; size_t i; + if (!ksl->head) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + if (!blk->leaf && blk->n == 2 && nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK && nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) { @@ -558,6 +616,11 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl, nghttp3_ksl_it it; size_t i; + if (!blk) { + nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, ksl->compar); @@ -595,6 +658,11 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl, nghttp3_ksl_it it; size_t i; + if (!blk) { + nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, compar); @@ -631,6 +699,8 @@ void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, nghttp3_ksl_node *node; size_t i; + assert(ksl->head); + for (;;) { i = ksl_bsearch(ksl, blk, old_key, ksl->compar); @@ -675,36 +745,49 @@ static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) { size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; } void nghttp3_ksl_clear(nghttp3_ksl *ksl) { - size_t i; - nghttp3_ksl_blk *head; - - if (!ksl->head->leaf) { - for (i = 0; i < ksl->head->n; ++i) { - ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, ksl->head, i)->blk); - } + if (!ksl->head) { + return; } - ksl->front = ksl->back = ksl->head; - ksl->n = 0; +#ifdef NOMEMPOOL + ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ - head = ksl->head; + ksl->front = ksl->back = ksl->head = NULL; + ksl->n = 0; - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; + nghttp3_objalloc_clear(&ksl->blkalloc); } -void nghttp3_ksl_print(nghttp3_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } +void nghttp3_ksl_print(nghttp3_ksl *ksl) { + if (!ksl->head) { + return; + } + + ksl_print(ksl, ksl->head, 0); +} nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) { nghttp3_ksl_it it; - nghttp3_ksl_it_init(&it, ksl, ksl->front, 0); + + if (ksl->head) { + nghttp3_ksl_it_init(&it, ksl, ksl->front, 0); + } else { + nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) { nghttp3_ksl_it it; - nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + + if (ksl->head) { + nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + } else { + nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } @@ -715,11 +798,6 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, it->i = i; } -void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) { - assert(it->i < it->blk->n); - return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data; -} - void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) { assert(!nghttp3_ksl_it_begin(it)); diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h index 7ba36bb9cb107e..0bc10e846fe418 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h @@ -34,6 +34,8 @@ #include +#include "nghttp3_objalloc.h" + /* * Skip List using single key instead of range. */ @@ -80,25 +82,34 @@ struct nghttp3_ksl_node { * nghttp3_ksl_blk contains nghttp3_ksl_node objects. */ struct nghttp3_ksl_blk { - /* next points to the next block if leaf field is nonzero. */ - nghttp3_ksl_blk *next; - /* prev points to the previous block if leaf field is nonzero. */ - nghttp3_ksl_blk *prev; - /* n is the number of nodes this object contains in nodes. */ - uint32_t n; - /* leaf is nonzero if this block contains leaf nodes. */ - uint32_t leaf; union { - uint64_t align; - /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK - nghttp3_ksl_node objects. Because nghttp3_ksl_node object is - allocated along with the additional variable length key - storage, the size of buffer is unknown until nghttp3_ksl_init - is called. */ - uint8_t nodes[1]; + struct { + /* next points to the next block if leaf field is nonzero. */ + nghttp3_ksl_blk *next; + /* prev points to the previous block if leaf field is + nonzero. */ + nghttp3_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + uint32_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + uint32_t leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK + nghttp3_ksl_node objects. Because nghttp3_ksl_node object + is allocated along with the additional variable length key + storage, the size of buffer is unknown until + nghttp3_ksl_init is called. */ + uint8_t nodes[1]; + }; + }; + + nghttp3_opl_entry oplent; }; }; +nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent); + /* * nghttp3_ksl_compar is a function type which returns nonzero if key * |lhs| should be placed before |rhs|. It returns 0 otherwise. @@ -123,6 +134,7 @@ struct nghttp3_ksl_it { * nghttp3_ksl is a deterministic paged skip list. */ struct nghttp3_ksl { + nghttp3_objalloc blkalloc; /* head points to the root block. */ nghttp3_ksl_blk *head; /* front points to the first leaf block. */ @@ -136,21 +148,14 @@ struct nghttp3_ksl { /* nodelen is the actual size of nghttp3_ksl_node including key storage. */ size_t nodelen; - const nghttp3_mem *mem; }; /* * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare * function. |keylen| is the length of key. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, - const nghttp3_mem *mem); +void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, + size_t keylen, const nghttp3_mem *mem); /* * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is @@ -192,6 +197,17 @@ int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, const nghttp3_ksl_key *key); +/* + * nghttp3_ksl_remove_hint removes the |key| from |ksl|. |hint| must + * point to the same node denoted by |key|. |hint| is used to remove + * a node efficiently in some cases. Other than that, it behaves + * exactly like nghttp3_ksl_remove. |it| and |hint| can point to the + * same object. + */ +int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_it *hint, + const nghttp3_ksl_key *key); + /* * nghttp3_ksl_lower_bound returns the iterator which points to the * first node which has the key which is equal to |key| or the last @@ -267,7 +283,8 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, * |it| points to. It is undefined to call this function when * nghttp3_ksl_it_end(it) returns nonzero. */ -void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it); +#define nghttp3_ksl_it_get(IT) \ + nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data /* * nghttp3_ksl_it_next advances the iterator by one. It is undefined diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h index 6ee704cc47dd98..a44e907661abbf 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h @@ -44,4 +44,8 @@ #define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) +/* NGHTTP3_MAX_VARINT` is the maximum value which can be encoded in + variable-length integer encoding. */ +#define NGHTTP3_MAX_VARINT ((1ULL << 62) - 1) + #endif /* NGHTTP3_MACRO_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c index a80955ad7a4c83..fcfc31ae41e8e0 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c @@ -28,172 +28,166 @@ #include #include +#include #include "nghttp3_conv.h" -#define INITIAL_TABLE_LENGTH 256 +#define NGHTTP3_INITIAL_TABLE_LENBITS 4 -int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { +void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { map->mem = mem; - map->tablelen = INITIAL_TABLE_LENGTH; - map->table = - nghttp3_mem_calloc(mem, map->tablelen, sizeof(nghttp3_map_bucket)); - if (map->table == NULL) { - return NGHTTP3_ERR_NOMEM; - } - + map->tablelen = 0; + map->tablelenbits = 0; + map->table = NULL; map->size = 0; - - return 0; } void nghttp3_map_free(nghttp3_map *map) { - size_t i; - nghttp3_map_bucket *bkt; - if (!map) { return; } - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } - } - nghttp3_mem_free(map->mem, map->table); } -void nghttp3_map_each_free(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), +void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr), void *ptr) { uint32_t i; nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - func(bkt->ptr, ptr); - bkt->ptr = NULL; - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - func(nghttp3_ksl_it_get(&it), ptr); - } - - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } + func(bkt->data, ptr); } } -int nghttp3_map_each(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), +int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr), void *ptr) { int rv; uint32_t i; nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; + + if (map->size == 0) { + return 0; + } for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - rv = func(bkt->ptr, ptr); - if (rv != 0) { - return rv; - } - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - rv = func(nghttp3_ksl_it_get(&it), ptr); - if (rv != 0) { - return rv; - } - } + rv = func(bkt->data, ptr); + if (rv != 0) { + return rv; } } + return 0; } -void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) { - entry->key = key; +static uint32_t hash(nghttp3_map_key_type key) { + return (uint32_t)((key * 11400714819323198485llu) >> 32); } -/* FNV1a hash */ -static uint32_t hash(key_type key, uint32_t mod) { - uint8_t *p, *end; - uint32_t h = 0x811C9DC5u; +static size_t h2idx(uint32_t hash, uint32_t bits) { + return hash >> (32 - bits); +} - key = nghttp3_htonl64(key); - p = (uint8_t *)&key; - end = p + sizeof(key_type); +static size_t distance(uint32_t tablelen, uint32_t tablelenbits, + nghttp3_map_bucket *bkt, size_t idx) { + return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1); +} - for (; p != end;) { - h ^= *p++; - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } +static void map_bucket_swap(nghttp3_map_bucket *bkt, uint32_t *phash, + nghttp3_map_key_type *pkey, void **pdata) { + uint32_t h = bkt->hash; + nghttp3_map_key_type key = bkt->key; + void *data = bkt->data; - return h & (mod - 1); + bkt->hash = *phash; + bkt->key = *pkey; + bkt->data = *pdata; + + *phash = h; + *pkey = key; + *pdata = data; } -static int less(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) { - return *(key_type *)lhs < *(key_type *)rhs; +static void map_bucket_set_data(nghttp3_map_bucket *bkt, uint32_t hash, + nghttp3_map_key_type key, void *data) { + bkt->hash = hash; + bkt->key = key; + bkt->data = data; } -static int map_insert(nghttp3_map *map, nghttp3_map_bucket *table, - uint32_t tablelen, nghttp3_map_entry *entry) { - uint32_t h = hash(entry->key, tablelen); - nghttp3_map_bucket *bkt = &table[h]; - const nghttp3_mem *mem = map->mem; - int rv; +void nghttp3_map_print_distance(nghttp3_map *map) { + uint32_t i; + size_t idx; + nghttp3_map_bucket *bkt; - if (bkt->ptr == NULL && - (bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0)) { - bkt->ptr = entry; - return 0; - } + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; - if (!bkt->ksl) { - bkt->ksl = nghttp3_mem_malloc(mem, sizeof(*bkt->ksl)); - if (bkt->ksl == NULL) { - return NGHTTP3_ERR_NOMEM; + if (bkt->data == NULL) { + fprintf(stderr, "@%u \n", i); + continue; } - nghttp3_ksl_init(bkt->ksl, less, sizeof(key_type), mem); + + idx = h2idx(bkt->hash, map->tablelenbits); + fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i, + bkt->hash, bkt->key, idx, + distance(map->tablelen, map->tablelenbits, bkt, idx)); } +} - if (bkt->ptr) { - rv = nghttp3_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); - if (rv != 0) { - return rv; +static int insert(nghttp3_map_bucket *table, uint32_t tablelen, + uint32_t tablelenbits, uint32_t hash, + nghttp3_map_key_type key, void *data) { + size_t idx = h2idx(hash, tablelenbits); + size_t d = 0, dd; + nghttp3_map_bucket *bkt; + + for (;;) { + bkt = &table[idx]; + + if (bkt->data == NULL) { + map_bucket_set_data(bkt, hash, key, data); + return 0; } - bkt->ptr = NULL; - } + dd = distance(tablelen, tablelenbits, bkt, idx); + if (d > dd) { + map_bucket_swap(bkt, &hash, &key, &data); + d = dd; + } else if (bkt->key == key) { + /* TODO This check is just a waste after first swap or if this + function is called from map_resize. That said, there is no + difference with or without this conditional in performance + wise. */ + return NGHTTP3_ERR_INVALID_ARGUMENT; + } - return nghttp3_ksl_insert(bkt->ksl, NULL, &entry->key, entry); + ++d; + idx = (idx + 1) & (tablelen - 1); + } } -/* new_tablelen must be power of 2 */ -static int map_resize(nghttp3_map *map, uint32_t new_tablelen) { +/* new_tablelen must be power of 2 and new_tablelen == (1 << + new_tablelenbits) must hold. */ +static int map_resize(nghttp3_map *map, uint32_t new_tablelen, + uint32_t new_tablelenbits) { uint32_t i; nghttp3_map_bucket *new_table; nghttp3_map_bucket *bkt; - nghttp3_ksl_it it; int rv; + (void)rv; new_table = nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket)); @@ -203,64 +197,46 @@ static int map_resize(nghttp3_map *map, uint32_t new_tablelen) { for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - - if (bkt->ptr) { - rv = map_insert(map, new_table, new_tablelen, bkt->ptr); - if (rv != 0) { - goto fail; - } - assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } + rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key, + bkt->data); - if (bkt->ksl) { - for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); - nghttp3_ksl_it_next(&it)) { - rv = map_insert(map, new_table, new_tablelen, nghttp3_ksl_it_get(&it)); - if (rv != 0) { - goto fail; - } - } - } - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } + assert(0 == rv); } nghttp3_mem_free(map->mem, map->table); map->tablelen = new_tablelen; + map->tablelenbits = new_tablelenbits; map->table = new_table; return 0; - -fail: - for (i = 0; i < new_tablelen; ++i) { - bkt = &new_table[i]; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - } - } - - return rv; } -int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) { +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) { int rv; + assert(data); + /* Load factor is 0.75 */ if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = map_resize(map, map->tablelen * 2); - if (rv != 0) { - return rv; + if (map->tablelen) { + rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); + if (rv != 0) { + return rv; + } + } else { + rv = map_resize(map, 1 << NGHTTP3_INITIAL_TABLE_LENBITS, + NGHTTP3_INITIAL_TABLE_LENBITS); + if (rv != 0) { + return rv; + } } } - rv = map_insert(map, map->table, map->tablelen, new_entry); + + rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key, + data); if (rv != 0) { return rv; } @@ -268,68 +244,93 @@ int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) { return 0; } -nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key) { - nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - nghttp3_ksl_it it; +void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key) { + uint32_t h; + size_t idx; + nghttp3_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - return bkt->ptr; - } + if (map->size == 0) { return NULL; } - if (bkt->ksl) { - it = nghttp3_ksl_lower_bound(bkt->ksl, &key); - if (nghttp3_ksl_it_end(&it) || - *(key_type *)nghttp3_ksl_it_key(&it) != key) { + h = hash(key); + idx = h2idx(h, map->tablelenbits); + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { return NULL; } - return nghttp3_ksl_it_get(&it); - } - return NULL; + if (bkt->key == key) { + return bkt->data; + } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } } -int nghttp3_map_remove(nghttp3_map *map, key_type key) { - nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - int rv; +int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) { + uint32_t h; + size_t idx, didx; + nghttp3_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - bkt->ptr = NULL; - --map->size; - return 0; - } + if (map->size == 0) { return NGHTTP3_ERR_INVALID_ARGUMENT; } - if (bkt->ksl) { - rv = nghttp3_ksl_remove(bkt->ksl, NULL, &key); - if (rv != 0) { - return rv; + h = hash(key); + idx = h2idx(h, map->tablelenbits); + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { + return NGHTTP3_ERR_INVALID_ARGUMENT; } - --map->size; - return 0; - } - return NGHTTP3_ERR_INVALID_ARGUMENT; -} + if (bkt->key == key) { + map_bucket_set_data(bkt, 0, 0, NULL); -void nghttp3_map_clear(nghttp3_map *map) { - uint32_t i; - nghttp3_map_bucket *bkt; + didx = idx; + idx = (idx + 1) & (map->tablelen - 1); - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - bkt->ptr = NULL; - if (bkt->ksl) { - nghttp3_ksl_free(bkt->ksl); - nghttp3_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; + for (;;) { + bkt = &map->table[idx]; + if (bkt->data == NULL || + distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) { + break; + } + + map->table[didx] = *bkt; + map_bucket_set_data(bkt, 0, 0, NULL); + didx = idx; + + idx = (idx + 1) & (map->tablelen - 1); + } + + --map->size; + + return 0; } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } +} + +void nghttp3_map_clear(nghttp3_map *map) { + if (map->tablelen == 0) { + return; } + memset(map->table, 0, sizeof(*map->table) * map->tablelen); map->size = 0; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h index 2eae2e7cb725c6..79dff0286bc3cc 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h @@ -34,21 +34,15 @@ #include #include "nghttp3_mem.h" -#include "nghttp3_ksl.h" /* Implementation of unordered map */ -typedef uint64_t key_type; - -typedef struct nghttp3_map_entry nghttp3_map_entry; - -struct nghttp3_map_entry { - key_type key; -}; +typedef uint64_t nghttp3_map_key_type; typedef struct nghttp3_map_bucket { - nghttp3_map_entry *ptr; - nghttp3_ksl *ksl; + uint32_t hash; + nghttp3_map_key_type key; + void *data; } nghttp3_map_bucket; typedef struct nghttp3_map { @@ -56,18 +50,13 @@ typedef struct nghttp3_map { const nghttp3_mem *mem; size_t size; uint32_t tablelen; + uint32_t tablelenbits; } nghttp3_map; /* * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory */ -int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); +void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); /* * Deallocates any resources allocated for |map|. The stored entries @@ -79,21 +68,14 @@ void nghttp3_map_free(nghttp3_map *map); /* * Deallocates each entries using |func| function and any resources * allocated for |map|. The |func| function is responsible for freeing - * given the |entry| object. The |ptr| will be passed to the |func| as + * given the |data| object. The |ptr| will be passed to the |func| as * send argument. The return value of the |func| will be ignored. */ -void nghttp3_map_each_free(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), +void nghttp3_map_each_free(nghttp3_map *map, int (*func)(void *data, void *ptr), void *ptr); /* - * Initializes the |entry| with the |key|. All entries to be inserted - * to the map must be initialized with this function. - */ -void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key); - -/* - * Inserts the new |entry| with the key |entry->key| to the map |map|. + * Inserts the new |data| with the |key| to the map |map|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -103,25 +85,25 @@ void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key); * NGHTTP3_ERR_NOMEM * Out of memory */ -int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *entry); +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data); /* - * Returns the entry associated by the key |key|. If there is no such - * entry, this function returns NULL. + * Returns the data associated by the key |key|. If there is no such + * data, this function returns NULL. */ -nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key); +void *nghttp3_map_find(nghttp3_map *map, nghttp3_map_key_type key); /* - * Removes the entry associated by the key |key| from the |map|. The - * removed entry is not freed by this function. + * Removes the data associated by the key |key| from the |map|. The + * removed data is not freed by this function. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP3_ERR_INVALID_ARGUMENT - * The entry associated by |key| does not exist. + * The data associated by |key| does not exist. */ -int nghttp3_map_remove(nghttp3_map *map, key_type key); +int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key); /* * Removes all entries from |map|. @@ -134,21 +116,22 @@ void nghttp3_map_clear(nghttp3_map *map); size_t nghttp3_map_size(nghttp3_map *map); /* - * Applies the function |func| to each entry in the |map| with the + * Applies the function |func| to each data in the |map| with the * optional user supplied pointer |ptr|. * * If the |func| returns 0, this function calls the |func| with the - * next entry. If the |func| returns nonzero, it will not call the + * next data. If the |func| returns nonzero, it will not call the * |func| for further entries and return the return value of the * |func| immediately. Thus, this function returns 0 if all the * invocations of the |func| return 0, or nonzero value which the last * invocation of |func| returns. * - * Don't use this function to free each entry. Use + * Don't use this function to free each data. Use * nghttp3_map_each_free() instead. */ -int nghttp3_map_each(nghttp3_map *map, - int (*func)(nghttp3_map_entry *entry, void *ptr), +int nghttp3_map_each(nghttp3_map *map, int (*func)(void *data, void *ptr), void *ptr); +void nghttp3_map_print_distance(nghttp3_map *map); + #endif /* NGHTTP3_MAP_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c index e5f93f10bdabf2..0379e99b59cb74 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c @@ -26,26 +26,28 @@ */ #include "nghttp3_mem.h" -static void *default_malloc(size_t size, void *mem_user_data) { - (void)mem_user_data; +#include + +static void *default_malloc(size_t size, void *user_data) { + (void)user_data; return malloc(size); } -static void default_free(void *ptr, void *mem_user_data) { - (void)mem_user_data; +static void default_free(void *ptr, void *user_data) { + (void)user_data; free(ptr); } -static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_calloc(size_t nmemb, size_t size, void *user_data) { + (void)user_data; return calloc(nmemb, size); } -static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_realloc(void *ptr, size_t size, void *user_data) { + (void)user_data; return realloc(ptr, size); } @@ -55,23 +57,68 @@ static nghttp3_mem mem_default = {NULL, default_malloc, default_free, const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; } +#ifndef MEMDEBUG void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); + return mem->malloc(size, mem->user_data); } void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, - void *mem_user_data) { - free_func(ptr, mem_user_data); + mem->free(ptr, mem->user_data); } void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); + return mem->calloc(nmemb, size, mem->user_data); } void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); + return mem->realloc(ptr, size, mem->user_data); +} +#else /* MEMDEBUG */ +void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size, + const char *func, const char *file, + size_t line) { + void *nptr = mem->malloc(size, mem->user_data); + + fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func, + file, line); + + return nptr; +} + +void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func, + const char *file, size_t line) { + fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + + mem->free(ptr, mem->user_data); +} + +void nghttp3_mem_free2_debug(const nghttp3_free free_func, void *ptr, + void *user_data, const char *func, + const char *file, size_t line) { + fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + + free_func(ptr, user_data); +} + +void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb, + size_t size, const char *func, const char *file, + size_t line) { + void *nptr = mem->calloc(nmemb, size, mem->user_data); + + fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb, + size, func, file, line); + + return nptr; +} + +void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size, + const char *func, const char *file, + size_t line) { + void *nptr = mem->realloc(ptr, size, mem->user_data); + + fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr, + size, func, file, line); + + return nptr; } +#endif /* MEMDEBUG */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h index 55ef86b4f921c9..d6c3ada6f7e894 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h @@ -35,11 +35,46 @@ /* Convenient wrapper functions to call allocator function in |mem|. */ +#ifndef MEMDEBUG void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size); void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr); -void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, - void *mem_user_data); void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size); void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size); +#else /* MEMDEBUG */ +void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size, + const char *func, const char *file, size_t line); + +# define nghttp3_mem_malloc(MEM, SIZE) \ + nghttp3_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__) + +void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func, + const char *file, size_t line); + +# define nghttp3_mem_free(MEM, PTR) \ + nghttp3_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__) + +void nghttp3_mem_free2_debug(nghttp3_free free_func, void *ptr, void *user_data, + const char *func, const char *file, size_t line); + +# define nghttp3_mem_free2(FREE_FUNC, PTR, USER_DATA) \ + nghttp3_mem_free2_debug((FREE_FUNC), (PTR), (USER_DATA), __func__, \ + __FILE__, __LINE__) + +void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb, + size_t size, const char *func, const char *file, + size_t line); + +# define nghttp3_mem_calloc(MEM, NMEMB, SIZE) \ + nghttp3_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \ + __LINE__) + +void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size, + const char *func, const char *file, + size_t line); + +# define nghttp3_mem_realloc(MEM, PTR, SIZE) \ + nghttp3_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, \ + __LINE__) +#endif /* MEMDEBUG */ #endif /* NGHTTP3_MEM_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c new file mode 100644 index 00000000000000..0c97860136ed34 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.c @@ -0,0 +1,41 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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. + */ +#include "nghttp3_objalloc.h" + +void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen, + const nghttp3_mem *mem) { + nghttp3_balloc_init(&objalloc->balloc, blklen, mem); + nghttp3_opl_init(&objalloc->opl); +} + +void nghttp3_objalloc_free(nghttp3_objalloc *objalloc) { + nghttp3_balloc_free(&objalloc->balloc); +} + +void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc) { + nghttp3_opl_clear(&objalloc->opl); + nghttp3_balloc_clear(&objalloc->balloc); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h new file mode 100644 index 00000000000000..da39447a872b02 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_objalloc.h @@ -0,0 +1,141 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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 NGHTTP3_OBJALLOC_H +#define NGHTTP3_OBJALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_balloc.h" +#include "nghttp3_opl.h" +#include "nghttp3_macro.h" +#include "nghttp3_mem.h" + +/* + * nghttp3_objalloc combines nghttp3_balloc and nghttp3_opl, and + * provides an object pool with the custom allocator to reduce the + * allocation and deallocation overheads for small objects. + */ +typedef struct nghttp3_objalloc { + nghttp3_balloc balloc; + nghttp3_opl opl; +} nghttp3_objalloc; + +/* + * nghttp3_objalloc_init initializes |objalloc|. |blklen| is directly + * passed to nghttp3_balloc_init. + */ +void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen, + const nghttp3_mem *mem); + +/* + * nghttp3_objalloc_free releases all allocated resources. + */ +void nghttp3_objalloc_free(nghttp3_objalloc *objalloc); + +/* + * nghttp3_objalloc_clear releases all allocated resources and + * initializes its state. + */ +void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc); + +#ifndef NOMEMPOOL +# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void nghttp3_objalloc_##NAME##_init( \ + nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \ + nghttp3_objalloc_init( \ + objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \ + } \ + \ + inline static TYPE *nghttp3_objalloc_##NAME##_get( \ + nghttp3_objalloc *objalloc) { \ + nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, \ + sizeof(TYPE)); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \ + nghttp3_objalloc *objalloc, size_t len) { \ + nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, len); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static void nghttp3_objalloc_##NAME##_release( \ + nghttp3_objalloc *objalloc, TYPE *obj) { \ + nghttp3_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \ + } +#else /* NOMEMPOOL */ +# define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void nghttp3_objalloc_##NAME##_init( \ + nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) { \ + nghttp3_objalloc_init( \ + objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \ + } \ + \ + inline static TYPE *nghttp3_objalloc_##NAME##_get( \ + nghttp3_objalloc *objalloc) { \ + return nghttp3_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \ + } \ + \ + inline static TYPE *nghttp3_objalloc_##NAME##_len_get( \ + nghttp3_objalloc *objalloc, size_t len) { \ + return nghttp3_mem_malloc(objalloc->balloc.mem, len); \ + } \ + \ + inline static void nghttp3_objalloc_##NAME##_release( \ + nghttp3_objalloc *objalloc, TYPE *obj) { \ + nghttp3_mem_free(objalloc->balloc.mem, obj); \ + } +#endif /* NOMEMPOOL */ + +#endif /* NGHTTP3_OBJALLOC_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c new file mode 100644 index 00000000000000..eb8ebdd1854c8f --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.c @@ -0,0 +1,47 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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. + */ +#include "nghttp3_opl.h" + +void nghttp3_opl_init(nghttp3_opl *opl) { opl->head = NULL; } + +void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent) { + ent->next = opl->head; + opl->head = ent; +} + +nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl) { + nghttp3_opl_entry *ent = opl->head; + + if (!ent) { + return NULL; + } + + opl->head = ent->next; + + return ent; +} + +void nghttp3_opl_clear(nghttp3_opl *opl) { opl->head = NULL; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h new file mode 100644 index 00000000000000..8c8a4f2051b25a --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_opl.h @@ -0,0 +1,66 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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 NGHTTP3_OPL_H +#define NGHTTP3_OPL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct nghttp3_opl_entry nghttp3_opl_entry; + +struct nghttp3_opl_entry { + nghttp3_opl_entry *next; +}; + +/* + * nghttp3_opl is an object memory pool. + */ +typedef struct nghttp3_opl { + nghttp3_opl_entry *head; +} nghttp3_opl; + +/* + * nghttp3_opl_init initializes |opl|. + */ +void nghttp3_opl_init(nghttp3_opl *opl); + +/* + * nghttp3_opl_push inserts |ent| to |opl| head. + */ +void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent); + +/* + * nghttp3_opl_pop removes the first nghttp3_opl_entry from |opl| and + * returns it. If |opl| does not have any entry, it returns NULL. + */ +nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl); + +void nghttp3_opl_clear(nghttp3_opl *opl); + +#endif /* NGHTTP3_OPL_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h index f1f369231dfcdc..c1a54f505bbd03 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h @@ -111,7 +111,7 @@ size_t nghttp3_pq_size(const nghttp3_pq *pq); typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg); /* - * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * Applies |fun| to each item in |pq|. The |arg| is passed as arg * parameter to callback function. This function must not change the * ordering key. If the return value from callback is nonzero, this * function returns 1 immediately without iterating remaining items. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c index 6837942687aa88..ddb3dd6d84bf8a 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c @@ -168,8 +168,8 @@ static nghttp3_qpack_static_entry token_stable[] = { /* Make scalar initialization form of nghttp3_qpack_static_entry */ #define MAKE_STATIC_HD(N, V, T) \ { \ - {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ - {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \ + {NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ + {NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \ } static nghttp3_qpack_static_header stable[] = { @@ -775,12 +775,12 @@ static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { /* * qpack_context_can_reference returns nonzero if dynamic table entry * at |absidx| can be referenced. In other words, it is within - * ctx->max_dtable_size. + * ctx->max_dtable_capacity. */ static int qpack_context_can_reference(nghttp3_qpack_context *ctx, uint64_t absidx) { nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); - return ctx->dtable_sum - ent->sum <= ctx->max_dtable_size; + return ctx->dtable_sum - ent->sum <= ctx->max_dtable_capacity; } /* |*ppb_match| (post-base match), if it is not NULL, is always exact @@ -824,8 +824,14 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, } /* - * qpack_context_init initializes |ctx|. |max_dtable_size| is the - * maximum size of dynamic table. |mem| is a memory allocator. + * qpack_context_init initializes |ctx|. |hard_max_dtable_capacity| + * is the upper bound of the dynamic table capacity. |mem| is a + * memory allocator. + * + * The maximum dynamic table size is governed by + * ctx->max_dtable_capacity and it is initialized to 0. + * |hard_max_dtable_capacity| is the upper bound of + * ctx->max_dtable_capacity. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -834,7 +840,8 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, * Out of memory. */ static int qpack_context_init(nghttp3_qpack_context *ctx, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem) { int rv; size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD; @@ -852,9 +859,9 @@ static int qpack_context_init(nghttp3_qpack_context *ctx, ctx->mem = mem; ctx->dtable_size = 0; ctx->dtable_sum = 0; - ctx->hard_max_dtable_size = max_dtable_size; - ctx->max_dtable_size = 0; - ctx->max_blocked = max_blocked; + ctx->hard_max_dtable_capacity = hard_max_dtable_capacity; + ctx->max_dtable_capacity = 0; + ctx->max_blocked_streams = max_blocked_streams; ctx->next_absidx = 0; ctx->bad = 0; @@ -896,25 +903,19 @@ static int max_cnt_greater(const nghttp3_ksl_key *lhs, } int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, const nghttp3_mem *mem) { int rv; - rv = qpack_context_init(&encoder->ctx, max_dtable_size, max_blocked, mem); + rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem); if (rv != 0) { return rv; } - rv = nghttp3_map_init(&encoder->streams, mem); - if (rv != 0) { - goto streams_init_fail; - } + nghttp3_map_init(&encoder->streams, mem); - rv = nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, - sizeof(nghttp3_blocked_streams_key), mem); - if (rv != 0) { - goto blocked_streams_init_fail; - } + nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, + sizeof(nghttp3_blocked_streams_key), mem); qpack_map_init(&encoder->dtable_map); nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem); @@ -929,19 +930,11 @@ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, nghttp3_qpack_read_state_reset(&encoder->rstate); return 0; - -blocked_streams_init_fail: - nghttp3_map_free(&encoder->streams); -streams_init_fail: - qpack_context_free(&encoder->ctx); - - return rv; } -static int map_stream_free(nghttp3_map_entry *entry, void *ptr) { +static int map_stream_free(void *data, void *ptr) { const nghttp3_mem *mem = ptr; - nghttp3_qpack_stream *stream = - nghttp3_struct_of(entry, nghttp3_qpack_stream, me); + nghttp3_qpack_stream *stream = data; nghttp3_qpack_stream_del(stream, mem); return 0; } @@ -955,49 +948,27 @@ void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) { qpack_context_free(&encoder->ctx); } -int nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size) { - if (encoder->ctx.hard_max_dtable_size < max_dtable_size) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } +void nghttp3_qpack_encoder_set_max_dtable_capacity( + nghttp3_qpack_encoder *encoder, size_t max_dtable_capacity) { + max_dtable_capacity = + nghttp3_min(max_dtable_capacity, encoder->ctx.hard_max_dtable_capacity); - if (encoder->ctx.max_dtable_size == max_dtable_size) { - return 0; + if (encoder->ctx.max_dtable_capacity == max_dtable_capacity) { + return; } encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; - if (encoder->min_dtable_update > max_dtable_size) { - encoder->min_dtable_update = max_dtable_size; - encoder->ctx.max_dtable_size = max_dtable_size; + if (encoder->min_dtable_update > max_dtable_capacity) { + encoder->min_dtable_update = max_dtable_capacity; + encoder->ctx.max_dtable_capacity = max_dtable_capacity; } - encoder->last_max_dtable_update = max_dtable_size; - - return 0; + encoder->last_max_dtable_update = max_dtable_capacity; } -int nghttp3_qpack_encoder_set_hard_max_dtable_size( - nghttp3_qpack_encoder *encoder, size_t hard_max_dtable_size) { - /* TODO This is not ideal. */ - if (encoder->ctx.hard_max_dtable_size) { - return NGHTTP3_ERR_INVALID_STATE; - } - - encoder->ctx.hard_max_dtable_size = hard_max_dtable_size; - - return 0; -} - -int nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, - size_t max_blocked) { - /* TODO This is not ideal. */ - if (encoder->ctx.max_blocked) { - return NGHTTP3_ERR_INVALID_STATE; - } - - encoder->ctx.max_blocked = max_blocked; - - return 0; +void nghttp3_qpack_encoder_set_max_blocked_streams( + nghttp3_qpack_encoder *encoder, size_t max_blocked_streams) { + encoder->ctx.max_blocked_streams = max_blocked_streams; } uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) { @@ -1015,7 +986,7 @@ void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) { size_t len; nghttp3_qpack_entry *ent; - if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_size) { + if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_capacity) { return; } @@ -1023,7 +994,7 @@ void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) { min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder); } - for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_size;) { + for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_capacity;) { len = nghttp3_ringbuf_len(dtable); ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); if (ent->absidx + 1 == min_cnt) { @@ -1069,7 +1040,8 @@ static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder, assert(rv == NGHTTP3_ERR_NOMEM); return rv; } - rv = nghttp3_map_insert(&encoder->streams, &stream->me); + rv = nghttp3_map_insert(&encoder->streams, + (nghttp3_map_key_type)stream->stream_id, stream); if (rv != 0) { assert(rv == NGHTTP3_ERR_NOMEM); nghttp3_qpack_stream_del(stream, mem); @@ -1110,7 +1082,8 @@ static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder, size_t i, len; nghttp3_qpack_header_block_ref *ref; - nghttp3_map_remove(&encoder->streams, stream->me.key); + nghttp3_map_remove(&encoder->streams, + (nghttp3_map_key_type)stream->stream_id); len = nghttp3_ringbuf_len(&stream->refs); for (i = 0; i < len; ++i) { @@ -1190,8 +1163,8 @@ int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder, blocked_stream = stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream); allow_blocking = - blocked_stream || - encoder->ctx.max_blocked > nghttp3_ksl_len(&encoder->blocked_streams); + blocked_stream || encoder->ctx.max_blocked_streams > + nghttp3_ksl_len(&encoder->blocked_streams); DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id, blocked_stream, allow_blocking); @@ -1265,7 +1238,7 @@ int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, nghttp3_qpack_encoder_shrink_dtable(encoder); - if (encoder->ctx.max_dtable_size < encoder->ctx.dtable_size || + if (encoder->ctx.max_dtable_capacity < encoder->ctx.dtable_size || !(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) { return 0; } @@ -1286,7 +1259,7 @@ int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; encoder->min_dtable_update = SIZE_MAX; - encoder->ctx.max_dtable_size = encoder->last_max_dtable_update; + encoder->ctx.max_dtable_capacity = encoder->last_max_dtable_update; return 0; } @@ -1300,9 +1273,7 @@ int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, nghttp3_qpack_stream * nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, int64_t stream_id) { - nghttp3_map_entry *me = - nghttp3_map_find(&encoder->streams, (uint64_t)stream_id); - return me == NULL ? NULL : nghttp3_struct_of(me, nghttp3_qpack_stream, me); + return nghttp3_map_find(&encoder->streams, (nghttp3_map_key_type)stream_id); } int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, @@ -1352,7 +1323,7 @@ qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder, } if (table_space(nv->namelen, nv->valuelen) > - encoder->ctx.max_dtable_size * 3 / 4) { + encoder->ctx.max_dtable_capacity * 3 / 4) { return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; } @@ -1372,8 +1343,8 @@ static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need, nghttp3_qpack_entry *min_ent, *last_ent; nghttp3_ringbuf *dtable = &encoder->ctx.dtable; - if (encoder->ctx.max_dtable_size > encoder->ctx.dtable_size) { - avail = encoder->ctx.max_dtable_size - encoder->ctx.dtable_size; + if (encoder->ctx.max_dtable_capacity > encoder->ctx.dtable_size) { + avail = encoder->ctx.max_dtable_capacity - encoder->ctx.dtable_size; if (need <= avail) { return 1; } @@ -1385,7 +1356,7 @@ static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need, } if (min_cnt == UINT64_MAX) { - return encoder->ctx.max_dtable_size >= need; + return encoder->ctx.max_dtable_capacity >= need; } min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1); @@ -1434,8 +1405,8 @@ static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder, */ static int qpack_context_check_draining(nghttp3_qpack_context *ctx, uint64_t absidx) { - const size_t safe = - ctx->max_dtable_size - nghttp3_min(512, ctx->max_dtable_size * 1 / 8); + const size_t safe = ctx->max_dtable_capacity - + nghttp3_min(512, ctx->max_dtable_capacity * 1 / 8); nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); return ctx->dtable_sum - ent->sum > safe; @@ -1714,7 +1685,7 @@ int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem); - stream->me.key = (uint64_t)stream_id; + stream->stream_id = stream_id; *pstream = stream; @@ -2059,9 +2030,9 @@ int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, space = table_space(qnv->name->len, qnv->value->len); - assert(space <= ctx->max_dtable_size); + assert(space <= ctx->max_dtable_capacity); - while (ctx->dtable_size + space > ctx->max_dtable_size) { + while (ctx->dtable_size + space > ctx->max_dtable_capacity) { i = nghttp3_ringbuf_len(&ctx->dtable); assert(i); ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); @@ -2265,7 +2236,7 @@ int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), nghttp3_qpack_header_block_ref, max_cnts_pe) ->max_cnt, - stream->me.key}; + (uint64_t)stream->stream_id}; return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream); } @@ -2276,7 +2247,7 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), nghttp3_qpack_header_block_ref, max_cnts_pe) ->max_cnt, - stream->me.key}; + (uint64_t)stream->stream_id}; nghttp3_ksl_it it; /* This is purely debugging purpose only */ @@ -2285,7 +2256,7 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, assert(!nghttp3_ksl_it_end(&it)); assert(nghttp3_ksl_it_get(&it) == stream); - nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &bsk); + nghttp3_ksl_remove_hint(&encoder->blocked_streams, NULL, &it, &bsk); } void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, @@ -2297,21 +2268,19 @@ void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, for (; !nghttp3_ksl_it_end(&it);) { bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it); - nghttp3_ksl_remove(&encoder->blocked_streams, &it, &bsk); + nghttp3_ksl_remove_hint(&encoder->blocked_streams, &it, &it, &bsk); } } -void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, - int64_t stream_id) { +int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, + int64_t stream_id) { nghttp3_qpack_stream *stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id); const nghttp3_mem *mem = encoder->ctx.mem; nghttp3_qpack_header_block_ref *ref; if (stream == NULL) { - /* This might be NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR, but we - don't create stream which does not use dynamic table. */ - return; + return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; } assert(nghttp3_ringbuf_len(&stream->refs)); @@ -2338,16 +2307,17 @@ void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, nghttp3_qpack_header_block_ref_del(ref, mem); if (nghttp3_ringbuf_len(&stream->refs)) { - return; + return 0; } qpack_encoder_remove_stream(encoder, stream); nghttp3_qpack_stream_del(stream, mem); + + return 0; } -int nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, - uint64_t n) { +int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n) { if (n == 0 || encoder->ctx.next_absidx - encoder->krcnt < n) { return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; } @@ -2365,6 +2335,7 @@ void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) { nghttp3_pq_clear(&encoder->min_cnts); nghttp3_map_each_free(&encoder->streams, map_stream_free, (void *)encoder->ctx.mem); + nghttp3_map_clear(&encoder->streams); } void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, @@ -2386,7 +2357,8 @@ void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, nghttp3_qpack_stream_del(stream, mem); } -size_t nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder) { +size_t +nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder) { return nghttp3_ksl_len(&encoder->blocked_streams); } @@ -2394,7 +2366,7 @@ int nghttp3_qpack_encoder_write_field_section_prefix( nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt, uint64_t base) { size_t max_ents = - encoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + encoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD; uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1; int sign = base < ricnt; uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt; @@ -2563,15 +2535,17 @@ nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder, switch (encoder->opcode) { case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT: - rv = nghttp3_qpack_encoder_add_insert_count(encoder, - encoder->rstate.left); + rv = nghttp3_qpack_encoder_add_icnt(encoder, encoder->rstate.left); if (rv != 0) { goto fail; } break; case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK: - nghttp3_qpack_encoder_ack_header(encoder, - (int64_t)encoder->rstate.left); + rv = nghttp3_qpack_encoder_ack_header(encoder, + (int64_t)encoder->rstate.left); + if (rv != 0) { + goto fail; + } break; case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL: nghttp3_qpack_encoder_cancel_stream(encoder, @@ -2661,11 +2635,13 @@ void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) { } int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem) { int rv; - rv = qpack_context_init(&decoder->ctx, max_dtable_size, max_blocked, mem); + rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity, + max_blocked_streams, mem); if (rv != 0) { return rv; } @@ -2741,7 +2717,7 @@ static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate, /* * qpack_decoder_validate_index checks rstate->absidx is acceptable. * - * It returns 0 if it suceeds, or one of the following negative error + * It returns 0 if it succeeds, or one of the following negative error * codes: * * NGHTTP3_ERR_QPACK_FATAL @@ -2843,14 +2819,14 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, } if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) { - if (decoder->rstate.left > decoder->ctx.hard_max_dtable_size) { + DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n", + decoder->rstate.left); + rv = nghttp3_qpack_decoder_set_max_dtable_capacity( + decoder, (size_t)decoder->rstate.left); + if (rv != 0) { rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; goto fail; } - DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n", - decoder->rstate.left); - nghttp3_qpack_decoder_set_dtable_cap(decoder, - (size_t)decoder->rstate.left); decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; nghttp3_qpack_read_state_reset(&decoder->rstate); @@ -3036,6 +3012,7 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, default: /* Unreachable */ assert(0); + abort(); } if (rv != 0) { goto fail; @@ -3070,6 +3047,7 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, default: /* Unreachable */ assert(0); + abort(); } if (rv != 0) { goto fail; @@ -3088,16 +3066,20 @@ nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, return rv; } -void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, - size_t cap) { +int nghttp3_qpack_decoder_set_max_dtable_capacity( + nghttp3_qpack_decoder *decoder, size_t max_dtable_capacity) { nghttp3_qpack_entry *ent; size_t i; nghttp3_qpack_context *ctx = &decoder->ctx; const nghttp3_mem *mem = ctx->mem; - ctx->max_dtable_size = cap; + if (max_dtable_capacity > decoder->ctx.hard_max_dtable_capacity) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + ctx->max_dtable_capacity = max_dtable_capacity; - while (ctx->dtable_size > cap) { + while (ctx->dtable_size > max_dtable_capacity) { i = nghttp3_ringbuf_len(&ctx->dtable); assert(i); ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); @@ -3108,6 +3090,8 @@ void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, nghttp3_qpack_entry_free(ent); nghttp3_mem_free(mem, ent); } + + return 0; } int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) { @@ -3131,7 +3115,7 @@ int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) { shd = &stable[decoder->rstate.absidx]; if (table_space(shd->name.len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { + decoder->ctx.max_dtable_capacity) { return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; } @@ -3155,7 +3139,7 @@ int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) { ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); if (table_space(ent->nv.name->len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { + decoder->ctx.max_dtable_capacity) { return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; } @@ -3185,7 +3169,7 @@ int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) { ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); if (table_space(ent->nv.name->len, ent->nv.value->len) > - decoder->ctx.max_dtable_size) { + decoder->ctx.max_dtable_capacity) { return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; } @@ -3210,7 +3194,7 @@ int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) { (int)decoder->rstate.value->len, decoder->rstate.value->base); if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) > - decoder->ctx.max_dtable_size) { + decoder->ctx.max_dtable_capacity) { return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; } @@ -3328,7 +3312,7 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, } if (sctx->dbase_sign) { - if (sctx->ricnt < sctx->rstate.left) { + if (sctx->ricnt <= sctx->rstate.left) { rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; goto fail; } @@ -3595,7 +3579,11 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, switch (sctx->opcode) { case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: - nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + if (rv != 0) { + goto fail; + } + break; case NGHTTP3_QPACK_RS_OPCODE_LITERAL: nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); @@ -3629,7 +3617,11 @@ nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, switch (sctx->opcode) { case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: - nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + if (rv != 0) { + goto fail; + } + break; case NGHTTP3_QPACK_RS_OPCODE_LITERAL: nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); @@ -3733,6 +3725,7 @@ void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, uint8_t *p; uint64_t n = 0; size_t len = 0; + (void)len; if (decoder->written_icnt < decoder->ctx.next_absidx) { n = decoder->ctx.next_absidx - decoder->written_icnt; @@ -3789,7 +3782,8 @@ int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, return 0; } - max_ents = decoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + max_ents = + decoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD; full = 2 * max_ents; if (encricnt > full) { @@ -3938,13 +3932,19 @@ qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder, sctx->rstate.value = NULL; } -static void +static int qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, nghttp3_qpack_nv *nv) { - nghttp3_qpack_entry *ent = - nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); - (void)decoder; + nghttp3_qpack_entry *ent; + + /* A broken encoder might change dtable capacity while processing + request stream instruction. Check the absidx again. */ + if (qpack_decoder_validate_index(decoder, &sctx->rstate) != 0) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); nv->name = ent->nv.name; nv->value = sctx->rstate.value; @@ -3955,11 +3955,13 @@ qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder, nghttp3_rcbuf_incref(nv->name); sctx->rstate.value = NULL; + + return 0; } -void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv) { +int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { (void)decoder; DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n", @@ -3967,10 +3969,12 @@ void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, (int)sctx->rstate.value->len, sctx->rstate.value->base); if (sctx->rstate.dynamic) { - qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv); - } else { - qpack_decoder_emit_static_indexed_name(decoder, sctx, nv); + return qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv); } + + qpack_decoder_emit_static_indexed_name(decoder, sctx, nv); + + return 0; } void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, @@ -3993,7 +3997,7 @@ void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, } int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, const nghttp3_mem *mem) { int rv; nghttp3_qpack_encoder *p; @@ -4003,7 +4007,7 @@ int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_qpack_encoder_init(p, max_dtable_size, max_blocked, mem); + rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem); if (rv != 0) { return rv; } @@ -4057,7 +4061,8 @@ void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) { } int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem) { int rv; nghttp3_qpack_decoder *p; @@ -4067,7 +4072,8 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_qpack_decoder_init(p, max_dtable_size, max_blocked, mem); + rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity, + max_blocked_streams, mem); if (rv != 0) { return rv; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h index 429c55a78081f1..804969e14d6091 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h @@ -118,7 +118,7 @@ void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, const nghttp3_mem *mem); typedef struct nghttp3_qpack_stream { - nghttp3_map_entry me; + int64_t stream_id; /* refs is an array of pointer to nghttp3_qpack_header_block_ref in the order of the time they are encoded. HTTP/3 allows multiple header blocks (e.g., non-final response headers, final response @@ -154,18 +154,15 @@ typedef struct nghttp3_qpack_context { NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */ size_t dtable_size; size_t dtable_sum; - /* hard_max_dtable_size is the maximum size of dynamic table. In - HTTP/3, it is notified by decoder as - SETTINGS_QPACK_MAX_TABLE_CAPACITY. Any value lower than or equal - to SETTINGS_QPACK_MAX_TABLE_CAPACITY is OK because encoder has - the authority to decide how many entries are inserted into - dynamic table. */ - size_t hard_max_dtable_size; - /* max_dtable_size is the effective maximum size of dynamic table. */ - size_t max_dtable_size; - /* max_blocked is the maximum number of stream which can be + /* hard_max_dtable_capacity is the upper bound of + max_dtable_capacity. */ + size_t hard_max_dtable_capacity; + /* max_dtable_capacity is the maximum capacity of the dynamic + table. */ + size_t max_dtable_capacity; + /* max_blocked_streams is the maximum number of stream which can be blocked. */ - size_t max_blocked; + size_t max_blocked_streams; /* next_absidx is the next absolute index for nghttp3_qpack_entry. It is equivalent to insert count. */ uint64_t next_absidx; @@ -218,10 +215,10 @@ typedef enum nghttp3_qpack_decoder_stream_opcode { /* QPACK encoder flags */ /* NGHTTP3_QPACK_ENCODER_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00 +#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00u /* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that Set Dynamic Table Capacity is required. */ -#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01 +#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01u struct nghttp3_qpack_encoder { nghttp3_qpack_context ctx; @@ -260,9 +257,8 @@ struct nghttp3_qpack_encoder { /* * nghttp3_qpack_encoder_init initializes |encoder|. - * |max_dtable_size| is the maximum size of dynamic table. - * |max_blocked| is the maximum number of stream which can be blocked. - * |mem| is a memory allocator. + * |hard_max_dtable_capacity| is the upper bound of the dynamic table + * capacity. |mem| is a memory allocator. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -271,7 +267,7 @@ struct nghttp3_qpack_encoder { * Out of memory. */ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, const nghttp3_mem *mem); /* @@ -628,6 +624,44 @@ int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, uint32_t hash); +/* + * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header + * block for a stream denoted by |stream_id| was acknowledged by + * decoder. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` + * Section Acknowledgement for a stream denoted by |stream_id| is + * unexpected. + */ +int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + +/* + * `nghttp3_qpack_encoder_add_icnt` increments known received count of + * |encoder| by |n|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` + * |n| is too large. + */ +int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n); + +/* + * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream + * denoted by |stream_id| is cancelled. This function is provided for + * debugging purpose only. In HTTP/3, |encoder| knows this by reading + * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + */ +void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + /* * nghttp3_qpack_context_dtable_get returns dynamic table entry whose * absolute index is |absidx|. This function assumes that such entry @@ -749,9 +783,9 @@ struct nghttp3_qpack_decoder { /* * nghttp3_qpack_decoder_init initializes |decoder|. - * |max_dtable_size| is the maximum size of dynamic table. - * |max_blocked| is the maximum number of stream which can be blocked. - * |mem| is a memory allocator. + * |hard_max_dtable_capacity| is the upper bound of the dynamic table + * capacity. |max_blocked_streams| is the maximum number of stream + * which can be blocked. |mem| is a memory allocator. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -760,7 +794,8 @@ struct nghttp3_qpack_decoder { * Out of memory. */ int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t max_dtable_size, size_t max_blocked, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, const nghttp3_mem *mem); /* @@ -935,9 +970,9 @@ void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, nghttp3_qpack_nv *nv); -void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, - nghttp3_qpack_stream_context *sctx, - nghttp3_qpack_nv *nv); +int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv); void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c index 1c31ecebf608d5..9e9dab51390696 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c @@ -41,8 +41,7 @@ int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, *rcbuf_ptr = (void *)p; - (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; - (*rcbuf_ptr)->free = mem->free; + (*rcbuf_ptr)->mem = mem; (*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf); (*rcbuf_ptr)->len = size; (*rcbuf_ptr)->ref = 1; @@ -76,7 +75,7 @@ int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src, * Frees |rcbuf| itself, regardless of its reference cout. */ void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) { - nghttp3_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); + nghttp3_mem_free(rcbuf->mem, rcbuf); } void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h index feea8040005422..f589c377bf6ea7 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h @@ -33,10 +33,9 @@ #include struct nghttp3_rcbuf { - /* custom memory allocator belongs to the mem parameter when - creating this object. */ - void *mem_user_data; - nghttp3_free free; + /* mem is the memory allocator that allocates memory for this + object. */ + const nghttp3_mem *mem; /* The pointer to the underlying buffer */ uint8_t *base; /* Size of buffer pointed by |base|. */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c index 96d60fe82f9a76..e655a7ec01d10b 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c @@ -34,6 +34,7 @@ #include "nghttp3_conn.h" #include "nghttp3_str.h" #include "nghttp3_http.h" +#include "nghttp3_vec.h" /* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which makes a copy to outq. */ @@ -44,45 +45,36 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, uint64_t seq, const nghttp3_stream_callbacks *callbacks, + nghttp3_objalloc *out_chunk_objalloc, + nghttp3_objalloc *stream_objalloc, const nghttp3_mem *mem) { - int rv; - nghttp3_stream *stream = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_stream)); + nghttp3_stream *stream = nghttp3_objalloc_stream_get(stream_objalloc); nghttp3_node_id nid; if (stream == NULL) { return NGHTTP3_ERR_NOMEM; } + memset(stream, 0, sizeof(*stream)); + + stream->out_chunk_objalloc = out_chunk_objalloc; + stream->stream_objalloc = stream_objalloc; + nghttp3_tnode_init( &stream->node, nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_STREAM, stream_id), seq, NGHTTP3_DEFAULT_URGENCY); - rv = nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem); - if (rv != 0) { - goto frq_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem); - if (rv != 0) { - goto chunks_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem); - if (rv != 0) { - goto outq_init_fail; - } - - rv = nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem); - if (rv != 0) { - goto inq_init_fail; - } + nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem); + nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem); + nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem); + nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem); nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem); - stream->me.key = (key_type)stream_id; stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; stream->mem = mem; + stream->tx.offset = 0; stream->rx.http.status_code = -1; stream->rx.http.content_length = -1; stream->rx.http.pri = NGHTTP3_DEFAULT_URGENCY; @@ -95,17 +87,6 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, *pstream = stream; return 0; - -inq_init_fail: - nghttp3_ringbuf_free(&stream->outq); -outq_init_fail: - nghttp3_ringbuf_free(&stream->chunks); -chunks_init_fail: - nghttp3_ringbuf_free(&stream->frq); -frq_init_fail: - nghttp3_mem_free(mem, stream); - - return rv; } static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) { @@ -134,6 +115,27 @@ static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) { nghttp3_ringbuf_free(chunks); } +static void delete_out_chunks(nghttp3_ringbuf *chunks, + nghttp3_objalloc *out_chunk_objalloc, + const nghttp3_mem *mem) { + nghttp3_buf *buf; + size_t i, len = nghttp3_ringbuf_len(chunks); + + for (i = 0; i < len; ++i) { + buf = nghttp3_ringbuf_get(chunks, i); + + if (nghttp3_buf_cap(buf) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { + nghttp3_objalloc_chunk_release(out_chunk_objalloc, + (nghttp3_chunk *)(void *)buf->begin); + continue; + } + + nghttp3_buf_free(buf, mem); + } + + nghttp3_ringbuf_free(chunks); +} + static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { nghttp3_frame_entry *frent; size_t i, len = nghttp3_ringbuf_len(frq); @@ -144,9 +146,6 @@ static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { case NGHTTP3_FRAME_HEADERS: nghttp3_frame_headers_free(&frent->fr.headers, mem); break; - case NGHTTP3_FRAME_PUSH_PROMISE: - nghttp3_frame_push_promise_free(&frent->fr.push_promise, mem); - break; default: break; } @@ -163,11 +162,11 @@ void nghttp3_stream_del(nghttp3_stream *stream) { nghttp3_qpack_stream_context_free(&stream->qpack_sctx); delete_chunks(&stream->inq, stream->mem); delete_outq(&stream->outq, stream->mem); - delete_chunks(&stream->chunks, stream->mem); + delete_out_chunks(&stream->chunks, stream->out_chunk_objalloc, stream->mem); delete_frq(&stream->frq, stream->mem); nghttp3_tnode_free(&stream->node); - nghttp3_mem_free(stream->mem, stream); + nghttp3_objalloc_stream_release(stream->stream_objalloc, stream); } void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) { @@ -267,19 +266,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { } nghttp3_frame_headers_free(&frent->fr.headers, stream->mem); break; - case NGHTTP3_FRAME_PUSH_PROMISE: - rv = nghttp3_stream_write_push_promise(stream, frent); - if (rv != 0) { - return rv; - } - nghttp3_frame_push_promise_free(&frent->fr.push_promise, stream->mem); - break; - case NGHTTP3_FRAME_CANCEL_PUSH: - rv = nghttp3_stream_write_cancel_push(stream, frent); - if (rv != 0) { - return rv; - } - break; case NGHTTP3_FRAME_DATA: rv = nghttp3_stream_write_data(stream, &data_eof, frent); if (rv != 0) { @@ -292,11 +278,14 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { return 0; } break; - case NGHTTP3_FRAME_MAX_PUSH_ID: - if (stream->conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) { - break; + case NGHTTP3_FRAME_GOAWAY: + rv = nghttp3_stream_write_goaway(stream, frent); + if (rv != 0) { + return rv; } - rv = nghttp3_stream_write_max_push_id(stream, frent); + break; + case NGHTTP3_FRAME_PRIORITY_UPDATE: + rv = nghttp3_stream_write_priority_update(stream, frent); if (rv != 0) { return rv; } @@ -338,34 +327,6 @@ int nghttp3_stream_write_stream_type(nghttp3_stream *stream) { return nghttp3_stream_outq_add(stream, &tbuf); } -int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream) { - size_t len; - nghttp3_buf *chunk; - nghttp3_typed_buf tbuf; - int rv; - nghttp3_push_promise *pp = stream->pp; - - assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); - assert(pp); - - len = nghttp3_put_varint_len((int64_t)stream->type) + - nghttp3_put_varint_len(pp->node.nid.id); - - rv = nghttp3_stream_ensure_chunk(stream, len); - if (rv != 0) { - return rv; - } - - chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); - - chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); - chunk->last = nghttp3_put_varint(chunk->last, pp->node.nid.id); - tbuf.buf.last = chunk->last; - - return nghttp3_stream_outq_add(stream, &tbuf); -} - int nghttp3_stream_write_settings(nghttp3_stream *stream, nghttp3_frame_entry *frent) { size_t len; @@ -386,10 +347,16 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE; iv[0].value = local_settings->max_field_section_size; iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY; - iv[1].value = local_settings->qpack_max_table_capacity; + iv[1].value = local_settings->qpack_max_dtable_capacity; iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS; iv[2].value = local_settings->qpack_blocked_streams; + if (local_settings->enable_connect_protocol) { + ++fr.settings.niv; + iv[3].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL; + iv[3].value = 1; + } + len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings); rv = nghttp3_stream_ensure_chunk(stream, len); @@ -407,15 +374,15 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, return nghttp3_stream_outq_add(stream, &tbuf); } -int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_cancel_push *fr = &frent->fr.cancel_push; +int nghttp3_stream_write_goaway(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_goaway *fr = &frent->fr.goaway; size_t len; int rv; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; - len = nghttp3_frame_write_cancel_push_len(&fr->hd.length, fr); + len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -425,30 +392,22 @@ int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, chunk = nghttp3_stream_get_chunk(stream); typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_cancel_push(chunk->last, fr); + chunk->last = nghttp3_frame_write_goaway(chunk->last, fr); tbuf.buf.last = chunk->last; return nghttp3_stream_outq_add(stream, &tbuf); } -int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_max_push_id *fr = &frent->fr.max_push_id; - nghttp3_conn *conn = stream->conn; +int nghttp3_stream_write_priority_update(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_priority_update *fr = &frent->fr.priority_update; size_t len; int rv; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; - assert(conn); - assert(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED); - - fr->push_id = (int64_t)conn->remote.uni.unsent_max_pushes - 1; - conn->remote.uni.max_pushes = conn->remote.uni.unsent_max_pushes; - conn->flags &= (uint16_t)~NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; - - len = nghttp3_frame_write_max_push_id_len(&fr->hd.length, fr); + len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -458,7 +417,7 @@ int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, chunk = nghttp3_stream_get_chunk(stream); typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_max_push_id(chunk->last, fr); + chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr); tbuf.buf.last = chunk->last; @@ -474,35 +433,21 @@ int nghttp3_stream_write_headers(nghttp3_stream *stream, return nghttp3_stream_write_header_block( stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, - &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, 0, fr->nva, fr->nvlen); -} - -int nghttp3_stream_write_push_promise(nghttp3_stream *stream, - nghttp3_frame_entry *frent) { - nghttp3_frame_push_promise *fr = &frent->fr.push_promise; - nghttp3_conn *conn = stream->conn; - - assert(conn); - - return nghttp3_stream_write_header_block( - stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, - &conn->tx.qpack.ebuf, NGHTTP3_FRAME_PUSH_PROMISE, fr->push_id, fr->nva, - fr->nvlen); + &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, fr->nva, fr->nvlen); } int nghttp3_stream_write_header_block(nghttp3_stream *stream, nghttp3_qpack_encoder *qenc, nghttp3_stream *qenc_stream, nghttp3_buf *rbuf, nghttp3_buf *ebuf, - int64_t frame_type, int64_t push_id, - const nghttp3_nv *nva, size_t nvlen) { + int64_t frame_type, const nghttp3_nv *nva, + size_t nvlen) { nghttp3_buf pbuf; int rv; size_t len; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; nghttp3_frame_hd hd; - size_t push_idlen = 0; uint8_t raw_pbuf[16]; size_t pbuflen, rbuflen, ebuflen; @@ -518,14 +463,10 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rbuflen = nghttp3_buf_len(rbuf); ebuflen = nghttp3_buf_len(ebuf); - if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { - push_idlen = nghttp3_put_varint_len(push_id); - } - hd.type = frame_type; - hd.length = (int64_t)(pbuflen + rbuflen + push_idlen); + hd.length = (int64_t)(pbuflen + rbuflen); - len = nghttp3_frame_write_hd_len(&hd) + push_idlen + pbuflen; + len = nghttp3_frame_write_hd_len(&hd) + pbuflen; if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) { len += rbuflen; @@ -541,10 +482,6 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); - if (push_idlen) { - chunk->last = nghttp3_put_varint(chunk->last, push_id); - } - chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen); nghttp3_buf_init(&pbuf); @@ -623,7 +560,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, nghttp3_buf *chunk; nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data; nghttp3_conn *conn = stream->conn; - size_t datalen; + int64_t datalen; uint32_t flags = 0; nghttp3_frame_hd hd; nghttp3_vec vec[8]; @@ -647,7 +584,10 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, return NGHTTP3_ERR_CALLBACK_FAILURE; } - datalen = nghttp3_vec_len(vec, (size_t)sveccnt); + datalen = nghttp3_vec_len_varint(vec, (size_t)sveccnt); + if (datalen == -1) { + return NGHTTP3_ERR_STREAM_DATA_OVERFLOW; + } assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF); @@ -677,7 +617,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, } hd.type = NGHTTP3_FRAME_DATA; - hd.length = (int64_t)datalen; + hd.length = datalen; len = nghttp3_frame_write_hd_len(&hd); @@ -762,8 +702,14 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream, int rv; nghttp3_typed_buf *dest; size_t len = nghttp3_ringbuf_len(outq); + size_t buflen = nghttp3_buf_len(&tbuf->buf); + + if (buflen > NGHTTP3_MAX_VARINT - stream->tx.offset) { + return NGHTTP3_ERR_STREAM_DATA_OVERFLOW; + } - stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf); + stream->tx.offset += buflen; + stream->unsent_bytes += buflen; if (len) { dest = nghttp3_ringbuf_get(outq, len - 1); @@ -816,7 +762,12 @@ int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) { for (; n < need; n *= 2) ; - p = nghttp3_mem_malloc(stream->mem, n); + if (n == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { + p = (uint8_t *)nghttp3_objalloc_chunk_len_get(stream->out_chunk_objalloc, + n); + } else { + p = nghttp3_mem_malloc(stream->mem, n); + } if (p == NULL) { return NGHTTP3_ERR_NOMEM; } @@ -863,7 +814,7 @@ nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, nghttp3_ringbuf *outq = &stream->outq; size_t len = nghttp3_ringbuf_len(outq); size_t i; - size_t offset = stream->outq_offset; + uint64_t offset = stream->outq_offset; size_t buflen; nghttp3_vec *vbegin = vec, *vend = vec + veccnt; nghttp3_typed_buf *tbuf; @@ -879,7 +830,7 @@ nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, } vec->base = tbuf->buf.pos + offset; - vec->len = buflen - offset; + vec->len = (size_t)(buflen - offset); ++vec; ++i; break; @@ -903,7 +854,7 @@ int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) { nghttp3_ringbuf *outq = &stream->outq; size_t i; size_t len = nghttp3_ringbuf_len(outq); - size_t offset = stream->outq_offset + n; + uint64_t offset = stream->outq_offset + n; size_t buflen; nghttp3_typed_buf *tbuf; @@ -945,7 +896,7 @@ static int stream_pop_outq_entry(nghttp3_stream *stream, break; case NGHTTP3_BUF_TYPE_ALIEN: break; - default: + case NGHTTP3_BUF_TYPE_SHARED: assert(nghttp3_ringbuf_len(chunks)); chunk = nghttp3_ringbuf_get(chunks, 0); @@ -954,9 +905,18 @@ static int stream_pop_outq_entry(nghttp3_stream *stream, assert(chunk->end == tbuf->buf.end); if (chunk->last == tbuf->buf.last) { - nghttp3_buf_free(chunk, stream->mem); + if (nghttp3_buf_cap(chunk) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { + nghttp3_objalloc_chunk_release(stream->out_chunk_objalloc, + (nghttp3_chunk *)(void *)chunk->begin); + } else { + nghttp3_buf_free(chunk, stream->mem); + } nghttp3_ringbuf_pop_front(chunks); } + break; + default: + assert(0); + abort(); }; nghttp3_ringbuf_pop_front(&stream->outq); @@ -969,7 +929,7 @@ int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) { uint64_t offset = stream->ack_offset + n; size_t buflen; size_t npopped = 0; - size_t nack; + uint64_t nack; nghttp3_typed_buf *tbuf; int rv; @@ -978,7 +938,7 @@ int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) { buflen = nghttp3_buf_len(&tbuf->buf); if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) { - nack = (size_t)nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done; + nack = nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done; if (stream->callbacks.acked_data) { rv = stream->callbacks.acked_data(stream, stream->node.nid.id, nack, stream->user_data); @@ -1110,16 +1070,16 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; return 0; case NGHTTP3_HTTP_EVENT_DATA_BEGIN: stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN; return 0; case NGHTTP3_HTTP_EVENT_MSG_END: + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; default: @@ -1141,13 +1101,13 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: rv = nghttp3_http_on_remote_end_stream(stream); if (rv != 0) { return rv; } - stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_MSG_END: stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; default: @@ -1165,6 +1125,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, spec. */ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; case NGHTTP3_HTTP_STATE_REQ_END: @@ -1192,10 +1156,6 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.http.status_code / 100 == 2) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } - rv = nghttp3_http_on_remote_end_stream(stream); - if (rv != 0) { - return rv; - } stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; return 0; case NGHTTP3_HTTP_EVENT_DATA_BEGIN: @@ -1205,6 +1165,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN; return 0; case NGHTTP3_HTTP_EVENT_MSG_END: + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; default: @@ -1226,13 +1190,13 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.http.status_code / 100 == 2) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: rv = nghttp3_http_on_remote_end_stream(stream); if (rv != 0) { return rv; } - stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; - return 0; - case NGHTTP3_HTTP_EVENT_MSG_END: stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; default: @@ -1248,12 +1212,17 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, if (event != NGHTTP3_HTTP_EVENT_MSG_END) { return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; case NGHTTP3_HTTP_STATE_RESP_END: return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; default: assert(0); + abort(); } } @@ -1267,11 +1236,6 @@ int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) { } } -int nghttp3_stream_bidi_or_push(nghttp3_stream *stream) { - return (!nghttp3_stream_uni(stream->node.nid.id) || - stream->type == NGHTTP3_STREAM_TYPE_PUSH); -} - int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; } int nghttp3_client_stream_bidi(int64_t stream_id) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h index f7e375c85eb7f5..06292738a17e93 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h @@ -37,6 +37,7 @@ #include "nghttp3_buf.h" #include "nghttp3_frame.h" #include "nghttp3_qpack.h" +#include "nghttp3_objalloc.h" #define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256 @@ -60,13 +61,14 @@ typedef enum nghttp3_stream_type { typedef enum nghttp3_ctrl_stream_state { NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE, NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH, - NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH, NGHTTP3_CTRL_STREAM_STATE_SETTINGS, NGHTTP3_CTRL_STREAM_STATE_GOAWAY, NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID, NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME, NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID, NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE, + NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID, + NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE, } nghttp3_ctrl_stream_state; typedef enum nghttp3_req_stream_state { @@ -74,23 +76,10 @@ typedef enum nghttp3_req_stream_state { NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH, NGHTTP3_REQ_STREAM_STATE_DATA, NGHTTP3_REQ_STREAM_STATE_HEADERS, - NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID, - NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE, - NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE, NGHTTP3_REQ_STREAM_STATE_IGN_FRAME, NGHTTP3_REQ_STREAM_STATE_IGN_REST, } nghttp3_req_stream_state; -typedef enum nghttp3_push_stream_state { - NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE, - NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH, - NGHTTP3_PUSH_STREAM_STATE_DATA, - NGHTTP3_PUSH_STREAM_STATE_HEADERS, - NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME, - NGHTTP3_PUSH_STREAM_STATE_PUSH_ID, - NGHTTP3_PUSH_STREAM_STATE_IGN_REST, -} nghttp3_push_stream_state; - typedef struct nghttp3_varint_read_state { int64_t acc; size_t left; @@ -104,38 +93,46 @@ typedef struct nghttp3_stream_read_state { } nghttp3_stream_read_state; /* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */ -#define NGHTTP3_STREAM_FLAG_NONE 0x0000 +#define NGHTTP3_STREAM_FLAG_NONE 0x0000u /* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional stream type is identified. */ -#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001 +#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001u /* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by QUIC flow control. */ -#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002 +#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002u /* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is temporarily unable to provide data. */ -#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004 +#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004u /* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application finished to feed outgoing data. */ -#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008 +#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008u /* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is blocked due to QPACK decoding. */ -#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010 +#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010u /* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent fin. */ -#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020 +#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020u /* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed. nghttp3_stream object can still alive because it might be blocked by QPACK decoder. */ -#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040 -/* NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED indicates that stream is - blocked because the corresponding PUSH_PROMISE has not been - received yet. */ -#define NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED 0x0080 +#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040u /* NGHTTP3_STREAM_FLAG_SHUT_WR indicates that any further write operation to a stream is prohibited. */ -#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100 -/* NGHTTP3_STREAM_FLAG_RESET indicates that stream is reset. */ -#define NGHTTP3_STREAM_FLAG_RESET 0x0200 +#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100u +/* NGHTTP3_STREAM_FLAG_SHUT_RD indicates that a read-side stream is + closed abruptly and any incoming and pending stream data is just + discarded for a stream. */ +#define NGHTTP3_STREAM_FLAG_SHUT_RD 0x0200u +/* NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET indicates that server + overrides stream priority. */ +#define NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET 0x0400u +/* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server + received PRIORITY_UPDATE frame for this stream. */ +#define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u +/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that + NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while + processing incoming HTTP fields. */ +#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u typedef enum nghttp3_stream_http_state { NGHTTP3_HTTP_STATE_NONE, @@ -164,15 +161,11 @@ typedef enum nghttp3_stream_http_event { NGHTTP3_HTTP_EVENT_DATA_END, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN, NGHTTP3_HTTP_EVENT_HEADERS_END, - NGHTTP3_HTTP_EVENT_PUSH_PROMISE_BEGIN, - NGHTTP3_HTTP_EVENT_PUSH_PROMISE_END, NGHTTP3_HTTP_EVENT_MSG_END, } nghttp3_stream_http_event; typedef struct nghttp3_stream nghttp3_stream; -typedef struct nghttp3_push_promise nghttp3_push_promise; - /* * nghttp3_stream_acked_data is a callback function which is invoked * when data sent on stream denoted by |stream_id| supplied from @@ -185,7 +178,7 @@ typedef struct nghttp3_push_promise nghttp3_push_promise; * NGHTTP3_ERR_CALLBACK_FAILURE. */ typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream, - int64_t stream_id, size_t datalen, + int64_t stream_id, uint64_t datalen, void *user_data); typedef struct nghttp3_stream_callbacks { @@ -202,65 +195,70 @@ typedef struct nghttp3_http_state { /* recv_content_length is the number of body bytes received so far. */ int64_t recv_content_length; - uint16_t flags; + uint32_t flags; /* pri is a stream priority produced by nghttp3_pri_to_uint8. */ uint8_t pri; } nghttp3_http_state; struct nghttp3_stream { - const nghttp3_mem *mem; - nghttp3_map_entry me; - /* node is a node in dependency tree. For server initiated - unidirectional stream (push), scheduling is done via - corresponding nghttp3_push_promise object pointed by pp. */ - nghttp3_tnode node; - nghttp3_pq_entry qpack_blocked_pe; - nghttp3_stream_callbacks callbacks; - nghttp3_ringbuf frq; - nghttp3_ringbuf chunks; - nghttp3_ringbuf outq; - /* inq stores the stream raw data which cannot be read because - stream is blocked by QPACK decoder. */ - nghttp3_ringbuf inq; - nghttp3_qpack_stream_context qpack_sctx; - /* conn is a reference to underlying connection. It could be NULL - if stream is not a request/push stream. */ - nghttp3_conn *conn; - void *user_data; - /* unsent_bytes is the number of bytes in outq not written yet */ - size_t unsent_bytes; - /* outq_idx is an index into outq where next write is made. */ - size_t outq_idx; - /* outq_offset is write offset relative to the element at outq_idx - in outq. */ - size_t outq_offset; - /* ack_offset is offset acknowledged by peer relative to the first - element in outq. */ - uint64_t ack_offset; - /* ack_done is the number of bytes notified to an application that - they are acknowledged inside the first outq element if it is of - type NGHTTP3_BUF_TYPE_ALIEN. */ - size_t ack_done; - size_t unscheduled_nwrite; - nghttp3_stream_type type; - nghttp3_stream_read_state rstate; - /* pp is nghttp3_push_promise that this stream fulfills. */ - nghttp3_push_promise *pp; - /* error_code indicates the reason of closure of this stream. */ - uint64_t error_code; - - struct { - nghttp3_stream_http_state hstate; - } tx; - - struct { - nghttp3_stream_http_state hstate; - nghttp3_http_state http; - } rx; - - uint16_t flags; + union { + struct { + const nghttp3_mem *mem; + nghttp3_objalloc *out_chunk_objalloc; + nghttp3_objalloc *stream_objalloc; + nghttp3_tnode node; + nghttp3_pq_entry qpack_blocked_pe; + nghttp3_stream_callbacks callbacks; + nghttp3_ringbuf frq; + nghttp3_ringbuf chunks; + nghttp3_ringbuf outq; + /* inq stores the stream raw data which cannot be read because + stream is blocked by QPACK decoder. */ + nghttp3_ringbuf inq; + nghttp3_qpack_stream_context qpack_sctx; + /* conn is a reference to underlying connection. It could be NULL + if stream is not a request stream. */ + nghttp3_conn *conn; + void *user_data; + /* unsent_bytes is the number of bytes in outq not written yet */ + uint64_t unsent_bytes; + /* outq_idx is an index into outq where next write is made. */ + size_t outq_idx; + /* outq_offset is write offset relative to the element at outq_idx + in outq. */ + uint64_t outq_offset; + /* ack_offset is offset acknowledged by peer relative to the first + element in outq. */ + uint64_t ack_offset; + /* ack_done is the number of bytes notified to an application that + they are acknowledged inside the first outq element if it is of + type NGHTTP3_BUF_TYPE_ALIEN. */ + uint64_t ack_done; + uint64_t unscheduled_nwrite; + nghttp3_stream_type type; + nghttp3_stream_read_state rstate; + /* error_code indicates the reason of closure of this stream. */ + uint64_t error_code; + + struct { + uint64_t offset; + nghttp3_stream_http_state hstate; + } tx; + + struct { + nghttp3_stream_http_state hstate; + nghttp3_http_state http; + } rx; + + uint16_t flags; + }; + + nghttp3_opl_entry oplent; + }; }; +nghttp3_objalloc_def(stream, nghttp3_stream, oplent); + typedef struct nghttp3_frame_entry { nghttp3_frame fr; union { @@ -275,6 +273,8 @@ typedef struct nghttp3_frame_entry { int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, uint64_t seq, const nghttp3_stream_callbacks *callbacks, + nghttp3_objalloc *out_chunk_objalloc, + nghttp3_objalloc *stream_objalloc, const nghttp3_mem *mem); void nghttp3_stream_del(nghttp3_stream *stream); @@ -293,8 +293,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream); int nghttp3_stream_write_stream_type(nghttp3_stream *stream); -int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream); - nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, nghttp3_vec *vec, size_t veccnt); @@ -308,15 +306,12 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream, int nghttp3_stream_write_headers(nghttp3_stream *stream, nghttp3_frame_entry *frent); -int nghttp3_stream_write_push_promise(nghttp3_stream *stream, - nghttp3_frame_entry *frent); - int nghttp3_stream_write_header_block(nghttp3_stream *stream, nghttp3_qpack_encoder *qenc, nghttp3_stream *qenc_stream, nghttp3_buf *rbuf, nghttp3_buf *ebuf, - int64_t frame_type, int64_t push_id, - const nghttp3_nv *nva, size_t nvlen); + int64_t frame_type, const nghttp3_nv *nva, + size_t nvlen); int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, nghttp3_frame_entry *frent); @@ -324,11 +319,11 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, int nghttp3_stream_write_settings(nghttp3_stream *stream, nghttp3_frame_entry *frent); -int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, - nghttp3_frame_entry *frent); +int nghttp3_stream_write_goaway(nghttp3_stream *stream, + nghttp3_frame_entry *frent); -int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, - nghttp3_frame_entry *frent); +int nghttp3_stream_write_priority_update(nghttp3_stream *stream, + nghttp3_frame_entry *frent); int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need); @@ -374,12 +369,6 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream); -/* - * nghttp3_stream_bidi_or_push returns nonzero if |stream| is - * bidirectional or push stream. - */ -int nghttp3_stream_bidi_or_push(nghttp3_stream *stream); - /* * nghttp3_stream_uni returns nonzero if stream identified by * |stream_id| is unidirectional. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c index 94dca7dbf76325..36e738c3469aca 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c @@ -82,7 +82,7 @@ static uint64_t pq_get_first_cycle(nghttp3_pq *pq) { } int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, - size_t nwrite) { + uint64_t nwrite) { uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN; if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h index 817aec034c03a4..f71dcf5ee31ad6 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h @@ -72,7 +72,8 @@ void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq); * If |tnode| has already been scheduled, it is rescheduled by the * amount of |nwrite|. */ -int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, size_t nwrite); +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, + uint64_t nwrite); /* * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c index ab292ac8ae31d2..ab58ff5832bea9 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c @@ -26,9 +26,9 @@ #include "nghttp3_vec.h" #include "nghttp3_macro.h" -size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { +uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { size_t i; - size_t res = 0; + uint64_t res = 0; for (i = 0; i < n; ++i) { res += vec[i].len; @@ -36,3 +36,20 @@ size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { return res; } + +int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n) { + uint64_t res = 0; + size_t len; + size_t i; + + for (i = 0; i < n; ++i) { + len = vec[i].len; + if (len > NGHTTP3_MAX_VARINT - res) { + return -1; + } + + res += len; + } + + return (int64_t)res; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h index c1a928e3e1d1ab..473d1467310062 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h @@ -32,4 +32,10 @@ #include +/* + * nghttp3_vec_len_varint is similar to nghttp3_vec_len, but it + * returns -1 if the sum of the length exceeds NGHTTP3_MAX_VARINT. + */ +int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n); + #endif /* NGHTTP3_VEC_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c index dfad4793c4bc11..c460cc72835b1d 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c @@ -31,7 +31,7 @@ static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM, NGHTTP3_VERSION}; -nghttp3_info *nghttp3_version(int least_version) { +const nghttp3_info *nghttp3_version(int least_version) { if (least_version > NGHTTP3_VERSION_NUM) { return NULL; } diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp index 9ea93be2091ac4..a47a791610e86a 100644 --- a/deps/ngtcp2/ngtcp2.gyp +++ b/deps/ngtcp2/ngtcp2.gyp @@ -6,6 +6,9 @@ 'ngtcp2_sources': [ 'ngtcp2/lib/ngtcp2_acktr.c', 'ngtcp2/lib/ngtcp2_addr.c', + 'ngtcp2/lib/ngtcp2_balloc.c', + 'ngtcp2/lib/ngtcp2_bbr.c', + 'ngtcp2/lib/ngtcp2_bbr2.c', 'ngtcp2/lib/ngtcp2_buf.c', 'ngtcp2/lib/ngtcp2_cc.c', 'ngtcp2/lib/ngtcp2_cid.c', @@ -19,8 +22,11 @@ 'ngtcp2/lib/ngtcp2_log.c', 'ngtcp2/lib/ngtcp2_map.c', 'ngtcp2/lib/ngtcp2_mem.c', + 'ngtcp2/lib/ngtcp2_objalloc.c', + 'ngtcp2/lib/ngtcp2_opl.c', 'ngtcp2/lib/ngtcp2_path.c', 'ngtcp2/lib/ngtcp2_pkt.c', + 'ngtcp2/lib/ngtcp2_pmtud.c', 'ngtcp2/lib/ngtcp2_ppe.c', 'ngtcp2/lib/ngtcp2_pq.c', 'ngtcp2/lib/ngtcp2_pv.c', @@ -34,7 +40,8 @@ 'ngtcp2/lib/ngtcp2_strm.c', 'ngtcp2/lib/ngtcp2_vec.c', 'ngtcp2/lib/ngtcp2_version.c', - 'ngtcp2/crypto/shared.c', + 'ngtcp2/lib/ngtcp2_window_filter.c', + 'ngtcp2/crypto/shared.c' ], 'ngtcp2_sources_openssl': [ 'ngtcp2/crypto/openssl/openssl.c' @@ -43,6 +50,7 @@ 'ngtcp2/crypto/boringssl/boringssl.c' ], 'nghttp3_sources': [ + 'nghttp3/lib/nghttp3_balloc.c', 'nghttp3/lib/nghttp3_buf.c', 'nghttp3/lib/nghttp3_conn.c', 'nghttp3/lib/nghttp3_conv.c', @@ -55,6 +63,8 @@ 'nghttp3/lib/nghttp3_ksl.c', 'nghttp3/lib/nghttp3_map.c', 'nghttp3/lib/nghttp3_mem.c', + 'nghttp3/lib/nghttp3_objalloc.c', + 'nghttp3/lib/nghttp3_opl.c', 'nghttp3/lib/nghttp3_pq.c', 'nghttp3/lib/nghttp3_qpack.c', 'nghttp3/lib/nghttp3_qpack_huffman.c', @@ -117,6 +127,7 @@ '', 'ngtcp2/lib/includes', 'ngtcp2/crypto/includes', + 'ngtcp2/crypto', ] }, 'sources': [ diff --git a/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c index 8f75b485e3e56b..015032d41ca0ce 100644 --- a/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c +++ b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c @@ -35,15 +35,15 @@ #include #include #include +#include #include +#include #include "shared.h" -/* Define cipher types because BoringSSL does not implement EVP - interface for chacha20. */ typedef enum ngtcp2_crypto_boringssl_cipher_type { - NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR, - NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR, + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128, + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256, NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20, } ngtcp2_crypto_boringssl_cipher_type; @@ -51,22 +51,31 @@ typedef struct ngtcp2_crypto_boringssl_cipher { ngtcp2_crypto_boringssl_cipher_type type; } ngtcp2_crypto_boringssl_cipher; -static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_128_ctr = { - NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR, +static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_128 = { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128, }; -static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_256_ctr = { - NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR, +static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_256 = { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256, }; static ngtcp2_crypto_boringssl_cipher crypto_cipher_chacha20 = { NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20, }; +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)EVP_aead_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)EVP_sha256(); + return md; +} + ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aead_aes_128_gcm()); ctx->md.native_handle = (void *)EVP_sha256(); - ctx->hp.native_handle = (void *)&crypto_cipher_evp_aes_128_ctr; + ctx->hp.native_handle = (void *)&crypto_cipher_aes_128; ctx->max_encryption = 0; ctx->max_decryption_failure = 0; return ctx; @@ -123,9 +132,9 @@ static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) { static const ngtcp2_crypto_boringssl_cipher *crypto_ssl_get_hp(SSL *ssl) { switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { case TLS1_CK_AES_128_GCM_SHA256: - return &crypto_cipher_evp_aes_128_ctr; + return &crypto_cipher_aes_128; case TLS1_CK_AES_256_GCM_SHA384: - return &crypto_cipher_evp_aes_256_ctr; + return &crypto_cipher_aes_256; case TLS1_CK_CHACHA20_POLY1305_SHA256: return &crypto_cipher_chacha20; default: @@ -216,19 +225,15 @@ void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { } } -typedef enum ngtcp2_crypto_boringssl_cipher_ctx_type { - NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP, - NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20, -} ngtcp2_crypto_boringssl_cipher_ctx_type; - typedef struct ngtcp2_crypto_boringssl_cipher_ctx { - ngtcp2_crypto_boringssl_cipher_ctx_type type; + ngtcp2_crypto_boringssl_cipher_type type; union { - /* ctx is EVP_CIPHER_CTX used when type == - NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP. */ - EVP_CIPHER_CTX *ctx; + /* aes_key is an encryption key when type is either + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128 or + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256. */ + AES_KEY aes_key; /* key contains an encryption key when type == - NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20. */ + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20. */ uint8_t key[32]; }; } ngtcp2_crypto_boringssl_cipher_ctx; @@ -238,70 +243,41 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, const uint8_t *key) { ngtcp2_crypto_boringssl_cipher *hp_cipher = cipher->native_handle; ngtcp2_crypto_boringssl_cipher_ctx *ctx; - EVP_CIPHER_CTX *actx; - const EVP_CIPHER *evp_cipher; - - switch (hp_cipher->type) { - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR: - evp_cipher = EVP_aes_128_ctr(); - break; - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR: - evp_cipher = EVP_aes_256_ctr(); - break; - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20: - ctx = malloc(sizeof(*ctx)); - if (ctx == NULL) { - return -1; - } - - ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20; - memcpy(ctx->key, key, sizeof(ctx->key)); - - cipher_ctx->native_handle = ctx; - - return 0; - default: - assert(0); - }; - - actx = EVP_CIPHER_CTX_new(); - if (actx == NULL) { - return -1; - } - - if (!EVP_EncryptInit_ex(actx, evp_cipher, NULL, key, NULL)) { - EVP_CIPHER_CTX_free(actx); - return -1; - } + int rv; + (void)rv; ctx = malloc(sizeof(*ctx)); if (ctx == NULL) { - EVP_CIPHER_CTX_free(actx); return -1; } - ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP; - ctx->ctx = actx; - + ctx->type = hp_cipher->type; cipher_ctx->native_handle = ctx; - return 0; + switch (hp_cipher->type) { + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128: + rv = AES_set_encrypt_key(key, 128, &ctx->aes_key); + assert(0 == rv); + return 0; + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256: + rv = AES_set_encrypt_key(key, 256, &ctx->aes_key); + assert(0 == rv); + return 0; + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20: + memcpy(ctx->key, key, sizeof(ctx->key)); + return 0; + default: + assert(0); + abort(); + }; } void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { - ngtcp2_crypto_boringssl_cipher_ctx *ctx; - if (!cipher_ctx->native_handle) { return; } - ctx = cipher_ctx->native_handle; - - if (ctx->type == NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP) { - EVP_CIPHER_CTX_free(ctx->ctx); - } - - free(ctx); + free(cipher_ctx->native_handle); } int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, @@ -331,18 +307,32 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, return 0; } +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { + const EVP_MD *prf = md->native_handle; + + if (HKDF(dest, destlen, prf, secret, secretlen, salt, saltlen, info, + infolen) != 1) { + return -1; + } + + return 0; +} + int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { const EVP_AEAD *cipher = aead->native_handle; EVP_AEAD_CTX *actx = aead_ctx->native_handle; size_t max_outlen = plaintextlen + EVP_AEAD_max_overhead(cipher); size_t outlen; if (EVP_AEAD_CTX_seal(actx, dest, &outlen, max_outlen, nonce, noncelen, - plaintext, plaintextlen, ad, adlen) != 1) { + plaintext, plaintextlen, aad, aadlen) != 1) { return -1; } @@ -353,15 +343,21 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { + const EVP_AEAD *cipher = aead->native_handle; EVP_AEAD_CTX *actx = aead_ctx->native_handle; - size_t max_outlen = ciphertextlen; + size_t max_overhead = EVP_AEAD_max_overhead(cipher); + size_t max_outlen; size_t outlen; - (void)aead; + if (ciphertextlen < max_overhead) { + return -1; + } + + max_outlen = ciphertextlen - max_overhead; if (EVP_AEAD_CTX_open(actx, dest, &outlen, max_outlen, nonce, noncelen, - ciphertext, ciphertextlen, ad, adlen) != 1) { + ciphertext, ciphertextlen, aad, aadlen) != 1) { return -1; } @@ -373,23 +369,16 @@ int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, const uint8_t *sample) { static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; ngtcp2_crypto_boringssl_cipher_ctx *ctx = hp_ctx->native_handle; - EVP_CIPHER_CTX *actx; - int len; uint32_t counter; (void)hp; switch (ctx->type) { - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP: - actx = ctx->ctx; - if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) || - !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, - sizeof(PLAINTEXT) - 1) || - !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { - return -1; - } + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128: + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256: + AES_ecb_encrypt(sample, dest, &ctx->aes_key, 1); return 0; - case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20: + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20: #if defined(WORDS_BIGENDIAN) counter = (uint32_t)sample[0] + (uint32_t)(sample[1] << 8) + (uint32_t)(sample[2] << 16) + (uint32_t)(sample[3] << 24); @@ -434,10 +423,7 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, SSL_reset_early_data_reject(ssl); - rv = ngtcp2_conn_early_data_rejected(conn); - if (rv != 0) { - return -1; - } + ngtcp2_conn_early_data_rejected(conn); goto retry; default: @@ -472,24 +458,13 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { SSL *ssl = tls; - ngtcp2_transport_params_type exttype = - ngtcp2_conn_is_server(conn) - ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO - : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS; const uint8_t *tp; size_t tplen; - ngtcp2_transport_params params; int rv; SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); - rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen); - if (rv != 0) { - ngtcp2_conn_set_tls_error(conn, rv); - return -1; - } - - rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen); if (rv != 0) { ngtcp2_conn_set_tls_error(conn, rv); return -1; @@ -520,6 +495,7 @@ ngtcp2_crypto_level ngtcp2_crypto_boringssl_from_ssl_encryption_level( return NGTCP2_CRYPTO_LEVEL_APPLICATION; default: assert(0); + abort(); } } @@ -536,5 +512,116 @@ enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level( return ssl_encryption_early_data; default: assert(0); + abort(); + } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + if (RAND_bytes(data, datalen) != 1) { + return -1; + } + + return 0; +} + +static int set_read_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level, + const SSL_CIPHER *cipher, const uint8_t *secret, + size_t secretlen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level); + (void)cipher; + + if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return 0; + } + + return 1; +} + +static int set_write_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level, + const SSL_CIPHER *cipher, const uint8_t *secret, + size_t secretlen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level); + (void)cipher; + + if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return 0; } + + return 1; +} + +static int add_handshake_data(SSL *ssl, enum ssl_encryption_level_t bssl_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level); + int rv; + + rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + return 1; +} + +static int flush_flight(SSL *ssl) { + (void)ssl; + return 1; +} + +static int send_alert(SSL *ssl, enum ssl_encryption_level_t bssl_level, + uint8_t alert) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + (void)bssl_level; + + ngtcp2_conn_set_tls_alert(conn, alert); + + return 1; +} + +static SSL_QUIC_METHOD quic_method = { + set_read_secret, set_write_secret, add_handshake_data, + flush_flight, send_alert, +}; + +static void crypto_boringssl_configure_context(SSL_CTX *ssl_ctx) { + SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_quic_method(ssl_ctx, &quic_method); +} + +int ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx) { + crypto_boringssl_configure_context(ssl_ctx); + + return 0; +} + +int ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx) { + crypto_boringssl_configure_context(ssl_ctx); + + return 0; } diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h index 23901d18c1646e..4736b51c3cb48d 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -31,6 +31,13 @@ extern "C" { #endif +#ifdef WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif /* WIN32 */ + /** * @macro * @@ -55,22 +62,13 @@ extern "C" { */ #define NGTCP2_CRYPTO_INITIAL_IVLEN 12 -/** - * @function - * - * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet - * encryption and decryption. - */ -NGTCP2_EXTERN ngtcp2_crypto_ctx * -ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); - /** * @function * * `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated * ciphers and message digests from native TLS session * |tls_native_handle|. This is used for encrypting/decrypting - * Handshake and Short packets. + * Handshake and Short header packets. * * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be * a pointer to SSL object. @@ -92,33 +90,6 @@ NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, NGTCP2_EXTERN ngtcp2_crypto_ctx * ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle); -/** - * @function - * - * `ngtcp2_crypto_aead_init` initializes |aead| with the provided - * |aead_native_handle| which is an underlying AEAD object. - * - * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be - * a pointer to EVP_CIPHER. - * - * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be - * gnutls_cipher_algorithm_t casted to ``void *``. - * - * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must - * be a pointer to EVP_AEAD. - */ -NGTCP2_EXTERN ngtcp2_crypto_aead * -ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, void *aead_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 * @@ -191,6 +162,20 @@ NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, const uint8_t *info, size_t infolen); +/** + * @function + * + * `ngtcp2_crypto_hkdf` performs HKDF operation. The result is + * |destlen| bytes long and is stored to the buffer pointed by |dest|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen); + /** * @function * @@ -235,39 +220,16 @@ typedef enum ngtcp2_crypto_side { NGTCP2_EXTERN size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead); -/** - * @function - * - * `ngtcp2_crypto_derive_packet_protection_key` derives packet - * protection key. This function writes packet protection key into - * the buffer pointed by |key|. |key| must point to the buffer which - * is at least ngtcp2_crypto_aead_keylen(aead) bytes long. This - * function writes packet protection IV into |iv|. |iv| must point to - * the buffer which is at least - * ngtcp2_crypto_packet_protection_ivlen(aead). |key| is - * ngtcp2_crypto_aead_keylen(aead) bytes long. |iv| is - * ngtcp2_crypto_packet_protection_ivlen(aead) bytes long. - * - * If |hp| is not NULL, this function also derives packet header - * protection key and writes the key into the buffer pointed by |hp|. - * The length of key is ngtcp2_crypto_aead_keylen(aead) bytes long. - * |hp|, if not NULL, must have enough capacity to store the key. - * - * This function returns 0 if it succeeds, or -1. - */ -NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key( - uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen); - /** * @function * * `ngtcp2_crypto_encrypt` encrypts |plaintext| of length * |plaintextlen| and writes the ciphertext into the buffer pointed by * |dest|. The length of ciphertext is plaintextlen + - * ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have - * enough capacity to store the ciphertext. It is allowed to specify - * the same value to |dest| and |plaintext|. + * :member:`aead->max_overhead ` + * bytes long. |dest| must have enough capacity to store the + * ciphertext. It is allowed to specify the same value to |dest| and + * |plaintext|. * * This function returns 0 if it succeeds, or -1. */ @@ -277,14 +239,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @function * * `ngtcp2_crypto_encrypt_cb` is a wrapper function around * `ngtcp2_crypto_encrypt`. It can be directly passed to - * :member:`ngtcp2_conn_callbacks.encrypt` field. + * :member:`ngtcp2_callbacks.encrypt` field. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. @@ -294,7 +256,7 @@ ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @function @@ -302,9 +264,10 @@ ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, * `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length * |ciphertextlen| and writes the plaintext into the buffer pointed by * |dest|. The length of plaintext is ciphertextlen - - * ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have - * enough capacity to store the plaintext. It is allowed to specify - * the same value to |dest| and |ciphertext|. + * :member:`aead->max_overhead ` + * bytes long. |dest| must have enough capacity to store the + * plaintext. It is allowed to specify the same value to |dest| and + * |ciphertext|. * * This function returns 0 if it succeeds, or -1. */ @@ -314,14 +277,14 @@ NGTCP2_EXTERN int ngtcp2_crypto_decrypt(uint8_t *dest, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @function * * `ngtcp2_crypto_decrypt_cb` is a wrapper function around * `ngtcp2_crypto_decrypt`. It can be directly passed to - * :member:`ngtcp2_conn_callbacks.decrypt` field. + * :member:`ngtcp2_callbacks.decrypt` field. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_TLS_DECRYPT`. @@ -331,15 +294,19 @@ ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @function * * `ngtcp2_crypto_hp_mask` generates mask which is used in packet * header encryption. The mask is written to the buffer pointed by - * |dest|. The length of mask is 5 bytes. |dest| must have enough - * capacity to store the mask. + * |dest|. The sample is passed as |sample| which is + * :macro:`NGTCP2_HP_SAMPLELEN` bytes long. The length of mask must + * be at least :macro:`NGTCP2_HP_MASKLEN`. The library only uses the + * first :macro:`NGTCP2_HP_MASKLEN` bytes of the produced mask. The + * buffer pointed by |dest| must have at least + * :macro:`NGTCP2_HP_SAMPLELEN` bytes available. * * This function returns 0 if it succeeds, or -1. */ @@ -353,7 +320,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest, * * `ngtcp2_crypto_hp_mask_cb` is a wrapper function around * `ngtcp2_crypto_hp_mask`. It can be directly passed to - * :member:`ngtcp2_conn_callbacks.hp_mask` field. + * :member:`ngtcp2_callbacks.hp_mask` field. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. @@ -379,16 +346,30 @@ ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * |secretlen| specifies the length of |secret|. * * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_crypto_ctx_tls`. + * `ngtcp2_crypto_aead_keylen(ctx->aead) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if + * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`). * * In the first call of this function, it calls - * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message - * digest algorithm. After the successful call of this function, - * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. - * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag - * length. + * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set + * negotiated AEAD and message digest algorithm. After the successful + * call of this function, application can use + * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get + * :type:`ngtcp2_crypto_ctx`. + * + * If |conn| is initialized as client, and |level| is + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this + * function retrieves a remote QUIC transport parameters extension + * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and + * sets it to |conn| by calling + * `ngtcp2_conn_decode_remote_transport_params`. * * This function returns 0 if it succeeds, or -1. */ @@ -412,20 +393,30 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key( * |secretlen| specifies the length of |secret|. * * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_crypto_ctx_tls`. + * `ngtcp2_crypto_aead_keylen(ctx->aead) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if + * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`). * * In the first call of this function, it calls - * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message - * digest algorithm. After the successful call of this function, - * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. - * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag - * length. - * - * If |level| is NGTCP2_CRYPTO_LEVEL_APP, this function retrieves a - * remote QUIC transport parameters extension from |tls| and sets it - * to |conn|. + * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set + * negotiated AEAD and message digest algorithm. After the successful + * call of this function, application can use + * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get + * :type:`ngtcp2_crypto_ctx`. + * + * If |conn| is initialized as server, and |level| is + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this + * function retrieves a remote QUIC transport parameters extension + * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and + * sets it to |conn| by calling + * `ngtcp2_conn_decode_remote_transport_params`. * * This function returns 0 if it succeeds, or -1. */ @@ -461,9 +452,11 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key( * length of |rx_secret| and |tx_secret|. * * The length of packet protection key and header protection key is - * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection - * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx - * can be obtained by `ngtcp2_conn_get_crypto_ctx`. + * `ngtcp2_crypto_aead_keylen(ctx->aead) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls`. * * This function returns 0 if it succeeds, or -1. */ @@ -479,7 +472,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_update_key( * * `ngtcp2_crypto_update_key_cb` is a wrapper function around * `ngtcp2_crypto_update_key`. It can be directly passed to - * :member:`ngtcp2_conn_callbacks.update_key` field. + * :member:`ngtcp2_callbacks.update_key` field. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. @@ -498,8 +491,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb( * encryption keys and sets QUIC transport parameters. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.client_initial` field. It is only - * used by client. + * :member:`ngtcp2_callbacks.client_initial` field. It is only used + * by client. * * This function returns 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. @@ -514,7 +507,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, * response to incoming Retry packet. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.recv_retry` field. It is only used + * :member:`ngtcp2_callbacks.recv_retry` field. It is only used * by client. * * This function returns 0 if it succeeds, or @@ -532,7 +525,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, * transport parameters. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.recv_client_initial` field. It is + * :member:`ngtcp2_callbacks.recv_client_initial` field. It is * only used by server. * * This function returns 0 if it succeeds, or @@ -549,8 +542,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, * length |datalen| in encryption level |crypto_level| and may feed * outgoing CRYPTO data to |conn|. This function can drive handshake. * This function can be also used after handshake completes. It is - * allowed to call this function with datalen == 0. In this case, no - * additional read operation is done. + * allowed to call this function with |datalen| == 0. In this case, + * no additional read operation is done. * * This function returns 0 if it succeeds, or a negative error code. * The generic error code is -1 if a specific error code is not @@ -563,34 +556,185 @@ ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, const uint8_t *data, size_t datalen); +/** + * @function + * + * `ngtcp2_crypto_recv_crypto_data_cb` is a wrapper function around + * `ngtcp2_crypto_read_write_crypto_data`. It can be directly passed + * to :member:`ngtcp2_callbacks.recv_crypto_data` field. + * + * If this function is used, the TLS implementation specific error + * codes described in `ngtcp2_crypto_read_write_crypto_data` are + * treated as if it returns -1. Do not use this function if an + * application wishes to use the TLS implementation specific error + * codes. + */ +NGTCP2_EXTERN int ngtcp2_crypto_recv_crypto_data_cb( + ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, uint64_t offset, + const uint8_t *data, size_t datalen, void *user_data); + /** * @function * * `ngtcp2_crypto_generate_stateless_reset_token` generates a - * stateless reset token using HKDF extraction with |md| using the - * given |cid| and static key |secret| as input. The token will be - * written to the buffer pointed by |token| and it must have a - * capacity of at least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` - * bytes. + * stateless reset token using HKDF extraction using the given |cid| + * and static key |secret| as input. The token will be written to + * the buffer pointed by |token| and it must have a capacity of at + * least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` bytes. * * This function returns 0 if it succeeds, or -1. */ 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); + uint8_t *token, const uint8_t *secret, size_t secretlen, + const ngtcp2_cid *cid); + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_RAND_DATALEN` is the length of random + * data added to a token generated by + * `ngtcp2_crypto_generate_retry_token` or + * `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_RAND_DATALEN 32 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY` is the magic byte for + * Retry token generated by `ngtcp2_crypto_generate_retry_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xb6 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR` is the magic byte for a + * token generated by `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR 0x36 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` is the maximum length of + * a token generated by `ngtcp2_crypto_generate_retry_token`. + */ +#define NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN \ + (/* magic = */ 1 + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN + \ + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \ + NGTCP2_CRYPTO_TOKEN_RAND_DATALEN) + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length + * of a token generated by `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN \ + (/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \ + NGTCP2_CRYPTO_TOKEN_RAND_DATALEN) + +/** + * @function + * + * `ngtcp2_crypto_generate_retry_token` generates a token in the + * buffer pointed by |token| that is sent with Retry packet. The + * buffer pointed by |token| must have at least + * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` bytes long. The + * successfully generated token starts with + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`. |secret| of length + * |secretlen| is an initial keying material to generate keys to + * encrypt the token. |version| is QUIC version. |remote_addr| of + * length |remote_addrlen| is an address of client. |retry_scid| is a + * Source Connection ID chosen by server and set in Retry packet. + * |odcid| is a Destination Connection ID in Initial packet sent by + * client. |ts| is the timestamp when the token is generated. + * + * This function returns the length of generated token if it succeeds, + * or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token( + uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in + * the buffer pointed by |token| of length |tokenlen|. |secret| of + * length |secretlen| is an initial keying material to generate keys + * to decrypt the token. |version| is QUIC version of the Initial + * packet that contains this token. |remote_addr| of length + * |remote_addrlen| is an address of client. |dcid| is a Destination + * Connection ID in Initial packet sent by client. |timeout| is the + * period during which the token is valid. |ts| is the current + * timestamp. When validation succeeds, the extracted Destination + * Connection ID (which is the Destination Connection ID in Initial + * packet sent by client that triggered Retry packet) is stored to the + * buffer pointed by |odcid|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token( + ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, + const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_generate_regular_token` generates a token in the + * buffer pointed by |token| that is sent with NEW_TOKEN frame. The + * buffer pointed by |token| must have at least + * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes long. The + * successfully generated token starts with + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`. |secret| of length + * |secretlen| is an initial keying material to generate keys to + * encrypt the token. |remote_addr| of length |remote_addrlen| is an + * address of client. |ts| is the timestamp when the token is + * generated. + * + * This function returns the length of generated token if it succeeds, + * or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token( + uint8_t *token, const uint8_t *secret, size_t secretlen, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_verify_regular_token` verifies a regular token + * stored in the buffer pointed by |token| of length |tokenlen|. + * |secret| of length |secretlen| is an initial keying material to + * generate keys to decrypt the token. |remote_addr| of length + * |remote_addrlen| is an address of client. |timeout| is the period + * during which the token is valid. |ts| is the current timestamp. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token( + const uint8_t *token, size_t tokenlen, const uint8_t *secret, + size_t secretlen, const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts); /** * @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. + * containing CONNECTION_CLOSE with the given |error_code| and the + * optional |reason| of length |reasonlen| 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. @@ -599,7 +743,8 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token( */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close( uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code); + const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, + size_t reasonlen); /** * @function @@ -664,7 +809,7 @@ ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx); * `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given |aead_ctx|. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.delete_crypto_aead_ctx` field. + * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` field. */ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb( ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data); @@ -676,11 +821,71 @@ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb( * |cipher_ctx|. * * This function can be directly passed to - * :member:`ngtcp2_conn_callbacks.delete_crypto_cipher_ctx` field. + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` field. */ NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); +/** + * @function + * + * `ngtcp2_crypto_get_path_challenge_data_cb` writes unpredictable + * sequence of :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes to |data| + * which is sent with PATH_CHALLENGE frame. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.get_path_challenge_data` field. + */ +NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, + uint8_t *data, + void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for + * |version| which is negotiated or being negotiated. |client_dcid| + * is the destination connection ID in first Initial packet of client. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.version_negotiation` field. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data); + +typedef struct ngtcp2_crypto_conn_ref ngtcp2_crypto_conn_ref; + +/** + * @functypedef + * + * :type:`ngtcp2_crypto_get_conn` is a callback function to get a + * pointer to :type:`ngtcp2_conn` from |conn_ref|. The implementation + * must return non-NULL :type:`ngtcp2_conn` object. + */ +typedef ngtcp2_conn *(*ngtcp2_crypto_get_conn)( + ngtcp2_crypto_conn_ref *conn_ref); + +/** + * @struct + * + * :type:`ngtcp2_crypto_conn_ref` is a structure to get a pointer to + * :type:`ngtcp2_conn`. It is meant to be set to TLS native handle as + * an application specific data (e.g. SSL_set_app_data in OpenSSL). + */ +typedef struct ngtcp2_crypto_conn_ref { + /** + * :member:`get_conn` is a callback function to get a pointer to + * :type:`ngtcp2_conn` object. + */ + ngtcp2_crypto_get_conn get_conn; + /** + * :member:`user_data` is a pointer to arbitrary user data. + */ + void *user_data; +} ngtcp2_crypto_conn_ref; + #ifdef __cplusplus } #endif diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h index 65f8414249a0c5..6497c09e79840d 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h @@ -55,6 +55,48 @@ NGTCP2_EXTERN enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level( ngtcp2_crypto_level crypto_level); +/** + * @function + * + * `ngtcp2_crypto_boringssl_configure_server_context` configures + * |ssl_ctx| for server side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx); + +/** + * @function + * + * `ngtcp2_crypto_boringssl_configure_client_context` configures + * |ssl_ctx| for client side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx); + #ifdef __cplusplus } #endif diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h index 10a8e712cfd63c..844081bfa8b055 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h @@ -83,6 +83,48 @@ NGTCP2_EXTERN OSSL_ENCRYPTION_LEVEL ngtcp2_crypto_openssl_from_ngtcp2_crypto_level( ngtcp2_crypto_level crypto_level); +/** + * @function + * + * `ngtcp2_crypto_openssl_configure_server_context` configures + * |ssl_ctx| for server side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx); + +/** + * @function + * + * `ngtcp2_crypto_openssl_configure_client_context` configures + * |ssl_ctx| for client side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx); + #ifdef __cplusplus } #endif diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h new file mode 100644 index 00000000000000..d4b551c382fd69 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h @@ -0,0 +1,246 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 NGTCP2_CRYPTO_PICOTLS_H +#define NGTCP2_CRYPTO_PICOTLS_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @struct + * + * :type:`ngtcp2_crypto_picotls_ctx` contains per-connection state + * of Picotls objects and must be an object to bet set to + * `ngtcp2_conn_set_tls_native_handle`. + */ +typedef struct ngtcp2_crypto_picotls_ctx { + /** + * :member:`ptls` is a pointer to ptls_t object. + */ + ptls_t *ptls; + /** + * :member:`handshake_properties` is a set of configurations used + * during this particular TLS handshake. + */ + ptls_handshake_properties_t handshake_properties; +} ngtcp2_crypto_picotls_ctx; + +/** + * @function + * + * `ngtcp2_crypto_picotls_ctx_init` initializes the object pointed by + * |cptls|. |cptls| must not be NULL. + */ +NGTCP2_EXTERN void +ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls); + +/** + * @function + * + * `ngtcp2_crypto_picotls_from_epoch` translates |epoch| to + * :type:`ngtcp2_crypto_level`. This function is only available for + * Picotls backend. + */ +NGTCP2_EXTERN ngtcp2_crypto_level +ngtcp2_crypto_picotls_from_epoch(size_t epoch); + +/** + * @function + * + * `ngtcp2_crypto_picotls_from_ngtcp2_crypto_level` translates + * |crypto_level| to epoch. This function is only available for + * Picotls backend. + */ +NGTCP2_EXTERN size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level); + +/** + * @function + * + * `ngtcp2_crypto_picotls_configure_server_context` configures |ctx| + * for server side QUIC connection. It performs the following + * modifications: + * + * - Set max_early_data_size to UINT32_MAX. + * - Set omit_end_of_early_data to 1. + * - Set update_traffic_key callback. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * ptls_t object by assigning the pointer using ptls_get_data_ptr, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx); + +/** + * @function + * + * `ngtcp2_crypto_picotls_configure_client_context` configures |ctx| + * for client side QUIC connection. It performs the following + * modifications: + * + * - Set omit_end_of_early_data to 1. + * - Set update_traffic_key callback. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * ptls_t object by assigning the pointer using ptls_get_data_ptr, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx); + +/** + * @function + * + * `ngtcp2_crypto_picotls_configure_server_session` configures |cptls| + * for server side QUIC connection. It performs the following + * modifications: + * + * - Set handshake_properties.collect_extension to + * `ngtcp2_crypto_picotls_collect_extension`. + * - Set handshake_properties.collected_extensions to + * `ngtcp2_crypto_picotls_collected_extensions`. + * + * The callbacks set by this function only handle QUIC Transport + * Parameters TLS extension. If an application needs to handle the + * other TLS extensions, set its own callbacks and call + * `ngtcp2_crypto_picotls_collect_extension` and + * `ngtcp2_crypto_picotls_collected_extensions` form them. + * + * During the QUIC handshake, the first element of + * handshake_properties.additional_extensions is assigned to send QUIC + * Transport Parameter TLS extension. Therefore, an application must + * allocate at least 2 elements for + * handshake_properties.additional_extensions. + * + * Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the + * resources. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * ptls_t object by assigning the pointer using ptls_get_data_ptr, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_picotls_configure_server_session( + ngtcp2_crypto_picotls_ctx *cptls); + +/** + * @function + * + * `ngtcp2_crypto_picotls_configure_client_session` configures |cptls| + * for client side QUIC connection. It performs the following + * modifications: + * + * - Set handshake_properties.max_early_data_size to a pointer to + * uint32_t, which is allocated dynamically by this function. + * - Set handshake_properties.collect_extension to + * `ngtcp2_crypto_picotls_collect_extension`. + * - Set handshake_properties.collected_extensions to + * `ngtcp2_crypto_picotls_collected_extensions`. + * - Set handshake_properties.additional_extensions[0].data to the + * dynamically allocated buffer which contains QUIC Transport + * Parameters TLS extension. An application must allocate at least + * 2 elements for handshake_properties.additional_extensions. + * + * The callbacks set by this function only handle QUIC Transport + * Parameters TLS extension. If an application needs to handle the + * other TLS extensions, set its own callbacks and call + * `ngtcp2_crypto_picotls_collect_extension` and + * `ngtcp2_crypto_picotls_collected_extensions` form them. + * + * Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the + * resources. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * ptls_t object by assigning the pointer using ptls_get_data_ptr, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_picotls_configure_client_session(ngtcp2_crypto_picotls_ctx *cptls, + ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_crypto_picotls_deconfigure_session` frees the resources + * allocated for |cptls| during QUIC connection. It frees the + * following data using :manpage:`free(3)`. + * + * - handshake_properties.max_early_data_size + * - handshake_properties.additional_extensions[0].data.base + * + * If |cptls| is NULL, this function does nothing. + */ +NGTCP2_EXTERN void +ngtcp2_crypto_picotls_deconfigure_session(ngtcp2_crypto_picotls_ctx *cptls); + +/** + * @function + * + * `ngtcp2_crypto_picotls_collect_extension` is a callback function + * which only returns nonzero if |type| == + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_picotls_collect_extension( + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + uint16_t type); + +/** + * @function + * + * `ngtcp2_crypto_picotls_collected_extensions` is a callback function + * which only handles the extension of type + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`. The other + * extensions are ignored. + */ +NGTCP2_EXTERN int ngtcp2_crypto_picotls_collected_extensions( + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + ptls_raw_extension_t *extensions); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_CRYPTO_PICOTLS_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h new file mode 100644 index 00000000000000..3b10802c25b5e8 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h @@ -0,0 +1,106 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 NGTCP2_CRYPTO_WOLFSSL_H +#define NGTCP2_CRYPTO_WOLFSSL_H + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @function + * + * `ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level` translates + * |wolfssl_level| to :type:`ngtcp2_crypto_level`. This function is only + * available for wolfSSL backend. + */ +NGTCP2_EXTERN ngtcp2_crypto_level +ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level( + WOLFSSL_ENCRYPTION_LEVEL wolfssl_level); + +/** + * @function + * + * `ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level` translates + * |crypto_level| to WOLFSSL_ENCRYPTION_LEVEL. This function is only + * available for wolfSSL backend. + */ +NGTCP2_EXTERN WOLFSSL_ENCRYPTION_LEVEL +ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level); + +/** + * @function + * + * `ngtcp2_crypto_wolfssl_configure_server_context` configures + * |ssl_ctx| for server side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * WOLFSSL object by calling wolfSSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx); + +/** + * @function + * + * `ngtcp2_crypto_wolfssl_configure_client_context` configures + * |ssl_ctx| for client side QUIC connection. It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling wolfSSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_CRYPTO_WOLFSSL_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c index d4b9e57f27581e..466d9e11ca6415 100644 --- a/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c +++ b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c @@ -34,6 +34,11 @@ #include #include #include +#include + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# include +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #include "shared.h" @@ -48,9 +53,19 @@ static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) { return EVP_CCM_TLS_TAG_LEN; default: assert(0); + abort(); /* if NDEBUG is set */ } } +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)EVP_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)EVP_sha256(); + return md; +} + ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aes_128_gcm()); ctx->md.native_handle = (void *)EVP_sha256(); @@ -187,18 +202,37 @@ int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, const EVP_CIPHER *cipher = aead->native_handle; int cipher_nid = EVP_CIPHER_nid(cipher); EVP_CIPHER_CTX *actx; + size_t taglen = crypto_aead_max_overhead(cipher); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM params[3]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ actx = EVP_CIPHER_CTX_new(); if (actx == NULL) { return -1; } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + + if (cipher_nid == NID_aes_128_ccm) { + params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + NULL, taglen); + params[2] = OSSL_PARAM_construct_end(); + } else { + params[1] = OSSL_PARAM_construct_end(); + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + !EVP_CIPHER_CTX_set_params(actx, params) || +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, NULL) || (cipher_nid == NID_aes_128_ccm && - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, - (int)crypto_aead_max_overhead(cipher), NULL)) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) { EVP_CIPHER_CTX_free(actx); return -1; @@ -215,18 +249,37 @@ int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, const EVP_CIPHER *cipher = aead->native_handle; int cipher_nid = EVP_CIPHER_nid(cipher); EVP_CIPHER_CTX *actx; + size_t taglen = crypto_aead_max_overhead(cipher); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM params[3]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ actx = EVP_CIPHER_CTX_new(); if (actx == NULL) { return -1; } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + + if (cipher_nid == NID_aes_128_ccm) { + params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + NULL, taglen); + params[2] = OSSL_PARAM_construct_end(); + } else { + params[1] = OSSL_PARAM_construct_end(); + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + !EVP_CIPHER_CTX_set_params(actx, params) || +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, NULL) || (cipher_nid == NID_aes_128_ccm && - !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, - (int)crypto_aead_max_overhead(cipher), NULL)) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) { EVP_CIPHER_CTX_free(actx); return -1; @@ -272,6 +325,33 @@ void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen, const uint8_t *salt, size_t saltlen) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, + saltlen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + EVP_KDF_free(kdf); + + if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ const EVP_MD *prf = md->native_handle; int rv = 0; EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); @@ -293,12 +373,40 @@ int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, EVP_PKEY_CTX_free(pctx); return rv; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ } int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen, const uint8_t *info, size_t infolen) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, + infolen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + EVP_KDF_free(kdf); + + if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ const EVP_MD *prf = md->native_handle; int rv = 0; EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); @@ -309,7 +417,7 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, if (EVP_PKEY_derive_init(pctx) != 1 || EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1 || EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || - EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1 || + EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)"", 0) != 1 || EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 || EVP_PKEY_derive(pctx, dest, &destlen) != 1) { @@ -319,29 +427,97 @@ int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, EVP_PKEY_CTX_free(pctx); return rv; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, + saltlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, + infolen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + EVP_KDF_free(kdf); + + if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ + const EVP_MD *prf = md->native_handle; + int rv = 0; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (pctx == NULL) { + return -1; + } + + if (EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) != + 1 || + EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || + EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 || + EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || + EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 || + EVP_PKEY_derive(pctx, dest, &destlen) != 1) { + rv = -1; + } + + EVP_PKEY_CTX_free(pctx); + + return rv; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ } int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { const EVP_CIPHER *cipher = aead->native_handle; size_t taglen = crypto_aead_max_overhead(cipher); int cipher_nid = EVP_CIPHER_nid(cipher); EVP_CIPHER_CTX *actx = aead_ctx->native_handle; int len; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM params[] = { + OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + dest + plaintextlen, taglen), + OSSL_PARAM_construct_end(), + }; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ (void)noncelen; if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) || (cipher_nid == NID_aes_128_ccm && !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) || - !EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) || + !EVP_EncryptUpdate(actx, NULL, &len, aad, (int)aadlen) || !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) || !EVP_EncryptFinal_ex(actx, dest + len, &len) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + !EVP_CIPHER_CTX_get_params(actx, params) +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen, - dest + plaintextlen)) { + dest + plaintextlen) +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ + ) { return -1; } @@ -352,13 +528,16 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { const EVP_CIPHER *cipher = aead->native_handle; size_t taglen = crypto_aead_max_overhead(cipher); int cipher_nid = EVP_CIPHER_nid(cipher); EVP_CIPHER_CTX *actx = aead_ctx->native_handle; int len; const uint8_t *tag; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM params[2]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ (void)noncelen; @@ -369,12 +548,22 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, ciphertextlen -= taglen; tag = ciphertext + ciphertextlen; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + (void *)tag, taglen); + params[1] = OSSL_PARAM_construct_end(); +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + !EVP_CIPHER_CTX_set_params(actx, params) || +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, (uint8_t *)tag) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ (cipher_nid == NID_aes_128_ccm && !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) || - !EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) || + !EVP_DecryptUpdate(actx, NULL, &len, aad, (int)aadlen) || !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) || (cipher_nid != NID_aes_128_ccm && !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) { @@ -457,24 +646,13 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { SSL *ssl = tls; - ngtcp2_transport_params_type exttype = - ngtcp2_conn_is_server(conn) - ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO - : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS; const uint8_t *tp; size_t tplen; - ngtcp2_transport_params params; int rv; SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); - rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen); - if (rv != 0) { - ngtcp2_conn_set_tls_error(conn, rv); - return -1; - } - - rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen); if (rv != 0) { ngtcp2_conn_set_tls_error(conn, rv); return -1; @@ -505,6 +683,7 @@ ngtcp2_crypto_level ngtcp2_crypto_openssl_from_ossl_encryption_level( return NGTCP2_CRYPTO_LEVEL_APPLICATION; default: assert(0); + abort(); /* if NDEBUG is set */ } } @@ -522,5 +701,107 @@ ngtcp2_crypto_openssl_from_ngtcp2_crypto_level( return ssl_encryption_early_data; default: assert(0); + abort(); /* if NDEBUG is set */ } } + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + if (RAND_bytes(data, (int)datalen) != 1) { + return -1; + } + + return 0; +} + +static int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level); + + if (rx_secret && + ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + rx_secret, secretlen) != 0) { + return 0; + } + + if (tx_secret && + ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + tx_secret, secretlen) != 0) { + return 0; + } + + return 1; +} + +static int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level); + int rv; + + rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + return 1; +} + +static int flush_flight(SSL *ssl) { + (void)ssl; + return 1; +} + +static int send_alert(SSL *ssl, enum ssl_encryption_level_t level, + uint8_t alert) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + (void)level; + + ngtcp2_conn_set_tls_alert(conn, alert); + + return 1; +} + +static SSL_QUIC_METHOD quic_method = { + set_encryption_secrets, + add_handshake_data, + flush_flight, + send_alert, +}; + +static void crypto_openssl_configure_context(SSL_CTX *ssl_ctx) { + SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_quic_method(ssl_ctx, &quic_method); +} + +int ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx) { + crypto_openssl_configure_context(ssl_ctx); + + return 0; +} + +int ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx) { + crypto_openssl_configure_context(ssl_ctx); + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c b/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c new file mode 100644 index 00000000000000..32d17adc6c3a35 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c @@ -0,0 +1,697 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include +#include + +#include +#include + +#include "shared.h" + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)&ptls_openssl_sha256; + return md; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ngtcp2_crypto_aead_init(&ctx->aead, (void *)&ptls_openssl_aes128gcm); + ctx->md.native_handle = (void *)&ptls_openssl_sha256; + ctx->hp.native_handle = (void *)&ptls_openssl_aes128ctr; + ctx->max_encryption = 0; + ctx->max_decryption_failure = 0; + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle) { + ptls_aead_algorithm_t *alg = aead_native_handle; + + aead->native_handle = aead_native_handle; + aead->max_overhead = alg->tag_size; + return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm); +} + +static const ptls_aead_algorithm_t *crypto_ptls_get_aead(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + return cs->aead; +} + +static uint64_t crypto_ptls_get_aead_max_encryption(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + if (cs->aead == &ptls_openssl_aes128gcm || + cs->aead == &ptls_openssl_aes256gcm) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; + } + + if (cs->aead == &ptls_openssl_chacha20poly1305) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; + } + + return 0; +} + +static uint64_t crypto_ptls_get_aead_max_decryption_failure(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + if (cs->aead == &ptls_openssl_aes128gcm || + cs->aead == &ptls_openssl_aes256gcm) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; + } + + if (cs->aead == &ptls_openssl_chacha20poly1305) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; + } + + return 0; +} + +static const ptls_cipher_algorithm_t *crypto_ptls_get_hp(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + if (cs->aead == &ptls_openssl_aes128gcm) { + return &ptls_openssl_aes128ctr; + } + + if (cs->aead == &ptls_openssl_aes256gcm) { + return &ptls_openssl_aes256ctr; + } + + if (cs->aead == &ptls_openssl_chacha20poly1305) { + return &ptls_openssl_chacha20; + } + + return NULL; +} + +static const ptls_hash_algorithm_t *crypto_ptls_get_md(ptls_t *ptls) { + ptls_cipher_suite_t *cs = ptls_get_cipher(ptls); + + return cs->hash; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + ngtcp2_crypto_picotls_ctx *cptls = tls_native_handle; + ngtcp2_crypto_aead_init(&ctx->aead, + (void *)crypto_ptls_get_aead(cptls->ptls)); + ctx->md.native_handle = (void *)crypto_ptls_get_md(cptls->ptls); + ctx->hp.native_handle = (void *)crypto_ptls_get_hp(cptls->ptls); + ctx->max_encryption = crypto_ptls_get_aead_max_encryption(cptls->ptls); + ctx->max_decryption_failure = + crypto_ptls_get_aead_max_decryption_failure(cptls->ptls); + return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const ptls_hash_algorithm_t *md) { + return md->digest_size; +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const ptls_aead_algorithm_t *aead) { + return aead->key_size; +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const ptls_aead_algorithm_t *aead) { + return aead->iv_size; +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const ptls_aead_algorithm_t *cipher = aead->native_handle; + size_t keylen = crypto_aead_keylen(cipher); + ptls_aead_context_t *actx; + static const uint8_t iv[PTLS_MAX_IV_SIZE] = {0}; + + (void)noncelen; + (void)keylen; + + actx = ptls_aead_new_direct(cipher, /* is_enc = */ 1, key, iv); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const ptls_aead_algorithm_t *cipher = aead->native_handle; + size_t keylen = crypto_aead_keylen(cipher); + ptls_aead_context_t *actx; + const uint8_t iv[PTLS_MAX_IV_SIZE] = {0}; + + (void)noncelen; + (void)keylen; + + actx = ptls_aead_new_direct(cipher, /* is_enc = */ 0, key, iv); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { + if (aead_ctx->native_handle) { + ptls_aead_free(aead_ctx->native_handle); + } +} + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key) { + ptls_cipher_context_t *actx; + + actx = ptls_cipher_new(cipher->native_handle, /* is_enc = */ 1, key); + if (actx == NULL) { + return -1; + } + + cipher_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { + if (cipher_ctx->native_handle) { + ptls_cipher_free(cipher_ctx->native_handle); + } +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + ptls_iovec_t saltv, ikm; + + saltv = ptls_iovec_init(salt, saltlen); + ikm = ptls_iovec_init(secret, secretlen); + + if (ptls_hkdf_extract(md->native_handle, dest, saltv, ikm) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *info, + size_t infolen) { + ptls_iovec_t prk, infov; + + prk = ptls_iovec_init(secret, secretlen); + infov = ptls_iovec_init(info, infolen); + + if (ptls_hkdf_expand(md->native_handle, dest, destlen, prk, infov) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { + ptls_iovec_t saltv, ikm, prk, infov; + uint8_t prkbuf[PTLS_MAX_DIGEST_SIZE]; + ptls_hash_algorithm_t *algo = md->native_handle; + + saltv = ptls_iovec_init(salt, saltlen); + ikm = ptls_iovec_init(secret, secretlen); + + if (ptls_hkdf_extract(algo, prkbuf, saltv, ikm) != 0) { + return -1; + } + + prk = ptls_iovec_init(prkbuf, algo->digest_size); + infov = ptls_iovec_init(info, infolen); + + if (ptls_hkdf_expand(algo, dest, destlen, prk, infov) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + ptls_aead_context_t *actx = aead_ctx->native_handle; + + (void)aead; + + ptls_aead_xor_iv(actx, nonce, noncelen); + + ptls_aead_encrypt(actx, dest, plaintext, plaintextlen, 0, aad, aadlen); + + /* zero-out static iv once again */ + ptls_aead_xor_iv(actx, nonce, noncelen); + + return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + ptls_aead_context_t *actx = aead_ctx->native_handle; + + (void)aead; + + ptls_aead_xor_iv(actx, nonce, noncelen); + + if (ptls_aead_decrypt(actx, dest, ciphertext, ciphertextlen, 0, aad, + aadlen) == SIZE_MAX) { + return -1; + } + + /* zero-out static iv once again */ + ptls_aead_xor_iv(actx, nonce, noncelen); + + return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + ptls_cipher_context_t *actx = hp_ctx->native_handle; + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + + (void)hp; + + ptls_cipher_init(actx, sample); + ptls_cipher_encrypt(actx, dest, PLAINTEXT, sizeof(PLAINTEXT) - 1); + + return 0; +} + +int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_picotls_ctx *cptls = ngtcp2_conn_get_tls_native_handle(conn); + ptls_buffer_t sendbuf; + size_t epoch_offsets[5] = {0}; + size_t epoch = ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(crypto_level); + size_t epoch_datalen; + size_t i; + int rv; + + ptls_buffer_init(&sendbuf, (void *)"", 0); + + assert(epoch == ptls_get_read_epoch(cptls->ptls)); + + rv = ptls_handle_message(cptls->ptls, &sendbuf, epoch_offsets, epoch, data, + datalen, &cptls->handshake_properties); + if (rv != 0 && rv != PTLS_ERROR_IN_PROGRESS) { + if (PTLS_ERROR_GET_CLASS(rv) == PTLS_ERROR_CLASS_SELF_ALERT) { + ngtcp2_conn_set_tls_alert(conn, (uint8_t)PTLS_ERROR_TO_ALERT(rv)); + } + + rv = -1; + goto fin; + } + + if (!ngtcp2_conn_is_server(conn) && + cptls->handshake_properties.client.early_data_acceptance == + PTLS_EARLY_DATA_REJECTED) { + ngtcp2_conn_early_data_rejected(conn); + } + + for (i = 0; i < 4; ++i) { + epoch_datalen = epoch_offsets[i + 1] - epoch_offsets[i]; + if (epoch_datalen == 0) { + continue; + } + + assert(i != 1); + + if (ngtcp2_conn_submit_crypto_data( + conn, ngtcp2_crypto_picotls_from_epoch(i), + sendbuf.base + epoch_offsets[i], epoch_datalen) != 0) { + rv = -1; + goto fin; + } + } + + if (rv == 0) { + ngtcp2_conn_handshake_completed(conn); + } + + rv = 0; + +fin: + ptls_buffer_dispose(&sendbuf); + + return rv; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { + (void)conn; + (void)tls; + + /* The remote transport parameters will be set via picotls + collected_extensions callback */ + + return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len) { + (void)tls; + (void)buf; + (void)len; + + /* The local transport parameters will be set in an external + call. */ + + return 0; +} + +ngtcp2_crypto_level ngtcp2_crypto_picotls_from_epoch(size_t epoch) { + switch (epoch) { + case 0: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case 1: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case 2: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case 3: + return NGTCP2_CRYPTO_LEVEL_APPLICATION; + default: + assert(0); + abort(); + } +} + +size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level) { + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + return 0; + case NGTCP2_CRYPTO_LEVEL_EARLY: + return 1; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + return 2; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + return 3; + default: + assert(0); + abort(); + } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + ptls_openssl_random_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN); + + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + ptls_openssl_random_bytes(data, datalen); + + return 0; +} + +void ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls) { + cptls->ptls = NULL; + memset(&cptls->handshake_properties, 0, sizeof(cptls->handshake_properties)); +} + +static int set_additional_extensions(ptls_handshake_properties_t *hsprops, + ngtcp2_conn *conn) { + const size_t buflen = 256; + uint8_t *buf; + ngtcp2_ssize nwrite; + ptls_raw_extension_t *exts = hsprops->additional_extensions; + + assert(exts); + + buf = malloc(buflen); + if (buf == NULL) { + return -1; + } + + nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, buflen); + if (nwrite < 0) { + goto fail; + } + + exts[0].type = NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1; + exts[0].data.base = buf; + exts[0].data.len = (size_t)nwrite; + + return 0; + +fail: + free(buf); + + return -1; +} + +int ngtcp2_crypto_picotls_collect_extension( + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + uint16_t type) { + (void)ptls; + (void)properties; + + return type == NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1; +} + +int ngtcp2_crypto_picotls_collected_extensions( + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + ptls_raw_extension_t *extensions) { + ngtcp2_crypto_conn_ref *conn_ref; + ngtcp2_conn *conn; + int rv; + + (void)properties; + + for (; extensions->type != UINT16_MAX; ++extensions) { + if (extensions->type != NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1) { + continue; + } + + conn_ref = *ptls_get_data_ptr(ptls); + conn = conn_ref->get_conn(conn_ref); + + rv = ngtcp2_conn_decode_remote_transport_params(conn, extensions->data.base, + extensions->data.len); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + return 0; + } + + return 0; +} + +static int update_traffic_key_server_cb(ptls_update_traffic_key_t *self, + ptls_t *ptls, int is_enc, size_t epoch, + const void *secret) { + ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch); + ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls); + size_t secretlen = cipher->hash->digest_size; + ngtcp2_crypto_picotls_ctx *cptls; + + (void)self; + + if (is_enc) { + if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return -1; + } + + if (level == NGTCP2_CRYPTO_LEVEL_HANDSHAKE) { + /* libngtcp2 allows an application to change QUIC transport + * parameters before installing Handshake tx key. We need to + * wait for the key to get the correct local transport + * parameters from ngtcp2_conn. + */ + cptls = ngtcp2_conn_get_tls_native_handle(conn); + + if (set_additional_extensions(&cptls->handshake_properties, conn) != 0) { + return -1; + } + } + + return 0; + } + + if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return -1; + } + + return 0; +} + +static ptls_update_traffic_key_t update_traffic_key_server = { + update_traffic_key_server_cb, +}; + +static int update_traffic_key_cb(ptls_update_traffic_key_t *self, ptls_t *ptls, + int is_enc, size_t epoch, const void *secret) { + ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch); + ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls); + size_t secretlen = cipher->hash->digest_size; + + (void)self; + + if (is_enc) { + if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return -1; + } + + return 0; + } + + if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return -1; + } + + return 0; +} + +static ptls_update_traffic_key_t update_traffic_key = {update_traffic_key_cb}; + +int ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx) { + ctx->max_early_data_size = UINT32_MAX; + ctx->omit_end_of_early_data = 1; + ctx->update_traffic_key = &update_traffic_key_server; + + return 0; +} + +int ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx) { + ctx->omit_end_of_early_data = 1; + ctx->update_traffic_key = &update_traffic_key; + + return 0; +} + +int ngtcp2_crypto_picotls_configure_server_session( + ngtcp2_crypto_picotls_ctx *cptls) { + ptls_handshake_properties_t *hsprops = &cptls->handshake_properties; + + hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension; + hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions; + + return 0; +} + +int ngtcp2_crypto_picotls_configure_client_session( + ngtcp2_crypto_picotls_ctx *cptls, ngtcp2_conn *conn) { + ptls_handshake_properties_t *hsprops = &cptls->handshake_properties; + + hsprops->client.max_early_data_size = calloc(1, sizeof(uint32_t)); + if (hsprops->client.max_early_data_size == NULL) { + return -1; + } + + if (set_additional_extensions(hsprops, conn) != 0) { + free(hsprops->client.max_early_data_size); + hsprops->client.max_early_data_size = NULL; + return -1; + } + + hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension; + hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions; + + return 0; +} + +void ngtcp2_crypto_picotls_deconfigure_session( + ngtcp2_crypto_picotls_ctx *cptls) { + ptls_handshake_properties_t *hsprops; + ptls_raw_extension_t *exts; + + if (cptls == NULL) { + return; + } + + hsprops = &cptls->handshake_properties; + + free(hsprops->client.max_early_data_size); + + exts = hsprops->additional_extensions; + if (exts) { + free(hsprops->additional_extensions[0].data.base); + } +} diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.c b/deps/ngtcp2/ngtcp2/crypto/shared.c index 5d040f2ce75558..78252b852b4fab 100644 --- a/deps/ngtcp2/ngtcp2/crypto/shared.c +++ b/deps/ngtcp2/ngtcp2/crypto/shared.c @@ -24,10 +24,18 @@ */ #include "shared.h" +#ifdef WIN32 +# include +# include +#else +# include +#endif + #include #include #include "ngtcp2_macro.h" +#include "ngtcp2_net.h" ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md, void *md_native_handle) { @@ -78,10 +86,16 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret, ngtcp2_crypto_ctx_initial(&ctx); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1; saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2_DRAFT; + saltlen = sizeof(NGTCP2_INITIAL_SALT_V2_DRAFT) - 1; + break; + default: salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT; saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1; } @@ -119,27 +133,55 @@ size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) { } int ngtcp2_crypto_derive_packet_protection_key( - uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) { - static const uint8_t KEY_LABEL[] = "quic key"; - static const uint8_t IV_LABEL[] = "quic iv"; - static const uint8_t HP_KEY_LABEL[] = "quic hp"; + uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version, + const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen) { + static const uint8_t KEY_LABEL_V1[] = "quic key"; + static const uint8_t IV_LABEL_V1[] = "quic iv"; + static const uint8_t HP_KEY_LABEL_V1[] = "quic hp"; + static const uint8_t KEY_LABEL_V2_DRAFT[] = "quicv2 key"; + static const uint8_t IV_LABEL_V2_DRAFT[] = "quicv2 iv"; + static const uint8_t HP_KEY_LABEL_V2_DRAFT[] = "quicv2 hp"; size_t keylen = ngtcp2_crypto_aead_keylen(aead); size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + const uint8_t *key_label; + size_t key_labellen; + const uint8_t *iv_label; + size_t iv_labellen; + const uint8_t *hp_key_label; + size_t hp_key_labellen; + + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + key_label = KEY_LABEL_V2_DRAFT; + key_labellen = sizeof(KEY_LABEL_V2_DRAFT) - 1; + iv_label = IV_LABEL_V2_DRAFT; + iv_labellen = sizeof(IV_LABEL_V2_DRAFT) - 1; + hp_key_label = HP_KEY_LABEL_V2_DRAFT; + hp_key_labellen = sizeof(HP_KEY_LABEL_V2_DRAFT) - 1; + break; + default: + key_label = KEY_LABEL_V1; + key_labellen = sizeof(KEY_LABEL_V1) - 1; + iv_label = IV_LABEL_V1; + iv_labellen = sizeof(IV_LABEL_V1) - 1; + hp_key_label = HP_KEY_LABEL_V1; + hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1; + } if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen, - KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) { + key_label, key_labellen) != 0) { return -1; } if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen, - IV_LABEL, sizeof(IV_LABEL) - 1) != 0) { + iv_label, iv_labellen) != 0) { return -1; } - if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label( - hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL, - sizeof(HP_KEY_LABEL) - 1) != 0) { + if (hp_key != NULL && + ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen, + hp_key_label, hp_key_labellen) != 0) { return -1; } @@ -176,6 +218,7 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, size_t ivlen; int rv; ngtcp2_crypto_ctx cctx; + uint32_t version; if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) { return 0; @@ -191,12 +234,25 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, hp_key = hp_keybuf; } - if (level == NGTCP2_CRYPTO_LEVEL_EARLY) { + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: ngtcp2_crypto_ctx_tls_early(&cctx, tls); ngtcp2_conn_set_early_crypto_ctx(conn, &cctx); ctx = ngtcp2_conn_get_early_crypto_ctx(conn); - } else { + version = ngtcp2_conn_get_client_chosen_version(conn); + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + if (ngtcp2_conn_is_server(conn) && + !ngtcp2_conn_get_negotiated_version(conn)) { + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); + if (rv != 0) { + return -1; + } + } + /* fall through */ + default: ctx = ngtcp2_conn_get_crypto_ctx(conn); + version = ngtcp2_conn_get_negotiated_version(conn); if (!ctx->aead.native_handle) { ngtcp2_crypto_ctx_tls(&cctx, tls); @@ -210,8 +266,8 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, hp = &ctx->hp; ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, - secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead, + md, secret, secretlen) != 0) { return -1; } @@ -272,17 +328,10 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, * This function returns 0 if it succeeds, or -1. */ static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) { - ngtcp2_transport_params_type exttype = - ngtcp2_conn_is_server(conn) - ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS - : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO; - ngtcp2_transport_params params; ngtcp2_ssize nwrite; uint8_t buf[256]; - ngtcp2_conn_get_local_transport_params(conn, ¶ms); - - nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, ¶ms); + nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf)); if (nwrite < 0) { return -1; } @@ -310,6 +359,7 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, size_t ivlen; int rv; ngtcp2_crypto_ctx cctx; + uint32_t version; if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) { return 0; @@ -325,12 +375,25 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, hp_key = hp_keybuf; } - if (level == NGTCP2_CRYPTO_LEVEL_EARLY) { + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: ngtcp2_crypto_ctx_tls_early(&cctx, tls); ngtcp2_conn_set_early_crypto_ctx(conn, &cctx); ctx = ngtcp2_conn_get_early_crypto_ctx(conn); - } else { + version = ngtcp2_conn_get_client_chosen_version(conn); + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + if (ngtcp2_conn_is_server(conn) && + !ngtcp2_conn_get_negotiated_version(conn)) { + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); + if (rv != 0) { + return -1; + } + } + /* fall through */ + default: ctx = ngtcp2_conn_get_crypto_ctx(conn); + version = ngtcp2_conn_get_negotiated_version(conn); if (!ctx->aead.native_handle) { ngtcp2_crypto_ctx_tls(&cctx, tls); @@ -344,8 +407,8 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, hp = &ctx->hp; ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, - secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead, + md, secret, secretlen) != 0) { return -1; } @@ -371,15 +434,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, goto fail; } - if (ngtcp2_conn_is_server(conn)) { - rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); - if (rv != 0) { - return rv; - } - - if (crypto_set_local_transport_params(conn, tls) != 0) { - return rv; - } + if (ngtcp2_conn_is_server(conn) && + crypto_set_local_transport_params(conn, tls) != 0) { + goto fail; } break; @@ -408,7 +465,7 @@ int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, - const ngtcp2_cid *client_dcid) { + uint32_t version, const ngtcp2_cid *client_dcid) { uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; @@ -427,7 +484,6 @@ int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_crypto_aead_ctx retry_aead_ctx = {0}; int rv; int server = ngtcp2_conn_is_server(conn); - uint32_t version = ngtcp2_conn_get_negotiated_version(conn); const uint8_t *retry_key; size_t retry_noncelen; @@ -472,13 +528,13 @@ int ngtcp2_crypto_derive_and_install_initial_key( } if (ngtcp2_crypto_derive_packet_protection_key( - rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret, + rx_key, rx_iv, rx_hp_key, version, &ctx.aead, &ctx.md, rx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } if (ngtcp2_crypto_derive_packet_protection_key( - tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } @@ -506,10 +562,16 @@ int ngtcp2_crypto_derive_and_install_initial_key( if (!server && !ngtcp2_conn_after_retry(conn)) { ngtcp2_crypto_aead_retry(&retry_aead); - if (ngtcp2_conn_get_negotiated_version(conn) == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT; + retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT; retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -543,6 +605,114 @@ int ngtcp2_crypto_derive_and_install_initial_key( return -1; } +int ngtcp2_crypto_derive_and_install_vneg_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, + uint32_t version, const ngtcp2_cid *client_dcid) { + uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn); + ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0}; + ngtcp2_crypto_aead_ctx tx_aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0}; + int rv; + int server = ngtcp2_conn_is_server(conn); + + if (!rx_secret) { + rx_secret = rx_secretbuf; + } + if (!tx_secret) { + tx_secret = tx_secretbuf; + } + if (!initial_secret) { + initial_secret = initial_secretbuf; + } + + if (!rx_key) { + rx_key = rx_keybuf; + } + if (!rx_iv) { + rx_iv = rx_ivbuf; + } + if (!rx_hp_key) { + rx_hp_key = rx_hp_keybuf; + } + if (!tx_key) { + tx_key = tx_keybuf; + } + if (!tx_iv) { + tx_iv = tx_ivbuf; + } + if (!tx_hp_key) { + tx_hp_key = tx_hp_keybuf; + } + + if (ngtcp2_crypto_derive_initial_secrets( + version, rx_secret, tx_secret, initial_secret, client_dcid, + server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) != + 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, rx_hp_key, version, &ctx->aead, &ctx->md, rx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, version, &ctx->aead, &ctx->md, tx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) != + 0) { + goto fail; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) != + 0) { + goto fail; + } + + rv = ngtcp2_conn_install_vneg_initial_key( + conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv, + &tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN); + if (rv != 0) { + goto fail; + } + + return 0; + +fail: + ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx); + ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx); + ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx); + ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx); + + return -1; +} + int ngtcp2_crypto_update_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, @@ -553,14 +723,15 @@ int ngtcp2_crypto_update_key( const ngtcp2_crypto_aead *aead = &ctx->aead; const ngtcp2_crypto_md *md = &ctx->md; size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + uint32_t version = ngtcp2_conn_get_negotiated_version(conn); if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret, secretlen) != 0) { return -1; } - if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md, - rx_secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) { return -1; } @@ -569,8 +740,8 @@ int ngtcp2_crypto_update_key( return -1; } - if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md, - tx_secret, secretlen) != 0) { + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) { return -1; } @@ -593,9 +764,9 @@ int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen, - nonce, noncelen, ad, adlen) != 0) { + nonce, noncelen, aad, aadlen) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; @@ -605,10 +776,10 @@ int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { + const uint8_t *aad, size_t aadlen) { if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen, - nonce, noncelen, ad, adlen) != 0) { - return NGTCP2_ERR_TLS_DECRYPT; + nonce, noncelen, aad, aadlen) != 0) { + return NGTCP2_ERR_DECRYPT; } return 0; } @@ -643,32 +814,413 @@ int ngtcp2_crypto_update_key_cb( } 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) { - uint8_t buf[64]; + static const uint8_t info[] = "stateless_reset"; + ngtcp2_crypto_md md; + + if (ngtcp2_crypto_hkdf(token, NGTCP2_STATELESS_RESET_TOKENLEN, + ngtcp2_crypto_md_sha256(&md), secret, secretlen, + cid->data, cid->datalen, info, + sizeof(info) - 1) != 0) { + return -1; + } + + return 0; +} + +static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv, + size_t ivlen, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen, + const uint8_t *info_prefix, + size_t info_prefixlen) { + static const uint8_t key_info_suffix[] = " key"; + static const uint8_t iv_info_suffix[] = " iv"; + uint8_t intsecret[32]; + uint8_t info[32]; + uint8_t *p; + + assert(ngtcp2_crypto_md_hashlen(md) == sizeof(intsecret)); + assert(info_prefixlen + sizeof(key_info_suffix) - 1 <= sizeof(info)); + assert(info_prefixlen + sizeof(iv_info_suffix) - 1 <= sizeof(info)); + + if (ngtcp2_crypto_hkdf_extract(intsecret, md, secret, secretlen, salt, + saltlen) != 0) { + return -1; + } + + memcpy(info, info_prefix, info_prefixlen); + p = info + info_prefixlen; + + memcpy(p, key_info_suffix, sizeof(key_info_suffix) - 1); + p += sizeof(key_info_suffix) - 1; + + if (ngtcp2_crypto_hkdf_expand(key, keylen, md, intsecret, sizeof(intsecret), + info, (size_t)(p - info)) != 0) { + return -1; + } + + p = info + info_prefixlen; + + memcpy(p, iv_info_suffix, sizeof(iv_info_suffix) - 1); + p += sizeof(iv_info_suffix) - 1; + + if (ngtcp2_crypto_hkdf_expand(iv, ivlen, md, intsecret, sizeof(intsecret), + info, (size_t)(p - info)) != 0) { + return -1; + } + + return 0; +} + +static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version, + const ngtcp2_sockaddr *sa, + ngtcp2_socklen salen, + const ngtcp2_cid *retry_scid) { + uint8_t *p = dest; + + version = ngtcp2_htonl(version); + memcpy(p, &version, sizeof(version)); + memcpy(p, sa, (size_t)salen); + p += salen; + memcpy(p, retry_scid->data, retry_scid->datalen); + p += retry_scid->datalen; + + return (size_t)(p - dest); +} + +static const uint8_t retry_token_info_prefix[] = "retry_token"; + +ngtcp2_ssize ngtcp2_crypto_generate_retry_token( + uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) { + uint8_t plaintext[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN]; + uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN]; + uint8_t key[32]; + uint8_t iv[32]; + size_t keylen; + size_t ivlen; + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + ngtcp2_crypto_aead_ctx aead_ctx; + size_t plaintextlen; + uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) + + NGTCP2_MAX_CIDLEN]; + size_t aadlen; + uint8_t *p = plaintext; + ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts); int rv; - assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf)); - assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf)); + memset(plaintext, 0, sizeof(plaintext)); + + *p++ = (uint8_t)odcid->datalen; + memcpy(p, odcid->data, odcid->datalen); + p += NGTCP2_MAX_CIDLEN; + memcpy(p, &ts_be, sizeof(ts_be)); + p += sizeof(ts_be); + + plaintextlen = (size_t)(p - plaintext); + + if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) { + return -1; + } + + ngtcp2_crypto_aead_aes_128_gcm(&aead); + ngtcp2_crypto_md_sha256(&md); + + keylen = ngtcp2_crypto_aead_keylen(&aead); + ivlen = ngtcp2_crypto_aead_noncelen(&aead); + + assert(sizeof(key) >= keylen); + assert(sizeof(iv) >= ivlen); + + if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, + rand_data, sizeof(rand_data), + retry_token_info_prefix, + sizeof(retry_token_info_prefix) - 1) != 0) { + return -1; + } + + aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr, + remote_addrlen, retry_scid); + + p = token; + *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY; + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { + return -1; + } + + rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv, + ivlen, aad, aadlen); + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); - rv = ngtcp2_crypto_hkdf_extract(buf, md, secret, secretlen, cid->data, - cid->datalen); if (rv != 0) { return -1; } - memcpy(token, buf, NGTCP2_STATELESS_RESET_TOKENLEN); + p += plaintextlen + aead.max_overhead; + memcpy(p, rand_data, sizeof(rand_data)); + p += sizeof(rand_data); + + return p - token; +} + +int ngtcp2_crypto_verify_retry_token( + ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, + const uint8_t *secret, size_t secretlen, uint32_t version, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) { + uint8_t + plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)]; + uint8_t key[32]; + uint8_t iv[32]; + size_t keylen; + size_t ivlen; + ngtcp2_crypto_aead_ctx aead_ctx; + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) + + NGTCP2_MAX_CIDLEN]; + size_t aadlen; + const uint8_t *rand_data; + const uint8_t *ciphertext; + size_t ciphertextlen; + size_t cil; + int rv; + ngtcp2_tstamp gen_ts; + + if (tokenlen != NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN || + token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY) { + return -1; + } + + rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + ciphertext = token + 1; + ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + + ngtcp2_crypto_aead_aes_128_gcm(&aead); + ngtcp2_crypto_md_sha256(&md); + + keylen = ngtcp2_crypto_aead_keylen(&aead); + ivlen = ngtcp2_crypto_aead_noncelen(&aead); + + if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, + rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, + retry_token_info_prefix, + sizeof(retry_token_info_prefix) - 1) != 0) { + return -1; + } + + aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr, + remote_addrlen, dcid); + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { + return -1; + } + + rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext, + ciphertextlen, iv, ivlen, aad, aadlen); + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + if (rv != 0) { + return -1; + } + + cil = plaintext[0]; + + assert(cil == 0 || (cil >= NGTCP2_MIN_CIDLEN && cil <= NGTCP2_MAX_CIDLEN)); + + memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN, + sizeof(gen_ts)); + + gen_ts = ngtcp2_ntohl64(gen_ts); + if (gen_ts + timeout <= ts) { + return -1; + } + + ngtcp2_cid_init(odcid, plaintext + /* cid len = */ 1, cil); return 0; } -ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, - uint32_t version, - const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, - uint64_t error_code) { +static size_t crypto_generate_regular_token_aad(uint8_t *dest, + const ngtcp2_sockaddr *sa) { + const uint8_t *addr; + size_t addrlen; + + switch (sa->sa_family) { + case AF_INET: + addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr; + addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr); + break; + case AF_INET6: + addr = + (const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr; + addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr); + break; + default: + assert(0); + abort(); + } + + memcpy(dest, addr, addrlen); + + return addrlen; +} + +static const uint8_t regular_token_info_prefix[] = "regular_token"; + +ngtcp2_ssize ngtcp2_crypto_generate_regular_token( + uint8_t *token, const uint8_t *secret, size_t secretlen, + const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, + ngtcp2_tstamp ts) { + uint8_t plaintext[sizeof(ngtcp2_tstamp)]; + uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN]; + uint8_t key[32]; + uint8_t iv[32]; + size_t keylen; + size_t ivlen; + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + ngtcp2_crypto_aead_ctx aead_ctx; + size_t plaintextlen; + uint8_t aad[sizeof(ngtcp2_sockaddr_in6)]; + size_t aadlen; + uint8_t *p = plaintext; + ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts); + int rv; + (void)remote_addrlen; + + memcpy(p, &ts_be, sizeof(ts_be)); + p += sizeof(ts_be); + + plaintextlen = (size_t)(p - plaintext); + + if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) { + return -1; + } + + ngtcp2_crypto_aead_aes_128_gcm(&aead); + ngtcp2_crypto_md_sha256(&md); + + keylen = ngtcp2_crypto_aead_keylen(&aead); + ivlen = ngtcp2_crypto_aead_noncelen(&aead); + + assert(sizeof(key) >= keylen); + assert(sizeof(iv) >= ivlen); + + if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, + rand_data, sizeof(rand_data), + regular_token_info_prefix, + sizeof(regular_token_info_prefix) - 1) != 0) { + return -1; + } + + aadlen = crypto_generate_regular_token_aad(aad, remote_addr); + + p = token; + *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR; + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { + return -1; + } + + rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv, + ivlen, aad, aadlen); + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + if (rv != 0) { + return -1; + } + + p += plaintextlen + aead.max_overhead; + memcpy(p, rand_data, sizeof(rand_data)); + p += sizeof(rand_data); + + return p - token; +} + +int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen, + const uint8_t *secret, size_t secretlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, + ngtcp2_duration timeout, + ngtcp2_tstamp ts) { + uint8_t plaintext[sizeof(ngtcp2_tstamp)]; + uint8_t key[32]; + uint8_t iv[32]; + size_t keylen; + size_t ivlen; + ngtcp2_crypto_aead_ctx aead_ctx; + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + uint8_t aad[sizeof(ngtcp2_sockaddr_in6)]; + size_t aadlen; + const uint8_t *rand_data; + const uint8_t *ciphertext; + size_t ciphertextlen; + int rv; + ngtcp2_tstamp gen_ts; + (void)remote_addrlen; + + if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN || + token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) { + return -1; + } + + rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + ciphertext = token + 1; + ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + + ngtcp2_crypto_aead_aes_128_gcm(&aead); + ngtcp2_crypto_md_sha256(&md); + + keylen = ngtcp2_crypto_aead_keylen(&aead); + ivlen = ngtcp2_crypto_aead_noncelen(&aead); + + if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, + rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, + regular_token_info_prefix, + sizeof(regular_token_info_prefix) - 1) != 0) { + return -1; + } + + aadlen = crypto_generate_regular_token_aad(aad, remote_addr); + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { + return -1; + } + + rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext, + ciphertextlen, iv, ivlen, aad, aadlen); + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + if (rv != 0) { + return -1; + } + + memcpy(&gen_ts, plaintext, sizeof(gen_ts)); + + gen_ts = ngtcp2_ntohl64(gen_ts); + if (gen_ts + timeout <= ts) { + return -1; + } + + return 0; +} + +ngtcp2_ssize ngtcp2_crypto_write_connection_close( + uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, + size_t reasonlen) { uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; @@ -689,7 +1241,7 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, } if (ngtcp2_crypto_derive_packet_protection_key( - tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { return -1; } @@ -706,8 +1258,9 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, } spktlen = ngtcp2_pkt_write_connection_close( - dest, destlen, version, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb, - &ctx.aead, &aead_ctx, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx); + dest, destlen, version, dcid, scid, error_code, reason, reasonlen, + ngtcp2_crypto_encrypt_cb, &ctx.aead, &aead_ctx, tx_iv, + ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx); if (spktlen < 0) { spktlen = -1; } @@ -732,10 +1285,16 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, ngtcp2_crypto_aead_retry(&aead); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -757,22 +1316,14 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, return spktlen; } -/* - * crypto_setup_initial_crypto establishes the initial secrets and - * encryption keys, and prepares local QUIC transport parameters. - */ -static int crypto_setup_initial_crypto(ngtcp2_conn *conn, - const ngtcp2_cid *dcid) { - return ngtcp2_crypto_derive_and_install_initial_key( - conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid); -} - int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) { const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn); void *tls = ngtcp2_conn_get_tls_native_handle(conn); (void)user_data; - if (crypto_setup_initial_crypto(conn, dcid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -792,9 +1343,9 @@ int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, void *user_data) { (void)user_data; - if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - &hd->scid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -806,7 +1357,23 @@ int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, void *user_data) { (void)user_data; - if (crypto_setup_initial_crypto(conn, dcid) != 0) { + if (ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data) { + (void)user_data; + + if (ngtcp2_crypto_derive_and_install_vneg_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version, + client_dcid) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -829,3 +1396,23 @@ void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( ngtcp2_crypto_cipher_ctx_free(cipher_ctx); } + +int ngtcp2_crypto_recv_crypto_data_cb(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen, void *user_data) { + int rv; + (void)offset; + (void)user_data; + + if (ngtcp2_crypto_read_write_crypto_data(conn, crypto_level, data, datalen) != + 0) { + rv = ngtcp2_conn_get_tls_error(conn); + if (rv) { + return rv; + } + return NGTCP2_ERR_CRYPTO; + } + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.h b/deps/ngtcp2/ngtcp2/crypto/shared.h index b7fe2f15da93a7..02b948901ae40e 100644 --- a/deps/ngtcp2/ngtcp2/crypto/shared.h +++ b/deps/ngtcp2/ngtcp2/crypto/shared.h @@ -51,6 +51,16 @@ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \ "\x7f\x0a" +/** + * @macro + * + * :macro:`NGTCP2_INITIAL_SALT_V2_DRAFT` is a salt value which is used to + * derive initial secret. It is used for QUIC v2 draft. + */ +#define NGTCP2_INITIAL_SALT_V2_DRAFT \ + "\xa7\x07\xc2\x03\xa5\x9b\x47\x18\x4a\x1d\x62\xca\x57\x04\x06\xea\x7a\xe3" \ + "\xe5\xd3" + /* Maximum key usage (encryption) limits */ #define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23) #define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62) @@ -62,6 +72,40 @@ #define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36) #define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL) +/** + * @function + * + * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet + * encryption and decryption. + */ +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_aead_init` initializes |aead| with the provided + * |aead_native_handle| which is an underlying AEAD object. + * + * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be + * a pointer to EVP_CIPHER. + * + * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be + * gnutls_cipher_algorithm_t casted to ``void *``. + * + * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must + * be a pointer to EVP_AEAD. + */ +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher + * AEAD_AES_128_GCM for Retry packet integrity protection. + */ +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); + /** * @function * @@ -84,6 +128,34 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret, const ngtcp2_cid *client_dcid, ngtcp2_crypto_side side); +/** + * @function + * + * `ngtcp2_crypto_derive_packet_protection_key` derives packet + * protection key. This function writes packet protection key into + * the buffer pointed by |key|. The length of derived key is + * `ngtcp2_crypto_aead_keylen(aead) ` + * bytes. |key| must have enough capacity to store the key. This + * function writes packet protection IV into |iv|. The length of + * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead) + * ` bytes. |iv| must have + * enough capacity to store the IV. + * + * If |hp| is not NULL, this function also derives packet header + * protection key and writes the key into the buffer pointed by |hp|. + * The length of derived key is `ngtcp2_crypto_aead_keylen(aead) + * ` bytes. |hp|, if not NULL, must have + * enough capacity to store the key. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv, + uint8_t *hp, uint32_t version, + const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen); + /** * @function * @@ -183,7 +255,58 @@ int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls); int ngtcp2_crypto_derive_and_install_initial_key( ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, - uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, + uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version, + const ngtcp2_cid *client_dcid); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial + * keying materials and installs keys to |conn|. This function is + * dedicated to install keys for |version| which is negotiated, or + * being negotiated. + * + * If |rx_secret| is not NULL, the secret for decryption is written to + * the buffer pointed by |rx_secret|. The length of secret is 32 + * bytes, and |rx_secret| must point to the buffer which has enough + * capacity. + * + * If |tx_secret| is not NULL, the secret for encryption is written to + * the buffer pointed by |tx_secret|. The length of secret is 32 + * bytes, and |tx_secret| must point to the buffer which has enough + * capacity. + * + * If |initial_secret| is not NULL, the initial secret is written to + * the buffer pointed by |initial_secret|. The length of secret is 32 + * bytes, and |initial_secret| must point to the buffer which has + * enough capacity. + * + * |client_dcid| is the destination connection ID in first Initial + * packet of client. + * + * If |rx_key| is not NULL, the derived packet protection key for + * decryption is written to the buffer pointed by |rx_key|. If + * |rx_iv| is not NULL, the derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp| + * is not NULL, the derived header protection key for decryption is + * written to the buffer pointed by |rx_hp|. + * + * If |tx_key| is not NULL, the derived packet protection key for + * encryption is written to the buffer pointed by |tx_key|. If + * |tx_iv| is not NULL, the derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp| + * is not NULL, the derived header protection key for encryption is + * written to the buffer pointed by |tx_hp|. + * + * The length of packet protection key and header protection key is 16 + * bytes long. The length of packet protection IV is 12 bytes long. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_and_install_vneg_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, + uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version, const ngtcp2_cid *client_dcid); /** @@ -208,4 +331,20 @@ int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, */ void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx); +/* + * `ngtcp2_crypto_md_sha256` initializes |md| with SHA256 message + * digest algorithm and returns |md|. + */ +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md); + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead); + +/* + * `ngtcp2_crypto_random` writes cryptographically-secure random + * |datalen| bytes into the buffer pointed by |data|. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_random(uint8_t *data, size_t datalen); + #endif /* NGTCP2_SHARED_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c b/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c new file mode 100644 index 00000000000000..9a58b9be2b76e9 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/wolfssl/wolfssl.c @@ -0,0 +1,524 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include + +#include +#include + +#include "shared.h" + +#define PRINTF_DEBUG 0 +#if PRINTF_DEBUG +# define DEBUG_MSG(...) fprintf(stderr, __VA_ARGS__) +#else +# define DEBUG_MSG(...) (void)0 +#endif + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)wolfSSL_EVP_sha256(); + return md; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_EVP_aes_128_gcm()); + ctx->md.native_handle = (void *)wolfSSL_EVP_sha256(); + ctx->hp.native_handle = (void *)wolfSSL_EVP_aes_128_ctr(); + ctx->max_encryption = 0; + ctx->max_decryption_failure = 0; + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle) { + aead->native_handle = aead_native_handle; + aead->max_overhead = wolfSSL_quic_get_aead_tag_len( + (const WOLFSSL_EVP_CIPHER *)(aead_native_handle)); + return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm()); +} + +static uint64_t crypto_wolfssl_get_aead_max_encryption(WOLFSSL *ssl) { + const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl); + + if (wolfSSL_quic_aead_is_gcm(aead)) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; + } + if (wolfSSL_quic_aead_is_chacha20(aead)) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; + } + if (wolfSSL_quic_aead_is_ccm(aead)) { + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM; + } + return 0; +} + +static uint64_t crypto_wolfssl_get_aead_max_decryption_failure(WOLFSSL *ssl) { + const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl); + + if (wolfSSL_quic_aead_is_gcm(aead)) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; + } + if (wolfSSL_quic_aead_is_chacha20(aead)) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; + } + if (wolfSSL_quic_aead_is_ccm(aead)) { + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM; + } + return 0; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + WOLFSSL *ssl = tls_native_handle; + + ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_quic_get_aead(ssl)); + ctx->md.native_handle = (void *)wolfSSL_quic_get_md(ssl); + ctx->hp.native_handle = (void *)wolfSSL_quic_get_hp(ssl); + ctx->max_encryption = crypto_wolfssl_get_aead_max_encryption(ssl); + ctx->max_decryption_failure = + crypto_wolfssl_get_aead_max_decryption_failure(ssl); + return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const WOLFSSL_EVP_MD *md) { + return (size_t)wolfSSL_EVP_MD_size(md); +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const WOLFSSL_EVP_CIPHER *aead) { + return (size_t)wolfSSL_EVP_Cipher_key_length(aead); +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const WOLFSSL_EVP_CIPHER *aead) { + return (size_t)wolfSSL_EVP_CIPHER_iv_length(aead); +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle; + WOLFSSL_EVP_CIPHER_CTX *actx; + static const uint8_t iv[AES_BLOCK_SIZE] = {0}; + + (void)noncelen; + actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 1); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle; + WOLFSSL_EVP_CIPHER_CTX *actx; + static const uint8_t iv[AES_BLOCK_SIZE] = {0}; + + (void)noncelen; + actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 0); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + return 0; +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { + if (aead_ctx->native_handle) { + wolfSSL_EVP_CIPHER_CTX_free(aead_ctx->native_handle); + } +} + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key) { + WOLFSSL_EVP_CIPHER_CTX *actx; + + actx = + wolfSSL_quic_crypt_new(cipher->native_handle, key, NULL, /* encrypt */ 1); + if (actx == NULL) { + return -1; + } + + cipher_ctx->native_handle = actx; + return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { + if (cipher_ctx->native_handle) { + wolfSSL_EVP_CIPHER_CTX_free(cipher_ctx->native_handle); + } +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + if (wolfSSL_quic_hkdf_extract(dest, md->native_handle, secret, secretlen, + salt, saltlen) != WOLFSSL_SUCCESS) { + return -1; + } + return 0; +} + +int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *info, + size_t infolen) { + if (wolfSSL_quic_hkdf_expand(dest, destlen, md->native_handle, secret, + secretlen, info, infolen) != WOLFSSL_SUCCESS) { + return -1; + } + return 0; +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { + if (wolfSSL_quic_hkdf(dest, destlen, md->native_handle, secret, secretlen, + salt, saltlen, info, infolen) != WOLFSSL_SUCCESS) { + return -1; + } + return 0; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)aead; + (void)noncelen; + if (wolfSSL_quic_aead_encrypt(dest, aead_ctx->native_handle, plaintext, + plaintextlen, nonce, aad, + aadlen) != WOLFSSL_SUCCESS) { + DEBUG_MSG("WOLFSSL: encrypt FAILED\n"); + return -1; + } + return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)aead; + (void)noncelen; + if (wolfSSL_quic_aead_decrypt(dest, aead_ctx->native_handle, ciphertext, + ciphertextlen, nonce, aad, + aadlen) != WOLFSSL_SUCCESS) { + + DEBUG_MSG("WOLFSSL: decrypt FAILED\n"); + return -1; + } + return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + WOLFSSL_EVP_CIPHER_CTX *actx = hp_ctx->native_handle; + int len; + + (void)hp; + + if (wolfSSL_EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) != + WOLFSSL_SUCCESS || + wolfSSL_EVP_CipherUpdate(actx, dest, &len, PLAINTEXT, + sizeof(PLAINTEXT) - 1) != WOLFSSL_SUCCESS || + wolfSSL_EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len) != + WOLFSSL_SUCCESS) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen) { + WOLFSSL *ssl = ngtcp2_conn_get_tls_native_handle(conn); + WOLFSSL_ENCRYPTION_LEVEL level = + ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(crypto_level); + int rv; + int err; + + DEBUG_MSG("WOLFSSL: read/write crypto data, level=%d len=%lu\n", level, + datalen); + if (datalen > 0) { + rv = wolfSSL_provide_quic_data(ssl, level, data, datalen); + if (rv != WOLFSSL_SUCCESS) { + DEBUG_MSG("WOLFSSL: read/write crypto data FAILED, rv=%d\n", rv); + return -1; + } + } + + if (!ngtcp2_conn_get_handshake_completed(conn)) { + rv = wolfSSL_quic_do_handshake(ssl); + DEBUG_MSG("WOLFSSL: do_handshake, rv=%d\n", rv); + if (rv <= 0) { + err = wolfSSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + return -1; + default: + return -1; + } + } + + DEBUG_MSG("WOLFSSL: handshake done\n"); + ngtcp2_conn_handshake_completed(conn); + } + + rv = wolfSSL_process_quic_post_handshake(ssl); + DEBUG_MSG("WOLFSSL: process post handshake, rv=%d\n", rv); + if (rv != 1) { + err = wolfSSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + return -1; + default: + return -1; + } + } + + return 0; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { + WOLFSSL *ssl = tls; + const uint8_t *tp; + size_t tplen; + int rv; + + wolfSSL_get_peer_quic_transport_params(ssl, &tp, &tplen); + DEBUG_MSG("WOLFSSL: get peer transport params, len=%lu\n", tplen); + + rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen); + if (rv != 0) { + DEBUG_MSG("WOLFSSL: decode peer transport params failed, rv=%d\n", rv); + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len) { + WOLFSSL *ssl = tls; + DEBUG_MSG("WOLFSSL: set local peer transport params, len=%lu\n", len); + if (wolfSSL_set_quic_transport_params(ssl, buf, len) != WOLFSSL_SUCCESS) { + return -1; + } + + return 0; +} + +ngtcp2_crypto_level ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level( + WOLFSSL_ENCRYPTION_LEVEL wolfssl_level) { + switch (wolfssl_level) { + case wolfssl_encryption_initial: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case wolfssl_encryption_early_data: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case wolfssl_encryption_handshake: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case wolfssl_encryption_application: + return NGTCP2_CRYPTO_LEVEL_APPLICATION; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +WOLFSSL_ENCRYPTION_LEVEL +ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level) { + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + return wolfssl_encryption_initial; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + return wolfssl_encryption_handshake; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + return wolfssl_encryption_application; + case NGTCP2_CRYPTO_LEVEL_EARLY: + return wolfssl_encryption_early_data; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + DEBUG_MSG("WOLFSSL: get path challenge data\n"); + if (wolfSSL_RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + DEBUG_MSG("WOLFSSL: get random\n"); + if (wolfSSL_RAND_bytes(data, (int)datalen) != 1) { + return -1; + } + return 0; +} + +static int set_encryption_secrets(WOLFSSL *ssl, + WOLFSSL_ENCRYPTION_LEVEL wolfssl_level, + const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level); + + DEBUG_MSG("WOLFSSL: set encryption secrets, level=%d, rxlen=%lu, txlen=%lu\n", + wolfssl_level, rx_secret ? secretlen : 0, + tx_secret ? secretlen : 0); + if (rx_secret && + ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + rx_secret, secretlen) != 0) { + return 0; + } + + if (tx_secret && + ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + tx_secret, secretlen) != 0) { + return 0; + } + + return 1; +} + +static int add_handshake_data(WOLFSSL *ssl, + WOLFSSL_ENCRYPTION_LEVEL wolfssl_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + ngtcp2_crypto_level level = + ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level); + int rv; + + DEBUG_MSG("WOLFSSL: add handshake data, level=%d len=%lu\n", wolfssl_level, + datalen); + rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + return 1; +} + +static int flush_flight(WOLFSSL *ssl) { + (void)ssl; + return 1; +} + +static int send_alert(WOLFSSL *ssl, enum wolfssl_encryption_level_t level, + uint8_t alert) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); + (void)level; + + DEBUG_MSG("WOLFSSL: send alert, level=%d alert=%d\n", level, alert); + ngtcp2_conn_set_tls_alert(conn, alert); + + return 1; +} + +static WOLFSSL_QUIC_METHOD quic_method = { + set_encryption_secrets, + add_handshake_data, + flush_flight, + send_alert, +}; + +static void crypto_wolfssl_configure_context(WOLFSSL_CTX *ssl_ctx) { + wolfSSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + wolfSSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + wolfSSL_CTX_set_quic_method(ssl_ctx, &quic_method); +} + +int ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx) { + crypto_wolfssl_configure_context(ssl_ctx); + return 0; +} + +int ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx) { + crypto_wolfssl_configure_context(ssl_ctx); + wolfSSL_CTX_UseSessionTicket(ssl_ctx); + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h index 8a37bebda6b095..ed71cb3ea0cb37 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -32,8 +32,9 @@ # define WIN32 #endif -#ifdef __cplusplus -extern "C" { +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4324) #endif #include @@ -49,10 +50,29 @@ extern "C" { #include #include -#ifdef WIN32 -# include +#ifndef NGTCP2_USE_GENERIC_SOCKADDR +# ifdef WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# else +# include +# include +# endif +#endif + +#ifdef AF_INET +# define NGTCP2_AF_INET AF_INET #else -# include +# define NGTCP2_AF_INET 2 +#endif + +#ifdef AF_INET6 +# define NGTCP2_AF_INET6 AF_INET6 +#else +# define NGTCP2_AF_INET6 23 +# define NGTCP2_USE_GENERIC_IPV6_SOCKADDR #endif #include @@ -73,6 +93,16 @@ extern "C" { # endif /* !BUILDING_NGTCP2 */ #endif /* !defined(WIN32) */ +#ifdef _MSC_VER +# define NGTCP2_ALIGN(N) __declspec(align(N)) +#else /* !_MSC_VER */ +# define NGTCP2_ALIGN(N) __attribute__((aligned(N))) +#endif /* !_MSC_VER */ + +#ifdef __cplusplus +extern "C" { +#endif + /** * @typedef * @@ -83,58 +113,69 @@ typedef ptrdiff_t ngtcp2_ssize; /** * @functypedef * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. + * :type:`ngtcp2_malloc` is a custom memory allocator to replace + * :manpage:`malloc(3)`. The |user_data| is + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data); +typedef void *(*ngtcp2_malloc)(size_t size, void *user_data); /** * @functypedef * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`ngtcp2_mem` structure. + * :type:`ngtcp2_free` is a custom memory allocator to replace + * :manpage:`free(3)`. The |user_data| is + * :member:`ngtcp2_mem.user_data`. */ -typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data); +typedef void (*ngtcp2_free)(void *ptr, void *user_data); /** * @functypedef * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. + * :type:`ngtcp2_calloc` is a custom memory allocator to replace + * :manpage:`calloc(3)`. The |user_data| is the + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); +typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *user_data); /** * @functypedef * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`ngtcp2_mem` structure. + * :type:`ngtcp2_realloc` is a custom memory allocator to replace + * :manpage:`realloc(3)`. The |user_data| is the + * :member:`ngtcp2_mem.user_data`. */ -typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); +typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *user_data); /** * @struct * - * Custom memory allocator functions and user defined pointer. The - * |mem_user_data| member is passed to each allocator function. This - * can be used, for example, to achieve per-session memory pool. + * :type:`ngtcp2_mem` is a custom memory allocator. The + * :member:`user_data` field is passed to each allocator function. + * This can be used, for example, to achieve per-connection memory + * pool. * * In the following example code, ``my_malloc``, ``my_free``, * ``my_calloc`` and ``my_realloc`` are the replacement of the - * standard allocators ``malloc``, ``free``, ``calloc`` and - * ``realloc`` respectively:: + * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`, + * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively:: * - * void *my_malloc_cb(size_t size, void *mem_user_data) { + * void *my_malloc_cb(size_t size, void *user_data) { + * (void)user_data; * return my_malloc(size); * } * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * void my_free_cb(void *ptr, void *user_data) { + * (void)user_data; + * my_free(ptr); + * } * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) { + * (void)user_data; * return my_calloc(nmemb, size); * } * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * void *my_realloc_cb(void *ptr, size_t size, void *user_data) { + * (void)user_data; * return my_realloc(ptr, size); * } * @@ -147,24 +188,28 @@ typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); */ typedef struct ngtcp2_mem { /** - * An arbitrary user supplied data. This is passed to each - * allocator function. + * :member:`user_data` is an arbitrary user supplied data. This + * is passed to each allocator function. */ - void *mem_user_data; + void *user_data; /** - * Custom allocator function to replace malloc(). + * :member:`malloc` is a custom allocator function to replace + * :manpage:`malloc(3)`. */ ngtcp2_malloc malloc; /** - * Custom allocator function to replace free(). + * :member:`free` is a custom allocator function to replace + * :manpage:`free(3)`. */ ngtcp2_free free; /** - * Custom allocator function to replace calloc(). + * :member:`calloc` is a custom allocator function to replace + * :manpage:`calloc(3)`. */ ngtcp2_calloc calloc; /** - * Custom allocator function to replace realloc(). + * :member:`realloc` is a custom allocator function to replace + * :manpage:`realloc(3)`. */ ngtcp2_realloc realloc; } ngtcp2_mem; @@ -180,7 +225,7 @@ typedef struct ngtcp2_mem { * * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 second. */ -#define NGTCP2_SECONDS ((uint64_t)1000000000ULL) +#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL) /** * @macro @@ -188,7 +233,7 @@ typedef struct ngtcp2_mem { * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds * to 1 millisecond. */ -#define NGTCP2_MILLISECONDS ((uint64_t)1000000ULL) +#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL) /** * @macro @@ -196,7 +241,7 @@ typedef struct ngtcp2_mem { * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds * to 1 microsecond. */ -#define NGTCP2_MICROSECONDS ((uint64_t)1000ULL) +#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL) /** * @macro @@ -204,7 +249,7 @@ typedef struct ngtcp2_mem { * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to * 1 nanosecond. */ -#define NGTCP2_NANOSECONDS ((uint64_t)1ULL) +#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL) /** * @macrosection @@ -217,7 +262,17 @@ typedef struct ngtcp2_mem { * * :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1. */ -#define NGTCP2_PROTO_VER_V1 0x00000001u +#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001u) + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_V2_DRAFT` is the provisional version + * number for QUIC version 2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_PROTO_VER_V2_DRAFT ((uint32_t)0x709a50c4u) /** * @macro @@ -251,43 +306,35 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_PROTO_VER_MIN NGTCP2_PROTO_VER_DRAFT_MIN -/** - * @macrosection - * - * IP packet related macros - */ - /** * @macro * - * :macro:`NGTCP2_MAX_PKTLEN_IPV4` is the maximum datagram size of - * IPv4 packet without PMTUD. + * :macro:`NGTCP2_RESERVED_VERSION_MASK` is the bit mask of reserved + * version. */ -#define NGTCP2_MAX_PKTLEN_IPV4 1252 +#define NGTCP2_RESERVED_VERSION_MASK 0x0a0a0a0au + /** - * @macro + * @macrosection * - * :macro:`NGTCP2_MAX_PKTLEN_IPV6` is the maximum datagram size of - * IPv6 packet without PMTUD. + * UDP datagram related macros */ -#define NGTCP2_MAX_PKTLEN_IPV6 1232 /** * @macro * - * :macro:`NGTCP2_MIN_INITIAL_PKTLEN` is the minimum datagram size for - * a packet sent by client which contains its first Initial packet. + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` is the default maximum UDP + * datagram payload size that this endpoint transmits. */ -#define NGTCP2_MIN_INITIAL_PKTLEN 1200 +#define NGTCP2_MAX_UDP_PAYLOAD_SIZE 1200 /** * @macro * - * :macro:`NGTCP2_DEFAULT_MAX_PKTLEN` is the default maximum datagram - * size that this endpoint transmits. It is used by congestion - * controller to compute congestion window. + * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` is the maximum UDP + * datagram payload size that Path MTU Discovery can discover. */ -#define NGTCP2_DEFAULT_MAX_PKTLEN 1200 +#define NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE 1452 /** * @macrosection @@ -315,10 +362,18 @@ typedef struct ngtcp2_mem { * @macro * * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` is the minimum length - * of random bytes (Unpredictable Bits) in Stateless Retry packet + * of random bytes (Unpredictable Bits) in Stateless Reset packet */ #define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5 +/** + * @macro + * + * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` is the length of + * PATH_CHALLENGE data. + */ +#define NGTCP2_PATH_CHALLENGE_DATALEN 8 + /** * @macro * @@ -354,6 +409,28 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb" +/** + * @macro + * + * :macro:`NGTCP2_RETRY_KEY_V2_DRAFT` is an encryption key to create + * integrity tag of Retry packet. It is used for QUIC v2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_RETRY_KEY_V2_DRAFT \ + "\xba\x85\x8d\xc7\xb4\x3d\xe5\xdb\xf8\x76\x17\xff\x4a\xb2\x53\xdb" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_NONCE_V2_DRAFT` is nonce used when generating + * integrity tag of Retry packet. It is used for QUIC v2 draft. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_RETRY_NONCE_V2_DRAFT \ + "\x14\x1b\x99\xc2\x39\xb0\x3e\x78\x5d\x6a\x2e\x9f" + /** * @macro * @@ -399,6 +476,14 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_MIN_INITIAL_DCIDLEN 8 +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT` is the default handshake + * timeout. + */ +#define NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) + /** * @macrosection * @@ -440,15 +525,18 @@ typedef struct ngtcp2_mem { */ #define NGTCP2_ECN_MASK 0x3 +#define NGTCP2_PKT_INFO_VERSION_V1 1 +#define NGTCP2_PKT_INFO_VERSION NGTCP2_PKT_INFO_VERSION_V1 + /** * @struct * * :type:`ngtcp2_pkt_info` is a packet metadata. */ -typedef struct ngtcp2_pkt_info { +typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info { /** - * :member:`ecn ` is ECN marking and when - * passing `ngtcp2_conn_read_pkt()`, and it should be either + * :member:`ecn` is ECN marking and when passing + * `ngtcp2_conn_read_pkt()`, and it should be either * :macro:`NGTCP2_ECN_NOT_ECT`, :macro:`NGTCP2_ECN_ECT_1`, * :macro:`NGTCP2_ECN_ECT_0`, or :macro:`NGTCP2_ECN_CE`. */ @@ -580,9 +668,9 @@ typedef struct ngtcp2_pkt_info { /** * @macro * - * :macro:`NGTCP2_ERR_TLS_DECRYPT` indicates TLS decryption failure. + * :macro:`NGTCP2_ERR_DECRYPT` indicates a decryption failure. */ -#define NGTCP2_ERR_TLS_DECRYPT -220 +#define NGTCP2_ERR_DECRYPT -220 /** * @macro * @@ -638,13 +726,6 @@ typedef struct ngtcp2_pkt_info { * :macro:`NGTCP2_ERR_DISCARD_PKT` indicates a packet was discarded. */ #define NGTCP2_ERR_DISCARD_PKT -235 -/** - * @macro - * - * :macro:`NGTCP2_ERR_PATH_VALIDATION_FAILED` indicates that a path - * validation failed. - */ -#define NGTCP2_ERR_PATH_VALIDATION_FAILED -236 /** * @macro * @@ -699,9 +780,38 @@ typedef struct ngtcp2_pkt_info { * @macro * * :macro:`NGTCP2_ERR_NO_VIABLE_PATH` indicates that path validation - * could not probe that a path is not capable of at least 1200 MTU. + * could not probe that a path is capable of sending UDP datagram + * payload of size at least 1200 bytes. */ #define NGTCP2_ERR_NO_VIABLE_PATH -244 +/** + * @macro + * + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` indicates that server + * should send Version Negotiation packet. + */ +#define NGTCP2_ERR_VERSION_NEGOTIATION -245 +/** + * @macro + * + * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` indicates that QUIC + * connection is not established before the specified deadline. + */ +#define NGTCP2_ERR_HANDSHAKE_TIMEOUT -246 +/** + * @macro + * + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` indicates the + * version negotiation failed. + */ +#define NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE -247 +/** + * @macro + * + * :macro:`NGTCP2_ERR_IDLE_CLOSE` indicates the connection should be + * closed silently because of idle timeout. + */ +#define NGTCP2_ERR_IDLE_CLOSE -248 /** * @macro * @@ -735,54 +845,68 @@ typedef struct ngtcp2_pkt_info { * * :macro:`NGTCP2_PKT_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_PKT_FLAG_NONE 0 +#define NGTCP2_PKT_FLAG_NONE 0x00u /** * @macro * - * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long packet + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long header packet * header. */ -#define NGTCP2_PKT_FLAG_LONG_FORM 0x01 +#define NGTCP2_PKT_FLAG_LONG_FORM 0x01u + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR` indicates that Fixed Bit + * (aka QUIC bit) is not set. + */ +#define NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR 0x02u /** * @macro * * :macro:`NGTCP2_PKT_FLAG_KEY_PHASE` indicates Key Phase bit set. */ -#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04 +#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04u /** * @enum * - * :type:`ngtcp2_pkt_type` defines QUIC packet types. + * :type:`ngtcp2_pkt_type` defines QUIC version-independent QUIC + * packet types. */ typedef enum ngtcp2_pkt_type { /** * :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` is defined by libngtcp2 * for convenience. */ - NGTCP2_PKT_VERSION_NEGOTIATION = 0xf0, + NGTCP2_PKT_VERSION_NEGOTIATION = 0x80, + /** + * :enum:`NGTCP2_PKT_STATELESS_RESET` is defined by libngtcp2 for + * convenience. + */ + NGTCP2_PKT_STATELESS_RESET = 0x81, /** * :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet. */ - NGTCP2_PKT_INITIAL = 0x0, + NGTCP2_PKT_INITIAL = 0x10, /** * :enum:`NGTCP2_PKT_0RTT` indicates 0RTT packet. */ - NGTCP2_PKT_0RTT = 0x1, + NGTCP2_PKT_0RTT = 0x11, /** * :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet. */ - NGTCP2_PKT_HANDSHAKE = 0x2, + NGTCP2_PKT_HANDSHAKE = 0x12, /** * :enum:`NGTCP2_PKT_RETRY` indicates Retry packet. */ - NGTCP2_PKT_RETRY = 0x3, + NGTCP2_PKT_RETRY = 0x13, /** - * :enum:`NGTCP2_PKT_SHORT` is defined by libngtcp2 for convenience. + * :enum:`NGTCP2_PKT_1RTT` is defined by libngtcp2 for convenience. */ - NGTCP2_PKT_SHORT = 0x70 + NGTCP2_PKT_1RTT = 0x40 } ngtcp2_pkt_type; /** @@ -934,6 +1058,16 @@ typedef enum ngtcp2_pkt_type { */ #define NGTCP2_CRYPTO_ERROR 0x100u +/** + * @macro + * + * :macro:`NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT` is QUIC transport + * error code ``VERSION_NEGOTIATION_ERROR``. + * + * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html + */ +#define NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT 0x53f8u + /** * @enum * @@ -950,7 +1084,12 @@ typedef enum ngtcp2_path_validation_result { * :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE` indicates * validation failure. */ - NGTCP2_PATH_VALIDATION_RESULT_FAILURE + NGTCP2_PATH_VALIDATION_RESULT_FAILURE, + /** + * :enum:`NGTCP2_PATH_VALIDATION_RESULT_ABORTED` indicates that path + * validation was aborted. + */ + NGTCP2_PATH_VALIDATION_RESULT_ABORTED } ngtcp2_path_validation_result; /** @@ -975,13 +1114,11 @@ typedef uint64_t ngtcp2_duration; */ typedef struct ngtcp2_cid { /** - * :member:`datalen ` is the length of - * Connection ID. + * :member:`datalen` is the length of Connection ID. */ size_t datalen; /** - * :member:`data ` is the buffer to store - * Connection ID. + * :member:`data` is the buffer to store Connection ID. */ uint8_t data[NGTCP2_MAX_CIDLEN]; } ngtcp2_cid; @@ -994,12 +1131,12 @@ typedef struct ngtcp2_cid { */ typedef struct ngtcp2_vec { /** - * :member:`base ` points to the data. + * :member:`base` points to the data. */ uint8_t *base; /** - * :member:`len ` is the number of bytes which the - * buffer pointed by base contains. + * :member:`len` is the number of bytes which the buffer pointed by + * base contains. */ size_t len; } ngtcp2_vec; @@ -1009,12 +1146,19 @@ 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 :macro:`NGTCP2_MIN_CIDLEN`, and at most - * :macro:`NGTCP2_MAX_CIDLEN`. + * must be at most :macro:`NGTCP2_MAX_CIDLEN`. */ NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen); +/** + * @function + * + * `ngtcp2_cid_eq` returns nonzero if |a| and |b| share the same + * Connection ID. + */ +NGTCP2_EXTERN int ngtcp2_cid_eq(const ngtcp2_cid *a, const ngtcp2_cid *b); + /** * @struct * @@ -1058,8 +1202,8 @@ typedef struct ngtcp2_pkt_hd { */ uint8_t type; /** - * :member:`flags` is zero or more of NGTCP2_PKT_FLAG_*. See - * :macro:`NGTCP2_PKT_FLAG_NONE`. + * :member:`flags` is zero or more of :macro:`NGTCP2_PKT_FLAG_* + * `. */ uint8_t flags; } ngtcp2_pkt_hd; @@ -1085,27 +1229,6 @@ typedef struct ngtcp2_pkt_stateless_reset { size_t randlen; } ngtcp2_pkt_stateless_reset; -typedef enum ngtcp2_transport_param_id { - NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000, - NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, - NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, - NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008, - NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009, - NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a, - NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b, - NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c, - NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d, - NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, - NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f, - NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010, - NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020 -} ngtcp2_transport_param_id; - /** * @enum * @@ -1125,20 +1248,6 @@ typedef enum ngtcp2_transport_params_type { NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS } ngtcp2_transport_params_type; -/** - * @enum - * - * ngtcp2_rand_usage describes the usage of the generated random data. - */ -typedef enum ngtcp2_rand_usage { - NGTCP2_RAND_USAGE_NONE, - /** - * :enum:`NGTCP2_RAND_USAGE_PATH_CHALLENGE` indicates that random - * value is used for PATH_CHALLENGE. - */ - NGTCP2_RAND_USAGE_PATH_CHALLENGE -} ngtcp2_rand_usage; - /** * @macrosection * @@ -1148,10 +1257,10 @@ typedef enum ngtcp2_rand_usage { /** * @macro * - * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE` is the default value - * of max_udp_payload_size transport parameter. + * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` is the default + * value of max_udp_payload_size transport parameter. */ -#define NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE 65527 +#define NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE 65527 /** * @macro @@ -1182,10 +1291,19 @@ typedef enum ngtcp2_rand_usage { /** * @macro * - * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS` is TLS extension - * type of quic_transport_parameters. + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1` is TLS + * extension type of quic_transport_parameters. + */ +#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1 0x39u + +/** + * @macro + * + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_DRAFT` is TLS + * extension type of quic_transport_parameters used during draft + * development. */ -#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS 0xffa5u +#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_DRAFT 0xffa5u /** * @struct @@ -1214,12 +1332,48 @@ typedef struct ngtcp2_preferred_addr { * :member:`ipv6_addr` contains IPv6 address in network byte order. */ uint8_t ipv6_addr[16]; + /** + * :member:`ipv4_present` indicates that :member:`ipv4_addr` and + * :member:`ipv4_port` contain IPv4 address and port respectively. + */ + uint8_t ipv4_present; + /** + * :member:`ipv6_present` indicates that :member:`ipv6_addr` and + * :member:`ipv6_port` contain IPv6 address and port respectively. + */ + uint8_t ipv6_present; /** * :member:`stateless_reset_token` contains stateless reset token. */ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; } ngtcp2_preferred_addr; +/** + * @struct + * + * :type:`ngtcp2_version_info` represents version_information + * structure. + */ +typedef struct ngtcp2_version_info { + /** + * :member:`chosen_version` is the version chosen by the sender. + */ + uint32_t chosen_version; + /** + * :member:`other_versions` points the wire image of other_versions + * field. The each version is therefore in network byte order. + */ + uint8_t *other_versions; + /** + * :member:`other_versionslen` is the number of bytes pointed by + * :member:`other_versions`, not the number of versions included. + */ + size_t other_versionslen; +} ngtcp2_version_info; + +#define NGTCP2_TRANSPORT_PARAMS_VERSION_V1 1 +#define NGTCP2_TRANSPORT_PARAMS_VERSION NGTCP2_TRANSPORT_PARAMS_VERSION_V1 + /** * @struct * @@ -1324,7 +1478,7 @@ typedef struct ngtcp2_transport_params { * :member:`max_datagram_frame_size` is the maximum size of DATAGRAM * frame that this endpoint willingly receives. Specifying 0 * disables DATAGRAM support. See - * https://tools.ietf.org/html/draft-ietf-quic-datagram-01 + * https://datatracker.ietf.org/doc/html/rfc9221 */ uint64_t max_datagram_frame_size; /** @@ -1351,15 +1505,28 @@ typedef struct ngtcp2_transport_params { * :member:`stateless_reset_token` contains stateless reset token. */ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; + /** + * :member:`grease_quic_bit` is nonzero if sender supports "Greasing + * the QUIC Bit" extension. See + * https://datatracker.ietf.org/doc/html/draft-ietf-quic-bit-grease. + * Note that the local endpoint always enables greasing QUIC bit + * regardless of this field value. + */ + uint8_t grease_quic_bit; + /** + * :member:`version_info` contains version_information field if + * :member:`version_info_present` is nonzero. Application should + * not specify this field. + */ + ngtcp2_version_info version_info; + /** + * :member:`version_info_present` is nonzero if + * :member:`version_info` is set. Application should not specify + * this field. + */ + uint8_t version_info_present; } ngtcp2_transport_params; -/** - * @struct - * - * :type:`ngtcp2_log` is ngtcp2 library internal logger. - */ -typedef struct ngtcp2_log ngtcp2_log; - /** * @enum * @@ -1388,6 +1555,9 @@ typedef enum ngtcp2_pktns_id { NGTCP2_PKTNS_ID_MAX } ngtcp2_pktns_id; +#define NGTCP2_CONN_STAT_VERSION_V1 1 +#define NGTCP2_CONN_STAT_VERSION NGTCP2_CONN_STAT_VERSION_V1 + /** * @struct * @@ -1435,13 +1605,11 @@ typedef struct ngtcp2_conn_stat { ngtcp2_tstamp loss_detection_timer; /** * :member:`last_tx_pkt_ts` corresponds to - * time_of_last_sent_ack_eliciting_packet in - * draft-ietf-quic-recovery-32. + * time_of_last_ack_eliciting_packet in :rfc:`9002`. */ ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX]; /** - * :member:`loss_time` corresponds to loss_time in - * draft-ietf-quic-recovery-32. + * :member:`loss_time` corresponds to loss_time in :rfc:`9002`. */ ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX]; /** @@ -1473,6 +1641,16 @@ typedef struct ngtcp2_conn_stat { * in byte per second. */ uint64_t delivery_rate_sec; + /** + * :member:`pacing_rate` is the current packet sending rate. If + * pacing is disabled, 0 is set. + */ + double pacing_rate; + /** + * :member:`send_quantum` is the maximum size of a data aggregate + * scheduled and transmitted together. + */ + size_t send_quantum; } ngtcp2_conn_stat; /** @@ -1490,195 +1668,16 @@ typedef enum ngtcp2_cc_algo { */ NGTCP2_CC_ALGO_CUBIC = 0x01, /** - * :enum:`NGTCP2_CC_ALGO_CUSTOM` represents custom congestion - * control algorithm. - */ - NGTCP2_CC_ALGO_CUSTOM = 0xff -} ngtcp2_cc_algo; - -/** - * @struct - * - * :type:`ngtcp2_cc_base` is the base structure of custom congestion - * control algorithm. It must be the first field of custom congestion - * controller. - */ -typedef struct ngtcp2_cc_base { - /** - * :member:`log` is ngtcp2 library internal logger. - */ - ngtcp2_log *log; -} ngtcp2_cc_base; - -/** - * @struct - * - * :type:`ngtcp2_cc_pkt` is a convenient structure to include - * acked/lost/sent packet. - */ -typedef struct ngtcp2_cc_pkt { - /** - * :member:`pkt_num` is the packet number - */ - int64_t pkt_num; - /** - * :member:`pktlen` is the length of packet. - */ - size_t pktlen; - /** - * :member:`pktns_id` is the ID of packet number space which this - * packet belongs to. - */ - ngtcp2_pktns_id pktns_id; - /** - * :member:`ts_sent` is the timestamp when packet is sent. - */ - ngtcp2_tstamp ts_sent; -} ngtcp2_cc_pkt; - -typedef struct ngtcp2_cc ngtcp2_cc; - -/** - * @functypedef - * - * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is - * called with an acknowledged packet. - */ -typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_congestion_event` is a callback function which is - * called when congestion event happens (e.g., when packet is lost). - */ -typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function - * which is called when persistent congestion is established. - */ -typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is - * called when an acknowledgement is received. - */ -typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is - * called when an ack-eliciting packet is sent. - */ -typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_pkt *pkt); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is - * called when new RTT sample is obtained. - */ -typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -/** - * @functypedef - * - * :type:`ngtcp2_cc_reset` is a callback function which is called when - * congestion state must be reset. - */ -typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc); - -/** - * @enum - * - * :type:`ngtcp2_cc_event_type` defines congestion control events. - */ -typedef enum ngtcp2_cc_event_type { - /** - * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet - * is sent and no other ack-eliciting packet is present. - */ - NGTCP2_CC_EVENT_TYPE_TX_START -} ngtcp2_cc_event_type; - -/** - * @functypedef - * - * :type:`ngtcp2_cc_event` is a callback function which is called when - * a specific event happens. - */ -typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_cc_event_type event, ngtcp2_tstamp ts); - -/** - * @struct - * - * :type:`ngtcp2_cc` is congestion control algorithm interface to - * allow custom implementation. - */ -typedef struct ngtcp2_cc { - /** - * :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which - * usually contains a state. - */ - ngtcp2_cc_base *ccb; - /** - * :member:`on_pkt_acked` is a callback function which is called - * when a packet is acknowledged. - */ - ngtcp2_cc_on_pkt_acked on_pkt_acked; - /** - * :member:`congestion_event` is a callback function which is called - * when congestion event happens (.e.g, packet is lost). - */ - ngtcp2_cc_congestion_event congestion_event; - /** - * :member:`on_persistent_congestion` is a callback function which - * is called when persistent congestion is established. - */ - ngtcp2_cc_on_persistent_congestion on_persistent_congestion; - /** - * :member:`on_ack_recv` is a callback function which is called when - * an acknowledgement is received. - */ - ngtcp2_cc_on_ack_recv on_ack_recv; - /** - * :member:`on_pkt_sent` is a callback function which is called when - * ack-eliciting packet is sent. + * :enum:`NGTCP2_CC_ALGO_BBR` represents BBR. If BBR is chosen, + * packet pacing is enabled. */ - ngtcp2_cc_on_pkt_sent on_pkt_sent; + NGTCP2_CC_ALGO_BBR = 0x02, /** - * :member:`new_rtt_sample` is a callback function which is called - * when new RTT sample is obtained. + * :enum:`NGTCP2_CC_ALGO_BBR2` represents BBR v2. If BBR v2 is + * chosen, packet pacing is enabled. */ - ngtcp2_cc_new_rtt_sample new_rtt_sample; - /** - * :member:`reset` is a callback function which is called when - * congestion control state must be reset. - */ - ngtcp2_cc_reset reset; - /** - * :member:`event` is a callback function which is called when a - * specific event happens. - */ - ngtcp2_cc_event event; -} ngtcp2_cc; + NGTCP2_CC_ALGO_BBR2 = 0x03 +} ngtcp2_cc_algo; /** * @functypedef @@ -1700,14 +1699,14 @@ typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...); * * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_QLOG_WRITE_FLAG_NONE 0 +#define NGTCP2_QLOG_WRITE_FLAG_NONE 0x00u /** * @macro * * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` indicates that this is the * final call to :type:`ngtcp2_qlog_write` in the current connection. */ -#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01 +#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01u /** * @struct @@ -1730,8 +1729,8 @@ typedef struct ngtcp2_rand_ctx { * * :type:`ngtcp2_qlog_write` is a callback function which is called to * write qlog |data| of length |datalen| bytes. |flags| is bitwise OR - * of zero or more of NGTCP2_QLOG_WRITE_FLAG_*. See - * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE`. If + * of zero or more of :macro:`NGTCP2_QLOG_WRITE_FLAG_* + * `. If * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0. */ typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags, @@ -1757,6 +1756,9 @@ typedef struct ngtcp2_qlog_settings { ngtcp2_qlog_write write; } ngtcp2_qlog_settings; +#define NGTCP2_SETTINGS_VERSION_V1 1 +#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_VERSION_V1 + /** * @struct * @@ -1768,18 +1770,9 @@ typedef struct ngtcp2_settings { */ ngtcp2_qlog_settings qlog; /** - * :member:`cc_algo` specifies congestion control algorithm. If - * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUSTOM` is set, :member:`cc` - * must be set to a pointer to custom congestion control algorithm. + * :member:`cc_algo` specifies congestion control algorithm. */ ngtcp2_cc_algo cc_algo; - /** - * :member:`cc` is a pointer to custom congestion control algorithm. - * :member:`cc_algo` must be set to - * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUSTOM` in order to enable - * custom congestion control algorithm. - */ - ngtcp2_cc *cc; /** * :member:`initial_ts` is an initial timestamp given to the * library. @@ -1798,8 +1791,7 @@ typedef struct ngtcp2_settings { /** * :member:`max_udp_payload_size` is the maximum size of UDP * datagram payload that this endpoint transmits. It is used by - * congestion controller to compute congestion window. If it is set - * to 0, it defaults to :macro:`NGTCP2_DEFAULT_MAX_PKTLEN`. + * congestion controller to compute congestion window. */ size_t max_udp_payload_size; /** @@ -1847,33 +1839,193 @@ typedef struct ngtcp2_settings { * immediate acknowledgement. */ size_t ack_thresh; -} ngtcp2_settings; - -/** - * @struct - * - * :type:`ngtcp2_addr` is the endpoint address. - */ -typedef struct ngtcp2_addr { /** - * :member:`addrlen` is the length of addr. + * :member:`no_udp_payload_size_shaping`, if set to nonzero, + * instructs the library not to limit the UDP payload size to + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended by + * Path MTU Discovery) and instead use the mininum size among the + * given buffer size, :member:`max_udp_payload_size`, and the + * received max_udp_payload QUIC transport parameter. + */ + int no_udp_payload_size_shaping; + /** + * :member:`handshake_timeout` is the period of time before giving + * up QUIC connection establishment. If QUIC handshake is not + * complete within this period, `ngtcp2_conn_handle_expiry` returns + * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` error. The deadline is + * :member:`initial_ts` + :member:`handshake_timeout`. If this + * field is set to ``UINT64_MAX``, no handshake timeout is set. + */ + ngtcp2_duration handshake_timeout; + /** + * :member:`preferred_versions` is the array of versions that are + * preferred by the local endpoint. All versions set in this array + * must be supported by the library, and compatible to QUIC v1. The + * reserved versions are not allowed. They are sorted in the order + * of preference. + * + * On compatible version negotiation, server will negotiate one of + * those versions contained in this array if a client initially + * chooses a less preferred version. This version set corresponds + * to Offered Versions in QUIC Version Negotiation draft, and it should + * be sent in Version Negotiation packet. + * + * Client uses this field and :member:`original_version` to prevent + * version downgrade attack if it reacted upon Version Negotiation + * packet. If this field is specified, client must include + * |client_chosen_version| passed to `ngtcp2_conn_client_new` unless + * |client_chosen_version| is a reserved version. */ - size_t addrlen; + uint32_t *preferred_versions; /** - * :member:`addr` points to the buffer which contains endpoint - * address. It must not be ``NULL``. + * :member:`preferred_versionslen` is the number of versions that + * are contained in the array pointed by + * :member:`preferred_versions`. */ - struct sockaddr *addr; + size_t preferred_versionslen; /** - * :member:`user_data` is an arbitrary data and opaque to the - * library. + * :member:`other_versions` is the array of versions that are set in + * :member:`other_versions ` + * field of outgoing version_information QUIC transport parameter. + * + * For server, this corresponds to Fully-Deployed Versions in QUIC + * Version Negotiation draft. If this field is set not, it is set + * to :member:`preferred_versions` internally if + * :member:`preferred_versionslen` is not zero. If this field is + * not set, and :member:`preferred_versionslen` is zero, this field + * is set to :macro:`NGTCP2_PROTO_VER_V1` internally. + * + * Client must include |client_chosen_version| passed to + * `ngtcp2_conn_client_new` in this array if this field is set and + * |client_chosen_version| is not a reserved version. If this field + * is not set, |client_chosen_version| passed to + * `ngtcp2_conn_client_new` will be set in this field internally + * unless |client_chosen_version| is a reserved version. */ - void *user_data; -} ngtcp2_addr; - -/** - * @struct - * + uint32_t *other_versions; + /** + * :member:`other_versionslen` is the number of versions that are + * contained in the array pointed by :member:`other_versions`. + */ + size_t other_versionslen; + /** + * :member:`original_version` is the original version that client + * initially used to make a connection attempt. If it is set, and + * it differs from |client_chosen_version| passed to + * `ngtcp2_conn_client_new`, the library assumes that client reacted + * upon Version Negotiation packet. Server does not use this field. + */ + uint32_t original_version; + /** + * :member:`no_pmtud`, if set to nonzero, disables Path MTU + * Discovery. + */ + int no_pmtud; +} ngtcp2_settings; + +#ifdef NGTCP2_USE_GENERIC_SOCKADDR +typedef struct ngtcp2_sockaddr { + uint16_t sa_family; + uint8_t sa_data[14]; +} ngtcp2_sockaddr; + +typedef struct ngtcp2_in_addr { + uint32_t s_addr; +} ngtcp2_in_addr; + +typedef struct ngtcp2_sockaddr_in { + uint16_t sin_family; + uint16_t sin_port; + ngtcp2_in_addr sin_addr; + uint8_t sin_zero[8]; +} ngtcp2_sockaddr_in; + +# define NGTCP2_SS_MAXSIZE 128 +# define NGTCP2_SS_ALIGNSIZE (sizeof(uint64_t)) +# define NGTCP2_SS_PAD1SIZE (NGTCP2_SS_ALIGNSIZE - sizeof(uint16_t)) +# define NGTCP2_SS_PAD2SIZE \ + (NGTCP2_SS_MAXSIZE - \ + (sizeof(uint16_t) + NGTCP2_SS_PAD1SIZE + NGTCP2_SS_ALIGNSIZE)) + +typedef struct ngtcp2_sockaddr_storage { + uint16_t ss_family; + uint8_t _ss_pad1[NGTCP2_SS_PAD1SIZE]; + uint64_t _ss_align; + uint8_t _ss_pad2[NGTCP2_SS_PAD2SIZE]; +} ngtcp2_sockaddr_storage; + +# undef NGTCP2_SS_PAD2SIZE +# undef NGTCP2_SS_PAD1SIZE +# undef NGTCP2_SS_ALIGNSIZE +# undef NGTCP2_SS_MAXSIZE + +typedef uint32_t ngtcp2_socklen; +#else +/** + * @typedef + * + * :type:`ngtcp2_sockaddr` is typedefed to struct sockaddr. If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * the generic struct sockaddr defined in ngtcp2.h. + */ +typedef struct sockaddr ngtcp2_sockaddr; +/** + * @typedef + * + * :type:`ngtcp2_sockaddr_storage` is typedefed to struct + * sockaddr_storage. If :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is + * defined, it is typedefed to the generic struct sockaddr_storage + * defined in ngtcp2.h. + */ +typedef struct sockaddr_storage ngtcp2_sockaddr_storage; +typedef struct sockaddr_in ngtcp2_sockaddr_in; +/** + * @typedef + * + * :type:`ngtcp2_socklen` is typedefed to socklen_t. If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * uint32_t. + */ +typedef socklen_t ngtcp2_socklen; +#endif + +#if defined(NGTCP2_USE_GENERIC_SOCKADDR) || \ + defined(NGTCP2_USE_GENERIC_IPV6_SOCKADDR) +typedef struct ngtcp2_in6_addr { + uint8_t in6_addr[16]; +} ngtcp2_in6_addr; + +typedef struct ngtcp2_sockaddr_in6 { + uint16_t sin6_family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + ngtcp2_in6_addr sin6_addr; + uint32_t sin6_scope_id; +} ngtcp2_sockaddr_in6; +#else +typedef struct sockaddr_in6 ngtcp2_sockaddr_in6; +#endif + +/** + * @struct + * + * :type:`ngtcp2_addr` is the endpoint address. + */ +typedef struct ngtcp2_addr { + /** + * :member:`addr` points to the buffer which contains endpoint + * address. It must not be ``NULL``. + */ + ngtcp2_sockaddr *addr; + /** + * :member:`addrlen` is the length of addr. + */ + ngtcp2_socklen addrlen; +} ngtcp2_addr; + +/** + * @struct + * * :type:`ngtcp2_path` is the network endpoints where a packet is sent * and received. */ @@ -1886,6 +2038,21 @@ typedef struct ngtcp2_path { * :member:`remote` is the address of remote endpoint. */ ngtcp2_addr remote; + /** + * :member:`user_data` is an arbitrary data and opaque to the + * library. + * + * Note that :type:`ngtcp2_path` is generally passed to + * :type:`ngtcp2_conn` by an application, and :type:`ngtcp2_conn` + * stores their copies. Unfortunately, there is no way for the + * application to know when :type:`ngtcp2_conn` finishes using a + * specific :type:`ngtcp2_path` object in mid connection, which + * means that the application cannot free the data pointed by this + * field. Therefore, it is advised to use this field only when the + * data pointed by this field persists in an entire lifetime of the + * connection. + */ + void *user_data; } ngtcp2_path; /** @@ -1896,17 +2063,17 @@ typedef struct ngtcp2_path { */ typedef struct ngtcp2_path_storage { /** - * :member:`local_addrbuf` is a buffer to store local address. + * :member:`path` stores network path. */ - struct sockaddr_storage local_addrbuf; + ngtcp2_path path; /** - * :member:`remote_addrbuf` is a buffer to store remote address. + * :member:`local_addrbuf` is a buffer to store local address. */ - struct sockaddr_storage remote_addrbuf; + ngtcp2_sockaddr_storage local_addrbuf; /** - * :member:`path` stores network path. + * :member:`remote_addrbuf` is a buffer to store remote address. */ - ngtcp2_path path; + ngtcp2_sockaddr_storage remote_addrbuf; } ngtcp2_path_storage; /** @@ -1993,7 +2160,7 @@ typedef struct ngtcp2_crypto_cipher_ctx { * :type:`ngtcp2_crypto_ctx` is a convenient structure to bind all * crypto related objects in one place. Use * `ngtcp2_crypto_ctx_initial` to initialize this struct for Initial - * packet encryption. For Handshake and Short packets, use + * packet encryption. For Handshake and 1RTT packets, use * `ngtcp2_crypto_ctx_tls`. */ typedef struct ngtcp2_crypto_ctx { @@ -2027,17 +2194,21 @@ typedef struct ngtcp2_crypto_ctx { * `ngtcp2_encode_transport_params` encodes |params| in |dest| of * length |destlen|. * + * If |dest| is NULL, and |destlen| is zero, this function just + * returns the number of bytes required to store the encoded transport + * parameters. + * * This function returns the number of written, or one of the * following negative error codes: * * :macro:`NGTCP2_ERR_NOBUF` * Buffer is too small. - * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`: + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` * |exttype| is invalid. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params_versioned( uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype, - const ngtcp2_transport_params *params); + int transport_params_version, const ngtcp2_transport_params *params); /** * @function @@ -2049,6 +2220,44 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( * If the optional parameters are missing, the default value is * assigned. * + * The following fields may point to somewhere inside the buffer + * pointed by |data| of length |datalen|: + * + * - :member:`ngtcp2_transport_params.version_info.other_versions + * ` + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * The required parameter is missing. + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * The input is malformed. + */ +NGTCP2_EXTERN int ngtcp2_decode_transport_params_versioned( + int transport_params_version, ngtcp2_transport_params *params, + ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_decode_transport_params_new` decodes transport parameters + * in |data| of length |datalen|, and stores the result in the object + * allocated dynamically. The pointer to the allocated object is + * assigned to |*pparams|. Unlike `ngtcp2_decode_transport_params`, + * all direct and indirect fields are also allocated dynamically if + * needed. + * + * |mem| is a memory allocator to allocate memory. If |mem| is + * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()` + * is used. + * + * If the optional parameters are missing, the default value is + * assigned. + * + * `ngtcp2_transport_params_del` frees the memory allocated by this + * function. + * * This function returns 0 if it succeeds, or one of the following * negative error codes: * @@ -2056,13 +2265,58 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( * The required parameter is missing. * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` * The input is malformed. - * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`: - * |exttype| is invalid. + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. */ -NGTCP2_EXTERN int -ngtcp2_decode_transport_params(ngtcp2_transport_params *params, - ngtcp2_transport_params_type exttype, - const uint8_t *data, size_t datalen); +NGTCP2_EXTERN int ngtcp2_decode_transport_params_new( + ngtcp2_transport_params **pparams, ngtcp2_transport_params_type exttype, + const uint8_t *data, size_t datalen, const ngtcp2_mem *mem); + +/** + * @function + * + * `ngtcp2_transport_params_del` frees the |params| which must be + * dynamically allocated by `ngtcp2_decode_transport_params_new`. + * + * |mem| is a memory allocator that allocated |params|. If |mem| is + * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()` + * is used. + * + * If |params| is ``NULL``, this function does nothing. + */ +NGTCP2_EXTERN void ngtcp2_transport_params_del(ngtcp2_transport_params *params, + const ngtcp2_mem *mem); + +/** + * @struct + * + * :type:`ngtcp2_version_cid` is a convenient struct to store the + * result of `ngtcp2_pkt_decode_version_cid`. + */ +typedef struct ngtcp2_version_cid { + /** + * :member:`version` stores QUIC version. + */ + uint32_t version; + /** + * :member:`dcid` points to the Destination Connection ID. + */ + const uint8_t *dcid; + /** + * :member:`dcidlen` is the length of the Destination Connection ID + * pointed by :member:`dcid`. + */ + size_t dcidlen; + /** + * :member:`scid` points to the Source Connection ID. + */ + const uint8_t *scid; + /** + * :member:`scidlen` is the length of the Source Connection ID + * pointed by :member:`scid`. + */ + size_t scidlen; +} ngtcp2_version_cid; /** * @function @@ -2076,34 +2330,44 @@ ngtcp2_decode_transport_params(ngtcp2_transport_params *params, * Longer Connection ID is only valid if the version is unsupported * QUIC version. * - * If the given packet is Long packet, this function extracts the - * version from the packet and assigns it to |*pversion|. It also + * If the given packet is Long header packet, this function extracts + * the version from the packet and assigns it to + * :member:`dest->version `. It also * extracts the pointer to the Destination Connection ID and its - * length and assigns them to |*pdcid| and |*pdcidlen| respectively. - * Similarly, it extracts the pointer to the Source Connection ID and - * its length and assigns them to |*pscid| and |*pscidlen| - * respectively. - * - * If the given packet is Short packet, |*pversion| will be 0, - * |*pscid| will be ``NULL``, and |*pscidlen| will be 0. Because the - * Short packet does not have the length of Destination Connection ID, - * the caller has to pass the length in |short_dcidlen|. This - * function extracts the pointer to the Destination Connection ID and - * assigns it to |*pdcid|. |short_dcidlen| is assigned to - * |*pdcidlen|. - * - * This function returns 0 or 1 if it succeeds. It returns 1 if - * Version Negotiation packet should be sent. Otherwise, one of the + * length and assigns them to :member:`dest->dcid + * ` and :member:`dest->dcidlen + * ` respectively. Similarly, it extracts + * the pointer to the Source Connection ID and its length and assigns + * them to :member:`dest->scid ` and + * :member:`dest->scidlen ` respectively. + * + * If the given packet is Short header packet, :member:`dest->version + * ` will be 0, :member:`dest->scid + * ` will be ``NULL``, and + * :member:`dest->scidlen ` will be 0. + * Because the Short header packet does not have the length of + * Destination Connection ID, the caller has to pass the length in + * |short_dcidlen|. This function extracts the pointer to the + * Destination Connection ID and assigns it to :member:`dest->dcid + * `. |short_dcidlen| is assigned to + * :member:`dest->dcidlen `. + * + * If Version Negotiation is required, this function returns + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`. Unlike the other error + * cases, all fields of |dest| are assigned as described above. + * + * This function returns 0 if it succeeds. Otherwise, one of the * following negative error code: * * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` * The function could not decode the packet header. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` + * Version Negotiation packet should be sent. */ -NGTCP2_EXTERN int -ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, - size_t *pdcidlen, const uint8_t **pscid, - size_t *pscidlen, const uint8_t *data, - size_t datalen, size_t short_dcidlen); +NGTCP2_EXTERN int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, + const uint8_t *data, + size_t datalen, + size_t short_dcidlen); /** * @function @@ -2124,8 +2388,11 @@ ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, * Negotiation packet has random type in wire format. For * convenience, this function sets * :enum:`ngtcp2_pkt_type.NGTCP2_PKT_VERSION_NEGOTIATION` to - * dest->type, and set dest->payloadlen and dest->pkt_num to 0. - * Version Negotiation packet occupies a single packet. + * :member:`dest->type `, clears + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` flag from :member:`dest->flags + * `, and sets 0 to :member:`dest->len + * `. Version Negotiation packet occupies a single + * packet. * * It stores the result in the object pointed by |dest|, and returns * the number of bytes decoded to read the packet header if it @@ -2141,12 +2408,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, /** * @function * - * `ngtcp2_pkt_decode_hd_short` decodes QUIC short packet header in - * |pkt| of length |pktlen|. |dcidlen| is the length of DCID in - * packet header. Short packet does not encode the length of - * connection ID, thus we need the input from the outside. This - * function only parses the input just before packet number field. - * This function can handle Connection ID up to + * `ngtcp2_pkt_decode_hd_short` decodes QUIC short header packet + * header in |pkt| of length |pktlen|. |dcidlen| is the length of + * DCID in packet header. Short header packet does not encode the + * length of connection ID, thus we need the input from the outside. + * This function only parses the input just before packet number + * field. This function can handle Connection ID up to * :macro:`NGTCP2_MAX_CIDLEN`. Consider to use * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. It * stores the result in the object pointed by |dest|, and returns the @@ -2170,7 +2437,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, * and its length must be :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` * bytes long. |rand| specifies the random octets preceding Stateless * Reset Token. The length of |rand| is specified by |randlen| which - * must be at least :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN` bytes + * must be at least :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` bytes * long. * * If |randlen| is too long to write them all in the buffer, |rand| is @@ -2183,7 +2450,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, * Buffer is too small. * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` * |randlen| is strictly less than - * :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN`. + * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN`. */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset( uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token, @@ -2231,15 +2498,12 @@ typedef struct ngtcp2_conn ngtcp2_conn; * `ngtcp2_conn_submit_crypto_data` function. Make sure that before * calling `ngtcp2_conn_submit_crypto_data` function, client * application must create initial packet protection keys and IVs, and - * provide them to ngtcp2 library using `ngtcp2_conn_set_initial_key` - * and + * provide them to ngtcp2 library using + * `ngtcp2_conn_install_initial_key`. * * This callback function must return 0 if it succeeds, or * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call * return immediately. - * - * TODO: Define error code for TLS stack failure. Suggestion: - * NGTCP2_ERR_CRYPTO. */ typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data); @@ -2250,16 +2514,13 @@ typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data); * Initial packet from client. An server application must implement * this callback, and generate initial keys and IVs for both * transmission and reception. Install them using - * `ngtcp2_conn_set_initial_key`. |dcid| is the destination + * `ngtcp2_conn_install_initial_key`. |dcid| is the destination * connection ID which client generated randomly. It is used to * derive initial packet protection keys. * * The callback function must return 0 if it succeeds. If an error * occurs, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the * library call return immediately. - * - * TODO: Define error code for TLS stack failure. Suggestion: - * NGTCP2_ERR_CRYPTO. */ typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn, const ngtcp2_cid *dcid, @@ -2310,12 +2571,24 @@ typedef enum ngtcp2_crypto_level { * * The application should provide the given data to TLS stack. * - * The callback function must return 0 if it succeeds. If TLS stack - * reported error, return :macro:`NGTCP2_ERR_CRYPTO`. If application - * encounters fatal error, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * which makes the library call return immediately. If the other - * value is returned, it is treated as + * The callback function must return 0 if it succeeds, or one of the + * following negative error codes: + * + * - :macro:`NGTCP2_ERR_CRYPTO` + * - :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_PROTO` + * - :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + * - :macro:`NGTCP2_ERR_NOMEM` + * - :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * + * If the other value is returned, it is treated as * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + * + * If application encounters fatal error, return + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. */ typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, @@ -2369,15 +2642,15 @@ typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn, * @functypedef * * :type:`ngtcp2_recv_retry` is invoked when Retry packet is received. - * This callback is client only. + * This callback is client use only. * * Application must regenerate packet protection key, IV, and header * protection key for Initial packets using the destination connection - * ID obtained by `ngtcp2_conn_get_dcid()` and install them by calling - * `ngtcp2_conn_install_initial_key()`. + * ID obtained by :member:`hd->scid ` and install + * them by calling `ngtcp2_conn_install_initial_key()`. * - * 0-RTT data accepted by the ngtcp2 library will be retransmitted by - * the library automatically. + * 0-RTT data accepted by the ngtcp2 library will be automatically + * retransmitted as 0-RTT data by the library. * * The callback function must return 0 if it succeeds. Returning * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return @@ -2394,13 +2667,13 @@ typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, * encrypt is passed as |plaintext| of length |plaintextlen|. The * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context * object which is initialized with encryption key. The nonce is - * passed as |nonce| of length |noncelen|. The ad, Additional Data to - * AEAD, is passed as |ad| of length |adlen|. + * passed as |nonce| of length |noncelen|. The Additional + * Authenticated Data is passed as |aad| of length |aadlen|. * * The implementation of this callback must encrypt |plaintext| using * the negotiated cipher suite and write the ciphertext into the * buffer pointed by |dest|. |dest| has enough capacity to store the - * ciphertext. + * ciphertext and any additional AEAD tag data. * * |dest| and |plaintext| may point to the same buffer. * @@ -2412,7 +2685,7 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *plaintext, size_t plaintextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @functypedef @@ -2422,8 +2695,8 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, * decrypt is passed as |ciphertext| of length |ciphertextlen|. The * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context * object which is initialized with decryption key. The nonce is - * passed as |nonce| of length |noncelen|. The ad, Additional Data to - * AEAD, is passed as |ad| of length |adlen|. + * passed as |nonce| of length |noncelen|. The Additional + * Authenticated Data is passed as |aad| of length |aadlen|. * * The implementation of this callback must decrypt |ciphertext| using * the negotiated cipher suite and write the ciphertext into the @@ -2433,34 +2706,38 @@ typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, * |dest| and |ciphertext| may point to the same buffer. * * The callback function must return 0 if it succeeds. If TLS stack - * fails to decrypt data, return :macro:`NGTCP2_ERR_TLS_DECRYPT`. For - * any other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which + * fails to decrypt data, return :macro:`NGTCP2_ERR_DECRYPT`. For any + * other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which * makes the library call return immediately. */ typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *ciphertext, size_t ciphertextlen, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); + const uint8_t *aad, size_t aadlen); /** * @functypedef * * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the - * application to produce mask to encrypt or decrypt packet header. + * application to produce a mask to encrypt or decrypt packet header. * The encryption cipher is |hp|. |hp_ctx| is the cipher context * object which is initialized with header protection key. The sample - * is passed as |sample|. + * is passed as |sample| which is :macro:`NGTCP2_HP_SAMPLELEN` bytes + * long. * * The implementation of this callback must produce a mask using the * header protection cipher suite specified by QUIC specification and * write the result into the buffer pointed by |dest|. The length of - * mask must be :macro:`NGTCP2_HP_MASKLEN`. The library ensures that - * |dest| has enough capacity. + * the mask must be at least :macro:`NGTCP2_HP_MASKLEN`. The library + * only uses the first :macro:`NGTCP2_HP_MASKLEN` bytes of the + * produced mask. The buffer pointed by |dest| is guaranteed to have + * at least :macro:`NGTCP2_HP_SAMPLELEN` bytes available for + * convenience. * * The callback function must return 0 if it succeeds, or - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call - * return immediately. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. */ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, const ngtcp2_crypto_cipher_ctx *hp_ctx, @@ -2477,7 +2754,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00 +#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00u /** * @macro @@ -2485,7 +2762,7 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` indicates that this chunk of * data is final piece of an incoming stream. */ -#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01 +#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01u /** * @macro @@ -2494,21 +2771,21 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * data contains data received in 0RTT packet and the handshake has * not completed yet, which means that the data might be replayed. */ -#define NGTCP2_STREAM_DATA_FLAG_EARLY 0x02 +#define NGTCP2_STREAM_DATA_FLAG_EARLY 0x02u /** * @functypedef * * :type:`ngtcp2_recv_stream_data` is invoked when stream data is * received. The stream is specified by |stream_id|. |flags| is the - * bitwise-OR of zero or more of NGTCP2_STREAM_DATA_FLAG_*. See - * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE`. If |flags| & + * bitwise-OR of zero or more of :macro:`NGTCP2_STREAM_DATA_FLAG_* + * `. If |flags| & * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of * the data is the last data in this stream. |offset| is the offset * where this data begins. The library ensures that data is passed to - * the application in the non-decreasing order of |offset|. The data - * is passed as |data| of length |datalen|. |datalen| may be 0 if and - * only if |fin| is nonzero. + * the application in the non-decreasing order of |offset| without any + * overlap. The data is passed as |data| of length |datalen|. + * |datalen| may be 0 if and only if |fin| is nonzero. * * If :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` is set in |flags|, it * indicates that a part of or whole data was received in 0RTT packet @@ -2538,21 +2815,55 @@ typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, uint32_t flags, typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id, void *user_data); +/** + * @macrosection + * + * Stream close flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_STREAM_CLOSE_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` indicates that + * app_error_code parameter is set. + */ +#define NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET 0x01u + /** * @functypedef * * :type:`ngtcp2_stream_close` is invoked when a stream is closed. * This callback is not called when QUIC connection is closed before - * existing streams are closed. |app_error_code| indicates the error - * code of this closure. + * existing streams are closed. |flags| is the bitwise-OR of zero or + * more of :macro:`NGTCP2_STREAM_CLOSE_FLAG_* + * `. |app_error_code| indicates the + * error code of this closure if + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is set in + * |flags|. If it is not set, the stream was closed without any error + * code, which generally means success. + * + * |app_error_code| is the first application error code sent by a + * local endpoint, or received from a remote endpoint. If a stream is + * closed cleanly, no application error code is exchanged. Since QUIC + * stack does not know the application error code which indicates "no + * errors", |app_error_code| is set to 0 and + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is not set in + * |flags| in this case. * * The implementation of this callback should return 0 if it succeeds. * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library * call return immediately. */ -typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data); +typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, uint32_t flags, + int64_t stream_id, uint64_t app_error_code, + void *user_data, void *stream_user_data); /** * @functypedef @@ -2575,10 +2886,10 @@ typedef int (*ngtcp2_stream_reset)(ngtcp2_conn *conn, int64_t stream_id, * which is called when stream data is acked, and application can free * the data. The acked range of data is [offset, offset + datalen). * For a given stream_id, this callback is called sequentially in - * increasing order of |offset|. |datalen| is normally strictly - * greater than 0. One exception is that when a packet which includes - * STREAM frame which has fin flag set, and 0 length data, this - * callback is invoked with 0 passed as |datalen|. + * increasing order of |offset| without any overlap. |datalen| is + * normally strictly greater than 0. One exception is that when a + * packet which includes STREAM frame which has fin flag set, and 0 + * length data, this callback is invoked with 0 passed as |datalen|. * * If a stream is closed prematurely and stream data is still * in-flight, this callback function is not called for those data. @@ -2591,26 +2902,6 @@ typedef int (*ngtcp2_acked_stream_data_offset)( ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen, void *user_data, void *stream_user_data); -/** - * @functypedef - * - * :type:`ngtcp2_acked_crypto_offset` is a callback function which is - * called when crypto stream data is acknowledged, and application can - * free the data. |crypto_level| indicates the encryption level where - * this data was sent. Crypto data never be sent in - * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`. This works - * like :type:`ngtcp2_acked_stream_data_offset` but crypto stream has - * no stream_id and stream_user_data, and |datalen| never become 0. - * - * The implementation of this callback should return 0 if it succeeds. - * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library - * call return immediately. - */ -typedef int (*ngtcp2_acked_crypto_offset)(ngtcp2_conn *conn, - ngtcp2_crypto_level crypto_level, - uint64_t offset, uint64_t datalen, - void *user_data); - /** * @functypedef * @@ -2663,16 +2954,11 @@ typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn, * * :type:`ngtcp2_rand` is a callback function to get randomized byte * string from application. Application must fill random |destlen| - * bytes to the buffer pointed by |dest|. |usage| provides the usage - * of the generated random data. - * - * The callback function must return 0 if it succeeds. Returning - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return - * immediately. + * bytes to the buffer pointed by |dest|. The generated bytes are + * used only in non-cryptographic context. */ -typedef int (*ngtcp2_rand)(uint8_t *dest, size_t destlen, - const ngtcp2_rand_ctx *rand_ctx, - ngtcp2_rand_usage usage); +typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx); /** * @functypedef @@ -2740,12 +3026,36 @@ typedef int (*ngtcp2_update_key)( const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, size_t secretlen, void *user_data); +/** + * @macrosection + * + * Path validation related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_PATH_VALIDATION_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR` indicates the + * validation involving server preferred address. This flag is only + * set for client. + */ +#define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01u + /** * @functypedef * * :type:`ngtcp2_path_validation` is a callback function which tells - * the application the outcome of path validation. |path| is the path - * that was validated. If |res| is + * the application the outcome of path validation. |flags| is zero or + * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_* + * `. |path| is the path that was + * validated. If |res| is * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`, * the path validation succeeded. If |res| is * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`, @@ -2755,7 +3065,7 @@ typedef int (*ngtcp2_update_key)( * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return * immediately. */ -typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, +typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path, ngtcp2_path_validation_result res, void *user_data); @@ -2766,16 +3076,28 @@ typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, * :type:`ngtcp2_select_preferred_addr` is a callback function which * asks a client application to choose server address from preferred * addresses |paddr| received from server. An application should - * write preferred address in |dest|. If an application denies the - * preferred addresses, just leave |dest| unmodified (or set dest->len - * to 0) and return 0. + * write a network path for a selected preferred address in |dest|. + * More specifically, the selected preferred address must be set to + * :member:`dest->remote `, a client source + * address must be set to :member:`dest->local `. + * If a client source address does not change for the new server + * address, leave :member:`dest->local ` + * unmodified, or copy the value of :member:`local + * ` field of the current network path obtained + * from `ngtcp2_conn_get_path()`. Both :member:`dest->local.addr + * ` and :member:`dest->remote.addr + * ` point to buffers which are at least + * ``sizeof(struct sockaddr_storage)`` bytes long, respectively. If + * an application denies the preferred addresses, just leave |dest| + * unmodified (or set :member:`dest->remote.addrlen + * ` to 0) and return 0. * * The callback function must return 0 if it succeeds. Returning * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return * immediately. */ typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn, - ngtcp2_addr *dest, + ngtcp2_path *dest, const ngtcp2_preferred_addr *paddr, void *user_data); @@ -2840,7 +3162,9 @@ typedef int (*ngtcp2_recv_new_token)(ngtcp2_conn *conn, const ngtcp2_vec *token, * @functypedef * * :type:`ngtcp2_delete_crypto_aead_ctx` is a callback function which - * must delete the native object pointed by |aead_ctx|->native_handle. + * must delete the native object pointed by + * :member:`aead_ctx->native_handle + * `. */ typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, @@ -2851,7 +3175,8 @@ typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn, * * :type:`ngtcp2_delete_crypto_cipher_ctx` is a callback function * which must delete the native object pointed by - * |cipher_ctx|->native_handle. + * :member:`cipher_ctx->native_handle + * `. */ typedef void (*ngtcp2_delete_crypto_cipher_ctx)( ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); @@ -2867,7 +3192,7 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)( * * :macro:`NGTCP2_DATAGRAM_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_DATAGRAM_FLAG_NONE 0x00 +#define NGTCP2_DATAGRAM_FLAG_NONE 0x00u /** * @macro @@ -2876,14 +3201,14 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)( * is received in 0RTT packet and the handshake has not completed yet, * which means that the data might be replayed. */ -#define NGTCP2_DATAGRAM_FLAG_EARLY 0x01 +#define NGTCP2_DATAGRAM_FLAG_EARLY 0x01u /** * @functypedef * * :type:`ngtcp2_recv_datagram` is invoked when DATAGRAM frame is * received. |flags| is bitwise-OR of zero or more of - * NGTCP2_DATAGRAM_FLAG_*. See :macro:`NGTCP2_DATAGRAM_FLAG_NONE`. + * :macro:`NGTCP2_DATAGRAM_FLAG_* `. * * If :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` is set in |flags|, it * indicates that DATAGRAM frame was received in 0RTT packet and a @@ -2897,6 +3222,107 @@ typedef int (*ngtcp2_recv_datagram)(ngtcp2_conn *conn, uint32_t flags, const uint8_t *data, size_t datalen, void *user_data); +/** + * @functypedef + * + * :type:`ngtcp2_ack_datagram` is invoked when a packet which contains + * DATAGRAM frame which is identified by |dgram_id| is acknowledged. + * |dgram_id| is the valued passed to `ngtcp2_conn_writev_datagram`. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_ack_datagram)(ngtcp2_conn *conn, uint64_t dgram_id, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_lost_datagram` is invoked when a packet which + * contains DATAGRAM frame which is identified by |dgram_id| is + * declared lost. |dgram_id| is the valued passed to + * `ngtcp2_conn_writev_datagram`. Note that the loss might be + * spurious, and DATAGRAM frame might be acknowledged later. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_lost_datagram)(ngtcp2_conn *conn, uint64_t dgram_id, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_get_path_challenge_data` is a callback function to + * ask an application for new data that is sent in PATH_CHALLENGE + * frame. Application must generate new unpredictable exactly + * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes of random data and + * store them into the buffer pointed by |data|. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_get_path_challenge_data)(ngtcp2_conn *conn, uint8_t *data, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_stop_sending` is invoked when a stream is no + * longer read by a local endpoint before it receives all stream data. + * This function is called at most once per stream. |app_error_code| + * is the error code passed to `ngtcp2_conn_shutdown_stream_read` or + * `ngtcp2_conn_shutdown_stream`. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_stream_stop_sending)(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_version_negotiation` is invoked when the compatible + * version negotiation takes place. For client, it is called when it + * sees a change in version field of a long header packet. This + * callback function might be called multiple times for client. For + * server, it is called once when the version is negotiated. + * + * The implementation of this callback must install new Initial keys + * for |version|. Use `ngtcp2_conn_install_vneg_initial_key` to + * install keys. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_version_negotiation)(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_key` is invoked when new key is installed to + * |conn| during QUIC cryptographic handshake. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_key)(ngtcp2_conn *conn, ngtcp2_crypto_level level, + void *user_data); + +#define NGTCP2_CALLBACKS_VERSION_V1 1 +#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_VERSION_V1 + /** * @struct * @@ -2906,13 +3332,14 @@ typedef struct ngtcp2_callbacks { /** * :member:`client_initial` is a callback function which is invoked * when client asks TLS stack to produce first TLS cryptographic - * handshake message. This callback function must be specified. + * handshake message. This callback function must be specified for + * a client application. */ ngtcp2_client_initial client_initial; /** * :member:`recv_client_initial` is a callback function which is * invoked when a server receives the first packet from client. - * This callback function must be specified. + * This callback function must be specified for a server application. */ ngtcp2_recv_client_initial recv_client_initial; /** @@ -2956,14 +3383,6 @@ typedef struct ngtcp2_callbacks { * received. This callback function is optional. */ ngtcp2_recv_stream_data recv_stream_data; - /** - * :member:`acked_crypto_offset` is a callback function which is - * invoked when CRYPTO data is acknowledged by a remote endpoint. - * It tells an application the largest offset of acknowledged CRYPTO - * data without a gap so that the application can free memory for - * the data. This callback function is optional. - */ - ngtcp2_acked_crypto_offset acked_crypto_offset; /** * :member:`acked_stream_data_offset` is a callback function which * is invoked when STREAM data, which includes application data, is @@ -3012,8 +3431,8 @@ typedef struct ngtcp2_callbacks { ngtcp2_extend_max_streams extend_max_local_streams_uni; /** * :member:`rand` is a callback function which is invoked when the - * library needs unpredictable sequence of random data. This - * callback function must be specified. + * library needs sequence of random data. This callback function + * must be specified. */ ngtcp2_rand rand; /** @@ -3031,18 +3450,21 @@ typedef struct ngtcp2_callbacks { /** * :member:`update_key` is a callback function which is invoked when * the library tells an application that it must update keying - * materials and install new keys. This function must be specified. + * materials and install new keys. This callback function must be + * specified. */ ngtcp2_update_key update_key; /** * :member:`path_validation` is a callback function which is invoked - * when path validation completed. This function is optional. + * when path validation completed. This callback function is + * optional. */ ngtcp2_path_validation path_validation; /** * :member:`select_preferred_addr` is a callback function which is * invoked when the library asks a client to select preferred - * address presented by a server. This function is optional. + * address presented by a server. This callback function is + * optional. */ ngtcp2_select_preferred_addr select_preferred_addr; /** @@ -3075,47 +3497,97 @@ typedef struct ngtcp2_callbacks { /** * :member:`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. + * activated destination Connection ID is now deactivated. This + * callback function is optional. */ ngtcp2_connection_id_status dcid_status; /** * :member:`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. + * indicates the handshake confirmation for server. This callback + * function is optional. */ ngtcp2_handshake_confirmed handshake_confirmed; /** * :member:`recv_new_token` is a callback function which is invoked * when new token is received from server. This field is ignored by - * server. + * server. This callback function is optional. */ ngtcp2_recv_new_token recv_new_token; /** * :member:`delete_crypto_aead_ctx` is a callback function which - * deletes a given AEAD cipher context object. + * deletes a given AEAD cipher context object. This callback + * function must be specified. */ ngtcp2_delete_crypto_aead_ctx delete_crypto_aead_ctx; /** * :member:`delete_crypto_cipher_ctx` is a callback function which - * deletes a given cipher context object. + * deletes a given cipher context object. This callback function + * must be specified. */ ngtcp2_delete_crypto_cipher_ctx delete_crypto_cipher_ctx; /** * :member:`recv_datagram` is a callback function which is invoked - * when DATAGRAM frame is received. + * when DATAGRAM frame is received. This callback function is + * optional. */ ngtcp2_recv_datagram recv_datagram; + /** + * :member:`ack_datagram` is a callback function which is invoked + * when a packet containing DATAGRAM frame is acknowledged. This + * callback function is optional. + */ + ngtcp2_ack_datagram ack_datagram; + /** + * :member:`lost_datagram` is a callback function which is invoked + * when a packet containing DATAGRAM frame is declared lost. This + * callback function is optional. + */ + ngtcp2_lost_datagram lost_datagram; + /** + * :member:`get_path_challenge_data` is a callback function which is + * invoked when the library needs new PATH_CHALLENGE data. This + * callback must be specified. + */ + ngtcp2_get_path_challenge_data get_path_challenge_data; + /** + * :member:`stream_stop_sending` is a callback function which is + * invoked when a local endpoint no longer reads from a stream + * before it receives all stream data. This callback function is + * optional. + */ + ngtcp2_stream_stop_sending stream_stop_sending; + /** + * :member:`version_negotiation` is a callback function which is + * invoked when the compatible version negotiation takes place. + * This callback function must be specified. + */ + ngtcp2_version_negotiation version_negotiation; + /** + * :member:`recv_rx_key` is a callback function which is invoked + * when a new key for decrypting packets is installed during QUIC + * cryptographic handshake. It is not called for + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`. + */ + ngtcp2_recv_key recv_rx_key; + /** + * :member:`recv_tx_key` is a callback function which is invoked + * when a new key for encrypting packets is installed during QUIC + * cryptographic handshake. It is not called for + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`. + */ + ngtcp2_recv_key recv_tx_key; } ngtcp2_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. + * containing CONNECTION_CLOSE frame with the given |error_code| and + * the optional |reason| of length |reasonlen| 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 @@ -3131,20 +3603,24 @@ typedef struct ngtcp2_callbacks { */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, - const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, const ngtcp2_crypto_cipher_ctx *hp_ctx); /** * @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. - * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as an - * encryption key. + * by |dest| whose length is |destlen|. |dcid| is the destination + * connection ID which appeared in a packet as a source connection ID + * sent by client. |scid| is a server chosen source connection ID. + * |odcid| specifies Original Destination Connection ID which appeared + * in a packet as a destination connection ID sent by client. |token| + * specifies Retry Token, and |tokenlen| specifies its length. |aead| + * must be AEAD_AES_128_GCM. |aead_ctx| must be initialized with + * :macro:`NGTCP2_RETRY_KEY` as an encryption key. * * This function returns the number of bytes written to the buffer, or * one of the following negative error codes: @@ -3153,6 +3629,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( * Buffer is too small. * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` * Callback function failed. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * :member:`odcid->datalen ` is less than + * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN`. */ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, @@ -3164,15 +3643,21 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( * @function * * `ngtcp2_accept` is used by server implementation, and decides - * whether packet |pkt| of length |pktlen| is acceptable for initial - * packet from client. + * whether packet |pkt| of length |pktlen| from client is acceptable + * for the very initial packet to a connection. + * + * If |dest| is not ``NULL`` and the function returns 0, or + * :macro:`NGTCP2_ERR_RETRY`, the decoded packet header is stored to + * the object pointed by |dest|. * - * If it is acceptable, it returns 0. If it is not acceptable, and - * Version Negotiation packet is required to send, it returns 1. - * Otherwise, it returns -1. + * This function returns 0 if it succeeds, or one of the following + * negative error codes: * - * If |dest| is not ``NULL``, and the return value is 0 or 1, the - * decoded packet header is stored to the object pointed by |dest|. + * :macro:`NGTCP2_ERR_RETRY` + * Retry packet should be sent. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * The packet is not acceptable for the very first packet to a new + * connection; or the function failed to parse the packet header. */ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen); @@ -3182,15 +3667,16 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, * * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and * initializes it as client. |dcid| is randomized destination - * connection ID. |scid| is source connection ID. |version| is a - * QUIC version to use. |path| is the network path where this QUIC - * connection is being established and must not be ``NULL``. - * |callbacks|, |settings|, and |params| must not be ``NULL``, and the - * function make a copy of each of them. |params| is local QUIC - * transport parameters and sent to a remote endpoint during - * handshake. |user_data| is the arbitrary pointer which is passed to - * the user-defined callback functions. If |mem| is ``NULL``, the - * memory allocator returned by `ngtcp2_mem_default()` is used. + * connection ID. |scid| is source connection ID. + * |client_chosen_version| is a QUIC version that a client chooses. + * |path| is the network path where this QUIC connection is being + * established and must not be ``NULL``. |callbacks|, |settings|, and + * |params| must not be ``NULL``, and the function make a copy of each + * of them. |params| is local QUIC transport parameters and sent to a + * remote endpoint during handshake. |user_data| is the arbitrary + * pointer which is passed to the user-defined callback functions. If + * |mem| is ``NULL``, the memory allocator returned by + * `ngtcp2_mem_default()` is used. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -3198,13 +3684,13 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, * :macro:`NGTCP2_ERR_NOMEM` * Out of memory. */ -NGTCP2_EXTERN int -ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_transport_params *params, - const ngtcp2_mem *mem, void *user_data); +NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned( + ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data); /** * @function @@ -3212,14 +3698,14 @@ ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, * `ngtcp2_conn_server_new` creates new :type:`ngtcp2_conn`, and * initializes it as server. |dcid| is a destination connection ID. * |scid| is a source connection ID. |path| is the network path where - * this QUIC connection is being established and must not be ``NULL`. - * |version| is a QUIC version to use. |callbacks|, |settings|, and - * |params| must not be ``NULL``, and the function make a copy of each - * of them. |params| is local QUIC transport parameters and sent to a - * remote endpoint during handshake. |user_data| is the arbitrary - * pointer which is passed to the user-defined callback functions. If - * |mem| is ``NULL``, the memory allocator returned by - * `ngtcp2_mem_default()` is used. + * this QUIC connection is being established and must not be ``NULL``. + * |client_chosen_version| is a QUIC version that a client chooses. + * |callbacks|, |settings|, and |params| must not be ``NULL``, and the + * function make a copy of each of them. |params| is local QUIC + * transport parameters and sent to a remote endpoint during + * handshake. |user_data| is the arbitrary pointer which is passed to + * the user-defined callback functions. If |mem| is ``NULL``, the + * memory allocator returned by `ngtcp2_mem_default()` is used. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -3227,13 +3713,13 @@ ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, * :macro:`NGTCP2_ERR_NOMEM` * Out of memory. */ -NGTCP2_EXTERN int -ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_transport_params *params, - const ngtcp2_mem *mem, void *user_data); +NGTCP2_EXTERN int ngtcp2_conn_server_new_versioned( + ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data); /** * @function @@ -3249,56 +3735,59 @@ NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn); * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of * length |pktlen| and processes it. |path| is the network path the * packet is delivered and must not be ``NULL``. |pi| is packet - * metadata and must not be ``NULL``. This function performs QUIC - * handshake as well. + * metadata and may be ``NULL``. This function performs QUIC handshake + * as well. * * This function must not be called from inside the callback * functions. * * This function returns 0 if it succeeds, or negative error codes. - * In general, if the error code which satisfies - * `ngtcp2_err_is_fatal(err) ` != 0 is returned, - * the application should just close the connection by calling - * `ngtcp2_conn_write_connection_close` or just delete the QUIC - * connection using `ngtcp2_conn_del`. It is undefined to call the - * other library functions. If :macro:`NGTCP2_ERR_RETRY` is returned, - * application must be a server and it must perform address validation - * by sending Retry packet and close the connection. If + * If :macro:`NGTCP2_ERR_RETRY` is returned, application must be a + * server and it must perform address validation by sending Retry + * packet and discard the connection state. If * :macro:`NGTCP2_ERR_DROP_CONN` is returned, server application must * drop the connection silently (without sending any CONNECTION_CLOSE - * frame) and discard connection state. + * frame) and discard connection state. If + * :macro:`NGTCP2_ERR_DRAINING` is returned, a connection has entered + * the draining state, and no further packet transmission is allowed. + * If :macro:`NGTCP2_ERR_CRYPTO` is returned, the error happened in + * TLS stack and `ngtcp2_conn_get_tls_alert` returns TLS alert if set. + * + * If any other negative errors are returned, call + * `ngtcp2_conn_write_connection_close` to get terminal packet, and + * sending it makes QUIC connection enter the closing state. */ -NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, - const ngtcp2_path *path, - const ngtcp2_pkt_info *pi, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts); +NGTCP2_EXTERN int +ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, + int pkt_info_version, const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts); /** * @function * * `ngtcp2_conn_write_pkt` is equivalent to calling - * `ngtcp2_conn_writev_stream` without specifying stream data and + * `ngtcp2_conn_writev_stream` with -1 as stream_id, no stream data, and * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, - ngtcp2_path *path, - ngtcp2_pkt_info *pi, - uint8_t *dest, size_t destlen, - ngtcp2_tstamp ts); +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts); /** * @function * - * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC - * handshake has completed. + * `ngtcp2_conn_handshake_completed` tells |conn| that the TLS stack + * declares TLS handshake completion. This does not mean QUIC + * handshake has completed. The library needs extra conditions to be + * met. */ NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake + * `ngtcp2_conn_get_handshake_completed` returns nonzero if QUIC handshake * has completed. */ NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); @@ -3308,14 +3797,17 @@ NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); * * `ngtcp2_conn_install_initial_key` installs packet protection keying * materials for Initial packets. |rx_aead_ctx| is AEAD cipher - * context object and must be initialized with decryption key, IV - * |rx_iv| of length |rx_ivlen|, and packet header protection cipher - * context object |rx_hp_ctx| to decrypt incoming Initial packets. + * context object and must be initialized with a decryption key. + * |rx_iv| is IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is + * a packet header protection cipher context object for decryption. * Similarly, |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for * encrypting outgoing packets and are the same length with the * decryption counterpart . If they have already been set, they are * overwritten. * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. * :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3339,15 +3831,53 @@ NGTCP2_EXTERN int ngtcp2_conn_install_initial_key( const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); +/** + * @function + * + * `ngtcp2_conn_install_vneg_initial_key` installs packet protection + * keying materials for Initial packets on compatible version + * negotiation for |version|. |rx_aead_ctx| is AEAD cipher context + * object and must be initialized with a decryption key. |rx_iv| is + * IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is a packet + * header protection cipher context object for decryption. Similarly, + * |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for encrypting outgoing + * packets and are the same length with the decryption counterpart . + * If they have already been set, they are overwritten. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, + * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. + * :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_vneg_initial_key( + ngtcp2_conn *conn, uint32_t version, + const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv, + const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, + const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, + const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); + /** * @function * * `ngtcp2_conn_install_rx_handshake_key` installs packet protection * keying materials for decrypting incoming Handshake packets. * |aead_ctx| is AEAD cipher context object which must be initialized - * with decryption key, IV |iv| of length |ivlen|, and packet header - * protection cipher context object |hp_ctx| to decrypt incoming - * Handshake packets. + * with a decryption key. |iv| is IV of length |ivlen|. |hp_ctx| is + * a packet header protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. * * If this function succeeds, |conn| takes ownership of |aead_ctx|, * and |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3371,9 +3901,11 @@ NGTCP2_EXTERN int ngtcp2_conn_install_rx_handshake_key( * `ngtcp2_conn_install_tx_handshake_key` installs packet protection * keying materials for encrypting outgoing Handshake packets. * |aead_ctx| is AEAD cipher context object which must be initialized - * with encryption key, IV |iv| of length |ivlen|, and packet header - * protection cipher context object |hp_ctx| to encrypt outgoing - * Handshake packets. + * with an encryption key. |iv| is IV of length |ivlen|. |hp_ctx| is + * a packet header protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. * * If this function succeeds, |conn| takes ownership of |aead_ctx| and * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3399,6 +3931,9 @@ NGTCP2_EXTERN int ngtcp2_conn_install_tx_handshake_key( * packet header protection cipher context object |hp_ctx| to encrypt * (for client) or decrypt (for server) 0RTT packets. * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * * If this function succeeds, |conn| takes ownership of |aead_ctx| and * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete @@ -3419,12 +3954,15 @@ NGTCP2_EXTERN int ngtcp2_conn_install_early_key( * @function * * `ngtcp2_conn_install_rx_key` installs packet protection keying - * materials for decrypting Short packets. |secret| of length + * materials for decrypting Short header packets. |secret| of length * |secretlen| is the decryption secret which is used to derive keying * materials passed to this function. |aead_ctx| is AEAD cipher - * context object which must be initialized with decryption key, IV - * |iv| of length |ivlen|, and packet header protection cipher context - * object |hp_ctx| to decrypt incoming Short packets. + * context object which must be initialized with a decryption key. + * |iv| is IV of length |ivlen|. |hp_ctx| is a packet header + * protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. * * If this function succeeds, |conn| takes ownership of |aead_ctx| and * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3447,12 +3985,15 @@ NGTCP2_EXTERN int ngtcp2_conn_install_rx_key( * @function * * `ngtcp2_conn_install_tx_key` installs packet protection keying - * materials for encrypting Short packets. |secret| of length + * materials for encrypting Short header packets. |secret| of length * |secretlen| is the encryption secret which is used to derive keying * materials passed to this function. |aead_ctx| is AEAD cipher - * context object which must be initialized with encryption key, IV - * |iv| of length |ivlen|, and packet header protection cipher context - * object |hp_ctx| to encrypt outgoing Short packets. + * context object which must be initialized with an encryption key. + * |iv| is IV of length |ivlen|. |hp_ctx| is a packet header + * protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. * * If this function succeeds, |conn| takes ownership of |aead_ctx| and * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and @@ -3492,7 +4033,7 @@ NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, * `ngtcp2_conn_set_tls_error` sets the TLS related error |liberr| in * |conn|. |liberr| must be one of ngtcp2 library error codes (which * is defined as NGTCP2_ERR_* macro, such as - * :macro:`NGTCP2_ERR_TLS_DECRYPT`). In general, error code should be + * :macro:`NGTCP2_ERR_DECRYPT`). In general, error code should be * propagated via return value, but sometimes ngtcp2 API is called * inside callback function of TLS stack and it does not allow to * return ngtcp2 error code directly. In this case, implementation @@ -3510,6 +4051,34 @@ NGTCP2_EXTERN void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr); */ NGTCP2_EXTERN int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_set_tls_alert` sets a TLS alert |alert| generated by a + * local endpoint to |conn|. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert); + +/** + * @function + * + * `ngtcp2_conn_get_tls_alert` returns the value set by + * `ngtcp2_conn_set_tls_alert`. If no value is set, this function + * returns 0. + */ +NGTCP2_EXTERN uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_keep_alive_timeout` sets keep-alive timeout. If + * nonzero value is given, after a connection is idle at least in a + * given amount of time, a keep-alive packet is sent. If 0 is set, + * keep-alive functionality is disabled and this is the default. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, + ngtcp2_duration timeout); + /** * @function * @@ -3529,15 +4098,6 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn); NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts); -/** - * @function - * - * `ngtcp2_conn_get_idle_expiry` returns the time when a connection - * should be closed if it continues to be idle. If idle timeout is - * disabled, this function returns ``UINT64_MAX``. - */ -NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); - /** * @function * @@ -3548,51 +4108,71 @@ NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_set_remote_transport_params` sets transport parameter - * |params| to |conn|. + * `ngtcp2_conn_decode_remote_transport_params` decodes QUIC transport + * parameters from the buffer pointed by |data| of length |datalen|, + * and sets the result to |conn|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`NGTCP2_ERR_PROTO` - * If |conn| is server, and negotiated_version field is not the - * same as the used version. + * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * The required parameter is missing. + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * The input is malformed. + * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + * Failed to validate the remote QUIC transport parameters. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + * Version negotiation failure. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed */ NGTCP2_EXTERN int -ngtcp2_conn_set_remote_transport_params(ngtcp2_conn *conn, - const ngtcp2_transport_params *params); +ngtcp2_conn_decode_remote_transport_params(ngtcp2_conn *conn, + const uint8_t *data, size_t datalen); /** * @function * - * `ngtcp2_conn_get_remote_transport_params` fills settings values in - * |params|. original_connection_id and - * original_connection_id_present are always zero filled. + * `ngtcp2_conn_get_remote_transport_params` returns a pointer to the + * remote QUIC transport parameters. If no remote transport + * parameters are set, it returns NULL. */ -NGTCP2_EXTERN void -ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params); +NGTCP2_EXTERN const ngtcp2_transport_params * +ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn); /** * @function * * `ngtcp2_conn_set_early_remote_transport_params` sets |params| as - * transport parameter previously received from a server. The - * parameters are used to send 0-RTT data. QUIC requires that client - * application should remember transport parameter as well as session - * ticket. - * - * At least following fields must be set: - * - * * initial_max_stream_id_bidi - * * initial_max_stream_id_uni - * * initial_max_stream_data_bidi_local - * * initial_max_stream_data_bidi_remote - * * initial_max_stream_data_uni - * * initial_max_data - */ -NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params); + * transport parameters previously received from a server. The + * parameters are used to send early data. QUIC requires that client + * application should remember transport parameters along with a + * session ticket. + * + * At least following fields should be set: + * + * - initial_max_stream_id_bidi + * - initial_max_stream_id_uni + * - initial_max_stream_data_bidi_local + * - initial_max_stream_data_bidi_remote + * - initial_max_stream_data_uni + * - initial_max_data + * - active_connection_id_limit + * - max_datagram_frame_size (if DATAGRAM extension was negotiated) + * + * The following fields are ignored: + * + * - ack_delay_exponent + * - max_ack_delay + * - initial_scid + * - original_dcid + * - preferred_address and preferred_address_present + * - retry_scid and retry_scid_present + * - stateless_reset_token and stateless_reset_token_present + */ +NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params_versioned( + ngtcp2_conn *conn, int transport_params_version, + const ngtcp2_transport_params *params); /** * @function @@ -3611,19 +4191,35 @@ NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params( * :macro:`NGTCP2_ERR_INVALID_STATE` * `ngtcp2_conn_install_tx_handshake_key` has been called. */ -NGTCP2_EXTERN int -ngtcp2_conn_set_local_transport_params(ngtcp2_conn *conn, - const ngtcp2_transport_params *params); +NGTCP2_EXTERN int ngtcp2_conn_set_local_transport_params_versioned( + ngtcp2_conn *conn, int transport_params_version, + const ngtcp2_transport_params *params); /** * @function * - * `ngtcp2_conn_get_local_transport_params` fills settings values in - * |params|. + * `ngtcp2_conn_get_local_transport_params` returns a pointer to the + * local QUIC transport parameters. */ -NGTCP2_EXTERN void -ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params); +NGTCP2_EXTERN const ngtcp2_transport_params * +ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_encode_local_transport_params` encodes the local QUIC + * transport parameters in |dest| of length |destlen|. This is + * equivalent to calling `ngtcp2_conn_get_local_transport_params` and + * then `ngtcp2_encode_transport_params`. + * + * This function returns the number of written, or one of the + * following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_encode_local_transport_params( + ngtcp2_conn *conn, uint8_t *dest, size_t destlen); /** * @function @@ -3636,7 +4232,7 @@ ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, * 0RTT packet, application can call this function after calling * `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet, * application can call this function after calling - * `ngtcp2_conn_set_remote_transport_params` and + * `ngtcp2_conn_decode_remote_transport_params` and * `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is * used, application can call this function after calling * `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet. @@ -3664,7 +4260,7 @@ NGTCP2_EXTERN int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, * 0RTT packet, application can call this function after calling * `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet, * application can call this function after calling - * `ngtcp2_conn_set_remote_transport_params` and + * `ngtcp2_conn_decode_remote_transport_params` and * `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is * used, application can call this function after calling * `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet. @@ -3698,8 +4294,6 @@ NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, * * :macro:`NGTCP2_ERR_NOMEM` * Out of memory - * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist */ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id, @@ -3720,8 +4314,6 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, * * :macro:`NGTCP2_ERR_NOMEM` * Out of memory - * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist */ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id, @@ -3741,8 +4333,6 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, * * :macro:`NGTCP2_ERR_NOMEM` * Out of memory - * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream does not exist */ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id, @@ -3759,7 +4349,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, * * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00 +#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00u /** * @macro @@ -3767,7 +4357,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` indicates that more data may * come and should be coalesced into the same packet if possible. */ -#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01 +#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01u /** * @macro @@ -3775,7 +4365,7 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, * :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` indicates that the passed * data is the final part of a stream. */ -#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02 +#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u /** * @function @@ -3784,10 +4374,11 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, * `ngtcp2_conn_writev_stream`. The only difference is that it * conveniently accepts a single buffer. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, - const uint8_t *data, size_t datalen, ngtcp2_tstamp ts); +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen, + ngtcp2_tstamp ts); /** * @function @@ -3797,6 +4388,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * pointed by |dest| of length |destlen|. This function performs QUIC * handshake as well. * + * |destlen| should be at least + * :member:`ngtcp2_settings.max_udp_payload_size`. + * * Specifying -1 to |stream_id| means no new stream data to send. * * If |path| is not ``NULL``, this function stores the network path @@ -3839,8 +4433,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * by default, application can only send 1 STREAM frame in one QUIC * packet. In order to include more than 1 STREAM frame in one QUIC * packet, specify :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|. - * This is analogous to ``MSG_MORE`` flag in ``send(2)``. If the - * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 + * This is analogous to ``MSG_MORE`` flag in :manpage:`send(2)`. If + * the :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 * outcomes: * * - The function returns the written length of packet just like @@ -3865,12 +4459,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not * call other ngtcp2 API functions (application can still call - * `ngtcp2_conn_write_connection_close` or - * `ngtcp2_conn_write_application_close` to handle error from this + * `ngtcp2_conn_write_connection_close` to handle error from this * function). Just keep calling `ngtcp2_conn_writev_stream`, * `ngtcp2_conn_write_pkt`, or `ngtcp2_conn_writev_datagram` until it * returns a positive number (which indicates a complete packet is - * ready). + * ready). If there is no stream data to include, call this function + * with |stream_id| as -1 to stop coalescing and write a packet. * * This function returns 0 if it cannot write any frame because buffer * is too small, or packet is congestion limited. Application should @@ -3879,6 +4473,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * This function must not be called from inside the callback * functions. * + * If pacing is enabled, `ngtcp2_conn_update_pkt_tx_time` must be + * called after this function. Application may call this function + * multiple times before calling `ngtcp2_conn_update_pkt_tx_time`. + * Packet pacing is enabled if BBR congestion controller algorithm is + * used. + * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: * @@ -3892,6 +4492,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * Packet number is exhausted, and cannot send any more packet. * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` * User callback failed + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * The total length of stream data is too large. * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` * Stream is blocked because of flow control. * :macro:`NGTCP2_ERR_WRITE_MORE` @@ -3906,10 +4508,11 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( * connection using `ngtcp2_conn_del`. It is undefined to call the * other library functions. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, - const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts); +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts); /** * @macrosection @@ -3922,7 +4525,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_NONE` indicates no flag set. */ -#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00 +#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00u /** * @macro @@ -3930,7 +4533,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` indicates that more data * may come and should be coalesced into the same packet if possible. */ -#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01 +#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u /** * @function @@ -3940,6 +4543,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * |dest| of length |destlen|. This function performs QUIC handshake * as well. * + * |destlen| should be at least + * :member:`ngtcp2_settings.max_udp_payload_size`. + * * For |path| and |pi| parameters, refer to * `ngtcp2_conn_writev_stream`. * @@ -3947,6 +4553,14 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * assigned to |*paccepted| if it is not NULL. The data in DATAGRAM * frame cannot be fragmented; writing partial data is not possible. * + * |dgram_id| is an opaque identifier which should uniquely identify + * the given DATAGRAM. It is passed to :type:`ngtcp2_ack_datagram` + * callback when a packet that contains DATAGRAM frame is + * acknowledged. It is passed to :type:`ngtcp2_lost_datagram` + * callback when a packet that contains DATAGRAM frame is declared + * lost. If an application uses neither of those callbacks, it can + * sets 0 to this parameter. + * * This function might write other frames other than DATAGRAM, just * like `ngtcp2_conn_writev_stream`. * @@ -3978,8 +4592,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not * call other ngtcp2 API functions (application can still call - * `ngtcp2_conn_write_connection_close` or - * `ngtcp2_conn_write_application_close` to handle error from this + * `ngtcp2_conn_write_connection_close` to handle error from this * function). Just keep calling `ngtcp2_conn_writev_datagram`, * `ngtcp2_conn_writev_stream` or `ngtcp2_conn_write_pkt` until it * returns a positive number (which indicates a complete packet is @@ -4011,96 +4624,17 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( * connection using `ngtcp2_conn_del`. It is undefined to call the * other library functions. */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, int *paccepted, uint32_t flags, const ngtcp2_vec *datav, - size_t datavcnt, ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_connection_close` writes a packet which contains - * a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed by - * |dest| whose capacity is |datalen|. - * - * If |path| is not ``NULL``, this function stores the network path - * with which the packet should be sent. Each addr field must point - * to the buffer which should be at least ``sizeof(struct - * sockaddr_storage)`` bytes long. The assignment might not be done - * if nothing is written to |dest|. - * - * If |pi| is not ``NULL``, this function stores packet metadata in it - * if it succeeds. The metadata includes ECN markings. - * - * This function must not be called from inside the callback - * functions. - * - * At the moment, successful call to this function makes connection - * close. We may change this behaviour in the future to allow - * graceful shutdown. - * - * :macro:`NGTCP2_ERR_NOMEM` - * Out of memory - * :macro:`NGTCP2_ERR_NOBUF` - * Buffer is too small - * :macro:`NGTCP2_ERR_INVALID_STATE` - * The current state does not allow sending CONNECTION_CLOSE. - * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, uint64_t error_code, ngtcp2_tstamp ts); - -/** - * @function - * - * `ngtcp2_conn_write_application_close` writes a packet which - * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed - * by |dest| whose capacity is |datalen|. - * - * If |path| is not ``NULL``, this function stores the network path - * with which the packet should be sent. Each addr field must point - * to the buffer which should be at least ``sizeof(struct - * sockaddr_storage)`` bytes long. The assignment might not be done - * if nothing is written to |dest|. - * - * If |pi| is not ``NULL``, this function stores packet metadata in it - * if it succeeds. The metadata includes ECN markings. - * - * If handshake has not been confirmed yet, CONNECTION_CLOSE (type - * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written - * instead. - * - * This function must not be called from inside the callback - * functions. - * - * At the moment, successful call to this function makes connection - * close. We may change this behaviour in the future to allow - * graceful shutdown. - * - * :macro:`NGTCP2_ERR_NOMEM` - * Out of memory - * :macro:`NGTCP2_ERR_NOBUF` - * Buffer is too small - * :macro:`NGTCP2_ERR_INVALID_STATE` - * The current state does not allow sending CONNECTION_CLOSE. - * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` - * Packet number is exhausted, and cannot send any more packet. - * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` - * User callback failed - */ -NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_application_close( - ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, uint64_t app_error_code, ngtcp2_tstamp ts); +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, + uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts); /** * @function * * `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in - * closing period. + * the closing period. */ NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn); @@ -4108,7 +4642,7 @@ NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn); * @function * * `ngtcp2_conn_is_in_draining_period` returns nonzero if |conn| is in - * draining period. + * the draining period. */ NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn); @@ -4121,8 +4655,8 @@ NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn); * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` - * Stream was not found + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. */ NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id, @@ -4175,6 +4709,16 @@ NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, */ NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_client_initial_dcid` returns the non-NULL pointer + * to the Destination Connection ID that client sent in its Initial + * packet. + */ +NGTCP2_EXTERN const ngtcp2_cid * +ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn); + /** * @function * @@ -4249,49 +4793,47 @@ NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, /** * @function * - * `ngtcp2_conn_get_negotiated_version` returns the negotiated version. + * `ngtcp2_conn_get_client_chosen_version` returns the client chosen + * version. */ -NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_early_data_rejected` tells |conn| that 0-RTT data was - * rejected by a server. + * `ngtcp2_conn_get_negotiated_version` returns the negotiated version. + * + * Until the version is negotiated, this function returns 0. */ -NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn); +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to - * |*cstat|. + * `ngtcp2_conn_early_data_rejected` tells |conn| that early data was + * rejected by a server. |conn| discards the following connection + * states: + * + * - Any opended streams. + * - Stream identifier allocations. + * - Max data extended by `ngtcp2_conn_extend_max_offset`. + * - Max bidi streams extended by `ngtcp2_conn_extend_max_streams_bidi`. + * - Max uni streams extended by `ngtcp2_conn_extend_max_streams_uni`. + * + * Application which wishes to retransmit early data, it has to open + * streams and send stream data again. */ -NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, - ngtcp2_conn_stat *cstat); +NGTCP2_EXTERN void ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_on_loss_detection_timer` should be called when a timer - * returned from `ngtcp2_conn_earliest_expiry` fires. - * - * Application should call `ngtcp2_conn_handshake` if handshake has - * not completed, otherwise `ngtcp2_conn_write_pkt` (or - * `ngtcp2_conn_write_stream` if it has data to send) to send PTO - * probe packets. - * - * This function must not be called from inside the callback - * functions. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :macro:`NGTCP2_ERR_NOMEM` - * Out of memory + * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to + * |*cstat|. */ -NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, - ngtcp2_tstamp ts); +NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn, + int conn_stat_version, + ngtcp2_conn_stat *cstat); /** * @function @@ -4300,9 +4842,8 @@ NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, * of length |datalen| to the library for transmission. The * encryption level is given in |crypto_level|. * - * Application should keep the buffer pointed by |data| alive until - * the data is acknowledged. The acknowledgement is notified by - * :type:`ngtcp2_acked_crypto_offset` callback. + * The library makes a copy of the buffer pointed by |data| of length + * |datalen|. Application can discard |data|. */ NGTCP2_EXTERN int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, @@ -4333,7 +4874,7 @@ NGTCP2_EXTERN int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, * @function * * `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to - * |conn|. + * the current path of |conn|. */ NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr); @@ -4341,11 +4882,11 @@ NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, /** * @function * - * `ngtcp2_conn_set_remote_addr` sets remote endpoint address |addr| - * to |conn|. + * `ngtcp2_conn_set_path_user_data` sets the |path_user_data| to the + * current path (see :member:`ngtcp2_path.user_data`). */ -NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, - const ngtcp2_addr *addr); +NGTCP2_EXTERN void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, + void *path_user_data); /** * @function @@ -4354,23 +4895,73 @@ NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, */ NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_max_udp_payload_size` returns the maximum UDP + * payload size that this local endpoint would send. This is the + * value of :member:`ngtcp2_settings.max_udp_payload_size` that is + * passed to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_path_max_udp_payload_size` returns the maximum UDP + * payload size for the current path. If + * :member:`ngtcp2_settings.no_udp_payload_size_shaping` is set to + * nonzero, this function is equivalent to + * `ngtcp2_conn_get_max_udp_payload_size`. Otherwise, it returns the + * maximum UDP payload size that is probed for the current path. + */ +NGTCP2_EXTERN size_t +ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_initiate_immediate_migration` starts connection + * migration to the given |path|. + * Only client can initiate migration. This function does + * immediate migration; it does not probe peer reachability from a new + * local address. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_STATE` + * Migration is disabled; or handshake is not yet confirmed; or + * client is migrating to server's preferred address. + * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` + * No unused connection ID is available. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * |local_addr| equals the current local address. + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + */ +NGTCP2_EXTERN int ngtcp2_conn_initiate_immediate_migration( + ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts); + /** * @function * * `ngtcp2_conn_initiate_migration` starts connection migration to the - * given |path| which must not be ``NULL``. Only client can initiate - * migration. This function does immediate migration; it does not - * probe peer reachability from a new local address. + * given |path|. Only client can initiate migration. Unlike + * `ngtcp2_conn_initiate_immediate_migration`, this function starts a + * path validation with a new path and migrate to the new path after + * successful path validation. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :macro:`NGTCP2_ERR_INVALID_STATE` - * Migration is disabled. + * Migration is disabled; or handshake is not yet confirmed; or + * client is migrating to server's preferred address. * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` * No unused connection ID is available. * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` - * |path| equals the current path. + * |local_addr| equals the current local address. * :macro:`NGTCP2_ERR_NOMEM` * Out of memory */ @@ -4394,6 +4985,16 @@ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn); */ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_max_stream_data_left` returns the number of bytes + * that this local endpoint can send to a stream identified by + * |stream_id|. If no such stream is found, this function returns 0. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn, + int64_t stream_id); + /** * @function * @@ -4412,6 +5013,15 @@ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn); */ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_cwnd_left` returns the cwnd minus the number of + * bytes in flight on the current path. If the former is smaller than + * the latter, this function returns 0. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn); + /** * @function * @@ -4436,7 +5046,7 @@ ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/Short packet + * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/1RTT packet * encryption. The passed data will be passed to * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and * :type:`ngtcp2_hp_mask` callbacks. @@ -4485,7 +5095,7 @@ ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead, * @function * * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx` - * object for Handshake/Short packet encryption. + * object for Handshake/1RTT packet encryption. */ NGTCP2_EXTERN const ngtcp2_crypto_ctx * ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn); @@ -4528,33 +5138,213 @@ typedef enum ngtcp2_connection_close_error_code_type { * indicates the error code is application error code. */ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, + /** + * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION` + * is a special case of QUIC transport error, and it indicates that + * client receives Version Negotiation packet. + */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION, + /** + * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE` + * is a special case of QUIC transport error, and it indicates that + * connection is closed because of idle timeout. + */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE } ngtcp2_connection_close_error_code_type; /** * @struct * - * `ngtcp2_connection_close_error_code` contains connection error code - * and its type. + * :type:`ngtcp2_connection_close_error` contains connection + * error code, its type, and the optional reason phrase. */ -typedef struct ngtcp2_connection_close_error_code { +typedef struct ngtcp2_connection_close_error { + /** + * :member:`type` is the type of :member:`error_code`. + */ + ngtcp2_connection_close_error_code_type type; /** * :member:`error_code` is the error code for connection closure. */ uint64_t error_code; /** - * :member:`type` is the type of :member:`error_code`. + * :member:`frame_type` is the type of QUIC frame which triggers + * this connection error. This field is set to 0 if the frame type + * is unknown. */ - ngtcp2_connection_close_error_code_type type; -} ngtcp2_connection_close_error_code; + uint64_t frame_type; + /** + * :member:`reason` points to the buffer which contains a reason + * phrase. It may be NULL if there is no reason phrase. If it is + * received from a remote endpoint, it is truncated to at most 1024 + * bytes. + */ + uint8_t *reason; + /** + * :member:`reasonlen` is the length of data pointed by + * :member:`reason`. + */ + size_t reasonlen; +} ngtcp2_connection_close_error; + +/** + * @function + * + * `ngtcp2_connection_close_error_default` initializes |ccerr| with + * the default values. It sets the following fields: + * + * - :member:`type ` = + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT` + * - :member:`error_code ` = + * :macro:`NGTCP2_NO_ERROR`. + * - :member:`frame_type ` = + * 0 + * - :member:`reason ` = NULL + * - :member:`reasonlen ` = 0 + */ +NGTCP2_EXTERN void +ngtcp2_connection_close_error_default(ngtcp2_connection_close_error *ccerr); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error` sets + * :member:`ccerr->type ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * ` to |error_code|. + * |reason| is the reason phrase of length |reasonlen|. This function + * does not make a copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error_liberr` sets + * type and error_code based on |liberr|. + * + * If |liberr| is :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION`, + * :member:`ccerr->type ` is set + * to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`, + * and :member:`ccerr->error_code + * ` to + * :macro:`NGTCP2_NO_ERROR`. If |liberr| is + * :macro:`NGTCP2_ERR_IDLE_CLOSE`, :member:`ccerr->type + * ` is set to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`, + * and :member:`ccerr->error_code + * ` to + * :macro:`NGTCP2_NO_ERROR`. Otherwise, :member:`ccerr->type + * ` is set to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * ` is set to an error code + * inferred by |liberr| (see + * `ngtcp2_err_infer_quic_transport_error_code`). |reason| is the + * reason phrase of length |reasonlen|. This function does not make a + * copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_liberr( + ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason, + size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_transport_error_tls_alert` sets + * :member:`ccerr->type ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->error_code + * ` to bitwise-OR of + * :macro:`NGTCP2_CRYPTO_ERROR` and |tls_alert|. |reason| is the + * reason phrase of length |reasonlen|. This function does not make a + * copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_tls_alert( + ngtcp2_connection_close_error *ccerr, uint8_t tls_alert, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_connection_close_error_set_application_error` sets + * :member:`ccerr->type ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`, + * and :member:`ccerr->error_code + * ` to |error_code|. + * |reason| is the reason phrase of length |reasonlen|. This function + * does not make a copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_connection_close_error_set_application_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close` writes a packet which contains + * CONNECTION_CLOSE frame(s) (type 0x1c or 0x1d) in the buffer pointed + * by |dest| whose capacity is |destlen|. + * + * For client, |destlen| should be at least + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * If :member:`ccerr->type ` == + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * this function sends CONNECTION_CLOSE (type 0x1c) frame. If + * :member:`ccerr->type ` == + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`, + * it sends CONNECTION_CLOSE (type 0x1d) frame. Otherwise, it does + * not produce any data, and returns 0. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts); /** * @function * - * `ngtcp2_conn_get_connection_close_error_code` stores the received - * connection close error code in |ccec|. + * `ngtcp2_conn_get_connection_close_error` stores the received + * connection close error in |ccerr|. */ -NGTCP2_EXTERN void ngtcp2_conn_get_connection_close_error_code( - ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec); +NGTCP2_EXTERN void +ngtcp2_conn_get_connection_close_error(ngtcp2_conn *conn, + ngtcp2_connection_close_error *ccerr); /** * @function @@ -4597,13 +5387,48 @@ NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, void *stream_user_data); +/** + * @function + * + * `ngtcp2_conn_update_pkt_tx_time` sets the time instant of the next + * packet transmission. This function is noop if packet pacing is + * disabled. If packet pacing is enabled, this function must be + * called after (multiple invocation of) `ngtcp2_conn_writev_stream`. + * If packet aggregation (e.g., packet batching, GSO) is used, call + * this function after all aggregated datagrams are sent, which + * indicates multiple invocation of `ngtcp2_conn_writev_stream`. + */ +NGTCP2_EXTERN void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_send_quantum` returns the maximum number of bytes + * that can be sent in one go without packet spacing. If packet + * pacing is disabled, this function returns SIZE_MAX. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_stream_loss_count` returns the number of packets + * that contain STREAM frame for a stream identified by |stream_id| + * and are declared to be lost. The number may include the spurious + * losses. If no stream identified by |stream_id| is found, this + * function returns 0. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, + int64_t stream_id); + /** * @function * * `ngtcp2_strerror` returns the text representation of |liberr|. * |liberr| must be one of ngtcp2 library error codes (which is * defined as NGTCP2_ERR_* macro, such as - * :macro:`NGTCP2_ERR_TLS_DECRYPT`). + * :macro:`NGTCP2_ERR_DECRYPT`). */ NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr); @@ -4613,7 +5438,7 @@ NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr); * `ngtcp2_err_is_fatal` returns nonzero if |liberr| is a fatal error. * |liberr| must be one of ngtcp2 library error codes (which is * defined as NGTCP2_ERR_* macro, such as - * :macro:`NGTCP2_ERR_TLS_DECRYPT`). + * :macro:`NGTCP2_ERR_DECRYPT`). */ NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr); @@ -4623,7 +5448,7 @@ NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr); * `ngtcp2_err_infer_quic_transport_error_code` returns a QUIC * transport error code which corresponds to |liberr|. |liberr| must * be one of ngtcp2 library error codes (which is defined as - * NGTCP2_ERR_* macro, such as :macro:`NGTCP2_ERR_TLS_DECRYPT`). + * NGTCP2_ERR_* macro, such as :macro:`NGTCP2_ERR_DECRYPT`). */ NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr); @@ -4634,8 +5459,22 @@ NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr); * returns |dest|. */ NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, - const struct sockaddr *addr, - size_t addrlen, void *user_data); + const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen); + +/** + * @function + * + * `ngtcp2_addr_copy_byte` copies |addr| of length |addrlen| into the + * buffer pointed by :member:`dest->addr `. + * :member:`dest->addrlen ` is updated to have + * |addrlen|. This function assumes that :member:`dest->addr + * ` points to a buffer which has a sufficient + * capacity to store the copy. + */ +NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, + const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen); /** * @function @@ -4644,12 +5483,11 @@ NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, * arguments. This function copies |local_addr| and |remote_addr|. */ NGTCP2_EXTERN void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, - const struct sockaddr *local_addr, - size_t local_addrlen, - void *local_user_data, - const struct sockaddr *remote_addr, - size_t remote_addrlen, - void *remote_user_data); + const ngtcp2_sockaddr *local_addr, + ngtcp2_socklen local_addrlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, + void *user_data); /** * @function @@ -4671,8 +5509,13 @@ NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps); * * :type:`initial_rtt ` = * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` * * :type:`ack_thresh ` = 2 + * * :type:`max_udp_payload_size + * ` = 1452 + * * :type:`handshake_timeout ` = + * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT`. */ -NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); +NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version, + ngtcp2_settings *settings); /** * @function @@ -4683,7 +5526,7 @@ NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); * * * :type:`max_udp_payload_size * ` = - * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE` + * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` * * :type:`ack_delay_exponent * ` = * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` @@ -4694,7 +5537,8 @@ NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT` */ NGTCP2_EXTERN void -ngtcp2_transport_params_default(ngtcp2_transport_params *params); +ngtcp2_transport_params_default_versioned(int transport_params_version, + ngtcp2_transport_params *params); /** * @function @@ -4746,13 +5590,14 @@ typedef struct ngtcp2_info { /** * @function * - * Returns a pointer to a ngtcp2_info struct with version information - * about the run-time library in use. The |least_version| argument - * can be set to a 24 bit numerical value for the least accepted - * version number and if the condition is not met, this function will - * return a ``NULL``. Pass in 0 to skip the version checking. + * `ngtcp2_version` returns a pointer to a ngtcp2_info struct with + * version information about the run-time library in use. The + * |least_version| argument can be set to a 24 bit numerical value for + * the least accepted version number and if the condition is not met, + * this function will return a ``NULL``. Pass in 0 to skip the + * version checking. */ -NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version); +NGTCP2_EXTERN const ngtcp2_info *ngtcp2_version(int least_version); /** * @function @@ -4763,66 +5608,211 @@ NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version); NGTCP2_EXTERN int ngtcp2_is_bidi_stream(int64_t stream_id); /** - * @enum + * @function * - * :type:`ngtcp2_log_event` defines an event of ngtcp2 library - * internal logger. + * `ngtcp2_path_copy` copies |src| into |dest|. This function assumes + * that |dest| has enough buffer to store the deep copy of + * :member:`src->local ` and :member:`src->remote + * `. */ -typedef enum { - /** - * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event. - */ - NGTCP2_LOG_EVENT_NONE, - /** - * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event - */ - NGTCP2_LOG_EVENT_CON, - /** - * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event. - */ - NGTCP2_LOG_EVENT_PKT, - /** - * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event. - */ - NGTCP2_LOG_EVENT_FRM, - /** - * :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event. - */ - NGTCP2_LOG_EVENT_RCV, - /** - * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event. - */ - NGTCP2_LOG_EVENT_CRY, - /** - * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event. - */ - NGTCP2_LOG_EVENT_PTV, -} ngtcp2_log_event; +NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); /** * @function * - * `ngtcp2_log_info` writes info level log. + * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same + * local and remote addresses. */ -NGTCP2_EXTERN void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, - const char *fmt, ...); +NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); /** * @function * - * `ngtcp2_path_copy` copies |src| into |dest|. This function assumes - * that |dest| has enough buffer to store the deep copy of src->local - * and src->remote. + * `ngtcp2_is_supported_version` returns nonzero if the library supports + * QUIC version |version|. */ -NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); +NGTCP2_EXTERN int ngtcp2_is_supported_version(uint32_t version); + +/* + * @function + * + * `ngtcp2_is_reserved_version` returns nonzero if |version| is a + * reserved version. + */ +NGTCP2_EXTERN int ngtcp2_is_reserved_version(uint32_t version); /** * @function * - * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same - * local and remote addresses. + * `ngtcp2_select_version` selects and returns a version from the + * version set |offered_versions| of |offered_versionslen| elements. + * |preferred_versions| of |preferred_versionslen| elements specifies + * the preference of versions, which is sorted in the order of + * preference. All versions included in |preferred_versions| must be + * supported by the library, that is, passing a version to + * `ngtcp2_is_supported_version` must return nonzero. This function + * is intended to be used by client when it receives Version + * Negotiation packet. If no version is selected, this function + * returns 0. */ -NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); +NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + const uint32_t *offered_versions, + size_t offered_versionslen); + +/* + * Versioned function wrappers + */ + +/* + * `ngtcp2_conn_read_pkt` is a wrapper around + * `ngtcp2_conn_read_pkt_versioned` to set the correct struct version. + */ +#define ngtcp2_conn_read_pkt(CONN, PATH, PI, PKT, PKTLEN, TS) \ + ngtcp2_conn_read_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION, \ + (PI), (PKT), (PKTLEN), (TS)) + +/* + * `ngtcp2_conn_write_pkt` is a wrapper around + * `ngtcp2_conn_write_pkt_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_write_pkt(CONN, PATH, PI, DEST, DESTLEN, TS) \ + ngtcp2_conn_write_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION, \ + (PI), (DEST), (DESTLEN), (TS)) + +/* + * `ngtcp2_conn_write_stream` is a wrapper around + * `ngtcp2_conn_write_stream_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_write_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN, \ + FLAGS, STREAM_ID, DATA, DATALEN, TS) \ + ngtcp2_conn_write_stream_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ + (PDATALEN), (FLAGS), (STREAM_ID), (DATA), (DATALEN), (TS)) + +/* + * `ngtcp2_conn_writev_stream` is a wrapper around + * `ngtcp2_conn_writev_stream_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_writev_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN, \ + FLAGS, STREAM_ID, DATAV, DATAVCNT, TS) \ + ngtcp2_conn_writev_stream_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ + (PDATALEN), (FLAGS), (STREAM_ID), (DATAV), (DATAVCNT), (TS)) + +/* + * `ngtcp2_conn_writev_datagram` is a wrapper around + * `ngtcp2_conn_writev_datagram_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_writev_datagram(CONN, PATH, PI, DEST, DESTLEN, PACCEPTED, \ + FLAGS, DGRAM_ID, DATAV, DATAVCNT, TS) \ + ngtcp2_conn_writev_datagram_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ + (PACCEPTED), (FLAGS), (DGRAM_ID), (DATAV), (DATAVCNT), (TS)) + +/* + * `ngtcp2_conn_write_connection_close` is a wrapper around + * `ngtcp2_conn_write_connection_close_versioned` to set the correct + * struct version. + */ +#define ngtcp2_conn_write_connection_close(CONN, PATH, PI, DEST, DESTLEN, \ + CCERR, TS) \ + ngtcp2_conn_write_connection_close_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \ + (CCERR), (TS)) + +/* + * `ngtcp2_encode_transport_params` is a wrapper around + * `ngtcp2_encode_transport_params_versioned` to set the correct + * struct version. + */ +#define ngtcp2_encode_transport_params(DEST, DESTLEN, EXTTYPE, PARAMS) \ + ngtcp2_encode_transport_params_versioned( \ + (DEST), (DESTLEN), (EXTTYPE), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS)) + +/* + * `ngtcp2_decode_transport_params` is a wrapper around + * `ngtcp2_decode_transport_params_versioned` to set the correct + * struct version. + */ +#define ngtcp2_decode_transport_params(PARAMS, EXTTYPE, DATA, DATALEN) \ + ngtcp2_decode_transport_params_versioned( \ + NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (EXTTYPE), (DATA), (DATALEN)) + +/* + * `ngtcp2_conn_client_new` is a wrapper around + * `ngtcp2_conn_client_new_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_client_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS, \ + SETTINGS, PARAMS, MEM, USER_DATA) \ + ngtcp2_conn_client_new_versioned( \ + (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION, \ + (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS), \ + NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA)) + +/* + * `ngtcp2_conn_server_new` is a wrapper around + * `ngtcp2_conn_server_new_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_server_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS, \ + SETTINGS, PARAMS, MEM, USER_DATA) \ + ngtcp2_conn_server_new_versioned( \ + (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION, \ + (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS), \ + NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA)) + +/* + * `ngtcp2_conn_set_early_remote_transport_params` is a wrapper around + * `ngtcp2_conn_set_early_remote_transport_params_versioned` to set + * the correct struct version. + */ +#define ngtcp2_conn_set_early_remote_transport_params(CONN, PARAMS) \ + ngtcp2_conn_set_early_remote_transport_params_versioned( \ + (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS)) + +/* + * `ngtcp2_conn_set_local_transport_params` is a wrapper around + * `ngtcp2_conn_set_local_transport_params_versioned` to set the + * correct struct version. + */ +#define ngtcp2_conn_set_local_transport_params(CONN, PARAMS) \ + ngtcp2_conn_set_local_transport_params_versioned( \ + (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS)) + +/* + * `ngtcp2_transport_params_default` is a wrapper around + * `ngtcp2_transport_params_default_versioned` to set the correct + * struct version. + */ +#define ngtcp2_transport_params_default(PARAMS) \ + ngtcp2_transport_params_default_versioned(NGTCP2_TRANSPORT_PARAMS_VERSION, \ + (PARAMS)) + +/* + * `ngtcp2_conn_get_conn_stat` is a wrapper around + * `ngtcp2_conn_get_conn_stat_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_get_conn_stat(CONN, CSTAT) \ + ngtcp2_conn_get_conn_stat_versioned((CONN), NGTCP2_CONN_STAT_VERSION, (CSTAT)) + +/* + * `ngtcp2_settings_default` is a wrapper around + * `ngtcp2_settings_default_versioned` to set the correct struct + * version. + */ +#define ngtcp2_settings_default(SETTINGS) \ + ngtcp2_settings_default_versioned(NGTCP2_SETTINGS_VERSION, (SETTINGS)) + +#ifdef _MSC_VER +# pragma warning(pop) +#endif #ifdef __cplusplus } diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h index fb0671de9fcb5d..9f7592b84a4585 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h @@ -36,7 +36,7 @@ * * Version number of the ngtcp2 library release. */ -#define NGTCP2_VERSION "0.1.0-DEV" +#define NGTCP2_VERSION "0.8.1" /** * @macro @@ -46,6 +46,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGTCP2_VERSION_NUM 0x000100 +#define NGTCP2_VERSION_NUM 0x000801 #endif /* VERSION_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c index 7a7f3e469a2f0e..02c45de90d112a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c @@ -26,22 +26,31 @@ #include -int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, - ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) { - *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry)); +#include "ngtcp2_macro.h" + +static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num, + ngtcp2_tstamp tstamp) { + ent->pkt_num = pkt_num; + ent->len = 1; + ent->tstamp = tstamp; +} + +int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, + ngtcp2_objalloc *objalloc) { + *ent = ngtcp2_objalloc_acktr_entry_get(objalloc); if (*ent == NULL) { return NGTCP2_ERR_NOMEM; } - (*ent)->pkt_num = pkt_num; - (*ent)->len = 1; - (*ent)->tstamp = tstamp; + acktr_entry_init(*ent, pkt_num, tstamp); return 0; } -void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, ent); +void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent, + ngtcp2_objalloc *objalloc) { + ngtcp2_objalloc_acktr_entry_release(objalloc, ent); } static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { @@ -52,17 +61,15 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, const ngtcp2_mem *mem) { int rv; - rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry), + ngtcp2_objalloc_acktr_entry_init(&acktr->objalloc, 32, mem); + + rv = ngtcp2_ringbuf_init(&acktr->acks, 32, sizeof(ngtcp2_acktr_ack_entry), mem); if (rv != 0) { return rv; } - rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); - if (rv != 0) { - ngtcp2_ringbuf_free(&acktr->acks); - return rv; - } + ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); acktr->log = log; acktr->mem = mem; @@ -74,24 +81,31 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, } void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { +#ifdef NOMEMPOOL ngtcp2_ksl_it it; +#endif /* NOMEMPOOL */ if (acktr == NULL) { return; } +#ifdef NOMEMPOOL for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ngtcp2_ksl_it_get(&it), &acktr->objalloc); } +#endif /* NOMEMPOOL */ + ngtcp2_ksl_free(&acktr->ents); ngtcp2_ringbuf_free(&acktr->acks); + + ngtcp2_objalloc_free(&acktr->objalloc); } int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, ngtcp2_tstamp ts) { - ngtcp2_ksl_it it; + ngtcp2_ksl_it it, prev_it; ngtcp2_acktr_entry *ent, *prev_ent, *delent; int rv; int added = 0; @@ -122,16 +136,17 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, added = 1; } } else { - ngtcp2_ksl_it_prev(&it); - prev_ent = ngtcp2_ksl_it_get(&it); + prev_it = it; + ngtcp2_ksl_it_prev(&prev_it); + prev_ent = ngtcp2_ksl_it_get(&prev_it); assert(prev_ent->pkt_num >= pkt_num + (int64_t)prev_ent->len); if (ent->pkt_num + 1 == pkt_num) { if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { prev_ent->len += ent->len + 1; - ngtcp2_ksl_remove(&acktr->ents, NULL, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &ent->pkt_num); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); added = 1; } else { ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); @@ -149,13 +164,13 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, } if (!added) { - rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem); + rv = ngtcp2_acktr_entry_objalloc_new(&ent, pkt_num, ts, &acktr->objalloc); if (rv != 0) { return rv; } rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent); if (rv != 0) { - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); return rv; } } @@ -171,8 +186,8 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, it = ngtcp2_ksl_end(&acktr->ents); ngtcp2_ksl_it_prev(&it); delent = ngtcp2_ksl_it_get(&it); - ngtcp2_ksl_remove(&acktr->ents, NULL, &delent->pkt_num); - ngtcp2_acktr_entry_del(delent, acktr->mem); + ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &delent->pkt_num); + ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc); } return 0; @@ -186,8 +201,8 @@ void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) { for (; !ngtcp2_ksl_it_end(&it);) { ent = ngtcp2_ksl_it_get(&it); - ngtcp2_ksl_remove(&acktr->ents, &it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_ksl_remove_hint(&acktr->ents, &it, &it, &ent->pkt_num); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); } } @@ -212,13 +227,14 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, } /* - * acktr_remove removes |ent| from |acktr|. The iterator which points - * to the entry next to |ent| is assigned to |it|. + * acktr_remove removes |ent| from |acktr|. |it| must point to the + * node whose key identifies |ent|. The iterator which points to the + * entry next to |ent| is assigned to |it|. */ static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it, ngtcp2_acktr_entry *ent) { - ngtcp2_ksl_remove(&acktr->ents, it, &ent->pkt_num); - ngtcp2_acktr_entry_del(ent, acktr->mem); + ngtcp2_ksl_remove_hint(&acktr->ents, it, it, &ent->pkt_num); + ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); } static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb, @@ -310,7 +326,8 @@ void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) { int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, ngtcp2_duration max_ack_delay, ngtcp2_tstamp ts) { - return acktr->first_unacked_ts <= ts - max_ack_delay; + return acktr->first_unacked_ts != UINT64_MAX && + acktr->first_unacked_ts + max_ack_delay <= ts; } void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h index 51e1588e3c4a8f..1b00d64fe62cb6 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h @@ -35,6 +35,7 @@ #include "ngtcp2_ringbuf.h" #include "ngtcp2_ksl.h" #include "ngtcp2_pkt.h" +#include "ngtcp2_objalloc.h" /* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry which ngtcp2_acktr stores. */ @@ -46,22 +47,30 @@ typedef struct ngtcp2_log ngtcp2_log; * ngtcp2_acktr_entry is a range of packets which need to be acked. */ typedef struct ngtcp2_acktr_entry { - /* pkt_num is the largest packet number to acknowledge in this - range. */ - int64_t pkt_num; - /* len is the consecutive packets started from pkt_num which - includes pkt_num itself counting in decreasing order. So pkt_num - = 987 and len = 2, this entry includes packet 987 and 986. */ - size_t len; - /* tstamp is the timestamp when a packet denoted by pkt_num is - received. */ - ngtcp2_tstamp tstamp; + union { + struct { + /* pkt_num is the largest packet number to acknowledge in this + range. */ + int64_t pkt_num; + /* len is the consecutive packets started from pkt_num which + includes pkt_num itself counting in decreasing order. So pkt_num + = 987 and len = 2, this entry includes packet 987 and 986. */ + size_t len; + /* tstamp is the timestamp when a packet denoted by pkt_num is + received. */ + ngtcp2_tstamp tstamp; + }; + + ngtcp2_opl_entry oplent; + }; } ngtcp2_acktr_entry; +ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent); + /* - * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it - * with the given parameters. The pointer to the allocated object is - * stored to |*ent|. + * ngtcp2_acktr_entry_objalloc_new allocates memory for ent, and + * initializes it with the given parameters. The pointer to the + * allocated object is stored to |*ent|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -69,14 +78,16 @@ typedef struct ngtcp2_acktr_entry { * NGTCP2_ERR_NOMEM * Out of memory. */ -int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, - ngtcp2_tstamp tstamp, const ngtcp2_mem *mem); +int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, + ngtcp2_objalloc *objalloc); /* - * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It - * deallocates memory pointed by |ent|. + * ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for + * |ent|. */ -void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem); +void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent, + ngtcp2_objalloc *objalloc); typedef struct ngtcp2_acktr_ack_entry { /* largest_ack is the largest packet number in outgoing ACK frame */ @@ -86,25 +97,22 @@ typedef struct ngtcp2_acktr_ack_entry { } ngtcp2_acktr_ack_entry; /* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */ -#define NGTCP2_ACKTR_FLAG_NONE 0x00 +#define NGTCP2_ACKTR_FLAG_NONE 0x00u /* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate acknowledgement is required. */ -#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01 +#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01u /* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending protected packet to be acknowledged. */ -#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02 -/* 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). */ -#define NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK 0x80 +#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02u /* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is expired and canceled. */ -#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100 +#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100u /* * ngtcp2_acktr tracks received packets which we have to send ack. */ typedef struct ngtcp2_acktr { + ngtcp2_objalloc objalloc; ngtcp2_ringbuf acks; /* ents includes ngtcp2_acktr_entry sorted by decreasing order of packet number. */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c index 22af219a9e1d75..daab5dd7ce664b 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c @@ -27,55 +27,41 @@ #include #include -#ifdef WIN32 -# include -# include -#else -# include -# include -# include -# include -# include -# include -#endif - -ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen, void *user_data) { +ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen) { dest->addrlen = addrlen; - dest->addr = (struct sockaddr *)addr; - dest->user_data = user_data; + dest->addr = (ngtcp2_sockaddr *)addr; return dest; } void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) { dest->addrlen = src->addrlen; if (src->addrlen) { - memcpy(dest->addr, src->addr, src->addrlen); + memcpy(dest->addr, src->addr, (size_t)src->addrlen); } - dest->user_data = src->user_data; } -void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen) { +void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, + ngtcp2_socklen addrlen) { dest->addrlen = addrlen; if (addrlen) { - memcpy(dest->addr, addr, addrlen); + memcpy(dest->addr, addr, (size_t)addrlen); } } -static int sockaddr_eq(const struct sockaddr *a, const struct sockaddr *b) { +static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) { assert(a->sa_family == b->sa_family); switch (a->sa_family) { - case AF_INET: { - const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, - *bi = (const struct sockaddr_in *)(void *)b; + case NGTCP2_AF_INET: { + const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, + *bi = (const ngtcp2_sockaddr_in *)(void *)b; return ai->sin_port == bi->sin_port && memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0; } - case AF_INET6: { - const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, - *bi = (const struct sockaddr_in6 *)(void *)b; + case NGTCP2_AF_INET6: { + const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, + *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; return ai->sin6_port == bi->sin6_port && memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0; } @@ -92,17 +78,17 @@ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) { uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE; - const struct sockaddr *a = aa->addr; - const struct sockaddr *b = bb->addr; + const ngtcp2_sockaddr *a = aa->addr; + const ngtcp2_sockaddr *b = bb->addr; if (a->sa_family != b->sa_family) { return NGTCP2_ADDR_COMPARE_FLAG_FAMILY; } switch (a->sa_family) { - case AF_INET: { - const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, - *bi = (const struct sockaddr_in *)(void *)b; + case NGTCP2_AF_INET: { + const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, + *bi = (const ngtcp2_sockaddr_in *)(void *)b; if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) { flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; } @@ -111,9 +97,9 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { } return flags; } - case AF_INET6: { - const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, - *bi = (const struct sockaddr_in6 *)(void *)b; + case NGTCP2_AF_INET6: { + const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, + *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) { flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h index 84c0c01ddbf2db..f1d7f7bdc70ea9 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h @@ -38,30 +38,21 @@ */ void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src); -/* - * ngtcp2_addr_copy_byte copies |addr| of length |addrlen| into the - * buffer pointed by dest->addr. dest->len is updated to have - * |addrlen|. This function assumes that dest->addr points to a - * buffer which have sufficient size to store the copy. - */ -void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, - size_t addrlen); - /* * ngtcp2_addr_eq returns nonzero if |a| equals |b|. */ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b); /* NGTCP2_ADDR_COMPARE_FLAG_NONE indicates that no flag set. */ -#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0 +#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0u /* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1 +#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1u /* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2 +#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2u /* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4 +#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4u /* * ngtcp2_addr_compare compares address and port between |a| and |b|, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c new file mode 100644 index 00000000000000..5cc39ee3b03da4 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.c @@ -0,0 +1,90 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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. + */ +#include "ngtcp2_balloc.h" + +#include + +#include "ngtcp2_mem.h" + +void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen, + const ngtcp2_mem *mem) { + assert((blklen & 0xfu) == 0); + + balloc->mem = mem; + balloc->blklen = blklen; + balloc->head = NULL; + ngtcp2_buf_init(&balloc->buf, (void *)"", 0); +} + +void ngtcp2_balloc_free(ngtcp2_balloc *balloc) { + if (balloc == NULL) { + return; + } + + ngtcp2_balloc_clear(balloc); +} + +void ngtcp2_balloc_clear(ngtcp2_balloc *balloc) { + ngtcp2_memblock_hd *p, *next; + + for (p = balloc->head; p; p = next) { + next = p->next; + ngtcp2_mem_free(balloc->mem, p); + } + + balloc->head = NULL; + ngtcp2_buf_init(&balloc->buf, (void *)"", 0); +} + +int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) { + uint8_t *p; + ngtcp2_memblock_hd *hd; + + assert(n <= balloc->blklen); + + if (ngtcp2_buf_left(&balloc->buf) < n) { + p = ngtcp2_mem_malloc(balloc->mem, + sizeof(ngtcp2_memblock_hd) + 0x10u + balloc->blklen); + if (p == NULL) { + return NGTCP2_ERR_NOMEM; + } + + hd = (ngtcp2_memblock_hd *)(void *)p; + hd->next = balloc->head; + balloc->head = hd; + ngtcp2_buf_init( + &balloc->buf, + (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfu) & + ~(uintptr_t)0xfu), + balloc->blklen); + } + + assert(((uintptr_t)balloc->buf.last & 0xfu) == 0); + + *pbuf = balloc->buf.last; + balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu; + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h new file mode 100644 index 00000000000000..1fb16325d9953d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_balloc.h @@ -0,0 +1,91 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 NGTCP2_BALLOC_H +#define NGTCP2_BALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_buf.h" + +typedef struct ngtcp2_memblock_hd ngtcp2_memblock_hd; + +/* + * ngtcp2_memblock_hd is the header of memory block. + */ +struct ngtcp2_memblock_hd { + ngtcp2_memblock_hd *next; +}; + +/* + * ngtcp2_balloc is a custom memory allocator. It allocates |blklen| + * bytes of memory at once on demand, and returns its slice when the + * allocation is requested. + */ +typedef struct ngtcp2_balloc { + /* mem is the underlying memory allocator. */ + const ngtcp2_mem *mem; + /* blklen is the size of memory block. */ + size_t blklen; + /* head points to the list of memory block allocated so far. */ + ngtcp2_memblock_hd *head; + /* buf wraps the current memory block for allocation requests. */ + ngtcp2_buf buf; +} ngtcp2_balloc; + +/* + * ngtcp2_balloc_init initializes |balloc| with |blklen| which is the + * size of memory block. + */ +void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_balloc_free releases all allocated memory blocks. + */ +void ngtcp2_balloc_free(ngtcp2_balloc *balloc); + +/* + * ngtcp2_balloc_get allocates |n| bytes of memory and assigns its + * pointer to |*pbuf|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n); + +/* + * ngtcp2_balloc_clear releases all allocated memory blocks and + * initializes its state. + */ +void ngtcp2_balloc_clear(ngtcp2_balloc *balloc); + +#endif /* NGTCP2_BALLOC_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c new file mode 100644 index 00000000000000..0816d69b816c52 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c @@ -0,0 +1,692 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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. + */ +#include "ngtcp2_bbr.h" + +#include + +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_rst.h" + +static const double pacing_gain_cycle[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1}; + +#define NGTCP2_BBR_GAIN_CYCLELEN \ + (sizeof(pacing_gain_cycle) / sizeof(pacing_gain_cycle[0])) + +#define NGTCP2_BBR_HIGH_GAIN 2.89 +#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS) +#define NGTCP2_RTPROP_FILTERLEN (10 * NGTCP2_SECONDS) +#define NGTCP2_BBR_BTL_BW_FILTERLEN 10 + +static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); +static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); +static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); +static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_init_round_counting(ngtcp2_bbr_cc *cc); +static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack); +static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); +static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); +static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + double pacing_gain); +static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); +static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat); +static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat); +static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); +static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp initial_ts); +static void bbr_enter_startup(ngtcp2_bbr_cc *cc); +static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc); +static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc); +static void bbr_enter_drain(ngtcp2_bbr_cc *cc); +static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); +static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts); +static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); +static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts); +static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); +static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat); +static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); +static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc); +static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); +static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts); + +void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_rst *rst, ngtcp2_tstamp initial_ts, + ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx, + ngtcp2_log *log) { + cc->ccb.log = log; + cc->rst = rst; + cc->rand = rand; + cc->rand_ctx = *rand_ctx; + cc->initial_cwnd = cstat->cwnd; + bbr_init(cc, cstat, initial_ts); +} + +void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc) { (void)cc; } + +int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, + ngtcp2_tstamp initial_ts, ngtcp2_rand rand, + const ngtcp2_rand_ctx *rand_ctx, + const ngtcp2_mem *mem) { + ngtcp2_bbr_cc *bbr_cc; + + bbr_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr_cc)); + if (bbr_cc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_bbr_cc_init(bbr_cc, cstat, rst, initial_ts, rand, rand_ctx, log); + + cc->ccb = &bbr_cc->ccb; + cc->on_pkt_acked = ngtcp2_cc_bbr_cc_on_pkt_acked; + cc->congestion_event = ngtcp2_cc_bbr_cc_congestion_event; + cc->on_spurious_congestion = ngtcp2_cc_bbr_cc_on_spurious_congestion; + cc->on_persistent_congestion = ngtcp2_cc_bbr_cc_on_persistent_congestion; + cc->on_ack_recv = ngtcp2_cc_bbr_cc_on_ack_recv; + cc->on_pkt_sent = ngtcp2_cc_bbr_cc_on_pkt_sent; + cc->new_rtt_sample = ngtcp2_cc_bbr_cc_new_rtt_sample; + cc->reset = ngtcp2_cc_bbr_cc_reset; + cc->event = ngtcp2_cc_bbr_cc_event; + + return 0; +} + +void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) { + ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr_cc, ccb); + + ngtcp2_bbr_cc_free(bbr_cc); + ngtcp2_mem_free(mem, bbr_cc); +} + +void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)pkt; + (void)ts; +} + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_time) { + return cstat->congestion_recovery_start_ts != UINT64_MAX && + sent_time <= cstat->congestion_recovery_start_ts; +} + +void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_ts, + ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + + if (cc->in_loss_recovery || cc->congestion_recovery_start_ts != UINT64_MAX || + in_congestion_recovery(cstat, sent_ts)) { + return; + } + + cc->congestion_recovery_start_ts = ts; +} + +void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + (void)ts; + + cc->congestion_recovery_start_ts = UINT64_MAX; + cstat->congestion_recovery_start_ts = UINT64_MAX; + + if (cc->in_loss_recovery) { + cc->in_loss_recovery = 0; + cc->packet_conservation = 0; + bbr_restore_cwnd(cc, cstat); + } +} + +void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + (void)ts; + + cstat->congestion_recovery_start_ts = UINT64_MAX; + cc->congestion_recovery_start_ts = UINT64_MAX; + cc->in_loss_recovery = 0; + cc->packet_conservation = 0; + + bbr_save_cwnd(cc, cstat); + cstat->cwnd = 2 * cstat->max_udp_payload_size; +} + +void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + + bbr_update_on_ack(bbr_cc, cstat, ack, ts); +} + +void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + (void)pkt; + + bbr_on_transmit(bbr_cc, cstat); +} + +void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)ts; +} + +void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb); + bbr_init(bbr_cc, cstat, ts); +} + +void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)event; + (void)ts; +} + +static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + bbr_update_model_and_state(cc, cstat, ack, ts); + bbr_update_control_parameters(cc, cstat, ack); +} + +static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + bbr_update_btl_bw(cc, cstat, ack); + bbr_check_cycle_phase(cc, cstat, ack, ts); + bbr_check_full_pipe(cc); + bbr_check_drain(cc, cstat, ts); + bbr_update_rtprop(cc, cstat, ts); + bbr_check_probe_rtt(cc, cstat, ts); +} + +static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_set_pacing_rate(cc, cstat); + bbr_set_send_quantum(cc, cstat); + bbr_set_cwnd(cc, cstat, ack); +} + +static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + bbr_handle_restart_from_idle(cc, cstat); +} + +static void bbr_init_round_counting(ngtcp2_bbr_cc *cc) { + cc->next_round_delivered = 0; + cc->round_start = 0; + cc->round_count = 0; +} + +static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack) { + if (ack->pkt_delivered >= cc->next_round_delivered) { + cc->next_round_delivered = cc->rst->delivered; + ++cc->round_count; + cc->round_start = 1; + + return; + } + + cc->round_start = 0; +} + +static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + if (cc->in_loss_recovery) { + if (ack->pkt_delivered >= cc->congestion_recovery_next_round_delivered) { + cc->packet_conservation = 0; + } + + if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) { + cc->in_loss_recovery = 0; + cc->packet_conservation = 0; + bbr_restore_cwnd(cc, cstat); + } + + return; + } + + if (cc->congestion_recovery_start_ts != UINT64_MAX) { + cc->in_loss_recovery = 1; + bbr_save_cwnd(cc, cstat); + cstat->cwnd = cstat->bytes_in_flight + + ngtcp2_max(ack->bytes_delivered, cstat->max_udp_payload_size); + + cstat->congestion_recovery_start_ts = cc->congestion_recovery_start_ts; + cc->congestion_recovery_start_ts = UINT64_MAX; + cc->packet_conservation = 1; + cc->congestion_recovery_next_round_delivered = cc->rst->delivered; + } +} + +static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_update_round(cc, ack); + bbr_handle_recovery(cc, cstat, ack); + + if (cstat->delivery_rate_sec < cc->btl_bw && cc->rst->rs.is_app_limited) { + return; + } + + ngtcp2_window_filter_update(&cc->btl_bw_filter, cstat->delivery_rate_sec, + cc->round_count); + + cc->btl_bw = ngtcp2_window_filter_get_best(&cc->btl_bw_filter); +} + +static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + cc->rtprop_expired = ts > cc->rtprop_stamp + NGTCP2_RTPROP_FILTERLEN; + + /* Need valid RTT sample */ + if (cstat->latest_rtt && + (cstat->latest_rtt <= cc->rt_prop || cc->rtprop_expired)) { + cc->rt_prop = cstat->latest_rtt; + cc->rtprop_stamp = ts; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr update RTprop=%" PRIu64, cc->rt_prop); + } +} + +static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + double nominal_bandwidth = + (double)cc->initial_cwnd / (double)NGTCP2_MILLISECONDS; + + cstat->pacing_rate = cc->pacing_gain * nominal_bandwidth; +} + +static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + double pacing_gain) { + double rate = pacing_gain * (double)cc->btl_bw / NGTCP2_SECONDS; + + if (cc->filled_pipe || rate > cstat->pacing_rate) { + cstat->pacing_rate = rate; + } +} + +static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + bbr_set_pacing_rate_with_gain(cc, cstat, cc->pacing_gain); +} + +static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + uint64_t send_quantum; + (void)cc; + + if (cstat->pacing_rate < 1.2 * 1024 * 1024 / 8 / NGTCP2_SECONDS) { + cstat->send_quantum = cstat->max_udp_payload_size; + } else if (cstat->pacing_rate < 24.0 * 1024 * 1024 / 8 / NGTCP2_SECONDS) { + cstat->send_quantum = cstat->max_udp_payload_size * 2; + } else { + send_quantum = + (uint64_t)(cstat->pacing_rate * (double)(cstat->min_rtt == UINT64_MAX + ? NGTCP2_MILLISECONDS + : cstat->min_rtt)); + cstat->send_quantum = (size_t)ngtcp2_min(send_quantum, 64 * 1024); + } + + cstat->send_quantum = + ngtcp2_max(cstat->send_quantum, cstat->max_udp_payload_size * 10); +} + +static uint64_t bbr_inflight(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + double gain) { + uint64_t quanta = 3 * cstat->send_quantum; + double estimated_bdp; + + if (cc->rt_prop == UINT64_MAX) { + /* no valid RTT samples yet */ + return cc->initial_cwnd; + } + + estimated_bdp = (double)cc->btl_bw * (double)cc->rt_prop / NGTCP2_SECONDS; + + return (uint64_t)(gain * estimated_bdp) + quanta; +} + +static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + cc->target_cwnd = bbr_inflight(cc, cstat, cc->cwnd_gain); +} + +static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + if (ack->bytes_lost > 0) { + if (cstat->cwnd > ack->bytes_lost) { + cstat->cwnd -= ack->bytes_lost; + cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size); + } else { + cstat->cwnd = cstat->max_udp_payload_size; + } + } + + if (cc->packet_conservation) { + cstat->cwnd = + ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered); + } +} + +static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + if (!cc->in_loss_recovery && cc->state != NGTCP2_BBR_STATE_PROBE_RTT) { + cc->prior_cwnd = cstat->cwnd; + return; + } + + cc->prior_cwnd = ngtcp2_max(cc->prior_cwnd, cstat->cwnd); +} + +static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) { + cstat->cwnd = ngtcp2_max(cstat->cwnd, cc->prior_cwnd); +} + +static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) { + return max_udp_payload_size * 4; +} + +static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat) { + if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) { + cstat->cwnd = + ngtcp2_min(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size)); + } +} + +static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_update_target_cwnd(cc, cstat); + bbr_modulate_cwnd_for_recovery(cc, cstat, ack); + + if (!cc->packet_conservation) { + if (cc->filled_pipe) { + cstat->cwnd = + ngtcp2_min(cstat->cwnd + ack->bytes_delivered, cc->target_cwnd); + } else if (cstat->cwnd < cc->target_cwnd || + cc->rst->delivered < cc->initial_cwnd) { + cstat->cwnd += ack->bytes_delivered; + } + + cstat->cwnd = + ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size)); + } + + bbr_modulate_cwnd_for_probe_rtt(cc, cstat); +} + +static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp initial_ts) { + cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN; + cc->prior_cwnd = 0; + cc->target_cwnd = 0; + cc->btl_bw = 0; + cc->rt_prop = UINT64_MAX; + cc->rtprop_stamp = initial_ts; + cc->cycle_stamp = UINT64_MAX; + cc->probe_rtt_done_stamp = UINT64_MAX; + cc->cycle_index = 0; + cc->rtprop_expired = 0; + cc->idle_restart = 0; + cc->packet_conservation = 0; + cc->probe_rtt_round_done = 0; + + cc->congestion_recovery_start_ts = UINT64_MAX; + cc->congestion_recovery_next_round_delivered = 0; + cc->in_loss_recovery = 0; + + cstat->send_quantum = cstat->max_udp_payload_size * 10; + + ngtcp2_window_filter_init(&cc->btl_bw_filter, NGTCP2_BBR_BTL_BW_FILTERLEN); + + bbr_init_round_counting(cc); + bbr_init_full_pipe(cc); + bbr_init_pacing_rate(cc, cstat); + bbr_enter_startup(cc); +} + +static void bbr_enter_startup(ngtcp2_bbr_cc *cc) { + cc->state = NGTCP2_BBR_STATE_STARTUP; + cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN; + cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN; +} + +static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc) { + cc->filled_pipe = 0; + cc->full_bw = 0; + cc->full_bw_count = 0; +} + +static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc) { + if (cc->filled_pipe || !cc->round_start || cc->rst->rs.is_app_limited) { + /* no need to check for a full pipe now. */ + return; + } + + /* cc->btl_bw still growing? */ + if (cc->btl_bw * 100 >= cc->full_bw * 125) { + /* record new baseline level */ + cc->full_bw = cc->btl_bw; + cc->full_bw_count = 0; + return; + } + /* another round w/o much growth */ + ++cc->full_bw_count; + if (cc->full_bw_count >= 3) { + cc->filled_pipe = 1; + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr filled pipe, btl_bw=%" PRIu64, cc->btl_bw); + } +} + +static void bbr_enter_drain(ngtcp2_bbr_cc *cc) { + cc->state = NGTCP2_BBR_STATE_DRAIN; + /* pace slowly */ + cc->pacing_gain = 1.0 / NGTCP2_BBR_HIGH_GAIN; + /* maintain cwnd */ + cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN; +} + +static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (cc->state == NGTCP2_BBR_STATE_STARTUP && cc->filled_pipe) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr exit Startup and enter Drain"); + + bbr_enter_drain(cc); + } + + if (cc->state == NGTCP2_BBR_STATE_DRAIN && + cstat->bytes_in_flight <= bbr_inflight(cc, cstat, 1.0)) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr exit Drain and enter ProbeBW"); + + /* we estimate queue is drained */ + bbr_enter_probe_bw(cc, ts); + } +} + +static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) { + uint8_t rand; + + cc->state = NGTCP2_BBR_STATE_PROBE_BW; + cc->pacing_gain = 1; + cc->cwnd_gain = 2; + + assert(cc->rand); + + cc->rand(&rand, 1, &cc->rand_ctx); + + cc->cycle_index = NGTCP2_BBR_GAIN_CYCLELEN - 1 - (size_t)(rand * 7 / 256); + bbr_advance_cycle_phase(cc, ts); +} + +static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + if (cc->state == NGTCP2_BBR_STATE_PROBE_BW && + bbr_is_next_cycle_phase(cc, cstat, ack, ts)) { + bbr_advance_cycle_phase(cc, ts); + } +} + +static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) { + cc->cycle_stamp = ts; + cc->cycle_index = (cc->cycle_index + 1) & (NGTCP2_BBR_GAIN_CYCLELEN - 1); + cc->pacing_gain = pacing_gain_cycle[cc->cycle_index]; +} + +static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + int is_full_length = (ts - cc->cycle_stamp) > cc->rt_prop; + + if (cc->pacing_gain > 1) { + return is_full_length && (ack->bytes_lost > 0 || + ack->prior_bytes_in_flight >= + bbr_inflight(cc, cstat, cc->pacing_gain)); + } + + if (cc->pacing_gain < 1) { + return is_full_length || + ack->prior_bytes_in_flight <= bbr_inflight(cc, cstat, 1); + } + + return is_full_length; +} + +static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc, + ngtcp2_conn_stat *cstat) { + if (cstat->bytes_in_flight == 0 && cc->rst->app_limited) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr restart from idle"); + + cc->idle_restart = 1; + + if (cc->state == NGTCP2_BBR_STATE_PROBE_BW) { + bbr_set_pacing_rate_with_gain(cc, cstat, 1); + } + } +} + +static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (cc->state != NGTCP2_BBR_STATE_PROBE_RTT && cc->rtprop_expired && + !cc->idle_restart) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr enter ProbeRTT"); + + bbr_enter_probe_rtt(cc); + bbr_save_cwnd(cc, cstat); + cc->probe_rtt_done_stamp = UINT64_MAX; + } + + if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) { + bbr_handle_probe_rtt(cc, cstat, ts); + } + + cc->idle_restart = 0; +} + +static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc) { + cc->state = NGTCP2_BBR_STATE_PROBE_RTT; + cc->pacing_gain = 1; + cc->cwnd_gain = 1; +} + +static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + uint64_t app_limited = cc->rst->delivered + cstat->bytes_in_flight; + + /* Ignore low rate samples during NGTCP2_BBR_STATE_PROBE_RTT. */ + cc->rst->app_limited = app_limited ? app_limited : 1; + + if (cc->probe_rtt_done_stamp == UINT64_MAX && + cstat->bytes_in_flight <= min_pipe_cwnd(cstat->max_udp_payload_size)) { + cc->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION; + cc->probe_rtt_round_done = 0; + cc->next_round_delivered = cc->rst->delivered; + + return; + } + + if (cc->probe_rtt_done_stamp != UINT64_MAX) { + if (cc->round_start) { + cc->probe_rtt_round_done = 1; + } + + if (cc->probe_rtt_round_done && ts > cc->probe_rtt_done_stamp) { + cc->rtprop_stamp = ts; + bbr_restore_cwnd(cc, cstat); + bbr_exit_probe_rtt(cc, ts); + } + } +} + +static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) { + if (cc->filled_pipe) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr exit ProbeRTT and enter ProbeBW"); + + bbr_enter_probe_bw(cc, ts); + + return; + } + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr exit ProbeRTT and enter Startup"); + + bbr_enter_startup(cc); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h new file mode 100644 index 00000000000000..7311f051e187bc --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h @@ -0,0 +1,156 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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 NGTCP2_BBR_H +#define NGTCP2_BBR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_cc.h" +#include "ngtcp2_window_filter.h" + +typedef struct ngtcp2_rst ngtcp2_rst; + +typedef enum ngtcp2_bbr_state { + NGTCP2_BBR_STATE_STARTUP, + NGTCP2_BBR_STATE_DRAIN, + NGTCP2_BBR_STATE_PROBE_BW, + NGTCP2_BBR_STATE_PROBE_RTT, +} ngtcp2_bbr_state; + +/* + * ngtcp2_bbr_cc is BBR congestion controller, described in + * https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00 + */ +typedef struct ngtcp2_bbr_cc { + ngtcp2_cc_base ccb; + + /* The max filter used to estimate BBR.BtlBw. */ + ngtcp2_window_filter btl_bw_filter; + uint64_t initial_cwnd; + ngtcp2_rst *rst; + ngtcp2_rand rand; + ngtcp2_rand_ctx rand_ctx; + + /* BBR variables */ + + /* The dynamic gain factor used to scale BBR.BtlBw to + produce BBR.pacing_rate. */ + double pacing_gain; + /* The dynamic gain factor used to scale the estimated BDP to produce a + congestion window (cwnd). */ + double cwnd_gain; + uint64_t full_bw; + /* packet.delivered value denoting the end of a packet-timed round trip. */ + uint64_t next_round_delivered; + /* Count of packet-timed round trips. */ + uint64_t round_count; + uint64_t prior_cwnd; + /* target_cwnd is the upper bound on the volume of data BBR + allows in flight. */ + uint64_t target_cwnd; + /* BBR's estimated bottleneck bandwidth available to the + transport flow, estimated from the maximum delivery rate sample in a + sliding window. */ + uint64_t btl_bw; + /* BBR's estimated two-way round-trip propagation delay of + the path, estimated from the windowed minimum recent round-trip delay + sample. */ + ngtcp2_duration rt_prop; + /* The wall clock time at which the current BBR.RTProp + sample was obtained. */ + ngtcp2_tstamp rtprop_stamp; + ngtcp2_tstamp cycle_stamp; + ngtcp2_tstamp probe_rtt_done_stamp; + /* congestion_recovery_start_ts is the time when congestion recovery + period started.*/ + ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t congestion_recovery_next_round_delivered; + size_t full_bw_count; + size_t cycle_index; + ngtcp2_bbr_state state; + /* A boolean that records whether BBR estimates that it has ever fully + utilized its available bandwidth ("filled the pipe"). */ + int filled_pipe; + /* A boolean that BBR sets to true once per packet-timed round trip, + on ACKs that advance BBR.round_count. */ + int round_start; + int rtprop_expired; + int idle_restart; + int packet_conservation; + int probe_rtt_round_done; + /* in_loss_recovery becomes nonzero when BBR enters loss recovery + period. */ + int in_loss_recovery; +} ngtcp2_bbr_cc; + +int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, + ngtcp2_tstamp initial_ts, ngtcp2_rand rand, + const ngtcp2_rand_ctx *rand_ctx, + const ngtcp2_mem *mem); + +void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem); + +void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *bbr_cc, ngtcp2_conn_stat *cstat, + ngtcp2_rst *rst, ngtcp2_tstamp initial_ts, + ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx, + ngtcp2_log *log); + +void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc); + +void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt); + +void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts); + +#endif /* NGTCP2_BBR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c new file mode 100644 index 00000000000000..585ea11e8e29a5 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.c @@ -0,0 +1,1486 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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. + */ +#include "ngtcp2_bbr2.h" + +#include + +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_rst.h" + +#define NGTCP2_BBR_MAX_BW_FILTERLEN 2 + +#define NGTCP2_BBR_EXTRA_ACKED_FILTERLEN 10 + +#define NGTCP2_BBR_STARTUP_PACING_GAIN ((double)2.77) + +#define NGTCP2_BBR_STARTUP_CWND_GAIN 2 + +#define NGTCP2_BBR_PROBE_RTT_CWND_GAIN ((double)0.5) + +#define NGTCP2_BBR_BETA_NUMER 7 +#define NGTCP2_BBR_BETA_DENOM 10 + +#define NGTCP2_BBR_LOSS_THRESH_NUMER 2 +#define NGTCP2_BBR_LOSS_THRESH_DENOM 100 + +#define NGTCP2_BBR_HEADROOM_NUMER 15 +#define NGTCP2_BBR_HEADROOM_DENOM 100 + +#define NGTCP2_BBR_PROBE_RTT_INTERVAL (5 * NGTCP2_SECONDS) +#define NGTCP2_BBR_MIN_RTT_FILTERLEN (10 * NGTCP2_SECONDS) + +#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS) + +#define NGTCP2_BBR_PACING_MARGIN_PERCENT 1 + +static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp initial_ts); + +static void bbr_on_transmit(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_reset_congestion_signals(ngtcp2_bbr2_cc *bbr); + +static void bbr_reset_lower_bounds(ngtcp2_bbr2_cc *bbr); + +static void bbr_init_round_counting(ngtcp2_bbr2_cc *bbr); + +static void bbr_init_full_pipe(ngtcp2_bbr2_cc *bbr); + +static void bbr_init_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + double pacing_gain); + +static void bbr_set_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_enter_startup(ngtcp2_bbr2_cc *bbr); + +static void bbr_check_startup_done(ngtcp2_bbr2_cc *bbr, + const ngtcp2_cc_ack *ack); + +static void bbr_update_on_ack(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +static void bbr_update_model_and_state(ngtcp2_bbr2_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +static void bbr_update_control_parameters(ngtcp2_bbr2_cc *cc, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_update_on_loss(ngtcp2_bbr2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +static void bbr_update_latest_delivery_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_advance_latest_delivery_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_init_lower_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_loss_lower_bounds(ngtcp2_bbr2_cc *bbr); + +static void bbr_bound_bw_for_model(ngtcp2_bbr2_cc *bbr); + +static void bbr_update_max_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_update_round(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack); + +static void bbr_start_round(ngtcp2_bbr2_cc *bbr); + +static int bbr_is_in_probe_bw_state(ngtcp2_bbr2_cc *bbr); + +static void bbr_update_ack_aggregation(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +static void bbr_enter_drain(ngtcp2_bbr2_cc *bbr); + +static void bbr_check_drain(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_enter_probe_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts); + +static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts); + +static void bbr_start_probe_bw_cruise(ngtcp2_bbr2_cc *bbr); + +static void bbr_start_probe_bw_refill(ngtcp2_bbr2_cc *bbr); + +static void bbr_start_probe_bw_up(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + +static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr, + ngtcp2_duration interval, ngtcp2_tstamp ts); + +static uint64_t bbr_inflight_with_headroom(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_raise_inflight_hi_slope(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_probe_inflight_hi_upward(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_adapt_upper_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +static int bbr_check_time_to_probe_bw(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_pick_probe_wait(ngtcp2_bbr2_cc *bbr); + +static int bbr_is_reno_coexistence_probe_time(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static uint64_t bbr_target_inflight(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static int bbr_check_inflight_too_high(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static int is_inflight_too_high(const ngtcp2_rs *rs); + +static void bbr_handle_inflight_too_high(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_rs *rs, ngtcp2_tstamp ts); + +static void bbr_handle_lost_packet(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_bbr2_cc *bbr, + const ngtcp2_rs *rs, + const ngtcp2_cc_pkt *pkt); + +static void bbr_update_min_rtt(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +static void bbr_check_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_enter_probe_rtt(ngtcp2_bbr2_cc *bbr); + +static void bbr_handle_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static void bbr_check_probe_rtt_done(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + +static void bbr_mark_connection_app_limited(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_exit_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts); + +static void bbr_handle_restart_from_idle(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +static uint64_t bbr_bdp_multiple(ngtcp2_bbr2_cc *bbr, uint64_t bw, double gain); + +static uint64_t bbr_quantization_budget(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + uint64_t inflight); + +static uint64_t bbr_inflight(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + uint64_t bw, double gain); + +static void bbr_update_max_inflight(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_update_offload_budget(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static uint64_t min_pipe_cwnd(size_t max_udp_payload_size); + +static void bbr_advance_max_bw_filter(ngtcp2_bbr2_cc *bbr); + +static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_save_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_restore_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static uint64_t bbr_probe_rtt_cwnd(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_set_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_bound_cwnd_for_model(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat); + +static void bbr_set_send_quantum(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat); + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_time); + +static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); + +static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp initial_ts) { + ngtcp2_window_filter_init(&bbr->max_bw_filter, NGTCP2_BBR_MAX_BW_FILTERLEN); + ngtcp2_window_filter_init(&bbr->extra_acked_filter, + NGTCP2_BBR_EXTRA_ACKED_FILTERLEN); + + bbr->min_rtt = UINT64_MAX; + bbr->min_rtt_stamp = initial_ts; + /* remark: Use UINT64_MAX instead of 0 for consistency. */ + bbr->probe_rtt_done_stamp = UINT64_MAX; + bbr->probe_rtt_round_done = 0; + bbr->prior_cwnd = 0; + bbr->idle_restart = 0; + bbr->extra_acked_interval_start = initial_ts; + bbr->extra_acked_delivered = 0; + + bbr_reset_congestion_signals(bbr); + bbr_reset_lower_bounds(bbr); + bbr_init_round_counting(bbr); + bbr_init_full_pipe(bbr); + bbr_init_pacing_rate(bbr, cstat); + bbr_enter_startup(bbr); + + cstat->send_quantum = cstat->max_udp_payload_size * 10; + + /* Missing in documentation */ + bbr->loss_round_start = 0; + bbr->loss_round_delivered = UINT64_MAX; + + bbr->rounds_since_bw_probe = 0; + + bbr->max_bw = 0; + bbr->bw = 0; + + bbr->cycle_count = 0; + + bbr->extra_acked = 0; + + bbr->bytes_lost_in_round = 0; + bbr->loss_events_in_round = 0; + + bbr->offload_budget = 0; + + bbr->probe_up_cnt = UINT64_MAX; + bbr->cycle_stamp = UINT64_MAX; + bbr->ack_phase = 0; + bbr->bw_probe_wait = 0; + bbr->bw_probe_samples = 0; + bbr->bw_probe_up_rounds = 0; + bbr->bw_probe_up_acks = 0; + + bbr->inflight_hi = UINT64_MAX; + bbr->bw_hi = UINT64_MAX; + + bbr->probe_rtt_expired = 0; + bbr->probe_rtt_min_delay = UINT64_MAX; + bbr->probe_rtt_min_stamp = initial_ts; + + bbr->in_loss_recovery = 0; + bbr->packet_conservation = 0; + + bbr->max_inflight = 0; + + bbr->congestion_recovery_start_ts = UINT64_MAX; + bbr->congestion_recovery_next_round_delivered = 0; + + bbr->prior_inflight_lo = 0; + bbr->prior_inflight_hi = 0; + bbr->prior_bw_lo = 0; +} + +static void bbr_reset_congestion_signals(ngtcp2_bbr2_cc *bbr) { + bbr->loss_in_round = 0; + bbr->bw_latest = 0; + bbr->inflight_latest = 0; +} + +static void bbr_reset_lower_bounds(ngtcp2_bbr2_cc *bbr) { + bbr->bw_lo = UINT64_MAX; + bbr->inflight_lo = UINT64_MAX; +} + +static void bbr_init_round_counting(ngtcp2_bbr2_cc *bbr) { + bbr->next_round_delivered = 0; + bbr->round_start = 0; + bbr->round_count = 0; +} + +static void bbr_init_full_pipe(ngtcp2_bbr2_cc *bbr) { + bbr->filled_pipe = 0; + bbr->full_bw = 0; + bbr->full_bw_count = 0; +} + +static void bbr_check_startup_full_bandwidth(ngtcp2_bbr2_cc *bbr) { + if (bbr->filled_pipe || !bbr->round_start || bbr->rst->rs.is_app_limited) { + return; + } + + if (bbr->max_bw * 100 >= bbr->full_bw * 125) { + bbr->full_bw = bbr->max_bw; + bbr->full_bw_count = 0; + } + + ++bbr->full_bw_count; + + if (bbr->full_bw_count >= 3) { + bbr->filled_pipe = 1; + + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 filled pipe, full_bw=%" PRIu64, bbr->full_bw); + } +} + +static void bbr_check_startup_high_loss(ngtcp2_bbr2_cc *bbr, + const ngtcp2_cc_ack *ack) { + if (bbr->filled_pipe || !bbr->round_start || bbr->rst->rs.is_app_limited) { + return; + } + + if (bbr->loss_events_in_round <= 3) { + return; + } + + /* loss_thresh = 2% */ + if (bbr->bytes_lost_in_round * 100 <= ack->prior_bytes_in_flight * 2) { + return; + } + + bbr->filled_pipe = 1; +} + +static void bbr_init_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + double nominal_bandwidth = (double)bbr->initial_cwnd; + + cstat->pacing_rate = NGTCP2_BBR_STARTUP_PACING_GAIN * nominal_bandwidth / + (double)NGTCP2_MILLISECONDS; +} + +static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + double pacing_gain) { + double rate = pacing_gain * (double)bbr->bw * + (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT) / 100 / NGTCP2_SECONDS; + + if (bbr->filled_pipe || rate > cstat->pacing_rate) { + cstat->pacing_rate = rate; + } +} + +static void bbr_set_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + bbr_set_pacing_rate_with_gain(bbr, cstat, bbr->pacing_gain); +} + +static void bbr_enter_startup(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter Startup"); + + bbr->state = NGTCP2_BBR2_STATE_STARTUP; + bbr->pacing_gain = NGTCP2_BBR_STARTUP_PACING_GAIN; + bbr->cwnd_gain = NGTCP2_BBR_STARTUP_CWND_GAIN; +} + +static void bbr_check_startup_done(ngtcp2_bbr2_cc *bbr, + const ngtcp2_cc_ack *ack) { + bbr_check_startup_full_bandwidth(bbr); + bbr_check_startup_high_loss(bbr, ack); + + if (bbr->state == NGTCP2_BBR2_STATE_STARTUP && bbr->filled_pipe) { + bbr_enter_drain(bbr); + } +} + +static void bbr_on_transmit(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + bbr_handle_restart_from_idle(bbr, cstat, ts); +} + +static void bbr_update_on_ack(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + bbr_update_model_and_state(bbr, cstat, ack, ts); + bbr_update_control_parameters(bbr, cstat, ack); +} + +static void bbr_update_model_and_state(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + bbr_update_latest_delivery_signals(bbr, cstat); + bbr_update_congestion_signals(bbr, cstat, ack); + bbr_update_ack_aggregation(bbr, cstat, ack, ts); + bbr_check_startup_done(bbr, ack); + bbr_check_drain(bbr, cstat, ts); + bbr_update_probe_bw_cycle_phase(bbr, cstat, ack, ts); + bbr_update_min_rtt(bbr, ack, ts); + bbr_check_probe_rtt(bbr, cstat, ts); + bbr_advance_latest_delivery_signals(bbr, cstat); + bbr_bound_bw_for_model(bbr); +} + +static void bbr_update_control_parameters(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_set_pacing_rate(bbr, cstat); + bbr_set_send_quantum(bbr, cstat); + bbr_set_cwnd(bbr, cstat, ack); +} + +static void bbr_update_on_loss(ngtcp2_bbr2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + bbr_handle_lost_packet(cc, cstat, pkt, ts); +} + +static void bbr_update_latest_delivery_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + bbr->loss_round_start = 0; + bbr->bw_latest = ngtcp2_max(bbr->bw_latest, cstat->delivery_rate_sec); + bbr->inflight_latest = + ngtcp2_max(bbr->inflight_latest, bbr->rst->rs.delivered); + + if (bbr->rst->rs.prior_delivered >= bbr->loss_round_delivered) { + bbr->loss_round_delivered = bbr->rst->delivered; + bbr->loss_round_start = 1; + } +} + +static void bbr_advance_latest_delivery_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + if (bbr->loss_round_start) { + bbr->bw_latest = cstat->delivery_rate_sec; + bbr->inflight_latest = bbr->rst->rs.delivered; + } +} + +static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_update_max_bw(bbr, cstat, ack); + + if (ack->bytes_lost) { + bbr->bytes_lost_in_round += ack->bytes_lost; + ++bbr->loss_events_in_round; + + if (!bbr->loss_in_round) { + bbr->loss_in_round = 1; + bbr->loss_round_delivered = bbr->rst->delivered; + } + } + + if (!bbr->loss_round_start) { + return; + } + + bbr_adapt_lower_bounds_from_congestion(bbr, cstat); + + bbr->loss_in_round = 0; +} + +static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + if (!bbr->filled_pipe || bbr_is_in_probe_bw_state(bbr)) { + return; + } + + if (bbr->loss_in_round) { + bbr_init_lower_bounds(bbr, cstat); + bbr_loss_lower_bounds(bbr); + } +} + +static void bbr_init_lower_bounds(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + if (bbr->bw_lo == UINT64_MAX) { + bbr->bw_lo = bbr->max_bw; + } + + if (bbr->inflight_lo == UINT64_MAX) { + bbr->inflight_lo = cstat->cwnd; + } +} + +static void bbr_loss_lower_bounds(ngtcp2_bbr2_cc *bbr) { + bbr->bw_lo = ngtcp2_max(bbr->bw_latest, bbr->bw_lo * NGTCP2_BBR_BETA_NUMER / + NGTCP2_BBR_BETA_DENOM); + bbr->inflight_lo = ngtcp2_max(bbr->inflight_latest, + bbr->inflight_lo * NGTCP2_BBR_BETA_NUMER / + NGTCP2_BBR_BETA_DENOM); +} + +static void bbr_bound_bw_for_model(ngtcp2_bbr2_cc *bbr) { + bbr->bw = ngtcp2_min(bbr->max_bw, bbr->bw_lo); + bbr->bw = ngtcp2_min(bbr->bw, bbr->bw_hi); +} + +static void bbr_update_max_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + bbr_update_round(bbr, ack); + bbr_handle_recovery(bbr, cstat, ack); + + if (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited) { + ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec, + bbr->cycle_count); + + bbr->max_bw = ngtcp2_window_filter_get_best(&bbr->max_bw_filter); + } +} + +static void bbr_update_round(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack) { + if (ack->pkt_delivered >= bbr->next_round_delivered) { + bbr_start_round(bbr); + + ++bbr->round_count; + ++bbr->rounds_since_bw_probe; + bbr->round_start = 1; + + bbr->bytes_lost_in_round = 0; + bbr->loss_events_in_round = 0; + + bbr->rst->is_cwnd_limited = 0; + + return; + } + + bbr->round_start = 0; +} + +static void bbr_start_round(ngtcp2_bbr2_cc *bbr) { + bbr->next_round_delivered = bbr->rst->delivered; +} + +static int bbr_is_in_probe_bw_state(ngtcp2_bbr2_cc *bbr) { + switch (bbr->state) { + case NGTCP2_BBR2_STATE_PROBE_BW_DOWN: + case NGTCP2_BBR2_STATE_PROBE_BW_CRUISE: + case NGTCP2_BBR2_STATE_PROBE_BW_REFILL: + case NGTCP2_BBR2_STATE_PROBE_BW_UP: + return 1; + default: + return 0; + } +} + +static void bbr_update_ack_aggregation(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + ngtcp2_duration interval = ts - bbr->extra_acked_interval_start; + uint64_t expected_delivered = bbr->bw * interval / NGTCP2_SECONDS; + uint64_t extra; + + if (bbr->extra_acked_delivered <= expected_delivered) { + bbr->extra_acked_delivered = 0; + bbr->extra_acked_interval_start = ts; + expected_delivered = 0; + } + + bbr->extra_acked_delivered += ack->bytes_delivered; + extra = bbr->extra_acked_delivered - expected_delivered; + extra = ngtcp2_min(extra, cstat->cwnd); + + ngtcp2_window_filter_update(&bbr->extra_acked_filter, extra, + bbr->round_count); + + bbr->extra_acked = ngtcp2_window_filter_get_best(&bbr->extra_acked_filter); +} + +static void bbr_enter_drain(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter Drain"); + + bbr->state = NGTCP2_BBR2_STATE_DRAIN; + bbr->pacing_gain = 1. / NGTCP2_BBR_STARTUP_CWND_GAIN; + bbr->cwnd_gain = NGTCP2_BBR_STARTUP_CWND_GAIN; +} + +static void bbr_check_drain(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (bbr->state == NGTCP2_BBR2_STATE_DRAIN && + cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, bbr->bw, 1.0)) { + bbr_enter_probe_bw(bbr, ts); + } +} + +static void bbr_enter_probe_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) { + bbr_start_probe_bw_down(bbr, ts); +} + +static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 start ProbeBW_DOWN"); + + bbr_reset_congestion_signals(bbr); + + bbr->probe_up_cnt = UINT64_MAX; + + bbr_pick_probe_wait(bbr); + + bbr->cycle_stamp = ts; + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING; + + bbr_start_round(bbr); + + bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_DOWN; + bbr->pacing_gain = 0.9; + bbr->cwnd_gain = 2; +} + +static void bbr_start_probe_bw_cruise(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 start ProbeBW_CRUISE"); + + bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_CRUISE; + bbr->pacing_gain = 1.0; + bbr->cwnd_gain = 2; +} + +static void bbr_start_probe_bw_refill(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 start ProbeBW_REFILL"); + + bbr_reset_lower_bounds(bbr); + + bbr->bw_probe_up_rounds = 0; + bbr->bw_probe_up_acks = 0; + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING; + + bbr_start_round(bbr); + + bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_REFILL; + bbr->pacing_gain = 1.0; + bbr->cwnd_gain = 2; +} + +static void bbr_start_probe_bw_up(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 start ProbeBW_UP"); + + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING; + + bbr_start_round(bbr); + + bbr->cycle_stamp = ts; + bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_UP; + bbr->pacing_gain = 1.25; + bbr->cwnd_gain = 2; + + bbr_raise_inflight_hi_slope(bbr, cstat); +} + +static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + if (!bbr->filled_pipe) { + return; + } + + bbr_adapt_upper_bounds(bbr, cstat, ack, ts); + + if (!bbr_is_in_probe_bw_state(bbr)) { + return; + } + + switch (bbr->state) { + case NGTCP2_BBR2_STATE_PROBE_BW_DOWN: + if (bbr_check_time_to_probe_bw(bbr, cstat, ts)) { + return; + } + + if (bbr_check_time_to_cruise(bbr, cstat, ts)) { + bbr_start_probe_bw_cruise(bbr); + } + + break; + case NGTCP2_BBR2_STATE_PROBE_BW_CRUISE: + if (bbr_check_time_to_probe_bw(bbr, cstat, ts)) { + return; + } + + break; + case NGTCP2_BBR2_STATE_PROBE_BW_REFILL: + if (bbr->round_start) { + bbr->bw_probe_samples = 1; + bbr_start_probe_bw_up(bbr, cstat, ts); + } + + break; + case NGTCP2_BBR2_STATE_PROBE_BW_UP: + if (bbr_has_elapsed_in_phase(bbr, bbr->min_rtt, ts) && + cstat->bytes_in_flight > bbr_inflight(bbr, cstat, bbr->max_bw, 1.25)) { + bbr_start_probe_bw_down(bbr, ts); + } + + break; + default: + break; + } +} + +static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { + (void)ts; + + if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) { + return 0; + } + + if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, bbr->max_bw, 1.0)) { + return 1; + } + + return 0; +} + +static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr, + ngtcp2_duration interval, + ngtcp2_tstamp ts) { + return ts > bbr->cycle_stamp + interval; +} + +static uint64_t bbr_inflight_with_headroom(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t headroom; + uint64_t mpcwnd; + if (bbr->inflight_hi == UINT64_MAX) { + return UINT64_MAX; + } + + headroom = ngtcp2_max(cstat->max_udp_payload_size, + bbr->inflight_hi * NGTCP2_BBR_HEADROOM_NUMER / + NGTCP2_BBR_HEADROOM_DENOM); + mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size); + + if (bbr->inflight_hi > headroom) { + return ngtcp2_max(bbr->inflight_hi - headroom, mpcwnd); + } + + return mpcwnd; +} + +static void bbr_raise_inflight_hi_slope(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t growth_this_round = cstat->max_udp_payload_size + << bbr->bw_probe_up_rounds; + + bbr->bw_probe_up_rounds = ngtcp2_min(bbr->bw_probe_up_rounds + 1, 30); + bbr->probe_up_cnt = ngtcp2_max(cstat->cwnd / growth_this_round, 1) * + cstat->max_udp_payload_size; +} + +static void bbr_probe_inflight_hi_upward(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + uint64_t delta; + + if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_hi) { + return; + } + + bbr->bw_probe_up_acks += ack->bytes_delivered; + + if (bbr->bw_probe_up_acks >= bbr->probe_up_cnt) { + delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt; + bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt; + bbr->inflight_hi += delta * cstat->max_udp_payload_size; + } + + if (bbr->round_start) { + bbr_raise_inflight_hi_slope(bbr, cstat); + } +} + +static void bbr_adapt_upper_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + if (bbr->ack_phase == NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING && + bbr->round_start) { + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK; + } + + if (bbr->ack_phase == NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING && + bbr->round_start) { + if (bbr_is_in_probe_bw_state(bbr) && !bbr->rst->rs.is_app_limited) { + bbr_advance_max_bw_filter(bbr); + } + } + + if (!bbr_check_inflight_too_high(bbr, cstat, ts)) { + /* bbr->bw_hi never be updated */ + if (bbr->inflight_hi == UINT64_MAX /* || bbr->bw_hi == UINT64_MAX */) { + return; + } + + if (bbr->rst->rs.tx_in_flight > bbr->inflight_hi) { + bbr->inflight_hi = bbr->rst->rs.tx_in_flight; + } + + if (cstat->delivery_rate_sec > bbr->bw_hi) { + bbr->bw_hi = cstat->delivery_rate_sec; + } + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) { + bbr_probe_inflight_hi_upward(bbr, cstat, ack); + } + } +} + +static int bbr_check_time_to_probe_bw(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (bbr_has_elapsed_in_phase(bbr, bbr->bw_probe_wait, ts) || + bbr_is_reno_coexistence_probe_time(bbr, cstat)) { + bbr_start_probe_bw_refill(bbr); + + return 1; + } + + return 0; +} + +static void bbr_pick_probe_wait(ngtcp2_bbr2_cc *bbr) { + uint8_t rand; + + bbr->rand(&rand, 1, &bbr->rand_ctx); + + bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256); + + bbr->rand(&rand, 1, &bbr->rand_ctx); + + bbr->bw_probe_wait = 2 * NGTCP2_SECONDS + + (ngtcp2_tstamp)((double)rand / 255. * NGTCP2_SECONDS); +} + +static int bbr_is_reno_coexistence_probe_time(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t reno_rounds = + bbr_target_inflight(bbr, cstat) / cstat->max_udp_payload_size; + + return bbr->rounds_since_bw_probe >= ngtcp2_min(reno_rounds, 63); +} + +static uint64_t bbr_target_inflight(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t bdp = bbr_inflight(bbr, cstat, bbr->bw, 1.0); + + return ngtcp2_min(bdp, cstat->cwnd); +} + +static int bbr_check_inflight_too_high(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (is_inflight_too_high(&bbr->rst->rs)) { + if (bbr->bw_probe_samples) { + bbr_handle_inflight_too_high(bbr, cstat, &bbr->rst->rs, ts); + } + + return 1; + } + + return 0; +} + +static int is_inflight_too_high(const ngtcp2_rs *rs) { + return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM > + rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER; +} + +static void bbr_handle_inflight_too_high(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_rs *rs, + ngtcp2_tstamp ts) { + bbr->bw_probe_samples = 0; + + if (!rs->is_app_limited) { + bbr->prior_inflight_hi = bbr->inflight_hi; + + bbr->inflight_hi = ngtcp2_max( + rs->tx_in_flight, bbr_target_inflight(bbr, cstat) * + NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); + } + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) { + bbr_start_probe_bw_down(bbr, ts); + } +} + +static void bbr_handle_lost_packet(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + ngtcp2_rs rs = {0}; + + if (!bbr->bw_probe_samples) { + return; + } + + rs.tx_in_flight = pkt->tx_in_flight; + rs.lost = bbr->rst->lost - pkt->lost; + rs.is_app_limited = pkt->is_app_limited; + + if (is_inflight_too_high(&rs)) { + rs.tx_in_flight = bbr_inflight_hi_from_lost_packet(bbr, &rs, pkt); + + bbr_handle_inflight_too_high(bbr, cstat, &rs, ts); + } +} + +static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_bbr2_cc *bbr, + const ngtcp2_rs *rs, + const ngtcp2_cc_pkt *pkt) { + uint64_t inflight_prev, lost_prefix; + (void)bbr; + + assert(rs->tx_in_flight >= pkt->pktlen); + + inflight_prev = rs->tx_in_flight - pkt->pktlen; + + assert(rs->lost >= pkt->pktlen); + + /* bbr->rst->lost is not incremented for pkt yet */ + + if (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER < + rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM) { + return inflight_prev; + } + + lost_prefix = (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER - + rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM) / + (NGTCP2_BBR_LOSS_THRESH_DENOM - NGTCP2_BBR_LOSS_THRESH_NUMER); + + return inflight_prev + lost_prefix; +} + +static void bbr_update_min_rtt(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts) { + int min_rtt_expired; + + bbr->probe_rtt_expired = + ts > bbr->probe_rtt_min_stamp + NGTCP2_BBR_PROBE_RTT_INTERVAL; + + if (ack->rtt != UINT64_MAX && + (ack->rtt < bbr->probe_rtt_min_delay || bbr->probe_rtt_expired)) { + bbr->probe_rtt_min_delay = ack->rtt; + bbr->probe_rtt_min_stamp = ts; + } + + min_rtt_expired = ts > bbr->min_rtt_stamp + NGTCP2_BBR_MIN_RTT_FILTERLEN; + + if (bbr->probe_rtt_min_delay < bbr->min_rtt || min_rtt_expired) { + bbr->min_rtt = bbr->probe_rtt_min_delay; + bbr->min_rtt_stamp = bbr->probe_rtt_min_stamp; + + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 update min_rtt=%" PRIu64, bbr->min_rtt); + } +} + +static void bbr_check_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (bbr->state != NGTCP2_BBR2_STATE_PROBE_RTT && bbr->probe_rtt_expired && + !bbr->idle_restart) { + bbr_enter_probe_rtt(bbr); + bbr_save_cwnd(bbr, cstat); + + bbr->probe_rtt_done_stamp = UINT64_MAX; + bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING; + + bbr_start_round(bbr); + } + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) { + bbr_handle_probe_rtt(bbr, cstat, ts); + } + + if (bbr->rst->rs.delivered) { + bbr->idle_restart = 0; + } +} + +static void bbr_enter_probe_rtt(ngtcp2_bbr2_cc *bbr) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter ProbeRTT"); + + bbr->state = NGTCP2_BBR2_STATE_PROBE_RTT; + bbr->pacing_gain = 1; + bbr->cwnd_gain = NGTCP2_BBR_PROBE_RTT_CWND_GAIN; +} + +static void bbr_handle_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + bbr_mark_connection_app_limited(bbr, cstat); + + if (bbr->probe_rtt_done_stamp == UINT64_MAX && + cstat->bytes_in_flight <= bbr_probe_rtt_cwnd(bbr, cstat)) { + bbr->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION; + bbr->probe_rtt_round_done = 0; + + bbr_start_round(bbr); + + return; + } + + if (bbr->probe_rtt_done_stamp != UINT64_MAX) { + if (bbr->round_start) { + bbr->probe_rtt_round_done = 1; + } + + if (bbr->probe_rtt_round_done) { + bbr_check_probe_rtt_done(bbr, cstat, ts); + } + } +} + +static void bbr_check_probe_rtt_done(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (bbr->probe_rtt_done_stamp != UINT64_MAX && + ts > bbr->probe_rtt_done_stamp) { + bbr->probe_rtt_min_stamp = ts; + bbr_restore_cwnd(bbr, cstat); + bbr_exit_probe_rtt(bbr, ts); + } +} + +static void bbr_mark_connection_app_limited(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t app_limited = bbr->rst->delivered + cstat->bytes_in_flight; + + if (app_limited) { + bbr->rst->app_limited = app_limited; + } else { + bbr->rst->app_limited = cstat->max_udp_payload_size; + } +} + +static void bbr_exit_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) { + bbr_reset_lower_bounds(bbr); + + if (bbr->filled_pipe) { + bbr_start_probe_bw_down(bbr, ts); + bbr_start_probe_bw_cruise(bbr); + } else { + bbr_enter_startup(bbr); + } +} + +static void bbr_handle_restart_from_idle(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + if (cstat->bytes_in_flight == 0 && bbr->rst->app_limited) { + ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, + "bbr2 restart from idle"); + + bbr->idle_restart = 1; + bbr->extra_acked_interval_start = ts; + + if (bbr_is_in_probe_bw_state(bbr)) { + bbr_set_pacing_rate_with_gain(bbr, cstat, 1); + } else if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) { + bbr_check_probe_rtt_done(bbr, cstat, ts); + } + } +} + +static uint64_t bbr_bdp_multiple(ngtcp2_bbr2_cc *bbr, uint64_t bw, + double gain) { + uint64_t bdp; + + if (bbr->min_rtt == UINT64_MAX) { + return bbr->initial_cwnd; + } + + bdp = bw * bbr->min_rtt / NGTCP2_SECONDS; + + return (uint64_t)(gain * (double)bdp); +} + +static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) { + return max_udp_payload_size * 4; +} + +static uint64_t bbr_quantization_budget(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + uint64_t inflight) { + bbr_update_offload_budget(bbr, cstat); + + inflight = ngtcp2_max(inflight, bbr->offload_budget); + inflight = ngtcp2_max(inflight, min_pipe_cwnd(cstat->max_udp_payload_size)); + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) { + inflight += 2 * cstat->max_udp_payload_size; + } + + return inflight; +} + +static uint64_t bbr_inflight(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + uint64_t bw, double gain) { + uint64_t inflight = bbr_bdp_multiple(bbr, bw, gain); + + return bbr_quantization_budget(bbr, cstat, inflight); +} + +static void bbr_update_max_inflight(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t inflight; + + /* Not documented */ + /* bbr_update_aggregation_budget(bbr); */ + + inflight = bbr_bdp_multiple(bbr, bbr->bw, bbr->cwnd_gain) + bbr->extra_acked; + bbr->max_inflight = bbr_quantization_budget(bbr, cstat, inflight); +} + +static void bbr_update_offload_budget(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + bbr->offload_budget = 3 * cstat->send_quantum; +} + +static void bbr_advance_max_bw_filter(ngtcp2_bbr2_cc *bbr) { + ++bbr->cycle_count; +} + +static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + if (ack->bytes_lost > 0) { + if (cstat->cwnd > ack->bytes_lost) { + cstat->cwnd -= ack->bytes_lost; + cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size); + } else { + cstat->cwnd = cstat->max_udp_payload_size; + } + } + + if (bbr->packet_conservation) { + cstat->cwnd = + ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered); + } +} + +static void bbr_save_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + if (!bbr->in_loss_recovery && bbr->state != NGTCP2_BBR2_STATE_PROBE_RTT) { + bbr->prior_cwnd = cstat->cwnd; + return; + } + + bbr->prior_cwnd = ngtcp2_max(bbr->prior_cwnd, cstat->cwnd); +} + +static void bbr_restore_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + cstat->cwnd = ngtcp2_max(cstat->cwnd, bbr->prior_cwnd); +} + +static uint64_t bbr_probe_rtt_cwnd(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t probe_rtt_cwnd = + bbr_bdp_multiple(bbr, bbr->bw, NGTCP2_BBR_PROBE_RTT_CWND_GAIN); + uint64_t mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size); + + return ngtcp2_max(probe_rtt_cwnd, mpcwnd); +} + +static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t probe_rtt_cwnd; + + if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) { + probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat); + + cstat->cwnd = ngtcp2_min(cstat->cwnd, probe_rtt_cwnd); + } +} + +static void bbr_set_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + uint64_t mpcwnd; + + bbr_update_max_inflight(bbr, cstat); + bbr_modulate_cwnd_for_recovery(bbr, cstat, ack); + + if (!bbr->packet_conservation) { + if (bbr->filled_pipe) { + cstat->cwnd += ack->bytes_delivered; + cstat->cwnd = ngtcp2_min(cstat->cwnd, bbr->max_inflight); + } else if (cstat->cwnd < bbr->max_inflight || + bbr->rst->delivered < bbr->initial_cwnd) { + cstat->cwnd += ack->bytes_delivered; + } + + mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size); + cstat->cwnd = ngtcp2_max(cstat->cwnd, mpcwnd); + } + + bbr_bound_cwnd_for_probe_rtt(bbr, cstat); + bbr_bound_cwnd_for_model(bbr, cstat); +} + +static void bbr_bound_cwnd_for_model(ngtcp2_bbr2_cc *bbr, + ngtcp2_conn_stat *cstat) { + uint64_t cap = UINT64_MAX; + uint64_t mpcwnd = min_pipe_cwnd(cstat->max_udp_payload_size); + + if (bbr_is_in_probe_bw_state(bbr) && + bbr->state != NGTCP2_BBR2_STATE_PROBE_BW_CRUISE) { + cap = bbr->inflight_hi; + } else if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT || + bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_CRUISE) { + cap = bbr_inflight_with_headroom(bbr, cstat); + } + + cap = ngtcp2_min(cap, bbr->inflight_lo); + cap = ngtcp2_max(cap, mpcwnd); + + cstat->cwnd = ngtcp2_min(cstat->cwnd, cap); +} + +static void bbr_set_send_quantum(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) { + size_t send_quantum = + (size_t)(cstat->pacing_rate * (double)(bbr->min_rtt == UINT64_MAX + ? NGTCP2_MILLISECONDS + : bbr->min_rtt)); + (void)bbr; + + cstat->send_quantum = ngtcp2_min(send_quantum, 64 * 1024); + cstat->send_quantum = + ngtcp2_max(cstat->send_quantum, cstat->max_udp_payload_size * 10); +} + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_time) { + return cstat->congestion_recovery_start_ts != UINT64_MAX && + sent_time <= cstat->congestion_recovery_start_ts; +} + +static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { + if (bbr->in_loss_recovery) { + if (ack->pkt_delivered >= bbr->congestion_recovery_next_round_delivered) { + bbr->packet_conservation = 0; + } + + if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) { + bbr->in_loss_recovery = 0; + bbr->packet_conservation = 0; + bbr_restore_cwnd(bbr, cstat); + } + + return; + } + + if (bbr->congestion_recovery_start_ts != UINT64_MAX) { + bbr->in_loss_recovery = 1; + bbr_save_cwnd(bbr, cstat); + cstat->cwnd = cstat->bytes_in_flight + + ngtcp2_max(ack->bytes_delivered, cstat->max_udp_payload_size); + + cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts; + bbr->congestion_recovery_start_ts = UINT64_MAX; + bbr->packet_conservation = 1; + bbr->congestion_recovery_next_round_delivered = bbr->rst->delivered; + bbr->prior_inflight_lo = bbr->inflight_lo; + bbr->prior_bw_lo = bbr->bw_lo; + } +} + +static void bbr2_cc_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_rst *rst, ngtcp2_tstamp initial_ts, + ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx, + ngtcp2_log *log) { + bbr->ccb.log = log; + bbr->rst = rst; + bbr->rand = rand; + bbr->rand_ctx = *rand_ctx; + bbr->initial_cwnd = cstat->cwnd; + + bbr_on_init(bbr, cstat, initial_ts); +} + +static void bbr2_cc_free(ngtcp2_bbr2_cc *bbr) { (void)bbr; } + +static void bbr2_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)pkt; + (void)ts; +} + +static void bbr2_cc_on_pkt_lost(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + bbr_update_on_loss(bbr, cstat, pkt, ts); +} + +static void bbr2_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + if (!bbr->filled_pipe || bbr->in_loss_recovery || + bbr->congestion_recovery_start_ts != UINT64_MAX || + in_congestion_recovery(cstat, sent_ts)) { + return; + } + + bbr->congestion_recovery_start_ts = ts; +} + +static void bbr2_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + (void)ts; + + bbr->congestion_recovery_start_ts = UINT64_MAX; + cstat->congestion_recovery_start_ts = UINT64_MAX; + + if (bbr->in_loss_recovery) { + bbr->in_loss_recovery = 0; + bbr->packet_conservation = 0; + bbr_restore_cwnd(bbr, cstat); + bbr->full_bw_count = 0; + bbr->loss_in_round = 0; + bbr->inflight_lo = ngtcp2_max(bbr->inflight_lo, bbr->prior_inflight_lo); + bbr->inflight_hi = ngtcp2_max(bbr->inflight_hi, bbr->prior_inflight_hi); + bbr->bw_lo = ngtcp2_max(bbr->bw_lo, bbr->prior_bw_lo); + } +} + +static void bbr2_cc_on_persistent_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + (void)ts; + + cstat->congestion_recovery_start_ts = UINT64_MAX; + bbr->congestion_recovery_start_ts = UINT64_MAX; + bbr->in_loss_recovery = 0; + bbr->packet_conservation = 0; + + bbr_save_cwnd(bbr, cstat); + cstat->cwnd = cstat->bytes_in_flight + cstat->max_udp_payload_size; + cstat->cwnd = + ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_udp_payload_size)); +} + +static void bbr2_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + bbr_update_on_ack(bbr, cstat, ack, ts); +} + +static void bbr2_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + bbr_on_transmit(bbr, cstat, pkt->sent_ts); +} + +static void bbr2_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)ts; +} + +static void bbr2_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb); + + bbr_on_init(bbr, cstat, ts); +} + +static void bbr2_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts) { + (void)ccx; + (void)cstat; + (void)event; + (void)ts; +} + +int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, + ngtcp2_tstamp initial_ts, ngtcp2_rand rand, + const ngtcp2_rand_ctx *rand_ctx, + const ngtcp2_mem *mem) { + ngtcp2_bbr2_cc *bbr; + + bbr = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr2_cc)); + if (bbr == NULL) { + return NGTCP2_ERR_NOMEM; + } + + bbr2_cc_init(bbr, cstat, rst, initial_ts, rand, rand_ctx, log); + + cc->ccb = &bbr->ccb; + cc->on_pkt_acked = bbr2_cc_on_pkt_acked; + cc->on_pkt_lost = bbr2_cc_on_pkt_lost; + cc->congestion_event = bbr2_cc_congestion_event; + cc->on_spurious_congestion = bbr2_cc_on_spurious_congestion; + cc->on_persistent_congestion = bbr2_cc_on_persistent_congestion; + cc->on_ack_recv = bbr2_cc_on_ack_recv; + cc->on_pkt_sent = bbr2_cc_on_pkt_sent; + cc->new_rtt_sample = bbr2_cc_new_rtt_sample; + cc->reset = bbr2_cc_reset; + cc->event = bbr2_cc_event; + + return 0; +} + +void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) { + ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr2_cc, ccb); + + bbr2_cc_free(bbr); + ngtcp2_mem_free(mem, bbr); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h new file mode 100644 index 00000000000000..50dc05a5f26121 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr2.h @@ -0,0 +1,149 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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 NGTCP2_BBR2_H +#define NGTCP2_BBR2_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_cc.h" +#include "ngtcp2_window_filter.h" + +typedef struct ngtcp2_rst ngtcp2_rst; + +typedef enum ngtcp2_bbr2_state { + NGTCP2_BBR2_STATE_STARTUP, + NGTCP2_BBR2_STATE_DRAIN, + NGTCP2_BBR2_STATE_PROBE_BW_DOWN, + NGTCP2_BBR2_STATE_PROBE_BW_CRUISE, + NGTCP2_BBR2_STATE_PROBE_BW_REFILL, + NGTCP2_BBR2_STATE_PROBE_BW_UP, + NGTCP2_BBR2_STATE_PROBE_RTT, +} ngtcp2_bbr2_state; + +typedef enum ngtcp2_bbr2_ack_phase { + NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING, + NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING, + NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK, + NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING, +} ngtcp2_bbr2_ack_phase; + +/* + * ngtcp2_bbr2_cc is BBR v2 congestion controller, described in + * https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control-01 + */ +typedef struct ngtcp2_bbr2_cc { + ngtcp2_cc_base ccb; + + uint64_t initial_cwnd; + ngtcp2_rst *rst; + ngtcp2_rand rand; + ngtcp2_rand_ctx rand_ctx; + + /* max_bw_filter for tracking the maximum recent delivery rate + samples for estimating max_bw. */ + ngtcp2_window_filter max_bw_filter; + + ngtcp2_window_filter extra_acked_filter; + + ngtcp2_duration min_rtt; + ngtcp2_tstamp min_rtt_stamp; + ngtcp2_tstamp probe_rtt_done_stamp; + int probe_rtt_round_done; + uint64_t prior_cwnd; + int idle_restart; + ngtcp2_tstamp extra_acked_interval_start; + uint64_t extra_acked_delivered; + + /* Congestion signals */ + int loss_in_round; + uint64_t bw_latest; + uint64_t inflight_latest; + + /* Lower bounds */ + uint64_t bw_lo; + uint64_t inflight_lo; + + /* Round counting */ + uint64_t next_round_delivered; + int round_start; + uint64_t round_count; + + /* Full pipe */ + int filled_pipe; + uint64_t full_bw; + size_t full_bw_count; + + /* Pacing rate */ + double pacing_gain; + + ngtcp2_bbr2_state state; + double cwnd_gain; + + int loss_round_start; + uint64_t loss_round_delivered; + uint64_t rounds_since_bw_probe; + uint64_t max_bw; + uint64_t bw; + uint64_t cycle_count; + uint64_t extra_acked; + uint64_t bytes_lost_in_round; + size_t loss_events_in_round; + uint64_t offload_budget; + uint64_t probe_up_cnt; + ngtcp2_tstamp cycle_stamp; + ngtcp2_bbr2_ack_phase ack_phase; + ngtcp2_duration bw_probe_wait; + int bw_probe_samples; + size_t bw_probe_up_rounds; + uint64_t bw_probe_up_acks; + uint64_t inflight_hi; + uint64_t bw_hi; + int probe_rtt_expired; + ngtcp2_duration probe_rtt_min_delay; + ngtcp2_tstamp probe_rtt_min_stamp; + int in_loss_recovery; + int packet_conservation; + uint64_t max_inflight; + ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t congestion_recovery_next_round_delivered; + + uint64_t prior_inflight_lo; + uint64_t prior_inflight_hi; + uint64_t prior_bw_lo; +} ngtcp2_bbr2_cc; + +int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, + ngtcp2_tstamp initial_ts, ngtcp2_rand rand, + const ngtcp2_rand_ctx *rand_ctx, + const ngtcp2_mem *mem); + +void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem); + +#endif /* NGTCP2_BBR2_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c index 373f23d91aed7c..75326d6b76b9ca 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ngtcp2_buf.h" +#include "ngtcp2_mem.h" void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) { buf->begin = buf->pos = buf->last = begin; @@ -31,14 +32,25 @@ void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) { void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; } -size_t ngtcp2_buf_left(const ngtcp2_buf *buf) { - return (size_t)(buf->end - buf->last); +size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) { + return (size_t)(buf->end - buf->begin); } -size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { - return (size_t)(buf->last - buf->pos); +int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len, + const ngtcp2_mem *mem) { + *pbufchain = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_buf_chain) + len); + if (*pbufchain == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*pbufchain)->next = NULL; + + ngtcp2_buf_init(&(*pbufchain)->buf, + (uint8_t *)(*pbufchain) + sizeof(ngtcp2_buf_chain), len); + + return 0; } -size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) { - return (size_t)(buf->end - buf->begin); +void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, bufchain); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h index 8f2c36a1bbb218..107d413382da20 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h @@ -62,13 +62,13 @@ void ngtcp2_buf_reset(ngtcp2_buf *buf); * written to the underlying buffer. In other words, it returns * buf->end - buf->last. */ -size_t ngtcp2_buf_left(const ngtcp2_buf *buf); +#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last) /* * ngtcp2_buf_len returns the number of bytes left to read. In other * words, it returns buf->last - buf->pos. */ -size_t ngtcp2_buf_len(const ngtcp2_buf *buf); +#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos) /* * ngtcp2_buf_cap returns the capacity of the buffer. In other words, @@ -76,4 +76,33 @@ size_t ngtcp2_buf_len(const ngtcp2_buf *buf); */ size_t ngtcp2_buf_cap(const ngtcp2_buf *buf); +/* + * ngtcp2_buf_chain is a linked list of ngtcp2_buf. + */ +typedef struct ngtcp2_buf_chain ngtcp2_buf_chain; + +struct ngtcp2_buf_chain { + ngtcp2_buf_chain *next; + ngtcp2_buf buf; +}; + +/* + * ngtcp2_buf_chain_new creates new ngtcp2_buf_chain and initializes + * the internal buffer with |len| bytes space. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len, + const ngtcp2_mem *mem); + +/* + * ngtcp2_buf_chain_del deletes the resource allocated by |bufchain|. + * It also deletes the memory pointed by |bufchain|. + */ +void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem); + #endif /* NGTCP2_BUF_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c index f4670805c73261..1ee7d96b04776e 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c @@ -43,11 +43,15 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) { ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, size_t pktlen, ngtcp2_pktns_id pktns_id, - ngtcp2_tstamp ts_sent) { + ngtcp2_tstamp sent_ts, uint64_t lost, + uint64_t tx_in_flight, int is_app_limited) { pkt->pkt_num = pkt_num; pkt->pktlen = pktlen; pkt->pktns_id = pktns_id; - pkt->ts_sent = ts_sent; + pkt->sent_ts = sent_ts; + pkt->lost = lost; + pkt->tx_in_flight = tx_in_flight; + pkt->is_app_limited = is_app_limited; return pkt; } @@ -106,7 +110,7 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, uint64_t m; (void)ts; - if (in_congestion_recovery(cstat, pkt->ts_sent)) { + if (in_congestion_recovery(cstat, pkt->sent_ts)) { return; } @@ -129,12 +133,12 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, } void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) { ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); uint64_t min_cwnd; - if (in_congestion_recovery(cstat, ts_sent)) { + if (in_congestion_recovery(cstat, sent_ts)) { return; } @@ -162,9 +166,10 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *ccx, } void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); uint64_t target_cwnd, initcwnd; + (void)ack; (void)ts; /* TODO Use sliding window for min rtt measurement */ @@ -184,8 +189,12 @@ void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, } } -void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx) { +void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); + (void)cstat; + (void)ts; + reno_cc_reset(cc); } @@ -198,6 +207,14 @@ static void cubic_cc_reset(ngtcp2_cubic_cc *cc) { cc->epoch_start = UINT64_MAX; cc->k = 0; + cc->prior.cwnd = 0; + cc->prior.ssthresh = 0; + cc->prior.w_last_max = 0; + cc->prior.w_tcp = 0; + cc->prior.origin_point = 0; + cc->prior.epoch_start = UINT64_MAX; + cc->prior.k = 0; + cc->rtt_sample_count = 0; cc->current_round_min_rtt = UINT64_MAX; cc->last_round_min_rtt = UINT64_MAX; @@ -225,6 +242,7 @@ int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, cc->ccb = &cubic_cc->ccb; cc->on_pkt_acked = ngtcp2_cc_cubic_cc_on_pkt_acked; cc->congestion_event = ngtcp2_cc_cubic_cc_congestion_event; + cc->on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion; cc->on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion; cc->on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv; cc->on_pkt_sent = ngtcp2_cc_cubic_cc_on_pkt_sent; @@ -300,7 +318,7 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, cc->window_end = -1; } - if (in_congestion_recovery(cstat, pkt->ts_sent)) { + if (in_congestion_recovery(cstat, pkt->sent_ts)) { return; } @@ -421,15 +439,25 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) { ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); uint64_t min_cwnd; - if (in_congestion_recovery(cstat, ts_sent)) { + if (in_congestion_recovery(cstat, sent_ts)) { return; } + if (cc->prior.cwnd < cstat->cwnd) { + cc->prior.cwnd = cstat->cwnd; + cc->prior.ssthresh = cstat->ssthresh; + cc->prior.w_last_max = cc->w_last_max; + cc->prior.w_tcp = cc->w_tcp; + cc->prior.origin_point = cc->origin_point; + cc->prior.epoch_start = cc->epoch_start; + cc->prior.k = cc->k; + } + cstat->congestion_recovery_start_ts = ts; cc->epoch_start = UINT64_MAX; @@ -449,6 +477,40 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx, cstat->cwnd); } +void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + (void)ts; + + if (cstat->cwnd >= cc->prior.cwnd) { + return; + } + + cstat->congestion_recovery_start_ts = UINT64_MAX; + + cstat->cwnd = cc->prior.cwnd; + cstat->ssthresh = cc->prior.ssthresh; + cc->w_last_max = cc->prior.w_last_max; + cc->w_tcp = cc->prior.w_tcp; + cc->origin_point = cc->prior.origin_point; + cc->epoch_start = cc->prior.epoch_start; + cc->k = cc->prior.k; + + cc->prior.cwnd = 0; + cc->prior.ssthresh = 0; + cc->prior.w_last_max = 0; + cc->prior.w_tcp = 0; + cc->prior.origin_point = 0; + cc->prior.epoch_start = UINT64_MAX; + cc->prior.k = 0; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "spurious congestion is detected and congestion state is " + "restored cwnd=%" PRIu64, + cstat->cwnd); +} + void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { @@ -460,9 +522,11 @@ void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx, } void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); uint64_t target_cwnd, initcwnd; + (void)ack; (void)ts; /* TODO Use sliding window for min rtt measurement */ @@ -511,8 +575,12 @@ void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, ++cc->rtt_sample_count; } -void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx) { +void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + (void)cstat; + (void)ts; + cubic_cc_reset(cc); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h index e1ca7594802994..6d9e0c2459ece4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h @@ -36,6 +36,273 @@ typedef struct ngtcp2_log ngtcp2_log; +/** + * @struct + * + * :type:`ngtcp2_cc_base` is the base structure of custom congestion + * control algorithm. It must be the first field of custom congestion + * controller. + */ +typedef struct ngtcp2_cc_base { + /** + * :member:`log` is ngtcp2 library internal logger. + */ + ngtcp2_log *log; +} ngtcp2_cc_base; + +/** + * @struct + * + * :type:`ngtcp2_cc_pkt` is a convenient structure to include + * acked/lost/sent packet. + */ +typedef struct ngtcp2_cc_pkt { + /** + * :member:`pkt_num` is the packet number + */ + int64_t pkt_num; + /** + * :member:`pktlen` is the length of packet. + */ + size_t pktlen; + /** + * :member:`pktns_id` is the ID of packet number space which this + * packet belongs to. + */ + ngtcp2_pktns_id pktns_id; + /** + * :member:`sent_ts` is the timestamp when packet is sent. + */ + ngtcp2_tstamp sent_ts; + /** + * :member:`lost` is the number of bytes lost when this packet was + * sent. + */ + uint64_t lost; + /** + * :member:`tx_in_flight` is the bytes in flight when this packet + * was sent. + */ + uint64_t tx_in_flight; + /** + * :member:`is_app_limited` is nonzero if the connection is + * app-limited when this packet was sent. + */ + int is_app_limited; +} ngtcp2_cc_pkt; + +/** + * @struct + * + * :type:`ngtcp2_cc_ack` is a convenient structure which stores + * acknowledged and lost bytes. + */ +typedef struct ngtcp2_cc_ack { + /** + * :member:`prior_bytes_in_flight` is the in-flight bytes before + * processing this ACK. + */ + uint64_t prior_bytes_in_flight; + /** + * :member:`bytes_delivered` is the number of bytes acknowledged. + */ + uint64_t bytes_delivered; + /** + * :member:`bytes_lost` is the number of bytes declared lost. + */ + uint64_t bytes_lost; + /** + * :member:`pkt_delivered` is the cumulative acknowledged bytes when + * the last packet acknowledged by this ACK was sent. + */ + uint64_t pkt_delivered; + /** + * :member:`largest_acked_sent_ts` is the time when the largest + * acknowledged packet was sent. + */ + ngtcp2_tstamp largest_acked_sent_ts; + /** + * :member:`rtt` is the RTT sample. It is UINT64_MAX if no RTT + * sample is available. + */ + ngtcp2_duration rtt; +} ngtcp2_cc_ack; + +typedef struct ngtcp2_cc ngtcp2_cc; + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is + * called with an acknowledged packet. + */ +typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_lost` is a callback function which is + * called with a lost packet. + */ +typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, + ngtcp2_tstamp ts); +/** + * @functypedef + * + * :type:`ngtcp2_cc_congestion_event` is a callback function which is + * called when congestion event happens (e.g., when packet is lost). + */ +typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_ts, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_spurious_congestion` is a callback function + * which is called when a spurious congestion is detected. + */ +typedef void (*ngtcp2_cc_on_spurious_congestion)(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function + * which is called when persistent congestion is established. + */ +typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is + * called when an acknowledgement is received. + */ +typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is + * called when an ack-eliciting packet is sent. + */ +typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is + * called when new RTT sample is obtained. + */ +typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_reset` is a callback function which is called when + * congestion state must be reset. + */ +typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @enum + * + * :type:`ngtcp2_cc_event_type` defines congestion control events. + */ +typedef enum ngtcp2_cc_event_type { + /** + * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet + * is sent and no other ack-eliciting packet is present. + */ + NGTCP2_CC_EVENT_TYPE_TX_START +} ngtcp2_cc_event_type; + +/** + * @functypedef + * + * :type:`ngtcp2_cc_event` is a callback function which is called when + * a specific event happens. + */ +typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts); + +/** + * @struct + * + * :type:`ngtcp2_cc` is congestion control algorithm interface to + * allow custom implementation. + */ +typedef struct ngtcp2_cc { + /** + * :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which + * usually contains a state. + */ + ngtcp2_cc_base *ccb; + /** + * :member:`on_pkt_acked` is a callback function which is called + * when a packet is acknowledged. + */ + ngtcp2_cc_on_pkt_acked on_pkt_acked; + /** + * :member:`on_pkt_lost` is a callback function which is called when + * a packet is lost. + */ + ngtcp2_cc_on_pkt_lost on_pkt_lost; + /** + * :member:`congestion_event` is a callback function which is called + * when congestion event happens (.e.g, packet is lost). + */ + ngtcp2_cc_congestion_event congestion_event; + /** + * :member:`on_spurious_congestion` is a callback function which is + * called when a spurious congestion is detected. + */ + ngtcp2_cc_on_spurious_congestion on_spurious_congestion; + /** + * :member:`on_persistent_congestion` is a callback function which + * is called when persistent congestion is established. + */ + ngtcp2_cc_on_persistent_congestion on_persistent_congestion; + /** + * :member:`on_ack_recv` is a callback function which is called when + * an acknowledgement is received. + */ + ngtcp2_cc_on_ack_recv on_ack_recv; + /** + * :member:`on_pkt_sent` is a callback function which is called when + * ack-eliciting packet is sent. + */ + ngtcp2_cc_on_pkt_sent on_pkt_sent; + /** + * :member:`new_rtt_sample` is a callback function which is called + * when new RTT sample is obtained. + */ + ngtcp2_cc_new_rtt_sample new_rtt_sample; + /** + * :member:`reset` is a callback function which is called when + * congestion control state must be reset. + */ + ngtcp2_cc_reset reset; + /** + * :member:`event` is a callback function which is called when a + * specific event happens. + */ + ngtcp2_cc_event event; +} ngtcp2_cc; + /* * ngtcp2_cc_compute_initcwnd computes initial cwnd. */ @@ -43,7 +310,8 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_packet_size); ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, size_t pktlen, ngtcp2_pktns_id pktns_id, - ngtcp2_tstamp ts_sent); + ngtcp2_tstamp sent_ts, uint64_t lost, + uint64_t tx_in_flight, int is_app_limited); /* ngtcp2_reno_cc is the RENO congestion controller. */ typedef struct ngtcp2_reno_cc { @@ -66,7 +334,7 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts); void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, @@ -74,9 +342,10 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, ngtcp2_tstamp ts); void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); -void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc); +void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); /* ngtcp2_cubic_cc is CUBIC congestion controller. */ typedef struct ngtcp2_cubic_cc { @@ -88,6 +357,18 @@ typedef struct ngtcp2_cubic_cc { uint64_t origin_point; ngtcp2_tstamp epoch_start; uint64_t k; + /* prior stores the congestion state when a congestion event occurs + in order to restore the state when it turns out that the event is + spurious. */ + struct { + uint64_t cwnd; + uint64_t ssthresh; + uint64_t w_last_max; + uint64_t w_tcp; + uint64_t origin_point; + ngtcp2_tstamp epoch_start; + uint64_t k; + } prior; /* HyStart++ variables */ size_t rtt_sample_count; uint64_t current_round_min_rtt; @@ -111,15 +392,19 @@ void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts_sent, + ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts); +void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); + const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt); @@ -127,7 +412,8 @@ void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); -void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc); +void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_cc_event_type event, ngtcp2_tstamp ts); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c index cdf31f8f624768..f3b92b569ec928 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c @@ -30,7 +30,7 @@ #include "ngtcp2_path.h" #include "ngtcp2_str.h" -void ngtcp2_cid_zero(ngtcp2_cid *cid) { cid->datalen = 0; } +void ngtcp2_cid_zero(ngtcp2_cid *cid) { memset(cid, 0, sizeof(*cid)); } void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) { assert(datalen <= NGTCP2_MAX_CIDLEN); @@ -56,23 +56,17 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) { int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; } -void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token) { +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid) { scid->pe.index = NGTCP2_PQ_BAD_INDEX; scid->seq = seq; scid->cid = *cid; - scid->ts_retired = UINT64_MAX; + scid->retired_ts = UINT64_MAX; scid->flags = NGTCP2_SCID_FLAG_NONE; - if (token) { - memcpy(scid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); - } else { - memset(scid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); - } } void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) { - ngtcp2_scid_init(dest, src->seq, &src->cid, src->token); - dest->ts_retired = src->ts_retired; + ngtcp2_scid_init(dest, src->seq, &src->cid); + dest->retired_ts = src->retired_ts; dest->flags = src->flags; } @@ -82,35 +76,58 @@ void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, dcid->cid = *cid; if (token) { memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); + dcid->flags = NGTCP2_DCID_FLAG_TOKEN_PRESENT; } else { - memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); + dcid->flags = NGTCP2_DCID_FLAG_NONE; } ngtcp2_path_storage_zero(&dcid->ps); - dcid->ts_retired = UINT64_MAX; - dcid->flags = NGTCP2_DCID_FLAG_NONE; + dcid->retired_ts = UINT64_MAX; + dcid->bound_ts = UINT64_MAX; dcid->bytes_sent = 0; dcid->bytes_recv = 0; + dcid->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE; +} + +void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token) { + assert(token); + + dcid->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT; + memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path) { + ngtcp2_path_copy(&dcid->ps.path, path); } void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { - ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token); + ngtcp2_dcid_init(dest, src->seq, &src->cid, + (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? src->token + : NULL); ngtcp2_path_copy(&dest->ps.path, &src->ps.path); - dest->ts_retired = src->ts_retired; + dest->retired_ts = src->retired_ts; + dest->bound_ts = src->bound_ts; dest->flags = src->flags; dest->bytes_sent = src->bytes_sent; dest->bytes_recv = src->bytes_recv; + dest->max_udp_payload_size = src->max_udp_payload_size; } void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { dest->seq = src->seq; dest->cid = src->cid; - memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + if (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) { + dest->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT; + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + } else if (dest->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) { + dest->flags &= (uint8_t)~NGTCP2_DCID_FLAG_TOKEN_PRESENT; + } } int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token) { if (dcid->seq == seq) { return ngtcp2_cid_eq(&dcid->cid, cid) && + (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) && memcmp(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN) == 0 ? 0 @@ -119,3 +136,12 @@ int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO; } + +int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, + const uint8_t *token) { + return (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) && + ngtcp2_cmemeq(dcid->token, token, + NGTCP2_STATELESS_RESET_TOKENLEN) + ? 0 + : NGTCP2_ERR_INVALID_ARGUMENT; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h index a356b432d4f548..0b37441178c72a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h @@ -34,14 +34,14 @@ #include "ngtcp2_pq.h" #include "ngtcp2_path.h" -/* NGTCP2_SCID_FLAG_NONE indicats that no flag is set. */ -#define NGTCP2_SCID_FLAG_NONE 0x00 +/* NGTCP2_SCID_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_SCID_FLAG_NONE 0x00u /* NGTCP2_SCID_FLAG_USED indicates that a local endpoint observed that a remote endpoint uses a particular Connection ID. */ -#define NGTCP2_SCID_FLAG_USED 0x01 +#define NGTCP2_SCID_FLAG_USED 0x01u /* NGTCP2_SCID_FLAG_RETIRED indicates that a particular Connection ID is retired. */ -#define NGTCP2_SCID_FLAG_RETIRED 0x02 +#define NGTCP2_SCID_FLAG_RETIRED 0x02u typedef struct ngtcp2_scid { ngtcp2_pq_entry pe; @@ -49,22 +49,21 @@ typedef struct ngtcp2_scid { uint64_t seq; /* cid is a connection ID */ ngtcp2_cid cid; - /* ts_retired is the timestamp when peer tells that this CID is + /* retired_ts is the timestamp when peer tells that this CID is retired. */ - ngtcp2_tstamp ts_retired; + ngtcp2_tstamp retired_ts; /* flags is the bitwise OR of zero or more of NGTCP2_SCID_FLAG_*. */ uint8_t flags; - /* token is a stateless reset token associated to this CID. - Actually, the stateless reset token is tied to the connection, - not to the particular connection ID. */ - uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; } ngtcp2_scid; /* NGTCP2_DCID_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_DCID_FLAG_NONE 0x00 +#define NGTCP2_DCID_FLAG_NONE 0x00u /* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path has been validated. */ -#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01 +#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01u +/* NGTCP2_DCID_FLAG_TOKEN_PRESENT indicates that a stateless reset + token is set in token field. */ +#define NGTCP2_DCID_FLAG_TOKEN_PRESENT 0x02u typedef struct ngtcp2_dcid { /* seq is the sequence number associated to the CID. */ @@ -74,14 +73,21 @@ typedef struct ngtcp2_dcid { /* path is a path which cid is bound to. The addresses are zero length if cid has not been bound to a particular path yet. */ ngtcp2_path_storage ps; - /* ts_retired is the timestamp when peer tells that this CID is + /* retired_ts is the timestamp when peer tells that this CID is retired. */ - ngtcp2_tstamp ts_retired; + ngtcp2_tstamp retired_ts; + /* bound_ts is the timestamp when this connection ID is bound to a + particular path. It is only assigned when a connection ID is + used just for sending PATH_RESPONSE and is not zero-length. */ + ngtcp2_tstamp bound_ts; /* bytes_sent is the number of bytes sent to an associated path. */ uint64_t bytes_sent; /* bytes_recv is the number of bytes received from an associated path. */ uint64_t bytes_recv; + /* max_udp_payload_size is the maximum size of UDP payload that is + allowed to send to this path. */ + size_t max_udp_payload_size; /* flags is bitwise OR of zero or more of NGTCP2_DCID_FLAG_*. */ uint8_t flags; /* token is a stateless reset token associated to this CID. @@ -93,12 +99,6 @@ typedef struct ngtcp2_dcid { /* ngtcp2_cid_zero makes |cid| zero-length. */ void ngtcp2_cid_zero(ngtcp2_cid *cid); -/* - * ngtcp2_cid_eq returns nonzero if |cid| and |other| share the same - * connection ID. - */ -int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other); - /* * ngtcp2_cid_less returns nonzero if |lhs| is lexicographical smaller * than |rhs|. @@ -112,12 +112,9 @@ int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs); int ngtcp2_cid_empty(const ngtcp2_cid *cid); /* - * ngtcp2_scid_init initializes |scid| with the given parameters. If - * |token| is NULL, the function fills scid->token it with 0. |token| - * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + * ngtcp2_scid_init initializes |scid| with the given parameters. */ -void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, - const uint8_t *token); +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid); /* * ngtcp2_scid_copy copies |src| into |dest|. @@ -132,6 +129,19 @@ void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src); void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token); +/* + * ngtcp2_dcid_set_token sets |token| to |dcid|. |token| must not be + * NULL and must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + */ +void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token); + +/* + * ngtcp2_dcid_set_path sets |path| to |dcid|. It sets + * max_udp_payload_size to the minimum UDP payload size supported + * by the IP protocol version. + */ +void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path); + /* * ngtcp2_dcid_copy copies |src| into |dest|. */ @@ -150,4 +160,16 @@ void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src); int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token); +/* + * ngtcp2_dcid_verify_stateless_reset_token verifies stateless reset + * token |token| against the one included in |dcid|. This function + * returns 0 if the verification succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Tokens do not match; or |dcid| does not contain a token. + */ +int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, + const uint8_t *token); + #endif /* NGTCP2_CID_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c index c8b1d15db5a6f7..a3135680ca160f 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -26,7 +26,6 @@ #include #include -#include #include "ngtcp2_macro.h" #include "ngtcp2_log.h" @@ -43,6 +42,9 @@ /* NGTCP2_FLOW_WINDOW_SCALING_FACTOR is the growth factor of flow control window. */ #define NGTCP2_FLOW_WINDOW_SCALING_FACTOR 2 +/* NGTCP2_MIN_COALESCED_PAYLOADLEN is the minimum length of QUIC + packet payload that should be coalesced to a long packet. */ +#define NGTCP2_MIN_COALESCED_PAYLOADLEN 128 /* * conn_local_stream returns nonzero if |stream_id| indicates that it @@ -58,6 +60,15 @@ static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) { */ static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; } +/* + * conn_is_handshake_completed returns nonzero if QUIC handshake has + * completed. + */ +static int conn_is_handshake_completed(ngtcp2_conn *conn) { + return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + conn->pktns.crypto.rx.ckm && conn->pktns.crypto.tx.ckm; +} + static int conn_call_recv_client_initial(ngtcp2_conn *conn, const ngtcp2_cid *dcid) { int rv; @@ -123,6 +134,8 @@ static int conn_call_recv_crypto_data(ngtcp2_conn *conn, case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: case NGTCP2_ERR_TRANSPORT_PARAM: case NGTCP2_ERR_PROTO: + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + case NGTCP2_ERR_NOMEM: case NGTCP2_ERR_CALLBACK_FAILURE: return rv; default: @@ -145,16 +158,21 @@ static int conn_call_stream_open(ngtcp2_conn *conn, ngtcp2_strm *strm) { return 0; } -static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { +static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm) { int rv; + uint32_t flags = NGTCP2_STREAM_CLOSE_FLAG_NONE; if (!conn->callbacks.stream_close) { return 0; } - rv = conn->callbacks.stream_close(conn, strm->stream_id, app_error_code, - conn->user_data, strm->stream_user_data); + if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) { + flags |= NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET; + } + + rv = conn->callbacks.stream_close(conn, flags, strm->stream_id, + strm->app_error_code, conn->user_data, + strm->stream_user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -245,15 +263,21 @@ static int conn_call_remove_connection_id(ngtcp2_conn *conn, return 0; } -static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path, +static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv, ngtcp2_path_validation_result res) { int rv; + uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE; if (!conn->callbacks.path_validation) { return 0; } - rv = conn->callbacks.path_validation(conn, path, res, conn->user_data); + if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) { + flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR; + } + + rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path, res, + conn->user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -262,17 +286,18 @@ static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path, } static int conn_call_select_preferred_addr(ngtcp2_conn *conn, - ngtcp2_addr *dest) { + ngtcp2_path *dest) { int rv; if (!conn->callbacks.select_preferred_addr) { return 0; } - assert(conn->remote.transport_params.preferred_address_present); + assert(conn->remote.transport_params); + assert(conn->remote.transport_params->preferred_address_present); rv = conn->callbacks.select_preferred_addr( - conn, dest, &conn->remote.transport_params.preferred_address, + conn, dest, &conn->remote.transport_params->preferred_address, conn->user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; @@ -345,8 +370,7 @@ static int conn_call_dcid_status(ngtcp2_conn *conn, rv = conn->callbacks.dcid_status( conn, (int)type, dcid->seq, &dcid->cid, - ngtcp2_check_invalid_stateless_reset_token(dcid->token) ? NULL - : dcid->token, + (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? dcid->token : NULL, conn->user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; @@ -366,6 +390,24 @@ static int conn_call_deactivate_dcid(ngtcp2_conn *conn, conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid); } +static int conn_call_stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *stream_user_data) { + int rv; + + if (!conn->callbacks.stream_stop_sending) { + return 0; + } + + rv = conn->callbacks.stream_stop_sending(conn, stream_id, app_error_code, + conn->user_data, stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static void conn_call_delete_crypto_aead_ctx(ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx) { if (!aead_ctx->native_handle) { @@ -389,6 +431,210 @@ conn_call_delete_crypto_cipher_ctx(ngtcp2_conn *conn, conn->callbacks.delete_crypto_cipher_ctx(conn, cipher_ctx, conn->user_data); } +static int conn_call_client_initial(ngtcp2_conn *conn) { + int rv; + + assert(conn->callbacks.client_initial); + + rv = conn->callbacks.client_initial(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_get_path_challenge_data(ngtcp2_conn *conn, uint8_t *data) { + int rv; + + assert(conn->callbacks.get_path_challenge_data); + + rv = conn->callbacks.get_path_challenge_data(conn, data, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_version_negotiation(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, size_t nsv) { + int rv; + + if (!conn->callbacks.recv_version_negotiation) { + return 0; + } + + rv = conn->callbacks.recv_version_negotiation(conn, hd, sv, nsv, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) { + int rv; + + assert(conn->callbacks.recv_retry); + + rv = conn->callbacks.recv_retry(conn, hd, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int +conn_call_recv_stateless_reset(ngtcp2_conn *conn, + const ngtcp2_pkt_stateless_reset *sr) { + int rv; + + if (!conn->callbacks.recv_stateless_reset) { + return 0; + } + + rv = conn->callbacks.recv_stateless_reset(conn, sr, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_new_token(ngtcp2_conn *conn, + const ngtcp2_vec *token) { + int rv; + + if (!conn->callbacks.recv_new_token) { + return 0; + } + + rv = conn->callbacks.recv_new_token(conn, token, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_handshake_confirmed(ngtcp2_conn *conn) { + int rv; + + if (!conn->callbacks.handshake_confirmed) { + return 0; + } + + rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_datagram(ngtcp2_conn *conn, + const ngtcp2_datagram *fr) { + const uint8_t *data; + size_t datalen; + int rv; + uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE; + + if (!conn->callbacks.recv_datagram) { + return 0; + } + + if (fr->datacnt) { + assert(fr->datacnt == 1); + + data = fr->data->base; + datalen = fr->data->len; + } else { + data = NULL; + datalen = 0; + } + + if (!conn_is_handshake_completed(conn)) { + flags |= NGTCP2_DATAGRAM_FLAG_EARLY; + } + + rv = conn->callbacks.recv_datagram(conn, flags, data, datalen, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int +conn_call_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, + ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, + const uint8_t *current_rx_secret, + const uint8_t *current_tx_secret, size_t secretlen) { + int rv; + + assert(conn->callbacks.update_key); + + rv = conn->callbacks.update_key( + conn, rx_secret, tx_secret, rx_aead_ctx, rx_iv, tx_aead_ctx, tx_iv, + current_rx_secret, current_tx_secret, secretlen, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_version_negotiation(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *dcid) { + int rv; + + assert(conn->callbacks.version_negotiation); + + rv = + conn->callbacks.version_negotiation(conn, version, dcid, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_rx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level) { + int rv; + + if (!conn->callbacks.recv_rx_key) { + return 0; + } + + rv = conn->callbacks.recv_rx_key(conn, level, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_tx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level) { + int rv; + + if (!conn->callbacks.recv_tx_key) { + return 0; + } + + rv = conn->callbacks.recv_tx_key(conn, level, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static int crypto_offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return *(int64_t *)lhs < *(int64_t *)rhs; @@ -396,15 +642,13 @@ static int crypto_offset_less(const ngtcp2_ksl_key *lhs, static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, - ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { int rv; memset(pktns, 0, sizeof(*pktns)); - rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem); - if (rv != 0) { - return rv; - } + ngtcp2_gaptr_init(&pktns->rx.pngap, mem); pktns->tx.last_pkt_num = -1; pktns->rx.max_pkt_num = -1; @@ -415,27 +659,17 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, goto fail_acktr_init; } - rv = ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, - NULL, mem); - if (rv != 0) { - goto fail_crypto_init; - } + ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, + NULL, mem); - rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, - sizeof(uint64_t), mem); - if (rv != 0) { - goto fail_tx_frq_init; - } + ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, sizeof(uint64_t), + mem); ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log, - qlog, mem); + qlog, rtb_entry_objalloc, frc_objalloc, mem); return 0; -fail_tx_frq_init: - ngtcp2_strm_free(&pktns->crypto.strm); -fail_crypto_init: - ngtcp2_acktr_free(&pktns->acktr); fail_acktr_init: ngtcp2_gaptr_free(&pktns->rx.pngap); @@ -444,7 +678,8 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, - ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { int rv; *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); @@ -452,7 +687,8 @@ static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, return NGTCP2_ERR_NOMEM; } - rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, mem); + rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, rtb_entry_objalloc, + frc_objalloc, mem); if (rv != 0) { ngtcp2_mem_free(mem, *ppktns); } @@ -481,13 +717,27 @@ static void delete_buffed_pkts(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { } } +static void delete_buf_chain(ngtcp2_buf_chain *bufchain, + const ngtcp2_mem *mem) { + ngtcp2_buf_chain *next; + + for (; bufchain;) { + next = bufchain->next; + ngtcp2_buf_chain_del(bufchain, mem); + bufchain = next; + } +} + static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { ngtcp2_frame_chain *frc; ngtcp2_ksl_it it; + delete_buf_chain(pktns->crypto.tx.data, mem); + delete_buffed_pkts(pktns->rx.buffed_pkts, mem); - ngtcp2_frame_chain_list_del(pktns->tx.frq, mem); + ngtcp2_frame_chain_list_objalloc_del(pktns->tx.frq, pktns->rtb.frc_objalloc, + mem); ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem); ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem); @@ -495,7 +745,7 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, mem); + ngtcp2_frame_chain_objalloc_del(frc, pktns->rtb.frc_objalloc, mem); } ngtcp2_ksl_free(&pktns->crypto.tx.frq); @@ -524,6 +774,12 @@ static void cc_del(ngtcp2_cc *cc, ngtcp2_cc_algo cc_algo, case NGTCP2_CC_ALGO_CUBIC: ngtcp2_cc_cubic_cc_free(cc, mem); break; + case NGTCP2_CC_ALGO_BBR: + ngtcp2_cc_bbr_cc_free(cc, mem); + break; + case NGTCP2_CC_ALGO_BBR2: + ngtcp2_cc_bbr2_cc_free(cc, mem); + break; default: break; } @@ -533,12 +789,12 @@ static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return ngtcp2_cid_less(lhs, rhs); } -static int ts_retired_less(const ngtcp2_pq_entry *lhs, +static int retired_ts_less(const ngtcp2_pq_entry *lhs, const ngtcp2_pq_entry *rhs) { const ngtcp2_scid *a = ngtcp2_struct_of(lhs, ngtcp2_scid, pe); const ngtcp2_scid *b = ngtcp2_struct_of(rhs, ngtcp2_scid, pe); - return a->ts_retired < b->ts_retired; + return a->retired_ts < b->retired_ts; } /* @@ -559,6 +815,8 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, cstat->congestion_recovery_start_ts = UINT64_MAX; cstat->bytes_in_flight = 0; cstat->delivery_rate_sec = 0; + cstat->pacing_rate = 0.0; + cstat->send_quantum = SIZE_MAX; } /* @@ -566,7 +824,7 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, * function */ static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) { - // Initializes them with UINT64_MAX. + /* Initializes them with UINT64_MAX. */ memset(cstat->loss_time, 0xff, sizeof(cstat->loss_time)); memset(cstat->last_tx_pkt_ts, 0xff, sizeof(cstat->last_tx_pkt_ts)); } @@ -605,10 +863,14 @@ static ngtcp2_duration compute_pto(ngtcp2_duration smoothed_rtt, static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { ngtcp2_duration initial_rtt = conn->local.settings.initial_rtt; - ngtcp2_duration max_ack_delay = - pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION - ? conn->remote.transport_params.max_ack_delay - : 0; + ngtcp2_duration max_ack_delay; + + if (pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION && + conn->remote.transport_params) { + max_ack_delay = conn->remote.transport_params->max_ack_delay; + } else { + max_ack_delay = 0; + } return compute_pto(initial_rtt, initial_rtt / 2, max_ack_delay); } @@ -618,15 +880,24 @@ static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn, static ngtcp2_duration conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { ngtcp2_conn_stat *cstat = &conn->cstat; - ngtcp2_duration max_ack_delay = - pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION - ? conn->remote.transport_params.max_ack_delay - : 0; + ngtcp2_duration max_ack_delay; + + if (pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION && + conn->remote.transport_params) { + max_ack_delay = conn->remote.transport_params->max_ack_delay; + } else { + max_ack_delay = 0; + } return compute_pto(cstat->smoothed_rtt, cstat->rttvar, max_ack_delay); } +ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, + ngtcp2_pktns *pktns) { + return conn_compute_pto(conn, pktns); +} + static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, - uint8_t *prtb_entry_flags, ngtcp2_pktns *pktns, + uint16_t *prtb_entry_flags, ngtcp2_pktns *pktns, const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) { assert(pi); @@ -718,23 +989,103 @@ static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) { pktns->tx.ecn.validation_pkt_lost = 0; } +/* server_default_other_versions is the default other_versions field + sent by server. */ +static uint8_t server_default_other_versions[] = {0, 0, 0, 1}; + +/* + * other_versions_new allocates new buffer, and writes |versions| of + * length |versionslen| in network byte order, suitable for sending in + * other_versions field of version_information QUIC transport + * parameter. The pointer to the allocated buffer is assigned to + * |*pbuf|. + * + * This function returns 0 if it succeeds, or one of the negative + * error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int other_versions_new(uint8_t **pbuf, const uint32_t *versions, + size_t versionslen, const ngtcp2_mem *mem) { + size_t i; + uint8_t *buf = ngtcp2_mem_malloc(mem, sizeof(uint32_t) * versionslen); + + if (buf == NULL) { + return NGTCP2_ERR_NOMEM; + } + + *pbuf = buf; + + for (i = 0; i < versionslen; ++i) { + buf = ngtcp2_put_uint32be(buf, versions[i]); + } + + return 0; +} + +static void +conn_set_local_transport_params(ngtcp2_conn *conn, + const ngtcp2_transport_params *params) { + ngtcp2_transport_params *p = &conn->local.transport_params; + uint32_t chosen_version = p->version_info.chosen_version; + + *p = *params; + + /* grease_quic_bit is always enabled. */ + p->grease_quic_bit = 1; + + if (conn->server) { + p->version_info.chosen_version = chosen_version; + } else { + p->version_info.chosen_version = conn->client_chosen_version; + } + p->version_info.other_versions = conn->vneg.other_versions; + p->version_info.other_versionslen = conn->vneg.other_versionslen; + p->version_info_present = 1; +} + static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, + uint32_t client_chosen_version, int callbacks_version, + const ngtcp2_callbacks *callbacks, int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, const ngtcp2_mem *mem, void *user_data, int server) { int rv; ngtcp2_scid *scident; uint8_t *buf; + uint8_t fixed_bit_byte; + size_t i; + uint32_t *preferred_versions; + (void)callbacks_version; + (void)settings_version; + (void)transport_params_version; assert(settings->max_window <= NGTCP2_MAX_VARINT); assert(settings->max_stream_window <= NGTCP2_MAX_VARINT); + assert(settings->max_udp_payload_size); + assert(settings->max_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE); assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); assert(params->initial_max_data <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_bidi_local <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_bidi_remote <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_uni <= NGTCP2_MAX_VARINT); + assert(server || callbacks->client_initial); + assert(!server || callbacks->recv_client_initial); + assert(callbacks->recv_crypto_data); + assert(callbacks->encrypt); + assert(callbacks->decrypt); + assert(callbacks->hp_mask); + assert(server || callbacks->recv_retry); + assert(callbacks->rand); + assert(callbacks->get_new_connection_id); + assert(callbacks->update_key); + assert(callbacks->delete_crypto_aead_ctx); + assert(callbacks->delete_crypto_cipher_ctx); + assert(callbacks->get_path_challenge_data); + assert(!server || !ngtcp2_is_reserved_version(client_chosen_version)); if (mem == NULL) { mem = ngtcp2_mem_default(); @@ -746,60 +1097,31 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, goto fail_conn; } - rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.bound, - NGTCP2_MAX_BOUND_DCID_POOL_SIZE, sizeof(ngtcp2_dcid), - mem); - if (rv != 0) { - goto fail_dcid_bound_init; - } + ngtcp2_objalloc_frame_chain_init(&(*pconn)->frc_objalloc, 64, mem); + ngtcp2_objalloc_rtb_entry_init(&(*pconn)->rtb_entry_objalloc, 64, mem); + ngtcp2_objalloc_strm_init(&(*pconn)->strm_objalloc, 64, mem); - rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE, - sizeof(ngtcp2_dcid), mem); - if (rv != 0) { - goto fail_dcid_unused_init; - } + ngtcp2_static_ringbuf_dcid_bound_init(&(*pconn)->dcid.bound); - rv = - ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE, - sizeof(ngtcp2_dcid), mem); - if (rv != 0) { - goto fail_dcid_retired_init; - } + ngtcp2_static_ringbuf_dcid_unused_init(&(*pconn)->dcid.unused); - rv = ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); - if (rv != 0) { - goto fail_seqgap_init; - } + ngtcp2_static_ringbuf_dcid_retired_init(&(*pconn)->dcid.retired); - rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); - if (rv != 0) { - goto fail_scid_set_init; - } + ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); - ngtcp2_pq_init(&(*pconn)->scid.used, ts_retired_less, mem); + ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); - rv = ngtcp2_map_init(&(*pconn)->strms, mem); - if (rv != 0) { - goto fail_strms_init; - } + ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem); + + ngtcp2_map_init(&(*pconn)->strms, mem); ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); - rv = ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); - if (rv != 0) { - goto fail_remote_bidi_idtr_init; - } + ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); - rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); - if (rv != 0) { - goto fail_remote_uni_idtr_init; - } + ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); - rv = ngtcp2_ringbuf_init(&(*pconn)->rx.path_challenge, 4, - sizeof(ngtcp2_path_challenge_entry), mem); - if (rv != 0) { - goto fail_rx_path_challenge_init; - } + ngtcp2_static_ringbuf_path_challenge_init(&(*pconn)->rx.path_challenge); ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf, settings->initial_ts, user_data); @@ -808,17 +1130,18 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, if ((*pconn)->qlog.write) { buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN); if (buf == NULL) { + rv = NGTCP2_ERR_NOMEM; goto fail_qlog_buf; } ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN); } (*pconn)->local.settings = *settings; - (*pconn)->local.transport_params = *params; if (settings->token.len) { buf = ngtcp2_mem_malloc(mem, settings->token.len); if (buf == NULL) { + rv = NGTCP2_ERR_NOMEM; goto fail_token; } memcpy(buf, settings->token.base, settings->token.len); @@ -828,8 +1151,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->local.settings.token.len = 0; } - if (settings->max_udp_payload_size == 0) { - (*pconn)->local.settings.max_udp_payload_size = NGTCP2_DEFAULT_MAX_PKTLEN; + if (!(*pconn)->local.settings.original_version) { + (*pconn)->local.settings.original_version = client_chosen_version; } conn_reset_conn_stat(*pconn, &(*pconn)->cstat); @@ -854,29 +1177,43 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, goto fail_cc_init; } break; - case NGTCP2_CC_ALGO_CUSTOM: - assert(settings->cc); - (*pconn)->cc = *settings->cc; - (*pconn)->cc.ccb->log = &(*pconn)->log; + case NGTCP2_CC_ALGO_BBR: + rv = ngtcp2_cc_bbr_cc_init(&(*pconn)->cc, &(*pconn)->log, &(*pconn)->cstat, + &(*pconn)->rst, settings->initial_ts, + callbacks->rand, &settings->rand_ctx, mem); + if (rv != 0) { + goto fail_cc_init; + } + break; + case NGTCP2_CC_ALGO_BBR2: + rv = ngtcp2_cc_bbr2_cc_init(&(*pconn)->cc, &(*pconn)->log, &(*pconn)->cstat, + &(*pconn)->rst, settings->initial_ts, + callbacks->rand, &settings->rand_ctx, mem); + if (rv != 0) { + goto fail_cc_init; + } break; default: assert(0); } rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_in_pktns_init; } rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_hs_pktns_init; } rv = pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APPLICATION, &(*pconn)->rst, - &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); if (rv != 0) { goto fail_pktns_init; } @@ -889,7 +1226,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, /* Set stateless reset token later if it is available in the local transport parameters */ - ngtcp2_scid_init(scident, 0, scid, NULL); + ngtcp2_scid_init(scident, 0, scid); rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident); if (rv != 0) { @@ -899,22 +1236,111 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, scident = NULL; ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); - ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path); + ngtcp2_dcid_set_path(&(*pconn)->dcid.current, path); rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1); if (rv != 0) { goto fail_seqgap_push; } + if (settings->preferred_versionslen) { + if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + for (i = 0; i < settings->preferred_versionslen; ++i) { + if (settings->preferred_versions[i] == client_chosen_version) { + break; + } + } + + assert(i < settings->preferred_versionslen); + } + + preferred_versions = ngtcp2_mem_malloc( + mem, sizeof(uint32_t) * settings->preferred_versionslen); + if (preferred_versions == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_preferred_versions; + } + + for (i = 0; i < settings->preferred_versionslen; ++i) { + assert(ngtcp2_is_supported_version(settings->preferred_versions[i])); + + preferred_versions[i] = settings->preferred_versions[i]; + } + + (*pconn)->vneg.preferred_versions = preferred_versions; + (*pconn)->vneg.preferred_versionslen = settings->preferred_versionslen; + } + + if (settings->other_versionslen) { + if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + for (i = 0; i < settings->other_versionslen; ++i) { + if (settings->other_versions[i] == client_chosen_version) { + break; + } + } + + assert(i < settings->other_versionslen); + } + + for (i = 0; i < settings->other_versionslen; ++i) { + assert(ngtcp2_is_reserved_version(settings->other_versions[i]) || + ngtcp2_is_supported_version(settings->other_versions[i])); + } + + rv = other_versions_new(&buf, settings->other_versions, + settings->other_versionslen, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = + sizeof(uint32_t) * settings->other_versionslen; + } else if (server) { + if (settings->preferred_versionslen) { + rv = other_versions_new(&buf, settings->preferred_versions, + settings->preferred_versionslen, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = + sizeof(uint32_t) * settings->preferred_versionslen; + } else { + (*pconn)->vneg.other_versions = server_default_other_versions; + (*pconn)->vneg.other_versionslen = sizeof(server_default_other_versions); + } + } else if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { + rv = other_versions_new(&buf, &client_chosen_version, 1, mem); + if (rv != 0) { + goto fail_other_versions; + } + + (*pconn)->vneg.other_versions = buf; + (*pconn)->vneg.other_versionslen = sizeof(uint32_t); + } + + (*pconn)->client_chosen_version = client_chosen_version; + + conn_set_local_transport_params(*pconn, params); + + callbacks->rand(&fixed_bit_byte, 1, &settings->rand_ctx); + if (fixed_bit_byte & 1) { + (*pconn)->flags |= NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT; + } + + (*pconn)->keep_alive.last_ts = UINT64_MAX; + (*pconn)->server = server; (*pconn)->oscid = *scid; (*pconn)->callbacks = *callbacks; - (*pconn)->version = version; (*pconn)->mem = mem; (*pconn)->user_data = user_data; (*pconn)->idle_ts = settings->initial_ts; (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX; (*pconn)->tx.last_max_data_ts = UINT64_MAX; + (*pconn)->tx.pacing.next_ts = UINT64_MAX; (*pconn)->early.discard_started_ts = UINT64_MAX; conn_reset_ecn_validation_state(*pconn); @@ -924,12 +1350,13 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, return 0; +fail_other_versions: + ngtcp2_mem_free(mem, (*pconn)->vneg.preferred_versions); +fail_preferred_versions: fail_seqgap_push: fail_scid_set_insert: ngtcp2_mem_free(mem, scident); fail_scident: - ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); -fail_token: pktns_free(&(*pconn)->pktns, mem); fail_pktns_init: pktns_del((*pconn)->hs_pktns, mem); @@ -938,41 +1365,36 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, fail_in_pktns_init: cc_del(&(*pconn)->cc, settings->cc_algo, mem); fail_cc_init: + ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); +fail_token: ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin); fail_qlog_buf: - ngtcp2_ringbuf_free(&(*pconn)->rx.path_challenge); -fail_rx_path_challenge_init: ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr); -fail_remote_uni_idtr_init: ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr); -fail_remote_bidi_idtr_init: ngtcp2_map_free(&(*pconn)->strms); -fail_strms_init: delete_scid(&(*pconn)->scid.set, mem); ngtcp2_ksl_free(&(*pconn)->scid.set); -fail_scid_set_init: ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap); -fail_seqgap_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.retired); -fail_dcid_retired_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.unused); -fail_dcid_unused_init: - ngtcp2_ringbuf_free(&(*pconn)->dcid.bound); -fail_dcid_bound_init: + ngtcp2_objalloc_free(&(*pconn)->strm_objalloc); + ngtcp2_objalloc_free(&(*pconn)->rtb_entry_objalloc); + ngtcp2_objalloc_free(&(*pconn)->frc_objalloc); ngtcp2_mem_free(mem, *pconn); fail_conn: return rv; } -int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_transport_params *params, - const ngtcp2_mem *mem, void *user_data) { +int ngtcp2_conn_client_new_versioned( + ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params, - mem, user_data, 0); + + rv = conn_new(pconn, dcid, scid, path, client_chosen_version, + callbacks_version, callbacks, settings_version, settings, + transport_params_version, params, mem, user_data, 0); if (rv != 0) { return rv; } @@ -990,18 +1412,22 @@ int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, return 0; } -int ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, const ngtcp2_path *path, - uint32_t version, const ngtcp2_callbacks *callbacks, - const ngtcp2_settings *settings, - const ngtcp2_transport_params *params, - const ngtcp2_mem *mem, void *user_data) { +int ngtcp2_conn_server_new_versioned( + ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + const ngtcp2_path *path, uint32_t client_chosen_version, + int callbacks_version, const ngtcp2_callbacks *callbacks, + int settings_version, const ngtcp2_settings *settings, + int transport_params_version, const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data) { int rv; - rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params, - mem, user_data, 1); + + rv = conn_new(pconn, dcid, scid, path, client_chosen_version, + callbacks_version, callbacks, settings_version, settings, + transport_params_version, params, mem, user_data, 1); if (rv != 0) { return rv; } + (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; (*pconn)->local.bidi.next_stream_id = 1; (*pconn)->local.uni.next_stream_id = 3; @@ -1029,22 +1455,37 @@ static uint64_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) { * sent to the given stream. |len| might be shorted because of * available flow control credits. */ -static size_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, - size_t len) { +static uint64_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t len) { uint64_t fc_credits = conn_fc_credits(conn, strm); - return (size_t)ngtcp2_min((uint64_t)len, fc_credits); + return ngtcp2_min(len, fc_credits); } -static int delete_strms_each(ngtcp2_map_entry *ent, void *ptr) { - const ngtcp2_mem *mem = ptr; - ngtcp2_strm *s = ngtcp2_struct_of(ent, ngtcp2_strm, me); +static int delete_strms_each(void *data, void *ptr) { + ngtcp2_conn *conn = ptr; + ngtcp2_strm *s = data; ngtcp2_strm_free(s); - ngtcp2_mem_free(mem, s); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s); return 0; } +static void conn_vneg_crypto_free(ngtcp2_conn *conn) { + if (conn->vneg.rx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx); + + if (conn->vneg.tx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx); + + ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem); + ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem); +} + void ngtcp2_conn_del(ngtcp2_conn *conn) { if (conn == NULL) { return; @@ -1111,6 +1552,16 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx); + ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); + ngtcp2_transport_params_del(conn->remote.pending_transport_params, conn->mem); + + conn_vneg_crypto_free(conn); + + ngtcp2_mem_free(conn->mem, conn->vneg.preferred_versions); + if (conn->vneg.other_versions != server_default_other_versions) { + ngtcp2_mem_free(conn->mem, conn->vneg.other_versions); + } + ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base); ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_hp_buf.base); ngtcp2_mem_free(conn->mem, conn->local.settings.token.base); @@ -1128,24 +1579,26 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin); - ngtcp2_ringbuf_free(&conn->rx.path_challenge); - + ngtcp2_pmtud_del(conn->pmtud); ngtcp2_pv_del(conn->pv); + ngtcp2_mem_free(conn->mem, conn->rx.ccerr.reason); + ngtcp2_idtr_free(&conn->remote.uni.idtr); ngtcp2_idtr_free(&conn->remote.bidi.idtr); ngtcp2_mem_free(conn->mem, conn->tx.ack); ngtcp2_pq_free(&conn->tx.strmq); - ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn->mem); + ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn); ngtcp2_map_free(&conn->strms); ngtcp2_pq_free(&conn->scid.used); delete_scid(&conn->scid.set, conn->mem); ngtcp2_ksl_free(&conn->scid.set); ngtcp2_gaptr_free(&conn->dcid.seqgap); - ngtcp2_ringbuf_free(&conn->dcid.retired); - ngtcp2_ringbuf_free(&conn->dcid.unused); - ngtcp2_ringbuf_free(&conn->dcid.bound); + + ngtcp2_objalloc_free(&conn->strm_objalloc); + ngtcp2_objalloc_free(&conn->rtb_entry_objalloc); + ngtcp2_objalloc_free(&conn->frc_objalloc); ngtcp2_mem_free(conn->mem, conn); } @@ -1284,7 +1737,7 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, ack->first_ack_blklen = 0; } - if (type == NGTCP2_PKT_SHORT) { + if (type == NGTCP2_PKT_1RTT) { ack->ack_delay_unscaled = ts - largest_ack_ts; ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / (1ULL << ack_delay_exponent); @@ -1412,11 +1865,11 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { ngtcp2_rtb *rtb = &pktns->rtb; int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num; - if (NGTCP2_MAX_PKT_NUM / 2 <= pkt_num) { + if (NGTCP2_MAX_PKT_NUM / 2 < n) { return 4; } - n = n * 2 + 1; + n = n * 2 - 1; if (n > 0xffffff) { return 4; @@ -1430,16 +1883,28 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { return 1; } +/* + * conn_get_cwnd returns cwnd for the current path. + */ +static uint64_t conn_get_cwnd(ngtcp2_conn *conn) { + return conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) + ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size) + : conn->cstat.cwnd; +} + /* * conn_cwnd_is_zero returns nonzero if the number of bytes the local * endpoint can sent at this time is zero. */ static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) { uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; - uint64_t cwnd = - conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) - ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size) - : conn->cstat.cwnd; + uint64_t cwnd = conn_get_cwnd(conn); + + if (bytes_in_flight >= cwnd) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "cwnd limited bytes_in_flight=%lu cwnd=%lu", + bytes_in_flight, cwnd); + } return bytes_in_flight >= cwnd; } @@ -1447,11 +1912,12 @@ static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) { /* * conn_retry_early_payloadlen returns the estimated wire length of * the first STREAM frame of 0-RTT packet which should be - * retransmitted due to Retry frame + * retransmitted due to Retry packet. */ -static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { +static uint64_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { ngtcp2_frame_chain *frc; ngtcp2_strm *strm; + uint64_t len; if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { return 0; @@ -1465,8 +1931,13 @@ static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { } frc = ngtcp2_strm_streamfrq_top(strm); - return ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) + - NGTCP2_STREAM_OVERHEAD; + + len = ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) + + NGTCP2_STREAM_OVERHEAD; + + /* Take the min because in conn_should_pad_pkt we take max in + order to deal with unbreakable DATAGRAM. */ + return ngtcp2_min(len, NGTCP2_MIN_COALESCED_PAYLOADLEN); } return 0; @@ -1479,7 +1950,7 @@ static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); } ngtcp2_ksl_clear(&pktns->crypto.tx.frq); } @@ -1496,7 +1967,7 @@ static uint64_t conn_cryptofrq_unacked_offset(ngtcp2_conn *conn, ngtcp2_range gap; ngtcp2_rtb *rtb = &pktns->rtb; ngtcp2_ksl_it it; - size_t datalen; + uint64_t datalen; (void)conn; @@ -1539,7 +2010,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, frc = ngtcp2_ksl_it_get(&it); fr = &frc->fr.crypto; - ngtcp2_ksl_remove(&pktns->crypto.tx.frq, &it, &fr->offset); + ngtcp2_ksl_remove_hint(&pktns->crypto.tx.frq, &it, &it, &fr->offset); idx = 0; offset = fr->offset; @@ -1561,7 +2032,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, } if (idx == fr->datacnt) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } @@ -1600,10 +2071,10 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, return 0; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx, - conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, fr->datacnt - end_idx, &conn->frc_objalloc, conn->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1622,8 +2093,8 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1656,7 +2127,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, ngtcp2_frame_chain *frc, *nfrc; int rv; size_t nmerged; - size_t datalen; + uint64_t datalen; ngtcp2_vec a[NGTCP2_MAX_CRYPTO_DATACNT]; ngtcp2_vec b[NGTCP2_MAX_CRYPTO_DATACNT]; size_t acnt, bcnt; @@ -1684,10 +2155,11 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, assert(acnt > 0); assert(bcnt > 0); - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, bcnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1700,15 +2172,16 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, acnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1717,14 +2190,14 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); *pfrc = nfrc; return 0; } - left -= datalen; + left -= (size_t)datalen; ngtcp2_vec_copy(a, fr->data, fr->datacnt); acnt = fr->datacnt; @@ -1742,7 +2215,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } if (nfrc == NULL) { @@ -1757,8 +2230,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } break; @@ -1768,7 +2241,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, left -= nmerged; if (nfr->datacnt == 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); continue; } @@ -1776,8 +2249,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1794,9 +2267,10 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, assert(acnt > fr->datacnt); - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, acnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -1805,7 +2279,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); *pfrc = nfrc; @@ -1866,59 +2340,74 @@ static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used, /* * conn_should_pad_pkt returns nonzero if the packet should be padded. * |type| is the type of packet. |left| is the space left in packet - * buffer. |early_datalen| is the number of bytes which will be sent - * in the next, coalesced 0-RTT packet. + * buffer. |write_datalen| is the number of bytes which will be sent + * in the next, coalesced 0-RTT or 1RTT packet. */ static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left, - size_t early_datalen, int ack_eliciting) { - size_t min_payloadlen; + uint64_t write_datalen, int ack_eliciting, + int require_padding) { + uint64_t min_payloadlen; - if (conn->server) { - if (type != NGTCP2_PKT_INITIAL || !ack_eliciting) { - return 0; - } + if (type == NGTCP2_PKT_INITIAL) { + if (conn->server) { + if (!ack_eliciting) { + return 0; + } - if (conn->hs_pktns->crypto.tx.ckm && - (conn->hs_pktns->rtb.probe_pkt_left || - ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || - !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { - /* If we have something to send in Handshake packet, then add - PADDING in Handshake packet. */ - min_payloadlen = 128; + if (conn->hs_pktns->crypto.tx.ckm && + (conn->hs_pktns->rtb.probe_pkt_left || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || + !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { + /* If we have something to send in Handshake packet, then add + PADDING in Handshake packet. */ + min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN; + } else { + return 1; + } } else { - return 1; + if (conn->hs_pktns->crypto.tx.ckm && + (conn->hs_pktns->rtb.probe_pkt_left || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || + !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { + /* If we have something to send in Handshake packet, then add + PADDING in Handshake packet. */ + min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN; + } else if ((!conn->early.ckm && !conn->pktns.crypto.tx.ckm) || + write_datalen == 0) { + return 1; + } else { + /* If we have something to send in 0RTT or 1RTT packet, then + add PADDING in that packet. Take maximum in case that + write_datalen includes DATAGRAM which cannot be split. */ + min_payloadlen = + ngtcp2_max(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); + } } } else { - if (type == NGTCP2_PKT_HANDSHAKE) { - return conn->in_pktns != NULL; - } - - if (conn->hs_pktns->crypto.tx.ckm && - (conn->hs_pktns->rtb.probe_pkt_left || - ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || - !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { - /* If we have something to send in Handshake packet, then add - PADDING in Handshake packet. */ - min_payloadlen = 128; - } else if (!conn->early.ckm || early_datalen == 0) { + assert(type == NGTCP2_PKT_HANDSHAKE); + + if (!require_padding) { + return 0; + } + + if (!conn->pktns.crypto.tx.ckm || write_datalen == 0) { return 1; - } else { - /* If we have something to send in 0RTT packet, then add PADDING - in 0RTT packet. */ - min_payloadlen = ngtcp2_min(early_datalen, 128); } + + min_payloadlen = ngtcp2_max(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); } + /* TODO the next packet type should be taken into account */ return left < /* TODO Assuming that pkt_num is encoded in 1 byte. */ NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen + - conn->oscid.datalen + 1 /* payloadlen bytes - 1 */ + - min_payloadlen + NGTCP2_MAX_AEAD_OVERHEAD; + conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 1 + min_payloadlen + + NGTCP2_MAX_AEAD_OVERHEAD; } static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn->idle_ts = ts; - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; } static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -1926,14 +2415,110 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn->flags |= NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; } -/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00 -/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet should - be padded */ -#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01 -/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come - and it should be encoded into the current packet. */ -#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02 +/* + * conn_keep_alive_enabled returns nonzero if keep-alive is enabled. + */ +static int conn_keep_alive_enabled(ngtcp2_conn *conn) { + return conn->keep_alive.last_ts != UINT64_MAX && conn->keep_alive.timeout; +} + +/* + * conn_keep_alive_expired returns nonzero if keep-alive timer has + * expired. + */ +static int conn_keep_alive_expired(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + return conn_keep_alive_enabled(conn) && + conn->keep_alive.last_ts + conn->keep_alive.timeout <= ts; +} + +/* + * conn_keep_alive_expiry returns the expiry time of keep-alive timer. + */ +static ngtcp2_tstamp conn_keep_alive_expiry(ngtcp2_conn *conn) { + if ((conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) || + !conn_keep_alive_enabled(conn)) { + return UINT64_MAX; + } + + return conn->keep_alive.last_ts + conn->keep_alive.timeout; +} + +/* + * conn_cancel_expired_keep_alive_timer cancels the expired keep-alive + * timer. + */ +static void conn_cancel_expired_keep_alive_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + if (!(conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) && + conn_keep_alive_expired(conn, ts)) { + conn->flags |= NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED; + } +} + +/* + * conn_update_keep_alive_last_ts updates the base time point of + * keep-alive timer. + */ +static void conn_update_keep_alive_last_ts(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + conn->keep_alive.last_ts = ts; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED; +} + +void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, + ngtcp2_duration timeout) { + conn->keep_alive.timeout = timeout; +} + +/* + * NGTCP2_PKT_PACING_OVERHEAD defines overhead of userspace event + * loop. Packet pacing might require sub milliseconds packet spacing, + * but userspace event loop might not offer such precision. + * Typically, if delay is 0.5 microseconds, the actual delay after + * which we can send packet is well over 1 millisecond when event loop + * is involved (which includes other stuff, like reading packets etc + * in a typical single threaded use case). + */ +#define NGTCP2_PKT_PACING_OVERHEAD NGTCP2_MILLISECONDS + +static void conn_cancel_expired_pkt_tx_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + if (conn->tx.pacing.next_ts == UINT64_MAX) { + return; + } + + if (conn->tx.pacing.next_ts > ts + NGTCP2_PKT_PACING_OVERHEAD) { + return; + } + + conn->tx.pacing.next_ts = UINT64_MAX; +} + +static int conn_pacing_pkt_tx_allowed(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + return conn->tx.pacing.next_ts == UINT64_MAX || + conn->tx.pacing.next_ts <= ts + NGTCP2_PKT_PACING_OVERHEAD; +} + +static uint8_t conn_pkt_flags(ngtcp2_conn *conn) { + if (conn->remote.transport_params && + conn->remote.transport_params->grease_quic_bit && + (conn->flags & NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT)) { + return NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; + } + + return NGTCP2_PKT_FLAG_NONE; +} + +static uint8_t conn_pkt_flags_long(ngtcp2_conn *conn) { + return NGTCP2_PKT_FLAG_LONG_FORM | conn_pkt_flags(conn); +} + +static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) { + return (uint8_t)(conn_pkt_flags(conn) | ((conn->pktns.crypto.tx.ckm->flags & + NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE)); +} /* * conn_write_handshake_pkt writes handshake packet in the buffer @@ -1941,6 +2526,9 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { * packet type. It should be either NGTCP2_PKT_INITIAL or * NGTCP2_PKT_HANDSHAKE_PKT. * + * |write_datalen| is the minimum length of application data ready to + * send in subsequent 0RTT or 1RTT packet. + * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: * @@ -1952,7 +2540,7 @@ static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { static ngtcp2_ssize conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint8_t type, uint8_t flags, - size_t early_datalen, ngtcp2_tstamp ts) { + uint64_t write_datalen, ngtcp2_tstamp ts) { int rv; ngtcp2_ppe ppe; ngtcp2_pkt_hd hd; @@ -1964,13 +2552,14 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, ngtcp2_rtb_entry *rtbent; ngtcp2_pktns *pktns; size_t left; - uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; + uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; int pkt_empty = 1; int padded = 0; int hd_logged = 0; uint64_t crypto_offset; ngtcp2_ssize num_reclaimed; + uint32_t version; switch (type) { case NGTCP2_PKT_INITIAL: @@ -1979,28 +2568,41 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, } assert(conn->in_pktns->crypto.tx.ckm); pktns = conn->in_pktns; + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; + if (version == conn->client_chosen_version) { + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; + } else { + assert(conn->vneg.version == version); + + cc.ckm = conn->vneg.tx.ckm; + cc.hp_ctx = conn->vneg.tx.hp_ctx; + } break; case NGTCP2_PKT_HANDSHAKE: if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) { return 0; } pktns = conn->hs_pktns; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; default: assert(0); + abort(); } cc.aead = pktns->crypto.ctx.aead; cc.hp = pktns->crypto.ctx.hp; - cc.ckm = pktns->crypto.tx.ckm; - cc.hp_ctx = pktns->crypto.tx.hp_ctx; cc.encrypt = conn->callbacks.encrypt; cc.hp_mask = conn->callbacks.hp_mask; - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, type, + ngtcp2_pkt_hd_init(&hd, conn_pkt_flags_long(conn), type, &conn->dcid.current.cid, &conn->oscid, pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - conn->version, 0); + version, 0); if (!conn->server && type == NGTCP2_PKT_INITIAL && conn->local.settings.token.len) { @@ -2023,7 +2625,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, /* ack_delay = */ 0, NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); if (rv != 0) { - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return rv; } @@ -2038,10 +2640,10 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, } } - /* Server requires at least NGTCP2_DEFAULT_MAX_PKTLEN bytes in order - to send ack-eliciting Initial packet. */ + /* Server requires at least NGTCP2_MAX_UDP_PAYLOAD_SIZE bytes in + order to send ack-eliciting Initial packet. */ if (!conn->server || type != NGTCP2_PKT_INITIAL || - destlen >= NGTCP2_DEFAULT_MAX_PKTLEN) { + destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { build_pkt: for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { left = ngtcp2_ppe_left(&ppe); @@ -2060,7 +2662,8 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, + conn->mem); return rv; } @@ -2078,15 +2681,16 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; } if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { - num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, - pktns->rtb.probe_pkt_left + 1); + num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); if (num_reclaimed < 0) { - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, + conn->mem); return rv; } if (num_reclaimed) { @@ -2098,21 +2702,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, send any probe packet. Client needs to send probe packets until it knows that server has completed address validation or handshake has been confirmed. */ - if (pktns->rtb.num_retransmittable == 0 && + if (pktns->rtb.num_pto_eliciting == 0 && (conn->server || (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { pktns->rtb.probe_pkt_left = 0; ngtcp2_conn_set_loss_detection_timer(conn, ts); + /* TODO If packet is empty, we should return now if cwnd is + zero. */ } } - /* Don't send any PING frame if client Initial has not been - acknowledged yet. */ if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && - pktns->rtb.probe_pkt_left && - (type != NGTCP2_PKT_INITIAL || - ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) { + pktns->rtb.probe_pkt_left) { lfr.type = NGTCP2_FRAME_PING; rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); @@ -2129,7 +2731,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_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) { + if (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); @@ -2154,10 +2756,10 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, /* If we cannot write another packet, then we need to add padding to Initial here. */ - if (require_padding || - conn_should_pad_pkt( - conn, type, ngtcp2_ppe_left(&ppe), early_datalen, - (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0)) { + if (conn_should_pad_pkt( + conn, type, ngtcp2_ppe_left(&ppe), write_datalen, + (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0, + require_padding)) { lfr.type = NGTCP2_FRAME_PADDING; lfr.padding.len = ngtcp2_ppe_padding(&ppe); } else { @@ -2174,7 +2776,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, spktlen = ngtcp2_ppe_final(&ppe, NULL); if (spktlen < 0) { assert(ngtcp2_err_is_fatal((int)spktlen)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return spktlen; } @@ -2185,17 +2787,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); } - rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen, - rtb_entry_flags, conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, frq, ts, (size_t)spktlen, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_del(frq, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); return rv; } rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); if (rv != 0) { - ngtcp2_rtb_entry_del(rtbent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } @@ -2212,8 +2816,12 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, --pktns->rtb.probe_pkt_left; } + conn_update_keep_alive_last_ts(conn, ts); + conn->dcid.current.bytes_sent += (uint64_t)spktlen; + conn->tx.pacing.pktlen += (size_t)spktlen; + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); ++pktns->tx.last_pkt_num; @@ -2258,13 +2866,14 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ack_delay = 0; ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; break; - case NGTCP2_PKT_SHORT: + case NGTCP2_PKT_1RTT: pktns = &conn->pktns; ack_delay = conn_compute_ack_delay(conn); ack_delay_exponent = conn->local.transport_params.ack_delay_exponent; break; default: assert(0); + abort(); } if (!pktns->crypto.tx.ckm) { @@ -2283,8 +2892,8 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } spktlen = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, type, &conn->dcid.current.cid, ackfr, - NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, type, NGTCP2_WRITE_PKT_FLAG_NONE, + &conn->dcid.current.cid, ackfr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (spktlen <= 0) { return spktlen; @@ -2333,6 +2942,11 @@ static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { "discarding Initial packet number space"); conn_discard_pktns(conn, &conn->in_pktns, ts); + + conn_vneg_crypto_free(conn); + + memset(&conn->vneg.rx, 0, sizeof(conn->vneg.rx)); + memset(&conn->vneg.tx, 0, sizeof(conn->vneg.tx)); } /* @@ -2429,15 +3043,13 @@ static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn, static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - size_t early_datalen, + uint64_t early_datalen, ngtcp2_tstamp ts) { int rv; - assert(conn->callbacks.client_initial); - - rv = conn->callbacks.client_initial(conn, conn->user_data); + rv = conn_call_client_initial(conn); if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return rv; } return conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, @@ -2445,6 +3057,39 @@ static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, ts); } +/* + * dcid_tx_left returns the maximum number of bytes that server is + * allowed to send to an unvalidated path associated to |dcid|. + */ +static uint64_t dcid_tx_left(ngtcp2_dcid *dcid) { + if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + return SIZE_MAX; + } + /* From QUIC spec: Prior to validating the client address, servers + MUST NOT send more than three times as many bytes as the number + of bytes they have received. */ + assert(dcid->bytes_recv * 3 >= dcid->bytes_sent); + + return dcid->bytes_recv * 3 - dcid->bytes_sent; +} + +/* + * conn_server_tx_left returns the maximum number of bytes that server + * is allowed to send to an unvalidated path. + */ +static uint64_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) { + assert(conn->server); + + /* If pv->dcid has the current path, use conn->dcid.current. This + is because conn->dcid.current gets update for bytes_recv and + bytes_sent. */ + if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) { + return dcid_tx_left(&conn->dcid.current); + } + + return dcid_tx_left(dcid); +} + /* * conn_write_handshake_pkts writes Initial and Handshake packets in * the buffer pointed by |dest| whose length is |destlen|. @@ -2460,13 +3105,13 @@ static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - size_t early_datalen, + uint64_t write_datalen, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; ngtcp2_ssize res = 0; - int64_t prev_pkt_num = -1; ngtcp2_rtb_entry *rtbent; uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ksl_it it; /* As a client, we would like to discard Initial packet number space @@ -2491,17 +3136,9 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, padded. */ conn_discard_initial_state(conn, ts); } else if (conn->in_pktns) { - if (conn->server) { - it = ngtcp2_rtb_head(&conn->in_pktns->rtb); - if (!ngtcp2_ksl_it_end(&it)) { - rtbent = ngtcp2_ksl_it_get(&it); - prev_pkt_num = rtbent->hd.pkt_num; - } - } - nwrite = conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, - NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, ts); + NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts); if (nwrite < 0) { assert(nwrite != NGTCP2_ERR_NOBUF); return nwrite; @@ -2510,6 +3147,15 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, if (nwrite == 0) { if (conn->server && (conn->in_pktns->rtb.probe_pkt_left || ngtcp2_ksl_len(&conn->in_pktns->crypto.tx.frq))) { + if (cstat->loss_detection_timer != UINT64_MAX && + conn_server_tx_left(conn, &conn->dcid.current) < + NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled due to amplification limit"); + cstat->loss_detection_timer = UINT64_MAX; + } + return 0; } } else { @@ -2517,24 +3163,27 @@ static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, dest += nwrite; destlen -= (size_t)nwrite; - if (conn->server && destlen) { - it = ngtcp2_rtb_head(&conn->in_pktns->rtb); - if (!ngtcp2_ksl_it_end(&it)) { - rtbent = ngtcp2_ksl_it_get(&it); - if (rtbent->hd.pkt_num != prev_pkt_num && - (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { - /* We might have already added padding to Initial, but in - that case, we should have destlen == 0 and no Handshake - packet will be written. */ - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + if (destlen) { + /* We might have already added padding to Initial, but in that + case, we should have destlen == 0 and no Handshake packet + will be written. */ + if (conn->server) { + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + if (!ngtcp2_ksl_it_end(&it)) { + rtbent = ngtcp2_ksl_it_get(&it); + if (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } } + } else { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; } } } } - nwrite = conn_write_handshake_pkt(conn, pi, dest, destlen, - NGTCP2_PKT_HANDSHAKE, wflags, 0, ts); + nwrite = conn_write_handshake_pkt( + conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, wflags, write_datalen, ts); if (nwrite < 0) { assert(nwrite != NGTCP2_ERR_NOBUF); return nwrite; @@ -2608,12 +3257,13 @@ static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) { return 0; } - assert(conn->remote.transport_params.active_connection_id_limit); + assert(conn->remote.transport_params); + assert(conn->remote.transport_params->active_connection_id_limit); /* len includes retired CID. We don't provide extra CID if doing so exceeds NGTCP2_MAX_SCID_POOL_SIZE. */ - n = conn->remote.transport_params.active_connection_id_limit + + n = conn->remote.transport_params->active_connection_id_limit + conn->scid.num_retired; return (size_t)ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len; @@ -2667,7 +3317,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { return NGTCP2_ERR_NOMEM; } - ngtcp2_scid_init(scid, seq, &cid, token); + ngtcp2_scid_init(scid, seq, &cid); rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid); if (rv != 0) { @@ -2675,7 +3325,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -2718,7 +3368,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, for (; !ngtcp2_pq_empty(&conn->scid.used);) { scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); - if (scid->ts_retired == UINT64_MAX || scid->ts_retired + timeout >= ts) { + if (scid->retired_ts == UINT64_MAX || scid->retired_ts + timeout >= ts) { break; } @@ -2737,9 +3387,9 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, --conn->scid.num_retired; } - for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); - if (dcid->ts_retired + timeout >= ts) { + for (; ngtcp2_ringbuf_len(&conn->dcid.retired.rb);) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); + if (dcid->retired_ts + timeout >= ts) { break; } @@ -2748,7 +3398,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, return rv; } - ngtcp2_ringbuf_pop_front(&conn->dcid.retired); + ngtcp2_ringbuf_pop_front(&conn->dcid.retired.rb); } return 0; @@ -2762,10 +3412,33 @@ static size_t conn_min_short_pktlen(ngtcp2_conn *conn) { return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN; } +/* + * conn_handle_unconfirmed_key_update_from_remote deals with key + * update which has not been confirmed yet and initiated by the remote + * endpoint. + * + * If key update was initiated by the remote endpoint, acknowledging a + * packet encrypted with the new key completes key update procedure. + */ +static void conn_handle_unconfirmed_key_update_from_remote(ngtcp2_conn *conn, + int64_t largest_ack, + ngtcp2_tstamp ts) { + if (!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) || + largest_ack < conn->pktns.crypto.rx.ckm->pkt_num) { + return; + } + + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + conn->crypto.key_update.confirmed_ts = ts; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); +} + /* * conn_write_pkt writes a protected packet in the buffer pointed by * |dest| whose length if |destlen|. |type| specifies the type of - * packet. It can be NGTCP2_PKT_SHORT or NGTCP2_PKT_0RTT. + * packet. It can be NGTCP2_PKT_1RTT or NGTCP2_PKT_0RTT. * * This function can send new stream data. In order to send stream * data, specify the underlying stream and parameters to @@ -2804,25 +3477,24 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_rtb_entry *ent; ngtcp2_strm *strm; int pkt_empty = 1; - size_t ndatalen = 0; + uint64_t ndatalen = 0; int send_stream = 0; int stream_blocked = 0; int send_datagram = 0; ngtcp2_pktns *pktns = &conn->pktns; size_t left; - size_t datalen = 0; + uint64_t datalen = 0; ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT]; size_t datacnt; - uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; + uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; int hd_logged = 0; ngtcp2_path_challenge_entry *pcent; - uint8_t hd_flags; + uint8_t hd_flags = NGTCP2_PKT_FLAG_NONE; int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; int write_more = (flags & NGTCP2_WRITE_PKT_FLAG_MORE) != 0; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; size_t min_pktlen = conn_min_short_pktlen(conn); int padded = 0; - int credit_expanded = 0; ngtcp2_cc_pkt cc_pkt; uint64_t crypto_offset; uint64_t stream_offset; @@ -2831,6 +3503,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint64_t target_max_data; ngtcp2_conn_stat *cstat = &conn->cstat; uint64_t delta; + const ngtcp2_cid *scid = NULL; + int keep_alive_expired = 0; + uint32_t version = 0; /* Return 0 if destlen is less than minimum packet length which can trigger Stateless Reset */ @@ -2861,21 +3536,22 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!ppe_pending) { switch (type) { - case NGTCP2_PKT_SHORT: - hd_flags = - (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) - ? NGTCP2_PKT_FLAG_KEY_PHASE - : NGTCP2_PKT_FLAG_NONE; + case NGTCP2_PKT_1RTT: + hd_flags = conn_pkt_flags_short(conn); + scid = NULL; cc->aead = pktns->crypto.ctx.aead; cc->hp = pktns->crypto.ctx.hp; cc->ckm = pktns->crypto.tx.ckm; cc->hp_ctx = pktns->crypto.tx.hp_ctx; + assert(conn->negotiated_version); + + version = conn->negotiated_version; + /* 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)) { + if (conn->oscid.datalen && conn_is_handshake_completed(conn)) { rv = conn_enqueue_new_connection_id(conn); if (rv != 0) { return rv; @@ -2888,11 +3564,13 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!conn->early.ckm) { return 0; } - hd_flags = NGTCP2_PKT_FLAG_LONG_FORM; + hd_flags = conn_pkt_flags_long(conn); + scid = &conn->oscid; cc->aead = conn->early.ctx.aead; cc->hp = conn->early.ctx.hp; cc->ckm = conn->early.ckm; cc->hp_ctx = conn->early.hp_ctx; + version = conn->client_chosen_version; break; default: /* Unreachable */ @@ -2903,7 +3581,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, cc->hp_mask = conn->callbacks.hp_mask; if (conn_should_send_max_data(conn)) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -2937,12 +3615,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->rx.max_offset = conn->rx.unsent_max_offset = nfrc->fr.max_data.max_data; - credit_expanded = 1; } - ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, - &conn->oscid, pktns->tx.last_pkt_num + 1, - pktns_select_pkt_numlen(pktns), conn->version, 0); + ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid, + pktns->tx.last_pkt_num + 1, + pktns_select_pkt_numlen(pktns), version, 0); ngtcp2_ppe_init(ppe, dest, destlen, cc); @@ -2956,8 +3633,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return 0; } - if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) { - pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + if (ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0); /* PATH_RESPONSE is bound to the path that the corresponding PATH_CHALLENGE is received. */ @@ -2970,11 +3647,12 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (rv != 0) { assert(NGTCP2_ERR_NOBUF == rv); } else { - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; - require_padding = !conn->server || destlen >= 1200; + require_padding = + !conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE; /* We don't retransmit PATH_RESPONSE. */ } } @@ -2996,6 +3674,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_acktr_commit_ack(&pktns->acktr); ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, ackfr->ack.largest_ack); + if (type == NGTCP2_PKT_1RTT) { + conn_handle_unconfirmed_key_update_from_remote( + conn, ackfr->ack.largest_ack, ts); + } pkt_empty = 0; } } @@ -3006,7 +3688,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } @@ -3017,9 +3699,22 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } + + if (!(strm->flags & NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED)) { + strm->flags |= NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED; + + rv = conn_call_stream_stop_sending( + conn, (*pfrc)->fr.stop_sending.stream_id, + (*pfrc)->fr.stop_sending.app_error_code, strm->stream_user_data); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + } + break; case NGTCP2_FRAME_STREAM: assert(0); @@ -3029,7 +3724,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->remote.bidi.max_streams) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3038,7 +3733,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->remote.uni.max_streams) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3049,7 +3744,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; @@ -3057,13 +3752,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) { frc = *pfrc; *pfrc = (*pfrc)->next; - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); continue; } break; - case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - ++conn->dcid.num_retire_queued; - break; case NGTCP2_FRAME_CRYPTO: assert(0); break; @@ -3077,6 +3769,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3117,6 +3810,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; } } @@ -3132,7 +3826,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3149,6 +3843,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } else { pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3163,7 +3858,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return rv; } - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3181,6 +3876,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } else { pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; } @@ -3193,7 +3889,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && conn_should_send_max_stream_data(conn, strm)) { - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3236,8 +3932,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } pkt_empty = 0; - credit_expanded = 1; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; pfrc = &(*pfrc)->next; strm->rx.max_offset = strm->rx.unsent_max_offset = @@ -3253,6 +3949,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (stream_offset == (uint64_t)-1) { ngtcp2_strm_streamfrq_clear(strm); ngtcp2_conn_tx_strmq_pop(conn); + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; continue; } @@ -3286,10 +3984,13 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; if (ngtcp2_strm_streamfrq_empty(strm)) { ngtcp2_conn_tx_strmq_pop(conn); + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; continue; } @@ -3303,35 +4004,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } } - /* Add ACK if MAX_DATA or MAX_STREAM_DATA frame is encoded to - decrease packet count. */ - if (ackfr == NULL && credit_expanded) { - rv = conn_create_ack_frame( - conn, &ackfr, pktns, type, ts, /* ack_delay = */ 0, - conn->local.transport_params.ack_delay_exponent); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - - if (ackfr) { - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); - if (rv != 0) { - assert(NGTCP2_ERR_NOBUF == rv); - } else { - ngtcp2_acktr_commit_ack(&pktns->acktr); - ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, - ackfr->ack.largest_ack); - } - } - } - if (rv != NGTCP2_ERR_NOBUF && !send_stream && !send_datagram && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && pktns->rtb.num_retransmittable && pktns->tx.frq == NULL && pktns->rtb.probe_pkt_left) { - num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, - pktns->rtb.probe_pkt_left + 1); + num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); if (num_reclaimed < 0) { return rv; } @@ -3343,9 +4020,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, those packets have been acknowledged (i.e., retransmission in another packet). In this case, we don't have to send any probe packet. */ - if (pktns->rtb.num_retransmittable == 0) { + if (pktns->rtb.num_pto_eliciting == 0) { pktns->rtb.probe_pkt_left = 0; ngtcp2_conn_set_loss_detection_timer(conn, ts); + /* TODO If packet is empty, we should return now if cwnd is + zero. */ } } } else { @@ -3362,11 +4041,15 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen, left)) != (size_t)-1 && (ndatalen || datalen == 0)) { - datacnt = ngtcp2_vec_copy_at_most( - data, &ndatalen, NGTCP2_MAX_STREAM_DATACNT, vmsg->stream.data, - vmsg->stream.datacnt, ndatalen); + datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT, + vmsg->stream.data, vmsg->stream.datacnt, + (size_t)ndatalen); + ndatalen = ngtcp2_vec_len(data, datacnt); + + assert((datacnt == 0 && datalen == 0) || (datacnt && datalen)); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, datacnt, &conn->frc_objalloc, conn->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); return rv; @@ -3393,6 +4076,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; vmsg->stream.strm->tx.offset += ndatalen; @@ -3410,22 +4094,49 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } if (rv != NGTCP2_ERR_NOBUF && send_datagram && - left >= ngtcp2_pkt_datagram_framelen(datalen)) { - lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; - lfr.datagram.datacnt = vmsg->datagram.datacnt; - lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; + left >= ngtcp2_pkt_datagram_framelen((size_t)datalen)) { + if (conn->callbacks.ack_datagram || conn->callbacks.lost_datagram) { + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } - rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); - assert(rv == 0); + nfrc->fr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; + nfrc->fr.datagram.dgram_id = vmsg->datagram.dgram_id; + nfrc->fr.datagram.datacnt = vmsg->datagram.datacnt; + nfrc->fr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; - pkt_empty = 0; - rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + assert(rv == 0); - if (vmsg->datagram.paccepted) { - *vmsg->datagram.paccepted = 1; - } - } else { - send_datagram = 0; + /* Because DATAGRAM will not be retransmitted, we do not use + data anymore. Just nullify it. The only reason to keep + track a frame is keep dgram_id to pass it to + ngtcp2_ack_datagram or ngtcp2_lost_datagram callbacks. */ + nfrc->fr.datagram.datacnt = 0; + nfrc->fr.datagram.data = NULL; + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + } else { + lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; + lfr.datagram.datacnt = vmsg->datagram.datacnt; + lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + assert(rv == 0); + } + + pkt_empty = 0; + rtb_entry_flags |= + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_DATAGRAM; + + if (vmsg->datagram.paccepted) { + *vmsg->datagram.paccepted = 1; + } + } else { + send_datagram = 0; } if (pkt_empty) { @@ -3434,7 +4145,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return NGTCP2_ERR_STREAM_DATA_BLOCKED; } - if (conn->pktns.rtb.probe_pkt_left == 0) { + keep_alive_expired = conn_keep_alive_expired(conn, ts); + + if (conn->pktns.rtb.probe_pkt_left == 0 && !keep_alive_expired) { return 0; } } else if (write_more) { @@ -3470,7 +4183,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { - if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) { + if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT || + keep_alive_expired || conn->pktns.rtb.probe_pkt_left) { lfr.type = NGTCP2_FRAME_PING; rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); @@ -3480,6 +4194,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, packet is still empty. */ } else { rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + if (conn->pktns.rtb.probe_pkt_left) { + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PROBE; + } pktns->tx.num_non_ack_pkt = 0; } } else { @@ -3491,18 +4208,16 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, /* 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; - if ((require_padding || - /* Making full sized packet will help GSO a bit */ - ngtcp2_ppe_left(ppe) < 10 || - (type == NGTCP2_PKT_0RTT && conn->state == NGTCP2_CS_CLIENT_INITIAL)) && - ngtcp2_ppe_left(ppe)) { + if (require_padding || + /* Making full sized packet will help GSO a bit */ + ngtcp2_ppe_left(ppe) < 10) { lfr.padding.len = ngtcp2_ppe_padding(ppe); } else { lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen); } if (lfr.padding.len) { + lfr.type = NGTCP2_FRAME_PADDING; padded = 1; ngtcp2_log_tx_fr(&conn->log, hd, &lfr); ngtcp2_qlog_write_frame(&conn->qlog, &lfr); @@ -3525,8 +4240,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, hd, ts); } - rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite, - rtb_entry_flags, conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&ent, hd, NULL, ts, (size_t)nwrite, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { assert(ngtcp2_err_is_fatal((int)nwrite)); return rv; @@ -3547,7 +4263,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, rv = conn_on_pkt_sent(conn, &pktns->rtb, ent); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_rtb_entry_del(ent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(ent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } @@ -3556,7 +4273,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->cc.on_pkt_sent( &conn->cc, &conn->cstat, ngtcp2_cc_pkt_init(&cc_pkt, hd->pkt_num, (size_t)nwrite, - NGTCP2_PKTNS_ID_APPLICATION, ts)); + NGTCP2_PKTNS_ID_APPLICATION, ts, ent->rst.lost, + ent->rst.tx_in_flight, ent->rst.is_app_limited)); } if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { @@ -3567,7 +4285,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts); } - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING; + conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_PPE_PENDING; if (pktns->rtb.probe_pkt_left && (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { @@ -3577,8 +4295,12 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, nwrite); } + conn_update_keep_alive_last_ts(conn, ts); + conn->dcid.current.bytes_sent += (uint64_t)nwrite; + conn->tx.pacing.pktlen += (size_t)nwrite; + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); ++pktns->tx.last_pkt_num; @@ -3588,8 +4310,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags, - const ngtcp2_path *path, ngtcp2_tstamp ts) { + uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr, + uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts) { int rv; ngtcp2_ppe ppe; ngtcp2_pkt_hd hd; @@ -3597,41 +4319,59 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_ssize nwrite; ngtcp2_crypto_cc cc; ngtcp2_pktns *pktns; - uint8_t flags; + uint8_t hd_flags; ngtcp2_rtb_entry *rtbent; int padded = 0; + const ngtcp2_cid *scid; + uint32_t version; switch (type) { case NGTCP2_PKT_INITIAL: pktns = conn->in_pktns; - flags = NGTCP2_PKT_FLAG_LONG_FORM; + hd_flags = conn_pkt_flags_long(conn); + scid = &conn->oscid; + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; + if (version == conn->client_chosen_version) { + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; + } else { + assert(version == conn->vneg.version); + + cc.ckm = conn->vneg.tx.ckm; + cc.hp_ctx = conn->vneg.tx.hp_ctx; + } break; case NGTCP2_PKT_HANDSHAKE: pktns = conn->hs_pktns; - flags = NGTCP2_PKT_FLAG_LONG_FORM; + hd_flags = conn_pkt_flags_long(conn); + scid = &conn->oscid; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; - case NGTCP2_PKT_SHORT: - /* 0 means Short packet. */ + case NGTCP2_PKT_1RTT: pktns = &conn->pktns; - flags = (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) - ? NGTCP2_PKT_FLAG_KEY_PHASE - : NGTCP2_PKT_FLAG_NONE; + hd_flags = conn_pkt_flags_short(conn); + scid = NULL; + version = conn->negotiated_version; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; break; default: /* We don't support 0-RTT packet in this function. */ assert(0); + abort(); } cc.aead = pktns->crypto.ctx.aead; cc.hp = pktns->crypto.ctx.hp; - cc.ckm = pktns->crypto.tx.ckm; - cc.hp_ctx = pktns->crypto.tx.hp_ctx; cc.encrypt = conn->callbacks.encrypt; cc.hp_mask = conn->callbacks.hp_mask; - ngtcp2_pkt_hd_init(&hd, flags, type, dcid, &conn->oscid, + ngtcp2_pkt_hd_init(&hd, hd_flags, type, dcid, scid, pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - conn->version, 0); + version, 0); ngtcp2_ppe_init(&ppe, dest, destlen, &cc); @@ -3655,21 +4395,25 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( } lfr.type = NGTCP2_FRAME_PADDING; - switch (fr->type) { - case NGTCP2_FRAME_PATH_CHALLENGE: - case NGTCP2_FRAME_PATH_RESPONSE: - if (!conn->server || destlen >= 1200) { - lfr.padding.len = ngtcp2_ppe_padding(&ppe); - } else { - lfr.padding.len = 0; - } - break; - default: - if (type == NGTCP2_PKT_SHORT) { - lfr.padding.len = - ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); - } else { - lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) { + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + switch (fr->type) { + case NGTCP2_FRAME_PATH_CHALLENGE: + case NGTCP2_FRAME_PATH_RESPONSE: + if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + lfr.padding.len = 0; + } + break; + default: + if (type == NGTCP2_PKT_1RTT) { + lfr.padding.len = + ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); + } else { + lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + } } } if (lfr.padding.len) { @@ -3683,7 +4427,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( return nwrite; } - if (type == NGTCP2_PKT_SHORT) { + if (type == NGTCP2_PKT_1RTT) { ++cc.ckm->use_count; } @@ -3695,35 +4439,57 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( case NGTCP2_FRAME_ACK_ECN: ngtcp2_acktr_commit_ack(&pktns->acktr); ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack); + if (type == NGTCP2_PKT_1RTT) { + conn_handle_unconfirmed_key_update_from_remote(conn, fr->ack.largest_ack, + ts); + } break; } - if (((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) && + if (((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) && (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) { - if (pi) { - conn_handle_tx_ecn(conn, pi, &rtb_flags, pktns, &hd, ts); + if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE)) { + conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); } - rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags, - conn->mem); + rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, + rtb_entry_flags, + &conn->rtb_entry_objalloc); if (rv != 0) { return rv; } rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); if (rv != 0) { - ngtcp2_rtb_entry_del(rtbent, conn->mem); + ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); return rv; } - if ((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && - (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { - conn_restart_timer_on_write(conn, ts); + if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { + conn_restart_timer_on_write(conn, ts); + } + + if (pktns->rtb.probe_pkt_left && path && + ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + --pktns->rtb.probe_pkt_left; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", + nwrite); + } } - } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { + } else if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && + conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts); } + if (path && ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + conn_update_keep_alive_last_ts(conn, ts); + } + + conn->tx.pacing.pktlen += (size_t)nwrite; + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); ++pktns->tx.last_pkt_num; @@ -3732,7 +4498,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( } /* - * conn_process_early_rtb makes any pending 0RTT packet Short packet. + * conn_process_early_rtb makes any pending 0RTT packet 1RTT packet. */ static void conn_process_early_rtb(ngtcp2_conn *conn) { ngtcp2_rtb_entry *ent; @@ -3748,9 +4514,9 @@ static void conn_process_early_rtb(ngtcp2_conn *conn) { continue; } - /* 0-RTT packet is retransmitted as a Short packet. */ + /* 0-RTT packet is retransmitted as a 1RTT packet. */ ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM; - ent->hd.type = NGTCP2_PKT_SHORT; + ent->hd.type = NGTCP2_PKT_1RTT; } } @@ -3763,10 +4529,10 @@ 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) || - (in_pktns && (in_pktns->rtb.num_retransmittable || + return !conn_is_handshake_completed(conn) || + (in_pktns && (in_pktns->rtb.num_pto_eliciting || ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) || - (hs_pktns && (hs_pktns->rtb.num_retransmittable || + (hs_pktns && (hs_pktns->rtb.num_pto_eliciting || ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq))); } @@ -3779,13 +4545,20 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { * * NGTCP2_ERR_NOMEM * Out of memory. + * NGTCP2_ERR_CONNECTION_ID_LIMIT + * The number of unacknowledged retirement exceeds the limit. */ 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); + rv = ngtcp2_conn_track_retired_dcid_seq(conn, seq); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -3809,7 +4582,7 @@ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { */ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, ngtcp2_tstamp ts) { - ngtcp2_ringbuf *rb = &conn->dcid.retired; + ngtcp2_ringbuf *rb = &conn->dcid.retired.rb; ngtcp2_dcid *dest, *stale_dcid; int rv; @@ -3827,7 +4600,7 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, dest = ngtcp2_ringbuf_push_back(rb); ngtcp2_dcid_copy(dest, dcid); - dest->ts_retired = ts; + dest->retired_ts = ts; return conn_retire_dcid_seq(conn, dcid->seq); } @@ -3848,20 +4621,19 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, */ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, const ngtcp2_path *path, ngtcp2_tstamp ts) { - ngtcp2_pv *pv = conn->pv; ngtcp2_dcid *dcid, *ndcid; ngtcp2_cid cid; size_t i, len; int rv; assert(!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)); - assert(!pv || !ngtcp2_path_eq(&pv->dcid.ps.path, path)); - assert(!pv || !(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || - !ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path)); + assert(!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)); + assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || + !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)); - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (ngtcp2_path_eq(&dcid->ps.path, path)) { *pdcid = dcid; @@ -3870,41 +4642,128 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, } if (conn->dcid.current.cid.datalen == 0) { - ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound); + ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); ngtcp2_cid_zero(&cid); ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL); - ngtcp2_path_copy(&ndcid->ps.path, path); + ngtcp2_dcid_set_path(ndcid, path); *pdcid = ndcid; return 0; } - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - if (ngtcp2_ringbuf_full(&conn->dcid.bound)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 0); + if (ngtcp2_ringbuf_full(&conn->dcid.bound.rb)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0); rv = conn_retire_dcid(conn, dcid, ts); if (rv != 0) { return rv; } } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); - ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); ngtcp2_dcid_copy(ndcid, dcid); - ngtcp2_path_copy(&ndcid->ps.path, path); + ndcid->bound_ts = ts; + ngtcp2_dcid_set_path(ndcid, path); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); *pdcid = ndcid; return 0; } +static int conn_start_pmtud(ngtcp2_conn *conn) { + int rv; + size_t hard_max_udp_payload_size; + + assert(!conn->local.settings.no_pmtud); + assert(!conn->pmtud); + assert(conn_is_handshake_completed(conn)); + assert(conn->remote.transport_params); + assert(conn->remote.transport_params->max_udp_payload_size >= + NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + hard_max_udp_payload_size = + (size_t)ngtcp2_min(conn->remote.transport_params->max_udp_payload_size, + (uint64_t)conn->local.settings.max_udp_payload_size); + + rv = ngtcp2_pmtud_new(&conn->pmtud, conn->dcid.current.max_udp_payload_size, + hard_max_udp_payload_size, + conn->pktns.tx.last_pkt_num + 1, conn->mem); + if (rv != 0) { + return rv; + } + + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + + return 0; +} + +int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn) { + return conn_start_pmtud(conn); +} + +void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn) { + if (!conn->pmtud) { + return; + } + + ngtcp2_pmtud_del(conn->pmtud); + + conn->pmtud = NULL; +} + +static ngtcp2_ssize conn_write_pmtud_probe(ngtcp2_conn *conn, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + size_t probelen; + ngtcp2_ssize nwrite; + ngtcp2_frame lfr; + + assert(conn->pmtud); + assert(!ngtcp2_pmtud_finished(conn->pmtud)); + + if (!ngtcp2_pmtud_require_probe(conn->pmtud)) { + return 0; + } + + probelen = ngtcp2_pmtud_probelen(conn->pmtud); + if (probelen > destlen) { + return 0; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "sending PMTUD probe packet len=%zu", probelen); + + lfr.type = NGTCP2_FRAME_PING; + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, probelen, NGTCP2_PKT_1RTT, + NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, &conn->dcid.current.cid, &lfr, + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE, + NULL, ts); + if (nwrite < 0) { + return nwrite; + } + + assert(nwrite); + + ngtcp2_pmtud_probe_sent(conn->pmtud, conn_compute_pto(conn, &conn->pktns), + ts); + + return nwrite; +} + /* * conn_stop_pv stops the path validation which is currently running. * This function does nothing if no path validation is currently being @@ -3948,7 +4807,59 @@ static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return rv; } -static void conn_reset_congestion_state(ngtcp2_conn *conn); +/* + * conn_abort_pv aborts the current path validation and frees + * resources allocated for it. This function assumes that conn->pv is + * not NULL. + * + * 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_abort_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_pv *pv = conn->pv; + int rv; + + assert(pv); + + if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { + rv = conn_call_path_validation(conn, pv, + NGTCP2_PATH_VALIDATION_RESULT_ABORTED); + if (rv != 0) { + return rv; + } + } + + return conn_stop_pv(conn, ts); +} + +static size_t conn_shape_udp_payload(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + size_t payloadlen) { + if (conn->remote.transport_params && + conn->remote.transport_params->max_udp_payload_size) { + assert(conn->remote.transport_params->max_udp_payload_size >= + NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + payloadlen = + (size_t)ngtcp2_min((uint64_t)payloadlen, + conn->remote.transport_params->max_udp_payload_size); + } + + payloadlen = + ngtcp2_min(payloadlen, conn->local.settings.max_udp_payload_size); + + if (conn->local.settings.no_udp_payload_size_shaping) { + return payloadlen; + } + + return ngtcp2_min(payloadlen, dcid->max_udp_payload_size); +} + +static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); /* * conn_on_path_validation_failed is called when path validation @@ -3966,10 +4877,12 @@ static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv, ngtcp2_tstamp ts) { int rv; - rv = conn_call_path_validation(conn, &pv->dcid.ps.path, - NGTCP2_PATH_VALIDATION_RESULT_FAILURE); - if (rv != 0) { - return rv; + if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { + rv = conn_call_path_validation(conn, pv, + NGTCP2_PATH_VALIDATION_RESULT_FAILURE); + if (rv != 0) { + return rv; + } } if (pv->flags & NGTCP2_PV_FLAG_MTU_PROBE) { @@ -3978,45 +4891,12 @@ 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); + conn_reset_congestion_state(conn, ts); } return conn_stop_pv(conn, ts); } -/* - * dcid_tx_left returns the maximum number of bytes that server is - * allowed to send to an unvalidated path associated to |dcid|. - */ -static size_t dcid_tx_left(ngtcp2_dcid *dcid) { - if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { - return SIZE_MAX; - } - /* From QUIC spec: Prior to validating the client address, servers - MUST NOT send more than three times as many bytes as the number - of bytes they have received. */ - assert(dcid->bytes_recv * 3 >= dcid->bytes_sent); - - return dcid->bytes_recv * 3 - dcid->bytes_sent; -} - -/* - * conn_server_tx_left returns the maximum number of bytes that server - * is allowed to send to an unvalidated path. - */ -static size_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) { - assert(conn->server); - - /* If pv->dcid has the current path, use conn->dcid.current. This - is because conn->dcid.current gets update for bytes_recv and - bytes_sent. */ - if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) { - return dcid_tx_left(&conn->dcid.current); - } - - return dcid_tx_left(dcid); -} - /* * conn_write_path_challenge writes a packet which includes * PATH_CHALLENGE frame into |dest| of length |destlen|. @@ -4034,19 +4914,30 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts) { - int rv; ngtcp2_ssize nwrite; ngtcp2_tstamp expiry; ngtcp2_pv *pv = conn->pv; ngtcp2_frame lfr; ngtcp2_duration timeout; uint8_t flags; - size_t tx_left; + uint64_t tx_left; + int rv; if (ngtcp2_pv_validation_timed_out(pv, ts)) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, "path validation was timed out"); - return conn_on_path_validation_failed(conn, pv, ts); + rv = conn_on_path_validation_failed(conn, pv, ts); + if (rv != 0) { + return rv; + } + + /* We might set path to the one which we just failed validate. + Set it to the current path here. */ + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + return 0; } ngtcp2_pv_handle_entry_expiry(pv, ts); @@ -4055,12 +4946,9 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, return 0; } - assert(conn->callbacks.rand); - rv = conn->callbacks.rand( - lfr.path_challenge.data, sizeof(lfr.path_challenge.data), - &conn->local.settings.rand_ctx, NGTCP2_RAND_USAGE_PATH_CHALLENGE); + rv = conn_call_get_path_challenge_data(conn, lfr.path_challenge.data); if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return rv; } lfr.type = NGTCP2_FRAME_PATH_CHALLENGE; @@ -4069,16 +4957,18 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, timeout = ngtcp2_max(timeout, 3 * conn->cstat.initial_rtt); expiry = ts + timeout * (1ULL << pv->round); + destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE); + if (conn->server) { if (!(pv->dcid.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { tx_left = conn_server_tx_left(conn, &pv->dcid); - destlen = ngtcp2_min(destlen, tx_left); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, tx_left); if (destlen == 0) { return 0; } } - if (destlen < 1200) { + if (destlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { flags = NGTCP2_PV_ENTRY_FLAG_UNDERSIZED; } else { flags = NGTCP2_PV_ENTRY_FLAG_NONE; @@ -4090,8 +4980,10 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts); nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &pv->dcid.cid, &lfr, - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pv->dcid.ps.path, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &pv->dcid.cid, &lfr, + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING, + &pv->dcid.ps.path, ts); if (nwrite <= 0) { return nwrite; } @@ -4131,10 +5023,10 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, ngtcp2_frame lfr; ngtcp2_ssize nwrite; int rv; - size_t tx_left; + uint64_t tx_left; - for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) { - pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb);) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0); if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { /* Send PATH_RESPONSE from conn_write_pkt. */ @@ -4159,7 +5051,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, /* Client does not expect to respond to path validation against unknown path */ - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); pcent = NULL; } @@ -4181,9 +5073,11 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, } } + destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE); + if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { tx_left = conn_server_tx_left(conn, dcid); - destlen = ngtcp2_min(destlen, tx_left); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, tx_left); if (destlen == 0) { return 0; } @@ -4193,8 +5087,9 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data)); nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &dcid->cid, &lfr, - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &dcid->cid, &lfr, NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, + ts); if (nwrite <= 0) { return nwrite; } @@ -4203,21 +5098,23 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, ngtcp2_path_copy(path, &pcent->ps.path); } - ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); dcid->bytes_sent += (uint64_t)nwrite; return nwrite; } -ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_tstamp ts) { - return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen, - /* pdatalen = */ NULL, - NGTCP2_WRITE_STREAM_FLAG_NONE, - /* stream_id = */ -1, - /* datav = */ NULL, /* datavcnt = */ 0, ts); +ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(ngtcp2_conn *conn, + ngtcp2_path *path, + int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + return ngtcp2_conn_writev_stream_versioned( + conn, path, pkt_info_version, pi, dest, destlen, + /* pdatalen = */ NULL, NGTCP2_WRITE_STREAM_FLAG_NONE, + /* stream_id = */ -1, + /* datav = */ NULL, /* datavcnt = */ 0, ts); } /* @@ -4250,6 +5147,12 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn, return NGTCP2_ERR_INVALID_ARGUMENT; } + /* Version Negotiation packet is ignored if client has reacted upon + Version Negotiation packet. */ + if (conn->local.settings.original_version != conn->client_chosen_version) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + if (payloadlen > sizeof(sv)) { p = ngtcp2_mem_malloc(conn->mem, payloadlen); if (p == NULL) { @@ -4263,29 +5166,24 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn, ngtcp2_log_rx_vn(&conn->log, hd, p, nsv); - for (i = 0; i < nsv; ++i) { - if (p[i] == conn->version) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "ignore Version Negotiation because it contains version " - "selected by client"); + ngtcp2_qlog_version_negotiation_pkt_received(&conn->qlog, hd, p, nsv); - rv = NGTCP2_ERR_INVALID_ARGUMENT; - goto fin; - } - } + if (!ngtcp2_is_reserved_version(conn->local.settings.original_version)) { + for (i = 0; i < nsv; ++i) { + if (p[i] == conn->local.settings.original_version) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "ignore Version Negotiation because it contains the " + "original version"); - if (conn->callbacks.recv_version_negotiation) { - rv = conn->callbacks.recv_version_negotiation(conn, hd, p, nsv, - conn->user_data); - if (rv != 0) { - rv = NGTCP2_ERR_CALLBACK_FAILURE; + rv = NGTCP2_ERR_INVALID_ARGUMENT; + goto fin; + } } } - if (rv == 0) { - /* TODO Just move to the terminal state for now in order not to - send CONNECTION_CLOSE frame. */ - conn->state = NGTCP2_CS_DRAINING; + rv = conn_call_recv_version_negotiation(conn, hd, p, nsv); + if (rv != 0) { + goto fin; } fin: @@ -4325,6 +5223,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_stream *sfr; ngtcp2_strm *strm; int rv; + int streamfrq_empty; if (*pfrc == NULL) { return 0; @@ -4341,12 +5240,13 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); if (!strm) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); break; } + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -4356,6 +5256,9 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } break; case NGTCP2_FRAME_CRYPTO: frc = *pfrc; @@ -4367,7 +5270,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, &frc->fr.crypto.offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } break; @@ -4401,7 +5304,8 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, * ODCID does not match; or Token is empty. */ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, - size_t hdpktlen, const uint8_t *pkt, size_t pktlen) { + size_t hdpktlen, const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { int rv; ngtcp2_pkt_retry retry; ngtcp2_pktns *in_pktns = conn->in_pktns; @@ -4424,7 +5328,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, retry.odcid = conn->dcid.current.cid; rv = ngtcp2_pkt_verify_retry_tag( - conn->version, &retry, pkt, pktlen, conn->callbacks.encrypt, + conn->client_chosen_version, &retry, pkt, pktlen, conn->callbacks.encrypt, &conn->crypto.retry_aead, &conn->crypto.retry_aead_ctx); if (rv != 0) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, @@ -4444,7 +5348,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, return 0; } - ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd); + ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd, &retry); /* DCID must be updated before invoking callback because client generates new initial keys there. */ @@ -4453,11 +5357,9 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; - assert(conn->callbacks.recv_retry); - - rv = conn->callbacks.recv_retry(conn, hd, conn->user_data); + rv = conn_call_recv_retry(conn, hd); if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return rv; } conn->state = NGTCP2_CS_CLIENT_INITIAL; @@ -4489,7 +5391,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, ngtcp2_cpymem(token->base, retry.token.base, retry.token.len); reset_conn_stat_recovery(&conn->cstat); - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); return 0; @@ -4497,13 +5399,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { - ngtcp2_duration pto = conn_compute_pto(conn, pktns); - int rv = ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, pto, ts); - if (rv != 0) { - return rv; - } - - return 0; + return ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, ts); } /* @@ -4547,7 +5443,7 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, if (num_acked < 0) { /* TODO assert this */ assert(ngtcp2_err_is_fatal((int)num_acked)); - ngtcp2_frame_chain_list_del(frc, conn->mem); + ngtcp2_frame_chain_list_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return (int)num_acked; } @@ -4555,11 +5451,6 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, return 0; } - rv = ngtcp2_conn_detect_lost_pkt(conn, pktns, &conn->cstat, ts); - if (rv != 0) { - return rv; - } - pktns->rtb.probe_pkt_left = 0; if (cstat->pto_count && @@ -4644,13 +5535,18 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn, return 0; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); + return rv; + } + + rv = conn_call_stream_open(conn, strm); + if (rv != 0) { return rv; } } @@ -4769,22 +5665,22 @@ static int conn_ensure_decrypt_buffer(ngtcp2_conn *conn, size_t n) { /* * decrypt_pkt decrypts the data pointed by |payload| whose length is * |payloadlen|, and writes plaintext data to the buffer pointed by - * |dest|. The buffer pointed by |ad| is the Additional Data, and its - * length is |adlen|. |pkt_num| is used to create a nonce. |ckm| is - * the cryptographic key, and iv to use. |decrypt| is a callback - * function which actually decrypts a packet. + * |dest|. The buffer pointed by |aad| is the Additional + * Authenticated Data, and its length is |aadlen|. |pkt_num| is used + * to create a nonce. |ckm| is the cryptographic key, and iv to use. + * |decrypt| is a callback function which actually decrypts a packet. * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: * * NGTCP2_ERR_CALLBACK_FAILURE * User callback failed. - * NGTCP2_ERR_TLS_DECRYPT - * TLS backend failed to decrypt data. + * NGTCP2_ERR_DECRYPT + * Failed to decrypt a packet. */ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const uint8_t *payload, size_t payloadlen, - const uint8_t *ad, size_t adlen, + const uint8_t *aad, size_t aadlen, int64_t pkt_num, ngtcp2_crypto_km *ckm, ngtcp2_decrypt decrypt) { /* TODO nonce is limited to 64 bytes. */ @@ -4796,10 +5692,10 @@ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, ngtcp2_crypto_create_nonce(nonce, ckm->iv.base, ckm->iv.len, pkt_num); rv = decrypt(dest, aead, &ckm->aead_ctx, payload, payloadlen, nonce, - ckm->iv.len, ad, adlen); + ckm->iv.len, aad, aadlen); if (rv != 0) { - if (rv == NGTCP2_ERR_TLS_DECRYPT) { + if (rv == NGTCP2_ERR_DECRYPT) { return rv; } return NGTCP2_ERR_CALLBACK_FAILURE; @@ -4825,20 +5721,17 @@ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, * User-defined callback function failed; or it does not return * expected result. */ -static ngtcp2_ssize decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, - const ngtcp2_crypto_cipher *hp, - const uint8_t *pkt, size_t pktlen, - size_t pkt_num_offset, ngtcp2_crypto_km *ckm, - const ngtcp2_crypto_cipher_ctx *hp_ctx, - ngtcp2_hp_mask hp_mask) { +static ngtcp2_ssize +decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const uint8_t *pkt, size_t pktlen, size_t pkt_num_offset, + const ngtcp2_crypto_cipher_ctx *hp_ctx, ngtcp2_hp_mask hp_mask) { size_t sample_offset; uint8_t *p = dest; - uint8_t mask[NGTCP2_HP_MASKLEN]; + uint8_t mask[NGTCP2_HP_SAMPLELEN]; size_t i; int rv; assert(hp_mask); - assert(ckm); if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) { return NGTCP2_ERR_PROTO; @@ -4921,40 +5814,65 @@ static int conn_emit_pending_crypto_data(ngtcp2_conn *conn, * conn_recv_connection_close is called when CONNECTION_CLOSE or * APPLICATION_CLOSE frame is received. */ -static void conn_recv_connection_close(ngtcp2_conn *conn, - ngtcp2_connection_close *fr) { +static int conn_recv_connection_close(ngtcp2_conn *conn, + ngtcp2_connection_close *fr) { + ngtcp2_connection_close_error *ccerr = &conn->rx.ccerr; + conn->state = NGTCP2_CS_DRAINING; if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { - conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT; + ccerr->type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT; } else { - conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION; + ccerr->type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION; + } + ccerr->error_code = fr->error_code; + ccerr->frame_type = fr->frame_type; + + if (!fr->reasonlen) { + ccerr->reasonlen = 0; + + return 0; + } + + if (ccerr->reason == NULL) { + ccerr->reason = ngtcp2_mem_malloc( + conn->mem, NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN); + if (ccerr->reason == NULL) { + return NGTCP2_ERR_NOMEM; + } } - conn->rx.ccec.error_code = fr->error_code; + + ccerr->reasonlen = + ngtcp2_min(fr->reasonlen, NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN); + ngtcp2_cpymem(ccerr->reason, fr->reason, ccerr->reasonlen); + + return 0; } static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_path_challenge *fr) { ngtcp2_path_challenge_entry *ent; - /* client only responds to PATH_CHALLENGE from the current path. */ - if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_CON, - "discard PATH_CHALLENGE from the path which is not current"); + /* client only responds to PATH_CHALLENGE from the current path or + path which client is migrating to. */ + if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && + (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path))) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "discard PATH_CHALLENGE from the path which is not current " + "or endpoint is migrating to"); return; } - ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge); + ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge.rb); ngtcp2_path_challenge_entry_init(ent, path, fr->data); } /* * conn_reset_congestion_state resets congestion state. */ -static void conn_reset_congestion_state(ngtcp2_conn *conn) { +static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn_reset_conn_stat_cc(conn, &conn->cstat); - conn->cc.reset(&conn->cc); + conn->cc.reset(&conn->cc, &conn->cstat, ts); if (conn->hs_pktns) { ngtcp2_rtb_reset_cc_state(&conn->hs_pktns->rtb, @@ -4962,6 +5880,8 @@ static void conn_reset_congestion_state(ngtcp2_conn *conn) { } ngtcp2_rtb_reset_cc_state(&conn->pktns.rtb, conn->pktns.tx.last_pkt_num + 1); ngtcp2_rst_init(&conn->rst); + + conn->tx.pacing.next_ts = UINT64_MAX; } static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, @@ -4977,9 +5897,8 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, rv = ngtcp2_pv_validate(pv, &ent_flags, fr->data); if (rv != 0) { - if (rv == NGTCP2_ERR_PATH_VALIDATION_FAILED) { - return conn_on_path_validation_failed(conn, pv, ts); - } + assert(!ngtcp2_err_is_fatal(rv)); + return 0; } @@ -4990,11 +5909,11 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, rv = conn_retire_dcid(conn, &conn->dcid.current, ts); if (rv != 0) { - goto fail; + return rv; } ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); } - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); } @@ -5002,10 +5921,21 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; } - rv = conn_call_path_validation(conn, &pv->dcid.ps.path, + rv = conn_call_path_validation(conn, pv, NGTCP2_PATH_VALIDATION_RESULT_SUCCESS); if (rv != 0) { - goto fail; + return rv; + } + + if (!conn->local.settings.no_pmtud) { + ngtcp2_conn_stop_pmtud(conn); + + if (!(pv->flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED)) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } } } @@ -5022,7 +5952,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, NGTCP2_PV_FLAG_MTU_PROBE, &conn->log, conn->mem); if (rv != 0) { - goto fail; + return rv; } npv->dcid.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; @@ -5032,7 +5962,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, timeout, NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem); if (rv != 0) { - goto fail; + return rv; } } @@ -5051,7 +5981,6 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, return 0; } -fail: return conn_stop_pv(conn, ts); } @@ -5141,6 +6070,33 @@ static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns, } } +/* + * vneg_other_versions_includes returns nonzero if |other_versions| of + * length |other_versionslen| includes |version|. |other_versions| is + * the wire image of other_versions field of version_information + * transport parameter, and each version is encoded in network byte + * order. + */ +static int vneg_other_versions_includes(const uint8_t *other_versions, + size_t other_versionslen, + uint32_t version) { + size_t i; + + assert(!(other_versionslen & 0x3)); + + if (other_versionslen == 0) { + return 0; + } + + for (i = 0; i < other_versionslen; i += sizeof(uint32_t)) { + if (version == ngtcp2_get_uint32(&other_versions[i])) { + return 1; + } + } + + return 0; +} + static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, ngtcp2_strm *strm, const ngtcp2_crypto *fr); @@ -5217,13 +6173,17 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { if (conn->state == NGTCP2_CS_SERVER_INITIAL) { - /* Ignore Short packet unless server's first Handshake packet - has been transmitted. */ + /* Ignore 1RTT packet unless server's first Handshake packet has + been transmitted. */ return (ngtcp2_ssize)pktlen; } + if (conn->pktns.crypto.rx.ckm) { + return 0; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, - "buffering Short packet len=%zu", pktlen); + "buffering 1RTT packet len=%zu", pktlen); rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen, ts); @@ -5239,8 +6199,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - switch (hd.type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: + if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) { hdpktlen = (size_t)nread; ngtcp2_log_rx_pkt_hd(&conn->log, &hd); @@ -5276,7 +6235,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } return NGTCP2_ERR_RECV_VERSION_NEGOTIATION; - case NGTCP2_PKT_RETRY: + } else if (hd.type == NGTCP2_PKT_RETRY) { hdpktlen = (size_t)nread; ngtcp2_log_rx_pkt_hd(&conn->log, &hd); @@ -5291,7 +6250,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen); + if (conn->client_chosen_version != hd.version) { + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen, ts); if (rv != 0) { if (ngtcp2_err_is_fatal(rv)) { return rv; @@ -5307,7 +6270,18 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, pktlen = (size_t)nread + hd.len; - if (conn->version != hd.version) { + if (!ngtcp2_is_supported_version(hd.version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + + if (conn->server) { + if (hd.version != conn->client_chosen_version && + (!conn->negotiated_version || hd.version != conn->negotiated_version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + } else if (hd.version != conn->client_chosen_version && + conn->negotiated_version && + hd.version != conn->negotiated_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -5326,6 +6300,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (!conn->server) { return NGTCP2_ERR_DISCARD_PKT; } + + if (hd.version != conn->client_chosen_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { if (conn->early.ckm) { ngtcp2_ssize nread2; @@ -5364,6 +6343,14 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, assert(conn->in_pktns); if (conn->server) { + if (dgramlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "Initial packet was ignored because it is included in UDP datagram " + "less than %zu bytes: %zu bytes", + NGTCP2_MAX_UDP_PAYLOAD_SIZE, dgramlen); + return NGTCP2_ERR_DISCARD_PKT; + } if (conn->local.settings.token.len) { rv = verify_token(&conn->local.settings.token, &hd); if (rv != 0) { @@ -5384,18 +6371,55 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return rv; } } - } else if (hd.token.len != 0) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet was ignored because token is not empty"); - return NGTCP2_ERR_DISCARD_PKT; + } else { + if (hd.token.len != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because token is not empty"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (hd.version != conn->client_chosen_version && + !conn->negotiated_version && conn->vneg.version != hd.version) { + if (!vneg_other_versions_includes(conn->vneg.other_versions, + conn->vneg.other_versionslen, + hd.version)) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Install new Initial keys using QUIC version = hd.version */ + rv = conn_call_version_negotiation( + conn, hd.version, + (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) + ? &conn->dcid.current.cid + : &conn->rcid); + if (rv != 0) { + return rv; + } + + assert(conn->vneg.version == hd.version); + } } pktns = conn->in_pktns; crypto = &pktns->crypto.strm; crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; + if (hd.version == conn->client_chosen_version) { + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; + } else { + assert(conn->vneg.version == hd.version); + + ckm = conn->vneg.rx.ckm; + hp_ctx = &conn->vneg.rx.hp_ctx; + } + break; case NGTCP2_PKT_HANDSHAKE: + if (hd.version != conn->negotiated_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (!conn->hs_pktns->crypto.rx.ckm) { if (conn->server) { ngtcp2_log_info( @@ -5418,6 +6442,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, pktns = conn->hs_pktns; crypto = &pktns->crypto.strm; crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; break; default: @@ -5431,8 +6457,6 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, decrypt = conn->callbacks.decrypt; aead = &pktns->crypto.ctx.aead; hp = &pktns->crypto.ctx.hp; - ckm = pktns->crypto.rx.ckm; - hp_ctx = &pktns->crypto.rx.hp_ctx; assert(ckm); assert(hp_mask); @@ -5444,7 +6468,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, - (size_t)nread, ckm, hp_ctx, hp_mask); + (size_t)nread, hp_ctx, hp_mask); if (nwrite < 0) { if (ngtcp2_err_is_fatal((int)nwrite)) { return nwrite; @@ -5505,6 +6529,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_PROTO; } + if (!conn->server && hd.version != conn->client_chosen_version && + !conn->negotiated_version) { + conn->negotiated_version = hd.version; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is 0x%08x", + conn->negotiated_version); + } + payload = conn->crypto.decrypt_buf.base; payloadlen = (size_t)nwrite; @@ -5589,6 +6622,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, case NGTCP2_FRAME_PADDING: break; case NGTCP2_FRAME_CRYPTO: + if (!conn->server && !conn->negotiated_version && + ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt)) { + conn->negotiated_version = hd.version; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is 0x%08x", + conn->negotiated_version); + } + rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto); if (rv != 0) { return rv; @@ -5596,7 +6638,10 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, require_ack = 1; break; case NGTCP2_FRAME_CONNECTION_CLOSE: - conn_recv_connection_close(conn, &fr->connection_close); + rv = conn_recv_connection_close(conn, &fr->connection_close); + if (rv != 0) { + return rv; + } break; case NGTCP2_FRAME_PING: require_ack = 1; @@ -5645,21 +6690,37 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, : (ngtcp2_ssize)pktlen; } +static int is_unrecoverable_error(int liberr) { + switch (liberr) { + case NGTCP2_ERR_CRYPTO: + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + case NGTCP2_ERR_TRANSPORT_PARAM: + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + return 1; + } + + return 0; +} + /* * conn_recv_handshake_cpkt processes compound packet during * handshake. The buffer pointed by |pkt| might contain multiple - * packets. The Short packet must be the last one because it does not + * packets. The 1RTT packet must be the last one because it does not * have payload length field. * * This function returns the same error code returned by * conn_recv_handshake_pkt. */ -static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const ngtcp2_pkt_info *pi, - const uint8_t *pkt, size_t pktlen, - ngtcp2_tstamp ts) { +static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn, + const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { ngtcp2_ssize nread; size_t dgramlen = pktlen; + const uint8_t *origpkt = pkt; + uint32_t version; if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { conn->dcid.current.bytes_recv += dgramlen; @@ -5670,53 +6731,55 @@ static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, conn_recv_handshake_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts); if (nread < 0) { if (ngtcp2_err_is_fatal((int)nread)) { - return (int)nread; + return nread; } if (nread == NGTCP2_ERR_DRAINING) { return NGTCP2_ERR_DRAINING; } - if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && - /* Not a Version Negotiation packet */ - pktlen > 4 && ngtcp2_get_uint32(&pkt[1]) > 0 && - ngtcp2_pkt_get_type_long(pkt[0]) == NGTCP2_PKT_INITIAL) { - if (conn->server) { - /* If server discards first Initial, then drop connection - state. This is because SCID in packet might be corrupted - and the current connection state might wrongly discard - valid packet and prevent the handshake from - completing. */ - if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { - /* If this is crypto related error, then return normally - in order to send CONNECTION_CLOSE with TLS alert (e.g., - no_application_protocol). */ - switch (nread) { - case NGTCP2_ERR_CRYPTO: - case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: - case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: - case NGTCP2_ERR_TRANSPORT_PARAM: - return (int)nread; + if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && pktlen > 4) { + /* Not a Version Negotiation packet */ + version = ngtcp2_get_uint32(&pkt[1]); + if (ngtcp2_pkt_get_type_long(version, pkt[0]) == NGTCP2_PKT_INITIAL) { + if (conn->server) { + if (is_unrecoverable_error((int)nread)) { + /* If server gets crypto error from TLS stack, it is + unrecoverable, therefore drop connection. */ + return nread; + } + + /* If server discards first Initial, then drop connection + state. This is because SCID in packet might be corrupted + and the current connection state might wrongly discard + valid packet and prevent the handshake from + completing. */ + if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { + return NGTCP2_ERR_DROP_CONN; } - return NGTCP2_ERR_DROP_CONN; + return (ngtcp2_ssize)dgramlen; } - return 0; - } - /* client */ - if (nread == NGTCP2_ERR_CRYPTO) { - /* If client gets crypto error from TLS stack, it is - unrecoverable, therefore drop connection. */ - return (int)nread; + /* client */ + if (is_unrecoverable_error((int)nread)) { + /* If client gets crypto error from TLS stack, it is + unrecoverable, therefore drop connection. */ + return nread; + } + return (ngtcp2_ssize)dgramlen; } - return 0; } if (nread == NGTCP2_ERR_DISCARD_PKT) { - return 0; + return (ngtcp2_ssize)dgramlen; } - return (int)nread; + return nread; + } + + if (nread == 0) { + assert(!(pkt[0] & NGTCP2_HEADER_FORM_BIT)); + return pkt - origpkt; } assert(pktlen >= (size_t)nread); @@ -5727,7 +6790,7 @@ static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, "read packet %td left %zu", nread, pktlen); } - return 0; + return (ngtcp2_ssize)dgramlen; } int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, @@ -5737,45 +6800,39 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, uint64_t max_tx_offset; int local_stream = conn_local_stream(conn, stream_id); + assert(conn->remote.transport_params); + if (bidi_stream(stream_id)) { if (local_stream) { max_rx_offset = conn->local.transport_params.initial_max_stream_data_bidi_local; max_tx_offset = - conn->remote.transport_params.initial_max_stream_data_bidi_remote; + conn->remote.transport_params->initial_max_stream_data_bidi_remote; } else { max_rx_offset = conn->local.transport_params.initial_max_stream_data_bidi_remote; max_tx_offset = - conn->remote.transport_params.initial_max_stream_data_bidi_local; + conn->remote.transport_params->initial_max_stream_data_bidi_local; } } else if (local_stream) { max_rx_offset = 0; - max_tx_offset = conn->remote.transport_params.initial_max_stream_data_uni; + max_tx_offset = conn->remote.transport_params->initial_max_stream_data_uni; } else { max_rx_offset = conn->local.transport_params.initial_max_stream_data_uni; max_tx_offset = 0; } - rv = ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, - max_tx_offset, stream_user_data, conn->mem); - if (rv != 0) { - return rv; - } + ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, + max_tx_offset, stream_user_data, &conn->frc_objalloc, + conn->mem); - rv = ngtcp2_map_insert(&conn->strms, &strm->me); + rv = ngtcp2_map_insert(&conn->strms, (ngtcp2_map_key_type)strm->stream_id, + strm); if (rv != 0) { assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); goto fail; } - if (!conn_local_stream(conn, stream_id)) { - rv = conn_call_stream_open(conn, strm); - if (rv != 0) { - goto fail; - } - } - return 0; fail: @@ -5806,7 +6863,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, int rv; uint64_t offset; uint32_t sdflags; - int handshake_completed = conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; + int handshake_completed = conn_is_handshake_completed(conn); if (!strm->rx.rob) { return 0; @@ -5816,7 +6873,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, /* Stop calling callback if application has called ngtcp2_conn_shutdown_stream_read() inside the callback. Because it doubly counts connection window. */ - if (strm->flags & (NGTCP2_STRM_FLAG_STOP_SENDING)) { + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { return 0; } @@ -5987,7 +7044,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { uint64_t rx_offset, fr_end_offset; int local_stream; int bidi; - size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; local_stream = conn_local_stream(conn, fr->stream_id); @@ -6038,16 +7095,22 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return 0; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } /* TODO Perhaps, call new_stream callback? */ rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); + return rv; + } + + rv = conn_call_stream_open(conn, strm); + if (rv != 0) { return rv; } + if (!bidi) { ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR); } @@ -6081,8 +7144,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return NGTCP2_ERR_FINAL_SIZE; } - if (strm->flags & - (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { + if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) { return 0; } @@ -6095,11 +7157,6 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { strm->rx.last_offset = fr_end_offset; ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); - - if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, - strm->app_error_code); - } } } else { if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && @@ -6113,8 +7170,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return 0; } - if (strm->flags & - (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { + if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) { return 0; } } @@ -6139,6 +7195,10 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { datalen = 0; } + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); + } + fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && rx_offset == strm->rx.last_offset; @@ -6146,11 +7206,11 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { if (fin) { sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY; } rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, - datalen); + (size_t)datalen); if (rv != 0) { return rv; } @@ -6167,7 +7227,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return rv; } } - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); } /* @@ -6186,7 +7246,7 @@ static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, ngtcp2_frame_chain *frc; ngtcp2_pktns *pktns = &conn->pktns; - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -6219,7 +7279,7 @@ static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm, ngtcp2_frame_chain *frc; ngtcp2_pktns *pktns = &conn->pktns; - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -6367,26 +7427,31 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn, return NGTCP2_ERR_FINAL_SIZE; } + if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) { + return 0; + } + + if (strm->rx.max_offset < fr->final_size) { + return NGTCP2_ERR_FLOW_CONTROL; + } + datalen = fr->final_size - strm->rx.last_offset; - if (strm->rx.max_offset < fr->final_size || - conn_max_data_violated(conn, datalen)) { + if (conn_max_data_violated(conn, datalen)) { return NGTCP2_ERR_FLOW_CONTROL; } - if (!(strm->flags & NGTCP2_STRM_FLAG_RECV_RST)) { - rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, - fr->app_error_code, strm->stream_user_data); - if (rv != 0) { - return rv; - } + rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, + fr->app_error_code, strm->stream_user_data); + if (rv != 0) { + return rv; + } - /* Extend connection flow control window for the amount of data - which are not passed to application. */ - if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { - ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - - ngtcp2_strm_rx_offset(strm)); - } + /* Extend connection flow control window for the amount of data + which are not passed to application. */ + if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { + ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - + ngtcp2_strm_rx_offset(strm)); } conn->rx.offset += datalen; @@ -6395,7 +7460,9 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn, strm->rx.last_offset = fr->final_size; strm->flags |= NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_RECV_RST; - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); + ngtcp2_strm_set_app_error_code(strm, fr->app_error_code); + + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); } /* @@ -6459,34 +7526,44 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, /* Frame is received reset before we create ngtcp2_strm object. */ - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); + return rv; + } + + rv = conn_call_stream_open(conn, strm); + if (rv != 0) { return rv; } } + ngtcp2_strm_set_app_error_code(strm, fr->app_error_code); + /* No RESET_STREAM is required if we have sent FIN and all data have been acknowledged. */ - if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) && - ngtcp2_strm_is_all_tx_data_acked(strm)) { - return 0; - } - - rv = conn_reset_stream(conn, strm, fr->app_error_code); - if (rv != 0) { - return rv; + if (!ngtcp2_strm_is_all_tx_data_fin_acked(strm) && + !(strm->flags & NGTCP2_STRM_FLAG_SENT_RST)) { + rv = conn_reset_stream(conn, strm, fr->app_error_code); + if (rv != 0) { + return rv; + } } strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; + if (ngtcp2_strm_is_tx_queued(strm) && !ngtcp2_strm_streamfrq_empty(strm)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } + ngtcp2_strm_streamfrq_clear(strm); - return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); } /* @@ -6497,8 +7574,8 @@ 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; + ngtcp2_dcid_verify_stateless_reset_token( + dcid, sr->stateless_reset_token) == 0; } /* @@ -6535,18 +7612,18 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, (!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); + len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); if (check_stateless_reset(dcid, path, &sr)) { break; } } if (i == len) { - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (check_stateless_reset(dcid, path, &sr)) { break; } @@ -6562,16 +7639,9 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_log_rx_sr(&conn->log, &sr); - if (!conn->callbacks.recv_stateless_reset) { - return 0; - } - - rv = conn->callbacks.recv_stateless_reset(conn, &sr, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } + ngtcp2_qlog_stateless_reset_pkt_received(&conn->qlog, &sr); - return 0; + return conn_call_recv_stateless_reset(conn, &sr); } /* @@ -6628,16 +7698,20 @@ static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb, if (rv != 0) { return rv; } + if (i == 0) { ngtcp2_ringbuf_pop_front(rb); - } else if (i == ngtcp2_ringbuf_len(rb) - 1) { + continue; + } + + if (i == ngtcp2_ringbuf_len(rb) - 1) { ngtcp2_ringbuf_pop_back(rb); break; - } else { - last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); - ngtcp2_dcid_copy(dcid, last); - ngtcp2_ringbuf_pop_back(rb); } + + last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); + ngtcp2_dcid_copy(dcid, last); + ngtcp2_ringbuf_pop_back(rb); } return 0; @@ -6691,10 +7765,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, } } - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); if (rv != 0) { @@ -6705,10 +7779,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, } } - len = ngtcp2_ringbuf_len(&conn->dcid.unused); + len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, i); rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); if (rv != 0) { @@ -6722,13 +7796,13 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, 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.bound, fr->retire_prior_to); + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.bound.rb, + fr->retire_prior_to); if (rv != 0) { return rv; } - rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused, + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused.rb, conn->dcid.retire_prior_to); if (rv != 0) { return rv; @@ -6742,13 +7816,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, For example, a peer might send seq = 50000 and retire_prior_to = 50000. Then send NEW_CONNECTION_ID frames with seq < 50000. */ - /* TODO we might queue lots of RETIRE_CONNECTION_ID frame here - because conn->dcid.num_retire_queued is incremented when the - frame is serialized. */ - if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) { - return conn_retire_dcid_seq(conn, fr->seq); - } - return 0; + return conn_retire_dcid_seq(conn, fr->seq); } if (found) { @@ -6768,7 +7836,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap); } - len = ngtcp2_ringbuf_len(&conn->dcid.unused); + len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) { ++extra_dcid; @@ -6795,7 +7863,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, return 0; } - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); return 0; @@ -6820,7 +7888,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, int rv; if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return 0; } @@ -6829,7 +7897,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); if (pv) { if (conn->dcid.current.seq == pv->dcid.seq) { ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); @@ -6841,7 +7909,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { @@ -6851,13 +7919,13 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, if (pv) { if (pv->dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) { rv = conn_retire_dcid(conn, &pv->dcid, ts); if (rv != 0) { return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && pv->dcid.seq == pv->fallback_dcid.seq) { @@ -6865,7 +7933,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &pv->dcid); if (rv != 0) { @@ -6876,20 +7944,20 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, "path migration is aborted because connection ID is" "retired and no unused connection ID is available"); - return conn_stop_pv(conn, ts); + return conn_abort_pv(conn, ts); } } if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && pv->fallback_dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) { rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); if (rv != 0) { return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); if (rv != 0) { @@ -6897,7 +7965,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, } } else { /* Now we have no fallback dcid. */ - return conn_stop_pv(conn, ts); + return conn_abort_pv(conn, ts); } } } @@ -6949,7 +8017,7 @@ static int conn_recv_retire_connection_id(ngtcp2_conn *conn, scid->pe.index = NGTCP2_PQ_BAD_INDEX; } - scid->ts_retired = ts; + scid->retired_ts = ts; return ngtcp2_pq_push(&conn->scid.used, &scid->pe); } @@ -6970,8 +8038,6 @@ static int conn_recv_retire_connection_id(ngtcp2_conn *conn, * Server received NEW_TOKEN. */ static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { - int rv; - if (conn->server) { return NGTCP2_ERR_PROTO; } @@ -6980,14 +8046,7 @@ static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { return NGTCP2_ERR_FRAME_ENCODING; } - if (conn->callbacks.recv_new_token) { - rv = conn->callbacks.recv_new_token(conn, &fr->token, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - - return 0; + return conn_call_recv_new_token(conn, &fr->token); } /* @@ -7042,49 +8101,48 @@ static int conn_recv_streams_blocked_uni(ngtcp2_conn *conn, * User-defined callback function failed. */ static int conn_select_preferred_addr(ngtcp2_conn *conn) { - struct sockaddr_storage buf; - ngtcp2_addr addr; + ngtcp2_path_storage ps; int rv; ngtcp2_duration pto, initial_pto, timeout; ngtcp2_pv *pv; ngtcp2_dcid *dcid; - ngtcp2_addr_init(&addr, (struct sockaddr *)&buf, 0, NULL); - - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return 0; } - rv = conn_call_select_preferred_addr(conn, &addr); + ngtcp2_path_storage_zero(&ps); + ngtcp2_addr_copy(&ps.path.local, &conn->dcid.current.ps.path.local); + + rv = conn_call_select_preferred_addr(conn, &ps.path); if (rv != 0) { return rv; } - if (addr.addrlen == 0 || - ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) { + if (ps.path.remote.addrlen == 0 || + ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &ps.path.remote)) { return 0; } assert(conn->pv == NULL); - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ngtcp2_dcid_set_path(dcid, &ps.path); + pto = conn_compute_pto(conn, &conn->pktns); initial_pto = conn_compute_initial_pto(conn, &conn->pktns); timeout = 3 * ngtcp2_max(pto, initial_pto); - rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log, - conn->mem); + rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_PREFERRED_ADDR, + &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); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); 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); } @@ -7120,18 +8178,18 @@ static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn_discard_handshake_state(conn, ts); - if (conn->remote.transport_params.preferred_address_present) { + assert(conn->remote.transport_params); + + if (conn->remote.transport_params->preferred_address_present) { rv = conn_select_preferred_addr(conn); if (rv != 0) { return rv; } } - if (conn->callbacks.handshake_confirmed) { - rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } + rv = conn_call_handshake_confirmed(conn); + if (rv != 0) { + return rv; } /* Re-arm loss detection timer after handshake has been @@ -7151,38 +8209,9 @@ static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) { * User-defined callback function failed. */ static int conn_recv_datagram(ngtcp2_conn *conn, ngtcp2_datagram *fr) { - const uint8_t *data; - size_t datalen; - int rv; - uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE; - assert(conn->local.transport_params.max_datagram_frame_size); - if (!conn->callbacks.recv_datagram) { - return 0; - } - - if (fr->datacnt) { - assert(fr->datacnt == 1); - - data = fr->data->base; - datalen = fr->data->len; - } else { - data = NULL; - datalen = 0; - } - - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - flags |= NGTCP2_DATAGRAM_FLAG_EARLY; - } - - rv = conn->callbacks.recv_datagram(conn, flags, data, datalen, - conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; + return conn_call_recv_datagram(conn, fr); } /* @@ -7246,14 +8275,12 @@ static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { new_rx_ckm = conn->crypto.key_update.new_rx_ckm; new_tx_ckm = conn->crypto.key_update.new_tx_ckm; - assert(conn->callbacks.update_key); - - rv = conn->callbacks.update_key( + rv = conn_call_update_key( conn, new_rx_ckm->secret.base, new_tx_ckm->secret.base, &rx_aead_ctx, new_rx_ckm->iv.base, &tx_aead_ctx, new_tx_ckm->iv.base, - rx_ckm->secret.base, tx_ckm->secret.base, secretlen, conn->user_data); + rx_ckm->secret.base, tx_ckm->secret.base, secretlen); if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return rv; } new_rx_ckm->aead_ctx = rx_aead_ctx; @@ -7276,9 +8303,11 @@ static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { /* * conn_rotate_keys rotates keys. The current key moves to old key, - * and new key moves to the current key. + * and new key moves to the current key. If the local endpoint + * initiated this key update, pass nonzero as |initiator|. */ -static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { +static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num, + int initiator) { ngtcp2_pktns *pktns = &conn->pktns; assert(conn->crypto.key_update.new_rx_ckm); @@ -7302,6 +8331,9 @@ static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1; conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + if (initiator) { + conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR; + } } /* @@ -7353,14 +8385,19 @@ 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); + conn_reset_congestion_state(conn, ts); conn->dcid.current.bytes_recv += dgramlen; conn_reset_ecn_validation_state(conn); - rv = conn_stop_pv(conn, ts); + + rv = conn_abort_pv(conn, ts); if (rv != 0) { return rv; } - return 0; + + /* Run PMTUD just in case if it is prematurely aborted */ + assert(!conn->pmtud); + + return conn_start_pmtud(conn); } remote_addr_cmp = @@ -7368,14 +8405,21 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, local_addr_eq = ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local); - /* The transport specification draft-27 says: + /* + * When to change DCID? RFC 9002 section 9.5 says: * - * An endpoint MUST use a new connection ID if it initiates - * connection migration as described in Section 9.2 or probes a new - * network path as described in Section 9.1. An endpoint MUST use a - * new connection ID in response to a change in the address of a - * peer if the packet with the new peer address uses an active - * connection ID that has not been previously used by the peer. + * An endpoint MUST NOT reuse a connection ID when sending from more + * than one local address -- for example, when initiating connection + * migration as described in Section 9.2 or when probing a new + * network path as described in Section 9.1. + * + * Similarly, an endpoint MUST NOT reuse a connection ID when + * sending to more than one destination address. Due to network + * changes outside the control of its peer, an endpoint might + * receive packets from a new source address with the same + * Destination Connection ID field value, in which case it MAY + * continue to use the current connection ID with the new remote + * address while still sending from the same local address. */ require_new_cid = conn->dcid.current.cid.datalen && ((new_cid_used && remote_addr_cmp) || !local_addr_eq); @@ -7387,10 +8431,10 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, initial_pto = conn_compute_initial_pto(conn, &conn->pktns); timeout = 3 * ngtcp2_max(pto, initial_pto); - len = ngtcp2_ringbuf_len(&conn->dcid.bound); + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); for (i = 0; i < len; ++i) { - bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) { ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_CON, @@ -7398,13 +8442,13 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, ngtcp2_dcid_copy(&dcid, bound_dcid); if (i == 0) { - ngtcp2_ringbuf_pop_front(&conn->dcid.bound); - } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound) - 1) { - ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); + } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); } else { - last = ngtcp2_ringbuf_get(&conn->dcid.bound, len - 1); + last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, len - 1); ngtcp2_dcid_copy(bound_dcid, last); - ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); } require_new_cid = 0; @@ -7420,11 +8464,11 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, if (i == len) { if (require_new_cid) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused, 0)); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0)); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &dcid); if (rv != 0) { @@ -7440,7 +8484,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, } } - ngtcp2_path_copy(&dcid.ps.path, path); + ngtcp2_dcid_set_path(&dcid, path); dcid.bytes_recv += dgramlen; rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE, @@ -7452,24 +8496,35 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) { ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->pv->fallback_dcid); pv->fallback_pto = conn->pv->fallback_pto; + /* Unset the flag bit so that conn_stop_pv does not retire + DCID. */ + conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE; } else { ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current); pv->fallback_pto = pto; } - ngtcp2_dcid_copy(&conn->dcid.current, &dcid); - if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_COMPARE_FLAG_ADDR | NGTCP2_ADDR_COMPARE_FLAG_FAMILY))) { - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); + } else { + /* For NAT rebinding, keep max_udp_payload_size since client most + likely does not send a padded PATH_CHALLENGE. */ + dcid.max_udp_payload_size = ngtcp2_max( + dcid.max_udp_payload_size, conn->dcid.current.max_udp_payload_size); } + + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); + conn_reset_ecn_validation_state(conn); + ngtcp2_conn_stop_pmtud(conn); + if (conn->pv) { ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_PTV, "path migration is aborted because new migration has started"); - rv = conn_stop_pv(conn, ts); + rv = conn_abort_pv(conn, ts); if (rv != 0) { return rv; } @@ -7481,7 +8536,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, } /* - * conn_recv_pkt_from_new_path is called when a Short packet is + * conn_recv_pkt_from_new_path is called when a 1RTT packet is * received from new path (not current path). This packet would be a * packet which only contains probing frame, or reordered packet, or a * path is being validated. @@ -7524,7 +8579,7 @@ static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn, return rv; } - ngtcp2_path_copy(&bound_dcid->ps.path, path); + ngtcp2_dcid_set_path(bound_dcid, path); bound_dcid->bytes_recv += dgramlen; return 0; @@ -7607,7 +8662,10 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi, case NGTCP2_FRAME_PADDING: break; case NGTCP2_FRAME_CONNECTION_CLOSE: - conn_recv_connection_close(conn, &fr->connection_close); + rv = conn_recv_connection_close(conn, &fr->connection_close); + if (rv != 0) { + return rv; + } break; case NGTCP2_FRAME_CRYPTO: case NGTCP2_FRAME_PING: @@ -7719,7 +8777,14 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return NGTCP2_ERR_DISCARD_PKT; } - if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) { + if (pktlen < (size_t)nread + hd.len) { + return NGTCP2_ERR_DISCARD_PKT; + } + + assert(conn->negotiated_version); + + if (hd.version != conn->client_chosen_version && + hd.version != conn->negotiated_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -7740,6 +8805,10 @@ 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: + if (hd.version != conn->negotiated_version) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (!conn->hs_pktns) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "delayed Handshake packet was discarded"); @@ -7755,7 +8824,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, decrypt = conn->callbacks.decrypt; break; case NGTCP2_PKT_0RTT: - if (!conn->server) { + if (!conn->server || hd.version != conn->client_chosen_version) { return NGTCP2_ERR_DISCARD_PKT; } @@ -7800,7 +8869,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, - (size_t)nread, ckm, hp_ctx, hp_mask); + (size_t)nread, hp_ctx, hp_mask); if (nwrite < 0) { if (ngtcp2_err_is_fatal((int)nwrite)) { return nwrite; @@ -7824,7 +8893,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_log_rx_pkt_hd(&conn->log, &hd); - if (hd.type == NGTCP2_PKT_SHORT) { + if (hd.type == NGTCP2_PKT_1RTT) { key_phase_bit_changed = conn_key_phase_changed(conn, &hd); } @@ -7834,7 +8903,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } if (key_phase_bit_changed) { - assert(hd.type == NGTCP2_PKT_SHORT); + assert(hd.type == NGTCP2_PKT_1RTT); ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE"); @@ -7867,7 +8936,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ckm, decrypt); if (force_decrypt_failure) { - nwrite = NGTCP2_ERR_TLS_DECRYPT; + nwrite = NGTCP2_ERR_DECRYPT; } if (nwrite < 0) { @@ -7875,9 +8944,9 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return nwrite; } - assert(NGTCP2_ERR_TLS_DECRYPT == nwrite); + assert(NGTCP2_ERR_DECRYPT == nwrite); - if (hd.type == NGTCP2_PKT_SHORT && + if (hd.type == NGTCP2_PKT_1RTT && ++conn->crypto.decryption_failure_count >= pktns->crypto.ctx.max_decryption_failure) { return NGTCP2_ERR_AEAD_LIMIT_REACHED; @@ -7963,8 +9032,6 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, "packet was ignored because of mismatched DCID"); return NGTCP2_ERR_DISCARD_PKT; } - - conn->flags |= NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT; } ngtcp2_qlog_pkt_received_start(&conn->qlog); @@ -7985,8 +9052,9 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, hd.type == NGTCP2_PKT_0RTT) { return NGTCP2_ERR_PROTO; } + assert(conn->remote.transport_params); assign_recved_ack_delay_unscaled( - &fr->ack, conn->remote.transport_params.ack_delay_exponent); + &fr->ack, conn->remote.transport_params->ack_delay_exponent); break; } @@ -8092,8 +9160,11 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, break; case NGTCP2_FRAME_CONNECTION_CLOSE: case NGTCP2_FRAME_CONNECTION_CLOSE_APP: - conn_recv_connection_close(conn, &fr->connection_close); - break; + rv = conn_recv_connection_close(conn, &fr->connection_close); + if (rv != 0) { + return rv; + } + break; case NGTCP2_FRAME_PING: non_probing_pkt = 1; break; @@ -8180,7 +9251,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } } - if (conn->server && hd.type == NGTCP2_PKT_SHORT && + if (conn->server && hd.type == NGTCP2_PKT_1RTT && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { if (non_probing_pkt && pktns->rx.max_pkt_num < hd.pkt_num && !conn_path_validation_in_progress(conn, path)) { @@ -8208,10 +9279,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } } - if (hd.type == NGTCP2_PKT_SHORT) { + if (hd.type == NGTCP2_PKT_1RTT) { if (ckm == conn->crypto.key_update.new_rx_ckm) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys"); - conn_rotate_keys(conn, hd.pkt_num); + conn_rotate_keys(conn, hd.pkt_num, /* initiator = */ 0); } else if (ckm->pkt_num > hd.pkt_num) { ckm->pkt_num = hd.pkt_num; } @@ -8220,6 +9291,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, conn->early.discard_started_ts == UINT64_MAX) { conn->early.discard_started_ts = ts; } + + if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + conn_update_keep_alive_last_ts(conn, ts); + } } rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts); @@ -8250,8 +9325,8 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } /* - * conn_process_buffered_protected_pkt processes buffered 0RTT or - * Short packets. + * conn_process_buffered_protected_pkt processes buffered 0RTT or 1RTT + * packets. * * This function returns 0 if it succeeds, or the same negative error * codes from conn_recv_pkt. @@ -8331,12 +9406,55 @@ static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn, } static void conn_sync_stream_id_limit(ngtcp2_conn *conn) { - ngtcp2_transport_params *params = &conn->remote.transport_params; + ngtcp2_transport_params *params = conn->remote.transport_params; + + assert(params); conn->local.bidi.max_streams = params->initial_max_streams_bidi; conn->local.uni.max_streams = params->initial_max_streams_uni; } +static int strm_set_max_offset(void *data, void *ptr) { + ngtcp2_conn *conn = ptr; + ngtcp2_transport_params *params = conn->remote.transport_params; + ngtcp2_strm *strm = data; + uint64_t max_offset; + int rv; + + assert(params); + + if (!conn_local_stream(conn, strm->stream_id)) { + return 0; + } + + if (bidi_stream(strm->stream_id)) { + max_offset = params->initial_max_stream_data_bidi_remote; + } else { + max_offset = params->initial_max_stream_data_uni; + } + + if (strm->tx.max_offset < max_offset) { + strm->tx.max_offset = max_offset; + + /* Don't call callback if stream is half-closed local */ + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + return 0; + } + + rv = conn_call_extend_max_stream_data(conn, strm, strm->stream_id, + strm->tx.max_offset); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +static int conn_sync_stream_data_limit(ngtcp2_conn *conn) { + return ngtcp2_map_each(&conn->strms, strm_set_max_offset, conn); +} + /* * conn_handshake_completed is called once cryptographic handshake has * completed. @@ -8377,7 +9495,7 @@ static int conn_handshake_completed(ngtcp2_conn *conn) { /* * conn_recv_cpkt processes compound packet after handshake. The - * buffer pointed by |pkt| might contain multiple packets. The Short + * buffer pointed by |pkt| might contain multiple packets. The 1RTT * packet must be the last one because it does not have payload length * field. * @@ -8435,11 +9553,11 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, * retired path list. */ static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) { - size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired); + size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); ngtcp2_dcid *dcid; for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); if (ngtcp2_path_eq(&dcid->ps.path, path)) { return 1; } @@ -8459,7 +9577,7 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { assert(conn->server); - rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -8478,27 +9596,36 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { * reading given data. |pkt| points to the buffer to read and * |pktlen| is the length of the buffer. |path| is the network path. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: (TBD). + * This function returns the number of bytes processed. Unless the + * last packet is 1RTT packet and an application decryption key has + * been installed, it returns |pktlen| if it succeeds. If it finds + * 1RTT packet and an application decryption key has been installed, + * it returns the number of bytes just before 1RTT packet begins. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: (TBD). */ -static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, - const ngtcp2_pkt_info *pi, const uint8_t *pkt, - size_t pktlen, ngtcp2_tstamp ts) { +static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, + const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { int rv; + ngtcp2_ssize nread; switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: /* TODO Better to log something when we ignore input */ - return 0; + return (ngtcp2_ssize)pktlen; case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: - rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); - if (rv < 0) { - return rv; + nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return nread; } if (conn->state == NGTCP2_CS_CLIENT_INITIAL) { /* Retry packet was received */ - return 0; + return (ngtcp2_ssize)pktlen; } assert(conn->hs_pktns); @@ -8510,20 +9637,24 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, } } - if ((conn->flags & (NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED | - NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) == - NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) { + if (conn_is_handshake_completed(conn) && + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { rv = conn_handshake_completed(conn); if (rv != 0) { return rv; } + + rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); + if (rv != 0) { + return rv; + } } - return 0; + return nread; case NGTCP2_CS_SERVER_INITIAL: - rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); - if (rv < 0) { - return rv; + nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return nread; } /* @@ -8538,7 +9669,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, 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 nread; } return NGTCP2_ERR_RETRY; } @@ -8562,11 +9693,11 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, } } - return 0; + return nread; case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); - if (rv < 0) { - return rv; + nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return nread; } if (conn->hs_pktns->crypto.rx.ckm) { @@ -8580,7 +9711,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, conn_discard_initial_state(conn, ts); } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { /* If server hits amplification limit, it cancels loss detection timer. If server receives a packet from client, the limit is increased and server can send more. If server has @@ -8603,7 +9734,24 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, } } - return 0; + if ((size_t)nread < pktlen) { + /* We have 1RTT packet and application rx key, but the + handshake has not completed yet. */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "buffering 1RTT packet len=%zu", + pktlen - (size_t)nread); + + rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt + nread, + pktlen - (size_t)nread, pktlen, ts); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + return (ngtcp2_ssize)pktlen; + } + + return nread; } if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { @@ -8633,26 +9781,38 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, return rv; } + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + conn->pktns.rtb.persistent_congestion_start_ts = ts; /* Re-arm loss detection timer here after handshake has been confirmed. */ ngtcp2_conn_set_loss_detection_timer(conn, ts); - return 0; + return nread; case NGTCP2_CS_CLOSING: return NGTCP2_ERR_CLOSING; case NGTCP2_CS_DRAINING: return NGTCP2_ERR_DRAINING; default: - return 0; + return (ngtcp2_ssize)pktlen; } } -int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, - const ngtcp2_pkt_info *pi, const uint8_t *pkt, - size_t pktlen, ngtcp2_tstamp ts) { +int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, + int pkt_info_version, + const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { int rv = 0; + ngtcp2_ssize nread = 0; + const ngtcp2_pkt_info zero_pi = {0}; + (void)pkt_info_version; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -8673,11 +9833,29 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return 0; } + if (!pi) { + pi = &zero_pi; + } + switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: - return conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return (int)nread; + } + + if ((size_t)nread == pktlen) { + return 0; + } + + assert(conn->pktns.crypto.rx.ckm); + + pkt += nread; + pktlen -= (size_t)nread; + + break; case NGTCP2_CS_SERVER_INITIAL: case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: @@ -8694,7 +9872,22 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, return 0; } - return conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + + nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + if (nread < 0) { + return (int)nread; + } + + if ((size_t)nread == pktlen) { + return 0; + } + + assert(conn->pktns.crypto.rx.ckm); + + pkt += nread; + pktlen -= (size_t)nread; + + break; case NGTCP2_CS_CLOSING: return NGTCP2_ERR_CLOSING; case NGTCP2_CS_DRAINING: @@ -8704,11 +9897,13 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (rv != 0) { return rv; } - return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts); + break; default: assert(0); abort(); } + + return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts); } /* @@ -8731,9 +9926,10 @@ static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) { static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + uint8_t flags, ngtcp2_tstamp ts) { - return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, - NGTCP2_WRITE_PKT_FLAG_NONE, ts); + return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, flags, + ts); } /* @@ -8746,11 +9942,45 @@ static int conn_handshake_probe_left(ngtcp2_conn *conn) { conn->hs_pktns->rtb.probe_pkt_left; } +/* + * conn_validate_early_transport_params_limits validates that the + * limits in transport parameters remembered by client for early data + * are not reduced. This function is only used by client and should + * only be called when early data is accepted by server. + */ +static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) { + const ngtcp2_transport_params *params = conn->remote.transport_params; + + assert(!conn->server); + assert(params); + + if (conn->early.transport_params.active_connection_id_limit > + params->active_connection_id_limit || + conn->early.transport_params.initial_max_data > + params->initial_max_data || + conn->early.transport_params.initial_max_stream_data_bidi_local > + params->initial_max_stream_data_bidi_local || + conn->early.transport_params.initial_max_stream_data_bidi_remote > + params->initial_max_stream_data_bidi_remote || + conn->early.transport_params.initial_max_stream_data_uni > + params->initial_max_stream_data_uni || + conn->early.transport_params.initial_max_streams_bidi > + params->initial_max_streams_bidi || + conn->early.transport_params.initial_max_streams_uni > + params->initial_max_streams_uni || + conn->early.transport_params.max_datagram_frame_size > + params->max_datagram_frame_size) { + return NGTCP2_ERR_PROTO; + } + + return 0; +} + /* * conn_write_handshake writes QUIC handshake packets to the buffer - * pointed by |dest| of length |destlen|. |early_datalen| specifies - * the expected length of early data to send. Specify 0 to - * |early_datalen| if there is no early data. + * pointed by |dest| of length |destlen|. |write_datalen| specifies + * the expected length of 0RTT or 1RTT packet payload. Specify 0 to + * |write_datalen| if there is no such data. * * This function returns the number of bytes written to the buffer, or * one of the following negative error codes: @@ -8771,14 +10001,12 @@ static int conn_handshake_probe_left(ngtcp2_conn *conn) { */ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - size_t early_datalen, + uint64_t write_datalen, ngtcp2_tstamp ts) { int rv; ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; size_t origlen = destlen; - size_t server_tx_left; - ngtcp2_conn_stat *cstat = &conn->cstat; - size_t pending_early_datalen; + uint64_t pending_early_datalen; ngtcp2_dcid *dcid; ngtcp2_preferred_addr *paddr; @@ -8786,27 +10014,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, case NGTCP2_CS_CLIENT_INITIAL: pending_early_datalen = conn_retry_early_payloadlen(conn); if (pending_early_datalen) { - early_datalen = pending_early_datalen; + write_datalen = pending_early_datalen; } if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) { nwrite = - conn_write_client_initial(conn, pi, dest, destlen, early_datalen, ts); + conn_write_client_initial(conn, pi, dest, destlen, write_datalen, ts); if (nwrite <= 0) { return nwrite; } } else { nwrite = conn_write_handshake_pkt( conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, - NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, ts); + NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts); if (nwrite < 0) { return nwrite; } } if (pending_early_datalen) { - early_spktlen = conn_retransmit_retry_early(conn, pi, dest + nwrite, - destlen - (size_t)nwrite, ts); + early_spktlen = conn_retransmit_retry_early( + conn, pi, dest + nwrite, destlen - (size_t)nwrite, + nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING + : NGTCP2_WRITE_PKT_FLAG_NONE, + ts); if (early_spktlen < 0) { assert(ngtcp2_err_is_fatal((int)early_spktlen)); @@ -8826,12 +10057,12 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { pending_early_datalen = conn_retry_early_payloadlen(conn); if (pending_early_datalen) { - early_datalen = pending_early_datalen; + write_datalen = pending_early_datalen; } } nwrite = - conn_write_handshake_pkts(conn, pi, dest, destlen, early_datalen, ts); + conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); if (nwrite < 0) { return nwrite; } @@ -8841,9 +10072,10 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, destlen -= (size_t)nwrite; } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn_is_handshake_completed(conn)) { if (!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { - nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, ts); + nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, + NGTCP2_WRITE_PKT_FLAG_NONE, ts); if (nwrite < 0) { return nwrite; } @@ -8866,13 +10098,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; } + if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED) && + !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { + rv = conn_validate_early_transport_params_limits(conn); + if (rv != 0) { + return rv; + } + } + + /* Server might increase stream data limits. Extend it if we have + streams created for early data. */ + rv = conn_sync_stream_data_limit(conn); + if (rv != 0) { + return rv; + } + conn->state = NGTCP2_CS_POST_HANDSHAKE; - if (conn->remote.transport_params.preferred_address_present) { - assert(!ngtcp2_ringbuf_full(&conn->dcid.unused)); + assert(conn->remote.transport_params); - paddr = &conn->remote.transport_params.preferred_address; - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + if (conn->remote.transport_params->preferred_address_present) { + assert(!ngtcp2_ringbuf_full(&conn->dcid.unused.rb)); + + paddr = &conn->remote.transport_params->preferred_address; + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token); rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1); @@ -8881,11 +10130,12 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } } - if (conn->remote.transport_params.stateless_reset_token_present) { + if (conn->remote.transport_params->stateless_reset_token_present) { assert(conn->dcid.current.seq == 0); - memcpy(conn->dcid.current.token, - conn->remote.transport_params.stateless_reset_token, - sizeof(conn->dcid.current.token)); + assert(!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT)); + ngtcp2_dcid_set_token( + &conn->dcid.current, + conn->remote.transport_params->stateless_reset_token); } rv = conn_call_activate_dcid(conn, &conn->dcid.current); @@ -8895,15 +10145,17 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn_process_early_rtb(conn); - rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); - if (rv != 0) { - return (ngtcp2_ssize)rv; + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } } return res; case NGTCP2_CS_SERVER_INITIAL: - nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, - /* early_datalen = */ 0, ts); + nwrite = + conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); if (nwrite < 0) { return nwrite; } @@ -8914,47 +10166,30 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return nwrite; case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { - if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { - server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); - if (server_tx_left == 0) { - if (cstat->loss_detection_timer != UINT64_MAX) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss detection timer canceled"); - cstat->loss_detection_timer = UINT64_MAX; - cstat->pto_count = 0; - } - return 0; - } - } - - if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) { - nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, - /* early_datalen = */ 0, ts); - if (nwrite < 0) { - return nwrite; - } - - res += nwrite; - dest += nwrite; - destlen -= (size_t)nwrite; + if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) { + nwrite = + conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); + if (nwrite < 0) { + return nwrite; } - if (res == 0) { - nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); - if (nwrite < 0) { - return nwrite; - } + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } - res += nwrite; - dest += nwrite; - origlen -= (size_t)nwrite; + if (res == 0) { + nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); + if (nwrite < 0) { + return nwrite; } - return res; + res += nwrite; + dest += nwrite; + origlen -= (size_t)nwrite; } - return 0; + return res; case NGTCP2_CS_CLOSING: return NGTCP2_ERR_CLOSING; case NGTCP2_CS_DRAINING: @@ -8996,17 +10231,17 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, int send_stream = 0; int send_datagram = 0; ngtcp2_ssize spktlen, early_spktlen; - int was_client_initial; - size_t datalen; - size_t early_datalen = 0; + uint64_t datalen; + uint64_t write_datalen = 0; uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + uint32_t version; assert(!conn->server); /* conn->early.ckm might be created in the first call of conn_handshake(). Check it later. */ - if (vmsg && !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { + if (vmsg) { switch (vmsg->type) { case NGTCP2_VMSG_TYPE_STREAM: datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); @@ -9018,9 +10253,11 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, (vmsg->stream.strm->tx.max_offset - vmsg->stream.strm->tx.offset) && (conn->tx.max_offset - conn->tx.offset))); if (send_stream) { - early_datalen = - conn_enforce_flow_control(conn, vmsg->stream.strm, datalen) + - NGTCP2_STREAM_OVERHEAD; + write_datalen = + conn_enforce_flow_control(conn, vmsg->stream.strm, datalen); + write_datalen = + ngtcp2_min(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); + write_datalen += NGTCP2_STREAM_OVERHEAD; if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; @@ -9031,11 +10268,9 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, break; case NGTCP2_VMSG_TYPE_DATAGRAM: datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt); - /* TODO Do we need this? DATAGRAM is independent from STREAM - data and no retransmission */ send_datagram = conn_retry_early_payloadlen(conn) == 0; if (send_datagram) { - early_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD; + write_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD; if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) { wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; @@ -9048,32 +10283,39 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, } if (!ppe_pending) { - was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL; - spktlen = conn_write_handshake(conn, pi, dest, destlen, early_datalen, ts); + spktlen = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts); if (spktlen < 0) { return spktlen; } - if (conn->pktns.crypto.tx.ckm || !conn->early.ckm || - (!send_stream && !send_datagram)) { + if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) || + !conn->early.ckm || (!send_stream && !send_datagram)) { return spktlen; } + + /* If spktlen > 0, we are making a compound packet. If Initial + packet is written, we have to pad bytes to 0-RTT packet. */ + version = conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version; + if (spktlen > 0 && + ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + conn->pkt.require_padding = 1; + } else { + conn->pkt.require_padding = 0; + } } else { + assert(!conn->pktns.crypto.rx.ckm); assert(!conn->pktns.crypto.tx.ckm); assert(conn->early.ckm); - was_client_initial = conn->pkt.was_client_initial; + if (conn->pkt.require_padding) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } spktlen = conn->pkt.hs_spktlen; } - /* If spktlen > 0, we are making a compound packet. If Initial - packet is written, we have to pad bytes to 0-RTT packet. */ - - if (spktlen && was_client_initial) { - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - } - dest += spktlen; destlen -= (size_t)spktlen; @@ -9089,7 +10331,6 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, case NGTCP2_ERR_STREAM_DATA_BLOCKED: return spktlen; case NGTCP2_ERR_WRITE_MORE: - conn->pkt.was_client_initial = was_client_initial; conn->pkt.hs_spktlen = spktlen; break; } @@ -9107,7 +10348,7 @@ void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) { } int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) { - return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + return conn_is_handshake_completed(conn) && (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED); } @@ -9136,35 +10377,29 @@ int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) { } if (pktlen == 0 || (pkt[0] & NGTCP2_HEADER_FORM_BIT) == 0) { - return -1; + return NGTCP2_ERR_INVALID_ARGUMENT; } nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen); if (nread < 0) { - return -1; + return (int)nread; } switch (p->type) { case NGTCP2_PKT_INITIAL: - if (pktlen < NGTCP2_MIN_INITIAL_PKTLEN) { - return -1; - } - if (p->token.len == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) { - return -1; - } break; case NGTCP2_PKT_0RTT: /* 0-RTT packet may arrive before Initial packet due to - re-ordering. */ - break; + re-ordering. ngtcp2 does not buffer 0RTT packet unless the + very first Initial packet is received or token is received. */ + return NGTCP2_ERR_RETRY; default: - return -1; + return NGTCP2_ERR_INVALID_ARGUMENT; } - if (p->version != NGTCP2_PROTO_VER_V1 && - (p->version < NGTCP2_PROTO_VER_DRAFT_MIN || - NGTCP2_PROTO_VER_DRAFT_MAX < p->version)) { - return 1; + if (pktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE || + (p->token.len == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN)) { + return NGTCP2_ERR_INVALID_ARGUMENT; } return 0; @@ -9178,6 +10413,7 @@ int ngtcp2_conn_install_initial_key( ngtcp2_pktns *pktns = conn->in_pktns; int rv; + assert(ivlen >= 8); assert(pktns); conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx); @@ -9220,12 +10456,64 @@ int ngtcp2_conn_install_initial_key( return 0; } +int ngtcp2_conn_install_vneg_initial_key( + ngtcp2_conn *conn, uint32_t version, + const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv, + const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, + const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, + const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) { + int rv; + + assert(ivlen >= 8); + + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx); + conn->vneg.rx.hp_ctx.native_handle = NULL; + + if (conn->vneg.rx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx); + ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem); + conn->vneg.rx.ckm = NULL; + } + + conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx); + conn->vneg.tx.hp_ctx.native_handle = NULL; + + if (conn->vneg.tx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx); + ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem); + conn->vneg.tx.ckm = NULL; + } + + rv = ngtcp2_crypto_km_new(&conn->vneg.rx.ckm, NULL, 0, NULL, rx_iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&conn->vneg.tx.ckm, NULL, 0, NULL, tx_iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + /* Take owner ship after we are sure that no failure occurs, so that + caller can delete these contexts on failure. */ + conn->vneg.rx.ckm->aead_ctx = *rx_aead_ctx; + conn->vneg.rx.hp_ctx = *rx_hp_ctx; + conn->vneg.tx.ckm->aead_ctx = *tx_aead_ctx; + conn->vneg.tx.hp_ctx = *tx_hp_ctx; + conn->vneg.version = version; + + return 0; +} + int ngtcp2_conn_install_rx_handshake_key( ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { ngtcp2_pktns *pktns = conn->hs_pktns; int rv; + assert(ivlen >= 8); assert(pktns); assert(!pktns->crypto.rx.hp_ctx.native_handle); assert(!pktns->crypto.rx.ckm); @@ -9238,6 +10526,16 @@ int ngtcp2_conn_install_rx_handshake_key( pktns->crypto.rx.hp_ctx = *hp_ctx; + rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE); + if (rv != 0) { + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); + pktns->crypto.rx.ckm = NULL; + + memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx)); + + return rv; + } + return 0; } @@ -9247,6 +10545,7 @@ int ngtcp2_conn_install_tx_handshake_key( ngtcp2_pktns *pktns = conn->hs_pktns; int rv; + assert(ivlen >= 8); assert(pktns); assert(!pktns->crypto.tx.hp_ctx.native_handle); assert(!pktns->crypto.tx.ckm); @@ -9260,7 +10559,20 @@ int ngtcp2_conn_install_tx_handshake_key( pktns->crypto.tx.hp_ctx = *hp_ctx; if (conn->server) { - return ngtcp2_conn_commit_local_transport_params(conn); + rv = ngtcp2_conn_commit_local_transport_params(conn); + if (rv != 0) { + return rv; + } + } + + rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE); + if (rv != 0) { + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + pktns->crypto.tx.ckm = NULL; + + memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx)); + + return rv; } return 0; @@ -9272,6 +10584,7 @@ int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, const ngtcp2_crypto_cipher_ctx *hp_ctx) { int rv; + assert(ivlen >= 8); assert(!conn->early.hp_ctx.native_handle); assert(!conn->early.ckm); @@ -9283,6 +10596,22 @@ int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, conn->early.hp_ctx = *hp_ctx; + conn->flags |= NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED; + + if (conn->server) { + rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_EARLY); + } else { + rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_EARLY); + } + if (rv != 0) { + ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); + conn->early.ckm = NULL; + + memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx)); + + return rv; + } + return 0; } @@ -9294,6 +10623,7 @@ int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret, ngtcp2_pktns *pktns = &conn->pktns; int rv; + assert(ivlen >= 8); assert(!pktns->crypto.rx.hp_ctx.native_handle); assert(!pktns->crypto.rx.ckm); @@ -9306,22 +10636,42 @@ int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret, pktns->crypto.rx.hp_ctx = *hp_ctx; if (!conn->server) { - conn->remote.transport_params = conn->remote.pending_transport_params; - conn_sync_stream_id_limit(conn); - conn->tx.max_offset = conn->remote.transport_params.initial_max_data; - } + if (conn->remote.pending_transport_params) { + ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); - return 0; -} + conn->remote.transport_params = conn->remote.pending_transport_params; + conn->remote.pending_transport_params = NULL; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params->initial_max_data; + } -int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, - size_t secretlen, - const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, size_t ivlen, - const ngtcp2_crypto_cipher_ctx *hp_ctx) { - ngtcp2_pktns *pktns = &conn->pktns; + if (conn->early.ckm) { + conn_discard_early_key(conn); + } + } + + rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION); + if (rv != 0) { + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); + pktns->crypto.rx.ckm = NULL; + + memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx)); + + return rv; + } + + return 0; +} + +int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, + size_t secretlen, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, + const ngtcp2_crypto_cipher_ctx *hp_ctx) { + ngtcp2_pktns *pktns = &conn->pktns; int rv; + assert(ivlen >= 8); assert(!pktns->crypto.tx.hp_ctx.native_handle); assert(!pktns->crypto.tx.ckm); @@ -9334,13 +10684,28 @@ int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, pktns->crypto.tx.hp_ctx = *hp_ctx; if (conn->server) { - conn->remote.transport_params = conn->remote.pending_transport_params; - conn_sync_stream_id_limit(conn); - conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + if (conn->remote.pending_transport_params) { + ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); + + conn->remote.transport_params = conn->remote.pending_transport_params; + conn->remote.pending_transport_params = NULL; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params->initial_max_data; + } } else if (conn->early.ckm) { conn_discard_early_key(conn); } + rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION); + if (rv != 0) { + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + pktns->crypto.tx.ckm = NULL; + + memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx)); + + return rv; + } + return 0; } @@ -9358,7 +10723,59 @@ int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return NGTCP2_ERR_INVALID_STATE; } - conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM); + conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM, /* initiator = */ 1); + + return 0; +} + +/* + * conn_retire_stale_bound_dcid retires stale destination connection + * ID in conn->dcid.bound to keep some unused destination connection + * IDs available. + * + * 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_stale_bound_dcid(ngtcp2_conn *conn, + ngtcp2_duration timeout, + ngtcp2_tstamp ts) { + size_t i; + ngtcp2_dcid *dcid, *last; + int rv; + + for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound.rb);) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); + + assert(dcid->cid.datalen); + + if (dcid->bound_ts + timeout > ts) { + ++i; + continue; + } + + rv = conn_retire_dcid_seq(conn, dcid->seq); + if (rv != 0) { + return rv; + } + + if (i == 0) { + ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); + continue; + } + + if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); + break; + } + + last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, + ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1); + ngtcp2_dcid_copy(dcid, last); + ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); + } return 0; } @@ -9372,21 +10789,41 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); ngtcp2_scid *scid; ngtcp2_dcid *dcid; + size_t i, len; if (conn->pv) { res = ngtcp2_pv_next_expiry(conn->pv); } + if (conn->pmtud) { + res = ngtcp2_min(res, conn->pmtud->expiry); + } + if (!ngtcp2_pq_empty(&conn->scid.used)) { scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); - if (scid->ts_retired != UINT64_MAX) { - res = ngtcp2_min(res, scid->ts_retired + pto); + if (scid->retired_ts != UINT64_MAX) { + t = scid->retired_ts + pto; + res = ngtcp2_min(res, t); } } - if (ngtcp2_ringbuf_len(&conn->dcid.retired)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); - res = ngtcp2_min(res, dcid->ts_retired + pto); + if (ngtcp2_ringbuf_len(&conn->dcid.retired.rb)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); + t = dcid->retired_ts + pto; + res = ngtcp2_min(res, t); + } + + if (conn->dcid.current.cid.datalen) { + len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); + + assert(dcid->cid.datalen); + assert(dcid->bound_ts != UINT64_MAX); + + t = dcid->bound_ts + 3 * pto; + res = ngtcp2_min(res, t); + } } if (conn->server && conn->early.ckm && @@ -9408,28 +10845,60 @@ ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) { return UINT64_MAX; } +static ngtcp2_tstamp conn_handshake_expiry(ngtcp2_conn *conn) { + if (conn_is_handshake_completed(conn) || + conn->local.settings.handshake_timeout == UINT64_MAX) { + return UINT64_MAX; + } + + return conn->local.settings.initial_ts + + conn->local.settings.handshake_timeout; +} + ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn); ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn); ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn); ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn); + ngtcp2_tstamp t5 = conn_keep_alive_expiry(conn); + ngtcp2_tstamp t6 = conn_handshake_expiry(conn); + ngtcp2_tstamp t7 = ngtcp2_conn_get_idle_expiry(conn); ngtcp2_tstamp res = ngtcp2_min(t1, t2); res = ngtcp2_min(res, t3); - return ngtcp2_min(res, t4); + res = ngtcp2_min(res, t4); + res = ngtcp2_min(res, t5); + res = ngtcp2_min(res, t6); + res = ngtcp2_min(res, t7); + return ngtcp2_min(res, conn->tx.pacing.next_ts); } int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { int rv; ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); + if (ngtcp2_conn_get_idle_expiry(conn) <= ts) { + return NGTCP2_ERR_IDLE_CLOSE; + } + ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts); + conn_cancel_expired_keep_alive_timer(conn, ts); + + conn_cancel_expired_pkt_tx_timer(conn, ts); + ngtcp2_conn_remove_lost_pkt(conn, ts); if (conn->pv) { ngtcp2_pv_cancel_expired_timer(conn->pv, ts); } + if (conn->pmtud) { + ngtcp2_pmtud_handle_expiry(conn->pmtud, ts); + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + } + if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); if (rv != 0) { @@ -9437,6 +10906,13 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { } } + if (conn->dcid.current.cid.datalen) { + rv = conn_retire_stale_bound_dcid(conn, 3 * pto, ts); + if (rv != 0) { + return rv; + } + } + rv = conn_remove_retired_connection_id(conn, pto, ts); if (rv != 0) { return rv; @@ -9449,26 +10925,38 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { } } + if (!conn_is_handshake_completed(conn) && + conn->local.settings.handshake_timeout != UINT64_MAX && + conn->local.settings.initial_ts + + conn->local.settings.handshake_timeout <= + ts) { + return NGTCP2_ERR_HANDSHAKE_TIMEOUT; + } + return 0; } static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr, + ngtcp2_duration max_ack_delay, ngtcp2_tstamp ts) { if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && - acktr->first_unacked_ts <= ts) { + acktr->first_unacked_ts != UINT64_MAX && + acktr->first_unacked_ts + max_ack_delay <= ts) { acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER; } } void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_duration ack_delay = conn_compute_ack_delay(conn); + if (conn->in_pktns) { - acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, ts); + acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, 0, ts); } if (conn->hs_pktns) { - acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, ts); + acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, 0, ts); } - acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts); + acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ack_delay, ts); } ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { @@ -9514,6 +11002,42 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); } +/* + * select_preferred_version selects the most preferred version. + * |fallback_version| is chosen if no preference is made, or + * |preferred_versions| does not include any of |chosen_version| or + * |other_versions|. |chosen_version| is treated as an extra other + * version. + */ +static uint32_t select_preferred_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + uint32_t chosen_version, + const uint8_t *other_versions, + size_t other_versionslen, + uint32_t fallback_version) { + size_t i, j; + + if (!preferred_versionslen || + (!other_versionslen && chosen_version == fallback_version)) { + return fallback_version; + } + + for (i = 0; i < preferred_versionslen; ++i) { + if (preferred_versions[i] == chosen_version) { + return chosen_version; + } + for (j = 0; j < other_versionslen; j += sizeof(uint32_t)) { + if (preferred_versions[i] != ngtcp2_get_uint32(&other_versions[j])) { + continue; + } + + return preferred_versions[i]; + } + } + + return fallback_version; +} + /* * conn_client_validate_transport_params validates |params| as client. * |params| must be sent with Encrypted Extensions. @@ -9521,12 +11045,11 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * NGTCP2_ERR_PROTO - * Validation against either of original_dcid and retry_scid is - * failed. * NGTCP2_ERR_TRANSPORT_PARAM * params contains preferred address but server chose zero-length * connection ID. + * NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE + * Validation against version negotiation parameters failed. */ static int conn_client_validate_transport_params(ngtcp2_conn *conn, @@ -9551,14 +11074,76 @@ conn_client_validate_transport_params(ngtcp2_conn *conn, return NGTCP2_ERR_TRANSPORT_PARAM; } + if (params->version_info_present) { + if (conn->negotiated_version != params->version_info.chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + assert(vneg_other_versions_includes(conn->vneg.other_versions, + conn->vneg.other_versionslen, + conn->negotiated_version)); + } else if (conn->client_chosen_version != conn->negotiated_version || + conn->client_chosen_version != + conn->local.settings.original_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + /* When client reacted upon Version Negotiation */ + if (conn->local.settings.original_version != conn->client_chosen_version) { + assert(params->version_info_present); + + /* Server choose original version after Version Negotiation. + Draft does not say this particular case, but this smells like + misbehaved server because server should accept original_version + in the original connection. */ + if (conn->local.settings.original_version == + params->version_info.chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + /* Check version downgrade on incompatible version negotiation. */ + if (params->version_info.other_versionslen == 0) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + if (conn->client_chosen_version != + select_preferred_version(conn->vneg.preferred_versions, + conn->vneg.preferred_versionslen, + params->version_info.chosen_version, + params->version_info.other_versions, + params->version_info.other_versionslen, + /* fallback_version = */ 0)) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + } + return 0; } +uint32_t +ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn, + const ngtcp2_version_info *version_info) { + assert(conn->server); + assert(conn->client_chosen_version == version_info->chosen_version); + + return select_preferred_version( + conn->vneg.preferred_versions, conn->vneg.preferred_versionslen, + version_info->chosen_version, version_info->other_versions, + version_info->other_versionslen, version_info->chosen_version); +} + int ngtcp2_conn_set_remote_transport_params( ngtcp2_conn *conn, const ngtcp2_transport_params *params) { int rv; - assert(!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)); + /* We expect this function is called once per QUIC connection, but + GnuTLS server seems to call TLS extension callback twice if it + sends HelloRetryRequest. In practice, same QUIC transport + parameters are sent in the 2nd client flight, just returning 0 + would cause no harm. */ + if (conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED) { + return 0; + } /* Assume that ngtcp2_decode_transport_params sets default value if active_connection_id_limit is omitted. */ @@ -9574,11 +11159,36 @@ int ngtcp2_conn_set_remote_transport_params( return NGTCP2_ERR_TRANSPORT_PARAM; } - if (params->max_udp_payload_size < 1200) { + if (params->max_udp_payload_size < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { return NGTCP2_ERR_TRANSPORT_PARAM; } - if (!conn->server) { + if (conn->server) { + if (params->version_info_present) { + if (params->version_info.chosen_version != conn->client_chosen_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; + } + + conn->negotiated_version = + ngtcp2_conn_server_negotiate_version(conn, ¶ms->version_info); + if (conn->negotiated_version != conn->client_chosen_version) { + rv = conn_call_version_negotiation(conn, conn->negotiated_version, + &conn->rcid); + if (rv != 0) { + return rv; + } + } + } else { + conn->negotiated_version = conn->client_chosen_version; + } + + conn->local.transport_params.version_info.chosen_version = + conn->negotiated_version; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "the negotiated version is 0x%08x", + conn->negotiated_version); + } else { rv = conn_client_validate_transport_params(conn, params); if (rv != 0) { return rv; @@ -9596,11 +11206,24 @@ int ngtcp2_conn_set_remote_transport_params( if ((conn->server && conn->pktns.crypto.tx.ckm) || (!conn->server && conn->pktns.crypto.rx.ckm)) { - conn->remote.transport_params = *params; + ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); + conn->remote.transport_params = NULL; + + rv = ngtcp2_transport_params_copy_new(&conn->remote.transport_params, + params, conn->mem); + if (rv != 0) { + return rv; + } conn_sync_stream_id_limit(conn); - conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + conn->tx.max_offset = conn->remote.transport_params->initial_max_data; } else { - conn->remote.pending_transport_params = *params; + assert(!conn->remote.pending_transport_params); + + rv = ngtcp2_transport_params_copy_new( + &conn->remote.pending_transport_params, params, conn->mem); + if (rv != 0) { + return rv; + } } conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED; @@ -9608,22 +11231,46 @@ int ngtcp2_conn_set_remote_transport_params( return 0; } -void ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params) { - if (conn->pktns.crypto.rx.ckm) { - *params = conn->remote.transport_params; - } else { - *params = conn->remote.pending_transport_params; +int ngtcp2_conn_decode_remote_transport_params(ngtcp2_conn *conn, + const uint8_t *data, + size_t datalen) { + ngtcp2_transport_params params; + int rv; + + rv = ngtcp2_decode_transport_params( + ¶ms, + conn->server ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO + : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + data, datalen); + if (rv != 0) { + return rv; } + + return ngtcp2_conn_set_remote_transport_params(conn, ¶ms); } -void ngtcp2_conn_set_early_remote_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params) { - ngtcp2_transport_params *p = &conn->remote.transport_params; +const ngtcp2_transport_params * +ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn) { + if (conn->remote.pending_transport_params) { + return conn->remote.pending_transport_params; + } + + return conn->remote.transport_params; +} + +void ngtcp2_conn_set_early_remote_transport_params_versioned( + ngtcp2_conn *conn, int transport_params_version, + const ngtcp2_transport_params *params) { + ngtcp2_transport_params *p; + (void)transport_params_version; assert(!conn->server); + assert(!conn->remote.transport_params); + + /* Assume that all pointer fields in p are NULL */ + p = ngtcp2_mem_calloc(conn->mem, 1, sizeof(*p)); - memset(p, 0, sizeof(*p)); + conn->remote.transport_params = p; p->initial_max_streams_bidi = params->initial_max_streams_bidi; p->initial_max_streams_uni = params->initial_max_streams_uni; @@ -9633,8 +11280,38 @@ void ngtcp2_conn_set_early_remote_transport_params( params->initial_max_stream_data_bidi_remote; p->initial_max_stream_data_uni = params->initial_max_stream_data_uni; p->initial_max_data = params->initial_max_data; + p->active_connection_id_limit = + ngtcp2_max(NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT, + params->active_connection_id_limit); + p->max_idle_timeout = params->max_idle_timeout; + if (!params->max_udp_payload_size) { + p->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + } else { + p->max_udp_payload_size = + ngtcp2_max(NGTCP2_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); + } + p->disable_active_migration = params->disable_active_migration; p->max_datagram_frame_size = params->max_datagram_frame_size; + /* These parameters are treated specially. If server accepts early + data, it must not set values for these parameters that are + smaller than these remembered values. */ + conn->early.transport_params.initial_max_streams_bidi = + params->initial_max_streams_bidi; + conn->early.transport_params.initial_max_streams_uni = + params->initial_max_streams_uni; + conn->early.transport_params.initial_max_stream_data_bidi_local = + params->initial_max_stream_data_bidi_local; + conn->early.transport_params.initial_max_stream_data_bidi_remote = + params->initial_max_stream_data_bidi_remote; + conn->early.transport_params.initial_max_stream_data_uni = + params->initial_max_stream_data_uni; + conn->early.transport_params.initial_max_data = params->initial_max_data; + conn->early.transport_params.active_connection_id_limit = + params->active_connection_id_limit; + conn->early.transport_params.max_datagram_frame_size = + params->max_datagram_frame_size; + conn_sync_stream_id_limit(conn); conn->tx.max_offset = p->initial_max_data; @@ -9643,8 +11320,11 @@ void ngtcp2_conn_set_early_remote_transport_params( NGTCP2_QLOG_SIDE_REMOTE); } -int ngtcp2_conn_set_local_transport_params( - ngtcp2_conn *conn, const ngtcp2_transport_params *params) { +int ngtcp2_conn_set_local_transport_params_versioned( + ngtcp2_conn *conn, int transport_params_version, + const ngtcp2_transport_params *params) { + (void)transport_params_version; + assert(conn->server); assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); @@ -9652,7 +11332,7 @@ int ngtcp2_conn_set_local_transport_params( return NGTCP2_ERR_INVALID_STATE; } - conn->local.transport_params = *params; + conn_set_local_transport_params(conn, params); return 0; } @@ -9661,7 +11341,6 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { const ngtcp2_mem *mem = conn->mem; ngtcp2_transport_params *params = &conn->local.transport_params; ngtcp2_scid *scident; - ngtcp2_ksl_it it; int rv; assert(1 == ngtcp2_ksl_len(&conn->scid.set)); @@ -9677,32 +11356,21 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { params->preferred_address_present = 0; } - if (conn->server) { - if (params->stateless_reset_token_present) { - it = ngtcp2_ksl_begin(&conn->scid.set); - scident = ngtcp2_ksl_it_get(&it); - - memcpy(scident->token, params->stateless_reset_token, - NGTCP2_STATELESS_RESET_TOKENLEN); + if (conn->server && params->preferred_address_present) { + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); + if (scident == NULL) { + return NGTCP2_ERR_NOMEM; } - if (params->preferred_address_present) { - scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); - if (scident == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid, - params->preferred_address.stateless_reset_token); + ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid); - rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident); - if (rv != 0) { - ngtcp2_mem_free(mem, scident); - return rv; - } - - conn->scid.last_seq = 1; + rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident); + if (rv != 0) { + ngtcp2_mem_free(mem, scident); + return rv; } + + conn->scid.last_seq = 1; } conn->rx.window = conn->rx.unsent_max_offset = conn->rx.max_offset = @@ -9712,15 +11380,27 @@ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni; conn->remote.uni.max_streams = params->initial_max_streams_uni; + conn->flags |= NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED; + ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server, NGTCP2_QLOG_SIDE_LOCAL); return 0; } -void ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, - ngtcp2_transport_params *params) { - *params = conn->local.transport_params; +const ngtcp2_transport_params * +ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn) { + return &conn->local.transport_params; +} + +ngtcp2_ssize ngtcp2_conn_encode_local_transport_params(ngtcp2_conn *conn, + uint8_t *dest, + size_t destlen) { + return ngtcp2_encode_transport_params( + dest, destlen, + conn->server ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS + : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, + &conn->local.transport_params); } int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, @@ -9732,7 +11412,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, return NGTCP2_ERR_STREAM_ID_BLOCKED; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } @@ -9740,7 +11420,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id, stream_user_data); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } @@ -9759,7 +11439,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, return NGTCP2_ERR_STREAM_ID_BLOCKED; } - strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); if (strm == NULL) { return NGTCP2_ERR_NOMEM; } @@ -9767,7 +11447,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id, stream_user_data); if (rv != 0) { - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); @@ -9779,39 +11459,72 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, } ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) { - ngtcp2_map_entry *me; - - me = ngtcp2_map_find(&conn->strms, (uint64_t)stream_id); - if (me == NULL) { - return NULL; - } - - return ngtcp2_struct_of(me, ngtcp2_strm, me); + return ngtcp2_map_find(&conn->strms, (uint64_t)stream_id); } -ngtcp2_ssize ngtcp2_conn_write_stream(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_ssize *pdatalen, - uint32_t flags, int64_t stream_id, - const uint8_t *data, size_t datalen, - ngtcp2_tstamp ts) { +ngtcp2_ssize ngtcp2_conn_write_stream_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen, + ngtcp2_tstamp ts) { ngtcp2_vec datav; datav.len = datalen; datav.base = (uint8_t *)data; - return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen, pdatalen, - flags, stream_id, &datav, 1, ts); + return ngtcp2_conn_writev_stream_versioned(conn, path, pkt_info_version, pi, + dest, destlen, pdatalen, flags, + stream_id, &datav, 1, ts); } -ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_ssize *pdatalen, - uint32_t flags, int64_t stream_id, - const ngtcp2_vec *datav, size_t datavcnt, - ngtcp2_tstamp ts) { +static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, + ngtcp2_path *path, + int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts) { + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_ssize nwrite; + int undersized; + + nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest, + destlen, vmsg, ts); + if (nwrite < 0) { + return nwrite; + } + + if (cstat->bytes_in_flight >= cstat->cwnd) { + conn->rst.is_cwnd_limited = 1; + } + + if (vmsg == NULL && cstat->bytes_in_flight < cstat->cwnd && + conn->tx.strmq_nretrans == 0) { + if (conn->local.settings.no_udp_payload_size_shaping) { + undersized = (size_t)nwrite < conn->local.settings.max_udp_payload_size; + } else { + undersized = (size_t)nwrite < conn->dcid.current.max_udp_payload_size; + } + + if (undersized) { + conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight; + + if (conn->rst.app_limited == 0) { + conn->rst.app_limited = cstat->max_udp_payload_size; + } + } + } + + return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts) { ngtcp2_vmsg vmsg, *pvmsg; ngtcp2_strm *strm; + int64_t datalen; if (pdatalen) { *pdatalen = -1; @@ -9827,6 +11540,16 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, return NGTCP2_ERR_STREAM_SHUT_WR; } + datalen = ngtcp2_vec_len_varint(datav, datavcnt); + if (datalen == -1) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if ((uint64_t)datalen > NGTCP2_MAX_VARINT - strm->tx.offset || + (uint64_t)datalen > NGTCP2_MAX_VARINT - conn->tx.offset) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + vmsg.type = NGTCP2_VMSG_TYPE_STREAM; vmsg.stream.strm = strm; vmsg.stream.flags = flags; @@ -9839,50 +11562,67 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, pvmsg = NULL; } - return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, pvmsg, ts); + return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, + destlen, pvmsg, ts); } -ngtcp2_ssize ngtcp2_conn_writev_datagram(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, int *paccepted, - uint32_t flags, - const ngtcp2_vec *datav, - size_t datavcnt, ngtcp2_tstamp ts) { +ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, + uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts) { ngtcp2_vmsg vmsg; + int64_t datalen; if (paccepted) { *paccepted = 0; } - if (conn->remote.transport_params.max_datagram_frame_size == 0) { + if (conn->remote.transport_params == NULL || + conn->remote.transport_params->max_datagram_frame_size == 0) { return NGTCP2_ERR_INVALID_STATE; } - if (conn->remote.transport_params.max_datagram_frame_size < - ngtcp2_pkt_datagram_framelen(ngtcp2_vec_len(datav, datavcnt))) { + + datalen = ngtcp2_vec_len_varint(datav, datavcnt); + if (datalen == -1 || (uint64_t)datalen > SIZE_MAX) { + return NGTCP2_ERR_INVALID_STATE; + } + + if (conn->remote.transport_params->max_datagram_frame_size < + ngtcp2_pkt_datagram_framelen((size_t)datalen)) { return NGTCP2_ERR_INVALID_ARGUMENT; } vmsg.type = NGTCP2_VMSG_TYPE_DATAGRAM; + vmsg.datagram.dgram_id = dgram_id; vmsg.datagram.flags = flags; vmsg.datagram.data = datav; vmsg.datagram.datacnt = datavcnt; vmsg.datagram.paccepted = paccepted; - return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, &vmsg, ts); + return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, + destlen, &vmsg, ts); } ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts) { + int pkt_info_version, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; - ngtcp2_pktns *pktns = &conn->pktns; - size_t origlen = destlen; + size_t origlen; + size_t origdestlen = destlen; int rv; uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ssize res = 0; - size_t server_tx_left; + uint64_t server_tx_left; + uint64_t datalen; + uint64_t write_datalen = 0; + int64_t prev_in_pkt_num = -1; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *rtbent; + (void)pkt_info_version; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -9891,25 +11631,47 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } + origlen = destlen = + conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + if (!ppe_pending && pi) { pi->ecn = NGTCP2_ECN_NOT_ECT; } + if (!conn_pacing_pkt_tx_allowed(conn, ts)) { + return 0; + } + switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts); - if (nwrite < 0) { + /* We might be unable to write a packet because of depletion of + congestion window budget, perhaps due to packet loss that + shrinks the window drastically. */ + if (nwrite <= 0) { return nwrite; } if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { return nwrite; } + + assert(nwrite); + assert(dest[0] & NGTCP2_HEADER_FORM_BIT); + assert(conn->negotiated_version); + + if (ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) == + NGTCP2_PKT_INITIAL) { + /* We have added padding already, but in that case, there is no + space left to write 1RTT packet. */ + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + res = nwrite; dest += nwrite; destlen -= (size_t)nwrite; - /* Break here so that we can coalesces Short packets. */ + /* Break here so that we can coalesces 1RTT packet. */ break; case NGTCP2_CS_SERVER_INITIAL: case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: @@ -9917,23 +11679,74 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (!ppe_pending) { if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); - destlen = ngtcp2_min(destlen, server_tx_left); + if (server_tx_left == 0) { + if (cstat->loss_detection_timer != UINT64_MAX) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled due to amplification limit"); + cstat->loss_detection_timer = UINT64_MAX; + } + + return 0; + } + + destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left); + } + + if (vmsg) { + switch (vmsg->type) { + case NGTCP2_VMSG_TYPE_STREAM: + datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); + if (datalen == 0 || (datalen > 0 && + (vmsg->stream.strm->tx.max_offset - + vmsg->stream.strm->tx.offset) && + (conn->tx.max_offset - conn->tx.offset))) { + write_datalen = + conn_enforce_flow_control(conn, vmsg->stream.strm, datalen); + write_datalen = + ngtcp2_min(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); + write_datalen += NGTCP2_STREAM_OVERHEAD; + } + break; + case NGTCP2_VMSG_TYPE_DATAGRAM: + write_datalen = + ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt) + + NGTCP2_DATAGRAM_OVERHEAD; + break; + default: + assert(0); + } + + if (conn->in_pktns && write_datalen > 0) { + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + if (!ngtcp2_ksl_it_end(&it)) { + rtbent = ngtcp2_ksl_it_get(&it); + prev_in_pkt_num = rtbent->hd.pkt_num; + } + } } - nwrite = conn_write_handshake(conn, pi, dest, destlen, 0, ts); + nwrite = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts); if (nwrite < 0) { return nwrite; } - if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { - destlen = origlen; - } else { - origlen = destlen; - } - res = nwrite; dest += nwrite; destlen -= (size_t)nwrite; + + if (conn->in_pktns && write_datalen > 0) { + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + if (!ngtcp2_ksl_it_end(&it)) { + rtbent = ngtcp2_ksl_it_get(&it); + if (rtbent->hd.pkt_num != prev_in_pkt_num && + (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + /* We have added padding already, but in that case, there + is no space left to write 1RTT packet. */ + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + } + } } if (conn->state != NGTCP2_CS_POST_HANDSHAKE && conn->pktns.crypto.tx.ckm == NULL) { @@ -9950,7 +11763,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, return 0; } - assert(pktns->crypto.tx.ckm); + assert(conn->pktns.crypto.tx.ckm); if (conn_check_pkt_num_exhausted(conn)) { return NGTCP2_ERR_PKT_NUM_EXHAUSTED; @@ -9976,12 +11789,18 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (ppe_pending) { res = conn->pkt.hs_spktlen; conn->pkt.hs_spktlen = 0; + if (conn->pkt.require_padding) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } /* dest and destlen have already been adjusted in ppe in the first run. They are adjusted for probe packet later. */ - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); goto fin; } else { + conn->pkt.require_padding = + (wflags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING); + if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { rv = conn_prepare_key_update(conn, ts); if (rv != 0) { @@ -9992,29 +11811,43 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) { destlen = 0; } else { - nwrite = conn_write_path_response(conn, path, pi, dest, destlen, ts); - if (nwrite) { - goto fin; - } - - if (conn->pv) { - nwrite = conn_write_path_challenge(conn, path, pi, dest, destlen, ts); + if (res == 0) { + nwrite = + conn_write_path_response(conn, path, pi, dest, origdestlen, ts); if (nwrite) { goto fin; } + + if (conn->pv) { + nwrite = + conn_write_path_challenge(conn, path, pi, dest, origdestlen, ts); + if (nwrite) { + goto fin; + } + } + + if (conn->pmtud && + (!conn->hs_pktns || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) == 0)) { + nwrite = conn_write_pmtud_probe(conn, pi, dest, origdestlen, ts); + if (nwrite) { + goto fin; + } + } } if (conn->server && !(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); - origlen = ngtcp2_min(origlen, server_tx_left); - destlen = ngtcp2_min(destlen, server_tx_left); - - if (destlen == 0 && conn->cstat.loss_detection_timer != UINT64_MAX) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss detection timer canceled"); + origlen = (size_t)ngtcp2_min((uint64_t)origlen, server_tx_left); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left); + + if (server_tx_left == 0 && + conn->cstat.loss_detection_timer != UINT64_MAX) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled due to amplification limit"); conn->cstat.loss_detection_timer = UINT64_MAX; - conn->cstat.pto_count = 0; } } } @@ -10025,7 +11858,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (conn_handshake_probe_left(conn)) { destlen = origlen; } - nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, 0, ts); + nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, + /* write_datalen = */ 0, ts); if (nwrite < 0) { return nwrite; } @@ -10042,13 +11876,13 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, "transmit probe pkt left=%zu", conn->pktns.rtb.probe_pkt_left); - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); goto fin; } - nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT, wflags, ts); if (nwrite) { assert(nwrite != NGTCP2_ERR_NOBUF); @@ -10056,7 +11890,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, } if (res == 0) { - nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_SHORT, ts); + nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts); } fin: @@ -10078,24 +11912,27 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, static ngtcp2_ssize conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint8_t pkt_type, - uint64_t error_code, ngtcp2_tstamp ts) { + uint64_t error_code, const uint8_t *reason, + size_t reasonlen, 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 flags = NGTCP2_WRITE_PKT_FLAG_NONE; fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; fr.connection_close.error_code = error_code; fr.connection_close.frame_type = 0; - fr.connection_close.reasonlen = 0; - fr.connection_close.reason = NULL; + fr.connection_close.reasonlen = reasonlen; + fr.connection_close.reason = (uint8_t *)reason; if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && pkt_type != NGTCP2_PKT_INITIAL) { if (in_pktns && conn->server) { nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, &conn->dcid.current.cid, - &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, + NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; } @@ -10109,7 +11946,8 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, hs_pktns->crypto.tx.ckm) { nwrite = ngtcp2_conn_write_single_frame_pkt( conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, - &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; } @@ -10120,8 +11958,12 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } } + if (!conn->server && pkt_type == NGTCP2_PKT_INITIAL) { + flags = NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, pkt_type, &conn->dcid.current.cid, &fr, + conn, pi, dest, destlen, pkt_type, flags, &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { @@ -10137,13 +11979,15 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return res; } -ngtcp2_ssize ngtcp2_conn_write_connection_close( +ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt( ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, uint64_t error_code, ngtcp2_tstamp ts) { + size_t destlen, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; uint8_t pkt_type; ngtcp2_ssize nwrite; + uint64_t server_tx_left; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -10154,9 +11998,10 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close( switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: + return NGTCP2_ERR_INVALID_STATE; case NGTCP2_CS_CLOSING: case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; + return 0; default: break; } @@ -10165,13 +12010,20 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close( ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } + destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + if (pi) { pi->ecn = NGTCP2_ECN_NOT_ECT; } + if (conn->server) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left); + } + if (conn->state == NGTCP2_CS_POST_HANDSHAKE || (conn->server && conn->pktns.crypto.tx.ckm)) { - pkt_type = NGTCP2_PKT_SHORT; + pkt_type = NGTCP2_PKT_1RTT; } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { pkt_type = NGTCP2_PKT_HANDSHAKE; } else if (in_pktns && in_pktns->crypto.tx.ckm) { @@ -10183,7 +12035,7 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close( } nwrite = conn_write_connection_close(conn, pi, dest, destlen, pkt_type, - error_code, ts); + error_code, reason, reasonlen, ts); if (nwrite < 0) { return nwrite; } @@ -10193,12 +12045,14 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close( return nwrite; } -ngtcp2_ssize ngtcp2_conn_write_application_close( +ngtcp2_ssize ngtcp2_conn_write_application_close_pkt( ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, uint64_t app_error_code, ngtcp2_tstamp ts) { + size_t destlen, uint64_t app_error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; ngtcp2_ssize res = 0; ngtcp2_frame fr; + uint64_t server_tx_left; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -10209,9 +12063,10 @@ ngtcp2_ssize ngtcp2_conn_write_application_close( switch (conn->state) { case NGTCP2_CS_CLIENT_INITIAL: + return NGTCP2_ERR_INVALID_STATE; case NGTCP2_CS_CLOSING: case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; + return 0; default: break; } @@ -10220,16 +12075,23 @@ ngtcp2_ssize ngtcp2_conn_write_application_close( ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } + destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + if (pi) { pi->ecn = NGTCP2_ECN_NOT_ECT; } + if (conn->server) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left); + } + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { nwrite = conn_write_connection_close(conn, pi, dest, destlen, conn->hs_pktns->crypto.tx.ckm ? NGTCP2_PKT_HANDSHAKE : NGTCP2_PKT_INITIAL, - NGTCP2_APPLICATION_ERROR, ts); + NGTCP2_APPLICATION_ERROR, NULL, 0, ts); if (nwrite < 0) { return nwrite; } @@ -10251,12 +12113,12 @@ ngtcp2_ssize ngtcp2_conn_write_application_close( fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP; fr.connection_close.error_code = app_error_code; fr.connection_close.frame_type = 0; - fr.connection_close.reasonlen = 0; - fr.connection_close.reason = NULL; + fr.connection_close.reasonlen = reasonlen; + fr.connection_close.reason = (uint8_t *)reason; nwrite = ngtcp2_conn_write_single_frame_pkt( - conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &conn->dcid.current.cid, &fr, - NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, + &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); if (nwrite < 0) { return nwrite; @@ -10273,6 +12135,92 @@ ngtcp2_ssize ngtcp2_conn_write_application_close( return res; } +static void +connection_close_error_init(ngtcp2_connection_close_error *ccerr, + ngtcp2_connection_close_error_code_type type, + uint64_t error_code, const uint8_t *reason, + size_t reasonlen) { + ccerr->type = type; + ccerr->error_code = error_code; + ccerr->frame_type = 0; + ccerr->reason = (uint8_t *)reason; + ccerr->reasonlen = reasonlen; +} + +void ngtcp2_connection_close_error_default( + ngtcp2_connection_close_error *ccerr) { + connection_close_error_init(ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + NGTCP2_NO_ERROR, NULL, 0); +} + +void ngtcp2_connection_close_error_set_transport_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen) { + connection_close_error_init(ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + error_code, reason, reasonlen); +} + +void ngtcp2_connection_close_error_set_transport_error_liberr( + ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason, + size_t reasonlen) { + switch (liberr) { + case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: + connection_close_error_init( + ccerr, + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION, + NGTCP2_NO_ERROR, reason, reasonlen); + + return; + case NGTCP2_ERR_IDLE_CLOSE: + connection_close_error_init( + ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE, + NGTCP2_NO_ERROR, reason, reasonlen); + + return; + }; + + ngtcp2_connection_close_error_set_transport_error( + ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason, + reasonlen); +} + +void ngtcp2_connection_close_error_set_transport_error_tls_alert( + ngtcp2_connection_close_error *ccerr, uint8_t tls_alert, + const uint8_t *reason, size_t reasonlen) { + ngtcp2_connection_close_error_set_transport_error( + ccerr, NGTCP2_CRYPTO_ERROR | tls_alert, reason, reasonlen); +} + +void ngtcp2_connection_close_error_set_application_error( + ngtcp2_connection_close_error *ccerr, uint64_t error_code, + const uint8_t *reason, size_t reasonlen) { + connection_close_error_init( + ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, error_code, + reason, reasonlen); +} + +ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts) { + (void)pkt_info_version; + + switch (ccerr->type) { + case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT: + return ngtcp2_conn_write_connection_close_pkt( + conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason, + ccerr->reasonlen, ts); + case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION: + return ngtcp2_conn_write_application_close_pkt( + conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason, + ccerr->reasonlen, ts); + default: + return 0; + } +} + int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) { return conn->state == NGTCP2_CS_CLOSING; } @@ -10281,47 +12229,45 @@ int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn) { return conn->state == NGTCP2_CS_DRAINING; } -int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm) { int rv; - if (!strm->app_error_code) { - app_error_code = strm->app_error_code; - } - - rv = ngtcp2_map_remove(&conn->strms, strm->me.key); + rv = ngtcp2_map_remove(&conn->strms, (ngtcp2_map_key_type)strm->stream_id); if (rv != 0) { assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); return rv; } - rv = conn_call_stream_close(conn, strm, app_error_code); + rv = conn_call_stream_close(conn, strm); if (rv != 0) { goto fin; } if (ngtcp2_strm_is_tx_queued(strm)) { ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe); + if (!ngtcp2_strm_streamfrq_empty(strm)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } } fin: ngtcp2_strm_free(strm); - ngtcp2_mem_free(conn->mem, strm); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); return rv; } -int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code) { +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, + ngtcp2_strm *strm) { if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RDWR) == NGTCP2_STRM_FLAG_SHUT_RDWR && ((strm->flags & NGTCP2_STRM_FLAG_RECV_RST) || ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) && (((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && (strm->flags & NGTCP2_STRM_FLAG_RST_ACKED)) || - (!(strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && - ngtcp2_strm_is_all_tx_data_acked(strm)))) { - return ngtcp2_conn_close_stream(conn, strm, app_error_code); + ngtcp2_strm_is_all_tx_data_fin_acked(strm))) { + return ngtcp2_conn_close_stream(conn, strm); } return 0; } @@ -10338,14 +12284,16 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, */ static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm, uint64_t app_error_code) { - if (strm->flags & NGTCP2_STRM_FLAG_SENT_RST) { + ngtcp2_strm_set_app_error_code(strm, app_error_code); + + if ((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) || + ngtcp2_strm_is_all_tx_data_fin_acked(strm)) { return 0; } /* Set this flag so that we don't accidentally send DATA to this stream. */ strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; - strm->app_error_code = app_error_code; ngtcp2_strm_streamfrq_clear(strm); @@ -10381,7 +12329,7 @@ static int conn_shutdown_stream_read(ngtcp2_conn *conn, ngtcp2_strm *strm, } strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING; - strm->app_error_code = app_error_code; + ngtcp2_strm_set_app_error_code(strm, app_error_code); return conn_stop_sending(conn, strm, app_error_code); } @@ -10393,7 +12341,7 @@ int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id, strm = ngtcp2_conn_find_stream(conn, stream_id); if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; + return 0; } rv = conn_shutdown_stream_read(conn, strm, app_error_code); @@ -10415,7 +12363,7 @@ int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id, strm = ngtcp2_conn_find_stream(conn, stream_id); if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; + return 0; } return conn_shutdown_stream_write(conn, strm, app_error_code); @@ -10427,7 +12375,7 @@ int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id, strm = ngtcp2_conn_find_stream(conn, stream_id); if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; + return 0; } return conn_shutdown_stream_read(conn, strm, app_error_code); @@ -10475,7 +12423,7 @@ int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id, strm = ngtcp2_conn_find_stream(conn, stream_id); if (strm == NULL) { - return NGTCP2_ERR_STREAM_NOT_FOUND; + return 0; } return conn_extend_max_stream_offset(conn, strm, datalen); @@ -10488,77 +12436,135 @@ void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, uint64_t datalen) { return; } - conn->rx.unsent_max_offset += datalen; + conn->rx.unsent_max_offset += datalen; +} + +void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) { + handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n); +} + +void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) { + handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n); +} + +const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) { + return &conn->dcid.current.cid; +} + +const ngtcp2_cid *ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn) { + return &conn->rcid; +} + +uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn) { + return conn->client_chosen_version; +} + +uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { + return conn->negotiated_version; +} + +static int delete_strms_pq_each(void *data, void *ptr) { + ngtcp2_conn *conn = ptr; + ngtcp2_strm *s = data; + + if (ngtcp2_strm_is_tx_queued(s)) { + ngtcp2_pq_remove(&conn->tx.strmq, &s->pe); + if (!ngtcp2_strm_streamfrq_empty(s)) { + assert(conn->tx.strmq_nretrans); + --conn->tx.strmq_nretrans; + } + } + + ngtcp2_strm_free(s); + ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s); + + return 0; } -void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) { - handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n); -} +/* + * conn_discard_early_data_state discards any connection states which + * are altered by any operations during early data transfer. + */ +static void conn_discard_early_data_state(ngtcp2_conn *conn) { + ngtcp2_frame_chain **pfrc, *frc; -void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) { - handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n); -} + ngtcp2_rtb_remove_early_data(&conn->pktns.rtb, &conn->cstat); -const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) { - return &conn->dcid.current.cid; -} + ngtcp2_map_each_free(&conn->strms, delete_strms_pq_each, conn); + ngtcp2_map_clear(&conn->strms); -uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { - return conn->version; -} + conn->tx.offset = 0; -int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { - ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_rtb *rtb = &conn->pktns.rtb; - int rv; + conn->rx.unsent_max_offset = conn->rx.max_offset = + conn->local.transport_params.initial_max_data; - conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; + conn->remote.bidi.unsent_max_streams = conn->remote.bidi.max_streams = + conn->local.transport_params.initial_max_streams_bidi; - rv = ngtcp2_rtb_remove_all(rtb, conn, pktns, &conn->cstat); - if (rv != 0) { - return rv; + conn->remote.uni.unsent_max_streams = conn->remote.uni.max_streams = + conn->local.transport_params.initial_max_streams_uni; + + if (conn->server) { + conn->local.bidi.next_stream_id = 1; + conn->local.uni.next_stream_id = 3; + } else { + conn->local.bidi.next_stream_id = 0; + conn->local.uni.next_stream_id = 2; } - return rv; + for (pfrc = &conn->pktns.tx.frq; *pfrc;) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); + } } -void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, - ngtcp2_duration ack_delay, ngtcp2_tstamp ts) { - ngtcp2_conn_stat *cstat = &conn->cstat; - ngtcp2_duration min_rtt; +void ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { + if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { + return; + } + + conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; - rtt = ngtcp2_max(rtt, NGTCP2_GRANULARITY); + conn_discard_early_data_state(conn); +} - cstat->latest_rtt = rtt; +int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay, ngtcp2_tstamp ts) { + ngtcp2_conn_stat *cstat = &conn->cstat; if (cstat->min_rtt == UINT64_MAX) { + cstat->latest_rtt = rtt; cstat->min_rtt = rtt; cstat->smoothed_rtt = rtt; cstat->rttvar = rtt / 2; cstat->first_rtt_sample_ts = ts; } else { - min_rtt = ngtcp2_min(cstat->min_rtt, rtt); if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { + assert(conn->remote.transport_params); + ack_delay = - ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay); - } else if (ack_delay > 0 && rtt < cstat->min_rtt + ack_delay) { + ngtcp2_min(ack_delay, conn->remote.transport_params->max_ack_delay); + } else if (ack_delay > 0 && rtt >= cstat->min_rtt && + rtt < cstat->min_rtt + ack_delay) { /* Ignore RTT sample if adjusting ack_delay causes the sample less than min_rtt before handshake confirmation. */ ngtcp2_log_info( &conn->log, NGTCP2_LOG_EVENT_RCV, "ignore rtt sample because ack_delay is too large latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " ack_delay=%" PRIu64, - (uint64_t)(rtt / NGTCP2_MILLISECONDS), - (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS), - (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); - return; + rtt / NGTCP2_MILLISECONDS, cstat->min_rtt / NGTCP2_MILLISECONDS, + ack_delay / NGTCP2_MILLISECONDS); + return NGTCP2_ERR_INVALID_ARGUMENT; } - if (rtt > min_rtt + ack_delay) { + cstat->latest_rtt = rtt; + cstat->min_rtt = ngtcp2_min(cstat->min_rtt, rtt); + + if (rtt >= cstat->min_rtt + ack_delay) { rtt -= ack_delay; } - cstat->min_rtt = min_rtt; cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt ? rtt - cstat->smoothed_rtt : cstat->smoothed_rtt - rtt)) / @@ -10566,56 +12572,91 @@ void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8; } - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 - " smoothed_rtt=%" PRIu64 " rttvar=%" PRIu64 - " ack_delay=%" PRIu64, - (uint64_t)(cstat->latest_rtt / NGTCP2_MILLISECONDS), - (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS), - cstat->smoothed_rtt / NGTCP2_MILLISECONDS, - cstat->rttvar / NGTCP2_MILLISECONDS, - (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " smoothed_rtt=%" PRIu64 + " rttvar=%" PRIu64 " ack_delay=%" PRIu64, + cstat->latest_rtt / NGTCP2_MILLISECONDS, + cstat->min_rtt / NGTCP2_MILLISECONDS, + cstat->smoothed_rtt / NGTCP2_MILLISECONDS, + cstat->rttvar / NGTCP2_MILLISECONDS, ack_delay / NGTCP2_MILLISECONDS); + + return 0; } -void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) { +void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn, + int conn_stat_version, + ngtcp2_conn_stat *cstat) { + (void)conn_stat_version; + *cstat = conn->cstat; } -static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn, - ngtcp2_tstamp *pts, - const ngtcp2_tstamp *times) { +static void conn_get_loss_time_and_pktns(ngtcp2_conn *conn, + ngtcp2_tstamp *ploss_time, + ngtcp2_pktns **ppktns) { + ngtcp2_pktns *const ns[] = {conn->hs_pktns, &conn->pktns}; + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_duration *loss_time = cstat->loss_time; + ngtcp2_tstamp earliest_loss_time = loss_time[NGTCP2_PKTNS_ID_INITIAL]; + ngtcp2_pktns *pktns = conn->in_pktns; + size_t i; + + for (i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) { + if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 || + loss_time[i] >= earliest_loss_time) { + continue; + } + + earliest_loss_time = loss_time[i]; + pktns = ns[i]; + } + + if (ploss_time) { + *ploss_time = earliest_loss_time; + } + if (ppktns) { + *ppktns = pktns; + } +} + +static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; - ngtcp2_pktns *res = ns[0]; size_t i; - ngtcp2_tstamp earliest_ts = times[NGTCP2_PKTNS_ID_INITIAL]; + ngtcp2_tstamp earliest_ts = UINT64_MAX, t; + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_tstamp *times = cstat->last_tx_pkt_ts; + ngtcp2_duration duration = + compute_pto(cstat->smoothed_rtt, cstat->rttvar, /* max_ack_delay = */ 0) * + (1ULL << cstat->pto_count); - for (i = NGTCP2_PKTNS_ID_HANDSHAKE; i < NGTCP2_PKTNS_ID_MAX; ++i) { - if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 || + for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) { + if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 || (times[i] == UINT64_MAX || - (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) || (i == NGTCP2_PKTNS_ID_APPLICATION && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { continue; } - earliest_ts = times[i]; - res = ns[i]; - } + t = times[i] + duration; - if (res == NULL && !conn->server) { - earliest_ts = UINT64_MAX; + if (i == NGTCP2_PKTNS_ID_APPLICATION) { + assert(conn->remote.transport_params); + t += conn->remote.transport_params->max_ack_delay * + (1ULL << cstat->pto_count); + } - if (conn->hs_pktns && conn->hs_pktns->crypto.tx.ckm) { - res = conn->hs_pktns; - } else { - res = conn->in_pktns; + if (t < earliest_ts) { + earliest_ts = t; } } - if (pts) { - *pts = earliest_ts; + if (earliest_ts == UINT64_MAX) { + return ts + duration; } - return res; + + return earliest_ts; } void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -10624,11 +12665,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_pktns *pktns = &conn->pktns; - ngtcp2_pktns *earliest_pktns; ngtcp2_tstamp earliest_loss_time; - ngtcp2_tstamp last_tx_pkt_ts; - conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time); + conn_get_loss_time_and_pktns(conn, &earliest_loss_time, NULL); if (earliest_loss_time != UINT64_MAX) { cstat->loss_detection_timer = earliest_loss_time; @@ -10639,9 +12678,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return; } - if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) && - (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) && - (pktns->rtb.num_retransmittable == 0 || + if ((!in_pktns || in_pktns->rtb.num_pto_eliciting == 0) && + (!hs_pktns || hs_pktns->rtb.num_pto_eliciting == 0) && + (pktns->rtb.num_pto_eliciting == 0 || !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) && (conn->server || (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | @@ -10655,24 +12694,14 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return; } - earliest_pktns = - conn_get_earliest_pktns(conn, &last_tx_pkt_ts, cstat->last_tx_pkt_ts); - - assert(earliest_pktns); - - if (last_tx_pkt_ts == UINT64_MAX) { - last_tx_pkt_ts = ts; - } - - timeout = conn_compute_pto(conn, earliest_pktns) * (1ULL << cstat->pto_count); + cstat->loss_detection_timer = conn_get_earliest_pto_expiry(conn, ts); - cstat->loss_detection_timer = last_tx_pkt_ts + timeout; + timeout = + cstat->loss_detection_timer > ts ? cstat->loss_detection_timer - ts : 0; ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, - "loss_detection_timer=%" PRIu64 " last_tx_pkt_ts=%" PRIu64 - " timeout=%" PRIu64, - cstat->loss_detection_timer, last_tx_pkt_ts, - (uint64_t)(timeout / NGTCP2_MILLISECONDS)); + "loss_detection_timer=%" PRIu64 " timeout=%" PRIu64, + cstat->loss_detection_timer, timeout / NGTCP2_MILLISECONDS); } int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -10681,9 +12710,7 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_tstamp earliest_loss_time; - ngtcp2_pktns *loss_pktns = - conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time); - ngtcp2_pktns *earliest_pktns; + ngtcp2_pktns *loss_pktns = NULL; conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -10702,10 +12729,14 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return 0; } + conn_get_loss_time_and_pktns(conn, &earliest_loss_time, &loss_pktns); + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "loss detection timer fired"); if (earliest_loss_time != UINT64_MAX) { + assert(loss_pktns); + rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts); if (rv != 0) { return rv; @@ -10714,35 +12745,26 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return 0; } - if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!conn->server && !conn_is_handshake_completed(conn)) { if (hs_pktns->crypto.tx.ckm) { hs_pktns->rtb.probe_pkt_left = 1; } else { in_pktns->rtb.probe_pkt_left = 1; } } else { - earliest_pktns = conn_get_earliest_pktns(conn, NULL, cstat->last_tx_pkt_ts); + if (in_pktns && in_pktns->rtb.num_pto_eliciting) { + in_pktns->rtb.probe_pkt_left = 1; - assert(earliest_pktns); + assert(hs_pktns); - switch (earliest_pktns->rtb.pktns_id) { - case NGTCP2_PKTNS_ID_INITIAL: - assert(in_pktns); - in_pktns->rtb.probe_pkt_left = 1; - if (!conn->server) { - break; + if (conn->server && hs_pktns->rtb.num_pto_eliciting) { + /* let server coalesce packets */ + hs_pktns->rtb.probe_pkt_left = 1; } - /* fall through for server so that it can coalesce packets. */ - /* fall through */ - case NGTCP2_PKTNS_ID_HANDSHAKE: - assert(hs_pktns); + } else if (hs_pktns && hs_pktns->rtb.num_pto_eliciting) { hs_pktns->rtb.probe_pkt_left = 1; - break; - case NGTCP2_PKTNS_ID_APPLICATION: + } else { conn->pktns.rtb.probe_pkt_left = 2; - break; - default: - assert(0); } } @@ -10756,6 +12778,34 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return 0; } +static int conn_buffer_crypto_data(ngtcp2_conn *conn, const uint8_t **pdata, + ngtcp2_pktns *pktns, const uint8_t *data, + size_t datalen) { + int rv; + ngtcp2_buf_chain **pbufchain = &pktns->crypto.tx.data; + + if (*pbufchain) { + for (; (*pbufchain)->next; pbufchain = &(*pbufchain)->next) + ; + + if (ngtcp2_buf_left(&(*pbufchain)->buf) < datalen) { + pbufchain = &(*pbufchain)->next; + } + } + + if (!*pbufchain) { + rv = ngtcp2_buf_chain_new(pbufchain, ngtcp2_max(1024, datalen), conn->mem); + if (rv != 0) { + return rv; + } + } + + *pdata = (*pbufchain)->buf.last; + (*pbufchain)->buf.last = ngtcp2_cpymem((*pbufchain)->buf.last, data, datalen); + + return 0; +} + int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, const uint8_t *data, const size_t datalen) { @@ -10784,7 +12834,12 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, return NGTCP2_ERR_INVALID_ARGUMENT; } - rv = ngtcp2_frame_chain_new(&frc, conn->mem); + rv = conn_buffer_crypto_data(conn, &data, pktns, data, datalen); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); if (rv != 0) { return rv; } @@ -10799,7 +12854,7 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); return rv; } @@ -10813,24 +12868,18 @@ int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token, size_t tokenlen) { int rv; ngtcp2_frame_chain *nfrc; - uint8_t *p; + ngtcp2_vec tokenv = {(uint8_t *)token, tokenlen}; assert(conn->server); assert(token); assert(tokenlen); - rv = ngtcp2_frame_chain_extralen_new(&nfrc, tokenlen, conn->mem); + rv = ngtcp2_frame_chain_new_token_objalloc_new( + &nfrc, &tokenv, &conn->frc_objalloc, conn->mem); if (rv != 0) { return rv; } - nfrc->fr.type = NGTCP2_FRAME_NEW_TOKEN; - - p = (uint8_t *)nfrc + sizeof(*nfrc); - memcpy(p, token, tokenlen); - - ngtcp2_vec_init(&nfrc->fr.new_token.token, p, tokenlen); - nfrc->next = conn->pktns.tx.frq; conn->pktns.tx.frq = nfrc; @@ -10853,11 +12902,20 @@ int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) { return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe); } +static int conn_has_uncommited_preferred_address_cid(ngtcp2_conn *conn) { + return conn->server && + !(conn->flags & NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED) && + conn->oscid.datalen && + conn->local.transport_params.preferred_address_present; +} + size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn) { - return ngtcp2_ksl_len(&conn->scid.set); + return ngtcp2_ksl_len(&conn->scid.set) + + (size_t)conn_has_uncommited_preferred_address_cid(conn); } size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { + ngtcp2_cid *origdest = dest; ngtcp2_ksl_it it; ngtcp2_scid *scid; @@ -10867,7 +12925,11 @@ size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { *dest++ = scid->cid; } - return ngtcp2_ksl_len(&conn->scid.set); + if (conn_has_uncommited_preferred_address_cid(conn)) { + *dest++ = conn->local.transport_params.preferred_address.cid; + } + + return (size_t)(dest - origdest); } size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { @@ -10889,7 +12951,7 @@ size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { } } - n += ngtcp2_ringbuf_len(&conn->dcid.retired); + n += ngtcp2_ringbuf_len(&conn->dcid.retired.rb); return n; } @@ -10899,9 +12961,10 @@ static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest, 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); + if ((dest->token_present = + (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) != 0)) { + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + } } size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { @@ -10930,9 +12993,9 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { } } - len = ngtcp2_ringbuf_len(&conn->dcid.retired); + len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); copy_dcid_to_cid_token(dest, dcid); ++dest; } @@ -10943,71 +13006,148 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { ngtcp2_addr *dest = &conn->dcid.current.ps.path.local; - assert(addr->addrlen <= sizeof(conn->dcid.current.ps.local_addrbuf)); + assert(addr->addrlen <= + (ngtcp2_socklen)sizeof(conn->dcid.current.ps.local_addrbuf)); ngtcp2_addr_copy(dest, addr); } -void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { - ngtcp2_addr *dest = &conn->dcid.current.ps.path.remote; - - assert(addr->addrlen <= sizeof(conn->dcid.current.ps.remote_addrbuf)); - ngtcp2_addr_copy(dest, addr); +void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, void *path_user_data) { + conn->dcid.current.ps.path.user_data = path_user_data; } const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) { return &conn->dcid.current.ps.path; } -int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, - ngtcp2_tstamp ts) { - int rv; - ngtcp2_dcid *dcid; +size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn) { + return conn->local.settings.max_udp_payload_size; +} - assert(!conn->server); +size_t ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn) { + if (conn->local.settings.no_udp_payload_size_shaping) { + return ngtcp2_conn_get_max_udp_payload_size(conn); + } - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; + return conn->dcid.current.max_udp_payload_size; +} - if (conn->remote.transport_params.disable_active_migration || +static int conn_initiate_migration_precheck(ngtcp2_conn *conn, + const ngtcp2_addr *local_addr) { + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) || + conn->remote.transport_params->disable_active_migration || conn->dcid.current.cid.datalen == 0 || - !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR))) { return NGTCP2_ERR_INVALID_STATE; } - if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + + if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, local_addr)) { return NGTCP2_ERR_INVALID_ARGUMENT; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + return 0; +} + +int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, + const ngtcp2_path *path, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_dcid *dcid; + + assert(!conn->server); + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; - rv = conn_stop_pv(conn, ts); + rv = conn_initiate_migration_precheck(conn, &path->local); if (rv != 0) { return rv; } + ngtcp2_conn_stop_pmtud(conn); + + if (conn->pv) { + rv = conn_abort_pv(conn, ts); + if (rv != 0) { + return rv; + } + } + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); if (rv != 0) { return rv; } + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ngtcp2_dcid_set_path(dcid, path); + ngtcp2_dcid_copy(&conn->dcid.current, dcid); - ngtcp2_path_copy(&conn->dcid.current.ps.path, path); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { return rv; } - conn_reset_congestion_state(conn); + conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + return 0; } +int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_dcid *dcid; + ngtcp2_duration pto, initial_pto, timeout; + ngtcp2_pv *pv; + + assert(!conn->server); + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + rv = conn_initiate_migration_precheck(conn, &path->local); + if (rv != 0) { + return rv; + } + + if (conn->pv) { + rv = conn_abort_pv(conn, ts); + if (rv != 0) { + return rv; + } + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ngtcp2_dcid_set_path(dcid, path); + + pto = conn_compute_pto(conn, &conn->pktns); + initial_pto = conn_compute_initial_pto(conn, &conn->pktns); + timeout = 3 * ngtcp2_max(pto, initial_pto); + + rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log, + conn->mem); + if (rv != 0) { + return rv; + } + + ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + conn->pv = pv; + + return conn_call_activate_dcid(conn, &pv->dcid); +} + uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn) { return conn->local.uni.max_streams; } @@ -11016,6 +13156,17 @@ uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { return conn->tx.max_offset - conn->tx.offset; } +uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn, + int64_t stream_id) { + ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); + + if (strm == NULL) { + return 0; + } + + return strm->tx.max_offset - strm->tx.offset; +} + uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn) { uint64_t n = ngtcp2_ord_stream_id(conn->local.bidi.next_stream_id); @@ -11031,6 +13182,17 @@ uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) { : conn->local.uni.max_streams - n + 1; } +uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn) { + uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; + uint64_t cwnd = conn_get_cwnd(conn); + + if (cwnd > bytes_in_flight) { + return cwnd - bytes_in_flight; + } + + return 0; +} + ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { ngtcp2_duration trpto; ngtcp2_duration idle_timeout; @@ -11038,33 +13200,30 @@ ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { /* TODO Remote max_idle_timeout becomes effective after handshake completion. */ - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || - conn->remote.transport_params.max_idle_timeout == 0 || + if (!conn_is_handshake_completed(conn) || + conn->remote.transport_params->max_idle_timeout == 0 || (conn->local.transport_params.max_idle_timeout && conn->local.transport_params.max_idle_timeout < - conn->remote.transport_params.max_idle_timeout)) { + conn->remote.transport_params->max_idle_timeout)) { idle_timeout = conn->local.transport_params.max_idle_timeout; } else { - idle_timeout = conn->remote.transport_params.max_idle_timeout; + idle_timeout = conn->remote.transport_params->max_idle_timeout; } if (idle_timeout == 0) { return UINT64_MAX; } - trpto = 3 * conn_compute_pto( - conn, (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) - ? &conn->pktns - : conn->hs_pktns); + trpto = 3 * conn_compute_pto(conn, conn_is_handshake_completed(conn) + ? &conn->pktns + : conn->hs_pktns); return conn->idle_ts + ngtcp2_max(idle_timeout, trpto); } ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { - return conn_compute_pto(conn, - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) - ? &conn->pktns - : conn->hs_pktns); + return conn_compute_pto( + conn, conn_is_handshake_completed(conn) ? &conn->pktns : conn->hs_pktns); } void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, @@ -11116,9 +13275,9 @@ void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn, conn->crypto.tls_native_handle = tls_native_handle; } -void ngtcp2_conn_get_connection_close_error_code( - ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec) { - *ccec = conn->rx.ccec; +void ngtcp2_conn_get_connection_close_error( + ngtcp2_conn *conn, ngtcp2_connection_close_error *ccerr) { + *ccerr = conn->rx.ccerr; } void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr) { @@ -11129,6 +13288,14 @@ int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn) { return conn->crypto.tls_error; } +void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert) { + conn->crypto.tls_alert = alert; +} + +uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn) { + return conn->crypto.tls_alert; +} + int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) { return conn_local_stream(conn, stream_id); } @@ -11152,6 +13319,71 @@ int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, return 0; } +void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + if (!(conn->cstat.pacing_rate > 0) || conn->tx.pacing.pktlen == 0) { + return; + } + + conn->tx.pacing.next_ts = + ts + (ngtcp2_duration)((double)conn->tx.pacing.pktlen / + conn->cstat.pacing_rate); + conn->tx.pacing.pktlen = 0; +} + +size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn) { + return conn->cstat.send_quantum; +} + +int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + size_t i; + + if (conn->dcid.retire_unacked.len >= + sizeof(conn->dcid.retire_unacked.seqs) / + sizeof(conn->dcid.retire_unacked.seqs[0])) { + return NGTCP2_ERR_CONNECTION_ID_LIMIT; + } + + /* Make sure that we do not have a duplicate */ + for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { + if (conn->dcid.retire_unacked.seqs[i] == seq) { + assert(0); + } + } + + conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len++] = seq; + + return 0; +} + +void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + size_t i; + + for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { + if (conn->dcid.retire_unacked.seqs[i] != seq) { + continue; + } + + if (i != conn->dcid.retire_unacked.len - 1) { + conn->dcid.retire_unacked.seqs[i] = + conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len - 1]; + } + + --conn->dcid.retire_unacked.len; + + return; + } +} + +size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) { + ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); + + if (strm == NULL) { + return 0; + } + + return strm->tx.loss_count; +} + void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, const ngtcp2_path *path, const uint8_t *data) { @@ -11159,16 +13391,24 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, memcpy(pcent->data, data, sizeof(pcent->data)); } -void ngtcp2_settings_default(ngtcp2_settings *settings) { +void ngtcp2_settings_default_versioned(int settings_version, + ngtcp2_settings *settings) { + (void)settings_version; + memset(settings, 0, sizeof(*settings)); settings->cc_algo = NGTCP2_CC_ALGO_CUBIC; settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; settings->ack_thresh = 2; + settings->max_udp_payload_size = 1500 - 48; + settings->handshake_timeout = NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT; } -void ngtcp2_transport_params_default(ngtcp2_transport_params *params) { +void ngtcp2_transport_params_default_versioned( + int transport_params_version, ngtcp2_transport_params *params) { + (void)transport_params_version; + memset(params, 0, sizeof(*params)); - params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; + params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; params->active_connection_id_limit = @@ -11181,9 +13421,10 @@ void ngtcp2_transport_params_default(ngtcp2_transport_params *params) { here. */ ngtcp2_ssize ngtcp2_pkt_write_connection_close( uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, - const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, - const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, - const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, const ngtcp2_crypto_cipher_ctx *hp_ctx) { ngtcp2_pkt_hd hd; ngtcp2_crypto_km ckm; @@ -11223,6 +13464,8 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; fr.connection_close.error_code = error_code; + fr.connection_close.reasonlen = reasonlen; + fr.connection_close.reason = (uint8_t *)reason; rv = ngtcp2_ppe_encode_frame(&ppe, &fr); if (rv != 0) { @@ -11234,3 +13477,26 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( } int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); } + +uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, + size_t preferred_versionslen, + const uint32_t *offered_versions, + size_t offered_versionslen) { + size_t i, j; + + if (!preferred_versionslen || !offered_versionslen) { + return 0; + } + + for (i = 0; i < preferred_versionslen; ++i) { + assert(ngtcp2_is_supported_version(preferred_versions[i])); + + for (j = 0; j < offered_versionslen; ++j) { + if (preferred_versions[i] == offered_versions[j]) { + return preferred_versions[i]; + } + } + } + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h index 825e4502e13e03..b1c6564175d482 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -36,14 +36,16 @@ #include "ngtcp2_acktr.h" #include "ngtcp2_rtb.h" #include "ngtcp2_strm.h" -#include "ngtcp2_mem.h" #include "ngtcp2_idtr.h" #include "ngtcp2_str.h" #include "ngtcp2_pkt.h" #include "ngtcp2_log.h" #include "ngtcp2_pq.h" #include "ngtcp2_cc.h" +#include "ngtcp2_bbr.h" +#include "ngtcp2_bbr2.h" #include "ngtcp2_pv.h" +#include "ngtcp2_pmtud.h" #include "ngtcp2_cid.h" #include "ngtcp2_buf.h" #include "ngtcp2_ppe.h" @@ -117,6 +119,21 @@ typedef enum { packets sent in NGTCP2_ECN_STATE_TESTING period. */ #define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10 +/* NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN is the maximum length + of reason phrase to remember. If the received reason phrase is + longer than this value, it is truncated. */ +#define NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN 1024 + +/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00u +/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other + than Initial packet should be padded. Initial packet might be + padded based on QUIC requirement regardless of this flag. */ +#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01u +/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come + and it should be encoded into the current packet. */ +#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02u + /* * ngtcp2_max_frame is defined so that it covers the largest ACK * frame. @@ -140,52 +157,69 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, const uint8_t *data); /* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_CONN_FLAG_NONE 0x00 -/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake - completed. */ -#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01 +#define NGTCP2_CONN_FLAG_NONE 0x00u +/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when TLS stack declares + that TLS handshake has completed. The condition of this + declaration varies between TLS implementations and this flag does + not indicate the completion of QUIC handshake. Some + implementations declare TLS handshake completion as server when + they write off Server Finished and before deriving application rx + secret. */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01u /* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is negotiated. This is only used for client. */ -#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02 +#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02u /* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport parameters are received. */ -#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04 -/* NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT is set when a protected packet - is received, and decrypted successfully. This flag is used to stop - retransmitting handshake packets. It might be replaced with an - another mechanism when we implement key update. */ -#define NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT 0x08 +#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04u +/* NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED is set when a + local transport parameters are applied. */ +#define NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED 0x08u /* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry packet. */ -#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10 +#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10u /* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is rejected by a peer. */ -#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20 +#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20u +/* NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED is set when the expired + keep-alive timer has been cancelled. */ +#define NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED 0x40u /* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint confirmed completion of handshake. */ -#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80 +#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80u /* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the library transitions its state to "post handshake". */ -#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100 +#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100u /* NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT is set when the early handshake retransmission has done when server receives overlapping Initial crypto data. */ -#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200 +#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200u +/* NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT indicates that the local endpoint + sends a QUIC packet without Fixed Bit set if a remote endpoint + supports Greasing QUIC Bit extension. */ +#define NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT 0x0400u /* 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 with new key. */ -#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800 +#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800u /* NGTCP2_CONN_FLAG_PPE_PENDING is set when NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state of ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */ -#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000 +#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000u /* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle timer should be restarted on next write. */ -#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000 +#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000u /* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as peer verified client address. This flag is only used by client. */ -#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000 +#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000u +/* NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED indicates that an early key is + installed. conn->early.ckm cannot be used for this purpose because + it might be discarded when a certain condition is met. */ +#define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000u +/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local + endpoint has initiated key update. */ +#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000u typedef struct ngtcp2_crypto_data { ngtcp2_buf buf; @@ -264,9 +298,9 @@ typedef struct ngtcp2_pktns { struct { /* ect0, ect1, ce are the ECN counts received in the latest ACK frame. */ - size_t ect0; - size_t ect1; - size_t ce; + uint64_t ect0; + uint64_t ect1; + uint64_t ce; } ack; } ecn; } rx; @@ -283,6 +317,8 @@ typedef struct ngtcp2_pktns { ngtcp2_crypto_km *ckm; /* hp_ctx is cipher context for packet header protection. */ ngtcp2_crypto_cipher_ctx hp_ctx; + /* data is the submitted crypto data. */ + ngtcp2_buf_chain *data; } tx; struct { @@ -308,16 +344,28 @@ typedef enum ngtcp2_ecn_state { NGTCP2_ECN_STATE_CAPABLE, } ngtcp2_ecn_state; +ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE, + sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(path_challenge, 4, + sizeof(ngtcp2_path_challenge_entry)); + +ngtcp2_objalloc_def(strm, ngtcp2_strm, oplent); + struct ngtcp2_conn { + ngtcp2_objalloc frc_objalloc; + ngtcp2_objalloc rtb_entry_objalloc; + ngtcp2_objalloc strm_objalloc; ngtcp2_conn_state state; ngtcp2_callbacks callbacks; /* rcid is a connection ID present in Initial or 0-RTT packet from client as destination connection ID. Server uses this field to check that duplicated Initial or 0-RTT packet are indeed sent to - this connection. It is also sent to client as - original_destination_connection_id transport parameter. Client - uses this field to validate original_destination_connection_id - transport parameter if no Retry packet is involved. */ + this connection. Client uses this field to validate + original_destination_connection_id transport parameter. */ ngtcp2_cid rcid; /* oscid is the source connection ID initially used by the local endpoint. */ @@ -335,21 +383,25 @@ struct ngtcp2_conn { ngtcp2_dcid current; /* bound is a set of destination connection IDs which are bound to particular paths. These paths are not validated yet. */ - ngtcp2_ringbuf bound; + ngtcp2_static_ringbuf_dcid_bound bound; /* unused is a set of unused CID received from peer. */ - ngtcp2_ringbuf unused; + ngtcp2_static_ringbuf_dcid_unused unused; /* 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; + ngtcp2_static_ringbuf_dcid_retired retired; /* seqgap tracks received sequence numbers in order to ignore retransmitted duplicated NEW_CONNECTION_ID frame. */ ngtcp2_gaptr seqgap; /* retire_prior_to is the largest 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; + struct { + /* seqs contains sequence number of Connection ID whose + retirement is not acknowledged by the remote endpoint yet. */ + uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2]; + /* len is the number of sequence numbers that seq contains. */ + size_t len; + } retire_unacked; /* zerolen_seq is a pseudo sequence number of zero-length Destination Connection ID in order to distinguish between them. */ @@ -374,6 +426,9 @@ struct ngtcp2_conn { struct { /* strmq contains ngtcp2_strm which has frames to send. */ ngtcp2_pq strmq; + /* strmq_nretrans is the number of entries in strmq which has + stream data to resent. */ + size_t strmq_nretrans; /* ack is ACK frame. The underlying buffer is reused. */ ngtcp2_frame *ack; /* max_ack_blks is the number of additional ngtcp2_ack_blk which @@ -399,6 +454,16 @@ struct ngtcp2_conn { validation period. */ size_t dgram_sent; } ecn; + + struct { + /* pktlen is the number of bytes written before calling + ngtcp2_conn_update_pkt_tx_time which resets this field to + 0. */ + size_t pktlen; + /* next_ts is the time to send next packet. It is UINT64_MAX if + packet pacing is disabled or expired.*/ + ngtcp2_tstamp next_ts; + } pacing; } tx; struct { @@ -415,9 +480,9 @@ struct ngtcp2_conn { /* window is the connection-level flow control window size. */ uint64_t window; /* path_challenge stores received PATH_CHALLENGE data. */ - ngtcp2_ringbuf path_challenge; - /* ccec is the received connection close error code. */ - ngtcp2_connection_close_error_code ccec; + ngtcp2_static_ringbuf_path_challenge path_challenge; + /* ccerr is the received connection close error. */ + ngtcp2_connection_close_error ccerr; } rx; struct { @@ -427,6 +492,21 @@ struct ngtcp2_conn { /* discard_started_ts is the timestamp when the timer to discard early key has started. Used by server only. */ ngtcp2_tstamp discard_started_ts; + /* transport_params is the values remembered by client from the + previous session. These are set by + ngtcp2_conn_set_early_remote_transport_params(). Server does + not use this field. Server must not set values for these + parameters that are smaller than the remembered values. */ + struct { + uint64_t initial_max_streams_bidi; + uint64_t initial_max_streams_uni; + uint64_t initial_max_stream_data_bidi_local; + uint64_t initial_max_stream_data_bidi_remote; + uint64_t initial_max_stream_data_uni; + uint64_t initial_max_data; + uint64_t active_connection_id_limit; + uint64_t max_datagram_frame_size; + } transport_params; } early; struct { @@ -456,11 +536,11 @@ struct ngtcp2_conn { struct { /* transport_params is the received transport parameters during handshake. It is used for Short packet only. */ - ngtcp2_transport_params transport_params; + ngtcp2_transport_params *transport_params; /* pending_transport_params is received transport parameters during handshake. It is copied to transport_params when 1RTT key is available. */ - ngtcp2_transport_params pending_transport_params; + ngtcp2_transport_params *pending_transport_params; struct { ngtcp2_idtr idtr; /* unsent_max_streams is the maximum number of streams of peer @@ -519,6 +599,8 @@ struct ngtcp2_conn { ngtcp2_crypto_aead_ctx retry_aead_ctx; /* tls_error is TLS related error. */ int tls_error; + /* tls_alert is TLS alert generated by the local endpoint. */ + uint8_t tls_alert; /* decryption_failure_count is the number of received packets that fail authentication. */ uint64_t decryption_failure_count; @@ -533,14 +615,63 @@ struct ngtcp2_conn { ngtcp2_frame_chain **pfrc; int pkt_empty; int hd_logged; - uint8_t rtb_entry_flags; - int was_client_initial; + /* flags is bitwise OR of zero or more of + NGTCP2_RTB_ENTRY_FLAG_*. */ + uint16_t rtb_entry_flags; ngtcp2_ssize hs_spktlen; + int require_padding; } pkt; + struct { + /* last_ts is a timestamp when a last packet is sent or received + on a current path. */ + ngtcp2_tstamp last_ts; + /* timeout is keep-alive timeout. When it expires, a packet + should be sent to a current path to keep connection alive. It + might be used to keep NAT binding intact. If 0 is set, + keep-alive timer is disabled. */ + ngtcp2_duration timeout; + } keep_alive; + + struct { + /* Initial keys for negotiated version. If original version == + negotiated version, these fields are not used. */ + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx hp_ctx; + } rx; + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx hp_ctx; + } tx; + /* version is QUIC version that the above Initial keys are created + for. */ + uint32_t version; + /* preferred_versions is the array of versions that are preferred + by the local endpoint. Server negotiates one of those versions + in this array if a client initially selects a less preferred + version. Client uses this field and original_version field to + prevent version downgrade attack if it reacted upon Version + Negotiation packet. */ + uint32_t *preferred_versions; + /* preferred_versionslen is the number of versions stored in the + array pointed by preferred_versions. This field is only used + by server. */ + size_t preferred_versionslen; + /* other_versions is the versions that the local endpoint sends in + version_information transport parameter. This is the wire + image of other_versions field of version_information transport + parameter. */ + uint8_t *other_versions; + /* other_versionslen is the length of data pointed by + other_versions field. */ + size_t other_versionslen; + } vneg; + ngtcp2_map strms; ngtcp2_conn_stat cstat; ngtcp2_pv *pv; + ngtcp2_pmtud *pmtud; ngtcp2_log log; ngtcp2_qlog qlog; ngtcp2_rst rst; @@ -550,9 +681,10 @@ struct ngtcp2_conn { /* idle_ts is the time instant when idle timer started. */ ngtcp2_tstamp idle_ts; void *user_data; - uint32_t version; + uint32_t client_chosen_version; + uint32_t negotiated_version; /* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */ - uint16_t flags; + uint32_t flags; int server; }; @@ -583,6 +715,8 @@ typedef struct ngtcp2_vmsg_datagram { const ngtcp2_vec *data; /* datacnt is the number of ngtcp2_vec pointed by data. */ size_t datacnt; + /* dgram_id is an opaque identifier chosen by an application. */ + uint64_t dgram_id; /* flags is bitwise OR of zero or more of NGTCP2_WRITE_DATAGRAM_FLAG_*. */ uint32_t flags; @@ -647,8 +781,7 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, * NGTCP2_ERR_CALLBACK_FAILURE * User-defined callback function failed. */ -int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code); +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm); /* * ngtcp2_conn_close_stream closes stream |strm| if no further @@ -664,8 +797,7 @@ int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, * NGTCP2_ERR_CALLBACK_FAILURE * User-defined callback function failed. */ -int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, - uint64_t app_error_code); +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm); /* * ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest @@ -673,12 +805,20 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, * ack_delay included in ACK frame. |ack_delay| is actually tainted * (sent by peer), so don't assume that |ack_delay| is always smaller * than, or equals to |rtt|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * RTT sample is ignored. */ -void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, - ngtcp2_duration ack_delay, ngtcp2_tstamp ts); +int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay, ngtcp2_tstamp ts); void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); +int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); + /* * ngtcp2_conn_detect_lost_pkt detects lost packets. * @@ -721,16 +861,17 @@ int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm); ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn); ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts); + int pkt_info_version, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts); /* - * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr| - * frame only in the buffer pointed by |dest| whose length if + * ngtcp2_conn_write_single_frame_pkt writes a packet which contains + * |fr| frame only in the buffer pointed by |dest| whose length if * |destlen|. |type| is a long packet type to send. If |type| is 0, * Short packet is used. |dcid| is used as a destination connection - * ID. + * ID. |flags| is zero or more of NGTCP2_WRITE_PKT_FLAG_*. Only + * NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING is recognized. * * The packet written by this function will not be retransmitted. * @@ -742,8 +883,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, */ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags, - const ngtcp2_path *path, ngtcp2_tstamp ts); + uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr, + uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts); /* * ngtcp2_conn_commit_local_transport_params commits the local @@ -819,4 +960,156 @@ void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, */ ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_get_idle_expiry` returns the time when a connection + * should be closed if it continues to be idle. If idle timeout is + * disabled, this function returns ``UINT64_MAX``. + */ +ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); + +ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns); + +/* + * ngtcp2_conn_track_retired_dcid_seq tracks the sequence number |seq| + * of unacknowledged retiring Destination Connection ID. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONNECTION_ID_LIMIT + * The number of unacknowledged retirement exceeds the limit. + */ +int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_untrack_retired_dcid_seq deletes the sequence number + * |seq| of unacknowledged retiring Destination Connection ID. It is + * fine if such sequence number is not found. + */ +void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_server_negotiate_version negotiates QUIC version. It + * is compatible version negotiation. It returns the negotiated QUIC + * version. This function must not be called by client. + */ +uint32_t +ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn, + const ngtcp2_version_info *version_info); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close_pkt` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_application_close_pkt` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * If handshake has not been confirmed yet, CONNECTION_CLOSE (type + * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written + * instead. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +ngtcp2_ssize ngtcp2_conn_write_application_close_pkt( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t app_error_code, const uint8_t *reason, + size_t reasonlen, ngtcp2_tstamp ts); + +int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn); + +void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_remote_transport_params` sets transport parameter + * |params| from a remote endpoint to |conn|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + * Failed to validate a remote transport parameters. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + * Version negotiation failure. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +int ngtcp2_conn_set_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params); + #endif /* NGTCP2_CONN_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c index 9064218dacb61d..dcf72e4d0ec980 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c @@ -29,6 +29,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_pkt.h" +#include "ngtcp2_net.h" uint64_t ngtcp2_get_uint64(const uint8_t *p) { uint64_t n; @@ -155,13 +156,13 @@ uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) { return rv; } -uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) { +uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n) { uint8_t *rv; - assert(n < 16384); + assert(n < 1073741824); - rv = ngtcp2_put_uint16be(p, n); - *p |= 0x40; + rv = ngtcp2_put_uint32be(p, n); + *p |= 0x80; return rv; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h index dcff0fdb8c174b..99746fdb4cefb7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h @@ -29,107 +29,8 @@ # include #endif /* HAVE_CONFIG_H */ -#ifdef HAVE_ARPA_INET_H -# include -#endif /* HAVE_ARPA_INET_H */ - -#ifdef HAVE_NETINET_IN_H -# include -#endif /* HAVE_NETINET_IN_H */ - -#ifdef HAVE_BYTESWAP_H -# include -#endif /* HAVE_BYTESWAP_H */ - -#ifdef HAVE_ENDIAN_H -# include -#endif /* HAVE_ENDIAN_H */ - -#ifdef HAVE_SYS_ENDIAN_H -# include -#endif /* HAVE_SYS_ENDIAN_H */ - #include -#if defined(HAVE_BSWAP_64) || \ - (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0) -# define ngtcp2_bswap64 bswap_64 -#else /* !HAVE_BSWAP_64 */ -# define ngtcp2_bswap64(N) \ - ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \ - ngtcp2_ntohl((uint32_t)((N) >> 32))) -#endif /* !HAVE_BSWAP_64 */ - -#if defined(HAVE_BE64TOH) || \ - (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0) -# define ngtcp2_ntohl64(N) be64toh(N) -# define ngtcp2_htonl64(N) htobe64(N) -#else /* !HAVE_BE64TOH */ -# if defined(WORDS_BIGENDIAN) -# define ngtcp2_ntohl64(N) (N) -# define ngtcp2_htonl64(N) (N) -# else /* !WORDS_BIGENDIAN */ -# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) -# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) -# endif /* !WORDS_BIGENDIAN */ -#endif /* !HAVE_BE64TOH */ - -#if defined(WIN32) -/* Windows requires ws2_32 library for ntonl family functions. We - define inline functions for those function so that we don't have - dependeny on that lib. */ - -# ifdef _MSC_VER -# define STIN static __inline -# else -# define STIN static inline -# endif - -STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; - *p++ = (hostlong >> 16) & 0xffu; - *p++ = (hostlong >> 8) & 0xffu; - *p = hostlong & 0xffu; - return res; -} - -STIN uint16_t ngtcp2_htons(uint16_t hostshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; - *p = hostshort & 0xffu; - return res; -} - -STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&netlong; - res = *p++ << 24; - res += *p++ << 16; - res += *p++ << 8; - res += *p; - return res; -} - -STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&netshort; - res = *p++ << 8; - res += *p; - return res; -} - -#else /* !WIN32 */ - -# define ngtcp2_htonl htonl -# define ngtcp2_htons htons -# define ngtcp2_ntohl ntohl -# define ngtcp2_ntohs ntohs - -#endif /* !WIN32 */ - /* * ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned * integer encoded as network byte order, and returns it in host byte @@ -220,12 +121,12 @@ uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n); uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n); /* - * ngtcp2_put_varint14 writes |n| in |p| using variable-length integer - * encoding. |n| must be strictly less than 16384. The function - * always encodes |n| in 2 bytes. It returns the one beyond of the + * ngtcp2_put_varint30 writes |n| in |p| using variable-length integer + * encoding. |n| must be strictly less than 1073741824. The function + * always encodes |n| in 4 bytes. It returns the one beyond of the * last written position. */ -uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n); +uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n); /* * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c index e11287fd4ad8dd..f7592f885b4cb2 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c @@ -30,6 +30,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_conv.h" #include "ngtcp2_conn.h" +#include "ngtcp2_net.h" int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, size_t secretlen, @@ -91,6 +92,8 @@ void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, size_t i; uint64_t n; + assert(ivlen >= 8); + memcpy(dest, iv, ivlen); n = ngtcp2_htonl64((uint64_t)pkt_num); @@ -146,14 +149,17 @@ static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id, return p; } -ngtcp2_ssize -ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, - ngtcp2_transport_params_type exttype, - const ngtcp2_transport_params *params) { +static const uint8_t empty_address[16]; + +ngtcp2_ssize ngtcp2_encode_transport_params_versioned( + uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype, + int transport_params_version, const ngtcp2_transport_params *params) { uint8_t *p; size_t len = 0; /* For some reason, gcc 7.3.0 requires this initialization. */ size_t preferred_addrlen = 0; + size_t version_infolen = 0; + (void)transport_params_version; switch (exttype) { case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO: @@ -218,7 +224,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, params->initial_max_streams_uni); } - if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) { + if (params->max_udp_payload_size != + NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) { len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); } @@ -249,6 +256,20 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, params->max_datagram_frame_size); } + if (params->grease_quic_bit) { + len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) + + ngtcp2_put_varint_len(0); + } + if (params->version_info_present) { + version_infolen = sizeof(uint32_t) + params->version_info.other_versionslen; + len += ngtcp2_put_varint_len( + NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) + + ngtcp2_put_varint_len(version_infolen) + version_infolen; + } + + if (dest == NULL && destlen == 0) { + return (ngtcp2_ssize)len; + } if (destlen < len) { return NGTCP2_ERR_NOBUF; @@ -271,13 +292,25 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS); p = ngtcp2_put_varint(p, preferred_addrlen); - p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr, - sizeof(params->preferred_address.ipv4_addr)); - p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port); + if (params->preferred_address.ipv4_present) { + p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr, + sizeof(params->preferred_address.ipv4_addr)); + p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port); + } else { + p = ngtcp2_cpymem(p, empty_address, + sizeof(params->preferred_address.ipv4_addr)); + p = ngtcp2_put_uint16be(p, 0); + } - p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr, - sizeof(params->preferred_address.ipv6_addr)); - p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port); + if (params->preferred_address.ipv6_present) { + p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr, + sizeof(params->preferred_address.ipv6_addr)); + p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port); + } else { + p = ngtcp2_cpymem(p, empty_address, + sizeof(params->preferred_address.ipv6_addr)); + p = ngtcp2_put_uint16be(p, 0); + } *p++ = (uint8_t)params->preferred_address.cid.datalen; if (params->preferred_address.cid.datalen) { @@ -330,7 +363,8 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, params->initial_max_streams_uni); } - if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) { + if (params->max_udp_payload_size != + NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) { p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size); } @@ -367,6 +401,21 @@ ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, params->max_datagram_frame_size); } + if (params->grease_quic_bit) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT); + p = ngtcp2_put_varint(p, 0); + } + + if (params->version_info_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT); + p = ngtcp2_put_varint(p, version_infolen); + p = ngtcp2_put_uint32be(p, params->version_info.chosen_version); + if (params->version_info.other_versionslen) { + p = ngtcp2_cpymem(p, params->version_info.other_versions, + params->version_info.other_versionslen); + } + } + assert((size_t)(p - dest) == len); return (ngtcp2_ssize)len; @@ -467,9 +516,9 @@ static ngtcp2_ssize decode_cid_param(ngtcp2_cid *pdest, const uint8_t *p, return (ngtcp2_ssize)(p - begin); } -int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, - ngtcp2_transport_params_type exttype, - const uint8_t *data, size_t datalen) { +int ngtcp2_decode_transport_params_versioned( + int transport_params_version, ngtcp2_transport_params *params, + ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen) { const uint8_t *p, *end; size_t len; uint64_t param_type; @@ -477,9 +526,12 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, ngtcp2_ssize nread; int initial_scid_present = 0; int original_dcid_present = 0; + size_t i; + (void)transport_params_version; - p = data; - end = data + datalen; + if (datalen == 0) { + return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; + } /* Set default values */ memset(params, 0, sizeof(*params)); @@ -488,7 +540,7 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, params->initial_max_stream_data_bidi_local = 0; params->initial_max_stream_data_bidi_remote = 0; params->initial_max_stream_data_uni = 0; - params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; + params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; params->stateless_reset_token_present = 0; params->preferred_address_present = 0; @@ -502,10 +554,10 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, memset(¶ms->retry_scid, 0, sizeof(params->retry_scid)); memset(¶ms->initial_scid, 0, sizeof(params->initial_scid)); memset(¶ms->original_dcid, 0, sizeof(params->original_dcid)); + params->version_info_present = 0; - if (datalen == 0) { - return 0; - } + p = data; + end = data + datalen; for (; (size_t)(end - p) >= 2;) { nread = decode_varint(¶m_type, p, end); @@ -634,12 +686,24 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, params->preferred_address.ipv4_port = ngtcp2_get_uint16(p); p += sizeof(uint16_t); + if (params->preferred_address.ipv4_port || + memcmp(empty_address, params->preferred_address.ipv4_addr, + sizeof(params->preferred_address.ipv4_addr)) != 0) { + params->preferred_address.ipv4_present = 1; + } + memcpy(params->preferred_address.ipv6_addr, p, sizeof(params->preferred_address.ipv6_addr)); p += sizeof(params->preferred_address.ipv6_addr); params->preferred_address.ipv6_port = ngtcp2_get_uint16(p); p += sizeof(uint16_t); + if (params->preferred_address.ipv6_port || + memcmp(empty_address, params->preferred_address.ipv6_addr, + sizeof(params->preferred_address.ipv6_addr)) != 0) { + params->preferred_address.ipv6_present = 1; + } + /* cid */ params->preferred_address.cid.datalen = *p++; len += params->preferred_address.cid.datalen; @@ -720,6 +784,45 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, } p += nread; break; + case NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT: + nread = decode_varint(&valuelen, p, end); + if (nread < 0 || valuelen != 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + params->grease_quic_bit = 1; + break; + case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT: + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + params->version_info.chosen_version = ngtcp2_get_uint32(p); + if (params->version_info.chosen_version == 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += sizeof(uint32_t); + if (valuelen > sizeof(uint32_t)) { + params->version_info.other_versions = (uint8_t *)p; + params->version_info.other_versionslen = + (size_t)valuelen - sizeof(uint32_t); + + for (i = sizeof(uint32_t); i < valuelen; + i += sizeof(uint32_t), p += sizeof(uint32_t)) { + if (ngtcp2_get_uint32(p) == 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + } + } + params->version_info_present = 1; + break; default: /* Ignore unknown parameter */ nread = decode_varint(&valuelen, p, end); @@ -747,3 +850,76 @@ int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, return 0; } + +static int transport_params_copy_new(ngtcp2_transport_params **pdest, + const ngtcp2_transport_params *src, + const ngtcp2_mem *mem) { + size_t len = sizeof(**pdest); + ngtcp2_transport_params *dest; + uint8_t *p; + + if (src->version_info_present) { + len += src->version_info.other_versionslen; + } + + dest = ngtcp2_mem_malloc(mem, len); + if (dest == NULL) { + return NGTCP2_ERR_NOMEM; + } + + *dest = *src; + + if (src->version_info_present && src->version_info.other_versionslen) { + p = (uint8_t *)dest + sizeof(*dest); + memcpy(p, src->version_info.other_versions, + src->version_info.other_versionslen); + dest->version_info.other_versions = p; + } + + *pdest = dest; + + return 0; +} + +int ngtcp2_decode_transport_params_new(ngtcp2_transport_params **pparams, + ngtcp2_transport_params_type exttype, + const uint8_t *data, size_t datalen, + const ngtcp2_mem *mem) { + int rv; + ngtcp2_transport_params params; + + rv = ngtcp2_decode_transport_params(¶ms, exttype, data, datalen); + if (rv < 0) { + return rv; + } + + if (mem == NULL) { + mem = ngtcp2_mem_default(); + } + + return transport_params_copy_new(pparams, ¶ms, mem); +} + +void ngtcp2_transport_params_del(ngtcp2_transport_params *params, + const ngtcp2_mem *mem) { + if (params == NULL) { + return; + } + + if (mem == NULL) { + mem = ngtcp2_mem_default(); + } + + ngtcp2_mem_free(mem, params); +} + +int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest, + const ngtcp2_transport_params *src, + const ngtcp2_mem *mem) { + if (src == NULL) { + *pdest = NULL; + return 0; + } + + return transport_params_copy_new(pdest, src, mem); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h index 6e6f12a0956ade..9a9d95f5b9fe82 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h @@ -41,11 +41,38 @@ /* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */ #define NGTCP2_MAX_AEAD_OVERHEAD 16 +/* ngtcp2_transport_param_id is the registry of QUIC transport + parameter ID. */ +typedef enum ngtcp2_transport_param_id { + NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000, + NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, + NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, + NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009, + NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a, + NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b, + NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c, + NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d, + NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f, + NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010, + /* https://datatracker.ietf.org/doc/html/rfc9221 */ + NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020, + NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2, + /* https://quicwg.org/quic-v2/draft-ietf-quic-v2.html */ + NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT = 0xff73db, +} ngtcp2_transport_param_id; + /* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00 +#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00u /* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is set. */ -#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01 +#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01u typedef struct ngtcp2_crypto_km { ngtcp2_vec secret; @@ -100,4 +127,21 @@ typedef struct ngtcp2_crypto_cc { void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, int64_t pkt_num); +/* + * ngtcp2_transport_params_copy_new makes a copy of |src|, and assigns + * it to |*pdest|. If |src| is NULL, NULL is assigned to |*pdest|. + * + * Caller is responsible to call ngtcp2_transport_params_del to free + * the memory assigned to |*pdest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest, + const ngtcp2_transport_params *src, + const ngtcp2_mem *mem); + #endif /* NGTCP2_CRYPTO_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c index bd15e0988be9d8..8f676da3ef0a13 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c @@ -64,8 +64,8 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_MALFORMED_TRANSPORT_PARAM"; case NGTCP2_ERR_FRAME_ENCODING: return "ERR_FRAME_ENCODING"; - case NGTCP2_ERR_TLS_DECRYPT: - return "ERR_TLS_DECRYPT"; + case NGTCP2_ERR_DECRYPT: + return "ERR_DECRYPT"; case NGTCP2_ERR_STREAM_SHUT_WR: return "ERR_STREAM_SHUT_WR"; case NGTCP2_ERR_STREAM_NOT_FOUND: @@ -82,8 +82,6 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_TRANSPORT_PARAM"; case NGTCP2_ERR_DISCARD_PKT: return "ERR_DISCARD_PKT"; - case NGTCP2_ERR_PATH_VALIDATION_FAILED: - return "ERR_PATH_VALIDATION_FAILED"; case NGTCP2_ERR_CONN_ID_BLOCKED: return "ERR_CONN_ID_BLOCKED"; case NGTCP2_ERR_CALLBACK_FAILURE: @@ -102,6 +100,14 @@ const char *ngtcp2_strerror(int liberr) { return "ERR_AEAD_LIMIT_REACHED"; case NGTCP2_ERR_NO_VIABLE_PATH: return "ERR_NO_VIABLE_PATH"; + case NGTCP2_ERR_VERSION_NEGOTIATION: + return "ERR_VERSION_NEGOTIATION"; + case NGTCP2_ERR_HANDSHAKE_TIMEOUT: + return "ERR_HANDSHAKE_TIMEOUT"; + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + return "ERR_VERSION_NEGOTIATION_FAILURE"; + case NGTCP2_ERR_IDLE_CLOSE: + return "ERR_IDLE_CLOSE"; default: return "(unknown)"; } @@ -129,6 +135,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { case NGTCP2_ERR_TRANSPORT_PARAM: return NGTCP2_TRANSPORT_PARAMETER_ERROR; case NGTCP2_ERR_INVALID_ARGUMENT: + case NGTCP2_ERR_NOMEM: + case NGTCP2_ERR_CALLBACK_FAILURE: return NGTCP2_INTERNAL_ERROR; case NGTCP2_ERR_STREAM_STATE: return NGTCP2_STREAM_STATE_ERROR; @@ -138,6 +146,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { return NGTCP2_AEAD_LIMIT_REACHED; case NGTCP2_ERR_NO_VIABLE_PATH: return NGTCP2_NO_VIABLE_PATH; + case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + return NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT; default: return NGTCP2_PROTOCOL_VIOLATION; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c index 6e7f3b7e554826..87c23898e8207d 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c @@ -23,29 +23,26 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ngtcp2_gaptr.h" -#include "ngtcp2_range.h" #include #include -int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { - int rv; - ngtcp2_range range = {0, UINT64_MAX}; +void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { + ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); - rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - return rv; - } + gaptr->mem = mem; +} + +static int gaptr_gap_init(ngtcp2_gaptr *gaptr) { + ngtcp2_range range = {0, UINT64_MAX}; + int rv; rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL); if (rv != 0) { - ngtcp2_ksl_free(&gaptr->gap); return rv; } - gaptr->mem = mem; - return 0; } @@ -57,11 +54,18 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) { ngtcp2_ksl_free(&gaptr->gap); } -int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { +int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) { int rv; ngtcp2_range k, m, l, r, q = {offset, offset + datalen}; ngtcp2_ksl_it it; + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + rv = gaptr_gap_init(gaptr); + if (rv != 0) { + return rv; + } + } + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar); @@ -73,7 +77,7 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { } if (ngtcp2_range_eq(&k, &m)) { - ngtcp2_ksl_remove(&gaptr->gap, &it, &k); + ngtcp2_ksl_remove_hint(&gaptr->gap, &it, &it, &k); continue; } ngtcp2_range_cut(&l, &r, &k, &m); @@ -95,35 +99,69 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { } uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); - ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + ngtcp2_ksl_it it; + ngtcp2_range r; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = ngtcp2_ksl_begin(&gaptr->gap); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + return r.begin; } -ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, - uint64_t offset) { +ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset) { ngtcp2_range q = {offset, offset + 1}; - return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, - ngtcp2_ksl_range_exclusive_compar); + ngtcp2_ksl_it it; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + ngtcp2_range r = {0, UINT64_MAX}; + return r; + } + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, + ngtcp2_ksl_range_exclusive_compar); + + assert(!ngtcp2_ksl_it_end(&it)); + + return *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); } int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, - size_t datalen) { + uint64_t datalen) { ngtcp2_range q = {offset, offset + datalen}; - ngtcp2_ksl_it it = ngtcp2_ksl_lower_bound_compar( - &gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar); - ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - ngtcp2_range m = ngtcp2_range_intersect(&q, &k); + ngtcp2_ksl_it it; + ngtcp2_range k; + ngtcp2_range m; + + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return 0; + } + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, + ngtcp2_ksl_range_exclusive_compar); + k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + m = ngtcp2_range_intersect(&q, &k); + return ngtcp2_range_len(&m) == 0; } void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) { - ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); + ngtcp2_ksl_it it; ngtcp2_range r; + if (ngtcp2_ksl_len(&gaptr->gap) == 0) { + return; + } + + it = ngtcp2_ksl_begin(&gaptr->gap); + assert(!ngtcp2_ksl_it_end(&it)); r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); - ngtcp2_ksl_remove(&gaptr->gap, NULL, &r); + ngtcp2_ksl_remove_hint(&gaptr->gap, NULL, &it, &r); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h index 500d376008ac29..0f100a81c4286c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h @@ -33,6 +33,7 @@ #include "ngtcp2_mem.h" #include "ngtcp2_ksl.h" +#include "ngtcp2_range.h" /* * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX). @@ -47,14 +48,8 @@ typedef struct ngtcp2_gaptr { /* * ngtcp2_gaptr_init initializes |gaptr|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); +void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); /* * ngtcp2_gaptr_free frees resources allocated for |gaptr|. @@ -71,7 +66,7 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr); * NGTCP2_ERR_NOMEM * Out of memory */ -int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen); +int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen); /* * ngtcp2_gaptr_first_gap_offset returns the offset to the first gap. @@ -80,18 +75,18 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen); uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr); /* - * ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to - * the first gap which overlaps or comes after |offset|. + * ngtcp2_gaptr_get_first_gap_after returns the first gap which + * overlaps or comes after |offset|. */ -ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, - uint64_t offset); +ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset); /* * ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset + * datalen) is completely pushed into this object. */ int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, - size_t datalen); + uint64_t datalen); /* * ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c index f04806b4a8b22f..d9880227690faf 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c @@ -26,17 +26,10 @@ #include -int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { - int rv; - - rv = ngtcp2_gaptr_init(&idtr->gap, mem); - if (rv != 0) { - return rv; - } +void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { + ngtcp2_gaptr_init(&idtr->gap, mem); idtr->server = server; - - return 0; } void ngtcp2_idtr_free(ngtcp2_idtr *idtr) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h index 1be64dc16e7ae7..edb8c68c8db9b5 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h @@ -51,14 +51,8 @@ typedef struct ngtcp2_idtr { * * If this object records server initiated ID (even number), set * |server| to nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); +void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); /* * ngtcp2_idtr_free frees resources allocated for |idtr|. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c index fd25e3514e827b..0bd424cb0bc1f1 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c @@ -33,9 +33,11 @@ #include "ngtcp2_mem.h" #include "ngtcp2_range.h" +static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; + static size_t ksl_nodelen(size_t keylen) { - return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & - (size_t)~0xf; + return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) & + ~(uintptr_t)0xfu; } static size_t ksl_blklen(size_t nodelen) { @@ -51,31 +53,48 @@ static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node, memcpy(node->key, key, ksl->keylen); } -int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, - const ngtcp2_mem *mem) { +void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem) { size_t nodelen = ksl_nodelen(keylen); - size_t blklen = ksl_blklen(nodelen); - ngtcp2_ksl_blk *head; - ksl->head = ngtcp2_mem_malloc(mem, blklen); - if (!ksl->head) { - return NGTCP2_ERR_NOMEM; - } - ksl->front = ksl->back = ksl->head; + ngtcp2_objalloc_init(&ksl->blkalloc, + ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8, + mem); + + ksl->head = NULL; + ksl->front = ksl->back = NULL; ksl->compar = compar; ksl->keylen = keylen; ksl->nodelen = nodelen; ksl->n = 0; - ksl->mem = mem; +} + +static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) { + return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc, + ksl_blklen(ksl->nodelen)); +} + +static void ksl_blk_objalloc_del(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { + ngtcp2_objalloc_ksl_blk_release(&ksl->blkalloc, blk); +} + +static int ksl_head_init(ngtcp2_ksl *ksl) { + ngtcp2_ksl_blk *head = ksl_blk_objalloc_new(ksl); + if (!head) { + return NGTCP2_ERR_NOMEM; + } - head = ksl->head; head->next = head->prev = NULL; head->n = 0; head->leaf = 1; + ksl->head = head; + ksl->front = ksl->back = head; + return 0; } +#ifdef NOMEMPOOL /* * ksl_free_blk frees |blk| recursively. */ @@ -88,15 +107,20 @@ static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { } } - ngtcp2_mem_free(ksl->mem, blk); + ksl_blk_objalloc_del(ksl, blk); } +#endif /* NOMEMPOOL */ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { - if (!ksl) { + if (!ksl || !ksl->head) { return; } +#ifdef NOMEMPOOL ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ + + ngtcp2_objalloc_free(&ksl->blkalloc); } /* @@ -110,7 +134,7 @@ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { ngtcp2_ksl_blk *rblk; - rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + rblk = ksl_blk_objalloc_new(ksl); if (rblk == NULL) { return NULL; } @@ -194,9 +218,9 @@ static int ksl_split_head(ngtcp2_ksl *ksl) { lblk = ksl->head; - nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + nhead = ksl_blk_objalloc_new(ksl); if (nhead == NULL) { - ngtcp2_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); return NGTCP2_ERR_NOMEM; } nhead->next = nhead->prev = NULL; @@ -240,29 +264,33 @@ static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i, static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) { - ngtcp2_ssize left = -1, right = (ngtcp2_ssize)blk->n, mid; + size_t i; ngtcp2_ksl_node *node; - while (right - left > 1) { - mid = (left + right) >> 1; - node = ngtcp2_ksl_nth_node(ksl, blk, (size_t)mid); - if (compar((ngtcp2_ksl_key *)node->key, key)) { - left = mid; - } else { - right = mid; - } - } + for (i = 0, node = (ngtcp2_ksl_node *)(void *)blk->nodes; + i < blk->n && compar((ngtcp2_ksl_key *)node->key, key); + ++i, node = (ngtcp2_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) + ; - return (size_t)right; + return i; } int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, const ngtcp2_ksl_key *key, void *data) { - ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_blk *blk; ngtcp2_ksl_node *node; size_t i; int rv; + if (!ksl->head) { + rv = ksl_head_init(ksl); + if (rv != 0) { + return rv; + } + } + + blk = ksl->head; + if (blk->n == NGTCP2_KSL_MAX_NBLK) { rv = ksl_split_head(ksl); if (rv != 0) { @@ -374,10 +402,10 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, ksl->back = lblk; } - ngtcp2_mem_free(ksl->mem, rblk); + ksl_blk_objalloc_del(ksl, rblk); if (ksl->head == blk && blk->n == 2) { - ngtcp2_mem_free(ksl->mem, ksl->head); + ksl_blk_objalloc_del(ksl, ksl->head); ksl->head = lblk; } else { ksl_remove_node(ksl, blk, i + 1); @@ -469,12 +497,42 @@ static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs, return !compar(lhs, rhs) && !compar(rhs, lhs); } +int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_it *hint, + const ngtcp2_ksl_key *key) { + ngtcp2_ksl_blk *blk = hint->blk; + + assert(ksl->head); + + if (blk->n <= NGTCP2_KSL_MIN_NBLK) { + return ngtcp2_ksl_remove(ksl, it, key); + } + + ksl_remove_node(ksl, blk, hint->i); + + --ksl->n; + + if (it) { + if (hint->i == blk->n && blk->next) { + ngtcp2_ksl_it_init(it, ksl, blk->next, 0); + } else { + ngtcp2_ksl_it_init(it, ksl, blk, hint->i); + } + } + + return 0; +} + int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, const ngtcp2_ksl_key *key) { ngtcp2_ksl_blk *blk = ksl->head; ngtcp2_ksl_node *node; size_t i; + if (!ksl->head) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + if (!blk->leaf && blk->n == 2 && ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK && ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) { @@ -550,6 +608,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, ngtcp2_ksl_it it; size_t i; + if (!blk) { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, ksl->compar); @@ -587,6 +650,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, ngtcp2_ksl_it it; size_t i; + if (!blk) { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + return it; + } + for (;;) { i = ksl_bsearch(ksl, blk, key, compar); @@ -623,6 +691,8 @@ void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, ngtcp2_ksl_node *node; size_t i; + assert(ksl->head); + for (;;) { i = ksl_bsearch(ksl, blk, old_key, ksl->compar); @@ -667,36 +737,49 @@ static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) { size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; } void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) { - size_t i; - ngtcp2_ksl_blk *head; - - if (!ksl->head->leaf) { - for (i = 0; i < ksl->head->n; ++i) { - ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, ksl->head, i)->blk); - } + if (!ksl->head) { + return; } - ksl->front = ksl->back = ksl->head; - ksl->n = 0; +#ifdef NOMEMPOOL + ksl_free_blk(ksl, ksl->head); +#endif /* NOMEMPOOL */ - head = ksl->head; + ksl->front = ksl->back = ksl->head = NULL; + ksl->n = 0; - head->next = head->prev = NULL; - head->n = 0; - head->leaf = 1; + ngtcp2_objalloc_clear(&ksl->blkalloc); } -void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } +void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { + if (!ksl->head) { + return; + } + + ksl_print(ksl, ksl->head, 0); +} ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) { ngtcp2_ksl_it it; - ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); + + if (ksl->head) { + ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); + } else { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) { ngtcp2_ksl_it it; - ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + + if (ksl->head) { + ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + } else { + ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); + } + return it; } @@ -707,11 +790,6 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, it->i = i; } -void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) { - assert(it->i < it->blk->n); - return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data; -} - void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) { assert(!ngtcp2_ksl_it_begin(it)); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h index f24487d03e9524..312a151d4aa9ec 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h @@ -33,6 +33,8 @@ #include +#include "ngtcp2_objalloc.h" + /* * Skip List using single key instead of range. */ @@ -79,25 +81,33 @@ struct ngtcp2_ksl_node { * ngtcp2_ksl_blk contains ngtcp2_ksl_node objects. */ struct ngtcp2_ksl_blk { - /* next points to the next block if leaf field is nonzero. */ - ngtcp2_ksl_blk *next; - /* prev points to the previous block if leaf field is nonzero. */ - ngtcp2_ksl_blk *prev; - /* n is the number of nodes this object contains in nodes. */ - uint32_t n; - /* leaf is nonzero if this block contains leaf nodes. */ - uint32_t leaf; union { - uint64_t align; - /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK - ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is - allocated along with the additional variable length key - storage, the size of buffer is unknown until ngtcp2_ksl_init is - called. */ - uint8_t nodes[1]; + struct { + /* next points to the next block if leaf field is nonzero. */ + ngtcp2_ksl_blk *next; + /* prev points to the previous block if leaf field is nonzero. */ + ngtcp2_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + uint32_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + uint32_t leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK + ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is + allocated along with the additional variable length key + storage, the size of buffer is unknown until ngtcp2_ksl_init is + called. */ + uint8_t nodes[1]; + }; + }; + + ngtcp2_opl_entry oplent; }; }; +ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent); + /* * ngtcp2_ksl_compar is a function type which returns nonzero if key * |lhs| should be placed before |rhs|. It returns 0 otherwise. @@ -122,6 +132,7 @@ struct ngtcp2_ksl_it { * ngtcp2_ksl is a deterministic paged skip list. */ struct ngtcp2_ksl { + ngtcp2_objalloc blkalloc; /* head points to the root block. */ ngtcp2_ksl_blk *head; /* front points to the first leaf block. */ @@ -135,21 +146,14 @@ struct ngtcp2_ksl { /* nodelen is the actual size of ngtcp2_ksl_node including key storage. */ size_t nodelen; - const ngtcp2_mem *mem; }; /* * ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare * function. |keylen| is the length of key. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. */ -int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, - const ngtcp2_mem *mem); +void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem); /* * ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is @@ -191,6 +195,17 @@ int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, const ngtcp2_ksl_key *key); +/* + * ngtcp2_ksl_remove_hint removes the |key| from |ksl|. |hint| must + * point to the same node denoted by |key|. |hint| is used to remove + * a node efficiently in some cases. Other than that, it behaves + * exactly like ngtcp2_ksl_remove. |it| and |hint| can point to the + * same object. + */ +int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_it *hint, + const ngtcp2_ksl_key *key); + /* * ngtcp2_ksl_lower_bound returns the iterator which points to the * first node which has the key which is equal to |key| or the last @@ -266,7 +281,8 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, * |it| points to. It is undefined to call this function when * ngtcp2_ksl_it_end(it) returns nonzero. */ -void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it); +#define ngtcp2_ksl_it_get(IT) \ + ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data /* * ngtcp2_ksl_it_next advances the iterator by one. It is undefined diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c index 0259404d3e2c06..ee37ff3517b2bc 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c @@ -34,6 +34,7 @@ #include "ngtcp2_str.h" #include "ngtcp2_vec.h" #include "ngtcp2_macro.h" +#include "ngtcp2_conv.h" void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, ngtcp2_printf log_printf, ngtcp2_tstamp ts, @@ -69,7 +70,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, * * # Frame event * - * () () + * () * * : * Flow direction. tx=transmission, rx=reception @@ -78,10 +79,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, * Packet number. * * : - * Packet name. (e.g., Initial, Handshake, S01) - * - * : - * Packet type in hex string. + * Packet name. (e.g., Initial, Handshake, 1RTT) * * : * Frame name. (e.g., STREAM, ACK, PING) @@ -94,16 +92,16 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, /* TODO Split second and remaining fraction with comma */ #define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s" -#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)" +#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s" #define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters" #define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \ - (DIR), hd->pkt_num, strpkttype(hd), hd->type + (DIR), hd->pkt_num, strpkttype(hd) #define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \ - (DIR), hd->pkt_num, strpkttype(hd), hd->type + (DIR), hd->pkt_num, strpkttype(hd) #define NGTCP2_LOG_TP_HD_FIELDS \ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry" @@ -140,6 +138,8 @@ static const char *strerrorcode(uint64_t error_code) { return "CRYPTO_BUFFER_EXCEEDED"; case NGTCP2_KEY_UPDATE_ERROR: return "KEY_UPDATE_ERROR"; + case NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT: + return "VERSION_NEGOTIATION_ERROR"; default: if (0x100u <= error_code && error_code <= 0x1ffu) { return "CRYPTO_ERROR"; @@ -155,8 +155,6 @@ static const char *strapperrorcode(uint64_t app_error_code) { static const char *strpkttype_long(uint8_t type) { switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: - return "VN"; case NGTCP2_PKT_INITIAL: return "Initial"; case NGTCP2_PKT_RETRY: @@ -174,7 +172,26 @@ static const char *strpkttype(const ngtcp2_pkt_hd *hd) { if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { return strpkttype_long(hd->type); } - return "Short"; + + switch (hd->type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + return "VN"; + case NGTCP2_PKT_STATELESS_RESET: + return "SR"; + case NGTCP2_PKT_1RTT: + return "1RTT"; + default: + return "(unknown)"; + } +} + +static const char *strpkttype_type_flags(uint8_t type, uint8_t flags) { + ngtcp2_pkt_hd hd = {0}; + + hd.type = type; + hd.flags = flags; + + return strpkttype(&hd); } static const char *strevent(ngtcp2_log_event ev) { @@ -201,13 +218,13 @@ static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; } static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, const ngtcp2_stream *fr, const char *dir) { - log->log_printf(log->user_data, - (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 - " fin=%d offset=%" PRIu64 " len=%zu uni=%d"), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, - fr->stream_id, fr->fin, fr->offset, - ngtcp2_vec_len(fr->data, fr->datacnt), - (fr->stream_id & 0x2) != 0); + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 " fin=%d offset=%" PRIu64 + " len=%" PRIu64 " uni=%d"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, fr->stream_id, + fr->fin, fr->offset, ngtcp2_vec_len(fr->data, fr->datacnt), + (fr->stream_id & 0x2) != 0); } static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, @@ -396,17 +413,11 @@ static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, const ngtcp2_crypto *fr, const char *dir) { - size_t datalen = 0; - size_t i; - - for (i = 0; i < fr->datacnt; ++i) { - datalen += fr->data[i].len; - } - log->log_printf( log->user_data, (NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64), - NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen); + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, + ngtcp2_vec_len(fr->data, fr->datacnt)); } static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, @@ -446,7 +457,8 @@ static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, static void log_fr_datagram(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, const ngtcp2_datagram *fr, const char *dir) { - log->log_printf(log->user_data, (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%zu"), + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%" PRIu64), NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, ngtcp2_vec_len(fr->data, fr->datacnt)); } @@ -570,6 +582,8 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) { memset(&shd, 0, sizeof(shd)); + shd.type = NGTCP2_PKT_STATELESS_RESET; + log->log_printf( log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"), NGTCP2_LOG_PKT_HD_FIELDS("rx"), @@ -583,6 +597,7 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1]; uint8_t addr[16 * 2 + 7 + 1]; uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1]; + size_t i; if (!log->log_printf) { return; @@ -694,6 +709,26 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64), NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " grease_quic_bit=%d"), + NGTCP2_LOG_TP_HD_FIELDS, params->grease_quic_bit); + + if (params->version_info_present) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " version_information.chosen_version=0x%08x"), + NGTCP2_LOG_TP_HD_FIELDS, params->version_info.chosen_version); + + assert(!(params->version_info.other_versionslen & 0x3)); + + for (i = 0; i < params->version_info.other_versionslen; + i += sizeof(uint32_t)) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " version_information.other_versions[%zu]=0x%08x"), + NGTCP2_LOG_TP_HD_FIELDS, i >> 2, + ngtcp2_get_uint32(¶ms->version_info.other_versions[i])); + } + } } void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, @@ -702,11 +737,9 @@ void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, return; } - ngtcp2_log_info( - log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num, - (flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short", - type, sent_ts); + ngtcp2_log_info(log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num, + strpkttype_type_flags(type, flags), sent_ts); } static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, @@ -718,15 +751,21 @@ static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, return; } - ngtcp2_log_info( - log, NGTCP2_LOG_EVENT_PKT, - "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d", - dir, hd->pkt_num, - (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), - (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), - (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type) - : "Short", - hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); + if (hd->type == NGTCP2_PKT_1RTT) { + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_PKT, "%s pkn=%" PRId64 " dcid=0x%s type=%s k=%d", + dir, hd->pkt_num, + (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), + strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); + } else { + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_PKT, + "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s version=0x%08x type=%s len=%zu", + dir, hd->pkt_num, + (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), + (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), + hd->version, strpkttype(hd), hd->len); + } } void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { @@ -762,6 +801,6 @@ void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT, - "cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num, - strpkttype(hd), hd->type); + "cancel tx pkn=%" PRId64 " type=%s", hd->pkt_num, + strpkttype(hd)); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h index bd1ac240a9398a..029ef1b757ab09 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h @@ -33,9 +33,7 @@ #include "ngtcp2_pkt.h" -typedef struct ngtcp2_log ngtcp2_log; - -struct ngtcp2_log { +typedef struct ngtcp2_log { /* log_printf is a sink to write log. NULL means no logging output. */ ngtcp2_printf log_printf; @@ -49,7 +47,44 @@ struct ngtcp2_log { void *user_data; /* scid is SCID encoded as NULL-terminated hex string. */ uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1]; -}; +} ngtcp2_log; + +/** + * @enum + * + * :type:`ngtcp2_log_event` defines an event of ngtcp2 library + * internal logger. + */ +typedef enum ngtcp2_log_event { + /** + * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event. + */ + NGTCP2_LOG_EVENT_NONE, + /** + * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event + */ + NGTCP2_LOG_EVENT_CON, + /** + * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event. + */ + NGTCP2_LOG_EVENT_PKT, + /** + * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event. + */ + NGTCP2_LOG_EVENT_FRM, + /** + * :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event. + */ + NGTCP2_LOG_EVENT_RCV, + /** + * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event. + */ + NGTCP2_LOG_EVENT_CRY, + /** + * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event. + */ + NGTCP2_LOG_EVENT_PTV, +} ngtcp2_log_event; void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, ngtcp2_printf log_printf, ngtcp2_tstamp ts, @@ -77,4 +112,12 @@ void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); +/** + * @function + * + * `ngtcp2_log_info` writes info level log. + */ +void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, + ...); + #endif /* NGTCP2_LOG_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c index 5d24961dc98afe..12bc6e84bd4f0c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c @@ -27,170 +27,166 @@ #include #include +#include #include "ngtcp2_conv.h" -#define INITIAL_TABLE_LENGTH 256 +#define NGTCP2_INITIAL_TABLE_LENBITS 4 -int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { +void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { map->mem = mem; - map->tablelen = INITIAL_TABLE_LENGTH; - map->table = ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_bucket)); - if (map->table == NULL) { - return NGTCP2_ERR_NOMEM; - } - + map->tablelen = 0; + map->tablelenbits = 0; + map->table = NULL; map->size = 0; - - return 0; } void ngtcp2_map_free(ngtcp2_map *map) { - size_t i; - ngtcp2_map_bucket *bkt; - if (!map) { return; } - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } - } - ngtcp2_mem_free(map->mem, map->table); } -void ngtcp2_map_each_free(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), +void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr), void *ptr) { uint32_t i; ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - func(bkt->ptr, ptr); - bkt->ptr = NULL; - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - func(ngtcp2_ksl_it_get(&it), ptr); - } - - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; - } + func(bkt->data, ptr); } } -int ngtcp2_map_each(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), +int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr), void *ptr) { int rv; uint32_t i; ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; + + if (map->size == 0) { + return 0; + } for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - if (bkt->ptr) { - rv = func(bkt->ptr, ptr); - if (rv != 0) { - return rv; - } - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - rv = func(ngtcp2_ksl_it_get(&it), ptr); - if (rv != 0) { - return rv; - } - } + rv = func(bkt->data, ptr); + if (rv != 0) { + return rv; } } + return 0; } -void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) { - entry->key = key; +static uint32_t hash(ngtcp2_map_key_type key) { + return (uint32_t)((key * 11400714819323198485llu) >> 32); } -/* FNV1a hash */ -static uint32_t hash(key_type key, uint32_t mod) { - uint8_t *p, *end; - uint32_t h = 0x811C9DC5u; +static size_t h2idx(uint32_t hash, uint32_t bits) { + return hash >> (32 - bits); +} - key = ngtcp2_htonl64(key); - p = (uint8_t *)&key; - end = p + sizeof(key_type); +static size_t distance(uint32_t tablelen, uint32_t tablelenbits, + ngtcp2_map_bucket *bkt, size_t idx) { + return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1); +} - for (; p != end;) { - h ^= *p++; - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } +static void map_bucket_swap(ngtcp2_map_bucket *bkt, uint32_t *phash, + ngtcp2_map_key_type *pkey, void **pdata) { + uint32_t h = bkt->hash; + ngtcp2_map_key_type key = bkt->key; + void *data = bkt->data; - return h & (mod - 1); + bkt->hash = *phash; + bkt->key = *pkey; + bkt->data = *pdata; + + *phash = h; + *pkey = key; + *pdata = data; } -static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { - return *(key_type *)lhs < *(key_type *)rhs; +static void map_bucket_set_data(ngtcp2_map_bucket *bkt, uint32_t hash, + ngtcp2_map_key_type key, void *data) { + bkt->hash = hash; + bkt->key = key; + bkt->data = data; } -static int map_insert(ngtcp2_map *map, ngtcp2_map_bucket *table, - uint32_t tablelen, ngtcp2_map_entry *entry) { - uint32_t h = hash(entry->key, tablelen); - ngtcp2_map_bucket *bkt = &table[h]; - const ngtcp2_mem *mem = map->mem; - int rv; +void ngtcp2_map_print_distance(ngtcp2_map *map) { + uint32_t i; + size_t idx; + ngtcp2_map_bucket *bkt; - if (bkt->ptr == NULL && (bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0)) { - bkt->ptr = entry; - return 0; - } + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; - if (!bkt->ksl) { - bkt->ksl = ngtcp2_mem_malloc(mem, sizeof(*bkt->ksl)); - if (bkt->ksl == NULL) { - return NGTCP2_ERR_NOMEM; + if (bkt->data == NULL) { + fprintf(stderr, "@%u \n", i); + continue; } - ngtcp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem); + + idx = h2idx(bkt->hash, map->tablelenbits); + fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i, + bkt->hash, bkt->key, idx, + distance(map->tablelen, map->tablelenbits, bkt, idx)); } +} - if (bkt->ptr) { - rv = ngtcp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); - if (rv != 0) { - return rv; +static int insert(ngtcp2_map_bucket *table, uint32_t tablelen, + uint32_t tablelenbits, uint32_t hash, ngtcp2_map_key_type key, + void *data) { + size_t idx = h2idx(hash, tablelenbits); + size_t d = 0, dd; + ngtcp2_map_bucket *bkt; + + for (;;) { + bkt = &table[idx]; + + if (bkt->data == NULL) { + map_bucket_set_data(bkt, hash, key, data); + return 0; } - bkt->ptr = NULL; - } + dd = distance(tablelen, tablelenbits, bkt, idx); + if (d > dd) { + map_bucket_swap(bkt, &hash, &key, &data); + d = dd; + } else if (bkt->key == key) { + /* TODO This check is just a waste after first swap or if this + function is called from map_resize. That said, there is no + difference with or without this conditional in performance + wise. */ + return NGTCP2_ERR_INVALID_ARGUMENT; + } - return ngtcp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry); + ++d; + idx = (idx + 1) & (tablelen - 1); + } } -/* new_tablelen must be power of 2 */ -static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) { +/* new_tablelen must be power of 2 and new_tablelen == (1 << + new_tablelenbits) must hold. */ +static int map_resize(ngtcp2_map *map, uint32_t new_tablelen, + uint32_t new_tablelenbits) { uint32_t i; ngtcp2_map_bucket *new_table; ngtcp2_map_bucket *bkt; - ngtcp2_ksl_it it; int rv; + (void)rv; new_table = ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_bucket)); @@ -200,64 +196,46 @@ static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) { for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; - - if (bkt->ptr) { - rv = map_insert(map, new_table, new_tablelen, bkt->ptr); - if (rv != 0) { - goto fail; - } - assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + if (bkt->data == NULL) { continue; } + rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key, + bkt->data); - if (bkt->ksl) { - for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); - ngtcp2_ksl_it_next(&it)) { - rv = map_insert(map, new_table, new_tablelen, ngtcp2_ksl_it_get(&it)); - if (rv != 0) { - goto fail; - } - } - } - } - - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } + assert(0 == rv); } ngtcp2_mem_free(map->mem, map->table); map->tablelen = new_tablelen; + map->tablelenbits = new_tablelenbits; map->table = new_table; return 0; - -fail: - for (i = 0; i < new_tablelen; ++i) { - bkt = &new_table[i]; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - } - } - - return rv; } -int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) { +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { int rv; + assert(data); + /* Load factor is 0.75 */ if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = map_resize(map, map->tablelen * 2); - if (rv != 0) { - return rv; + if (map->tablelen) { + rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); + if (rv != 0) { + return rv; + } + } else { + rv = map_resize(map, 1 << NGTCP2_INITIAL_TABLE_LENBITS, + NGTCP2_INITIAL_TABLE_LENBITS); + if (rv != 0) { + return rv; + } } } - rv = map_insert(map, map->table, map->tablelen, new_entry); + + rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key, + data); if (rv != 0) { return rv; } @@ -265,67 +243,93 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) { return 0; } -ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key) { - ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - ngtcp2_ksl_it it; +void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) { + uint32_t h; + size_t idx; + ngtcp2_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - return bkt->ptr; - } + if (map->size == 0) { return NULL; } - if (bkt->ksl) { - it = ngtcp2_ksl_lower_bound(bkt->ksl, &key); - if (ngtcp2_ksl_it_end(&it) || *(key_type *)ngtcp2_ksl_it_key(&it) != key) { + h = hash(key); + idx = h2idx(h, map->tablelenbits); + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { return NULL; } - return ngtcp2_ksl_it_get(&it); - } - return NULL; + if (bkt->key == key) { + return bkt->data; + } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } } -int ngtcp2_map_remove(ngtcp2_map *map, key_type key) { - ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; - int rv; +int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) { + uint32_t h; + size_t idx, didx; + ngtcp2_map_bucket *bkt; + size_t d = 0; - if (bkt->ptr) { - if (bkt->ptr->key == key) { - bkt->ptr = NULL; - --map->size; - return 0; - } + if (map->size == 0) { return NGTCP2_ERR_INVALID_ARGUMENT; } - if (bkt->ksl) { - rv = ngtcp2_ksl_remove(bkt->ksl, NULL, &key); - if (rv != 0) { - return rv; + h = hash(key); + idx = h2idx(h, map->tablelenbits); + + for (;;) { + bkt = &map->table[idx]; + + if (bkt->data == NULL || + d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { + return NGTCP2_ERR_INVALID_ARGUMENT; } - --map->size; - return 0; - } - return NGTCP2_ERR_INVALID_ARGUMENT; -} + if (bkt->key == key) { + map_bucket_set_data(bkt, 0, 0, NULL); -void ngtcp2_map_clear(ngtcp2_map *map) { - uint32_t i; - ngtcp2_map_bucket *bkt; + didx = idx; + idx = (idx + 1) & (map->tablelen - 1); - for (i = 0; i < map->tablelen; ++i) { - bkt = &map->table[i]; - bkt->ptr = NULL; - if (bkt->ksl) { - ngtcp2_ksl_free(bkt->ksl); - ngtcp2_mem_free(map->mem, bkt->ksl); - bkt->ksl = NULL; + for (;;) { + bkt = &map->table[idx]; + if (bkt->data == NULL || + distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) { + break; + } + + map->table[didx] = *bkt; + map_bucket_set_data(bkt, 0, 0, NULL); + didx = idx; + + idx = (idx + 1) & (map->tablelen - 1); + } + + --map->size; + + return 0; } + + ++d; + idx = (idx + 1) & (map->tablelen - 1); + } +} + +void ngtcp2_map_clear(ngtcp2_map *map) { + if (map->tablelen == 0) { + return; } + memset(map->table, 0, sizeof(*map->table) * map->tablelen); map->size = 0; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h index bbb2e705d69e6c..a64344a9a301a3 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h @@ -33,21 +33,15 @@ #include #include "ngtcp2_mem.h" -#include "ngtcp2_ksl.h" /* Implementation of unordered map */ -typedef uint64_t key_type; - -typedef struct ngtcp2_map_entry ngtcp2_map_entry; - -struct ngtcp2_map_entry { - key_type key; -}; +typedef uint64_t ngtcp2_map_key_type; typedef struct ngtcp2_map_bucket { - ngtcp2_map_entry *ptr; - ngtcp2_ksl *ksl; + uint32_t hash; + ngtcp2_map_key_type key; + void *data; } ngtcp2_map_bucket; typedef struct ngtcp2_map { @@ -55,18 +49,13 @@ typedef struct ngtcp2_map { const ngtcp2_mem *mem; size_t size; uint32_t tablelen; + uint32_t tablelenbits; } ngtcp2_map; /* * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory */ -int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); +void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); /* * Deallocates any resources allocated for |map|. The stored entries @@ -78,21 +67,14 @@ void ngtcp2_map_free(ngtcp2_map *map); /* * Deallocates each entries using |func| function and any resources * allocated for |map|. The |func| function is responsible for freeing - * given the |entry| object. The |ptr| will be passed to the |func| as + * given the |data| object. The |ptr| will be passed to the |func| as * send argument. The return value of the |func| will be ignored. */ -void ngtcp2_map_each_free(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), +void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr), void *ptr); /* - * Initializes the |entry| with the |key|. All entries to be inserted - * to the map must be initialized with this function. - */ -void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key); - -/* - * Inserts the new |entry| with the key |entry->key| to the map |map|. + * Inserts the new |data| with the |key| to the map |map|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -102,25 +84,25 @@ void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key); * NGTCP2_ERR_NOMEM * Out of memory */ -int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *entry); +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data); /* - * Returns the entry associated by the key |key|. If there is no such - * entry, this function returns NULL. + * Returns the data associated by the key |key|. If there is no such + * data, this function returns NULL. */ -ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key); +void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key); /* - * Removes the entry associated by the key |key| from the |map|. The - * removed entry is not freed by this function. + * Removes the data associated by the key |key| from the |map|. The + * removed data is not freed by this function. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGTCP2_ERR_INVALID_ARGUMENT - * The entry associated by |key| does not exist. + * The data associated by |key| does not exist. */ -int ngtcp2_map_remove(ngtcp2_map *map, key_type key); +int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key); /* * Removes all entries from |map|. @@ -133,20 +115,22 @@ void ngtcp2_map_clear(ngtcp2_map *map); size_t ngtcp2_map_size(ngtcp2_map *map); /* - * Applies the function |func| to each entry in the |map| with the + * Applies the function |func| to each data in the |map| with the * optional user supplied pointer |ptr|. * * If the |func| returns 0, this function calls the |func| with the - * next entry. If the |func| returns nonzero, it will not call the + * next data. If the |func| returns nonzero, it will not call the * |func| for further entries and return the return value of the * |func| immediately. Thus, this function returns 0 if all the * invocations of the |func| return 0, or nonzero value which the last * invocation of |func| returns. * - * Don't use this function to free each entry. Use + * Don't use this function to free each data. Use * ngtcp2_map_each_free() instead. */ -int ngtcp2_map_each(ngtcp2_map *map, - int (*func)(ngtcp2_map_entry *entry, void *ptr), void *ptr); +int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr), + void *ptr); + +void ngtcp2_map_print_distance(ngtcp2_map *map); #endif /* NGTCP2_MAP_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c index 2c036ad1634b05..bcce0b5cdfcf02 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c @@ -25,26 +25,28 @@ */ #include "ngtcp2_mem.h" -static void *default_malloc(size_t size, void *mem_user_data) { - (void)mem_user_data; +#include + +static void *default_malloc(size_t size, void *user_data) { + (void)user_data; return malloc(size); } -static void default_free(void *ptr, void *mem_user_data) { - (void)mem_user_data; +static void default_free(void *ptr, void *user_data) { + (void)user_data; free(ptr); } -static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_calloc(size_t nmemb, size_t size, void *user_data) { + (void)user_data; return calloc(nmemb, size); } -static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { - (void)mem_user_data; +static void *default_realloc(void *ptr, size_t size, void *user_data) { + (void)user_data; return realloc(ptr, size); } @@ -54,22 +56,58 @@ static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free, const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; } +#ifndef MEMDEBUG void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); + return mem->malloc(size, mem->user_data); } void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) { - free_func(ptr, mem_user_data); + mem->free(ptr, mem->user_data); } void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); + return mem->calloc(nmemb, size, mem->user_data); } void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); + return mem->realloc(ptr, size, mem->user_data); +} +#else /* MEMDEBUG */ +void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size, + const char *func, const char *file, size_t line) { + void *nptr = mem->malloc(size, mem->user_data); + + fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func, + file, line); + + return nptr; +} + +void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func, + const char *file, size_t line) { + fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + + mem->free(ptr, mem->user_data); +} + +void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size, + const char *func, const char *file, size_t line) { + void *nptr = mem->calloc(nmemb, size, mem->user_data); + + fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb, + size, func, file, line); + + return nptr; +} + +void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size, + const char *func, const char *file, + size_t line) { + void *nptr = mem->realloc(ptr, size, mem->user_data); + + fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr, + size, func, file, line); + + return nptr; } +#endif /* MEMDEBUG */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h index cdecf8763a5f36..c99b6c59726891 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h @@ -34,10 +34,39 @@ /* Convenient wrapper functions to call allocator function in |mem|. */ +#ifndef MEMDEBUG void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size); + void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr); -void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data); + void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size); + void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size); +#else /* MEMDEBUG */ +void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_malloc(MEM, SIZE) \ + ngtcp2_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__) + +void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func, + const char *file, size_t line); + +# define ngtcp2_mem_free(MEM, PTR) \ + ngtcp2_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__) + +void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_calloc(MEM, NMEMB, SIZE) \ + ngtcp2_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \ + __LINE__) + +void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size, + const char *func, const char *file, size_t line); + +# define ngtcp2_mem_realloc(MEM, PTR, SIZE) \ + ngtcp2_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, __LINE__) +#endif /* MEMDEBUG */ #endif /* NGTCP2_MEM_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h new file mode 100644 index 00000000000000..b1f28096174605 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_net.h @@ -0,0 +1,136 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 NGTCP2_NET_H +#define NGTCP2_NET_H + +/* This header file is explicitly allowed to be shared with + ngtcp2_crypto library. */ + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_BYTESWAP_H +# include +#endif /* HAVE_BYTESWAP_H */ + +#ifdef HAVE_ENDIAN_H +# include +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include +#endif /* HAVE_SYS_ENDIAN_H */ + +#include + +#if defined(HAVE_BSWAP_64) || \ + (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0) +# define ngtcp2_bswap64 bswap_64 +#else /* !HAVE_BSWAP_64 */ +# define ngtcp2_bswap64(N) \ + ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \ + ngtcp2_ntohl((uint32_t)((N) >> 32))) +#endif /* !HAVE_BSWAP_64 */ + +#if defined(HAVE_BE64TOH) || \ + (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0) +# define ngtcp2_ntohl64(N) be64toh(N) +# define ngtcp2_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# if defined(WORDS_BIGENDIAN) +# define ngtcp2_ntohl64(N) (N) +# define ngtcp2_htonl64(N) (N) +# else /* !WORDS_BIGENDIAN */ +# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) +# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) +# endif /* !WORDS_BIGENDIAN */ +#endif /* !HAVE_BE64TOH */ + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = (unsigned char)(hostlong >> 24); + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t ngtcp2_htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = (unsigned char)(hostshort >> 8); + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#else /* !WIN32 */ + +# define ngtcp2_htonl htonl +# define ngtcp2_htons htons +# define ngtcp2_ntohl ntohl +# define ngtcp2_ntohs ntohs + +#endif /* !WIN32 */ + +#endif /* NGTCP2_NET_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c new file mode 100644 index 00000000000000..8b06cdd5de3d1d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.c @@ -0,0 +1,40 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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. + */ +#include "ngtcp2_objalloc.h" + +void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen, + const ngtcp2_mem *mem) { + ngtcp2_balloc_init(&objalloc->balloc, blklen, mem); + ngtcp2_opl_init(&objalloc->opl); +} + +void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc) { + ngtcp2_balloc_free(&objalloc->balloc); +} + +void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc) { + ngtcp2_opl_clear(&objalloc->opl); + ngtcp2_balloc_clear(&objalloc->balloc); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h new file mode 100644 index 00000000000000..f1bbd3a5c9405c --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_objalloc.h @@ -0,0 +1,140 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 NGTCP2_OBJALLOC_H +#define NGTCP2_OBJALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_balloc.h" +#include "ngtcp2_opl.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" + +/* + * ngtcp2_objalloc combines ngtcp2_balloc and ngtcp2_opl, and provides + * an object pool with the custom allocator to reduce the allocation + * and deallocation overheads for small objects. + */ +typedef struct ngtcp2_objalloc { + ngtcp2_balloc balloc; + ngtcp2_opl opl; +} ngtcp2_objalloc; + +/* + * ngtcp2_objalloc_init initializes |objalloc|. |blklen| is directly + * passed to ngtcp2_balloc_init. + */ +void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_objalloc_free releases all allocated resources. + */ +void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc); + +/* + * ngtcp2_objalloc_clear releases all allocated resources and + * initializes its state. + */ +void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc); + +#ifndef NOMEMPOOL +# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void ngtcp2_objalloc_##NAME##_init( \ + ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \ + ngtcp2_objalloc_init( \ + objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_get( \ + ngtcp2_objalloc *objalloc) { \ + ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = \ + ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE)); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \ + ngtcp2_objalloc *objalloc, size_t len) { \ + ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \ + TYPE *obj; \ + int rv; \ + \ + if (!oplent) { \ + rv = ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, len); \ + if (rv != 0) { \ + return NULL; \ + } \ + \ + return obj; \ + } \ + \ + return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \ + } \ + \ + inline static void ngtcp2_objalloc_##NAME##_release( \ + ngtcp2_objalloc *objalloc, TYPE *obj) { \ + ngtcp2_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \ + } +#else /* NOMEMPOOL */ +# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \ + inline static void ngtcp2_objalloc_##NAME##_init( \ + ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \ + ngtcp2_objalloc_init( \ + objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_get( \ + ngtcp2_objalloc *objalloc) { \ + return ngtcp2_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \ + } \ + \ + inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \ + ngtcp2_objalloc *objalloc, size_t len) { \ + return ngtcp2_mem_malloc(objalloc->balloc.mem, len); \ + } \ + \ + inline static void ngtcp2_objalloc_##NAME##_release( \ + ngtcp2_objalloc *objalloc, TYPE *obj) { \ + ngtcp2_mem_free(objalloc->balloc.mem, obj); \ + } +#endif /* NOMEMPOOL */ + +#endif /* NGTCP2_OBJALLOC_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c new file mode 100644 index 00000000000000..a29361c6349f9d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.c @@ -0,0 +1,46 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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. + */ +#include "ngtcp2_opl.h" + +void ngtcp2_opl_init(ngtcp2_opl *opl) { opl->head = NULL; } + +void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent) { + ent->next = opl->head; + opl->head = ent; +} + +ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl) { + ngtcp2_opl_entry *ent = opl->head; + + if (!ent) { + return NULL; + } + + opl->head = ent->next; + + return ent; +} + +void ngtcp2_opl_clear(ngtcp2_opl *opl) { opl->head = NULL; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h new file mode 100644 index 00000000000000..714aa366304f0d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_opl.h @@ -0,0 +1,65 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 NGTCP2_OPL_H +#define NGTCP2_OPL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_opl_entry ngtcp2_opl_entry; + +struct ngtcp2_opl_entry { + ngtcp2_opl_entry *next; +}; + +/* + * ngtcp2_opl is an object memory pool. + */ +typedef struct ngtcp2_opl { + ngtcp2_opl_entry *head; +} ngtcp2_opl; + +/* + * ngtcp2_opl_init initializes |opl|. + */ +void ngtcp2_opl_init(ngtcp2_opl *opl); + +/* + * ngtcp2_opl_push inserts |ent| to |opl| head. + */ +void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent); + +/* + * ngtcp2_opl_pop removes the first ngtcp2_opl_entry from |opl| and + * returns it. If |opl| does not have any entry, it returns NULL. + */ +ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl); + +void ngtcp2_opl_clear(ngtcp2_opl *opl); + +#endif /* NGTCP2_OPL_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c index 3f35f28ef6a394..83238730033537 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c @@ -37,6 +37,7 @@ void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) { ngtcp2_addr_copy(&dest->local, &src->local); ngtcp2_addr_copy(&dest->remote, &src->remote); + dest->user_data = src->user_data; } int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) { @@ -45,30 +46,32 @@ int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) { } void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, - const struct sockaddr *local_addr, - size_t local_addrlen, void *local_user_data, - const struct sockaddr *remote_addr, - size_t remote_addrlen, void *remote_user_data) { - ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, - 0, local_user_data); + const ngtcp2_sockaddr *local_addr, + ngtcp2_socklen local_addrlen, + const ngtcp2_sockaddr *remote_addr, + ngtcp2_socklen remote_addrlen, void *user_data) { + ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf, + 0); ngtcp2_addr_init(&ps->path.remote, - (const struct sockaddr *)&ps->remote_addrbuf, 0, - remote_user_data); + (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0); ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen); ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen); + + ps->path.user_data = user_data; } void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, const ngtcp2_path *path) { ngtcp2_path_storage_init(ps, path->local.addr, path->local.addrlen, - path->local.user_data, path->remote.addr, - path->remote.addrlen, path->remote.user_data); + path->remote.addr, path->remote.addrlen, + path->user_data); } void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) { - ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, - 0, NULL); + ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf, + 0); ngtcp2_addr_init(&ps->path.remote, - (const struct sockaddr *)&ps->remote_addrbuf, 0, NULL); + (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0); + ps->path.user_data = NULL; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c index ddfbd24bbc9c77..62fef7d6005ed6 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c @@ -26,7 +26,6 @@ #include #include -#include #include "ngtcp2_conv.h" #include "ngtcp2_str.h" @@ -61,13 +60,12 @@ void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { ngtcp2_mem_free(mem, pc); } -int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, - size_t *pdcidlen, const uint8_t **pscid, - size_t *pscidlen, const uint8_t *data, +int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, const uint8_t *data, size_t datalen, size_t short_dcidlen) { size_t len; uint32_t version; size_t dcidlen, scidlen; + int supported_version; assert(datalen); @@ -95,23 +93,31 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, version = ngtcp2_get_uint32(&data[1]); - if ((version == 0 || version == NGTCP2_PROTO_VER_V1 || - (NGTCP2_PROTO_VER_DRAFT_MIN <= version && - version <= NGTCP2_PROTO_VER_DRAFT_MAX)) && + supported_version = ngtcp2_is_supported_version(version); + + if (supported_version && (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) { return NGTCP2_ERR_INVALID_ARGUMENT; } - *pversion = version; - *pdcid = &data[6]; - *pdcidlen = dcidlen; - *pscid = &data[6 + dcidlen + 1]; - *pscidlen = scidlen; + if (version && !supported_version && + datalen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } - if (version && version != NGTCP2_PROTO_VER_V1 && - (version < NGTCP2_PROTO_VER_DRAFT_MIN || - NGTCP2_PROTO_VER_DRAFT_MAX < version)) { - return 1; + dest->version = version; + dest->dcid = &data[6]; + dest->dcidlen = dcidlen; + dest->scid = &data[6 + dcidlen + 1]; + dest->scidlen = scidlen; + + if (!version) { + /* VN */ + return 0; + } + + if (!supported_version) { + return NGTCP2_ERR_VERSION_NEGOTIATION; } return 0; } @@ -123,11 +129,11 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, return NGTCP2_ERR_INVALID_ARGUMENT; } - *pversion = 0; - *pdcid = &data[1]; - *pdcidlen = short_dcidlen; - *pscid = NULL; - *pscidlen = 0; + dest->version = 0; + dest->dcid = &data[1]; + dest->dcidlen = short_dcidlen; + dest->scid = NULL; + dest->scidlen = 0; return 0; } @@ -170,6 +176,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, const uint8_t *token = NULL; size_t tokenlen = 0; uint64_t vi; + uint8_t flags = NGTCP2_PKT_FLAG_LONG_FORM; if (pktlen < 5) { return NGTCP2_ERR_INVALID_ARGUMENT; @@ -183,16 +190,20 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, if (version == 0) { type = NGTCP2_PKT_VERSION_NEGOTIATION; + /* Version Negotiation is not a long header packet. */ + flags = NGTCP2_PKT_FLAG_NONE; /* This must be Version Negotiation packet which lacks packet number and payload length fields. */ len = 5 + 2; } else { if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) { - return NGTCP2_ERR_INVALID_ARGUMENT; + flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; } - type = ngtcp2_pkt_get_type_long(pkt[0]); + type = ngtcp2_pkt_get_type_long(version, pkt[0]); switch (type) { + case 0: + return NGTCP2_ERR_INVALID_ARGUMENT; case NGTCP2_PKT_INITIAL: len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */ @@ -267,10 +278,15 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, } switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: case NGTCP2_PKT_RETRY: break; default: + if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) { + assert(type == NGTCP2_PKT_VERSION_NEGOTIATION); + /* Version Negotiation is not a long header packet. */ + break; + } + /* Length */ n = ngtcp2_get_varint_len(p); len += n - 1; @@ -280,7 +296,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, } } - dest->flags = NGTCP2_PKT_FLAG_LONG_FORM; + dest->flags = flags; dest->type = type; dest->version = version; dest->pkt_num = 0; @@ -297,11 +313,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, p += ntokenlen + tokenlen; switch (type) { - case NGTCP2_PKT_VERSION_NEGOTIATION: case NGTCP2_PKT_RETRY: dest->len = 0; break; default: + if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) { + assert(type == NGTCP2_PKT_VERSION_NEGOTIATION); + /* Version Negotiation is not a long header packet. */ + dest->len = 0; + break; + } + vi = ngtcp2_get_varint(&n, p); if (vi > SIZE_MAX) { return NGTCP2_ERR_INVALID_ARGUMENT; @@ -319,6 +341,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen, size_t dcidlen) { size_t len = 1 + dcidlen; const uint8_t *p = pkt; + uint8_t flags = NGTCP2_PKT_FLAG_NONE; assert(dcidlen <= NGTCP2_MAX_CIDLEN); @@ -326,14 +349,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, return NGTCP2_ERR_INVALID_ARGUMENT; } - if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) || - (pkt[0] & NGTCP2_FIXED_BIT_MASK) == 0) { + if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { return NGTCP2_ERR_INVALID_ARGUMENT; } + if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) { + flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; + } + p = &pkt[1]; - dest->type = NGTCP2_PKT_SHORT; + dest->type = NGTCP2_PKT_1RTT; ngtcp2_cid_init(&dest->dcid, p, dcidlen); p += dcidlen; @@ -342,11 +368,13 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, garbage. */ ngtcp2_cid_zero(&dest->scid); - dest->flags = NGTCP2_PKT_FLAG_NONE; + dest->flags = flags; dest->version = 0; dest->len = 0; dest->pkt_num = 0; dest->pkt_numlen = 0; + dest->token.base = NULL; + dest->token.len = 0; assert((size_t)(p - pkt) == len); @@ -361,7 +389,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, len and 1 byte for packet number. */ if (hd->type != NGTCP2_PKT_RETRY) { - len += 2 /* Length */ + hd->pkt_numlen; + len += NGTCP2_PKT_LENGTHLEN /* Length */ + hd->pkt_numlen; } if (hd->type == NGTCP2_PKT_INITIAL) { @@ -374,8 +402,15 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, p = out; - *p++ = (uint8_t)(NGTCP2_HEADER_FORM_BIT | NGTCP2_FIXED_BIT_MASK | - (hd->type << 4) | (uint8_t)(hd->pkt_numlen - 1)); + *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT | + (ngtcp2_pkt_versioned_type(hd->version, hd->type) << 4) | + (uint8_t)(hd->pkt_numlen - 1)); + if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) { + *p |= NGTCP2_FIXED_BIT_MASK; + } + + ++p; + p = ngtcp2_put_uint32be(p, hd->version); *p++ = (uint8_t)hd->dcid.datalen; if (hd->dcid.datalen) { @@ -394,7 +429,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, } if (hd->type != NGTCP2_PKT_RETRY) { - p = ngtcp2_put_varint14(p, (uint16_t)hd->len); + p = ngtcp2_put_varint30(p, (uint32_t)hd->len); p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); } @@ -414,7 +449,10 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, p = out; - *p = NGTCP2_FIXED_BIT_MASK | (uint8_t)(hd->pkt_numlen - 1); + *p = (uint8_t)(hd->pkt_numlen - 1); + if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) { + *p |= NGTCP2_FIXED_BIT_MASK; + } if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) { *p |= NGTCP2_SHORT_KEY_PHASE_BIT; } @@ -1428,7 +1466,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest, vi = ngtcp2_get_varint(&n, p); if (payloadlen - len < vi) { - return NGTCP2_FRAME_ENCODING_ERROR; + return NGTCP2_ERR_FRAME_ENCODING; } datalen = (size_t)vi; @@ -1436,6 +1474,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest, break; default: assert(0); + abort(); } dest->type = type; @@ -2020,8 +2059,8 @@ ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen, const ngtcp2_datagram *fr) { - size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); - size_t len = + uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + uint64_t len = 1 + (fr->type == NGTCP2_FRAME_DATAGRAM ? 0 : ngtcp2_put_varint_len(datalen)) + datalen; @@ -2039,7 +2078,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen, *p++ = fr->type; if (fr->type == NGTCP2_FRAME_DATAGRAM_LEN) { - p = ngtcp2_put_varint(p, (uint64_t)datalen); + p = ngtcp2_put_varint(p, datalen); } for (i = 0; i < fr->datacnt; ++i) { @@ -2228,8 +2267,8 @@ ngtcp2_ssize ngtcp2_pkt_write_retry( /* Retry packet is sent at most once per one connection attempt. In the first connection attempt, client has to send random DCID - which is at least 8 bytes long. */ - if (odcid->datalen < 8) { + which is at least NGTCP2_MIN_INITIAL_DCIDLEN bytes long. */ + if (odcid->datalen < NGTCP2_MIN_INITIAL_DCIDLEN) { return NGTCP2_ERR_INVALID_ARGUMENT; } @@ -2244,10 +2283,16 @@ ngtcp2_ssize ngtcp2_pkt_write_retry( return pseudo_retrylen; } - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -2330,10 +2375,16 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, pseudo_retrylen = (size_t)(p - pseudo_retry); - if (version == NGTCP2_PROTO_VER_V1) { + switch (version) { + case NGTCP2_PROTO_VER_V1: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; - } else { + break; + case NGTCP2_PROTO_VER_V2_DRAFT: + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1; + break; + default: nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT; noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; } @@ -2353,7 +2404,7 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, } size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, - size_t len, size_t left) { + uint64_t len, size_t left) { size_t n = 1 /* type */ + ngtcp2_put_varint_len((uint64_t)stream_id) + (offset ? ngtcp2_put_varint_len(offset) : 0); @@ -2367,21 +2418,21 @@ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, #if SIZE_MAX > UINT32_MAX len = ngtcp2_min(len, 4611686018427387903lu); #endif /* SIZE_MAX > UINT32_MAX */ - return ngtcp2_min(len, left - 8); + return (size_t)ngtcp2_min(len, (uint64_t)(left - 8)); } if (left > 4 + 16383 && len > 16383) { len = ngtcp2_min(len, 1073741823); - return ngtcp2_min(len, left - 4); + return (size_t)ngtcp2_min(len, (uint64_t)(left - 4)); } if (left > 2 + 63 && len > 63) { len = ngtcp2_min(len, 16383); - return ngtcp2_min(len, left - 2); + return (size_t)ngtcp2_min(len, (uint64_t)(left - 2)); } len = ngtcp2_min(len, 63); - return ngtcp2_min(len, left - 1); + return (size_t)ngtcp2_min(len, (uint64_t)(left - 1)); } size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { @@ -2417,11 +2468,101 @@ size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { } size_t ngtcp2_pkt_datagram_framelen(size_t len) { - return 1 /* type */ + ngtcp2_put_varint_len((uint64_t)len) + len; + return 1 /* type */ + ngtcp2_put_varint_len(len) + len; } -uint8_t ngtcp2_pkt_get_type_long(uint8_t c) { - return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); +int ngtcp2_is_supported_version(uint32_t version) { + switch (version) { + case NGTCP2_PROTO_VER_V1: + case NGTCP2_PROTO_VER_V2_DRAFT: + return 1; + default: + return NGTCP2_PROTO_VER_DRAFT_MIN <= version && + version <= NGTCP2_PROTO_VER_DRAFT_MAX; + } +} + +int ngtcp2_is_reserved_version(uint32_t version) { + return (version & NGTCP2_RESERVED_VERSION_MASK) == + NGTCP2_RESERVED_VERSION_MASK; +} + +uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c) { + uint8_t pkt_type = (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); + + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + switch (pkt_type) { + case NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT: + return NGTCP2_PKT_INITIAL; + case NGTCP2_PKT_TYPE_0RTT_V2_DRAFT: + return NGTCP2_PKT_0RTT; + case NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT: + return NGTCP2_PKT_HANDSHAKE; + case NGTCP2_PKT_TYPE_RETRY_V2_DRAFT: + return NGTCP2_PKT_RETRY; + default: + return 0; + } + default: + if (!ngtcp2_is_supported_version(version)) { + return 0; + } + + /* QUIC v1 and draft versions share the same numeric packet + types. */ + switch (pkt_type) { + case NGTCP2_PKT_TYPE_INITIAL_V1: + return NGTCP2_PKT_INITIAL; + case NGTCP2_PKT_TYPE_0RTT_V1: + return NGTCP2_PKT_0RTT; + case NGTCP2_PKT_TYPE_HANDSHAKE_V1: + return NGTCP2_PKT_HANDSHAKE; + case NGTCP2_PKT_TYPE_RETRY_V1: + return NGTCP2_PKT_RETRY; + default: + return 0; + } + } +} + +uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type) { + switch (version) { + case NGTCP2_PROTO_VER_V2_DRAFT: + switch (pkt_type) { + case NGTCP2_PKT_INITIAL: + return NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT; + case NGTCP2_PKT_0RTT: + return NGTCP2_PKT_TYPE_0RTT_V2_DRAFT; + case NGTCP2_PKT_HANDSHAKE: + return NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT; + case NGTCP2_PKT_RETRY: + return NGTCP2_PKT_TYPE_RETRY_V2_DRAFT; + default: + assert(0); + abort(); + } + default: + /* Assume that unsupported versions share the numeric long packet + types with QUIC v1 in order to send a packet to elicit Version + Negotiation packet. */ + + /* QUIC v1 and draft versions share the same numeric packet + types. */ + switch (pkt_type) { + case NGTCP2_PKT_INITIAL: + return NGTCP2_PKT_TYPE_INITIAL_V1; + case NGTCP2_PKT_0RTT: + return NGTCP2_PKT_TYPE_0RTT_V1; + case NGTCP2_PKT_HANDSHAKE: + return NGTCP2_PKT_TYPE_HANDSHAKE_V1; + case NGTCP2_PKT_RETRY: + return NGTCP2_PKT_TYPE_RETRY_V1; + default: + assert(0); + abort(); + } + } } int ngtcp2_pkt_verify_reserved_bits(uint8_t c) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h index 708e0f6df3f87d..2f7838a08a5625 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h @@ -103,6 +103,40 @@ /* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ #define NGTCP2_RETRY_TAGLEN 16 +/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP payload size + that this library can write. */ +#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1) + +/* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by + Length field in Long packet header. */ +#define NGTCP2_PKT_LENGTHLEN 4 + +/* NGTCP2_PKT_TYPE_INITIAL_V1 is Initial long header packet type for + QUIC v1. */ +#define NGTCP2_PKT_TYPE_INITIAL_V1 0x0 +/* NGTCP2_PKT_TYPE_0RTT_V1 is 0RTT long header packet type for QUIC + v1. */ +#define NGTCP2_PKT_TYPE_0RTT_V1 0x1 +/* NGTCP2_PKT_TYPE_HANDSHAKE_V1 is Handshake long header packet type + for QUIC v1. */ +#define NGTCP2_PKT_TYPE_HANDSHAKE_V1 0x2 +/* NGTCP2_PKT_TYPE_RETRY_V1 is Retry long header packet type for QUIC + v1. */ +#define NGTCP2_PKT_TYPE_RETRY_V1 0x3 + +/* NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT is Initial long header packet type + for QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT 0x1 +/* NGTCP2_PKT_TYPE_0RTT_V2_DRAFT is 0RTT long header packet type for + QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_0RTT_V2_DRAFT 0x2 +/* NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT is Handshake long header packet + type for QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT 0x3 +/* NGTCP2_PKT_TYPE_RETRY_V2_DRAFT is Retry long header packet type for + QUIC v2 draft. */ +#define NGTCP2_PKT_TYPE_RETRY_V2_DRAFT 0x0 + typedef struct ngtcp2_pkt_retry { ngtcp2_cid odcid; ngtcp2_vec token; @@ -258,12 +292,12 @@ typedef struct ngtcp2_stop_sending { typedef struct ngtcp2_path_challenge { uint8_t type; - uint8_t data[8]; + uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN]; } ngtcp2_path_challenge; typedef struct ngtcp2_path_response { uint8_t type; - uint8_t data[8]; + uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN]; } ngtcp2_path_response; typedef struct ngtcp2_crypto { @@ -293,6 +327,8 @@ typedef struct ngtcp2_handshake_done { typedef struct ngtcp2_datagram { uint8_t type; + /* dgram_id is an opaque identifier chosen by an application. */ + uint64_t dgram_id; /* datacnt is the number of elements that data contains. */ size_t datacnt; /* data is a pointer to ngtcp2_vec array that stores data. */ @@ -1123,7 +1159,7 @@ int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr); * small to write STREAM frame, this function returns (size_t)-1. */ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, - size_t len, size_t left); + uint64_t len, size_t left); /* * ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes @@ -1184,12 +1220,21 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx); +/* + * ngtcp2_pkt_versioned_type returns versioned packet type for a + * version |version| that corresponds to the version-independent + * |pkt_type|. + */ +uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type); + /** * @function * - * `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is - * the first byte of Long packet header. + * `ngtcp2_pkt_get_type_long` returns the version-independent long + * packet type. |version| is the QUIC version. |c| is the first byte + * of Long packet header. If |version| is not supported by the + * library, it returns 0. */ -uint8_t ngtcp2_pkt_get_type_long(uint8_t c); +uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c); #endif /* NGTCP2_PKT_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c new file mode 100644 index 00000000000000..26318bb1c8e38c --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.c @@ -0,0 +1,160 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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. + */ +#include "ngtcp2_pmtud.h" + +#include + +#include "ngtcp2_mem.h" +#include "ngtcp2_macro.h" + +/* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent + for each probe. */ +#define NGTCP2_PMTUD_PROBE_NUM_MAX 3 + +static size_t mtu_probes[] = { + 1454 - 48, /* The well known MTU used by a domestic optic fiber + service in Japan. */ + 1390 - 48, /* Typical Tunneled MTU */ + 1280 - 48, /* IPv6 minimum MTU */ + 1492 - 48, /* PPPoE */ +}; + +static size_t mtu_probeslen = sizeof(mtu_probes) / sizeof(mtu_probes[0]); + +int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size, + size_t hard_max_udp_payload_size, int64_t tx_pkt_num, + const ngtcp2_mem *mem) { + ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud)); + + if (pmtud == NULL) { + return NGTCP2_ERR_NOMEM; + } + + pmtud->mem = mem; + pmtud->mtu_idx = 0; + pmtud->num_pkts_sent = 0; + pmtud->expiry = UINT64_MAX; + pmtud->tx_pkt_num = tx_pkt_num; + pmtud->max_udp_payload_size = max_udp_payload_size; + pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size; + pmtud->min_fail_udp_payload_size = SIZE_MAX; + + for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) { + if (mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) { + continue; + } + if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) { + break; + } + } + + *ppmtud = pmtud; + + return 0; +} + +void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) { + if (!pmtud) { + return; + } + + ngtcp2_mem_free(pmtud->mem, pmtud); +} + +size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) { + assert(pmtud->mtu_idx < mtu_probeslen); + + return mtu_probes[pmtud->mtu_idx]; +} + +void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto, + ngtcp2_tstamp ts) { + ngtcp2_tstamp timeout; + + if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) { + timeout = pto; + } else { + timeout = 3 * pto; + } + + pmtud->expiry = ts + timeout; +} + +int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) { + return pmtud->expiry == UINT64_MAX; +} + +static void pmtud_next_probe(ngtcp2_pmtud *pmtud) { + assert(pmtud->mtu_idx < mtu_probeslen); + + ++pmtud->mtu_idx; + pmtud->num_pkts_sent = 0; + pmtud->expiry = UINT64_MAX; + + for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) { + if (mtu_probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size || + mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) { + continue; + } + + if (mtu_probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) { + break; + } + } +} + +void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) { + pmtud->max_udp_payload_size = + ngtcp2_max(pmtud->max_udp_payload_size, payloadlen); + + assert(pmtud->mtu_idx < mtu_probeslen); + + if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) { + return; + } + + pmtud_next_probe(pmtud); +} + +void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) { + if (ts < pmtud->expiry) { + return; + } + + pmtud->expiry = UINT64_MAX; + + if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) { + return; + } + + pmtud->min_fail_udp_payload_size = + ngtcp2_min(pmtud->min_fail_udp_payload_size, mtu_probes[pmtud->mtu_idx]); + + pmtud_next_probe(pmtud); +} + +int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) { + return pmtud->mtu_idx >= mtu_probeslen; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h new file mode 100644 index 00000000000000..6b2e691cfc793a --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pmtud.h @@ -0,0 +1,123 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 NGTCP2_PMTUD_H +#define NGTCP2_PMTUD_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_pmtud { + const ngtcp2_mem *mem; + /* mtu_idx is the index of UDP payload size candidates to try + out. */ + size_t mtu_idx; + /* num_pkts_sent is the number of mtu_idx sized UDP datagram payload + sent */ + size_t num_pkts_sent; + /* expiry is the expired, if it is reached, send out the next UDP + datagram. UINT64_MAX means no expiry, or expiration is canceled. + In either case, new probe packet should be sent unless we have + done all attempts. */ + ngtcp2_tstamp expiry; + /* tx_pkt_num is the smallest outgoing packet number where the + current discovery is performed. In other words, acknowledging + packet whose packet number lower than that does not indicate the + success of Path MTU Discovery. */ + int64_t tx_pkt_num; + /* max_udp_payload_size is the maximum UDP payload size which is + known to work. */ + size_t max_udp_payload_size; + /* hard_max_udp_payload_size is the maximum UDP payload size that is + going to be probed. */ + size_t hard_max_udp_payload_size; + /* min_fail_udp_payload_size is the minimum UDP payload size that is + known to fail. */ + size_t min_fail_udp_payload_size; +} ngtcp2_pmtud; + +/* + * ngtcp2_pmtud_new creates new ngtcp2_pmtud object, and assigns its + * pointer to |*ppmtud|. |max_udp_payload_size| is the maximum UDP + * payload size that is known to work for the current path. + * |tx_pkt_num| should be the next packet number to send, which is + * used to differentiate the PMTUD probe packet sent by the previous + * PMTUD. PMTUD might finish immediately if |max_udp_payload_size| is + * larger than or equal to all UDP payload probe candidates. + * Therefore, call ngtcp2_pmtud_finished to check this situation. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size, + size_t hard_max_udp_payload_size, int64_t tx_pkt_num, + const ngtcp2_mem *mem); + +/* + * ngtcp2_pmtud_del deletes |pmtud|. + */ +void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probelen returns the length of UDP payload size for a + * PMTUD probe packet. + */ +size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probe_sent should be invoked when a PMTUD probe packet is + * sent. + */ +void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto, + ngtcp2_tstamp ts); + +/* + * ngtcp2_pmtud_require_probe returns nonzero if a PMTUD probe packet + * should be sent. + */ +int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probe_success should be invoked when a PMTUD probe + * UDP datagram sized |payloadlen| is acknowledged. + */ +void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen); + +/* + * ngtcp2_pmtud_handle_expiry handles expiry. + */ +void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts); + +/* + * ngtcp2_pmtud_finished returns nonzero if PMTUD has finished. + */ +int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud); + +#endif /* NGTCP2_PMTUD_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c index 47f4f10a29f34c..5376246bd4caa9 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c @@ -57,7 +57,7 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { if (hd->type == NGTCP2_PKT_INITIAL) { ppe->len_offset += ngtcp2_put_varint_len(hd->token.len) + hd->token.len; } - ppe->pkt_num_offset = ppe->len_offset + 2; + ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN; rv = ngtcp2_pkt_encode_hd_long( buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); } else { @@ -115,7 +115,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { assert(cc->hp_mask); if (ppe->len_offset) { - ngtcp2_put_varint14( + ngtcp2_put_varint30( buf->begin + ppe->len_offset, (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead)); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c index d5f7759d6c9bad..314e005293c279 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c @@ -42,19 +42,12 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, const ngtcp2_mem *mem) { - int rv; - (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv)); if (*ppv == NULL) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_ringbuf_init(&(*ppv)->ents, NGTCP2_PV_MAX_ENTRIES, - sizeof(ngtcp2_pv_entry), mem); - if (rv != 0) { - ngtcp2_mem_free(mem, *ppv); - return 0; - } + ngtcp2_static_ringbuf_pv_ents_init(&(*ppv)->ents); ngtcp2_dcid_copy(&(*ppv)->dcid, dcid); @@ -74,7 +67,6 @@ void ngtcp2_pv_del(ngtcp2_pv *pv) { if (pv == NULL) { return; } - ngtcp2_ringbuf_free(&pv->ents); ngtcp2_mem_free(pv->mem, pv); } @@ -85,11 +77,11 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, assert(pv->probe_pkt_left); - if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { pv->started_ts = ts; } - ent = ngtcp2_ringbuf_push_back(&pv->ents); + ent = ngtcp2_ringbuf_push_back(&pv->ents.rb); ngtcp2_pv_entry_init(ent, data, expiry, flags); pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER; @@ -97,7 +89,7 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, } int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { - size_t len = ngtcp2_ringbuf_len(&pv->ents); + size_t len = ngtcp2_ringbuf_len(&pv->ents.rb); size_t i; ngtcp2_pv_entry *ent; @@ -106,7 +98,7 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { } for (i = 0; i < len; ++i) { - ent = ngtcp2_ringbuf_get(&pv->ents, i); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, i); if (memcmp(ent->data, data, sizeof(ent->data)) == 0) { *pflags = ent->flags; ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated"); @@ -120,11 +112,11 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) { ngtcp2_pv_entry *ent; - if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { return; } - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); if (ent->expiry > ts) { return; @@ -146,9 +138,9 @@ int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) { return 0; } - assert(ngtcp2_ringbuf_len(&pv->ents)); + assert(ngtcp2_ringbuf_len(&pv->ents.rb)); - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); t = pv->started_ts + pv->timeout; t = ngtcp2_max(t, ent->expiry); @@ -160,11 +152,11 @@ ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) { ngtcp2_pv_entry *ent; if ((pv->flags & NGTCP2_PV_FLAG_CANCEL_TIMER) || - ngtcp2_ringbuf_len(&pv->ents) == 0) { + ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { return UINT64_MAX; } - ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); return ent->expiry; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h index b532dbca98356b..293cbcaaf6e881 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h @@ -46,10 +46,10 @@ typedef struct ngtcp2_log ngtcp2_log; typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; /* NGTCP2_PV_ENTRY_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00 +#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00u /* NGTCP2_PV_ENTRY_FLAG_UNDERSIZED indicates that UDP datagram which contains PATH_CHALLENGE is undersized (< 1200 bytes) */ -#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01 +#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01u typedef struct ngtcp2_pv_entry { /* expiry is the timestamp when this PATH_CHALLENGE expires. */ @@ -64,25 +64,30 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, ngtcp2_tstamp expiry, uint8_t flags); /* NGTCP2_PV_FLAG_NONE indicates no flag is set. */ -#define NGTCP2_PV_FLAG_NONE 0x00 +#define NGTCP2_PV_FLAG_NONE 0x00u /* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of path validation should be ignored entirely. */ -#define NGTCP2_PV_FLAG_DONT_CARE 0x01 +#define NGTCP2_PV_FLAG_DONT_CARE 0x01u /* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is cancelled. */ -#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02 +#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02u /* NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE indicates that fallback DCID is available in ngtcp2_pv. If path validation fails, fallback to the fallback DCID. If path validation succeeds, fallback DCID is retired if it does not equal to the current DCID. */ -#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04 +#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04u /* NGTCP2_PV_FLAG_MTU_PROBE indicates that a validation must probe least MTU that QUIC requires, which is 1200 bytes. If it fails, a path is not viable. */ -#define NGTCP2_PV_FLAG_MTU_PROBE 0x08 +#define NGTCP2_PV_FLAG_MTU_PROBE 0x08u +/* NGTCP2_PV_FLAG_PREFERRED_ADDR indicates that client is migrating to + server's preferred address. This flag is only used by client. */ +#define NGTCP2_PV_FLAG_PREFERRED_ADDR 0x10u typedef struct ngtcp2_pv ngtcp2_pv; +ngtcp2_static_ringbuf_def(pv_ents, NGTCP2_PV_MAX_ENTRIES, + sizeof(ngtcp2_pv_entry)); /* * ngtcp2_pv is the context of a single path validation. */ @@ -95,7 +100,7 @@ struct ngtcp2_pv { fallback if this path validation fails. */ ngtcp2_dcid fallback_dcid; /* ents is the ring buffer of ngtcp2_pv_entry */ - ngtcp2_ringbuf ents; + ngtcp2_static_ringbuf_pv_ents ents; /* timeout is the duration within which this path validation should succeed. */ ngtcp2_duration timeout; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c index 7c31ab64af6955..69eaeb7367438d 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c @@ -28,6 +28,8 @@ #include "ngtcp2_str.h" #include "ngtcp2_vec.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_net.h" void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, ngtcp2_tstamp ts, void *user_data) { @@ -185,8 +187,8 @@ static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name, static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) { p = write_verbatim( - p, "\"common_fields\":{\"protocol_type\":\"QUIC_HTTP3\",\"time_format\":" - "\"relative\",\"reference_time\":\"0\",\"group_id\":"); + p, "\"common_fields\":{\"protocol_type\":[\"QUIC\"],\"time_format\":" + "\"relative\",\"reference_time\":0,\"group_id\":"); p = write_cid(p, odcid); *p++ = '}'; return p; @@ -215,7 +217,7 @@ void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) { } p = write_verbatim( - p, "{\"qlog_format\":\"NDJSON\",\"qlog_version\":\"draft-02\","); + p, "\x1e{\"qlog_format\":\"JSON-SEQ\",\"qlog_version\":\"0.3\","); p = write_trace(p, server, odcid); p = write_verbatim(p, "}\n"); @@ -238,6 +240,10 @@ static ngtcp2_vec vec_pkt_type_handshake = ngtcp2_make_vec_lit("handshake"); static ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT"); static ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT"); static ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry"); +static ngtcp2_vec vec_pkt_type_version_negotiation = + ngtcp2_make_vec_lit("version_negotiation"); +static ngtcp2_vec vec_pkt_type_stateless_reset = + ngtcp2_make_vec_lit("stateless_reset"); static ngtcp2_vec vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown"); static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) { @@ -256,19 +262,33 @@ static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) { } } - return &vec_pkt_type_1rtt; + switch (hd->type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + return &vec_pkt_type_version_negotiation; + case NGTCP2_PKT_STATELESS_RESET: + return &vec_pkt_type_stateless_reset; + case NGTCP2_PKT_1RTT: + return &vec_pkt_type_1rtt; + default: + return &vec_pkt_type_unknown; + } } static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) { /* - * {"packet_type":"version_negotiation","packet_number":"0000000000000000000"} + * {"packet_type":"version_negotiation","packet_number":"0000000000000000000","token":{"data":""}} */ -#define NGTCP2_QLOG_PKT_HD_OVERHEAD 75 +#define NGTCP2_QLOG_PKT_HD_OVERHEAD 95 *p++ = '{'; p = write_pair(p, "packet_type", qlog_pkt_type(hd)); *p++ = ','; p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num); + if (hd->type == NGTCP2_PKT_INITIAL && hd->token.len) { + p = write_verbatim(p, ",\"token\":{"); + p = write_pair_hex(p, "data", hd->token.base, hd->token.len); + *p++ = '}'; + } /* TODO Write DCIL and DCID */ /* TODO Write SCIL and SCID */ *p++ = '}'; @@ -313,10 +333,7 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) { p = write_verbatim(p, "{\"frame_type\":\"ack\","); p = write_pair_duration(p, "ack_delay", fr->ack_delay_unscaled); - *p++ = ','; - p = write_string(p, "acked_ranges"); - *p++ = ':'; - *p++ = '['; + p = write_verbatim(p, ",\"acked_ranges\":["); largest_ack = fr->largest_ack; min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; @@ -410,14 +427,15 @@ static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) { static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) { /* - * {"frame_type":"new_token","length":0000000000000000000,"token":""} + * {"frame_type":"new_token","length":0000000000000000000,"token":{"data":""}} */ -#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 66 +#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 75 p = write_verbatim(p, "{\"frame_type\":\"new_token\","); p = write_pair_number(p, "length", fr->token.len); - *p++ = ','; - p = write_pair_hex(p, "token", fr->token.base, fr->token.len); + p = write_verbatim(p, ",\"token\":{"); + p = write_pair_hex(p, "data", fr->token.base, fr->token.len); + *p++ = '}'; *p++ = '}'; return p; @@ -480,9 +498,7 @@ static uint8_t *write_max_streams_frame(uint8_t *p, */ #define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89 - p = write_verbatim(p, "{\"frame_type\":\"max_streams\","); - p = write_string(p, "stream_type"); - *p++ = ':'; + p = write_verbatim(p, "{\"frame_type\":\"max_streams\",\"stream_type\":"); if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) { p = write_string(p, "bidirectional"); } else { @@ -541,9 +557,9 @@ static uint8_t *write_streams_blocked_frame(uint8_t *p, static uint8_t * write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { /* - * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} + * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":{"data":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}} */ -#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 271 +#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 280 p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\","); p = write_pair_number(p, "sequence_number", fr->seq); @@ -553,10 +569,11 @@ write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { p = write_pair_number(p, "connection_id_length", fr->cid.datalen); *p++ = ','; p = write_pair_cid(p, "connection_id", &fr->cid); - *p++ = ','; - p = write_pair_hex(p, "stateless_reset_token", fr->stateless_reset_token, + p = write_verbatim(p, ",\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", fr->stateless_reset_token, sizeof(fr->stateless_reset_token)); *p++ = '}'; + *p++ = '}'; return p; } @@ -611,9 +628,8 @@ write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) { */ #define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131 - p = write_verbatim(p, "{\"frame_type\":\"connection_close\","); - p = write_string(p, "error_space"); - *p++ = ':'; + p = write_verbatim(p, + "{\"frame_type\":\"connection_close\",\"error_space\":"); if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { p = write_string(p, "transport"); } else { @@ -669,6 +685,7 @@ static void qlog_pkt_write_start(ngtcp2_qlog *qlog, int sent) { ngtcp2_buf_reset(&qlog->buf); p = qlog->buf.last; + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim(p, ",\"name\":"); @@ -690,14 +707,18 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, } /* - * ],"header":,"raw":{"packet_size":0000000000000000000}}} + * ],"header":,"raw":{"length":0000000000000000000}}} * * plus, terminating LF */ #define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD \ - (1 + 55 + NGTCP2_QLOG_PKT_HD_OVERHEAD) + (1 + 50 + NGTCP2_QLOG_PKT_HD_OVERHEAD) + + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD + hd->token.len * 2) { + return; + } - assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD); assert(ngtcp2_buf_len(&qlog->buf)); /* Eat last ',' */ @@ -707,7 +728,7 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, p = write_verbatim(p, "],\"header\":"); p = write_pkt_hd(p, hd); - p = write_verbatim(p, ",\"raw\":{\"packet_size\":"); + p = write_verbatim(p, ",\"raw\":{\"length\":"); p = write_number(p, pktlen); p = write_verbatim(p, "}}}\n"); @@ -726,15 +747,13 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { switch (fr->type) { case NGTCP2_FRAME_PADDING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1) { return; } p = write_padding_frame(p, &fr->padding); break; case NGTCP2_FRAME_PING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1) { return; } p = write_ping_frame(p, &fr->ping); @@ -746,86 +765,75 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { (size_t)(fr->type == NGTCP2_FRAME_ACK_ECN ? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD : 0) + - NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1) { return; } p = write_ack_frame(p, &fr->ack); break; case NGTCP2_FRAME_RESET_STREAM: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + 1) { return; } p = write_reset_stream_frame(p, &fr->reset_stream); break; case NGTCP2_FRAME_STOP_SENDING: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + 1) { return; } p = write_stop_sending_frame(p, &fr->stop_sending); break; case NGTCP2_FRAME_CRYPTO: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1) { return; } p = write_crypto_frame(p, &fr->crypto); break; case NGTCP2_FRAME_NEW_TOKEN: if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD + - fr->new_token.token.len * 2 + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + fr->new_token.token.len * 2 + 1) { return; } p = write_new_token_frame(p, &fr->new_token); break; case NGTCP2_FRAME_STREAM: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1) { return; } p = write_stream_frame(p, &fr->stream); break; case NGTCP2_FRAME_MAX_DATA: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1) { return; } p = write_max_data_frame(p, &fr->max_data); break; case NGTCP2_FRAME_MAX_STREAM_DATA: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1) { return; } p = write_max_stream_data_frame(p, &fr->max_stream_data); break; case NGTCP2_FRAME_MAX_STREAMS_BIDI: case NGTCP2_FRAME_MAX_STREAMS_UNI: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + 1) { return; } p = write_max_streams_frame(p, &fr->max_streams); break; case NGTCP2_FRAME_DATA_BLOCKED: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_data_blocked_frame(p, &fr->data_blocked); break; case NGTCP2_FRAME_STREAM_DATA_BLOCKED: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked); @@ -833,40 +841,35 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1) { return; } p = write_streams_blocked_frame(p, &fr->streams_blocked); break; case NGTCP2_FRAME_NEW_CONNECTION_ID: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1) { return; } p = write_new_connection_id_frame(p, &fr->new_connection_id); break; case NGTCP2_FRAME_RETIRE_CONNECTION_ID: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1) { return; } p = write_retire_connection_id_frame(p, &fr->retire_connection_id); break; case NGTCP2_FRAME_PATH_CHALLENGE: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1) { return; } p = write_path_challenge_frame(p, &fr->path_challenge); break; case NGTCP2_FRAME_PATH_RESPONSE: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + - 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + 1) { return; } p = write_path_response_frame(p, &fr->path_response); @@ -874,24 +877,21 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { case NGTCP2_FRAME_CONNECTION_CLOSE: case NGTCP2_FRAME_CONNECTION_CLOSE_APP: if (ngtcp2_buf_left(&qlog->buf) < - NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1) { return; } 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) { + NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1) { return; } p = write_handshake_done_frame(p, &fr->handshake_done); break; case NGTCP2_FRAME_DATAGRAM: case NGTCP2_FRAME_DATAGRAM_LEN: - if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1 + - NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1) { return; } p = write_datagram_frame(p, &fr->datagram); @@ -934,6 +934,7 @@ void ngtcp2_qlog_parameters_set_transport_params( return; } + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim( @@ -958,9 +959,10 @@ void ngtcp2_qlog_parameters_set_transport_params( *p++ = ','; } if (params->stateless_reset_token_present) { - p = write_pair_hex(p, "stateless_reset_token", - params->stateless_reset_token, + p = write_verbatim(p, "\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", params->stateless_reset_token, sizeof(params->stateless_reset_token)); + *p++ = '}'; *p++ = ','; } p = write_pair_bool(p, "disable_active_migration", @@ -1009,14 +1011,17 @@ void ngtcp2_qlog_parameters_set_transport_params( p = write_pair_number(p, "port_v6", paddr->ipv6_port); *p++ = ','; p = write_pair_cid(p, "connection_id", &paddr->cid); - *p++ = ','; - p = write_pair_hex(p, "stateless_reset_token", paddr->stateless_reset_token, + p = write_verbatim(p, ",\"stateless_reset_token\":{"); + p = write_pair_hex(p, "data", paddr->stateless_reset_token, sizeof(paddr->stateless_reset_token)); *p++ = '}'; + *p++ = '}'; } *p++ = ','; p = write_pair_number(p, "max_datagram_frame_size", params->max_datagram_frame_size); + *p++ = ','; + p = write_pair_bool(p, "grease_quic_bit", params->grease_quic_bit); p = write_verbatim(p, "}}\n"); qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, @@ -1032,6 +1037,7 @@ void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, return; } + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim(p, ",\"name\":\"recovery:metrics_updated\",\"data\":{"); @@ -1071,6 +1077,7 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { return; } + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim( @@ -1087,22 +1094,110 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { (size_t)(p - buf)); } -void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, - const ngtcp2_pkt_hd *hd) { +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + const ngtcp2_pkt_retry *retry) { + uint8_t rawbuf[1024]; + ngtcp2_buf buf; + + if (!qlog->write) { + return; + } + + ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf)); + + *buf.last++ = '\x1e'; + *buf.last++ = '{'; + buf.last = qlog_write_time(qlog, buf.last); + buf.last = write_verbatim( + buf.last, + ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); + + if (ngtcp2_buf_left(&buf) < + NGTCP2_QLOG_PKT_HD_OVERHEAD + hd->token.len * 2 + + sizeof(",\"retry_token\":{\"data\":\"\"}}}\n") - 1 + + retry->token.len * 2) { + return; + } + + buf.last = write_pkt_hd(buf.last, hd); + buf.last = write_verbatim(buf.last, ",\"retry_token\":{"); + buf.last = + write_pair_hex(buf.last, "data", retry->token.base, retry->token.len); + buf.last = write_verbatim(buf.last, "}}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos, + ngtcp2_buf_len(&buf)); +} + +void ngtcp2_qlog_stateless_reset_pkt_received( + ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr) { uint8_t buf[256]; uint8_t *p = buf; + ngtcp2_pkt_hd hd = {0}; if (!qlog->write) { return; } + hd.type = NGTCP2_PKT_STATELESS_RESET; + + *p++ = '\x1e'; *p++ = '{'; p = qlog_write_time(qlog, p); p = write_verbatim( p, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); - p = write_pkt_hd(p, hd); + p = write_pkt_hd(p, &hd); + *p++ = ','; + p = write_pair_hex(p, "stateless_reset_token", sr->stateless_reset_token, + NGTCP2_STATELESS_RESET_TOKENLEN); p = write_verbatim(p, "}}\n"); qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, (size_t)(p - buf)); } + +void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, + size_t nsv) { + uint8_t rawbuf[512]; + ngtcp2_buf buf; + size_t i; + uint32_t v; + + if (!qlog->write) { + return; + } + + ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf)); + + *buf.last++ = '\x1e'; + *buf.last++ = '{'; + buf.last = qlog_write_time(qlog, buf.last); + buf.last = write_verbatim( + buf.last, + ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); + buf.last = write_pkt_hd(buf.last, hd); + buf.last = write_verbatim(buf.last, ",\"supported_versions\":["); + + if (nsv) { + if (ngtcp2_buf_left(&buf) < + (sizeof("\"xxxxxxxx\",") - 1) * nsv - 1 + sizeof("]}}\n") - 1) { + return; + } + + v = ngtcp2_htonl(sv[0]); + buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v)); + + for (i = 1; i < nsv; ++i) { + *buf.last++ = ','; + v = ngtcp2_htonl(sv[i]); + buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v)); + } + } + + buf.last = write_verbatim(buf.last, "]}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos, + ngtcp2_buf_len(&buf)); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h index cb3c2063f766c8..b9107c0e5c031a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h @@ -139,6 +139,23 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent); * ngtcp2_qlog_retry_pkt_received writes packet_received event for a * received Retry packet. */ -void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd); +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + const ngtcp2_pkt_retry *retry); + +/* + * ngtcp2_qlog_stateless_reset_pkt_received writes packet_received + * event for a received Stateless Reset packet. + */ +void ngtcp2_qlog_stateless_reset_pkt_received( + ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr); + +/* + * ngtcp2_qlog_version_negotiation_pkt_received writes packet_received + * event for a received Version Negotiation packet. + */ +void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, + size_t nsv); #endif /* NGTCP2_QLOG_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h index e392c34ebfedb7..4cb40882192a77 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h @@ -31,12 +31,10 @@ #include -/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in - draft-ietf-quic-recovery-22. */ +/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in RFC 9002. */ #define NGTCP2_PKT_THRESHOLD 3 -/* NGTCP2_GRANULARITY is kGranularity described in - draft-ietf-quic-recovery-17. */ +/* NGTCP2_GRANULARITY is kGranularity described in RFC 9002. */ #define NGTCP2_GRANULARITY NGTCP2_MILLISECONDS #endif /* NGTCP2_RCVRY_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c index e4deab1ff76b83..a6b3f73e73339c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c @@ -43,24 +43,30 @@ unsigned int __popcnt(unsigned int x) { int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, const ngtcp2_mem *mem) { + uint8_t *buf = ngtcp2_mem_malloc(mem, nmemb * size); + if (buf == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_ringbuf_buf_init(rb, nmemb, size, buf, mem); + + return 0; +} + +void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + uint8_t *buf, const ngtcp2_mem *mem) { #ifdef WIN32 assert(1 == __popcnt((unsigned int)nmemb)); #else assert(1 == __builtin_popcount((unsigned int)nmemb)); #endif - rb->buf = ngtcp2_mem_malloc(mem, nmemb * size); - if (rb->buf == NULL) { - return NGTCP2_ERR_NOMEM; - } - + rb->buf = buf; rb->mem = mem; rb->nmemb = nmemb; rb->size = size; rb->first = 0; rb->len = 0; - - return 0; } void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h index c6e1421518342b..16635c941032c7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h @@ -62,6 +62,13 @@ typedef struct ngtcp2_ringbuf { int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, const ngtcp2_mem *mem); +/* + * ngtcp2_ringbuf_buf_init initializes |rb| with given buffer and + * size. + */ +void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + uint8_t *buf, const ngtcp2_mem *mem); + /* * ngtcp2_ringbuf_free frees resources allocated for |rb|. This * function does not free the memory pointed by |rb|. @@ -107,4 +114,19 @@ void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset); /* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb); +/* ngtcp2_static_ringbuf_def defines ngtcp2_ringbuf struct wrapper + which uses a statically allocated buffer that is suitable for a + usage that does not change buffer size with ngtcp2_ringbuf_resize. + ngtcp2_ringbuf_free should never be called for rb field. */ +#define ngtcp2_static_ringbuf_def(NAME, NMEMB, SIZE) \ + typedef struct ngtcp2_static_ringbuf_##NAME { \ + ngtcp2_ringbuf rb; \ + uint8_t buf[(NMEMB) * (SIZE)]; \ + } ngtcp2_static_ringbuf_##NAME; \ + \ + static inline void ngtcp2_static_ringbuf_##NAME##_init( \ + ngtcp2_static_ringbuf_##NAME *srb) { \ + ngtcp2_ringbuf_buf_init(&srb->rb, (NMEMB), (SIZE), srb->buf, NULL); \ + } + #endif /* NGTCP2_RINGBUF_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c index 499c07ec6be247..9c3d75dc33ae0c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c @@ -69,11 +69,8 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { int rv; ngtcp2_rob_gap *g; - rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - goto fail_gapksl_ksl_init; - } + ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem); if (rv != 0) { @@ -85,23 +82,18 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { goto fail_gapksl_ksl_insert; } - rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, - sizeof(ngtcp2_range), mem); - if (rv != 0) { - goto fail_dataksl_ksl_init; - } + ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), + mem); rob->chunk = chunk; rob->mem = mem; return 0; -fail_dataksl_ksl_init: fail_gapksl_ksl_insert: ngtcp2_rob_gap_del(g, mem); fail_rob_gap_new: ngtcp2_ksl_free(&rob->gapksl); -fail_gapksl_ksl_init: return rv; } @@ -185,7 +177,7 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, break; } if (ngtcp2_range_eq(&g->range, &m)) { - ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range); + ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range); ngtcp2_rob_gap_del(g, rob->mem); rv = rob_write_data(rob, m.begin, data + (m.begin - offset), (size_t)ngtcp2_range_len(&m)); @@ -244,7 +236,7 @@ int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { g->range.begin = offset; break; } - ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range); + ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range); ngtcp2_rob_gap_del(g, rob->mem); } @@ -255,7 +247,7 @@ int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { if (offset < d->range.begin + rob->chunk) { return 0; } - ngtcp2_ksl_remove(&rob->dataksl, &it, &d->range); + ngtcp2_ksl_remove_hint(&rob->dataksl, &it, &it, &d->range); ngtcp2_rob_data_del(d, rob->mem); } @@ -305,7 +297,7 @@ void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) { return; } - ngtcp2_ksl_remove(&rob->dataksl, NULL, &d->range); + ngtcp2_ksl_remove_hint(&rob->dataksl, NULL, &it, &d->range); ngtcp2_rob_data_del(d, rob->mem); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c index e546fdf85c623b..7b50f98d41ec7b 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c @@ -23,6 +23,9 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ngtcp2_rst.h" + +#include + #include "ngtcp2_rtb.h" #include "ngtcp2_cc.h" #include "ngtcp2_macro.h" @@ -32,6 +35,9 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) { rs->delivered = 0; rs->prior_delivered = 0; rs->prior_ts = 0; + rs->tx_in_flight = 0; + rs->lost = 0; + rs->prior_lost = 0; rs->send_elapsed = 0; rs->ack_elapsed = 0; rs->is_app_limited = 0; @@ -39,10 +45,15 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) { void ngtcp2_rst_init(ngtcp2_rst *rst) { ngtcp2_rs_init(&rst->rs); + ngtcp2_window_filter_init(&rst->wf, 12); rst->delivered = 0; rst->delivered_ts = 0; rst->first_sent_ts = 0; rst->app_limited = 0; + rst->next_round_delivered = 0; + rst->round_count = 0; + rst->is_cwnd_limited = 0; + rst->lost = 0; } void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, @@ -54,15 +65,24 @@ void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, ent->rst.delivered_ts = rst->delivered_ts; ent->rst.delivered = rst->delivered; ent->rst.is_app_limited = rst->app_limited != 0; + ent->rst.tx_in_flight = cstat->bytes_in_flight + ent->pktlen; + ent->rst.lost = rst->lost; } -int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat, + uint64_t pkt_delivered) { ngtcp2_rs *rs = &rst->rs; + uint64_t rate; if (rst->app_limited && rst->delivered > rst->app_limited) { rst->app_limited = 0; } + if (pkt_delivered >= rst->next_round_delivered) { + rst->next_round_delivered = pkt_delivered; + ++rst->round_count; + } + if (rs->prior_ts == 0) { return 0; } @@ -70,14 +90,22 @@ int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { rs->interval = ngtcp2_max(rs->send_elapsed, rs->ack_elapsed); rs->delivered = rst->delivered - rs->prior_delivered; + rs->lost = rst->lost - rs->prior_lost; if (rs->interval < cstat->min_rtt) { rs->interval = UINT64_MAX; return 0; } - if (rs->interval) { - cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval; + if (!rs->interval) { + return 0; + } + + rate = rs->delivered * NGTCP2_SECONDS / rs->interval; + + if (rate > ngtcp2_window_filter_get_best(&rst->wf) || !rst->app_limited) { + ngtcp2_window_filter_update(&rst->wf, rate, rst->round_count); + cstat->delivery_rate_sec = ngtcp2_window_filter_get_best(&rst->wf); } return 0; @@ -96,6 +124,8 @@ void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, rs->is_app_limited = ent->rst.is_app_limited; rs->send_elapsed = ent->ts - ent->rst.first_sent_ts; rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts; + rs->tx_in_flight = ent->rst.tx_in_flight; + rs->prior_lost = ent->rst.lost; rst->first_sent_ts = ent->ts; } } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h index 14aae998ebdf65..488c65575a5589 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h @@ -31,6 +31,8 @@ #include +#include "ngtcp2_window_filter.h" + typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; /** @@ -43,6 +45,9 @@ typedef struct ngtcp2_rs { uint64_t delivered; uint64_t prior_delivered; ngtcp2_tstamp prior_ts; + uint64_t tx_in_flight; + uint64_t lost; + uint64_t prior_lost; ngtcp2_duration send_elapsed; ngtcp2_duration ack_elapsed; int is_app_limited; @@ -56,17 +61,23 @@ void ngtcp2_rs_init(ngtcp2_rs *rs); */ typedef struct ngtcp2_rst { ngtcp2_rs rs; + ngtcp2_window_filter wf; uint64_t delivered; ngtcp2_tstamp delivered_ts; ngtcp2_tstamp first_sent_ts; uint64_t app_limited; + uint64_t next_round_delivered; + uint64_t round_count; + uint64_t lost; + int is_cwnd_limited; } ngtcp2_rst; void ngtcp2_rst_init(ngtcp2_rst *rst); void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, const ngtcp2_conn_stat *cstat); -int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat, + uint64_t pkt_delivered); void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, ngtcp2_tstamp ts); void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c index 1ef67ccbc6af5d..644071400a6eb7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c @@ -46,6 +46,18 @@ int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) { return 0; } +int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, + ngtcp2_objalloc *objalloc) { + *pfrc = ngtcp2_objalloc_frame_chain_get(objalloc); + if (*pfrc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_frame_chain_init(*pfrc); + + return 0; +} + int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, const ngtcp2_mem *mem) { *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen); @@ -58,35 +70,44 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, return 0; } -int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem) { - size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); - size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream); +int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { + size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream); - if (datacnt > 0 && need > avail) { - return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + if (datacnt > 1) { + need = sizeof(ngtcp2_vec) * (datacnt - 1); + + if (need > avail) { + return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + } } - return ngtcp2_frame_chain_new(pfrc, mem); + return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } -int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem) { - size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); - size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto); +int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { + size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto); + + if (datacnt > 1) { + need = sizeof(ngtcp2_vec) * (datacnt - 1); - if (datacnt > 0 && need > avail) { - return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + if (need > avail) { + return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + } } - return ngtcp2_frame_chain_new(pfrc, mem); + return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } -int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, - const ngtcp2_vec *token, - const ngtcp2_mem *mem) { +int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, + const ngtcp2_vec *token, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token); int rv; uint8_t *p; @@ -95,7 +116,7 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, if (token->len > avail) { rv = ngtcp2_frame_chain_extralen_new(pfrc, token->len - avail, mem); } else { - rv = ngtcp2_frame_chain_new(pfrc, mem); + rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } if (rv != 0) { return rv; @@ -104,7 +125,7 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, fr = &(*pfrc)->fr; fr->type = NGTCP2_FRAME_NEW_TOKEN; - p = (uint8_t *)(*pfrc) + sizeof(ngtcp2_new_token); + p = (uint8_t *)fr + sizeof(ngtcp2_new_token); memcpy(p, token->base, token->len); ngtcp2_vec_init(&fr->new_token.token, p, token->len); @@ -127,19 +148,71 @@ void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) { ngtcp2_mem_free(mem, frc); } +void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain_binder *binder; + + if (frc == NULL) { + return; + } + + switch (frc->fr.type) { + case NGTCP2_FRAME_STREAM: + if (frc->fr.stream.datacnt && + sizeof(ngtcp2_vec) * (frc->fr.stream.datacnt - 1) > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + case NGTCP2_FRAME_CRYPTO: + if (frc->fr.crypto.datacnt && + sizeof(ngtcp2_vec) * (frc->fr.crypto.datacnt - 1) > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + case NGTCP2_FRAME_NEW_TOKEN: + if (frc->fr.new_token.token.len > + sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) { + ngtcp2_frame_chain_del(frc, mem); + + return; + } + + break; + } + + binder = frc->binder; + if (binder && --binder->refcount == 0) { + ngtcp2_mem_free(mem, binder); + } + + frc->binder = NULL; + + ngtcp2_objalloc_frame_chain_release(objalloc, frc); +} + void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) { frc->next = NULL; frc->binder = NULL; } -void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, - const ngtcp2_mem *mem) { +void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem) { ngtcp2_frame_chain *next; - for (; frc;) { + for (; frc; frc = next) { next = frc->next; - ngtcp2_frame_chain_del(frc, mem); - frc = next; + + ngtcp2_frame_chain_objalloc_del(frc, objalloc, mem); } } @@ -176,35 +249,46 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, return 0; } -int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, - ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, - size_t pktlen, uint8_t flags, const ngtcp2_mem *mem) { - (*pent) = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_rtb_entry)); +static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags) { + memset(ent, 0, sizeof(*ent)); + + ent->hd.pkt_num = hd->pkt_num; + ent->hd.type = hd->type; + ent->hd.flags = hd->flags; + ent->frc = frc; + ent->ts = ts; + ent->lost_ts = UINT64_MAX; + ent->pktlen = pktlen; + ent->flags = flags; + ent->next = NULL; +} + +int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, + const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags, + ngtcp2_objalloc *objalloc) { + *pent = ngtcp2_objalloc_rtb_entry_get(objalloc); if (*pent == NULL) { return NGTCP2_ERR_NOMEM; } - (*pent)->hd.pkt_num = hd->pkt_num; - (*pent)->hd.type = hd->type; - (*pent)->hd.flags = hd->flags; - (*pent)->frc = frc; - (*pent)->ts = ts; - (*pent)->lost_ts = UINT64_MAX; - (*pent)->pktlen = pktlen; - (*pent)->flags = flags; - (*pent)->next = NULL; + rtb_entry_init(*pent, hd, frc, ts, pktlen, flags); return 0; } -void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) { - if (ent == NULL) { - return; - } +void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent, + ngtcp2_objalloc *objalloc, + ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain_list_objalloc_del(ent->frc, frc_objalloc, mem); - ngtcp2_frame_chain_list_del(ent->frc, mem); + ent->frc = NULL; - ngtcp2_mem_free(mem, ent); + ngtcp2_objalloc_rtb_entry_release(objalloc, ent); } static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { @@ -214,7 +298,10 @@ static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, ngtcp2_qlog *qlog, - const ngtcp2_mem *mem) { + ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { + rtb->rtb_entry_objalloc = rtb_entry_objalloc; + rtb->frc_objalloc = frc_objalloc; ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem); rtb->crypto = crypto; rtb->rst = rst; @@ -225,12 +312,14 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, rtb->largest_acked_tx_pkt_num = -1; rtb->num_ack_eliciting = 0; rtb->num_retransmittable = 0; + rtb->num_pto_eliciting = 0; rtb->probe_pkt_left = 0; rtb->pktns_id = pktns_id; rtb->cc_pkt_num = 0; rtb->cc_bytes_in_flight = 0; rtb->persistent_congestion_start_ts = UINT64_MAX; rtb->num_lost_pkts = 0; + rtb->num_lost_pmtud_pkts = 0; } void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { @@ -243,7 +332,9 @@ void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { it = ngtcp2_ksl_begin(&rtb->ents); for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_rtb_entry_del(ngtcp2_ksl_it_get(&it), rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ngtcp2_ksl_it_get(&it), + rtb->rtb_entry_objalloc, rtb->frc_objalloc, + rtb->mem); } ngtcp2_ksl_free(&rtb->ents); @@ -266,14 +357,23 @@ static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { ++rtb->num_retransmittable; } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + ++rtb->num_pto_eliciting; + } } -static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, - ngtcp2_conn_stat *cstat) { +static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn_stat *cstat) { if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { assert(rtb->num_lost_pkts); --rtb->num_lost_pkts; - return; + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + assert(rtb->num_lost_pmtud_pkts); + --rtb->num_lost_pmtud_pkts; + } + + return 0; } if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { @@ -287,22 +387,44 @@ static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, --rtb->num_retransmittable; } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + assert(rtb->num_pto_eliciting); + --rtb->num_pto_eliciting; + } + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { assert(cstat->bytes_in_flight >= ent->pktlen); cstat->bytes_in_flight -= ent->pktlen; assert(rtb->cc_bytes_in_flight >= ent->pktlen); rtb->cc_bytes_in_flight -= ent->pktlen; + + /* If PMTUD packet is lost, we do not report the lost bytes to the + caller in order to ignore loss of PMTUD packet. */ + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + return 0; + } + + return ent->pktlen; } + + return 0; } +/* NGTCP2_RECLAIM_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_RECLAIM_FLAG_NONE 0x00u +/* NGTCP2_RECLAIM_FLAG_ON_LOSS indicates that frames are reclaimed + because of the packet loss.*/ +#define NGTCP2_RECLAIM_FLAG_ON_LOSS 0x01u + /* * rtb_reclaim_frame queues unacknowledged frames included in |ent| * for retransmission. The re-queued frames are not deleted from - * |ent|. It returns the number of frames queued. + * |ent|. It returns the number of frames queued. |flags| is bitwise + * OR of 0 or more of NGTCP2_RECLAIM_FLAG_*. */ -static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, +static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags, + ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_rtb_entry *ent) { ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq; ngtcp2_frame *fr; @@ -310,9 +432,10 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_range gap, range; size_t num_reclaimed = 0; int rv; + int streamfrq_empty; + + assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE); - /* PADDING only (or PADDING + ACK ) packets will have NULL - ent->frc. */ /* TODO Reconsider the order of pfrc */ for (frc = ent->frc; frc; frc = frc->next) { fr = &frc->fr; @@ -334,13 +457,28 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, range.end = fr->stream.offset + ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt); range = ngtcp2_range_intersect(&range, &gap); - if (ngtcp2_range_len(&range) == 0 && - (!fr->stream.fin || (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED))) { - continue; + if (ngtcp2_range_len(&range) == 0) { + if (!fr->stream.fin) { + /* 0 length STREAM frame with offset == 0 must be + retransmitted if no non-empty data is sent to this stream + and no data in this stream is acknowledged. */ + if (fr->stream.offset != 0 || fr->stream.datacnt != 0 || + strm->tx.offset || (strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) { + continue; + } + } else if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { + continue; + } + } + + if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) && + ent->hd.pkt_num != strm->tx.last_lost_pkt_num) { + strm->tx.last_lost_pkt_num = ent->hd.pkt_num; + ++strm->tx.loss_count; } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->stream.datacnt, - rtb->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -349,9 +487,10 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data, fr->stream.datacnt); + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -361,6 +500,9 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } ++num_reclaimed; @@ -378,8 +520,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, continue; } - rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->crypto.datacnt, - rtb->mem); + rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new( + &nfrc, fr->crypto.datacnt, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -392,7 +534,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, &nfrc->fr.crypto.offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem); return rv; } @@ -400,8 +542,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, continue; case NGTCP2_FRAME_NEW_TOKEN: - rv = ngtcp2_frame_chain_new_token_new(&nfrc, &fr->new_token.token, - rtb->mem); + rv = ngtcp2_frame_chain_new_token_objalloc_new( + &nfrc, &fr->new_token.token, rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -412,8 +554,11 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, } break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + continue; default: - rv = ngtcp2_frame_chain_new(&nfrc, rtb->mem); + rv = ngtcp2_frame_chain_objalloc_new(&nfrc, rtb->frc_objalloc); if (rv != 0) { return rv; } @@ -438,11 +583,41 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return (ngtcp2_ssize)num_reclaimed; } +/* + * conn_process_lost_datagram calls ngtcp2_lost_datagram callback for + * lost DATAGRAM frames. + */ +static int conn_process_lost_datagram(ngtcp2_conn *conn, + ngtcp2_rtb_entry *ent) { + ngtcp2_frame_chain *frc; + int rv; + + for (frc = ent->frc; frc; frc = frc->next) { + switch (frc->fr.type) { + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + assert(conn->callbacks.lost_datagram); + + rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + break; + } + } + + return 0; +} + static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, - ngtcp2_rtb_entry *ent, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_tstamp ts) { + ngtcp2_rtb_entry *ent, ngtcp2_conn_stat *cstat, + ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_tstamp ts) { int rv; ngtcp2_ssize reclaimed; + ngtcp2_cc *cc = rtb->cc; + ngtcp2_cc_pkt pkt; ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, ent->ts); @@ -451,55 +626,60 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, ngtcp2_qlog_pkt_lost(rtb->qlog, ent); } - if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE)) { - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 " has already been reclaimed on PTO", - ent->hd.pkt_num); - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); - assert(UINT64_MAX == ent->lost_ts); - - ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; - ent->lost_ts = ts; - - ++rtb->num_lost_pkts; + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + ++rtb->num_lost_pmtud_pkts; + } else if (rtb->cc->on_pkt_lost) { + cc->on_pkt_lost(cc, cstat, + ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, + rtb->pktns_id, ent->ts, ent->rst.lost, + ent->rst.tx_in_flight, + ent->rst.is_app_limited), + ts); + } - ngtcp2_ksl_it_next(it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " has already been reclaimed on PTO", + ent->hd.pkt_num); + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(UINT64_MAX == ent->lost_ts); - return 0; - } + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = ts; - if (ent->frc) { - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); - assert(UINT64_MAX == ent->lost_ts); + ++rtb->num_lost_pkts; - reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); - if (reclaimed < 0) { - return (int)reclaimed; - } + ngtcp2_ksl_it_next(it); - if (reclaimed) { - ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; - ent->lost_ts = ts; + return 0; + } - ++rtb->num_lost_pkts; + if (conn->callbacks.lost_datagram && + (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) { + rv = conn_process_lost_datagram(conn, ent); + if (rv != 0) { + return rv; + } + } - ngtcp2_ksl_it_next(it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { + assert(ent->frc); + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(UINT64_MAX == ent->lost_ts); - return 0; - } + reclaimed = + rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_ON_LOSS, conn, pktns, ent); + if (reclaimed < 0) { + return (int)reclaimed; } - } else { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "pkn=%" PRId64 - " is a probe packet, no retransmission is necessary", - ent->hd.pkt_num); } - rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num); - assert(0 == rv); + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = ts; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ++rtb->num_lost_pkts; + + ngtcp2_ksl_it_next(it); return 0; } @@ -526,7 +706,9 @@ static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, ngtcp2_rtb_entry **pent, ngtcp2_rtb_entry *ent, ngtcp2_conn_stat *cstat) { int rv; - rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num); + (void)rv; + + rv = ngtcp2_ksl_remove_hint(&rtb->ents, it, it, &ent->hd.pkt_num); assert(0 == rv); rtb_on_remove(rtb, ent, cstat); @@ -535,6 +717,35 @@ static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, ngtcp2_list_insert(ent, pent); } +static void conn_ack_crypto_data(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + uint64_t datalen) { + ngtcp2_buf_chain **pbufchain, *bufchain; + size_t left; + + for (pbufchain = &pktns->crypto.tx.data; *pbufchain;) { + left = ngtcp2_buf_len(&(*pbufchain)->buf); + if (left > datalen) { + (*pbufchain)->buf.pos += datalen; + return; + } + + bufchain = *pbufchain; + *pbufchain = bufchain->next; + + ngtcp2_mem_free(conn->mem, bufchain); + + datalen -= left; + + if (datalen == 0) { + return; + } + } + + assert(datalen == 0); + + return; +} + static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, ngtcp2_conn *conn) { ngtcp2_frame_chain *frc; @@ -543,7 +754,19 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, int rv; uint64_t datalen; ngtcp2_strm *crypto = rtb->crypto; - ngtcp2_crypto_level crypto_level; + ngtcp2_pktns *pktns = NULL; + + if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && conn->pmtud && + conn->pmtud->tx_pkt_num <= ent->hd.pkt_num) { + ngtcp2_pmtud_probe_success(conn->pmtud, ent->pktlen); + + conn->dcid.current.max_udp_payload_size = + ngtcp2_max(conn->dcid.current.max_udp_payload_size, ent->pktlen); + + if (ngtcp2_pmtud_finished(conn->pmtud)) { + ngtcp2_conn_stop_pmtud(conn); + } + } for (frc = ent->frc; frc; frc = frc->next) { if (frc->binder) { @@ -557,6 +780,8 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, break; } + strm->flags |= NGTCP2_STRM_FLAG_ANY_ACKED; + if (frc->fr.stream.fin) { strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED; } @@ -584,7 +809,7 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, } } - rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); if (rv != 0) { return rv; } @@ -598,33 +823,28 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, return rv; } - if (conn->callbacks.acked_crypto_offset) { - stream_offset = ngtcp2_strm_get_acked_offset(crypto); - datalen = stream_offset - prev_stream_offset; - if (datalen == 0) { - break; - } - - switch (rtb->pktns_id) { - case NGTCP2_PKTNS_ID_INITIAL: - crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; - break; - case NGTCP2_PKTNS_ID_HANDSHAKE: - crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; - break; - case NGTCP2_PKTNS_ID_APPLICATION: - crypto_level = NGTCP2_CRYPTO_LEVEL_APPLICATION; - break; - default: - assert(0); - } + stream_offset = ngtcp2_strm_get_acked_offset(crypto); + datalen = stream_offset - prev_stream_offset; + if (datalen == 0) { + break; + } - rv = conn->callbacks.acked_crypto_offset( - conn, crypto_level, prev_stream_offset, datalen, conn->user_data); - if (rv != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } + switch (rtb->pktns_id) { + case NGTCP2_PKTNS_ID_INITIAL: + pktns = conn->in_pktns; + break; + case NGTCP2_PKTNS_ID_HANDSHAKE: + pktns = conn->hs_pktns; + break; + case NGTCP2_PKTNS_ID_APPLICATION: + pktns = &conn->pktns; + break; + default: + assert(0); } + + conn_ack_crypto_data(conn, pktns, datalen); + break; case NGTCP2_FRAME_RESET_STREAM: strm = ngtcp2_conn_find_stream(conn, frc->fr.reset_stream.stream_id); @@ -632,14 +852,26 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, break; } strm->flags |= NGTCP2_STRM_FLAG_RST_ACKED; - rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); if (rv != 0) { return rv; } break; case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - assert(conn->dcid.num_retire_queued); - --conn->dcid.num_retire_queued; + ngtcp2_conn_untrack_retired_dcid_seq(conn, + frc->fr.retire_connection_id.seq); + break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + if (!conn->callbacks.ack_datagram) { + break; + } + + rv = conn->callbacks.ack_datagram(conn, frc->fr.datagram.dgram_id, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } break; } } @@ -655,7 +887,9 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, cc->on_pkt_acked(cc, cstat, ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, - rtb->pktns_id, ent->ts), + rtb->pktns_id, ent->ts, ent->rst.lost, + ent->rst.tx_in_flight, + ent->rst.is_app_limited), ts); if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) && @@ -705,6 +939,10 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns, } } +static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost, + ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts, @@ -723,10 +961,17 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, int ack_eliciting_pkt_acked = 0; size_t ecn_acked = 0; int verify_ecn = 0; + ngtcp2_cc_ack cc_ack = {0}; + size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts; + + cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight; + cc_ack.rtt = UINT64_MAX; if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) && + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) && largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) { - conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + conn->flags &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED | + NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR); conn->crypto.key_update.confirmed_ts = ts; ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); @@ -740,7 +985,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack); if (ngtcp2_ksl_it_end(&it)) { - if (verify_ecn) { + if (conn && verify_ecn) { conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, largest_acked_sent_ts, ts); } @@ -800,15 +1045,14 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, } if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) { - ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, - fr->ack_delay_unscaled, ts); - if (cc->new_rtt_sample) { + cc_ack.rtt = pkt_ts - largest_pkt_sent_ts; + + rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts); + if (rv == 0 && cc->new_rtt_sample) { cc->new_rtt_sample(cc, cstat, ts); } } - ngtcp2_rst_on_ack_recv(rtb->rst, cstat); - if (conn) { for (ent = acked_ent; ent; ent = acked_ent) { if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num && @@ -826,9 +1070,17 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, goto fail; } + if (ent->hd.pkt_num >= rtb->cc_pkt_num) { + assert(cc_ack.pkt_delivered <= ent->rst.delivered); + + cc_ack.bytes_delivered += ent->pktlen; + cc_ack.pkt_delivered = ent->rst.delivered; + } + rtb_on_pkt_acked(rtb, ent, cstat, ts); acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } if (verify_ecn) { @@ -840,29 +1092,48 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, for (ent = acked_ent; ent; ent = acked_ent) { rtb_on_pkt_acked(rtb, ent, cstat, ts); acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); + } + } + + if (rtb->cc->on_spurious_congestion && num_lost_pkts && + rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts == 0) { + rtb->cc->on_spurious_congestion(cc, cstat, ts); + } + + ngtcp2_rst_on_ack_recv(rtb->rst, cstat, cc_ack.pkt_delivered); + + if (conn && num_acked > 0) { + rv = rtb_detect_lost_pkt(rtb, &cc_ack.bytes_lost, conn, pktns, cstat, ts); + if (rv != 0) { + return rv; } } - cc->on_ack_recv(cc, cstat, ts); + rtb->rst->lost += cc_ack.bytes_lost; + + cc_ack.largest_acked_sent_ts = largest_acked_sent_ts; + cc->on_ack_recv(cc, cstat, &cc_ack, ts); return num_acked; fail: for (ent = acked_ent; ent; ent = acked_ent) { acked_ent = ent->next; - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } return rv; } static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat, - const ngtcp2_rtb_entry *ent, uint64_t loss_delay, - ngtcp2_tstamp lost_send_time, uint64_t pkt_thres) { + const ngtcp2_rtb_entry *ent, ngtcp2_duration loss_delay, + size_t pkt_thres, ngtcp2_tstamp ts) { ngtcp2_tstamp loss_time; - if (ent->ts <= lost_send_time || + if (ent->ts + loss_delay <= ts || rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) { return 1; } @@ -906,12 +1177,11 @@ static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) { pktns->tx.ecn.validation_pkt_sent == pktns->tx.ecn.validation_pkt_lost; } -int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, - ngtcp2_duration pto, ngtcp2_tstamp ts) { +static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost, + ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { ngtcp2_rtb_entry *ent; ngtcp2_duration loss_delay; - ngtcp2_tstamp lost_send_time; ngtcp2_ksl_it it; ngtcp2_tstamp latest_ts, oldest_ts; int64_t last_lost_pkt_num; @@ -922,11 +1192,14 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, rtb->cc_bytes_in_flight / cstat->max_udp_payload_size / 2; size_t ecn_pkt_lost = 0; ngtcp2_tstamp start_ts; + ngtcp2_duration pto = ngtcp2_conn_compute_pto(conn, pktns); + uint64_t bytes_lost = 0; + ngtcp2_duration max_ack_delay; pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD); + pkt_thres = ngtcp2_min(pkt_thres, 256); cstat->loss_time[rtb->pktns_id] = UINT64_MAX; loss_delay = compute_pkt_loss_delay(cstat); - lost_send_time = ts - loss_delay; it = ngtcp2_ksl_lower_bound(&rtb->ents, &rtb->largest_acked_tx_pkt_num); for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { @@ -936,15 +1209,18 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, break; } - if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, lost_send_time, pkt_thres)) { + if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, (size_t)pkt_thres, ts)) { /* All entries from ent are considered to be lost. */ latest_ts = oldest_ts = ent->ts; last_lost_pkt_num = ent->hd.pkt_num; + max_ack_delay = conn->remote.transport_params + ? conn->remote.transport_params->max_ack_delay + : 0; - congestion_period = (cstat->smoothed_rtt + - ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) + - conn->remote.transport_params.max_ack_delay) * - NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; + congestion_period = + (cstat->smoothed_rtt + + ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) + max_ack_delay) * + NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; start_ts = ngtcp2_max(rtb->persistent_congestion_start_ts, cstat->first_rtt_sample_ts); @@ -974,13 +1250,19 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ++ecn_pkt_lost; } - rtb_on_remove(rtb, ent, cstat); - rv = rtb_on_pkt_lost(rtb, &it, ent, conn, pktns, ts); + bytes_lost += rtb_on_remove(rtb, ent, cstat); + rv = rtb_on_pkt_lost(rtb, &it, ent, cstat, conn, pktns, ts); if (rv != 0) { return rv; } } + /* If only PMTUD packets are lost, do not trigger congestion + event. */ + if (bytes_lost == 0) { + break; + } + switch (conn->tx.ecn.state) { case NGTCP2_ECN_STATE_TESTING: if (conn->tx.ecn.validation_start_ts == UINT64_MAX) { @@ -1038,15 +1320,27 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, } } - ngtcp2_rtb_remove_excessive_lost_pkt(rtb, pkt_thres); + ngtcp2_rtb_remove_excessive_lost_pkt(rtb, (size_t)pkt_thres); + + if (ppkt_lost) { + *ppkt_lost = bytes_lost; + } return 0; } +int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + return rtb_detect_lost_pkt(rtb, /* ppkt_lost = */ NULL, conn, pktns, cstat, + ts); +} + void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { ngtcp2_ksl_it it = ngtcp2_ksl_end(&rtb->ents); ngtcp2_rtb_entry *ent; int rv; + (void)rv; for (; rtb->num_lost_pkts > n;) { assert(ngtcp2_ksl_it_end(&it)); @@ -1059,9 +1353,15 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); --rtb->num_lost_pkts; - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + --rtb->num_lost_pmtud_pkts; + } + + rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); } } @@ -1070,6 +1370,7 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, ngtcp2_ksl_it it; ngtcp2_rtb_entry *ent; int rv; + (void)rv; if (ngtcp2_ksl_len(&rtb->ents) == 0) { return; @@ -1092,9 +1393,15 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); --rtb->num_lost_pkts; - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + --rtb->num_lost_pmtud_pkts; + } + + rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); if (ngtcp2_ksl_len(&rtb->ents) == 0) { return; @@ -1128,6 +1435,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_stream *sfr; ngtcp2_strm *strm; int rv; + int streamfrq_empty; ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, ent->ts); @@ -1144,19 +1452,21 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } - if (!ent->frc) { - /* PADDING only (or PADDING + ACK ) packets will have NULL - ent->frc. */ - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); - assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 + " is a PMTUD probe packet, no retransmission is necessary", + ent->hd.pkt_num); return 0; } if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { --rtb->num_lost_pkts; - } - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { + --rtb->num_lost_pmtud_pkts; + } + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, "pkn=%" PRId64 " was declared lost and has already been retransmitted", @@ -1171,6 +1481,14 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && + (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) || + !conn->callbacks.lost_datagram)) { + /* PADDING only (or PADDING + ACK ) packets will have NULL + ent->frc. */ + return 0; + } + pfrc = &ent->frc; for (; *pfrc;) { @@ -1184,12 +1502,13 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); if (!strm) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); break; } + streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm); rv = ngtcp2_strm_streamfrq_push(strm, frc); if (rv != 0) { - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); return rv; } if (!ngtcp2_strm_is_tx_queued(strm)) { @@ -1199,6 +1518,9 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return rv; } } + if (streamfrq_empty) { + ++conn->tx.strmq_nretrans; + } break; case NGTCP2_FRAME_CRYPTO: frc = *pfrc; @@ -1210,10 +1532,26 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, &frc->fr.crypto.offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, conn->mem); + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); return rv; } break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + frc = *pfrc; + + if (conn->callbacks.lost_datagram) { + rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + *pfrc = (*pfrc)->next; + + ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); + break; default: pfrc = &(*pfrc)->next; } @@ -1238,11 +1576,12 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ent = ngtcp2_ksl_it_get(&it); rtb_on_remove(rtb, ent, cstat); - rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent); - ngtcp2_rtb_entry_del(ent, rtb->mem); + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); if (rv != 0) { return rv; } @@ -1251,6 +1590,31 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } +void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat) { + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + int rv; + (void)rv; + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + + if (ent->hd.type != NGTCP2_PKT_0RTT) { + ngtcp2_ksl_it_next(&it); + continue; + } + + rtb_on_remove(rtb, ent, cstat); + rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); + assert(0 == rv); + + ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, + rtb->frc_objalloc, rtb->mem); + } +} + int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) { return ngtcp2_ksl_len(&rtb->ents) == 0; } @@ -1280,7 +1644,8 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, assert(ent->frc); - reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); + reclaimed = + rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_NONE, conn, pktns, ent); if (reclaimed < 0) { return reclaimed; } @@ -1292,6 +1657,12 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, assert(rtb->num_retransmittable); --rtb->num_retransmittable; + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { + ent->flags &= (uint16_t)~NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING; + assert(rtb->num_pto_eliciting); + --rtb->num_pto_eliciting; + } + if (reclaimed) { --num_pkts; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h index 70f43ffd92416a..a97805dbaf3bc3 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h @@ -34,6 +34,7 @@ #include "ngtcp2_pkt.h" #include "ngtcp2_ksl.h" #include "ngtcp2_pq.h" +#include "ngtcp2_objalloc.h" typedef struct ngtcp2_conn ngtcp2_conn; typedef struct ngtcp2_pktns ngtcp2_pktns; @@ -41,13 +42,14 @@ typedef struct ngtcp2_log ngtcp2_log; typedef struct ngtcp2_qlog ngtcp2_qlog; typedef struct ngtcp2_strm ngtcp2_strm; typedef struct ngtcp2_rst ngtcp2_rst; +typedef struct ngtcp2_cc ngtcp2_cc; /* NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00 +#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00u /* NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK indicates that an information which a frame carries has been acknowledged. */ -#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01 +#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01u /* * ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to @@ -71,11 +73,19 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; * ngtcp2_frame_chain chains frames in a single packet. */ struct ngtcp2_frame_chain { - ngtcp2_frame_chain *next; - ngtcp2_frame_chain_binder *binder; - ngtcp2_frame fr; + union { + struct { + ngtcp2_frame_chain *next; + ngtcp2_frame_chain_binder *binder; + ngtcp2_frame fr; + }; + + ngtcp2_opl_entry oplent; + }; }; +ngtcp2_objalloc_def(frame_chain, ngtcp2_frame_chain, oplent); + /* * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using * new or existing ngtcp2_frame_chain_binder. |a| might have non-NULL @@ -110,6 +120,13 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, */ int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem); +/* + * ngtcp2_frame_chain_objalloc_new behaves like + * ngtcp2_frame_chain_new, but it uses |objalloc| to allocate the object. + */ +int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, + ngtcp2_objalloc *objalloc); + /* * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new, * but it allocates extra memory |extralen| in order to extend @@ -119,30 +136,33 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, const ngtcp2_mem *mem); /* - * ngtcp2_frame_chain_stream_datacnt_new works like + * ngtcp2_frame_chain_stream_datacnt_objalloc_new works like * ngtcp2_frame_chain_new, but it allocates enough data to store * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream - * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called - * internally. + * object. If no additional space is required, + * ngtcp2_frame_chain_objalloc_new is called internally. */ -int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* - * ngtcp2_frame_chain_crypto_datacnt_new works like + * ngtcp2_frame_chain_crypto_datacnt_objalloc_new works like * ngtcp2_frame_chain_new, but it allocates enough data to store * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto - * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called - * internally. + * object. If no additional space is required, + * ngtcp2_frame_chain_objalloc_new is called internally. */ -int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, - size_t datacnt, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); -int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, - const ngtcp2_vec *token, - const ngtcp2_mem *mem); +int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, + const ngtcp2_vec *token, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the @@ -150,43 +170,62 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, */ void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem); +/* + * ngtcp2_frame_chain_objalloc_del adds |frc| to |objalloc| for reuse. + * It might just delete |frc| depending on the frame type and the size + * of |frc|. + */ +void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); + /* * ngtcp2_frame_chain_init initializes |frc|. */ void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc); /* - * ngtcp2_frame_chain_list_del deletes |frc|, and all objects - * connected by next field. + * ngtcp2_frame_chain_list_objalloc_del adds all ngtcp2_frame_chain + * linked from |frc| to |objalloc| for reuse. Depending on the frame type + * and its size, ngtcp2_frame_chain might be deleted instead. */ -void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, - const ngtcp2_mem *mem); +void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc, + ngtcp2_objalloc *objalloc, + const ngtcp2_mem *mem); /* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00 +#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00u /* NGTCP2_RTB_ENTRY_FLAG_PROBE indicates that the entry includes a probe packet. */ -#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01 +#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01u /* NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE indicates that the entry includes a frame which must be retransmitted until it is acknowledged. In most cases, this flag is used along with - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING. We have these 2 flags because - NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE triggers PTO, but just - NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING does not. */ -#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02 + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING and + NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING. */ +#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02u /* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry elicits acknowledgement. */ -#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04 +#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04u /* NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED indicates that the packet has been reclaimed on PTO. It is not marked lost yet and still consumes congestion window. */ -#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08 +#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08u /* NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED indicates that the entry - has been marked lost and scheduled to retransmit. */ -#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10 + has been marked lost and, optionally, scheduled to retransmit. */ +#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10u /* NGTCP2_RTB_ENTRY_FLAG_ECN indicates that the entry is included in a UDP datagram with ECN marking. */ -#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20 +#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20u +/* NGTCP2_RTB_ENTRY_FLAG_DATAGRAM indicates that the entry includes + DATAGRAM frame. */ +#define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40u +/* NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE indicates that the entry includes + a PMTUD probe packet. */ +#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80u +/* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry + includes a packet which elicits PTO probe packets. */ +#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100u typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; @@ -195,58 +234,69 @@ typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; * to the one packet which is waiting for its ACK. */ struct ngtcp2_rtb_entry { - ngtcp2_rtb_entry *next; - - struct { - int64_t pkt_num; - uint8_t type; - uint8_t flags; - } hd; - ngtcp2_frame_chain *frc; - /* ts is the time point when a packet included in this entry is sent - to a peer. */ - ngtcp2_tstamp ts; - /* lost_ts is the time when this entry is marked lost. */ - ngtcp2_tstamp lost_ts; - /* pktlen is the length of QUIC packet */ - size_t pktlen; - struct { - uint64_t delivered; - ngtcp2_tstamp delivered_ts; - ngtcp2_tstamp first_sent_ts; - int is_app_limited; - } rst; - /* flags is bitwise-OR of zero or more of - NGTCP2_RTB_ENTRY_FLAG_*. */ - uint8_t flags; + union { + struct { + ngtcp2_rtb_entry *next; + + struct { + int64_t pkt_num; + uint8_t type; + uint8_t flags; + } hd; + ngtcp2_frame_chain *frc; + /* ts is the time point when a packet included in this entry is sent + to a peer. */ + ngtcp2_tstamp ts; + /* lost_ts is the time when this entry is marked lost. */ + ngtcp2_tstamp lost_ts; + /* pktlen is the length of QUIC packet */ + size_t pktlen; + struct { + uint64_t delivered; + ngtcp2_tstamp delivered_ts; + ngtcp2_tstamp first_sent_ts; + uint64_t tx_in_flight; + uint64_t lost; + int is_app_limited; + } rst; + /* flags is bitwise-OR of zero or more of + NGTCP2_RTB_ENTRY_FLAG_*. */ + uint16_t flags; + }; + + ngtcp2_opl_entry oplent; + }; }; +ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent); + /* * ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns - * its pointer to |*pent|. On success, |*pent| takes ownership of - * |frc|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. + * its pointer to |*pent|. */ -int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, - ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, - size_t pktlen, uint8_t flags, const ngtcp2_mem *mem); +int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, + const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint16_t flags, + ngtcp2_objalloc *objalloc); /* - * ngtcp2_rtb_entry_del deallocates |ent|. It also frees memory - * pointed by |ent|. + * ngtcp2_rtb_entry_objalloc_del adds |ent| to |objalloc| for reuse. + * ngtcp2_frame_chain linked from ent->frc are also added to + * |frc_objalloc| depending on their frame type and size. */ -void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem); +void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent, + ngtcp2_objalloc *objalloc, + ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_rtb tracks sent packets, and its ACK timeout for * retransmission. */ typedef struct ngtcp2_rtb { + ngtcp2_objalloc *frc_objalloc; + ngtcp2_objalloc *rtb_entry_objalloc; /* ents includes ngtcp2_rtb_entry sorted by decreasing order of packet number. */ ngtcp2_ksl ents; @@ -265,6 +315,9 @@ typedef struct ngtcp2_rtb { /* num_retransmittable is the number of packets which contain frames that must be retransmitted on loss. */ size_t num_retransmittable; + /* num_pto_eliciting is the number of packets that elicit PTO probe + packets. */ + size_t num_pto_eliciting; /* probe_pkt_left is the number of probe packet to send */ size_t probe_pkt_left; /* pktns_id is the identifier of packet number space. */ @@ -283,6 +336,10 @@ typedef struct ngtcp2_rtb { /* num_lost_pkts is the number entries in ents which has NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */ size_t num_lost_pkts; + /* num_lost_pmtud_pkts is the number of entries in ents which have + both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and + NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */ + size_t num_lost_pmtud_pkts; } ngtcp2_rtb; /* @@ -290,7 +347,9 @@ typedef struct ngtcp2_rtb { */ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, - ngtcp2_log *log, ngtcp2_qlog *qlog, const ngtcp2_mem *mem); + ngtcp2_log *log, ngtcp2_qlog *qlog, + ngtcp2_objalloc *rtb_entry_objalloc, + ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem); /* * ngtcp2_rtb_free deallocates resources allocated for |rtb|. @@ -344,7 +403,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, */ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, - ngtcp2_duration pto, ngtcp2_tstamp ts); + ngtcp2_tstamp ts); /* * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet. @@ -367,6 +426,11 @@ ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb); int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); +/* + * ngtcp2_rtb_remove_early_data removes all entries for 0RTT packets. + */ +void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat); + /* * ngtcp2_rtb_empty returns nonzero if |rtb| have no entry. */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c index 3118955b248902..c1ce64a2e57ac4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c @@ -216,20 +216,6 @@ uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr) { return dest; } -int ngtcp2_verify_stateless_reset_token(const uint8_t *want, - const uint8_t *got) { - 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/ngtcp2/lib/ngtcp2_str.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h index bd0145747c8f54..04735d6dec5c63 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h @@ -77,25 +77,6 @@ uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr); char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, size_t len); -/* - * ngtcp2_verify_stateless_reset_token verifies stateless reset token - * |want| and |got|. This function returns 0 if |want| equals |got| - * and |got| is not all zero, or one of the following negative error - * codes: - * - * NGTCP2_ERR_INVALID_ARGUMENT - * Token does not match; or token is all zero. - */ -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 diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c index 8e8eef0c9c9c93..6f20e866ad51c0 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c @@ -35,9 +35,11 @@ static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { return *(int64_t *)lhs < *(int64_t *)rhs; } -int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, - uint64_t max_rx_offset, uint64_t max_tx_offset, - void *stream_user_data, const ngtcp2_mem *mem) { +void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem) { + strm->frc_objalloc = frc_objalloc; strm->cycle = 0; strm->tx.acked_offset = NULL; strm->tx.cont_acked_offset = 0; @@ -45,6 +47,8 @@ int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, strm->tx.offset = 0; strm->tx.max_offset = max_tx_offset; strm->tx.last_max_stream_data_ts = UINT64_MAX; + strm->tx.loss_count = 0; + strm->tx.last_lost_pkt_num = -1; strm->rx.rob = NULL; strm->rx.cont_offset = 0; strm->rx.last_offset = 0; @@ -53,12 +57,9 @@ int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, strm->stream_user_data = stream_user_data; strm->rx.window = strm->rx.max_offset = strm->rx.unsent_max_offset = max_rx_offset; - strm->me.key = (uint64_t)stream_id; strm->pe.index = NGTCP2_PQ_BAD_INDEX; strm->mem = mem; strm->app_error_code = 0; - - return 0; } void ngtcp2_strm_free(ngtcp2_strm *strm) { @@ -71,17 +72,23 @@ void ngtcp2_strm_free(ngtcp2_strm *strm) { if (strm->tx.streamfrq) { for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - ngtcp2_frame_chain_del(ngtcp2_ksl_it_get(&it), strm->mem); + ngtcp2_frame_chain_objalloc_del(ngtcp2_ksl_it_get(&it), + strm->frc_objalloc, strm->mem); } ngtcp2_ksl_free(strm->tx.streamfrq); ngtcp2_mem_free(strm->mem, strm->tx.streamfrq); } - ngtcp2_rob_free(strm->rx.rob); - ngtcp2_mem_free(strm->mem, strm->rx.rob); - ngtcp2_gaptr_free(strm->tx.acked_offset); - ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); + if (strm->rx.rob) { + ngtcp2_rob_free(strm->rx.rob); + ngtcp2_mem_free(strm->mem, strm->rx.rob); + } + + if (strm->tx.acked_offset) { + ngtcp2_gaptr_free(strm->tx.acked_offset); + ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); + } } static int strm_rob_init(ngtcp2_strm *strm) { @@ -155,17 +162,12 @@ void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) { } static int strm_streamfrq_init(ngtcp2_strm *strm) { - int rv; ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq)); if (streamfrq == NULL) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, streamfrq); - return rv; - } + ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); strm->tx.streamfrq = streamfrq; @@ -210,7 +212,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, frc = ngtcp2_ksl_it_get(&it); fr = &frc->fr.stream; - ngtcp2_ksl_remove(strm->tx.streamfrq, &it, &fr->offset); + ngtcp2_ksl_remove_hint(strm->tx.streamfrq, &it, &it, &fr->offset); idx = 0; offset = fr->offset; @@ -234,19 +236,27 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, if (idx == fr->datacnt) { if (fr->fin) { if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0); return 0; } - fr->offset = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt); + fr->offset += ngtcp2_vec_len(fr->data, fr->datacnt); fr->datacnt = 0; *pfrc = frc; return 0; } - ngtcp2_frame_chain_del(frc, strm->mem); + + if (fr->offset == 0 && fr->datacnt == 0 && strm->tx.offset == 0 && + !(strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) { + *pfrc = frc; + + return 0; + } + + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); continue; } @@ -285,10 +295,10 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, return 0; } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->datacnt - end_idx, - strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, fr->datacnt - end_idx, strm->frc_objalloc, strm->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -310,8 +320,8 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -346,7 +356,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, ngtcp2_frame_chain *frc, *nfrc; int rv; size_t nmerged; - size_t datalen; + uint64_t datalen; ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT]; ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT]; size_t acnt, bcnt; @@ -375,7 +385,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } *pfrc = NULL; @@ -393,10 +403,11 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, assert(acnt > 0); assert(bcnt > 0); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, bcnt, strm->frc_objalloc, strm->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -412,15 +423,16 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, acnt, strm->frc_objalloc, strm->mem); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -430,14 +442,14 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); *pfrc = nfrc; return 0; } - left -= datalen; + left -= (size_t)datalen; ngtcp2_vec_copy(a, fr->data, fr->datacnt); acnt = fr->datacnt; @@ -452,7 +464,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = strm_streamfrq_unacked_pop(strm, &nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } if (nfrc == NULL) { @@ -463,7 +475,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, if (nfr->fin && nfr->datacnt == 0) { fr->fin = 1; - ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); break; } @@ -473,8 +485,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } break; @@ -485,7 +497,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, if (nfr->datacnt == 0) { fr->fin = nfr->fin; - ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); continue; } @@ -493,8 +505,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); if (rv != 0) { - ngtcp2_frame_chain_del(nfrc, strm->mem); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -512,9 +524,10 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, assert(acnt > fr->datacnt); - rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &nfrc, acnt, strm->frc_objalloc, strm->mem); if (rv != 0) { - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); return rv; } @@ -523,7 +536,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, nfr->datacnt = acnt; ngtcp2_vec_copy(nfr->data, a, acnt); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); *pfrc = nfrc; @@ -535,7 +548,7 @@ uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm) { ngtcp2_stream *fr; ngtcp2_range gap; ngtcp2_ksl_it it; - size_t datalen; + uint64_t datalen; assert(strm->tx.streamfrq); assert(ngtcp2_ksl_len(strm->tx.streamfrq)); @@ -589,7 +602,7 @@ void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) { for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { frc = ngtcp2_ksl_it_get(&it); - ngtcp2_frame_chain_del(frc, strm->mem); + ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); } ngtcp2_ksl_clear(strm->tx.streamfrq); } @@ -607,9 +620,13 @@ int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) { strm->tx.offset; } +int ngtcp2_strm_is_all_tx_data_fin_acked(ngtcp2_strm *strm) { + return (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) && + ngtcp2_strm_is_all_tx_data_acked(strm); +} + ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, uint64_t offset) { - ngtcp2_ksl_it gapit; ngtcp2_range gap; if (strm->tx.acked_offset == NULL) { @@ -618,8 +635,7 @@ ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, return gap; } - gapit = ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); - return *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit); + return ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); } uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) { @@ -631,7 +647,6 @@ uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) { } static int strm_acked_offset_init(ngtcp2_strm *strm) { - int rv; ngtcp2_gaptr *acked_offset = ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset)); @@ -639,11 +654,7 @@ static int strm_acked_offset_init(ngtcp2_strm *strm) { return NGTCP2_ERR_NOMEM; } - rv = ngtcp2_gaptr_init(acked_offset, strm->mem); - if (rv != 0) { - ngtcp2_mem_free(strm->mem, acked_offset); - return rv; - } + ngtcp2_gaptr_init(acked_offset, strm->mem); strm->tx.acked_offset = acked_offset; @@ -673,3 +684,15 @@ int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) { return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); } + +void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, + uint64_t app_error_code) { + if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) { + return; + } + + assert(0 == strm->app_error_code); + + strm->flags |= NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET; + strm->app_error_code = app_error_code; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h index 6b7418706c760e..8e3cfe83543509 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h @@ -40,110 +40,138 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; /* NGTCP2_STRM_FLAG_NONE indicates that no flag is set. */ -#define NGTCP2_STRM_FLAG_NONE 0x00 +#define NGTCP2_STRM_FLAG_NONE 0x00u /* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of stream data is not allowed. */ -#define NGTCP2_STRM_FLAG_SHUT_RD 0x01 +#define NGTCP2_STRM_FLAG_SHUT_RD 0x01u /* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of stream data is not allowed. */ -#define NGTCP2_STRM_FLAG_SHUT_WR 0x02 +#define NGTCP2_STRM_FLAG_SHUT_WR 0x02u #define NGTCP2_STRM_FLAG_SHUT_RDWR \ (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR) /* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is sent from the local endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_WR is also set. */ -#define NGTCP2_STRM_FLAG_SENT_RST 0x04 +#define NGTCP2_STRM_FLAG_SENT_RST 0x04u /* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is received from the remote endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_RD is also set. */ -#define NGTCP2_STRM_FLAG_RECV_RST 0x08 +#define NGTCP2_STRM_FLAG_RECV_RST 0x08u /* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent from the local endpoint. */ -#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10 +#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10u /* NGTCP2_STRM_FLAG_RST_ACKED indicates that the outgoing RST_STREAM is acknowledged by peer. */ -#define NGTCP2_STRM_FLAG_RST_ACKED 0x20 +#define NGTCP2_STRM_FLAG_RST_ACKED 0x20u /* NGTCP2_STRM_FLAG_FIN_ACKED indicates that a STREAM with FIN bit set is acknowledged by a remote endpoint. */ -#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40 +#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40u +/* NGTCP2_STRM_FLAG_ANY_ACKED indicates that any portion of stream + data, including 0 length segment, is acknowledged. */ +#define NGTCP2_STRM_FLAG_ANY_ACKED 0x80u +/* NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET indicates that app_error_code + field is set. This resolves the ambiguity that the initial + app_error_code value 0 might be a proper application error code. + In this case, without this flag, we are unable to distinguish + assigned value from unassigned one. */ +#define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100u +/* NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED is set when + stream_stop_sending callback is called. */ +#define NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED 0x200u typedef struct ngtcp2_strm ngtcp2_strm; struct ngtcp2_strm { - ngtcp2_map_entry me; - ngtcp2_pq_entry pe; - uint64_t cycle; + union { + struct { + ngtcp2_pq_entry pe; + uint64_t cycle; + ngtcp2_objalloc *frc_objalloc; - struct { - /* acked_offset tracks acknowledged outgoing data. */ - ngtcp2_gaptr *acked_offset; - /* cont_acked_offset is the offset that all data up to this offset - is acknowledged by a remote endpoint. It is used until the - remote endpoint acknowledges data in out-of-order. After that, - acked_offset is used instead. */ - uint64_t cont_acked_offset; - /* streamfrq contains STREAM frame for retransmission. The flow - control credits have been paid when they are transmitted first - time. There are no restriction regarding flow control for - retransmission. */ - ngtcp2_ksl *streamfrq; - /* offset is the next offset of outgoing data. In other words, it - is the number of bytes sent in this stream without - duplication. */ - uint64_t offset; - /* max_tx_offset is the maximum offset that local endpoint can - send for this stream. */ - uint64_t max_offset; - /* last_max_stream_data_ts is the timestamp when last - MAX_STREAM_DATA frame is sent. */ - ngtcp2_tstamp last_max_stream_data_ts; - } tx; + struct { + /* acked_offset tracks acknowledged outgoing data. */ + ngtcp2_gaptr *acked_offset; + /* cont_acked_offset is the offset that all data up to this offset + is acknowledged by a remote endpoint. It is used until the + remote endpoint acknowledges data in out-of-order. After that, + acked_offset is used instead. */ + uint64_t cont_acked_offset; + /* streamfrq contains STREAM frame for retransmission. The flow + control credits have been paid when they are transmitted first + time. There are no restriction regarding flow control for + retransmission. */ + ngtcp2_ksl *streamfrq; + /* offset is the next offset of outgoing data. In other words, it + is the number of bytes sent in this stream without + duplication. */ + uint64_t offset; + /* max_tx_offset is the maximum offset that local endpoint can + send for this stream. */ + uint64_t max_offset; + /* last_max_stream_data_ts is the timestamp when last + MAX_STREAM_DATA frame is sent. */ + ngtcp2_tstamp last_max_stream_data_ts; + /* loss_count is the number of packets that contain STREAM + frame for this stream and are declared to be lost. It may + include the spurious losses. It does not include a packet + whose contents have been reclaimed for PTO and which is + later declared to be lost. Those data are not blocked by + the flow control and will be sent immediately if no other + restrictions are applied. */ + size_t loss_count; + /* last_lost_pkt_num is the packet number of the packet that + is counted to loss_count. It is used to avoid to count + multiple STREAM frames in one lost packet. */ + int64_t last_lost_pkt_num; + } tx; - struct { - /* rob is the reorder buffer for incoming stream data. The data - received in out of order is buffered and sorted by its offset - in this object. */ - ngtcp2_rob *rob; - /* cont_offset is the largest offset of consecutive data. It is - used until the endpoint receives out-of-order data. After - that, rob is used to track the offset and data. */ - uint64_t cont_offset; - /* last_offset is the largest offset of stream data received for - this stream. */ - uint64_t last_offset; - /* max_offset is the maximum offset that remote endpoint can send - to this stream. */ - uint64_t max_offset; - /* unsent_max_offset is the maximum offset that remote endpoint - can send to this stream, and it is not notified to the remote - endpoint. unsent_max_offset >= max_offset must be hold. */ - uint64_t unsent_max_offset; - /* window is the stream-level flow control window size. */ - uint64_t window; - } rx; + struct { + /* rob is the reorder buffer for incoming stream data. The data + received in out of order is buffered and sorted by its offset + in this object. */ + ngtcp2_rob *rob; + /* cont_offset is the largest offset of consecutive data. It is + used until the endpoint receives out-of-order data. After + that, rob is used to track the offset and data. */ + uint64_t cont_offset; + /* last_offset is the largest offset of stream data received for + this stream. */ + uint64_t last_offset; + /* max_offset is the maximum offset that remote endpoint can send + to this stream. */ + uint64_t max_offset; + /* unsent_max_offset is the maximum offset that remote endpoint + can send to this stream, and it is not notified to the remote + endpoint. unsent_max_offset >= max_offset must be hold. */ + uint64_t unsent_max_offset; + /* window is the stream-level flow control window size. */ + uint64_t window; + } rx; - const ngtcp2_mem *mem; - int64_t stream_id; - void *stream_user_data; - /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */ - uint32_t flags; - /* app_error_code is an error code the local endpoint sent in - RST_STREAM or STOP_SENDING. */ - uint64_t app_error_code; + const ngtcp2_mem *mem; + int64_t stream_id; + void *stream_user_data; + /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */ + uint32_t flags; + /* app_error_code is an error code the local endpoint sent in + RESET_STREAM or STOP_SENDING, or received from a remote endpoint + in RESET_STREAM or STOP_SENDING. First application error code is + chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is + set in flags field. */ + uint64_t app_error_code; + }; + + ngtcp2_opl_entry oplent; + }; }; /* * ngtcp2_strm_init initializes |strm|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory */ -int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, - uint64_t max_rx_offset, uint64_t max_tx_offset, - void *stream_user_data, const ngtcp2_mem *mem); +void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem); /* * ngtcp2_strm_free deallocates memory allocated for |strm|. This @@ -245,6 +273,13 @@ int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm); */ int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm); +/* + * ngtcp2_strm_is_all_tx_data_fin_acked behaves like + * ngtcp2_strm_is_all_tx_data_acked, but it also requires that STREAM + * frame with fin bit set is acknowledged. + */ +int ngtcp2_strm_is_all_tx_data_fin_acked(ngtcp2_strm *strm); + /* * ngtcp2_strm_get_unacked_range_after returns the range that is not * acknowledged yet and intersects or comes after |offset|. @@ -265,4 +300,11 @@ uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm); */ int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len); +/* + * ngtcp2_strm_set_app_error_code sets |app_error_code| to |strm| and + * set NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag. If the flag is + * already set, this function does nothing. + */ +void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, uint64_t app_error_code); + #endif /* NGTCP2_STRM_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c index 7a6f8afa051f20..257332e27a2abe 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c @@ -61,7 +61,7 @@ void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem) { ngtcp2_mem_free(mem, vec); } -size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { +uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { size_t i; size_t res = 0; @@ -72,6 +72,23 @@ size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { return res; } +int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n) { + uint64_t res = 0; + size_t len; + size_t i; + + for (i = 0; i < n; ++i) { + len = vec[i].len; + if (len > NGTCP2_MAX_VARINT - res) { + return -1; + } + + res += len; + } + + return (int64_t)res; +} + ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst, size_t *pdstcnt, size_t left, size_t maxcnt) { size_t i; @@ -198,13 +215,10 @@ size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, return orig_left - left; } -size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, - size_t dstcnt, const ngtcp2_vec *src, - size_t srccnt, size_t left) { +size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt, + const ngtcp2_vec *src, size_t srccnt, + size_t left) { size_t i, j; - size_t len = left; - - *pnwritten = 0; for (i = 0, j = 0; left > 0 && i < srccnt && j < dstcnt;) { if (src[i].len == 0) { @@ -214,7 +228,6 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, dst[j] = src[i]; if (dst[j].len > left) { dst[j].len = left; - *pnwritten = len; return j + 1; } left -= dst[j].len; @@ -222,8 +235,6 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, ++j; } - *pnwritten = len - left; - return j; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h index 077820a9efed23..a39c4392fd2627 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h @@ -65,7 +65,13 @@ void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem); /* * ngtcp2_vec_len returns the sum of length in |vec| of |n| elements. */ -size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n); +uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n); + +/* + * ngtcp2_vec_len_varint is similar to ngtcp2_vec_len, but it returns + * -1 if the sum of the length exceeds NGTCP2_MAX_VARINT. + */ +int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n); /* * ngtcp2_vec_split splits |src| to |dst| so that the sum of the @@ -97,13 +103,13 @@ size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, /* * ngtcp2_vec_copy_at_most copies |src| of length |srccnt| to |dst| of * length |dstcnt|. The total number of bytes which the copied - * ngtcp2_vec refers to is at most |left| and is assigned to - * |*pnwritten|. The empty elements in |src| are ignored. This - * function returns the number of elements copied. + * ngtcp2_vec refers to is at most |left|. The empty elements in + * |src| are ignored. This function returns the number of elements + * copied. */ -size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, - size_t dstcnt, const ngtcp2_vec *src, - size_t srccnt, size_t left); +size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt, + const ngtcp2_vec *src, size_t srccnt, + size_t left); /* * ngtcp2_vec_copy copies |src| of length |cnt| to |dst|. |dst| must diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c index 40f3ae3f9eade4..b31162c3ebe0d7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c @@ -31,7 +31,7 @@ static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM, NGTCP2_VERSION}; -ngtcp2_info *ngtcp2_version(int least_version) { +const ngtcp2_info *ngtcp2_version(int least_version) { if (least_version > NGTCP2_VERSION_NUM) { return NULL; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c new file mode 100644 index 00000000000000..71c816e4d3d815 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.c @@ -0,0 +1,99 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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. + */ + +/* + * Translated to C from the original C++ code + * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h + * with the following license: + * + * // Copyright (c) 2016 The Chromium Authors. All rights reserved. + * // Use of this source code is governed by a BSD-style license that can be + * // found in the LICENSE file. + */ +#include "ngtcp2_window_filter.h" + +#include + +void ngtcp2_window_filter_init(ngtcp2_window_filter *wf, + uint64_t window_length) { + wf->window_length = window_length; + memset(wf->estimates, 0, sizeof(wf->estimates)); +} + +void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample, + uint64_t new_time) { + if (wf->estimates[0].sample == 0 || new_sample > wf->estimates[0].sample || + new_time - wf->estimates[2].time > wf->window_length) { + ngtcp2_window_filter_reset(wf, new_sample, new_time); + return; + } + + if (new_sample > wf->estimates[1].sample) { + wf->estimates[1].sample = new_sample; + wf->estimates[1].time = new_time; + wf->estimates[2] = wf->estimates[1]; + } else if (new_sample > wf->estimates[2].sample) { + wf->estimates[2].sample = new_sample; + wf->estimates[2].time = new_time; + } + + if (new_time - wf->estimates[0].time > wf->window_length) { + wf->estimates[0] = wf->estimates[1]; + wf->estimates[1] = wf->estimates[2]; + wf->estimates[2].sample = new_sample; + wf->estimates[2].time = new_time; + + if (new_time - wf->estimates[0].time > wf->window_length) { + wf->estimates[0] = wf->estimates[1]; + wf->estimates[1] = wf->estimates[2]; + } + return; + } + + if (wf->estimates[1].sample == wf->estimates[0].sample && + new_time - wf->estimates[1].time > wf->window_length >> 2) { + wf->estimates[2].sample = new_sample; + wf->estimates[2].time = new_time; + wf->estimates[1] = wf->estimates[2]; + return; + } + + if (wf->estimates[2].sample == wf->estimates[1].sample && + new_time - wf->estimates[2].time > wf->window_length >> 1) { + wf->estimates[2].sample = new_sample; + wf->estimates[2].time = new_time; + } +} + +void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample, + uint64_t new_time) { + wf->estimates[0].sample = new_sample; + wf->estimates[0].time = new_time; + wf->estimates[1] = wf->estimates[2] = wf->estimates[0]; +} + +uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf) { + return wf->estimates[0].sample; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h new file mode 100644 index 00000000000000..50415f10b8c37b --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_window_filter.h @@ -0,0 +1,65 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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. + */ + +/* + * Translated to C from the original C++ code + * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h + * with the following license: + * + * // Copyright (c) 2016 The Chromium Authors. All rights reserved. + * // Use of this source code is governed by a BSD-style license that can be + * // found in the LICENSE file. + */ +#ifndef NGTCP2_WINDOW_FILTER_H +#define NGTCP2_WINDOW_FILTER_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_window_filter_sample { + uint64_t sample; + uint64_t time; +} ngtcp2_window_filter_sample; + +typedef struct ngtcp2_window_filter { + uint64_t window_length; + ngtcp2_window_filter_sample estimates[3]; +} ngtcp2_window_filter; + +void ngtcp2_window_filter_init(ngtcp2_window_filter *wf, + uint64_t window_length); + +void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample, + uint64_t new_time); + +void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample, + uint64_t new_time); + +uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf); + +#endif /* NGTCP2_WINDOW_FILTER_H */