]> The Tcpdump Group git mirrors - tcpdump/blob - print-quic.c
CI: Add warning exemptions for Sun C (suncc-5.14) on Solaris 10
[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 #include <config.h>
27
28 #include "netdissect-stdinc.h"
29 #include "netdissect-alloc.h"
30 #include "netdissect.h"
31 #include "extract.h"
32
33 #define QUIC_MAX_CID_LENGTH 20
34
35 typedef uint8_t quic_cid[QUIC_MAX_CID_LENGTH];
36
37 struct quic_cid_array {
38 uint8_t cid[QUIC_MAX_CID_LENGTH];
39 uint8_t length;
40 };
41
42 enum quic_lh_packet_type {
43 QUIC_LH_TYPE_INITIAL = 0,
44 QUIC_LH_TYPE_0RTT = 1,
45 QUIC_LH_TYPE_HANDSHAKE = 2,
46 QUIC_LH_TYPE_RETRY = 3
47 };
48
49 static void
50 hexprint(netdissect_options *ndo, const uint8_t *cp, size_t len)
51 {
52 size_t i;
53
54 for (i = 0; i < len; i++)
55 ND_PRINT("%02x", cp[i]);
56 }
57
58 #define QUIC_CID_LIST_MAX 512
59
60 static struct quic_cid_array quic_cid_array[QUIC_CID_LIST_MAX];
61
62 static struct quic_cid_array *
63 lookup_quic_cid(const u_char *cid, size_t length)
64 {
65 for (unsigned int i = 0; i < QUIC_CID_LIST_MAX; i++) {
66 if (quic_cid_array[i].length > length) {
67 continue;
68 }
69 if (quic_cid_array[i].length == 0) {
70 break;
71 }
72 if (memcmp(quic_cid_array[i].cid, cid,
73 quic_cid_array[i].length) == 0) {
74 /*
75 * Swap the entries so that it behaves like an
76 * LRU cache.
77 */
78 if (i != 0) {
79 struct quic_cid_array tmp = quic_cid_array[i];
80 quic_cid_array[i] = quic_cid_array[0];
81 quic_cid_array[0] = tmp;
82 }
83
84 return &quic_cid_array[0];
85 }
86 }
87
88 return NULL;
89 }
90
91 static void
92 register_quic_cid(const quic_cid cid, uint8_t length)
93 {
94 static uint16_t next_cid = 0;
95
96 if (length == 0 ||
97 lookup_quic_cid(cid, length) != NULL) {
98 return;
99 }
100 memcpy(&quic_cid_array[next_cid].cid, cid, QUIC_MAX_CID_LENGTH);
101 quic_cid_array[next_cid].length = length;
102 next_cid = (next_cid + 1) % QUIC_CID_LIST_MAX;
103 }
104
105 /* Returns 1 if the first octet looks like a QUIC packet. */
106 int
107 quic_detect(netdissect_options *ndo, const u_char *p, const u_int len)
108 {
109 uint8_t first_octet;
110
111 if (len < 1)
112 return 0;
113 first_octet = GET_U_1(p);
114 /* All QUIC packets must have the Fixed Bit set to 1. */
115 if ((first_octet & 0x40) == 0x40)
116 return 1;
117 else
118 return 0;
119 }
120
121 /* Extracts the variable length integer (see RFC 9000 section 16). */
122 static inline uint64_t
123 get_be_vli(netdissect_options *ndo, const u_char *p, uint8_t *out_length)
124 {
125 uint64_t v;
126 uint8_t prefix;
127 uint8_t length;
128
129 v = GET_U_1(p);
130 p++;
131 prefix = (uint8_t)v >> 6;
132 length = 1 << prefix;
133 if (out_length != NULL)
134 *out_length = length;
135 v = v & 0x3f;
136 while (length > 1) {
137 v = (v << 8) + GET_U_1(p);
138 p++;
139 length--;
140 }
141
142 return v;
143 }
144
145 #define GET_BE_VLI(p, l) get_be_vli(ndo, (const u_char *)(p), l)
146
147 static const u_char *
148 quic_print_packet(netdissect_options *ndo, const u_char *bp, const u_char *end)
149 {
150 uint8_t first_octet = 0;
151 uint8_t packet_type = 0;
152 uint32_t version = 0;
153 quic_cid dcid = {0};
154 quic_cid scid = {0};
155 uint8_t dcil = 0; /* DCID length */
156 uint8_t scil = 0; /* SCID length */
157 uint8_t vli_length = 0;
158 uint8_t *token = NULL;
159 uint64_t token_length = 0;
160
161 first_octet = GET_U_1(bp);
162 bp += 1;
163 if (first_octet & 0x80) {
164 /* Long Header */
165 packet_type = (first_octet >> 4) & 0x03;
166 version = GET_BE_U_4(bp);
167 bp += 4;
168
169 if (version == 0)
170 ND_PRINT(", version negotiation");
171 else if (packet_type == QUIC_LH_TYPE_INITIAL)
172 ND_PRINT(", initial");
173 else if (packet_type == QUIC_LH_TYPE_0RTT)
174 ND_PRINT(", 0-rtt");
175 else if (packet_type == QUIC_LH_TYPE_HANDSHAKE)
176 ND_PRINT(", handshake");
177 else if (packet_type == QUIC_LH_TYPE_RETRY)
178 ND_PRINT(", retry");
179 if (version != 0 && version != 1)
180 ND_PRINT(", v%x", version);
181 dcil = GET_U_1(bp);
182 bp += 1;
183 if (dcil > 0 && dcil <= QUIC_MAX_CID_LENGTH) {
184 memset(dcid, 0, sizeof(dcid));
185 GET_CPY_BYTES(&dcid, bp, dcil);
186 bp += dcil;
187 ND_PRINT(", dcid ");
188 hexprint(ndo, dcid, dcil);
189 register_quic_cid(dcid, dcil);
190 }
191 scil = GET_U_1(bp);
192 bp += 1;
193 if (scil > 0 && scil <= QUIC_MAX_CID_LENGTH) {
194 memset(scid, 0, sizeof(dcid));
195 GET_CPY_BYTES(&scid, bp, scil);
196 bp += scil;
197 ND_PRINT(", scid ");
198 hexprint(ndo, scid, scil);
199 register_quic_cid(scid, scil);
200 }
201 if (version == 0) {
202 /* Version Negotiation packet */
203 while (bp < end) {
204 if (!ND_TTEST_4(bp)) {
205 nd_print_trunc(ndo);
206 bp = end;
207 } else {
208 uint32_t vn_version = GET_BE_U_4(bp);
209 bp += 4;
210 ND_PRINT(", version 0x%x", vn_version);
211 }
212 }
213 } else {
214 if (packet_type == QUIC_LH_TYPE_INITIAL) {
215 token_length = GET_BE_VLI(bp, &vli_length);
216 bp += vli_length;
217 if (token_length > 0 && token_length < 1000) {
218 token = nd_malloc(ndo, (size_t)token_length);
219 GET_CPY_BYTES(token, bp, (size_t)token_length);
220 bp += token_length;
221 ND_PRINT(", token ");
222 hexprint(ndo, token, (size_t)token_length);
223 }
224 }
225 if (packet_type == QUIC_LH_TYPE_RETRY) {
226 ND_PRINT(", token ");
227 if (end > bp && end - bp > 16 &&
228 ND_TTEST_LEN(bp, end - bp - 16)) {
229 token_length = end - bp - 16;
230 token = nd_malloc(ndo, (size_t)token_length);
231 GET_CPY_BYTES(token, bp, (size_t)token_length);
232 bp += token_length;
233 hexprint(ndo, token, (size_t)token_length);
234 } else {
235 nd_print_trunc(ndo);
236 }
237 bp = end;
238 } else {
239 /* Initial/Handshake/0-RTT */
240 uint64_t payload_length =
241 GET_BE_VLI(bp, &vli_length);
242 bp += vli_length;
243 ND_PRINT(", length %" PRIu64, payload_length);
244 if (!ND_TTEST_LEN(bp, payload_length)) {
245 nd_print_trunc(ndo);
246 bp = end;
247 } else
248 bp += payload_length;
249 }
250 }
251 } else {
252 /* Short Header */
253 ND_PRINT(", protected");
254 if (end > bp && end - bp > 16 &&
255 ND_TTEST_LEN(bp, end - bp)) {
256 struct quic_cid_array *cid_array =
257 lookup_quic_cid(bp, end - bp);
258 if (cid_array != NULL) {
259 ND_PRINT(", dcid ");
260 hexprint(ndo, cid_array->cid,
261 cid_array->length);
262 }
263 } else {
264 nd_print_trunc(ndo);
265 }
266 bp = end;
267 }
268
269 return bp;
270 }
271
272 void
273 quic_print(netdissect_options *ndo, const u_char *bp)
274 {
275 const uint8_t *end = bp + ND_BYTES_AVAILABLE_AFTER(bp);
276
277 ndo->ndo_protocol = "quic";
278 nd_print_protocol(ndo);
279
280 while (bp < end) {
281 bp = quic_print_packet(ndo, bp, end);
282 /*
283 * Skip all zero bytes which are
284 * considered padding.
285 */
286 while (ND_TTEST_1(bp) && GET_U_1(bp) == 0)
287 bp++;
288 }
289 }