changeset 8251:c217a907ce42 quic

Added checks for permitted frame types. + cleanup in macros for packet types + some style fixes in quic_transport.h (case, indentation)
author Vladimir Homutov <vl@nginx.com>
date Fri, 20 Mar 2020 20:03:44 +0300
parents 8f9cb6d66662
children c955b7eaa2d9
files src/event/ngx_event_quic.c src/event/ngx_event_quic_protection.c src/event/ngx_event_quic_transport.c src/event/ngx_event_quic_transport.h
diffstat 4 files changed, 172 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -336,7 +336,7 @@ ngx_quic_new_connection(ngx_connection_t
         return NGX_ERROR;
     }
 
-    if ((pkt->flags & 0xf0) != NGX_QUIC_PKT_INITIAL) {
+    if (!ngx_quic_pkt_in(pkt->flags)) {
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
                       "invalid initial packet: 0x%xi", pkt->flags);
         return NGX_ERROR;
@@ -563,20 +563,21 @@ ngx_quic_input(ngx_connection_t *c, ngx_
         pkt.data = p;
         pkt.len = b->last - p;
         pkt.log = c->log;
+        pkt.flags = p[0];
 
-        if (p[0] == 0) {
+        if (pkt.flags == 0) {
             /* XXX: no idea WTF is this, just ignore */
             ngx_log_error(NGX_LOG_ALERT, c->log, 0, "FIREFOX: ZEROES");
             break;
         }
 
         // TODO: check current state
-        if (p[0] & NGX_QUIC_PKT_LONG) {
+        if (ngx_quic_long_pkt(pkt.flags)) {
 
-            if ((p[0] & 0xf0) == NGX_QUIC_PKT_INITIAL) {
+            if (ngx_quic_pkt_in(pkt.flags)) {
                 rc = ngx_quic_initial_input(c, &pkt);
 
-            } else if ((p[0] & 0xf0) == NGX_QUIC_PKT_HANDSHAKE) {
+            } else if (ngx_quic_pkt_hs(pkt.flags)) {
                 rc = ngx_quic_handshake_input(c, &pkt);
 
             } else {
@@ -665,7 +666,7 @@ ngx_quic_handshake_input(ngx_connection_
         return NGX_ERROR;
     }
 
-    if ((pkt->flags & 0xf0) != NGX_QUIC_PKT_HANDSHAKE) {
+    if (!ngx_quic_pkt_hs(pkt->flags)) {
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
                       "invalid packet type: 0x%xi", pkt->flags);
         return NGX_ERROR;
@@ -734,6 +735,14 @@ ngx_quic_payload_handler(ngx_connection_
     while (p < end) {
 
         len = ngx_quic_parse_frame(pkt, p, end, &frame);
+
+        if (len == NGX_DECLINED) {
+            /* TODO: handle protocol violation:
+             *       such frame not allowed in this packet
+             */
+            return NGX_ERROR;
+        }
+
         if (len < 0) {
             return NGX_ERROR;
         }
--- a/src/event/ngx_event_quic_protection.c
+++ b/src/event/ngx_event_quic_protection.c
@@ -904,7 +904,7 @@ ngx_quic_decrypt(ngx_pool_t *pool, ngx_s
         return NGX_ERROR;
     }
 
-    if (pkt->flags & NGX_QUIC_PKT_LONG) {
+    if (ngx_quic_long_pkt(pkt->flags)) {
         clearflags = pkt->flags ^ (mask[0] & 0x0f);
 
     } else {
@@ -926,7 +926,7 @@ ngx_quic_decrypt(ngx_pool_t *pool, ngx_s
 
     in.data = p;
 
-    if (pkt->flags & NGX_QUIC_PKT_LONG) {
+    if (ngx_quic_long_pkt(pkt->flags)) {
         in.len = pkt->len - pnl;
 
     } else {
--- a/src/event/ngx_event_quic_transport.c
+++ b/src/event/ngx_event_quic_transport.c
@@ -261,7 +261,7 @@ ngx_quic_parse_long_header(ngx_quic_head
         return NGX_ERROR;
     }
 
-    if (!(pkt->flags & NGX_QUIC_PKT_LONG)) {
+    if (!ngx_quic_long_pkt(pkt->flags)) {
         ngx_log_error(NGX_LOG_ERR, pkt->log, 0, "not a long packet");
         return NGX_ERROR;
     }
@@ -368,7 +368,7 @@ ngx_quic_parse_short_header(ngx_quic_hea
         return NGX_ERROR;
     }
 
-    if (pkt->flags & NGX_QUIC_PKT_LONG) {
+    if (!ngx_quic_short_pkt(pkt->flags)) {
         ngx_log_error(NGX_LOG_ERR, pkt->log, 0, "not a short packet");
         return NGX_ERROR;
     }
@@ -489,12 +489,12 @@ ngx_quic_parse_frame(ngx_quic_header_t *
     ngx_quic_frame_t *f)
 {
     u_char    *p;
+    uint8_t    flags;
     uint64_t   varint;
 
+    flags = pkt->flags;
     p = start;
 
-    /* TODO: add a check if frame is allowed in this type of packet */
-
     p = ngx_quic_parse_int(p, end, &varint);
     if (p == NULL) {
         ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
@@ -508,6 +508,10 @@ ngx_quic_parse_frame(ngx_quic_header_t *
 
     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) {
             ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
@@ -539,6 +543,9 @@ ngx_quic_parse_frame(ngx_quic_header_t *
         break;
 
     case NGX_QUIC_FT_PADDING:
+
+        /* allowed in any packet type */
+
         while (p < end && *p == NGX_QUIC_FT_PADDING) {
             p++;
         }
@@ -548,6 +555,10 @@ ngx_quic_parse_frame(ngx_quic_header_t *
     case NGX_QUIC_FT_ACK:
     case NGX_QUIC_FT_ACK_ECN:
 
+        if (ngx_quic_pkt_zrtt(flags)) {
+            goto not_allowed;
+        }
+
         p = ngx_quic_parse_int_multi(p, end, &f->u.ack.largest,
                                      &f->u.ack.delay, &f->u.ack.range_count,
                                      &f->u.ack.first_range, NULL);
@@ -583,10 +594,17 @@ ngx_quic_parse_frame(ngx_quic_header_t *
         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_multi(p, end, &f->u.ncid.seqnum,
                                      &f->u.ncid.retire, NULL);
         if (p == NULL) {
@@ -621,8 +639,19 @@ ngx_quic_parse_frame(ngx_quic_header_t *
                        f->u.ncid.seqnum, f->u.ncid.retire, f->u.ncid.len);
         break;
 
+    case NGX_QUIC_FT_CONNECTION_CLOSE2:
+
+        if (!ngx_quic_short_pkt(flags)) {
+            goto not_allowed;
+        }
+
+        /* fall through */
+
     case NGX_QUIC_FT_CONNECTION_CLOSE:
-    case NGX_QUIC_FT_CONNECTION_CLOSE2:
+
+        if (ngx_quic_pkt_zrtt(flags)) {
+            goto not_allowed;
+        }
 
         p = ngx_quic_parse_int(p, end, &f->u.close.error_code);
         if (p == NULL) {
@@ -685,6 +714,10 @@ ngx_quic_parse_frame(ngx_quic_header_t *
     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);
@@ -743,6 +776,10 @@ ngx_quic_parse_frame(ngx_quic_header_t *
 
     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) {
             ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
@@ -757,6 +794,10 @@ ngx_quic_parse_frame(ngx_quic_header_t *
 
     case NGX_QUIC_FT_RESET_STREAM:
 
+        if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
+            goto not_allowed;
+        }
+
         p = ngx_quic_parse_int_multi(p, end, &f->u.reset_stream.id,
                                      &f->u.reset_stream.error_code,
                                      &f->u.reset_stream.final_size, NULL);
@@ -775,6 +816,10 @@ ngx_quic_parse_frame(ngx_quic_header_t *
 
     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_multi(p, end, &f->u.stop_sending.id,
                                      &f->u.stop_sending.error_code, NULL);
         if (p == NULL) {
@@ -792,6 +837,10 @@ ngx_quic_parse_frame(ngx_quic_header_t *
     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) {
             ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
@@ -809,14 +858,52 @@ ngx_quic_parse_frame(ngx_quic_header_t *
 
         break;
 
+    /* TODO: implement parsing for all frames below */
+    case NGX_QUIC_FT_NEW_TOKEN:
+    case NGX_QUIC_FT_HANDSHAKE_DONE:
+
+        if (!ngx_quic_short_pkt(flags)) {
+            goto not_allowed;
+        }
+
+        ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
+                      "unimplemented frame type 0x%xi in packet", f->type);
+
+        break;
+
+    case NGX_QUIC_FT_MAX_STREAMS:
+    case NGX_QUIC_FT_MAX_STREAMS2:
+    case NGX_QUIC_FT_MAX_STREAM_DATA:
+    case NGX_QUIC_FT_DATA_BLOCKED:
+    case NGX_QUIC_FT_STREAM_DATA_BLOCKED:
+    case NGX_QUIC_FT_RETIRE_CONNECTION_ID:
+    case NGX_QUIC_FT_PATH_CHALLENGE:
+    case NGX_QUIC_FT_PATH_RESPONSE:
+
+        if (!(ngx_quic_short_pkt(flags) || ngx_quic_pkt_zrtt(flags))) {
+            goto not_allowed;
+        }
+
+        ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
+                      "unimplemented frame type 0x%xi in packet", f->type);
+        break;
+
     default:
         ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
-                      "unsupported frame type 0x%xd in packet", f->type);
+                      "unknown frame type 0x%xi in packet", f->type);
 
         return NGX_ERROR;
     }
 
     return p - start;
+
+not_allowed:
+
+    ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
+                  "frame type 0x%xi is not allowed in packet with flags 0x%xi",
+                  f->type, pkt->flags);
+
+    return NGX_DECLINED;
 }
 
 
--- a/src/event/ngx_event_quic_transport.h
+++ b/src/event/ngx_event_quic_transport.h
@@ -11,62 +11,70 @@
 #include <ngx_event_openssl.h>
 
 
-/* 17.2.  Long Header Packets */
-#define NGX_QUIC_PKT_LONG                       0x80
+#define ngx_quic_long_pkt(flags)  ((flags) & 0x80)            /* 17.2   */
+#define ngx_quic_short_pkt(flags) (((flags) & 0x80) == 0)     /* 17.3   */
 
-#define NGX_QUIC_PKT_INITIAL                    0xC0
-#define NGX_QUIC_PKT_HANDSHAKE                  0xE0
+/* 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_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)
 
 /* 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
-#define NGX_QUIC_FT_HANDSHAKE_DONE              0x1E
+#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
+#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
+#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
+#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
+#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_BUFFER_EXCEEDED              0x0D
 /* 0xE is not defined */
-#define NGX_QUIC_ERR_CRYPTO_ERROR               0x10
+#define NGX_QUIC_ERR_CRYPTO_ERROR                        0x10
 
 #define NGX_QUIC_ERR_LAST  NGX_QUIC_ERR_CRYPTO_ERROR
 
@@ -81,11 +89,11 @@
 #define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI          0x07
 #define NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI             0x08
 #define NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI              0x09
-#define NGX_QUIC_TP_ACK_DELAY_EXPONENT                   0x0a
-#define NGX_QUIC_TP_MAX_ACK_DELAY                        0x0b
-#define NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION             0x0c
-#define NGX_QUIC_TP_PREFERRED_ADDRESS                    0x0d
-#define NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT           0x0e
+#define NGX_QUIC_TP_ACK_DELAY_EXPONENT                   0x0A
+#define NGX_QUIC_TP_MAX_ACK_DELAY                        0x0B
+#define NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION             0x0C
+#define NGX_QUIC_TP_PREFERRED_ADDRESS                    0x0D
+#define NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT           0x0E
 
 
 typedef struct {