Skip to content

Commit

Permalink
Quic Support (#32)
Browse files Browse the repository at this point in the history
* Add quic support

* Update main.rs

* fix indexing errors

* probe indexing change

* configs

* fix usize error

* further usize fixes

* another index fix

* return configs

* minimal performance improvements

* config changes

* rename file

* lazy load bytes

* lazy string copy

* other string conversion

* small change again

* renove config changes

* rename quic stream

* parseresult done

* revert parse result

* remove configs

* this is gonna break a lot

* config changes

* fix reference errors

* import fixes

* pub header

* public vec_u8

* import hell

* rename

* slight fixes

* everything mutable

* this wont work

* better cloning

* cloning

* fix

* fully clone

* more cloning

* copy trait

* partial move

* remove ref

* fix

* println debugging

* fix

* more prints

* hashset

* from clone

* y not

* cleanliness

* print quic clone

* done

* remove prints

* clear on end

* remove dead code

* remove imports

* remove configs

* reference based conn_ids

* configs

* rename quic to quicpacket

* remove quic.toml

* remove configs

* revert

* remove configs

* clippy fix
  • Loading branch information
bokeefe123 authored Jun 6, 2024
1 parent 2afeb05 commit 33139bd
Show file tree
Hide file tree
Showing 12 changed files with 611 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"examples/log_http",
"examples/log_dns",
"examples/log_tls",
"examples/log_quic",
"examples/pcap_dump",
"examples/spin",
"examples/video",
Expand Down
4 changes: 4 additions & 0 deletions core/src/filter/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ lazy_static! {
let http = g.add_node(protocol!("http"));
let ssh = g.add_node(protocol!("ssh"));
let dns = g.add_node(protocol!("dns"));
let quic = g.add_node(protocol!("quic"));
// define valid outer layers for each protocol header
g.extend_with_edges([
(ipv4, ethernet),
Expand All @@ -34,6 +35,7 @@ lazy_static! {
(http, tcp),
(ssh, tcp),
(dns, udp), (dns, tcp),
(quic, udp), //TODO: tls over quic
]);
g
};
Expand Down Expand Up @@ -250,6 +252,8 @@ mod tests {
assert!(!has_path(&protocol!("ipv4"), &protocol!("ipv4")));
assert!(!has_path(&protocol!("http"), &protocol!("udp")));
assert!(!has_path(&protocol!("tls"), &protocol!("ssh")));
assert!(has_path(&protocol!("quic"), &protocol!("udp")));
assert!(!has_path(&protocol!("quic"), &protocol!("dns")));
}

#[test]
Expand Down
11 changes: 11 additions & 0 deletions core/src/protocols/stream/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

pub mod dns;
pub mod http;
pub mod quic;
pub mod tls;

use self::dns::{parser::DnsParser, Dns};
use self::http::{parser::HttpParser, Http};
use self::quic::{parser::QuicParser, QuicPacket};
use self::tls::{parser::TlsParser, Tls};
use crate::conntrack::conn::conn_info::ConnState;
use crate::conntrack::conn_id::FiveTuple;
Expand Down Expand Up @@ -193,6 +195,7 @@ pub enum SessionData {
Tls(Box<Tls>),
Dns(Box<Dns>),
Http(Box<Http>),
Quic(Box<QuicPacket>),
Null,
}

Expand Down Expand Up @@ -235,6 +238,7 @@ pub enum ConnParser {
Tls(TlsParser),
Dns(DnsParser),
Http(HttpParser),
Quic(QuicParser),
Unknown,
}

Expand All @@ -245,6 +249,7 @@ impl ConnParser {
ConnParser::Tls(_) => ConnParser::Tls(TlsParser::default()),
ConnParser::Dns(_) => ConnParser::Dns(DnsParser::default()),
ConnParser::Http(_) => ConnParser::Http(HttpParser::default()),
ConnParser::Quic(_) => ConnParser::Quic(QuicParser::default()),
ConnParser::Unknown => ConnParser::Unknown,
}
}
Expand All @@ -255,6 +260,7 @@ impl ConnParser {
ConnParser::Tls(parser) => parser.parse(pdu),
ConnParser::Dns(parser) => parser.parse(pdu),
ConnParser::Http(parser) => parser.parse(pdu),
ConnParser::Quic(parser) => parser.parse(pdu),
ConnParser::Unknown => ParseResult::Skipped,
}
}
Expand All @@ -265,6 +271,7 @@ impl ConnParser {
ConnParser::Tls(parser) => parser.probe(pdu),
ConnParser::Dns(parser) => parser.probe(pdu),
ConnParser::Http(parser) => parser.probe(pdu),
ConnParser::Quic(parser) => parser.probe(pdu),
ConnParser::Unknown => ProbeResult::Error,
}
}
Expand All @@ -276,6 +283,7 @@ impl ConnParser {
ConnParser::Tls(parser) => parser.remove_session(session_id),
ConnParser::Dns(parser) => parser.remove_session(session_id),
ConnParser::Http(parser) => parser.remove_session(session_id),
ConnParser::Quic(parser) => parser.remove_session(session_id),
ConnParser::Unknown => None,
}
}
Expand All @@ -286,6 +294,7 @@ impl ConnParser {
ConnParser::Tls(parser) => parser.drain_sessions(),
ConnParser::Dns(parser) => parser.drain_sessions(),
ConnParser::Http(parser) => parser.drain_sessions(),
ConnParser::Quic(parser) => parser.drain_sessions(),
ConnParser::Unknown => vec![],
}
}
Expand All @@ -296,6 +305,7 @@ impl ConnParser {
ConnParser::Tls(parser) => parser.session_match_state(),
ConnParser::Dns(parser) => parser.session_match_state(),
ConnParser::Http(parser) => parser.session_match_state(),
ConnParser::Quic(parser) => parser.session_match_state(),
ConnParser::Unknown => ConnState::Remove,
}
}
Expand All @@ -306,6 +316,7 @@ impl ConnParser {
ConnParser::Tls(parser) => parser.session_nomatch_state(),
ConnParser::Dns(parser) => parser.session_nomatch_state(),
ConnParser::Http(parser) => parser.session_nomatch_state(),
ConnParser::Quic(parser) => parser.session_nomatch_state(),
ConnParser::Unknown => ConnState::Remove,
}
}
Expand Down
24 changes: 24 additions & 0 deletions core/src/protocols/stream/quic/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Quic header types

use serde::Serialize;

/// Quic Long Header
#[derive(Debug, Serialize, Clone)]
pub struct QuicLongHeader {
pub packet_type: u8,
pub type_specific: u8,
pub version: u32,
pub dcid_len: u8, // length of dcid in bytes
pub dcid: String, // hex string
pub scid_len: u8, // length of scid in bytes
pub scid: String, // hex string
}

/// Quic Short Header
#[derive(Debug, Serialize, Clone)]
pub struct QuicShortHeader {
pub dcid: Option<String>, // optional. If not pre-existing cid then none.

#[serde(skip)]
pub dcid_bytes: Vec<u8>,
}
108 changes: 108 additions & 0 deletions core/src/protocols/stream/quic/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! QUIC protocol parser.
//!
//! ## Remarks
//! [QUIC-INVARIANTS] https://datatracker.ietf.org/doc/rfc8999/
//! [QUIC-RFC9000] https://datatracker.ietf.org/doc/rfc9000/ (Quic V1)
//! Retina currently only parses Quic Long and Short Headers and does not attempt to parse TLS or HTTP/3 out of
//! Quic packets. The Quic protocol parser makes several assumptions about the way that quic
//! packets will behave:
//! - Assume that the Quic version is one as listed in the QuicVersion Enum in the quic/parser.rs file
//! - Assume that the dcid of a short header is a maximum of 20 bytes.
//! - Assume that the packet will not try to grease the fixed bit.
//! [QUIC-GREASE](https://www.rfc-editor.org/rfc/rfc9287.html)
//!
//! Additionally, there are a couple decisions made in the design of the quic parser:
//! - The parser will not parse a short header dcid if it is not a part of a pre-identified connection
//! - The payload bytes count is a lazy counter which does not try to exclude tokens for encryption,
//! which is a process that happens in wireshark.
/*
TODO: support parsing the tls out of the initial quic packet setup
TODO support dns over quic
TODO: support HTTP/3
*/
pub(crate) mod parser;

pub use self::header::{QuicLongHeader, QuicShortHeader};
use serde::Serialize;
pub(crate) mod header;

/// Parsed Quic Packet contents
#[derive(Debug, Serialize, Clone)]
pub struct QuicPacket {
/// Quic Short header
pub short_header: Option<QuicShortHeader>,

/// Quic Long header
pub long_header: Option<QuicLongHeader>,

/// The number of bytes contained in the estimated payload
pub payload_bytes_count: u16,
}

impl QuicPacket {
/// Returns the header type of the Quic packet (ie. "long" or "short")
pub fn header_type(&self) -> &str {
match &self.long_header {
Some(_) => "long",
None => match &self.short_header {
Some(_) => "short",
None => "",
},
}
}

/// Returns the packet type of the Quic packet
pub fn packet_type(&self) -> u8 {
match &self.long_header {
Some(long_header) => long_header.packet_type,
None => 0,
}
}

/// Returns the version of the Quic packet
pub fn version(&self) -> u32 {
match &self.long_header {
Some(long_header) => long_header.version,
None => 0,
}
}

/// Returns the destination connection ID of the Quic packet or an empty string if it does not exist
pub fn dcid(&self) -> &str {
match &self.long_header {
Some(long_header) => {
if long_header.dcid_len > 0 {
&long_header.dcid
} else {
""
}
}
None => {
if let Some(short_header) = &self.short_header {
short_header.dcid.as_deref().unwrap_or("")
} else {
""
}
}
}
}

/// Returns the source connection ID of the Quic packet or an empty string if it does not exist
pub fn scid(&self) -> &str {
match &self.long_header {
Some(long_header) => {
if long_header.scid_len > 0 {
&long_header.scid
} else {
""
}
}
None => "",
}
}

/// Returns the number of bytes in the payload of the Quic packet
pub fn payload_bytes_count(&self) -> u16 {
self.payload_bytes_count
}
}
Loading

0 comments on commit 33139bd

Please sign in to comment.