static u_char *ngx_quic_copy_bytes(u_char *pos, u_char *end, size_t len,
u_char *dst);
+static ngx_int_t ngx_quic_frame_allowed(ngx_quic_header_t *pkt,
+ ngx_uint_t frame_type);
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);
ngx_quic_frame_t *f)
{
u_char *p;
- uint8_t flags;
uint64_t varint;
ngx_uint_t i;
- flags = pkt->flags;
p = start;
p = ngx_quic_parse_int(p, end, &varint);
f->type = varint;
+ if (ngx_quic_frame_allowed(pkt, f->type) != NGX_OK) {
+ return NGX_DECLINED;
+ }
+
switch (f->type) {
case NGX_QUIC_FT_CRYPTO:
- if (ngx_quic_pkt_zrtt(flags)) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.crypto.offset);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_PADDING:
- /* allowed in any packet type */
-
while (p < end && *p == NGX_QUIC_FT_PADDING) {
p++;
}
case NGX_QUIC_FT_ACK:
case NGX_QUIC_FT_ACK_ECN:
- if (ngx_quic_pkt_zrtt(flags)) {
- goto not_allowed;
- }
-
if (!((p = ngx_quic_parse_int(p, end, &f->u.ack.largest))
&& (p = ngx_quic_parse_int(p, end, &f->u.ack.delay))
&& (p = ngx_quic_parse_int(p, end, &f->u.ack.range_count))
break;
case NGX_QUIC_FT_PING:
-
- /* allowed in any packet type */
-
break;
case NGX_QUIC_FT_NEW_CONNECTION_ID:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.ncid.seqnum);
if (p == NULL) {
goto error;
break;
case NGX_QUIC_FT_CONNECTION_CLOSE2:
-
- if (!ngx_quic_short_pkt(flags)) {
- goto not_allowed;
- }
-
/* fall through */
case NGX_QUIC_FT_CONNECTION_CLOSE:
- if (ngx_quic_pkt_zrtt(flags)) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.close.error_code);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_STREAM6:
case NGX_QUIC_FT_STREAM7:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
f->u.stream.type = f->type;
f->u.stream.off = ngx_quic_stream_bit_off(f->type);
case NGX_QUIC_FT_MAX_DATA:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.max_data.max_data);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_RESET_STREAM:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
if (!((p = ngx_quic_parse_int(p, end, &f->u.reset_stream.id))
&& (p = ngx_quic_parse_int(p, end, &f->u.reset_stream.error_code))
&& (p = ngx_quic_parse_int(p, end,
case NGX_QUIC_FT_STOP_SENDING:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.stop_sending.id);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_STREAMS_BLOCKED:
case NGX_QUIC_FT_STREAMS_BLOCKED2:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.streams_blocked.limit);
if (p == NULL) {
goto error;
break;
- case NGX_QUIC_FT_HANDSHAKE_DONE:
- /* only sent by server, not by client */
- goto not_allowed;
-
case NGX_QUIC_FT_NEW_TOKEN:
-
- if (!ngx_quic_short_pkt(flags)) {
- goto not_allowed;
- }
-
/* TODO: implement */
ngx_log_error(NGX_LOG_ALERT, pkt->log, 0,
case NGX_QUIC_FT_MAX_STREAMS:
case NGX_QUIC_FT_MAX_STREAMS2:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.max_streams.limit);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_MAX_STREAM_DATA:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.max_stream_data.id);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_DATA_BLOCKED:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.data_blocked.limit);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_STREAM_DATA_BLOCKED:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.stream_data_blocked.id);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_RETIRE_CONNECTION_ID:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_parse_int(p, end, &f->u.retire_cid.sequence_number);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_PATH_CHALLENGE:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_copy_bytes(p, end, 8, f->u.path_challenge.data);
if (p == NULL) {
goto error;
case NGX_QUIC_FT_PATH_RESPONSE:
- if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
- goto not_allowed;
- }
-
p = ngx_quic_copy_bytes(p, end, 8, f->u.path_response.data);
if (p == NULL) {
goto error;
return p - start;
-not_allowed:
+error:
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
- "quic frame type 0x%xi is not "
- "allowed in packet with flags 0x%xi",
- f->type, pkt->flags);
+ "quic failed to parse frame type 0x%xi", f->type);
- return NGX_DECLINED;
+ return NGX_ERROR;
+}
-error:
+
+static ngx_int_t
+ngx_quic_frame_allowed(ngx_quic_header_t *pkt, ngx_uint_t frame_type)
+{
+ uint8_t ptype;
+
+ /* frame permissions per packet: 4 bits: IH01: 12.4, Table 3 */
+ static uint8_t ngx_quic_frame_masks[] = {
+ /* PADDING */ 0xF,
+ /* PING */ 0xF,
+ /* ACK */ 0xD,
+ /* ACK_ECN */ 0xD,
+ /* RESET_STREAM */ 0x3,
+ /* STOP_SENDING */ 0x3,
+ /* CRYPTO */ 0xD,
+ /* NEW_TOKEN */ 0x1,
+ /* STREAM0 */ 0x3,
+ /* STREAM1 */ 0x3,
+ /* STREAM2 */ 0x3,
+ /* STREAM3 */ 0x3,
+ /* STREAM4 */ 0x3,
+ /* STREAM5 */ 0x3,
+ /* STREAM6 */ 0x3,
+ /* STREAM7 */ 0x3,
+ /* MAX_DATA */ 0x3,
+ /* MAX_STREAM_DATA */ 0x3,
+ /* MAX_STREAMS */ 0x3,
+ /* MAX_STREAMS2 */ 0x3,
+ /* DATA_BLOCKED */ 0x3,
+ /* STREAM_DATA_BLOCKED */ 0x3,
+ /* STREAMS_BLOCKED */ 0x3,
+ /* STREAMS_BLOCKED2 */ 0x3,
+ /* NEW_CONNECTION_ID */ 0x3,
+ /* RETIRE_CONNECTION_ID */ 0x3,
+ /* PATH_CHALLENGE */ 0x3,
+ /* PATH_RESPONSE */ 0x3,
+ /* CONNECTION_CLOSE */ 0xD,
+ /* CONNECTION_CLOSE2 */ 0x1,
+ /* HANDSHAKE_DONE */ 0x0, /* only sent by server */
+ };
+
+ if (ngx_quic_long_pkt(pkt->flags)) {
+
+ if (ngx_quic_pkt_in(pkt->flags)) {
+ ptype = 8; /* initial */
+
+ } else if (ngx_quic_pkt_hs(pkt->flags)) {
+ ptype = 4; /* handshake */
+
+ } else {
+ ptype = 2; /* zero-rtt */
+ }
+
+ } else {
+ ptype = 1; /* application data */
+ }
+
+ if (ptype & ngx_quic_frame_masks[frame_type]) {
+ return NGX_OK;
+ }
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
- "quic failed to parse frame type 0x%xi", f->type);
+ "quic frame type 0x%xi is not "
+ "allowed in packet with flags 0x%xi",
+ frame_type, pkt->flags);
- return NGX_ERROR;
+ return NGX_DECLINED;
}
#include <ngx_core.h>
-#define ngx_quic_long_pkt(flags) ((flags) & 0x80) /* 17.2 */
-#define ngx_quic_short_pkt(flags) (((flags) & 0x80) == 0) /* 17.3 */
+/* QUIC flags in first byte, see quic-transport 17.2 and 17.3 */
+
+#define NGX_QUIC_PKT_LONG 0x80 /* header form */
+#define NGX_QUIC_PKT_FIXED_BIT 0x40
+#define NGX_QUIC_PKT_TYPE 0x30 /* in long packet */
+#define NGX_QUIC_PKT_KPHASE 0x04 /* in short packet */
+
+#define ngx_quic_long_pkt(flags) ((flags) & NGX_QUIC_PKT_LONG)
+#define ngx_quic_short_pkt(flags) (((flags) & NGX_QUIC_PKT_LONG) == 0)
/* Long packet types */
-#define NGX_QUIC_PKT_INITIAL 0xC0 /* 17.2.2 */
-#define NGX_QUIC_PKT_ZRTT 0xD0 /* 17.2.3 */
-#define NGX_QUIC_PKT_HANDSHAKE 0xE0 /* 17.2.4 */
-#define NGX_QUIC_PKT_RETRY 0xF0 /* 17.2.5 */
-#define NGX_QUIC_PKT_KPHASE 0x04 /* 17.3 */
-
-#define ngx_quic_pkt_in(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_INITIAL)
-#define ngx_quic_pkt_zrtt(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_ZRTT)
-#define ngx_quic_pkt_hs(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_HANDSHAKE)
-#define ngx_quic_pkt_retry(flags) (((flags) & 0xF0) == NGX_QUIC_PKT_RETRY)
+#define NGX_QUIC_PKT_INITIAL 0x00
+#define NGX_QUIC_PKT_ZRTT 0x10
+#define NGX_QUIC_PKT_HANDSHAKE 0x20
+#define NGX_QUIC_PKT_RETRY 0x30
+
+#define ngx_quic_pkt_in(flags) \
+ (((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_INITIAL)
+#define ngx_quic_pkt_zrtt(flags) \
+ (((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_ZRTT)
+#define ngx_quic_pkt_hs(flags) \
+ (((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_HANDSHAKE)
+#define ngx_quic_pkt_retry(flags) \
+ (((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_RETRY)
+
/* 12.4. Frames and Frame Types */
#define NGX_QUIC_FT_PADDING 0x00