Skip to content

Commit 71a754d

Browse files
authored
Merge pull request #58 from sippejw/main
Rebase on PR #37
2 parents c3e1885 + 80aca43 commit 71a754d

File tree

9 files changed

+572
-138
lines changed

9 files changed

+572
-138
lines changed

core/src/protocols/stream/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub mod tls;
1111

1212
use self::dns::{parser::DnsParser, Dns};
1313
use self::http::{parser::HttpParser, Http};
14-
use self::quic::{parser::QuicParser, QuicPacket};
14+
use self::quic::parser::QuicParser;
1515
use self::tls::{parser::TlsParser, Tls};
1616
use crate::conntrack::conn::conn_info::ConnState;
1717
use crate::conntrack::conn_id::FiveTuple;
@@ -22,6 +22,7 @@ use crate::subscription::*;
2222
use std::str::FromStr;
2323

2424
use anyhow::{bail, Result};
25+
use quic::QuicConn;
2526
use strum_macros::EnumString;
2627

2728
/// Represents the result of parsing one packet as a protocol message.
@@ -195,7 +196,7 @@ pub enum SessionData {
195196
Tls(Box<Tls>),
196197
Dns(Box<Dns>),
197198
Http(Box<Http>),
198-
Quic(Box<QuicPacket>),
199+
Quic(Box<QuicConn>),
199200
Null,
200201
}
201202

+241
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
// QUIC Frame types and parsing
2+
// Implemented per RFC 9000: https://datatracker.ietf.org/doc/html/rfc9000#name-frame-types-and-formats
3+
4+
use serde::Serialize;
5+
use std::collections::BTreeMap;
6+
7+
use crate::protocols::stream::quic::QuicError;
8+
use crate::protocols::stream::quic::QuicPacket;
9+
10+
// Types of supported QUIC frames
11+
// Currently only includes those seen in the Init and Handshake packets
12+
#[derive(Debug, Serialize, Clone)]
13+
pub enum QuicFrame {
14+
Padding {
15+
length: usize,
16+
},
17+
Ping,
18+
Ack {
19+
largest_acknowledged: u64,
20+
ack_delay: u64,
21+
first_ack_range: u64,
22+
ack_ranges: Vec<AckRange>,
23+
ecn_counts: Option<EcnCounts>,
24+
},
25+
Crypto {
26+
offset: u64,
27+
},
28+
}
29+
30+
// ACK Range field, part of ACK frame
31+
// https://datatracker.ietf.org/doc/html/rfc9000#ack-range-format
32+
#[derive(Debug, Serialize, Clone)]
33+
pub struct AckRange {
34+
gap: u64,
35+
ack_range_len: u64,
36+
}
37+
38+
// ECN Counts field, part of some ACK frames
39+
// https://datatracker.ietf.org/doc/html/rfc9000#ecn-count-format
40+
#[derive(Debug, Serialize, Clone)]
41+
pub struct EcnCounts {
42+
ect0_count: u64,
43+
ect1_count: u64,
44+
ecn_ce_count: u64,
45+
}
46+
47+
impl QuicFrame {
48+
// parse_frames takes the plaintext QUIC packet payload and parses the frame list
49+
// it also returns the reassembled CRYPTO frame bytes as a Vec<u8>
50+
pub fn parse_frames(
51+
data: &[u8],
52+
mut expected_offset: usize,
53+
) -> Result<(Vec<QuicFrame>, Vec<u8>), QuicError> {
54+
let mut frames: Vec<QuicFrame> = Vec::new();
55+
let mut crypto_map: BTreeMap<usize, Vec<u8>> = BTreeMap::new();
56+
let mut offset = 0;
57+
// Iterate over plaintext payload bytes, this is a list of frames
58+
while offset < data.len() {
59+
// Parse frame type
60+
let frame_type_len =
61+
QuicPacket::get_var_len(QuicPacket::access_data(data, offset, offset + 1)?[0])?;
62+
let frame_type = QuicPacket::slice_to_u64(QuicPacket::access_data(
63+
data,
64+
offset,
65+
offset + frame_type_len,
66+
)?)?;
67+
offset += frame_type_len;
68+
match frame_type {
69+
0x00 => {
70+
// Handle PADDING
71+
let mut length = 0;
72+
while offset + length + 1 < data.len()
73+
&& QuicPacket::access_data(data, offset + length, offset + length + 1)?[0]
74+
== 0
75+
{
76+
length += 1;
77+
}
78+
offset += length;
79+
length += frame_type_len; // Add the original frame type bytes to length. Wireshark also does this
80+
frames.push(QuicFrame::Padding { length });
81+
}
82+
0x01 => {
83+
// Handle PING
84+
frames.push(QuicFrame::Ping);
85+
}
86+
0x02 | 0x03 => {
87+
// Handle ACK
88+
// Parse Largest Acknowledged
89+
let largest_acknowledged_len = QuicPacket::get_var_len(
90+
QuicPacket::access_data(data, offset, offset + 1)?[0],
91+
)?;
92+
let largest_acknowledged = QuicPacket::slice_to_u64(QuicPacket::access_data(
93+
data,
94+
offset,
95+
offset + largest_acknowledged_len,
96+
)?)?;
97+
offset += largest_acknowledged_len;
98+
// Parse ACK Delay
99+
let ack_delay_len = QuicPacket::get_var_len(
100+
QuicPacket::access_data(data, offset, offset + 1)?[0],
101+
)?;
102+
let ack_delay = QuicPacket::slice_to_u64(QuicPacket::access_data(
103+
data,
104+
offset,
105+
offset + ack_delay_len,
106+
)?)?;
107+
offset += ack_delay_len;
108+
// Parse ACK Range Count
109+
let ack_range_count_len = QuicPacket::get_var_len(
110+
QuicPacket::access_data(data, offset, offset + 1)?[0],
111+
)?;
112+
let ack_range_count = QuicPacket::slice_to_u64(QuicPacket::access_data(
113+
data,
114+
offset,
115+
offset + ack_range_count_len,
116+
)?)?;
117+
offset += ack_range_count_len;
118+
// Parse First ACK Range
119+
let first_ack_range_len = QuicPacket::get_var_len(
120+
QuicPacket::access_data(data, offset, offset + 1)?[0],
121+
)?;
122+
let first_ack_range = QuicPacket::slice_to_u64(QuicPacket::access_data(
123+
data,
124+
offset,
125+
offset + first_ack_range_len,
126+
)?)?;
127+
offset += first_ack_range_len;
128+
// Parse ACK Range list field
129+
let mut ack_ranges = Vec::new();
130+
for _ in 0..ack_range_count {
131+
let gap_len = QuicPacket::get_var_len(
132+
QuicPacket::access_data(data, offset, offset + 1)?[0],
133+
)?;
134+
let gap = QuicPacket::slice_to_u64(QuicPacket::access_data(
135+
data,
136+
offset,
137+
offset + gap_len,
138+
)?)?;
139+
offset += gap_len;
140+
let ack_range_len_len = QuicPacket::get_var_len(
141+
QuicPacket::access_data(data, offset, offset + 1)?[0],
142+
)?;
143+
let ack_range_len = QuicPacket::slice_to_u64(QuicPacket::access_data(
144+
data,
145+
offset,
146+
offset + ack_range_len_len,
147+
)?)?;
148+
offset += ack_range_len_len;
149+
ack_ranges.push(AckRange { gap, ack_range_len })
150+
}
151+
// Parse ECN Counts, if the ACK frame contains them
152+
let ecn_counts: Option<EcnCounts> = if frame_type == 0x03 {
153+
let ect0_count_len = QuicPacket::get_var_len(
154+
QuicPacket::access_data(data, offset, offset + 1)?[0],
155+
)?;
156+
let ect0_count = QuicPacket::slice_to_u64(QuicPacket::access_data(
157+
data,
158+
offset,
159+
offset + ect0_count_len,
160+
)?)?;
161+
offset += ect0_count_len;
162+
let ect1_count_len = QuicPacket::get_var_len(
163+
QuicPacket::access_data(data, offset, offset + 1)?[0],
164+
)?;
165+
let ect1_count = QuicPacket::slice_to_u64(QuicPacket::access_data(
166+
data,
167+
offset,
168+
offset + ect1_count_len,
169+
)?)?;
170+
offset += ect1_count_len;
171+
let ecn_ce_count_len = QuicPacket::get_var_len(
172+
QuicPacket::access_data(data, offset, offset + 1)?[0],
173+
)?;
174+
let ecn_ce_count = QuicPacket::slice_to_u64(QuicPacket::access_data(
175+
data,
176+
offset,
177+
offset + ecn_ce_count_len,
178+
)?)?;
179+
Some(EcnCounts {
180+
ect0_count,
181+
ect1_count,
182+
ecn_ce_count,
183+
})
184+
} else {
185+
None
186+
};
187+
frames.push(QuicFrame::Ack {
188+
largest_acknowledged,
189+
ack_delay,
190+
first_ack_range,
191+
ack_ranges,
192+
ecn_counts,
193+
})
194+
}
195+
0x06 => {
196+
// Handle CRYPTO frame
197+
// Parse offset
198+
let crypto_offset_len = QuicPacket::get_var_len(
199+
QuicPacket::access_data(data, offset, offset + 1)?[0],
200+
)?;
201+
let crypto_offset = QuicPacket::slice_to_u64(QuicPacket::access_data(
202+
data,
203+
offset,
204+
offset + crypto_offset_len,
205+
)?)?;
206+
offset += crypto_offset_len;
207+
// Parse length
208+
let crypto_len_len = QuicPacket::get_var_len(
209+
QuicPacket::access_data(data, offset, offset + 1)?[0],
210+
)?;
211+
let crypto_len = QuicPacket::slice_to_u64(QuicPacket::access_data(
212+
data,
213+
offset,
214+
offset + crypto_len_len,
215+
)?)? as usize;
216+
offset += crypto_len_len;
217+
// Parse data
218+
let crypto_data =
219+
QuicPacket::access_data(data, offset, offset + crypto_len)?.to_vec();
220+
crypto_map
221+
.entry(crypto_offset as usize)
222+
.or_insert(crypto_data);
223+
frames.push(QuicFrame::Crypto {
224+
offset: crypto_offset,
225+
});
226+
offset += crypto_len;
227+
}
228+
_ => return Err(QuicError::UnknownFrameType),
229+
}
230+
}
231+
let mut reassembled_crypto: Vec<u8> = Vec::new();
232+
for (crypto_offset, crypto_data) in crypto_map {
233+
if crypto_offset != expected_offset {
234+
return Err(QuicError::MissingCryptoFrames);
235+
}
236+
expected_offset += crypto_data.len();
237+
reassembled_crypto.extend(crypto_data);
238+
}
239+
Ok((frames, reassembled_crypto))
240+
}
241+
}

core/src/protocols/stream/quic/header.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use serde::Serialize;
44

5-
use crate::protocols::stream::quic::parser::QuicError;
5+
use crate::protocols::stream::quic::QuicError;
66

77
/// Quic Long Header
88
#[derive(Debug, Serialize, Clone)]
@@ -23,9 +23,6 @@ pub struct QuicLongHeader {
2323
#[derive(Debug, Serialize, Clone)]
2424
pub struct QuicShortHeader {
2525
pub dcid: Option<String>, // optional. If not pre-existing cid then none.
26-
27-
#[serde(skip)]
28-
pub dcid_bytes: Vec<u8>,
2926
}
3027

3128
// Long Header Packet Types from RFC 9000 Table 5

core/src/protocols/stream/quic/mod.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,66 @@ TODO: support HTTP/3
2222
*/
2323
pub(crate) mod parser;
2424

25+
use std::collections::HashSet;
26+
2527
pub use self::header::{QuicLongHeader, QuicShortHeader};
28+
use crypto::Open;
29+
use frame::QuicFrame;
2630
use header::LongHeaderPacketType;
27-
use parser::QuicError;
2831
use serde::Serialize;
32+
33+
use super::tls::Tls;
34+
pub(crate) mod crypto;
35+
pub(crate) mod frame;
2936
pub(crate) mod header;
3037

38+
/// Errors Thrown throughout QUIC parsing. These are handled by retina and used to skip packets.
39+
#[derive(Debug)]
40+
pub enum QuicError {
41+
FixedBitNotSet,
42+
PacketTooShort,
43+
UnknownVersion,
44+
ShortHeader,
45+
UnknowLongHeaderPacketType,
46+
NoLongHeader,
47+
UnsupportedVarLen,
48+
InvalidDataIndices,
49+
CryptoFail,
50+
FailedHeaderProtection,
51+
UnknownFrameType,
52+
TlsParseFail,
53+
MissingCryptoFrames,
54+
}
55+
56+
/// Parsed Quic connections
57+
#[derive(Debug, Serialize)]
58+
pub struct QuicConn {
59+
// All packets associated with the connection
60+
pub packets: Vec<QuicPacket>,
61+
62+
// All cids, both src and destination, seen in Long Header packets
63+
pub cids: HashSet<String>,
64+
65+
// Parsed TLS messsages
66+
pub tls: Tls,
67+
68+
// Crypto needed to decrypt initial packets sent by client
69+
pub client_opener: Option<Open>,
70+
71+
// Crypto needed to decrypt initial packets sent by server
72+
pub server_opener: Option<Open>,
73+
74+
// Client buffer for multi-packet TLS messages
75+
#[serde(skip_serializing)]
76+
pub client_buffer: Vec<u8>,
77+
78+
// Server buffer for multi-packet TLS messages
79+
#[serde(skip_serializing)]
80+
pub server_buffer: Vec<u8>,
81+
}
82+
3183
/// Parsed Quic Packet contents
32-
#[derive(Debug, Serialize, Clone)]
84+
#[derive(Debug, Serialize)]
3385
pub struct QuicPacket {
3486
/// Quic Short header
3587
pub short_header: Option<QuicShortHeader>,
@@ -39,6 +91,8 @@ pub struct QuicPacket {
3991

4092
/// The number of bytes contained in the estimated payload
4193
pub payload_bytes_count: Option<u64>,
94+
95+
pub frames: Option<Vec<QuicFrame>>,
4296
}
4397

4498
impl QuicPacket {

0 commit comments

Comments
 (0)