# HG changeset patch # User Vladimir Homutov # Date 1584525507 -10800 # Node ID ae35ccba7aa65e88d867a69d1cc9ac4149fec1b3 # Parent 61f9b873e2e78b09db39a05731843fafc01278bf Extracted transport part of the code into separate file. All code dealing with serializing/deserializing is moved int srv/event/ngx_event_quic_transport.c/h file. All macros for dealing with data are internal to source file. The header file exposes frame types and error codes. The exported functions are currently packet header parsers and writers and frames parser/writer. The ngx_quic_header_t structure is updated with 'log' member. This avoids passing extra argument to parsing functions that need to report errors. diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -1265,10 +1265,12 @@ if [ $USE_OPENSSL = YES ]; then ngx_module_incs= ngx_module_deps="src/event/ngx_event_openssl.h \ src/event/ngx_event_quic.h \ + src/event/ngx_event_quic_transport.h \ src/event/ngx_event_quic_protection.h" - ngx_module_srcs="src/event/ngx_event_openssl.c - src/event/ngx_event_openssl_stapling.c - src/event/ngx_event_quic.c + ngx_module_srcs="src/event/ngx_event_openssl.c \ + src/event/ngx_event_openssl_stapling.c \ + src/event/ngx_event_quic.c \ + src/event/ngx_event_quic_transport.c \ src/event/ngx_event_quic_protection.c" ngx_module_libs= ngx_module_link=YES diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h --- a/src/core/ngx_core.h +++ b/src/core/ngx_core.h @@ -85,6 +85,7 @@ typedef void (*ngx_connection_handler_pt #if (NGX_OPENSSL) #include #include +#include #include #endif #include diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -9,82 +9,6 @@ #include -/* 12.4. Frames and Frame Types */ -#define NGX_QUIC_FT_PADDING 0x00 -#define NGX_QUIC_FT_PING 0x01 -#define NGX_QUIC_FT_ACK 0x02 -#define NGX_QUIC_FT_ACK_ECN 0x03 -#define NGX_QUIC_FT_RESET_STREAM 0x04 -#define NGX_QUIC_FT_STOP_SENDING 0x05 -#define NGX_QUIC_FT_CRYPTO 0x06 -#define NGX_QUIC_FT_NEW_TOKEN 0x07 -#define NGX_QUIC_FT_STREAM0 0x08 -#define NGX_QUIC_FT_STREAM1 0x09 -#define NGX_QUIC_FT_STREAM2 0x0A -#define NGX_QUIC_FT_STREAM3 0x0B -#define NGX_QUIC_FT_STREAM4 0x0C -#define NGX_QUIC_FT_STREAM5 0x0D -#define NGX_QUIC_FT_STREAM6 0x0E -#define NGX_QUIC_FT_STREAM7 0x0F -#define NGX_QUIC_FT_MAX_DATA 0x10 -#define NGX_QUIC_FT_MAX_STREAM_DATA 0x11 -#define NGX_QUIC_FT_MAX_STREAMS 0x12 -#define NGX_QUIC_FT_MAX_STREAMS2 0x13 // XXX -#define NGX_QUIC_FT_DATA_BLOCKED 0x14 -#define NGX_QUIC_FT_STREAM_DATA_BLOCKED 0x15 -#define NGX_QUIC_FT_STREAMS_BLOCKED 0x16 -#define NGX_QUIC_FT_STREAMS_BLOCKED2 0x17 // XXX -#define NGX_QUIC_FT_NEW_CONNECTION_ID 0x18 -#define NGX_QUIC_FT_RETIRE_CONNECTION_ID 0x19 -#define NGX_QUIC_FT_PATH_CHALLENGE 0x1a -#define NGX_QUIC_FT_PATH_RESPONSE 0x1b -#define NGX_QUIC_FT_CONNECTION_CLOSE 0x1c -#define NGX_QUIC_FT_CONNECTION_CLOSE2 0x1d // XXX -#define NGX_QUIC_FT_HANDSHAKE_DONE 0x1e - -#define ngx_quic_stream_bit_off(val) (((val) & 0x04) ? 1 : 0) -#define ngx_quic_stream_bit_len(val) (((val) & 0x02) ? 1 : 0) -#define ngx_quic_stream_bit_fin(val) (((val) & 0x01) ? 1 : 0) - - -#define NGX_QUIC_ERR_NO_ERROR 0x0 -#define NGX_QUIC_ERR_INTERNAL_ERROR 0x1 -#define NGX_QUIC_ERR_SERVER_BUSY 0x2 -#define NGX_QUIC_ERR_FLOW_CONTROL_ERROR 0x3 -#define NGX_QUIC_ERR_STREAM_LIMIT_ERROR 0x4 -#define NGX_QUIC_ERR_STREAM_STATE_ERROR 0x5 -#define NGX_QUIC_ERR_FINAL_SIZE_ERROR 0x6 -#define NGX_QUIC_ERR_FRAME_ENCODING_ERROR 0x7 -#define NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR 0x8 -#define NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR 0x9 -#define NGX_QUIC_ERR_PROTOCOL_VIOLATION 0xA -#define NGX_QUIC_ERR_INVALID_TOKEN 0xB -/* 0xC is not defined */ -#define NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED 0xD -#define NGX_QUIC_ERR_CRYPTO_ERROR 0x10 - -#define NGX_QUIC_ERR_LAST NGX_QUIC_ERR_CRYPTO_ERROR - -/* literal errors indexed by corresponding value */ -static char *ngx_quic_errors[] = { - "NO_ERROR", - "INTERNAL_ERROR", - "SERVER_BUSY", - "FLOW_CONTROL_ERROR", - "STREAM_LIMIT_ERROR", - "STREAM_STATE_ERROR", - "FINAL_SIZE_ERROR", - "FRAME_ENCODING_ERROR", - "TRANSPORT_PARAMETER_ERROR", - "CONNECTION_ID_LIMIT_ERROR", - "PROTOCOL_VIOLATION", - "INVALID_TOKEN", - "", - "CRYPTO_BUFFER_EXCEEDED", - "CRYPTO_ERROR", -}; - - /* TODO: real states, these are stubs */ typedef enum { NGX_QUIC_ST_INITIAL, @@ -93,69 +17,6 @@ typedef enum { } ngx_quic_state_t; -typedef struct ngx_quic_frame_s ngx_quic_frame_t; - -typedef struct { - ngx_uint_t pn; - - // input - uint64_t largest; - uint64_t delay; - uint64_t range_count; - uint64_t first_range; - uint64_t ranges[20]; - /* ecn counts */ -} ngx_quic_ack_frame_t; - -typedef struct { - size_t offset; - size_t len; - u_char *data; -} ngx_quic_crypto_frame_t; - - -typedef struct { - uint64_t seqnum; - uint64_t retire; - uint64_t len; - u_char cid[20]; - u_char srt[16]; -} ngx_quic_ncid_t; - - -typedef struct { - uint8_t type; - uint64_t stream_id; - uint64_t offset; - uint64_t length; - u_char *data; -} ngx_quic_stream_frame_t; - - -typedef struct { - uint64_t error_code; - uint64_t frame_type; - ngx_str_t reason; -} ngx_quic_close_frame_t; - - -struct ngx_quic_frame_s { - ngx_uint_t type; - ngx_quic_level_t level; - ngx_quic_frame_t *next; - union { - ngx_quic_crypto_frame_t crypto; - ngx_quic_ack_frame_t ack; - ngx_quic_ncid_t ncid; - ngx_quic_stream_frame_t stream; - ngx_quic_close_frame_t close; - // more frames - } u; - - u_char info[128]; // for debug purposes -}; - - struct ngx_quic_connection_s { ngx_quic_state_t state; @@ -229,16 +90,6 @@ static int ngx_quic_flush_flight(ngx_ssl static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, uint8_t alert); -static ngx_int_t ngx_quic_process_long_header(ngx_connection_t *c, - ngx_quic_header_t *pkt); -static ngx_int_t ngx_quic_process_short_header(ngx_connection_t *c, - ngx_quic_header_t *pkt); -static ngx_int_t ngx_quic_process_initial_header(ngx_connection_t *c, - ngx_quic_header_t *pkt); -static ngx_int_t ngx_quic_process_handshake_header(ngx_connection_t *c, - ngx_quic_header_t *pkt); - -static uint64_t ngx_quic_parse_int(u_char **pos); static ssize_t ngx_quic_stream_recv(ngx_connection_t *c, u_char *buf, size_t size); @@ -282,6 +133,7 @@ ngx_quic_run(ngx_connection_t *c, ngx_ss b = c->buffer; + pkt.log = c->log; pkt.raw = b; pkt.data = b->start; pkt.len = b->last - b->start; @@ -407,6 +259,7 @@ ngx_quic_input(ngx_connection_t *c, ngx_ pkt.raw = b; pkt.data = p; pkt.len = b->last - p; + pkt.log = c->log; if (p[0] == 0) { /* XXX: no idea WTF is this, just ignore */ @@ -448,11 +301,13 @@ ngx_quic_input(ngx_connection_t *c, ngx_ static ngx_int_t ngx_quic_send_packet(ngx_connection_t *c, ngx_quic_connection_t *qc, - ngx_quic_level_t level, ngx_str_t *payload) + enum ssl_encryption_level_t level, ngx_str_t *payload) { ngx_str_t res; ngx_quic_header_t pkt; + pkt.log = c->log; + static ngx_str_t initial_token = ngx_null_string; ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); @@ -494,139 +349,12 @@ ngx_quic_send_packet(ngx_connection_t *c } -static size_t -ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack) -{ - size_t len; - - /* minimal ACK packet */ - - if (p == NULL) { - len = ngx_quic_varint_len(NGX_QUIC_FT_ACK); - len += ngx_quic_varint_len(ack->pn); - len += ngx_quic_varint_len(0); - len += ngx_quic_varint_len(0); - len += ngx_quic_varint_len(ack->pn); - - return len; - } - - ngx_quic_build_int(&p, NGX_QUIC_FT_ACK); - ngx_quic_build_int(&p, ack->pn); - ngx_quic_build_int(&p, 0); - ngx_quic_build_int(&p, 0); - ngx_quic_build_int(&p, ack->pn); - - return 5; -} - - -static size_t -ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto) -{ - size_t len; - u_char *start; - - if (p == NULL) { - len = ngx_quic_varint_len(NGX_QUIC_FT_CRYPTO); - len += ngx_quic_varint_len(crypto->offset); - len += ngx_quic_varint_len(crypto->len); - len += crypto->len; - - return len; - } - - start = p; - - ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO); - ngx_quic_build_int(&p, crypto->offset); - ngx_quic_build_int(&p, crypto->len); - p = ngx_cpymem(p, crypto->data, crypto->len); - - return p - start; -} - - -static size_t -ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf) -{ - size_t len; - u_char *start; - - if (!ngx_quic_stream_bit_len(sf->type)) { -#if 0 - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "attempt to generate a stream frame without length"); -#endif - // XXX: handle error in caller - return NGX_ERROR; - } - - if (p == NULL) { - len = ngx_quic_varint_len(sf->type); - - if (ngx_quic_stream_bit_off(sf->type)) { - len += ngx_quic_varint_len(sf->offset); - } - - len += ngx_quic_varint_len(sf->stream_id); - - /* length is always present in generated frames */ - len += ngx_quic_varint_len(sf->length); - - len += sf->length; - - return len; - } - - start = p; - - ngx_quic_build_int(&p, sf->type); - ngx_quic_build_int(&p, sf->stream_id); - - if (ngx_quic_stream_bit_off(sf->type)) { - ngx_quic_build_int(&p, sf->offset); - } - - /* length is always present in generated frames */ - ngx_quic_build_int(&p, sf->length); - - p = ngx_cpymem(p, sf->data, sf->length); - - return p - start; -} - - -size_t -ngx_quic_frame_len(ngx_quic_frame_t *frame) -{ - switch (frame->type) { - case NGX_QUIC_FT_ACK: - return ngx_quic_create_ack(NULL, &frame->u.ack); - case NGX_QUIC_FT_CRYPTO: - return ngx_quic_create_crypto(NULL, &frame->u.crypto); - - case NGX_QUIC_FT_STREAM0: - case NGX_QUIC_FT_STREAM1: - case NGX_QUIC_FT_STREAM2: - case NGX_QUIC_FT_STREAM3: - case NGX_QUIC_FT_STREAM4: - case NGX_QUIC_FT_STREAM5: - case NGX_QUIC_FT_STREAM6: - case NGX_QUIC_FT_STREAM7: - return ngx_quic_create_stream(NULL, &frame->u.stream); - default: - /* BUG: unsupported frame type generated */ - return 0; - } -} - - /* pack a group of frames [start; end) into memory p and send as single packet */ ngx_int_t ngx_quic_frames_send(ngx_connection_t *c, ngx_quic_frame_t *start, ngx_quic_frame_t *end, size_t total) { + ssize_t len; u_char *p; ngx_str_t out; ngx_quic_frame_t *f; @@ -645,30 +373,12 @@ ngx_quic_frames_send(ngx_connection_t *c ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "frame: %s", f->info); - switch (f->type) { - case NGX_QUIC_FT_ACK: - p += ngx_quic_create_ack(p, &f->u.ack); - break; - - case NGX_QUIC_FT_CRYPTO: - p += ngx_quic_create_crypto(p, &f->u.crypto); - break; - - case NGX_QUIC_FT_STREAM0: - case NGX_QUIC_FT_STREAM1: - case NGX_QUIC_FT_STREAM2: - case NGX_QUIC_FT_STREAM3: - case NGX_QUIC_FT_STREAM4: - case NGX_QUIC_FT_STREAM5: - case NGX_QUIC_FT_STREAM6: - case NGX_QUIC_FT_STREAM7: - p += ngx_quic_create_stream(p, &f->u.stream); - break; - - default: - /* BUG: unsupported frame type generated */ + len = ngx_quic_create_frame(p, p + total, f); + if (len == -1) { return NGX_ERROR; } + + p += len; } out.len = p - out.data; @@ -892,279 +602,6 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_ } -static ngx_int_t -ngx_quic_process_short_header(ngx_connection_t *c, ngx_quic_header_t *pkt) -{ - u_char *p; - - p = pkt->data; - - ngx_quic_hexdump0(c->log, "short input", pkt->data, pkt->len); - - if ((p[0] & NGX_QUIC_PKT_LONG)) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "not a short packet"); - return NGX_ERROR; - } - - pkt->flags = *p++; - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic flags:%xi", pkt->flags); - - if (ngx_memcmp(p, c->quic->dcid.data, c->quic->dcid.len) != 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "unexpected quic dcid"); - return NGX_ERROR; - } - - pkt->dcid.len = c->quic->dcid.len; - pkt->dcid.data = p; - p += pkt->dcid.len; - - pkt->raw->pos = p; - - return NGX_OK; -} - - -static ngx_int_t -ngx_quic_process_long_header(ngx_connection_t *c, ngx_quic_header_t *pkt) -{ - u_char *p; - - p = pkt->data; - - ngx_quic_hexdump0(c->log, "long input", pkt->data, pkt->len); - - if (!(p[0] & NGX_QUIC_PKT_LONG)) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "not a long packet"); - return NGX_ERROR; - } - - pkt->flags = *p++; - - pkt->version = ngx_quic_parse_uint32(p); - p += sizeof(uint32_t); - - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic flags:%xi version:%xD", pkt->flags, pkt->version); - - if (pkt->version != quic_version) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "unsupported quic version"); - return NGX_ERROR; - } - - pkt->dcid.len = *p++; - pkt->dcid.data = p; - p += pkt->dcid.len; - - pkt->scid.len = *p++; - pkt->scid.data = p; - p += pkt->scid.len; - - pkt->raw->pos = p; - - return NGX_OK; -} - - -static ngx_int_t -ngx_quic_process_initial_header(ngx_connection_t *c, ngx_quic_header_t *pkt) -{ - u_char *p; - ngx_int_t plen; - - p = pkt->raw->pos; - - pkt->token.len = ngx_quic_parse_int(&p); - pkt->token.data = p; - - p += pkt->token.len; - - plen = ngx_quic_parse_int(&p); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic packet length: %d", plen); - - if (plen > pkt->data + pkt->len - p) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "truncated initial packet"); - return NGX_ERROR; - } - - pkt->raw->pos = p; - pkt->len = plen; - - ngx_quic_hexdump0(c->log, "DCID", pkt->dcid.data, pkt->dcid.len); - ngx_quic_hexdump0(c->log, "SCID", pkt->scid.data, pkt->scid.len); - ngx_quic_hexdump0(c->log, "token", pkt->token.data, pkt->token.len); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic packet length: %d", plen); - - return NGX_OK; -} - -static ngx_int_t -ngx_quic_process_handshake_header(ngx_connection_t *c, ngx_quic_header_t *pkt) -{ - u_char *p; - ngx_int_t plen; - - p = pkt->raw->pos; - - plen = ngx_quic_parse_int(&p); - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic packet length: %d", plen); - - if (plen > pkt->data + pkt->len - p) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, "truncated handshake packet"); - return NGX_ERROR; - } - - pkt->raw->pos = p; - pkt->len = plen; - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic packet length: %d", plen); - - return NGX_OK; -} - - -ssize_t -ngx_quic_read_frame(ngx_connection_t *c, u_char *start, u_char *end, - ngx_quic_frame_t *frame) -{ - u_char *p; - - size_t npad; - - p = start; - - frame->type = *p++; // TODO: check overflow (p < end) - - switch (frame->type) { - - case NGX_QUIC_FT_CRYPTO: - frame->u.crypto.offset = *p++; - frame->u.crypto.len = ngx_quic_parse_int(&p); - frame->u.crypto.data = p; - p += frame->u.crypto.len; - - ngx_quic_hexdump0(c->log, "CRYPTO frame", - frame->u.crypto.data, frame->u.crypto.len); - - ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic CRYPTO frame length: %uL off:%uL pp:%p", - frame->u.crypto.len, frame->u.crypto.offset, - frame->u.crypto.data); - break; - - case NGX_QUIC_FT_PADDING: - npad = 0; - while (p < end && *p == NGX_QUIC_FT_PADDING) { // XXX - p++; npad++; - } - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "PADDING frame length %uL", npad); - - break; - - case NGX_QUIC_FT_ACK: - case NGX_QUIC_FT_ACK_ECN: - - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ACK frame"); - - frame->u.ack.largest = ngx_quic_parse_int(&p); - frame->u.ack.delay = ngx_quic_parse_int(&p); - frame->u.ack.range_count =ngx_quic_parse_int(&p); - frame->u.ack.first_range =ngx_quic_parse_int(&p); - - if (frame->u.ack.range_count) { - frame->u.ack.ranges[0] = ngx_quic_parse_int(&p); - } - - if (frame->type ==NGX_QUIC_FT_ACK_ECN) { - return NGX_ERROR; - } - - break; - - case NGX_QUIC_FT_PING: - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "PING frame"); - break; - - case NGX_QUIC_FT_NEW_CONNECTION_ID: - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "NCID frame"); - - frame->u.ncid.seqnum = ngx_quic_parse_int(&p); - frame->u.ncid.retire = ngx_quic_parse_int(&p); - frame->u.ncid.len = *p++; - ngx_memcpy(frame->u.ncid.cid, p, frame->u.ncid.len); - p += frame->u.ncid.len; - - ngx_memcpy(frame->u.ncid.srt, p, 16); - p += 16; - - break; - - case NGX_QUIC_FT_CONNECTION_CLOSE: - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "connection close frame"); - - frame->u.close.error_code = ngx_quic_parse_int(&p); - frame->u.close.frame_type = ngx_quic_parse_int(&p); // not in 0x1d CC - frame->u.close.reason.len = ngx_quic_parse_int(&p); - frame->u.close.reason.data = p; - p += frame->u.close.reason.len; - - if (frame->u.close.error_code > NGX_QUIC_ERR_LAST) { - frame->u.close.error_code = NGX_QUIC_ERR_LAST; - } - break; - - case NGX_QUIC_FT_STREAM0: - case NGX_QUIC_FT_STREAM1: - case NGX_QUIC_FT_STREAM2: - case NGX_QUIC_FT_STREAM3: - case NGX_QUIC_FT_STREAM4: - case NGX_QUIC_FT_STREAM5: - case NGX_QUIC_FT_STREAM6: - case NGX_QUIC_FT_STREAM7: - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "STREAM frame, type: 0x%xi", frame->type); - - frame->u.stream.type = frame->type; - - frame->u.stream.stream_id = ngx_quic_parse_int(&p); - if (frame->type & 0x04) { - frame->u.stream.offset = ngx_quic_parse_int(&p); - } else { - frame->u.stream.offset = 0; - } - - if (frame->type & 0x02) { - frame->u.stream.length = ngx_quic_parse_int(&p); - } else { - frame->u.stream.length = end - p; /* up to packet end */ - } - - frame->u.stream.data = p; - - p += frame->u.stream.length; - - break; - - default: - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "unknown frame type %xi", frame->type); - return NGX_ERROR; - } - - return p - start; -} - static ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, @@ -1349,6 +786,9 @@ ngx_quic_stream_send(ngx_connection_t *c frame->level = ssl_encryption_application; frame->type = NGX_QUIC_FT_STREAM6; /* OFF=1 LEN=1 FIN=0 */ + frame->u.stream.off = 1; + frame->u.stream.len = 1; + frame->u.stream.fin = 0; frame->u.stream.type = frame->type; frame->u.stream.stream_id = qs->id; @@ -1433,8 +873,11 @@ ngx_quic_payload_handler(ngx_connection_ while (p < end) { - len = ngx_quic_read_frame(c, p, end, &frame); + len = ngx_quic_parse_frame(p, end, &frame); if (len < 0) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "unknown frame type %xi", frame.type); + // XXX: log here return NGX_ERROR; } @@ -1456,6 +899,13 @@ ngx_quic_payload_handler(ngx_connection_ break; case NGX_QUIC_FT_CRYPTO: + ngx_quic_hexdump0(c->log, "CRYPTO frame", + frame.u.crypto.data, frame.u.crypto.len); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic CRYPTO frame length: %uL off:%uL pp:%p", + frame.u.crypto.len, frame.u.crypto.offset, + frame.u.crypto.data); if (frame.u.crypto.offset != 0x0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, @@ -1491,7 +941,7 @@ ngx_quic_payload_handler(ngx_connection_ case NGX_QUIC_FT_CONNECTION_CLOSE: ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "CONN.CLOSE: { %s (0x%xi) type=0x%xi reason='%V'}", - ngx_quic_errors[frame.u.close.error_code], + ngx_quic_error_text(frame.u.close.error_code), frame.u.close.error_code, frame.u.close.frame_type, &frame.u.close.reason); @@ -1516,10 +966,9 @@ ngx_quic_payload_handler(ngx_connection_ frame.u.stream.stream_id, frame.u.stream.offset, frame.u.stream.length, - ngx_quic_stream_bit_off(frame.u.stream.type), - ngx_quic_stream_bit_len(frame.u.stream.type), - ngx_quic_stream_bit_fin(frame.u.stream.type)); - + frame.u.stream.off, + frame.u.stream.len, + frame.u.stream.fin); sn = ngx_quic_stream_lookup(&qc->stree, frame.u.stream.stream_id); if (sn == NULL) { @@ -1727,7 +1176,7 @@ ngx_quic_new_connection(ngx_connection_t return NGX_ERROR; } - if (ngx_quic_process_long_header(c, pkt) != NGX_OK) { + if (ngx_quic_parse_long_header(pkt) != NGX_OK) { return NGX_ERROR; } @@ -1737,7 +1186,7 @@ ngx_quic_new_connection(ngx_connection_t return NGX_ERROR; } - if (ngx_quic_process_initial_header(c, pkt) != NGX_OK) { + if (ngx_quic_parse_initial_header(pkt) != NGX_OK) { return NGX_ERROR; } @@ -1804,11 +1253,11 @@ ngx_quic_initial_input(ngx_connection_t qc = c->quic; ssl_conn = c->ssl->connection; - if (ngx_quic_process_long_header(c, pkt) != NGX_OK) { + if (ngx_quic_parse_long_header(pkt) != NGX_OK) { return NGX_ERROR; } - if (ngx_quic_process_initial_header(c, pkt) != NGX_OK) { + if (ngx_quic_parse_initial_header(pkt) != NGX_OK) { return NGX_ERROR; } @@ -1833,7 +1282,7 @@ ngx_quic_handshake_input(ngx_connection_ ssl_conn = c->ssl->connection; /* extract cleartext data into pkt */ - if (ngx_quic_process_long_header(c, pkt) != NGX_OK) { + if (ngx_quic_parse_long_header(pkt) != NGX_OK) { return NGX_ERROR; } @@ -1863,7 +1312,7 @@ ngx_quic_handshake_input(ngx_connection_ return NGX_ERROR; } - if (ngx_quic_process_handshake_header(c, pkt) != NGX_OK) { + if (ngx_quic_parse_handshake_header(pkt) != NGX_OK) { return NGX_ERROR; } @@ -1891,7 +1340,7 @@ ngx_quic_app_input(ngx_connection_t *c, return NGX_DECLINED; } - if (ngx_quic_process_short_header(c, pkt) != NGX_OK) { + if (ngx_quic_parse_short_header(pkt, &qc->dcid) != NGX_OK) { return NGX_ERROR; } @@ -1906,52 +1355,3 @@ ngx_quic_app_input(ngx_connection_t *c, } -uint64_t -ngx_quic_parse_int(u_char **pos) -{ - u_char *p; - uint64_t value; - ngx_uint_t len; - - p = *pos; - len = 1 << ((*p & 0xc0) >> 6); - value = *p++ & 0x3f; - - while (--len) { - value = (value << 8) + *p++; - } - - *pos = p; - return value; -} - - -void -ngx_quic_build_int(u_char **pos, uint64_t value) -{ - u_char *p; - ngx_uint_t len;//, len2; - - p = *pos; - len = 0; - - while (value >> ((1 << len) * 8 - 2)) { - len++; - } - - *p = len << 6; - -// len2 = - len = (1 << len); - len--; - *p |= value >> (len * 8); - p++; - - while (len) { - *p++ = value >> ((len-- - 1) * 8); - } - - *pos = p; -// return len2; -} - diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h --- a/src/event/ngx_event_quic.h +++ b/src/event/ngx_event_quic.h @@ -11,57 +11,8 @@ #include -#define quic_version 0xff000018 /* draft-24 (ngtcp2) */ -//#define quic_version 0xff00001b /* draft-27 (FFN 76) */ - -/* 17.2. Long Header Packets */ - -#define NGX_QUIC_PKT_LONG 0x80 - -#define NGX_QUIC_PKT_INITIAL 0xc0 -#define NGX_QUIC_PKT_HANDSHAKE 0xe0 - - -#if (NGX_HAVE_NONALIGNED) - -#define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p)) -#define ngx_quic_parse_uint32(p) ntohl(*(uint32_t *) (p)) - -#define ngx_quic_write_uint16 ngx_quic_write_uint16_aligned -#define ngx_quic_write_uint32 ngx_quic_write_uint32_aligned - -#else - -#define ngx_quic_parse_uint16(p) ((p)[0] << 8 | (p)[1]) -#define ngx_quic_parse_uint32(p) \ - ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) - -#define ngx_quic_write_uint16(p, s) \ - ((p)[0] = (u_char) ((s) >> 8), \ - (p)[1] = (u_char) (s), \ - (p) + sizeof(uint16_t)) - -#define ngx_quic_write_uint32(p, s) \ - ((p)[0] = (u_char) ((s) >> 24), \ - (p)[1] = (u_char) ((s) >> 16), \ - (p)[2] = (u_char) ((s) >> 8), \ - (p)[3] = (u_char) (s), \ - (p) + sizeof(uint32_t)) - -#endif - - -#define ngx_quic_write_uint16_aligned(p, s) \ - (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) - -#define ngx_quic_write_uint32_aligned(p, s) \ - (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) - -#define ngx_quic_varint_len(value) \ - ((value) <= 63 ? 1 \ - : ((uint32_t) value) <= 16383 ? 2 \ - : ((uint64_t) value) <= 1073741823 ? 4 \ - : 8) +#define quic_version 0xff000018 /* draft-24 (ngtcp2) */ +//#define quic_version 0xff00001b /* draft-27 (FFN 76) */ struct ngx_quic_stream_s { @@ -71,35 +22,6 @@ struct ngx_quic_stream_s { void *data; }; -typedef struct ngx_quic_secret_s ngx_quic_secret_t; -typedef enum ssl_encryption_level_t ngx_quic_level_t; - -typedef struct { - ngx_quic_secret_t *secret; - ngx_uint_t type; - ngx_uint_t *number; - ngx_uint_t flags; - uint32_t version; - ngx_str_t token; - ngx_quic_level_t level; - - /* filled in by parser */ - ngx_buf_t *raw; /* udp datagram from wire */ - - u_char *data; /* quic packet */ - size_t len; - - /* cleartext fields */ - ngx_str_t dcid; - ngx_str_t scid; - - uint64_t pn; - - ngx_str_t payload; /* decrypted payload */ - -} ngx_quic_header_t; - -void ngx_quic_build_int(u_char **pos, uint64_t value); void ngx_quic_init_ssl_methods(SSL_CTX* ctx); diff --git a/src/event/ngx_event_quic_protection.c b/src/event/ngx_event_quic_protection.c --- a/src/event/ngx_event_quic_protection.c +++ b/src/event/ngx_event_quic_protection.c @@ -702,30 +702,7 @@ ngx_quic_create_long_packet(ngx_pool_t * return NGX_ERROR; } - p = ad.data; - - *p++ = pkt->flags; - - p = ngx_quic_write_uint32(p, quic_version); - - *p++ = pkt->scid.len; - p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len); - - *p++ = pkt->dcid.len; - p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len); - - if (pkt->level == ssl_encryption_initial) { - ngx_quic_build_int(&p, pkt->token.len); - } - - ngx_quic_build_int(&p, out.len + 1); // length (inc. pnl) - pnp = p; - - pn = *pkt->number; - - *p++ = pn; - - ad.len = p - ad.data; + ad.len = ngx_quic_create_long_header(pkt, &ad, out.len, &pnp); ngx_quic_hexdump0(log, "ad", ad.data, ad.len); @@ -734,9 +711,8 @@ ngx_quic_create_long_packet(ngx_pool_t * } nonce = ngx_pstrdup(pool, &pkt->secret->iv); - if (pkt->level == ssl_encryption_handshake) { - nonce[11] ^= pn; - } + pn = *pkt->number; + nonce[11] ^= pn; ngx_quic_hexdump0(log, "server_iv", pkt->secret->iv.data, 12); ngx_quic_hexdump0(log, "nonce", nonce, 12); diff --git a/src/event/ngx_event_quic_protection.h b/src/event/ngx_event_quic_protection.h --- a/src/event/ngx_event_quic_protection.h +++ b/src/event/ngx_event_quic_protection.h @@ -8,12 +8,12 @@ #define _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_ -struct ngx_quic_secret_s { +typedef struct ngx_quic_secret_s { ngx_str_t secret; ngx_str_t key; ngx_str_t iv; ngx_str_t hp; -}; +} ngx_quic_secret_t; typedef struct { diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_quic_transport.c @@ -0,0 +1,588 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#if (NGX_HAVE_NONALIGNED) + +#define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p)) +#define ngx_quic_parse_uint32(p) ntohl(*(uint32_t *) (p)) + +#define ngx_quic_write_uint16 ngx_quic_write_uint16_aligned +#define ngx_quic_write_uint32 ngx_quic_write_uint32_aligned + +#else + +#define ngx_quic_parse_uint16(p) ((p)[0] << 8 | (p)[1]) +#define ngx_quic_parse_uint32(p) \ + ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) + +#define ngx_quic_write_uint16(p, s) \ + ((p)[0] = (u_char) ((s) >> 8), \ + (p)[1] = (u_char) (s), \ + (p) + sizeof(uint16_t)) + +#define ngx_quic_write_uint32(p, s) \ + ((p)[0] = (u_char) ((s) >> 24), \ + (p)[1] = (u_char) ((s) >> 16), \ + (p)[2] = (u_char) ((s) >> 8), \ + (p)[3] = (u_char) (s), \ + (p) + sizeof(uint32_t)) + +#endif + + +#define ngx_quic_write_uint16_aligned(p, s) \ + (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) + +#define ngx_quic_write_uint32_aligned(p, s) \ + (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) + +#define ngx_quic_varint_len(value) \ + ((value) <= 63 ? 1 \ + : ((uint32_t) value) <= 16383 ? 2 \ + : ((uint64_t) value) <= 1073741823 ? 4 \ + : 8) + + +static uint64_t ngx_quic_parse_int(u_char **pos); +static void ngx_quic_build_int(u_char **pos, uint64_t value); + +static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack); +static size_t ngx_quic_create_crypto(u_char *p, + ngx_quic_crypto_frame_t *crypto); +static size_t ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf); + + +/* literal errors indexed by corresponding value */ +static char *ngx_quic_errors[] = { + "NO_ERROR", + "INTERNAL_ERROR", + "SERVER_BUSY", + "FLOW_CONTROL_ERROR", + "STREAM_LIMIT_ERROR", + "STREAM_STATE_ERROR", + "FINAL_SIZE_ERROR", + "FRAME_ENCODING_ERROR", + "TRANSPORT_PARAMETER_ERROR", + "CONNECTION_ID_LIMIT_ERROR", + "PROTOCOL_VIOLATION", + "INVALID_TOKEN", + "", + "CRYPTO_BUFFER_EXCEEDED", + "CRYPTO_ERROR", +}; + + +static uint64_t +ngx_quic_parse_int(u_char **pos) +{ + u_char *p; + uint64_t value; + ngx_uint_t len; + + p = *pos; + len = 1 << ((*p & 0xc0) >> 6); + value = *p++ & 0x3f; + + while (--len) { + value = (value << 8) + *p++; + } + + *pos = p; + return value; +} + + +static void +ngx_quic_build_int(u_char **pos, uint64_t value) +{ + u_char *p; + ngx_uint_t len;//, len2; + + p = *pos; + len = 0; + + while (value >> ((1 << len) * 8 - 2)) { + len++; + } + + *p = len << 6; + +// len2 = + len = (1 << len); + len--; + *p |= value >> (len * 8); + p++; + + while (len) { + *p++ = value >> ((len-- - 1) * 8); + } + + *pos = p; +// return len2; +} + + +u_char * +ngx_quic_error_text(uint64_t error_code) +{ + return (u_char *) ngx_quic_errors[error_code]; +} + + +ngx_int_t +ngx_quic_parse_long_header(ngx_quic_header_t *pkt) +{ + u_char *p; + + p = pkt->data; + + ngx_quic_hexdump0(pkt->log, "long input", pkt->data, pkt->len); + + if (!(p[0] & NGX_QUIC_PKT_LONG)) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "not a long packet"); + return NGX_ERROR; + } + + pkt->flags = *p++; + + pkt->version = ngx_quic_parse_uint32(p); + p += sizeof(uint32_t); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0, + "quic flags:%xi version:%xD", pkt->flags, pkt->version); + + if (pkt->version != quic_version) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unsupported quic version"); + return NGX_ERROR; + } + + pkt->dcid.len = *p++; + pkt->dcid.data = p; + p += pkt->dcid.len; + + pkt->scid.len = *p++; + pkt->scid.data = p; + p += pkt->scid.len; + + pkt->raw->pos = p; + + return NGX_OK; +} + + +size_t +ngx_quic_create_long_header(ngx_quic_header_t *pkt, ngx_str_t *out, + size_t pkt_len, u_char **pnp) +{ + u_char *p, *start; + + p = start = out->data; + + *p++ = pkt->flags; + + p = ngx_quic_write_uint32(p, quic_version); + + *p++ = pkt->scid.len; + p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len); + + *p++ = pkt->dcid.len; + p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len); + + if (pkt->level == ssl_encryption_initial) { + ngx_quic_build_int(&p, pkt->token.len); + } + + ngx_quic_build_int(&p, pkt_len + 1); // length (inc. pnl) + + *pnp = p; + + *p++ = (uint64_t) (*pkt->number); + + return p - start; +} + + +ngx_int_t +ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid) +{ + u_char *p; + + p = pkt->data; + + ngx_quic_hexdump0(pkt->log, "short input", pkt->data, pkt->len); + + if ((p[0] & NGX_QUIC_PKT_LONG)) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "not a short packet"); + return NGX_ERROR; + } + + pkt->flags = *p++; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, + "quic flags:%xi", pkt->flags); + + if (ngx_memcmp(p, dcid->data, dcid->len) != 0) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unexpected quic dcid"); + return NGX_ERROR; + } + + pkt->dcid.len = dcid->len; + pkt->dcid.data = p; + p += pkt->dcid.len; + + pkt->raw->pos = p; + + return NGX_OK; +} + + +ngx_int_t +ngx_quic_parse_initial_header(ngx_quic_header_t *pkt) +{ + u_char *p; + ngx_int_t plen; + + p = pkt->raw->pos; + + pkt->token.len = ngx_quic_parse_int(&p); + pkt->token.data = p; + + p += pkt->token.len; + + plen = ngx_quic_parse_int(&p); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, + "quic packet length: %d", plen); + + if (plen > pkt->data + pkt->len - p) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "truncated initial packet"); + return NGX_ERROR; + } + + pkt->raw->pos = p; + pkt->len = plen; + + ngx_quic_hexdump0(pkt->log, "DCID", pkt->dcid.data, pkt->dcid.len); + ngx_quic_hexdump0(pkt->log, "SCID", pkt->scid.data, pkt->scid.len); + ngx_quic_hexdump0(pkt->log, "token", pkt->token.data, pkt->token.len); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, + "quic packet length: %d", plen); + + return NGX_OK; +} + + +ngx_int_t +ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt) +{ + u_char *p; + ngx_int_t plen; + + p = pkt->raw->pos; + + plen = ngx_quic_parse_int(&p); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, + "quic packet length: %d", plen); + + if (plen > pkt->data + pkt->len - p) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "truncated handshake packet"); + return NGX_ERROR; + } + + pkt->raw->pos = p; + pkt->len = plen; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0, + "quic packet length: %d", plen); + + return NGX_OK; +} + + +#define ngx_quic_stream_bit_off(val) (((val) & 0x04) ? 1 : 0) +#define ngx_quic_stream_bit_len(val) (((val) & 0x02) ? 1 : 0) +#define ngx_quic_stream_bit_fin(val) (((val) & 0x01) ? 1 : 0) + +ssize_t +ngx_quic_parse_frame(u_char *start, u_char *end, ngx_quic_frame_t *frame) +{ + u_char *p; + + size_t npad; + + p = start; + + frame->type = *p++; // TODO: check overflow (p < end) + + switch (frame->type) { + + case NGX_QUIC_FT_CRYPTO: + frame->u.crypto.offset = *p++; + frame->u.crypto.len = ngx_quic_parse_int(&p); + frame->u.crypto.data = p; + p += frame->u.crypto.len; + + break; + + case NGX_QUIC_FT_PADDING: + npad = 0; + while (p < end && *p == NGX_QUIC_FT_PADDING) { // XXX + p++; npad++; + } + + break; + + case NGX_QUIC_FT_ACK: + case NGX_QUIC_FT_ACK_ECN: + + frame->u.ack.largest = ngx_quic_parse_int(&p); + frame->u.ack.delay = ngx_quic_parse_int(&p); + frame->u.ack.range_count =ngx_quic_parse_int(&p); + frame->u.ack.first_range =ngx_quic_parse_int(&p); + + if (frame->u.ack.range_count) { + frame->u.ack.ranges[0] = ngx_quic_parse_int(&p); + } + + if (frame->type ==NGX_QUIC_FT_ACK_ECN) { + return NGX_ERROR; + } + + break; + + case NGX_QUIC_FT_PING: + break; + + case NGX_QUIC_FT_NEW_CONNECTION_ID: + + frame->u.ncid.seqnum = ngx_quic_parse_int(&p); + frame->u.ncid.retire = ngx_quic_parse_int(&p); + frame->u.ncid.len = *p++; + ngx_memcpy(frame->u.ncid.cid, p, frame->u.ncid.len); + p += frame->u.ncid.len; + + ngx_memcpy(frame->u.ncid.srt, p, 16); + p += 16; + + break; + + case NGX_QUIC_FT_CONNECTION_CLOSE: + + frame->u.close.error_code = ngx_quic_parse_int(&p); + frame->u.close.frame_type = ngx_quic_parse_int(&p); // not in 0x1d CC + frame->u.close.reason.len = ngx_quic_parse_int(&p); + frame->u.close.reason.data = p; + p += frame->u.close.reason.len; + + if (frame->u.close.error_code > NGX_QUIC_ERR_LAST) { + frame->u.close.error_code = NGX_QUIC_ERR_LAST; + } + break; + + case NGX_QUIC_FT_STREAM0: + case NGX_QUIC_FT_STREAM1: + case NGX_QUIC_FT_STREAM2: + case NGX_QUIC_FT_STREAM3: + case NGX_QUIC_FT_STREAM4: + case NGX_QUIC_FT_STREAM5: + case NGX_QUIC_FT_STREAM6: + case NGX_QUIC_FT_STREAM7: + + frame->u.stream.type = frame->type; + + frame->u.stream.off = ngx_quic_stream_bit_off(frame->type); + frame->u.stream.len = ngx_quic_stream_bit_len(frame->type); + frame->u.stream.fin = ngx_quic_stream_bit_fin(frame->type); + + frame->u.stream.stream_id = ngx_quic_parse_int(&p); + if (frame->type & 0x04) { + frame->u.stream.offset = ngx_quic_parse_int(&p); + } else { + frame->u.stream.offset = 0; + } + + if (frame->type & 0x02) { + frame->u.stream.length = ngx_quic_parse_int(&p); + } else { + frame->u.stream.length = end - p; /* up to packet end */ + } + + frame->u.stream.data = p; + + p += frame->u.stream.length; + + break; + + default: + return NGX_ERROR; + } + + return p - start; +} + + +ssize_t +ngx_quic_create_frame(u_char *p, u_char *end, ngx_quic_frame_t *f) +{ + // TODO: handle end arg + + switch (f->type) { + case NGX_QUIC_FT_ACK: + return ngx_quic_create_ack(p, &f->u.ack); + + case NGX_QUIC_FT_CRYPTO: + return ngx_quic_create_crypto(p, &f->u.crypto); + + case NGX_QUIC_FT_STREAM0: + case NGX_QUIC_FT_STREAM1: + case NGX_QUIC_FT_STREAM2: + case NGX_QUIC_FT_STREAM3: + case NGX_QUIC_FT_STREAM4: + case NGX_QUIC_FT_STREAM5: + case NGX_QUIC_FT_STREAM6: + case NGX_QUIC_FT_STREAM7: + return ngx_quic_create_stream(p, &f->u.stream); + + default: + /* BUG: unsupported frame type generated */ + return NGX_ERROR; + } +} + + +size_t +ngx_quic_frame_len(ngx_quic_frame_t *frame) +{ + switch (frame->type) { + case NGX_QUIC_FT_ACK: + return ngx_quic_create_ack(NULL, &frame->u.ack); + case NGX_QUIC_FT_CRYPTO: + return ngx_quic_create_crypto(NULL, &frame->u.crypto); + + case NGX_QUIC_FT_STREAM0: + case NGX_QUIC_FT_STREAM1: + case NGX_QUIC_FT_STREAM2: + case NGX_QUIC_FT_STREAM3: + case NGX_QUIC_FT_STREAM4: + case NGX_QUIC_FT_STREAM5: + case NGX_QUIC_FT_STREAM6: + case NGX_QUIC_FT_STREAM7: + return ngx_quic_create_stream(NULL, &frame->u.stream); + default: + /* BUG: unsupported frame type generated */ + return 0; + } +} + + +static size_t +ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack) +{ + size_t len; + + /* minimal ACK packet */ + + if (p == NULL) { + len = ngx_quic_varint_len(NGX_QUIC_FT_ACK); + len += ngx_quic_varint_len(ack->pn); + len += ngx_quic_varint_len(0); + len += ngx_quic_varint_len(0); + len += ngx_quic_varint_len(ack->pn); + + return len; + } + + ngx_quic_build_int(&p, NGX_QUIC_FT_ACK); + ngx_quic_build_int(&p, ack->pn); + ngx_quic_build_int(&p, 0); + ngx_quic_build_int(&p, 0); + ngx_quic_build_int(&p, ack->pn); + + return 5; +} + + +static size_t +ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto) +{ + size_t len; + u_char *start; + + if (p == NULL) { + len = ngx_quic_varint_len(NGX_QUIC_FT_CRYPTO); + len += ngx_quic_varint_len(crypto->offset); + len += ngx_quic_varint_len(crypto->len); + len += crypto->len; + + return len; + } + + start = p; + + ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO); + ngx_quic_build_int(&p, crypto->offset); + ngx_quic_build_int(&p, crypto->len); + p = ngx_cpymem(p, crypto->data, crypto->len); + + return p - start; +} + + +static size_t +ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf) +{ + size_t len; + u_char *start; + + if (!sf->len) { +#if 0 + ngx_log_error(NGX_LOG_INFO, log, 0, + "attempt to generate a stream frame without length"); +#endif + // XXX: handle error in caller + return NGX_ERROR; + } + + if (p == NULL) { + len = ngx_quic_varint_len(sf->type); + + if (sf->off) { + len += ngx_quic_varint_len(sf->offset); + } + + len += ngx_quic_varint_len(sf->stream_id); + + /* length is always present in generated frames */ + len += ngx_quic_varint_len(sf->length); + + len += sf->length; + + return len; + } + + start = p; + + ngx_quic_build_int(&p, sf->type); + ngx_quic_build_int(&p, sf->stream_id); + + if (sf->off) { + ngx_quic_build_int(&p, sf->offset); + } + + /* length is always present in generated frames */ + ngx_quic_build_int(&p, sf->length); + + p = ngx_cpymem(p, sf->data, sf->length); + + return p - start; +} diff --git a/src/event/ngx_event_quic_transport.h b/src/event/ngx_event_quic_transport.h new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_quic_transport.h @@ -0,0 +1,177 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_EVENT_QUIC_WIRE_H_INCLUDED_ +#define _NGX_EVENT_QUIC_WIRE_H_INCLUDED_ + + +#include + + +/* 17.2. Long Header Packets */ +#define NGX_QUIC_PKT_LONG 0x80 + +#define NGX_QUIC_PKT_INITIAL 0xC0 +#define NGX_QUIC_PKT_HANDSHAKE 0xE0 + +/* 12.4. Frames and Frame Types */ +#define NGX_QUIC_FT_PADDING 0x00 +#define NGX_QUIC_FT_PING 0x01 +#define NGX_QUIC_FT_ACK 0x02 +#define NGX_QUIC_FT_ACK_ECN 0x03 +#define NGX_QUIC_FT_RESET_STREAM 0x04 +#define NGX_QUIC_FT_STOP_SENDING 0x05 +#define NGX_QUIC_FT_CRYPTO 0x06 +#define NGX_QUIC_FT_NEW_TOKEN 0x07 +#define NGX_QUIC_FT_STREAM0 0x08 +#define NGX_QUIC_FT_STREAM1 0x09 +#define NGX_QUIC_FT_STREAM2 0x0A +#define NGX_QUIC_FT_STREAM3 0x0B +#define NGX_QUIC_FT_STREAM4 0x0C +#define NGX_QUIC_FT_STREAM5 0x0D +#define NGX_QUIC_FT_STREAM6 0x0E +#define NGX_QUIC_FT_STREAM7 0x0F +#define NGX_QUIC_FT_MAX_DATA 0x10 +#define NGX_QUIC_FT_MAX_STREAM_DATA 0x11 +#define NGX_QUIC_FT_MAX_STREAMS 0x12 +#define NGX_QUIC_FT_MAX_STREAMS2 0x13 // XXX +#define NGX_QUIC_FT_DATA_BLOCKED 0x14 +#define NGX_QUIC_FT_STREAM_DATA_BLOCKED 0x15 +#define NGX_QUIC_FT_STREAMS_BLOCKED 0x16 +#define NGX_QUIC_FT_STREAMS_BLOCKED2 0x17 // XXX +#define NGX_QUIC_FT_NEW_CONNECTION_ID 0x18 +#define NGX_QUIC_FT_RETIRE_CONNECTION_ID 0x19 +#define NGX_QUIC_FT_PATH_CHALLENGE 0x1A +#define NGX_QUIC_FT_PATH_RESPONSE 0x1B +#define NGX_QUIC_FT_CONNECTION_CLOSE 0x1C +#define NGX_QUIC_FT_CONNECTION_CLOSE2 0x1D // XXX +#define NGX_QUIC_FT_HANDSHAKE_DONE 0x1E + +/* 22.4. QUIC Transport Error Codes Registry */ +#define NGX_QUIC_ERR_NO_ERROR 0x00 +#define NGX_QUIC_ERR_INTERNAL_ERROR 0x01 +#define NGX_QUIC_ERR_SERVER_BUSY 0x02 +#define NGX_QUIC_ERR_FLOW_CONTROL_ERROR 0x03 +#define NGX_QUIC_ERR_STREAM_LIMIT_ERROR 0x04 +#define NGX_QUIC_ERR_STREAM_STATE_ERROR 0x05 +#define NGX_QUIC_ERR_FINAL_SIZE_ERROR 0x06 +#define NGX_QUIC_ERR_FRAME_ENCODING_ERROR 0x07 +#define NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR 0x08 +#define NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR 0x09 +#define NGX_QUIC_ERR_PROTOCOL_VIOLATION 0x0A +#define NGX_QUIC_ERR_INVALID_TOKEN 0x0B +/* 0xC is not defined */ +#define NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED 0x0D +#define NGX_QUIC_ERR_CRYPTO_ERROR 0x10 + +#define NGX_QUIC_ERR_LAST NGX_QUIC_ERR_CRYPTO_ERROR + + +typedef struct { + ngx_uint_t pn; + uint64_t largest; + uint64_t delay; + uint64_t range_count; + uint64_t first_range; + uint64_t ranges[20]; + /* TODO: ecn counts */ +} ngx_quic_ack_frame_t; + + +typedef struct { + size_t offset; + size_t len; + u_char *data; +} ngx_quic_crypto_frame_t; + + +typedef struct { + uint64_t seqnum; + uint64_t retire; + uint64_t len; + u_char cid[20]; + u_char srt[16]; +} ngx_quic_new_conn_id_frame_t; + + +typedef struct { + uint8_t type; + uint64_t stream_id; + uint64_t offset; + uint64_t length; + unsigned off:1; + unsigned len:1; + unsigned fin:1; + u_char *data; +} ngx_quic_stream_frame_t; + + +typedef struct { + uint64_t error_code; + uint64_t frame_type; + ngx_str_t reason; +} ngx_quic_close_frame_t; + + +typedef struct ngx_quic_frame_s ngx_quic_frame_t; + +struct ngx_quic_frame_s { + ngx_uint_t type; + enum ssl_encryption_level_t level; + ngx_quic_frame_t *next; + union { + ngx_quic_ack_frame_t ack; + ngx_quic_crypto_frame_t crypto; + ngx_quic_new_conn_id_frame_t ncid; + ngx_quic_stream_frame_t stream; + ngx_quic_close_frame_t close; + } u; + u_char info[128]; // for debug +}; + + +typedef struct { + ngx_log_t *log; + + struct ngx_quic_secret_s *secret; + ngx_uint_t type; + ngx_uint_t *number; + ngx_uint_t flags; + uint32_t version; + ngx_str_t token; + enum ssl_encryption_level_t level; + + /* filled in by parser */ + ngx_buf_t *raw; /* udp datagram */ + + u_char *data; /* quic packet */ + size_t len; + + /* cleartext fields */ + ngx_str_t dcid; + ngx_str_t scid; + uint64_t pn; + ngx_str_t payload; /* decrypted */ +} ngx_quic_header_t; + + +u_char *ngx_quic_error_text(uint64_t error_code); + +ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt); +size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, ngx_str_t *out, + size_t pkt_len, u_char **pnp); + +ngx_int_t ngx_quic_parse_short_header(ngx_quic_header_t *pkt, + ngx_str_t *dcid); +ngx_int_t ngx_quic_parse_initial_header(ngx_quic_header_t *pkt); +ngx_int_t ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt); + +ssize_t ngx_quic_parse_frame(u_char *start, u_char *end, + ngx_quic_frame_t *frame); +ssize_t ngx_quic_create_frame(u_char *p, u_char *end, ngx_quic_frame_t *f); +size_t ngx_quic_frame_len(ngx_quic_frame_t *frame); + +#endif /* _NGX_EVENT_QUIC_WIRE_H_INCLUDED_ */