deps: update to ngtcp2 0.8.1 and nghttp3 0.7.0

Refs: https://github.com/nodejs/node/pull/44619
Co-authored-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/44622
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
Tobias NießenandJames M Snell authored and GitHub committed 2022-09-18 12:06:09 +00:00
1 parent bf47abe5f3
commit 7621d56a1b
121 files changed
+19288 -7153

No files matched your search

File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -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 */
+91
View File
@@ -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 <assert.h>
#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;
}
+92
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp3/nghttp3.h>
#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 */
+482 -1463
View File
File diff suppressed because it is too large. Load diff
+27 -115
View File
@@ -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 */
+1
View File
@@ -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; }
+2 -2
View File
@@ -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;
}
+6 -6
View File
@@ -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; }
+33 -47
View File
@@ -26,6 +26,7 @@
#include "nghttp3_frame.h"
#include <string.h>
#include <assert.h>
#include "nghttp3_conv.h"
#include "nghttp3_str.h"
@@ -70,44 +71,6 @@ 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) {
p = nghttp3_frame_write_hd(p, &fr->hd);
p = nghttp3_put_varint(p, fr->push_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);
*ppayloadlen = (int64_t)payloadlen;
return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) +
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) {
p = nghttp3_frame_write_hd(p, &fr->hd);
p = nghttp3_put_varint(p, fr->push_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);
*ppayloadlen = (int64_t)payloadlen;
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);
@@ -126,6 +89,38 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
}
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->pri_elem_id);
assert(fr->pri.urgency <= NGHTTP3_URGENCY_LOW);
*p++ = 'u';
*p++ = '=';
*p++ = (uint8_t)('0' + fr->pri.urgency);
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_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(fr->hd.type) +
nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen;
}
int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen,
const nghttp3_mem *mem) {
size_t i;
@@ -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);
}
+33 -61
View File
@@ -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 */
+66 -27
View File
@@ -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 <string.h>
#include <assert.h>
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);
}
+8 -13
View File
@@ -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
+916 -106
View File
File diff suppressed because it is too large. Load diff
+78 -22
View File
@@ -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 */
+2 -10
View File
@@ -27,18 +27,10 @@
#include <assert.h>
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) {
+2 -11
View File
@@ -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|.
+131 -53
View File
@@ -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;
#ifdef NOMEMPOOL
ksl_free_blk(ksl, ksl->head);
#endif /* NOMEMPOOL */
ksl->front = ksl->back = ksl->head = NULL;
ksl->n = 0;
head = ksl->head;
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));
+42 -25
View File
@@ -34,6 +34,8 @@
#include <nghttp3/nghttp3.h>
#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
+4
View File
@@ -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 */
+199 -198
View File
@@ -28,172 +28,166 @@
#include <string.h>
#include <assert.h>
#include <stdio.h>
#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;
}
}
}
}
return 0;
}
void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) {
entry->key = key;
}
/* FNV1a hash */
static uint32_t hash(key_type key, uint32_t mod) {
uint8_t *p, *end;
uint32_t h = 0x811C9DC5u;
key = nghttp3_htonl64(key);
p = (uint8_t *)&key;
end = p + sizeof(key_type);
for (; p != end;) {
h ^= *p++;
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
}
return h & (mod - 1);
}
static int less(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) {
return *(key_type *)lhs < *(key_type *)rhs;
}
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;
if (bkt->ptr == NULL &&
(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0)) {
bkt->ptr = entry;
return 0;
}
if (!bkt->ksl) {
bkt->ksl = nghttp3_mem_malloc(mem, sizeof(*bkt->ksl));
if (bkt->ksl == NULL) {
return NGHTTP3_ERR_NOMEM;
}
nghttp3_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
}
if (bkt->ptr) {
rv = nghttp3_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
rv = func(bkt->data, ptr);
if (rv != 0) {
return rv;
}
bkt->ptr = NULL;
}
return nghttp3_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
return 0;
}
/* new_tablelen must be power of 2 */
static int map_resize(nghttp3_map *map, uint32_t new_tablelen) {
static uint32_t hash(nghttp3_map_key_type key) {
return (uint32_t)((key * 11400714819323198485llu) >> 32);
}
static size_t h2idx(uint32_t hash, uint32_t bits) {
return hash >> (32 - bits);
}
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);
}
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;
bkt->hash = *phash;
bkt->key = *pkey;
bkt->data = *pdata;
*phash = h;
*pkey = key;
*pdata = data;
}
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;
}
void nghttp3_map_print_distance(nghttp3_map *map) {
uint32_t i;
size_t idx;
nghttp3_map_bucket *bkt;
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
if (bkt->data == NULL) {
fprintf(stderr, "@%u <EMPTY>\n", i);
continue;
}
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));
}
}
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;
}
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;
}
++d;
idx = (idx + 1) & (tablelen - 1);
}
}
/* 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;
}
--map->size;
return 0;
}
h = hash(key);
idx = h2idx(h, map->tablelenbits);
return NGHTTP3_ERR_INVALID_ARGUMENT;
for (;;) {
bkt = &map->table[idx];
if (bkt->data == NULL ||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
if (bkt->key == key) {
map_bucket_set_data(bkt, 0, 0, NULL);
didx = idx;
idx = (idx + 1) & (map->tablelen - 1);
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) {
uint32_t i;
nghttp3_map_bucket *bkt;
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;
}
if (map->tablelen == 0) {
return;
}
memset(map->table, 0, sizeof(*map->table) * map->tablelen);
map->size = 0;
}
+23 -40
View File
@@ -34,21 +34,15 @@
#include <nghttp3/nghttp3.h>
#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 */
+64 -17
View File
@@ -26,26 +26,28 @@
*/
#include "nghttp3_mem.h"
static void *default_malloc(size_t size, void *mem_user_data) {
(void)mem_user_data;
#include <stdio.h>
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 */
+37 -2
View File
@@ -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 */
+41
View File
@@ -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);
}
+141
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp3/nghttp3.h>
#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 */
+47
View File
@@ -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; }
+66
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp3/nghttp3.h>
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 */
+1 -1
View File
@@ -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.
+144 -138
View File
@@ -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 @@ fail:
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;
}
while (ctx->dtable_size > cap) {
ctx->max_dtable_capacity = max_dtable_capacity;
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;
}
+60 -25
View File
@@ -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,
+2 -3
View File
@@ -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) {
+3 -4
View File
@@ -33,10 +33,9 @@
#include <nghttp3/nghttp3.h>
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|. */
+124 -160
View File
@@ -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);
stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf);
if (buflen > NGHTTP3_MAX_VARINT - stream->tx.offset) {
return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
}
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) {
+89 -100
View File
@@ -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;
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 {
nghttp3_stream_http_state hstate;
} tx;
struct {
uint64_t offset;
nghttp3_stream_http_state hstate;
} tx;
struct {
nghttp3_stream_http_state hstate;
nghttp3_http_state http;
} rx;
struct {
nghttp3_stream_http_state hstate;
nghttp3_http_state http;
} rx;
uint16_t flags;
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.
+1 -1
View File
@@ -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) {
+2 -1
View File
@@ -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.
+19 -2
View File
@@ -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;
}
+6
View File
@@ -32,4 +32,10 @@
#include <nghttp3/nghttp3.h>
/*
* 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 */
+1 -1
View File
@@ -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;
}
+12 -1
View File
@@ -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': [
+190 -103
View File
@@ -35,15 +35,15 @@
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
#include <openssl/aes.h>
#include <openssl/chacha.h>
#include <openssl/rand.h>
#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(&params, exttype, tp, tplen);
if (rv != 0) {
ngtcp2_conn_set_tls_error(conn, rv);
return -1;
}
rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
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;
}
+328 -123
View File
@@ -31,6 +31,13 @@
extern "C" {
#endif
#ifdef WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <ws2tcpip.h>
#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 <ngtcp2_crypto_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 <ngtcp2_crypto_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) <ngtcp2_crypto_aead_keylen>`,
* and the length of packet protection IV is
* `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
* <ngtcp2_crypto_packet_protection_ivlen>` 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) <ngtcp2_crypto_aead_keylen>`,
* and the length of packet protection IV is
* `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
* <ngtcp2_crypto_packet_protection_ivlen>` 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 |level| is NGTCP2_CRYPTO_LEVEL_APP, this function retrieves a
* remote QUIC transport parameters extension from |tls| and sets it
* to |conn|.
* 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) <ngtcp2_crypto_aead_keylen>`,
* and the length of packet protection IV is
* `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
* <ngtcp2_crypto_packet_protection_ivlen>` 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
@@ -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
@@ -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
@@ -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 <ngtcp2/ngtcp2.h>
#include <picotls.h>
#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 */
@@ -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 <ngtcp2/ngtcp2.h>
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <wolfssl/quic.h>
#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 */
+303 -22
View File
@@ -34,6 +34,11 @@
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/rand.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
# include <openssl/core_names.h>
#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(&params, exttype, tp, tplen);
if (rv != 0) {
ngtcp2_conn_set_tls_error(conn, rv);
return -1;
}
rv = ngtcp2_conn_set_remote_transport_params(conn, &params);
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;
}
+697
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <assert.h>
#include <string.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <ngtcp2/ngtcp2_crypto_picotls.h>
#include <picotls.h>
#include <picotls/openssl.h>
#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);
}
}
+675 -88
View File
@@ -24,10 +24,18 @@
*/
#include "shared.h"
#ifdef WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <netinet/in.h>
#endif
#include <string.h>
#include <assert.h>
#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 @@ fail:
* 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, &params);
nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, &params);
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 @@ fail:
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];
int rv;
static const uint8_t info[] = "stateless_reset";
ngtcp2_crypto_md md;
assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf));
assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf));
rv = ngtcp2_crypto_hkdf_extract(buf, md, secret, secretlen, cid->data,
cid->datalen);
if (rv != 0) {
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;
}
memcpy(token, buf, NGTCP2_STATELESS_RESET_TOKENLEN);
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 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;
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);
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_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;
}
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;
}
+140 -1
View File
@@ -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) <ngtcp2_crypto_aead_keylen>`
* 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)
* <ngtcp2_crypto_packet_protection_ivlen>` 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)
* <ngtcp2_crypto_aead_keylen>` 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 */
+524
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <assert.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
#include <wolfssl/ssl.h>
#include <wolfssl/quic.h>
#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;
}
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -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 */
+48 -31
View File
@@ -26,22 +26,31 @@
#include <assert.h>
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) {
+34 -26
View File
@@ -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. */
+22 -36
View File
@@ -27,55 +27,41 @@
#include <string.h>
#include <assert.h>
#ifdef WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <netinet/ip.h>
# include <netinet/tcp.h>
# include <arpa/inet.h>
#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;
}
+4 -13
View File
@@ -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|,
+90
View File
@@ -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 <assert.h>
#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;
}
+91
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <ngtcp2/ngtcp2.h>
#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 */
+692
View File
@@ -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 <assert.h>
#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);
}
+156
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <ngtcp2/ngtcp2.h>
#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 */
File diff suppressed because it is too large. Load diff
+149
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <ngtcp2/ngtcp2.h>
#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 */
+20 -8
View File
@@ -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_len(const ngtcp2_buf *buf) {
return (size_t)(buf->last - buf->pos);
}
size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) {
return (size_t)(buf->end - buf->begin);
}
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;
}
void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem) {
ngtcp2_mem_free(mem, bufchain);
}
+31 -2
View File
@@ -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 */
+79 -11
View File
@@ -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);
}
+293 -7
View File
@@ -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);
+43 -17
View File
@@ -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;
}
+47 -25
View File
@@ -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 */
+3637 -1371
View File
File diff suppressed because it is too large. Load diff
+350 -57
View File
@@ -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 */
+5 -4
View File
@@ -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;
}
+4 -103
View File
@@ -29,107 +29,8 @@
# include <config.h>
#endif /* HAVE_CONFIG_H */
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif /* HAVE_ARPA_INET_H */
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#ifdef HAVE_BYTESWAP_H
# include <byteswap.h>
#endif /* HAVE_BYTESWAP_H */
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif /* HAVE_ENDIAN_H */
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif /* HAVE_SYS_ENDIAN_H */
#include <ngtcp2/ngtcp2.h>
#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
+197 -21
View File
@@ -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(&params->retry_scid, 0, sizeof(params->retry_scid));
memset(&params->initial_scid, 0, sizeof(params->initial_scid));
memset(&params->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(&param_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(&params, exttype, data, datalen);
if (rv < 0) {
return rv;
}
if (mem == NULL) {
mem = ngtcp2_mem_default();
}
return transport_params_copy_new(pparams, &params, 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);
}
+46 -2
View File
@@ -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 */
+14 -4
View File
@@ -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;
}
+65 -27
View File
@@ -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 <string.h>
#include <assert.h>
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);
}
+8 -13
View File
@@ -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
+2 -9
View File
@@ -26,17 +26,10 @@
#include <assert.h>
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) {
+1 -7
View File
@@ -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|.
+131 -53
View File
@@ -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;
#ifdef NOMEMPOOL
ksl_free_blk(ksl, ksl->head);
#endif /* NOMEMPOOL */
ksl->front = ksl->back = ksl->head = NULL;
ksl->n = 0;
head = ksl->head;
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));
+41 -25
View File
@@ -33,6 +33,8 @@
#include <ngtcp2/ngtcp2.h>
#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
+82 -43
View File
@@ -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
*
* <DIR> <PKN> <PKTNAME>(<PKTTYPE>) <FRAMENAME>(<FRAMETYPE>)
* <DIR> <PKN> <PKTNAME> <FRAMENAME>(<FRAMETYPE>)
*
* <DIR>:
* Flow direction. tx=transmission, rx=reception
@@ -78,10 +79,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
* Packet number.
*
* <PKTNAME>:
* Packet name. (e.g., Initial, Handshake, S01)
*
* <PKTTYPE>:
* Packet type in hex string.
* Packet name. (e.g., Initial, Handshake, 1RTT)
*
* <FRAMENAME>:
* 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(&params->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));
}
+47 -4
View File
@@ -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 */
+199 -195
View File
@@ -27,170 +27,166 @@
#include <string.h>
#include <assert.h>
#include <stdio.h>
#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;
}
}
}
}
return 0;
}
void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) {
entry->key = key;
}
/* FNV1a hash */
static uint32_t hash(key_type key, uint32_t mod) {
uint8_t *p, *end;
uint32_t h = 0x811C9DC5u;
key = ngtcp2_htonl64(key);
p = (uint8_t *)&key;
end = p + sizeof(key_type);
for (; p != end;) {
h ^= *p++;
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
}
return h & (mod - 1);
}
static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
return *(key_type *)lhs < *(key_type *)rhs;
}
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;
if (bkt->ptr == NULL && (bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0)) {
bkt->ptr = entry;
return 0;
}
if (!bkt->ksl) {
bkt->ksl = ngtcp2_mem_malloc(mem, sizeof(*bkt->ksl));
if (bkt->ksl == NULL) {
return NGTCP2_ERR_NOMEM;
}
ngtcp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem);
}
if (bkt->ptr) {
rv = ngtcp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr);
rv = func(bkt->data, ptr);
if (rv != 0) {
return rv;
}
bkt->ptr = NULL;
}
return ngtcp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry);
return 0;
}
/* new_tablelen must be power of 2 */
static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) {
static uint32_t hash(ngtcp2_map_key_type key) {
return (uint32_t)((key * 11400714819323198485llu) >> 32);
}
static size_t h2idx(uint32_t hash, uint32_t bits) {
return hash >> (32 - bits);
}
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);
}
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;
bkt->hash = *phash;
bkt->key = *pkey;
bkt->data = *pdata;
*phash = h;
*pkey = key;
*pdata = data;
}
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;
}
void ngtcp2_map_print_distance(ngtcp2_map *map) {
uint32_t i;
size_t idx;
ngtcp2_map_bucket *bkt;
for (i = 0; i < map->tablelen; ++i) {
bkt = &map->table[i];
if (bkt->data == NULL) {
fprintf(stderr, "@%u <EMPTY>\n", i);
continue;
}
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));
}
}
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;
}
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;
}
++d;
idx = (idx + 1) & (tablelen - 1);
}
}
/* 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;
}
--map->size;
return 0;
}
h = hash(key);
idx = h2idx(h, map->tablelenbits);
return NGTCP2_ERR_INVALID_ARGUMENT;
for (;;) {
bkt = &map->table[idx];
if (bkt->data == NULL ||
d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
if (bkt->key == key) {
map_bucket_set_data(bkt, 0, 0, NULL);
didx = idx;
idx = (idx + 1) & (map->tablelen - 1);
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) {
uint32_t i;
ngtcp2_map_bucket *bkt;
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;
}
if (map->tablelen == 0) {
return;
}
memset(map->table, 0, sizeof(*map->table) * map->tablelen);
map->size = 0;
}
+24 -40
View File
@@ -33,21 +33,15 @@
#include <ngtcp2/ngtcp2.h>
#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 */
+54 -16
View File
@@ -25,26 +25,28 @@
*/
#include "ngtcp2_mem.h"
static void *default_malloc(size_t size, void *mem_user_data) {
(void)mem_user_data;
#include <stdio.h>
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 */
+30 -1
View File
@@ -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 */
+136
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif /* HAVE_ARPA_INET_H */
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#ifdef HAVE_BYTESWAP_H
# include <byteswap.h>
#endif /* HAVE_BYTESWAP_H */
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif /* HAVE_ENDIAN_H */
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif /* HAVE_SYS_ENDIAN_H */
#include <ngtcp2/ngtcp2.h>
#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 */
+40
View File
@@ -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);
}
+140
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <ngtcp2/ngtcp2.h>
#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 */
+46
View File
@@ -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; }
+65
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <ngtcp2/ngtcp2.h>
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 */
+16 -13
View File
@@ -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;
}
+194 -53
View File
@@ -26,7 +26,6 @@
#include <assert.h>
#include <string.h>
#include <stdio.h>
#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) {
+51 -6
View File
@@ -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 */
+160
View File
@@ -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 <assert.h>
#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;
}
+123
View File
@@ -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 <config.h>
#endif /* HAVE_CONFIG_H */
#include <ngtcp2/ngtcp2.h>
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 */
+2 -2
View File
@@ -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));
}
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff. Show more