/src/PcapPlusPlus/Packet++/header/QuicLayer.h
Line | Count | Source |
1 | | #pragma once |
2 | | #include "Layer.h" |
3 | | #include "Packet.h" |
4 | | #include <vector> |
5 | | #include <string> |
6 | | |
7 | | /// @file |
8 | | |
9 | | /// @namespace pcpp |
10 | | /// @brief The main namespace for the PcapPlusPlus lib |
11 | | namespace pcpp |
12 | | { |
13 | | /// @class QuicV1Layer |
14 | | /// Represents a QUIC v1 (RFC 9000/9001) protocol layer. |
15 | | class QuicV1Layer : public Layer |
16 | | { |
17 | | public: |
18 | | /// @enum QuicPacketType |
19 | | /// Identifies the kind of QUIC v1 packet, as carried by the Long Packet Type field for |
20 | | /// long-header packets (RFC 9000 Section 17.2), or inferred for short-header (1-RTT) and |
21 | | /// Version Negotiation packets, which don't carry this field on the wire |
22 | | enum class QuicPacketType : uint8_t |
23 | | { |
24 | | /// Initial packet - carries the start of the cryptographic handshake, plus an |
25 | | /// optional address-validation Token |
26 | | Initial = 0, |
27 | | /// 0-RTT packet - carries application data sent before the handshake completes |
28 | | ZeroRTT = 1, |
29 | | /// Handshake packet - carries the remainder of the cryptographic handshake |
30 | | Handshake = 2, |
31 | | /// Retry packet - sent by a server to perform address validation before committing |
32 | | /// state to a connection |
33 | | Retry = 3, |
34 | | /// Version Negotiation packet - sent by a server that doesn't support the client's |
35 | | /// requested QUIC version |
36 | | VersionNegotiation = 253, |
37 | | /// 1-RTT packet - a short-header packet carrying post-handshake application data |
38 | | OneRtt = 254, |
39 | | }; |
40 | | |
41 | | /// @enum QuicHeaderForm |
42 | | /// Identifies whether a QUIC packet uses the long or short header form (RFC 9000 Section |
43 | | /// 17.2 vs. 17.3) |
44 | | enum class QuicHeaderForm : uint8_t |
45 | | { |
46 | | /// Short header - used for 1-RTT packets once the connection ID length is known out |
47 | | /// of band |
48 | | ShortHeader = 0, |
49 | | /// Long header - used for Initial, 0-RTT, Handshake, Retry and Version Negotiation |
50 | | /// packets, all of which are exchanged before that connection ID length is known |
51 | | LongHeader = 1 |
52 | | }; |
53 | | |
54 | | /// @struct ProtectedPayload |
55 | | /// A non-owning view of the protected portion of a QUIC packet |
56 | | struct ProtectedPayload |
57 | | { |
58 | | /// Pointer to the beginning of the protected data |
59 | | const uint8_t* data; |
60 | | /// Length of the protected data in bytes |
61 | | size_t length; |
62 | | }; |
63 | | |
64 | | /// A static method that creates a QUIC v11 layer from packet raw data. Returns nullptr if |
65 | | /// data is not valid. |
66 | | /// @param[in] data A pointer to the raw data |
67 | | /// @param[in] dataLen Size of the data in bytes |
68 | | /// @param[in] prevLayer A pointer to the previous layer |
69 | | /// @param[in] packet A pointer to the Packet instance where layer will be stored |
70 | | /// @return The newly allocated layer or nullptr if the data isn't valid |
71 | | static QuicV1Layer* parseQuicLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet); |
72 | | |
73 | | /// @return The type of this QUIC packet |
74 | | virtual QuicPacketType getPacketType() const = 0; |
75 | | |
76 | | /// @return The header form (long or short) of this QUIC packet, as read straight off the |
77 | | /// wire |
78 | | QuicHeaderForm getHeaderForm() const; |
79 | | |
80 | | /// @return The value of the Fixed Bit field. Per RFC 9000 this is always 1 |
81 | | uint8_t getFixedBit() const; |
82 | | |
83 | | /// A static method that checks whether the port is considered as QUIC |
84 | | /// @param[in] port The port number to be checked |
85 | | /// @return True if the port is considered as QUIC, false otherwise |
86 | | static bool isQuicPort(uint16_t port) |
87 | 570k | { |
88 | 570k | return port == 443; |
89 | 570k | } |
90 | | |
91 | | // implement abstract methods |
92 | | |
93 | | /// Wraps any bytes remaining after this packet's header/payload as a subsequent |
94 | | /// QuicV1Layer if getHeaderLen() leaves data unconsumed - QUIC |
95 | | /// datagrams commonly coalesce multiple packets back to back - falling back to a |
96 | | /// PayloadLayer if what follows can't be parsed as QUIC |
97 | | void parseNextLayer() override; |
98 | | |
99 | | /// Does nothing for this layer |
100 | | void computeCalculateFields() override |
101 | 168 | {} |
102 | | |
103 | | /// @return A string representation of the packet, e.g. "QUIC v1 Layer, Initial message" |
104 | | std::string toString() const override; |
105 | | |
106 | | /// @return @ref OsiModelTransportLayer |
107 | | OsiModelLayer getOsiModelLayer() const override |
108 | 168 | { |
109 | 168 | return OsiModelTransportLayer; |
110 | 168 | } |
111 | | |
112 | | protected: |
113 | | QuicV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) |
114 | 990 | : Layer(data, dataLen, prevLayer, packet, QUICv1) |
115 | 990 | {} |
116 | | |
117 | | /// @struct quic_common_header |
118 | | /// The one-byte prefix shared by every QUIC v1 packet form, holding just the Header Form |
119 | | /// and Fixed Bit - enough to tell long-header and short-header packets apart before |
120 | | /// committing to either layout |
121 | | struct quic_common_header |
122 | | { |
123 | | #if (BYTE_ORDER == LITTLE_ENDIAN) |
124 | | uint8_t : 6, fixedBit : 1, headerForm : 1; |
125 | | #else |
126 | | uint8_t headerForm : 1, fixedBit : 1, : 6; |
127 | | #endif |
128 | | }; |
129 | | |
130 | | #pragma pack(push, 1) |
131 | | /// @struct quic_long_header |
132 | | /// The fixed-size prefix of every long-header QUIC v1 packet (Initial, 0-RTT, Handshake, |
133 | | /// Retry and Version Negotiation): the common header byte, the 4-byte version, and the |
134 | | /// Destination Connection ID length. The variable-length fields that follow (DCID, SCID, |
135 | | /// Token, Length) are not part of this struct - they're read via pointer+length accessors |
136 | | /// on QuicV1LongHeaderLayer and its subclasses instead |
137 | | struct quic_long_header |
138 | | { |
139 | | #if (BYTE_ORDER == LITTLE_ENDIAN) |
140 | | uint8_t packetNumberLength : 2, reserved : 2, longPacketType : 2, fixedBit : 1, headerForm : 1; |
141 | | #else |
142 | | uint8_t headerForm : 1, fixedBit : 1, longPacketType : 2, reserved : 2, packetNumberLength : 2; |
143 | | #endif |
144 | | uint32_t version; |
145 | | uint8_t destinationConnectionIdLength; |
146 | | }; |
147 | | #pragma pack(pop) |
148 | | static_assert(sizeof(quic_long_header) == 6, "quic_long_header size is not 6 bytes"); |
149 | | |
150 | | #pragma pack(push, 1) |
151 | | /// @struct quic_short_header |
152 | | /// The single-byte header of a 1-RTT (short-header) QUIC v1 packet. There is no |
153 | | /// Destination Connection ID length field here - a 1-RTT packet's connection ID has a |
154 | | /// length that's negotiated out of band during the handshake, so it can't be recovered |
155 | | /// from the packet alone |
156 | | struct quic_short_header |
157 | | { |
158 | | #if (BYTE_ORDER == LITTLE_ENDIAN) |
159 | | uint8_t packetNumberLength : 2, keyPhase : 1, reserved : 2, spinBit : 1, fixedBit : 1, headerForm : 1; |
160 | | #else |
161 | | uint8_t headerForm : 1, fixedBit : 1, spinBit : 1, reserved : 2, keyPhase : 1, packetNumberLength : 2; |
162 | | #endif |
163 | | }; |
164 | | #pragma pack(pop) |
165 | | static_assert(sizeof(quic_short_header) == 1, "quic_short_header size is not 1 byte"); |
166 | | |
167 | | private: |
168 | | quic_common_header* getCommonHeader() const |
169 | 0 | { |
170 | 0 | return reinterpret_cast<quic_common_header*>(m_Data); |
171 | 0 | } |
172 | | }; |
173 | | |
174 | | /// @class QuicV1LongHeaderLayer |
175 | | /// Base class for all QUIC v1 packet forms that use the long header (Initial, 0-RTT, |
176 | | /// Handshake, Retry and Version Negotiation). Provides parsing shared by all of them: the |
177 | | /// QUIC version and the Destination/Source Connection IDs, which sit at a fixed offset |
178 | | /// (Destination) or immediately after it (Source) in every long-header packet |
179 | | class QuicV1LongHeaderLayer : public QuicV1Layer |
180 | | { |
181 | | public: |
182 | | /// @return The packet type, read from the Long Packet Type field of the long header |
183 | | QuicPacketType getPacketType() const override; |
184 | | |
185 | | /// @return The QUIC version |
186 | | uint32_t getVersion() const; |
187 | | |
188 | | /// @return The Destination Connection ID, or an empty vector if the packet doesn't |
189 | | /// contain enough data to read it |
190 | | std::vector<uint8_t> getDestinationConnectionId() const; |
191 | | |
192 | | /// @return The Destination Connection ID as hex string, or an empty string if the packet doesn't |
193 | | /// contain enough data to read it |
194 | | std::string getDestinationConnectionIdAsString() const; |
195 | | |
196 | | /// @return The Source Connection ID, or an empty vector if the packet doesn't contain |
197 | | /// enough data to read it |
198 | | std::vector<uint8_t> getSourceConnectionId() const; |
199 | | |
200 | | /// @return The Source Connection ID as hex string, or an empty string if the packet doesn't contain |
201 | | /// enough data to read it |
202 | | std::string getSourceConnectionIdAsString() const; |
203 | | |
204 | | protected: |
205 | | using QuicV1Layer::QuicV1Layer; |
206 | | |
207 | | /// @struct OffsetAndLength |
208 | | /// The offset (from the start of the packet) and length, in bytes, of a variable-length |
209 | | /// field - used internally while walking the packet to locate the Source Connection ID, |
210 | | /// Token, and Length fields that follow the Destination Connection ID |
211 | | struct OffsetAndLength |
212 | | { |
213 | | /// The field's length, in bytes |
214 | | size_t length; |
215 | | /// The field's offset from the start of the packet, in bytes |
216 | | size_t offset; |
217 | | |
218 | | /// A constructor that creates an instance from a length and offset |
219 | | /// @param[in] length The field's length, in bytes |
220 | | /// @param[in] offset The field's offset from the start of the packet, in bytes |
221 | 1.25k | OffsetAndLength(size_t length, size_t offset) : length(length), offset(offset) |
222 | 1.25k | {} |
223 | | }; |
224 | | |
225 | | OffsetAndLength getDestConIdOffsetAndLength() const; |
226 | | |
227 | | OffsetAndLength getSrcConIdOffsetAndLength() const; |
228 | | |
229 | | private: |
230 | | static constexpr int destinationConnectionIdOffset = sizeof(quic_long_header); |
231 | | |
232 | | static bool isDataValid(const uint8_t* data, size_t dataLen); |
233 | | |
234 | | quic_long_header* getLongHeader() const |
235 | 835 | { |
236 | 835 | return reinterpret_cast<quic_long_header*>(m_Data); |
237 | 835 | } |
238 | | |
239 | | friend class QuicV1Layer; |
240 | | }; |
241 | | |
242 | | /// @class QuicV1EstablishmentLayer |
243 | | /// Base class for the long-header packet forms that carry cryptographic handshake data and a |
244 | | /// Length field (Initial, 0-RTT and Handshake). Adds parsing of the QUIC variable-length |
245 | | /// integer ("varint") encoding (RFC 9000 Section 16) used for the Length field and, in |
246 | | /// QuicV1InitialLayer, the Token Length field as well |
247 | | class QuicV1EstablishmentLayer : public QuicV1LongHeaderLayer |
248 | | { |
249 | | public: |
250 | | /// @return The value of the Length field: the number of bytes in the Packet Number and |
251 | | /// Payload fields combined, or 0 if the packet doesn't contain enough data to read it |
252 | | uint64_t getLength() const; |
253 | | |
254 | | // implement abstract methods |
255 | | |
256 | | /// @return The header length, computed as the offset of the Length field plus the |
257 | | /// varint's own encoded size plus the value of the Length field itself, clamped to the |
258 | | /// amount of data actually captured |
259 | | size_t getHeaderLen() const override; |
260 | | |
261 | | /// Get the protected portion of the QUIC packet. |
262 | | /// The returned data points directly into the packet buffer and is not copied. |
263 | | /// This includes the Packet Number and the protected payload (including the AEAD authentication tag). |
264 | | /// @return A non-owning view of the protected portion of the packet |
265 | | ProtectedPayload getProtectedPayload() const; |
266 | | |
267 | | protected: |
268 | | /// @struct VarintValueAndSize |
269 | | /// The decoded value of a QUIC variable-length integer, together with the number of |
270 | | /// bytes it occupied on the wire (1, 2, 4 or 8, per RFC 9000 Section 16) - returned by |
271 | | /// getVarintValueAndSize so callers can advance past the field without re-decoding |
272 | | /// its length |
273 | | struct VarintValueAndSize |
274 | | { |
275 | | /// The varint's decoded value |
276 | | uint64_t value; |
277 | | /// The varint's encoded size on the wire, in bytes (1, 2, 4 or 8) |
278 | | size_t size; |
279 | | |
280 | | /// A constructor that creates an instance from a decoded value and its encoded size |
281 | | /// @param[in] value The varint's decoded value |
282 | | /// @param[in] size The varint's encoded size on the wire, in bytes |
283 | 920 | VarintValueAndSize(uint64_t value, size_t size) : value(value), size(size) |
284 | 920 | {} |
285 | | }; |
286 | | |
287 | | virtual size_t getLengthOffset() const; |
288 | | |
289 | | VarintValueAndSize getVarintValueAndSize(size_t offset) const; |
290 | | |
291 | | private: |
292 | | using QuicV1LongHeaderLayer::QuicV1LongHeaderLayer; |
293 | | }; |
294 | | |
295 | | /// @class QuicV1InitialLayer |
296 | | /// Represents a QUIC v1 Initial packet - the first packet of a connection, carrying the |
297 | | /// start of the cryptographic handshake and, optionally, an address-validation Token |
298 | | /// (RFC 9000 Section 17.2.2) |
299 | | class QuicV1InitialLayer : public QuicV1EstablishmentLayer |
300 | | { |
301 | | public: |
302 | | /// @return The address-validation Token, or an empty vector if the packet carries no |
303 | | /// token or doesn't contain enough data to read it |
304 | | std::vector<uint8_t> getToken() const; |
305 | | |
306 | | /// @return The address-validation Token as hex string, or an empty string if the packet carries no |
307 | | /// token or doesn't contain enough data to read it |
308 | | std::string getTokenAsString() const; |
309 | | |
310 | | private: |
311 | | using QuicV1EstablishmentLayer::QuicV1EstablishmentLayer; |
312 | | |
313 | | size_t getLengthOffset() const override; |
314 | | |
315 | | size_t getTokenLengthOffset() const; |
316 | | |
317 | | friend class QuicV1Layer; |
318 | | }; |
319 | | |
320 | | /// @class QuicV1ZeroRttLayer |
321 | | /// Represents a QUIC v1 0-RTT packet - carries application data sent before the |
322 | | /// cryptographic handshake completes (RFC 9000 Section 17.2.3). Adds no parsing beyond what |
323 | | /// QuicV1EstablishmentLayer already provides |
324 | | class QuicV1ZeroRttLayer : public QuicV1EstablishmentLayer |
325 | | { |
326 | | using QuicV1EstablishmentLayer::QuicV1EstablishmentLayer; |
327 | | |
328 | | friend class QuicV1Layer; |
329 | | }; |
330 | | |
331 | | /// @class QuicV1HandshakeLayer |
332 | | /// Represents a QUIC v1 Handshake packet - carries the remainder of the cryptographic |
333 | | /// handshake after the Initial packet (RFC 9000 Section 17.2.4). Adds no parsing beyond what |
334 | | /// QuicV1EstablishmentLayer already provides |
335 | | class QuicV1HandshakeLayer : public QuicV1EstablishmentLayer |
336 | | { |
337 | | using QuicV1EstablishmentLayer::QuicV1EstablishmentLayer; |
338 | | |
339 | | friend class QuicV1Layer; |
340 | | }; |
341 | | |
342 | | /// @class QuicV1RetryLayer |
343 | | /// Represents a QUIC v1 Retry packet - sent by a server to perform address validation before |
344 | | /// committing state to a connection (RFC 9000 Section 17.2.5). Carries a Retry Token and, |
345 | | /// unlike the other long-header forms, a fixed-size 16-byte integrity tag rather than a |
346 | | /// varint-prefixed Length field |
347 | | class QuicV1RetryLayer : public QuicV1LongHeaderLayer |
348 | | { |
349 | | public: |
350 | | /// @return The Retry Token, or an empty vector if the packet doesn't contain enough |
351 | | /// data - beyond the Source Connection ID - to also hold the 16-byte integrity tag |
352 | | std::vector<uint8_t> getRetryToken() const; |
353 | | |
354 | | /// @return The Retry Token as hex string, or an empty string if the packet doesn't contain enough |
355 | | /// data - beyond the Source Connection ID - to also hold the 16-byte integrity tag |
356 | | std::string getRetryTokenAsString() const; |
357 | | |
358 | | /// @return The 16-byte Retry Integrity Tag, or an empty vector if the packet doesn't |
359 | | /// contain enough data - beyond the Source Connection ID - to hold it |
360 | | std::vector<uint8_t> getRetryIntegrityTag() const; |
361 | | |
362 | | /// @return The 16-byte Retry Integrity Tag as hex string, or an empty string if the packet doesn't |
363 | | /// contain enough data - beyond the Source Connection ID - to hold it |
364 | | std::string getRetryIntegrityTagAsString() const; |
365 | | |
366 | | // implement abstract methods |
367 | | |
368 | | /// @return sizeof(m_DataLen) - a Retry packet has no Length field, so its header is |
369 | | /// considered to span the entire packet |
370 | | size_t getHeaderLen() const override |
371 | 48 | { |
372 | 48 | return m_DataLen; |
373 | 48 | } |
374 | | |
375 | | private: |
376 | | using QuicV1LongHeaderLayer::QuicV1LongHeaderLayer; |
377 | | |
378 | | static constexpr size_t retryIntegritySize = 16; |
379 | | |
380 | | size_t getRetryTokenOffset() const; |
381 | | |
382 | | friend class QuicV1Layer; |
383 | | }; |
384 | | |
385 | | /// @class QuicV1VersionNegotiationLayer |
386 | | /// Represents a QUIC Version Negotiation packet - sent by a server that doesn't support the |
387 | | /// QUIC version a client requested, listing the versions it does support (RFC 9000 Section |
388 | | /// 17.2.1). Identified by a version field of 0 rather than a Long Packet Type value, so |
389 | | /// getPacketType() always returns QuicPacketType::VersionNegotiation |
390 | | class QuicV1VersionNegotiationLayer : public QuicV1LongHeaderLayer |
391 | | { |
392 | | public: |
393 | | /// @return QuicPacketType::VersionNegotiation |
394 | | QuicPacketType getPacketType() const override |
395 | 18 | { |
396 | 18 | return QuicPacketType::VersionNegotiation; |
397 | 18 | } |
398 | | |
399 | | /// @return The list of QUIC versions the server supports, in host byte order. Returns |
400 | | /// an empty list if a trailing partial version is encountered |
401 | | std::vector<uint32_t> getSupportedVersions() const; |
402 | | |
403 | | // implement abstract methods |
404 | | |
405 | | /// @return sizeof(m_DataLen) - a Version Negotiation packet has no further layers beyond |
406 | | /// the list of supported versions, so its header is considered to span the entire packet |
407 | | size_t getHeaderLen() const override |
408 | 54 | { |
409 | 54 | return m_DataLen; |
410 | 54 | } |
411 | | |
412 | | private: |
413 | | using QuicV1LongHeaderLayer::QuicV1LongHeaderLayer; |
414 | | |
415 | | friend class QuicV1Layer; |
416 | | }; |
417 | | |
418 | | /// @class QuicV1OneRttLayer |
419 | | /// Represents a QUIC v1 1-RTT packet - a short-header packet carrying post-handshake |
420 | | /// application data (RFC 9000 Section 17.3.1). Since the short header carries no explicit |
421 | | /// Length field, getHeaderLen() treats the header as spanning the entire packet |
422 | | class QuicV1OneRttLayer : public QuicV1Layer |
423 | | { |
424 | | public: |
425 | | /// @return QuicPacketType::OneRtt |
426 | | QuicPacketType getPacketType() const override |
427 | 120 | { |
428 | 120 | return QuicPacketType::OneRtt; |
429 | 120 | } |
430 | | |
431 | | /// @return The value of the Spin Bit, used for passive latency measurement along the |
432 | | /// connection's path |
433 | | bool getSpinBit() const |
434 | 0 | { |
435 | 0 | return getShortHeader()->spinBit; |
436 | 0 | } |
437 | | |
438 | | /// @return The value of the Key Phase bit, used to identify which packet protection keys |
439 | | /// were used to protect this packet |
440 | | bool getKeyPhaseBit() const |
441 | 0 | { |
442 | 0 | return getShortHeader()->keyPhase; |
443 | 0 | } |
444 | | |
445 | | /// Get the protected portion of the QUIC packet. |
446 | | /// The returned data points directly into the packet buffer and is not copied. |
447 | | /// @return A non-owning view of the protected portion of the packet |
448 | | ProtectedPayload getProtectedPayload() const; |
449 | | |
450 | | // implement abstract methods |
451 | | |
452 | | /// @return sizeof(m_DataLen) - a 1-RTT packet's short header carries no Length field, so |
453 | | /// its header is considered to span the entire packet |
454 | | size_t getHeaderLen() const override |
455 | 419 | { |
456 | 419 | return m_DataLen; |
457 | 419 | } |
458 | | |
459 | | private: |
460 | | using QuicV1Layer::QuicV1Layer; |
461 | | |
462 | | static bool isDataValid(const uint8_t* data, size_t dataLen); |
463 | | |
464 | | quic_short_header* getShortHeader() const |
465 | 0 | { |
466 | 0 | return reinterpret_cast<quic_short_header*>(m_Data); |
467 | 0 | } |
468 | | |
469 | | friend class QuicV1Layer; |
470 | | }; |
471 | | } // namespace pcpp |