]> The Tcpdump Group git mirrors - tcpdump/blob - print-quic.c
Revert "Clean a bunch of fuzzed files not to fuzz the container."
[tcpdump] / print-quic.c
1 /*
2 * Copyright (c) 2021 Apple, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 * 3. The names of the authors may not be used to endorse or promote
15 * products derived from this software without specific prior
16 * written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21 */
22
23 /* \summary: QUIC Protocol printer */
24 /* specification: https://www.rfc-editor.org/rfc/rfc9000.txt */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include "netdissect-stdinc.h"
31 #include "netdissect-alloc.h"
32 #include "netdissect.h"
33 #include "extract.h"
34
35 #define QUIC_MAX_CID_LENGTH 20
36
37 typedef uint8_t quic_cid[QUIC_MAX_CID_LENGTH];
38
39 struct quic_cid_array {
40 uint8_t cid[QUIC_MAX_CID_LENGTH];
41 uint8_t length;
42 };
43
44 enum quic_lh_packet_type {
45 QUIC_LH_TYPE_INITIAL = 0,
46 QUIC_LH_TYPE_0RTT = 1,
47 QUIC_LH_TYPE_HANDSHAKE = 2,
48 QUIC_LH_TYPE_RETRY = 3
49 };
50
51 static void
52 hexprint(netdissect_options *ndo, const uint8_t *cp, size_t len)
53 {
54 size_t i;
55
56 for (i = 0; i < len; i++)
57 ND_PRINT("%02x", cp[i]);
58 }
59
60 #define QUIC_CID_LIST_MAX 512
61
62 static struct quic_cid_array quic_cid_array[QUIC_CID_LIST_MAX];
63
64 static struct quic_cid_array *
65 lookup_quic_cid(const u_char *cid, size_t length)
66 {
67 for (unsigned int i = 0; i < QUIC_CID_LIST_MAX; i++) {
68 if (quic_cid_array[i].length > length) {
69 continue;
70 }
71 if (quic_cid_array[i].length == 0) {
72 break;
73 }
74 if (memcmp(quic_cid_array[i].cid, cid,
75 quic_cid_array[i].length) == 0) {
76 /*
77 * Swap the entries so that it behaves like an
78 * LRU cache.
79 */
80 if (i != 0) {
81 struct quic_cid_array tmp = quic_cid_array[i];
82 quic_cid_array[i] = quic_cid_array[0];
83 quic_cid_array[0] = tmp;
84 }
85
86 return &quic_cid_array[0];
87 }
88 }
89
90 return NULL;
91 }
92
93 static void
94 register_quic_cid(const quic_cid cid, uint8_t length)
95 {
96 static uint16_t next_cid = 0;
97
98 if (length == 0 ||
99 lookup_quic_cid(cid, length) != NULL) {
100 return;
101 }
102 memcpy(&quic_cid_array[next_cid].cid, cid, QUIC_MAX_CID_LENGTH);
103 quic_cid_array[next_cid].length = length;
104 next_cid = (next_cid + 1) % QUIC_CID_LIST_MAX;
105 }
106
107 /* Returns 1 if the first octet looks like a QUIC packet. */
108 int
109 quic_detect(netdissect_options *ndo, const u_char *p, const u_int len)
110 {
111 uint8_t first_octet;
112
113 if (len < 1)
114 return 0;
115 first_octet = GET_U_1(p);
116 /* All QUIC packets must have the Fixed Bit set to 1. */
117 if ((first_octet & 0x40) == 0x40)
118 return 1;
119 else
120 return 0;
121 }
122
123 /* Extracts the variable length integer (see RFC 9000 section 16). */
124 static inline uint64_t
125 get_be_vli(netdissect_options *ndo, const u_char *p, uint8_t *out_length)
126 {
127 uint64_t v;
128 uint8_t prefix;
129 uint8_t length;
130
131 v = GET_U_1(p);
132 p++;
133 prefix = (uint8_t)v >> 6;
134 length = 1 << prefix;
135 if (out_length != NULL)
136 *out_length = length;
137 v = v & 0x3f;
138 while (length > 1) {
139 v = (v << 8) + GET_U_1(p);
140 p++;
141 length--;
142 }
143
144 return v;
145 }
146
147 #define GET_BE_VLI(p, l) get_be_vli(ndo, (const u_char *)(p), l)
148
149 static const u_char *
150 quic_print_packet(netdissect_options *ndo, const u_char *bp, const u_char *end)
151 {
152 uint8_t first_octet = 0;
153 uint8_t packet_type = 0;
154 uint32_t version = 0;
155 quic_cid dcid = {0};
156 quic_cid scid = {0};
157 uint8_t dcil = 0; /* DCID length */
158 uint8_t scil = 0; /* SCID length */
159 uint8_t vli_length = 0;
160 uint8_t *token = NULL;
161 uint64_t token_length = 0;
162
163 first_octet = GET_U_1(bp);
164 bp += 1;
165 if (first_octet & 0x80) {
166 /* Long Header */
167 packet_type = (first_octet >> 4) & 0x03;
168 version = GET_BE_U_4(bp);
169 bp += 4;
170
171 if (version == 0)
172 ND_PRINT(", version negotiation");
173 else if (packet_type == QUIC_LH_TYPE_INITIAL)
174 ND_PRINT(", initial");
175 else if (packet_type == QUIC_LH_TYPE_0RTT)
176 ND_PRINT(", 0-rtt");
177 else if (packet_type == QUIC_LH_TYPE_HANDSHAKE)
178 ND_PRINT(", handshake");
179 else if (packet_type == QUIC_LH_TYPE_RETRY)
180 ND_PRINT(", retry");
181 if (version != 0 && version != 1)
182 ND_PRINT(", v%x", version);
183 dcil = GET_U_1(bp);
184 bp += 1;
185 if (dcil > 0 && dcil <= QUIC_MAX_CID_LENGTH) {
186 memset(dcid, 0, sizeof(dcid));
187 GET_CPY_BYTES(&dcid, bp, dcil);
188 bp += dcil;
189 ND_PRINT(", dcid ");
190 hexprint(ndo, dcid, dcil);
191 register_quic_cid(dcid, dcil);
192 }
193 scil = GET_U_1(bp);
194 bp += 1;
195 if (scil > 0 && scil <= QUIC_MAX_CID_LENGTH) {
196 memset(scid, 0, sizeof(dcid));
197 GET_CPY_BYTES(&scid, bp, scil);
198 bp += scil;
199 ND_PRINT(", scid ");
200 hexprint(ndo, scid, scil);
201 register_quic_cid(scid, scil);
202 }
203 if (version == 0) {
204 /* Version Negotiation packet */
205 while (bp < end) {
206 if (!ND_TTEST_4(bp)) {
207 nd_print_trunc(ndo);
208 bp = end;
209 } else {
210 uint32_t vn_version = GET_BE_U_4(bp);
211 bp += 4;
212 ND_PRINT(", version 0x%x", vn_version);
213 }
214 }
215 } else {
216 if (packet_type == QUIC_LH_TYPE_INITIAL) {
217 token_length = GET_BE_VLI(bp, &vli_length);
218 bp += vli_length;
219 if (token_length > 0 && token_length < 1000) {
220 token = nd_malloc(ndo, (size_t)token_length);
221 GET_CPY_BYTES(token, bp, (size_t)token_length);
222 bp += token_length;
223 ND_PRINT(", token ");
224 hexprint(ndo, token, (size_t)token_length);
225 }
226 }
227 if (packet_type == QUIC_LH_TYPE_RETRY) {
228 ND_PRINT(", token ");
229 if (end > bp && end - bp > 16 &&
230 ND_TTEST_LEN(bp, end - bp - 16)) {
231 token_length = end - bp - 16;
232 token = nd_malloc(ndo, (size_t)token_length);
233 GET_CPY_BYTES(token, bp, (size_t)token_length);
234 bp += token_length;
235 hexprint(ndo, token, (size_t)token_length);
236 } else {
237 nd_print_trunc(ndo);
238 }
239 bp = end;
240 } else {
241 /* Initial/Handshake/0-RTT */
242 uint64_t payload_length =
243 GET_BE_VLI(bp, &vli_length);
244 bp += vli_length;
245 ND_PRINT(", length %" PRIu64, payload_length);
246 if (!ND_TTEST_LEN(bp, payload_length)) {
247 nd_print_trunc(ndo);
248 bp = end;
249 } else
250 bp += payload_length;
251 }
252 }
253 } else {
254 /* Short Header */
255 ND_PRINT(", protected");
256 if (end > bp && end - bp > 16 &&
257 ND_TTEST_LEN(bp, end - bp)) {
258 struct quic_cid_array *cid_array =
259 lookup_quic_cid(bp, end - bp);
260 if (cid_array != NULL) {
261 ND_PRINT(", dcid ");
262 hexprint(ndo, cid_array->cid,
263 cid_array->length);
264 }
265 } else {
266 nd_print_trunc(ndo);
267 }
268 bp = end;
269 }
270
271 return bp;
272 }
273
274 void
275 quic_print(netdissect_options *ndo, const u_char *bp, const u_int len)
276 {
277 const uint8_t *end = bp + len;
278
279 ndo->ndo_protocol = "quic";
280 nd_print_protocol(ndo);
281
282 while (bp < end) {
283 bp = quic_print_packet(ndo, bp, end);
284 /*
285 * Skip all zero bytes which are
286 * considered padding.
287 */
288 while (ND_TTEST_1(bp) && GET_U_1(bp) == 0)
289 bp++;
290 }
291 }