comparison src/event/ngx_event_quic_transport.c @ 7690:ae35ccba7aa6 quic

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.
author Vladimir Homutov <vl@nginx.com>
date Wed, 18 Mar 2020 12:58:27 +0300
parents
children 78540e2160d0
comparison
equal deleted inserted replaced
7689:61f9b873e2e7 7690:ae35ccba7aa6
1
2 /*
3 * Copyright (C) Nginx, Inc.
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_event.h>
10
11
12 #if (NGX_HAVE_NONALIGNED)
13
14 #define ngx_quic_parse_uint16(p) ntohs(*(uint16_t *) (p))
15 #define ngx_quic_parse_uint32(p) ntohl(*(uint32_t *) (p))
16
17 #define ngx_quic_write_uint16 ngx_quic_write_uint16_aligned
18 #define ngx_quic_write_uint32 ngx_quic_write_uint32_aligned
19
20 #else
21
22 #define ngx_quic_parse_uint16(p) ((p)[0] << 8 | (p)[1])
23 #define ngx_quic_parse_uint32(p) \
24 ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
25
26 #define ngx_quic_write_uint16(p, s) \
27 ((p)[0] = (u_char) ((s) >> 8), \
28 (p)[1] = (u_char) (s), \
29 (p) + sizeof(uint16_t))
30
31 #define ngx_quic_write_uint32(p, s) \
32 ((p)[0] = (u_char) ((s) >> 24), \
33 (p)[1] = (u_char) ((s) >> 16), \
34 (p)[2] = (u_char) ((s) >> 8), \
35 (p)[3] = (u_char) (s), \
36 (p) + sizeof(uint32_t))
37
38 #endif
39
40
41 #define ngx_quic_write_uint16_aligned(p, s) \
42 (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))
43
44 #define ngx_quic_write_uint32_aligned(p, s) \
45 (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))
46
47 #define ngx_quic_varint_len(value) \
48 ((value) <= 63 ? 1 \
49 : ((uint32_t) value) <= 16383 ? 2 \
50 : ((uint64_t) value) <= 1073741823 ? 4 \
51 : 8)
52
53
54 static uint64_t ngx_quic_parse_int(u_char **pos);
55 static void ngx_quic_build_int(u_char **pos, uint64_t value);
56
57 static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack);
58 static size_t ngx_quic_create_crypto(u_char *p,
59 ngx_quic_crypto_frame_t *crypto);
60 static size_t ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf);
61
62
63 /* literal errors indexed by corresponding value */
64 static char *ngx_quic_errors[] = {
65 "NO_ERROR",
66 "INTERNAL_ERROR",
67 "SERVER_BUSY",
68 "FLOW_CONTROL_ERROR",
69 "STREAM_LIMIT_ERROR",
70 "STREAM_STATE_ERROR",
71 "FINAL_SIZE_ERROR",
72 "FRAME_ENCODING_ERROR",
73 "TRANSPORT_PARAMETER_ERROR",
74 "CONNECTION_ID_LIMIT_ERROR",
75 "PROTOCOL_VIOLATION",
76 "INVALID_TOKEN",
77 "",
78 "CRYPTO_BUFFER_EXCEEDED",
79 "CRYPTO_ERROR",
80 };
81
82
83 static uint64_t
84 ngx_quic_parse_int(u_char **pos)
85 {
86 u_char *p;
87 uint64_t value;
88 ngx_uint_t len;
89
90 p = *pos;
91 len = 1 << ((*p & 0xc0) >> 6);
92 value = *p++ & 0x3f;
93
94 while (--len) {
95 value = (value << 8) + *p++;
96 }
97
98 *pos = p;
99 return value;
100 }
101
102
103 static void
104 ngx_quic_build_int(u_char **pos, uint64_t value)
105 {
106 u_char *p;
107 ngx_uint_t len;//, len2;
108
109 p = *pos;
110 len = 0;
111
112 while (value >> ((1 << len) * 8 - 2)) {
113 len++;
114 }
115
116 *p = len << 6;
117
118 // len2 =
119 len = (1 << len);
120 len--;
121 *p |= value >> (len * 8);
122 p++;
123
124 while (len) {
125 *p++ = value >> ((len-- - 1) * 8);
126 }
127
128 *pos = p;
129 // return len2;
130 }
131
132
133 u_char *
134 ngx_quic_error_text(uint64_t error_code)
135 {
136 return (u_char *) ngx_quic_errors[error_code];
137 }
138
139
140 ngx_int_t
141 ngx_quic_parse_long_header(ngx_quic_header_t *pkt)
142 {
143 u_char *p;
144
145 p = pkt->data;
146
147 ngx_quic_hexdump0(pkt->log, "long input", pkt->data, pkt->len);
148
149 if (!(p[0] & NGX_QUIC_PKT_LONG)) {
150 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "not a long packet");
151 return NGX_ERROR;
152 }
153
154 pkt->flags = *p++;
155
156 pkt->version = ngx_quic_parse_uint32(p);
157 p += sizeof(uint32_t);
158
159 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
160 "quic flags:%xi version:%xD", pkt->flags, pkt->version);
161
162 if (pkt->version != quic_version) {
163 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unsupported quic version");
164 return NGX_ERROR;
165 }
166
167 pkt->dcid.len = *p++;
168 pkt->dcid.data = p;
169 p += pkt->dcid.len;
170
171 pkt->scid.len = *p++;
172 pkt->scid.data = p;
173 p += pkt->scid.len;
174
175 pkt->raw->pos = p;
176
177 return NGX_OK;
178 }
179
180
181 size_t
182 ngx_quic_create_long_header(ngx_quic_header_t *pkt, ngx_str_t *out,
183 size_t pkt_len, u_char **pnp)
184 {
185 u_char *p, *start;
186
187 p = start = out->data;
188
189 *p++ = pkt->flags;
190
191 p = ngx_quic_write_uint32(p, quic_version);
192
193 *p++ = pkt->scid.len;
194 p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
195
196 *p++ = pkt->dcid.len;
197 p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len);
198
199 if (pkt->level == ssl_encryption_initial) {
200 ngx_quic_build_int(&p, pkt->token.len);
201 }
202
203 ngx_quic_build_int(&p, pkt_len + 1); // length (inc. pnl)
204
205 *pnp = p;
206
207 *p++ = (uint64_t) (*pkt->number);
208
209 return p - start;
210 }
211
212
213 ngx_int_t
214 ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid)
215 {
216 u_char *p;
217
218 p = pkt->data;
219
220 ngx_quic_hexdump0(pkt->log, "short input", pkt->data, pkt->len);
221
222 if ((p[0] & NGX_QUIC_PKT_LONG)) {
223 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "not a short packet");
224 return NGX_ERROR;
225 }
226
227 pkt->flags = *p++;
228
229 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
230 "quic flags:%xi", pkt->flags);
231
232 if (ngx_memcmp(p, dcid->data, dcid->len) != 0) {
233 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unexpected quic dcid");
234 return NGX_ERROR;
235 }
236
237 pkt->dcid.len = dcid->len;
238 pkt->dcid.data = p;
239 p += pkt->dcid.len;
240
241 pkt->raw->pos = p;
242
243 return NGX_OK;
244 }
245
246
247 ngx_int_t
248 ngx_quic_parse_initial_header(ngx_quic_header_t *pkt)
249 {
250 u_char *p;
251 ngx_int_t plen;
252
253 p = pkt->raw->pos;
254
255 pkt->token.len = ngx_quic_parse_int(&p);
256 pkt->token.data = p;
257
258 p += pkt->token.len;
259
260 plen = ngx_quic_parse_int(&p);
261
262 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
263 "quic packet length: %d", plen);
264
265 if (plen > pkt->data + pkt->len - p) {
266 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "truncated initial packet");
267 return NGX_ERROR;
268 }
269
270 pkt->raw->pos = p;
271 pkt->len = plen;
272
273 ngx_quic_hexdump0(pkt->log, "DCID", pkt->dcid.data, pkt->dcid.len);
274 ngx_quic_hexdump0(pkt->log, "SCID", pkt->scid.data, pkt->scid.len);
275 ngx_quic_hexdump0(pkt->log, "token", pkt->token.data, pkt->token.len);
276
277 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
278 "quic packet length: %d", plen);
279
280 return NGX_OK;
281 }
282
283
284 ngx_int_t
285 ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt)
286 {
287 u_char *p;
288 ngx_int_t plen;
289
290 p = pkt->raw->pos;
291
292 plen = ngx_quic_parse_int(&p);
293
294 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
295 "quic packet length: %d", plen);
296
297 if (plen > pkt->data + pkt->len - p) {
298 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "truncated handshake packet");
299 return NGX_ERROR;
300 }
301
302 pkt->raw->pos = p;
303 pkt->len = plen;
304
305 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
306 "quic packet length: %d", plen);
307
308 return NGX_OK;
309 }
310
311
312 #define ngx_quic_stream_bit_off(val) (((val) & 0x04) ? 1 : 0)
313 #define ngx_quic_stream_bit_len(val) (((val) & 0x02) ? 1 : 0)
314 #define ngx_quic_stream_bit_fin(val) (((val) & 0x01) ? 1 : 0)
315
316 ssize_t
317 ngx_quic_parse_frame(u_char *start, u_char *end, ngx_quic_frame_t *frame)
318 {
319 u_char *p;
320
321 size_t npad;
322
323 p = start;
324
325 frame->type = *p++; // TODO: check overflow (p < end)
326
327 switch (frame->type) {
328
329 case NGX_QUIC_FT_CRYPTO:
330 frame->u.crypto.offset = *p++;
331 frame->u.crypto.len = ngx_quic_parse_int(&p);
332 frame->u.crypto.data = p;
333 p += frame->u.crypto.len;
334
335 break;
336
337 case NGX_QUIC_FT_PADDING:
338 npad = 0;
339 while (p < end && *p == NGX_QUIC_FT_PADDING) { // XXX
340 p++; npad++;
341 }
342
343 break;
344
345 case NGX_QUIC_FT_ACK:
346 case NGX_QUIC_FT_ACK_ECN:
347
348 frame->u.ack.largest = ngx_quic_parse_int(&p);
349 frame->u.ack.delay = ngx_quic_parse_int(&p);
350 frame->u.ack.range_count =ngx_quic_parse_int(&p);
351 frame->u.ack.first_range =ngx_quic_parse_int(&p);
352
353 if (frame->u.ack.range_count) {
354 frame->u.ack.ranges[0] = ngx_quic_parse_int(&p);
355 }
356
357 if (frame->type ==NGX_QUIC_FT_ACK_ECN) {
358 return NGX_ERROR;
359 }
360
361 break;
362
363 case NGX_QUIC_FT_PING:
364 break;
365
366 case NGX_QUIC_FT_NEW_CONNECTION_ID:
367
368 frame->u.ncid.seqnum = ngx_quic_parse_int(&p);
369 frame->u.ncid.retire = ngx_quic_parse_int(&p);
370 frame->u.ncid.len = *p++;
371 ngx_memcpy(frame->u.ncid.cid, p, frame->u.ncid.len);
372 p += frame->u.ncid.len;
373
374 ngx_memcpy(frame->u.ncid.srt, p, 16);
375 p += 16;
376
377 break;
378
379 case NGX_QUIC_FT_CONNECTION_CLOSE:
380
381 frame->u.close.error_code = ngx_quic_parse_int(&p);
382 frame->u.close.frame_type = ngx_quic_parse_int(&p); // not in 0x1d CC
383 frame->u.close.reason.len = ngx_quic_parse_int(&p);
384 frame->u.close.reason.data = p;
385 p += frame->u.close.reason.len;
386
387 if (frame->u.close.error_code > NGX_QUIC_ERR_LAST) {
388 frame->u.close.error_code = NGX_QUIC_ERR_LAST;
389 }
390 break;
391
392 case NGX_QUIC_FT_STREAM0:
393 case NGX_QUIC_FT_STREAM1:
394 case NGX_QUIC_FT_STREAM2:
395 case NGX_QUIC_FT_STREAM3:
396 case NGX_QUIC_FT_STREAM4:
397 case NGX_QUIC_FT_STREAM5:
398 case NGX_QUIC_FT_STREAM6:
399 case NGX_QUIC_FT_STREAM7:
400
401 frame->u.stream.type = frame->type;
402
403 frame->u.stream.off = ngx_quic_stream_bit_off(frame->type);
404 frame->u.stream.len = ngx_quic_stream_bit_len(frame->type);
405 frame->u.stream.fin = ngx_quic_stream_bit_fin(frame->type);
406
407 frame->u.stream.stream_id = ngx_quic_parse_int(&p);
408 if (frame->type & 0x04) {
409 frame->u.stream.offset = ngx_quic_parse_int(&p);
410 } else {
411 frame->u.stream.offset = 0;
412 }
413
414 if (frame->type & 0x02) {
415 frame->u.stream.length = ngx_quic_parse_int(&p);
416 } else {
417 frame->u.stream.length = end - p; /* up to packet end */
418 }
419
420 frame->u.stream.data = p;
421
422 p += frame->u.stream.length;
423
424 break;
425
426 default:
427 return NGX_ERROR;
428 }
429
430 return p - start;
431 }
432
433
434 ssize_t
435 ngx_quic_create_frame(u_char *p, u_char *end, ngx_quic_frame_t *f)
436 {
437 // TODO: handle end arg
438
439 switch (f->type) {
440 case NGX_QUIC_FT_ACK:
441 return ngx_quic_create_ack(p, &f->u.ack);
442
443 case NGX_QUIC_FT_CRYPTO:
444 return ngx_quic_create_crypto(p, &f->u.crypto);
445
446 case NGX_QUIC_FT_STREAM0:
447 case NGX_QUIC_FT_STREAM1:
448 case NGX_QUIC_FT_STREAM2:
449 case NGX_QUIC_FT_STREAM3:
450 case NGX_QUIC_FT_STREAM4:
451 case NGX_QUIC_FT_STREAM5:
452 case NGX_QUIC_FT_STREAM6:
453 case NGX_QUIC_FT_STREAM7:
454 return ngx_quic_create_stream(p, &f->u.stream);
455
456 default:
457 /* BUG: unsupported frame type generated */
458 return NGX_ERROR;
459 }
460 }
461
462
463 size_t
464 ngx_quic_frame_len(ngx_quic_frame_t *frame)
465 {
466 switch (frame->type) {
467 case NGX_QUIC_FT_ACK:
468 return ngx_quic_create_ack(NULL, &frame->u.ack);
469 case NGX_QUIC_FT_CRYPTO:
470 return ngx_quic_create_crypto(NULL, &frame->u.crypto);
471
472 case NGX_QUIC_FT_STREAM0:
473 case NGX_QUIC_FT_STREAM1:
474 case NGX_QUIC_FT_STREAM2:
475 case NGX_QUIC_FT_STREAM3:
476 case NGX_QUIC_FT_STREAM4:
477 case NGX_QUIC_FT_STREAM5:
478 case NGX_QUIC_FT_STREAM6:
479 case NGX_QUIC_FT_STREAM7:
480 return ngx_quic_create_stream(NULL, &frame->u.stream);
481 default:
482 /* BUG: unsupported frame type generated */
483 return 0;
484 }
485 }
486
487
488 static size_t
489 ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack)
490 {
491 size_t len;
492
493 /* minimal ACK packet */
494
495 if (p == NULL) {
496 len = ngx_quic_varint_len(NGX_QUIC_FT_ACK);
497 len += ngx_quic_varint_len(ack->pn);
498 len += ngx_quic_varint_len(0);
499 len += ngx_quic_varint_len(0);
500 len += ngx_quic_varint_len(ack->pn);
501
502 return len;
503 }
504
505 ngx_quic_build_int(&p, NGX_QUIC_FT_ACK);
506 ngx_quic_build_int(&p, ack->pn);
507 ngx_quic_build_int(&p, 0);
508 ngx_quic_build_int(&p, 0);
509 ngx_quic_build_int(&p, ack->pn);
510
511 return 5;
512 }
513
514
515 static size_t
516 ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto)
517 {
518 size_t len;
519 u_char *start;
520
521 if (p == NULL) {
522 len = ngx_quic_varint_len(NGX_QUIC_FT_CRYPTO);
523 len += ngx_quic_varint_len(crypto->offset);
524 len += ngx_quic_varint_len(crypto->len);
525 len += crypto->len;
526
527 return len;
528 }
529
530 start = p;
531
532 ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO);
533 ngx_quic_build_int(&p, crypto->offset);
534 ngx_quic_build_int(&p, crypto->len);
535 p = ngx_cpymem(p, crypto->data, crypto->len);
536
537 return p - start;
538 }
539
540
541 static size_t
542 ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf)
543 {
544 size_t len;
545 u_char *start;
546
547 if (!sf->len) {
548 #if 0
549 ngx_log_error(NGX_LOG_INFO, log, 0,
550 "attempt to generate a stream frame without length");
551 #endif
552 // XXX: handle error in caller
553 return NGX_ERROR;
554 }
555
556 if (p == NULL) {
557 len = ngx_quic_varint_len(sf->type);
558
559 if (sf->off) {
560 len += ngx_quic_varint_len(sf->offset);
561 }
562
563 len += ngx_quic_varint_len(sf->stream_id);
564
565 /* length is always present in generated frames */
566 len += ngx_quic_varint_len(sf->length);
567
568 len += sf->length;
569
570 return len;
571 }
572
573 start = p;
574
575 ngx_quic_build_int(&p, sf->type);
576 ngx_quic_build_int(&p, sf->stream_id);
577
578 if (sf->off) {
579 ngx_quic_build_int(&p, sf->offset);
580 }
581
582 /* length is always present in generated frames */
583 ngx_quic_build_int(&p, sf->length);
584
585 p = ngx_cpymem(p, sf->data, sf->length);
586
587 return p - start;
588 }