Skip to content

Commit

Permalink
feat(s2n-quic-core): make XDP decoder take mutable validator
Browse files Browse the repository at this point in the history
  • Loading branch information
camshaft committed Apr 28, 2023
1 parent b898af2 commit 56748c1
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 27 deletions.
8 changes: 6 additions & 2 deletions common/s2n-codec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub mod decoder;
pub mod encoder;
pub mod unaligned;

pub use decoder::*;
pub use encoder::*;
pub use decoder::{
CheckedRange, DecoderBuffer, DecoderBufferMut, DecoderBufferMutResult, DecoderBufferResult,
DecoderError, DecoderParameterizedValue, DecoderParameterizedValueMut, DecoderValue,
DecoderValueMut,
};
pub use encoder::{Encoder, EncoderBuffer, EncoderLenEstimator, EncoderValue};
pub use unaligned::*;
2 changes: 2 additions & 0 deletions quic/s2n-quic-core/src/inet/ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ impl fmt::Display for Protocol {
Self::IPV6_ICMP => "ICMP for IPv6",
Self::IPV6_NO_NXT => "No Next Header for IPv6",
Self::IPV6_OPTS => "Destination Options for IPv6",
Self::UDPLITE => "Lightweight User Datagram",
Self { id } => return write!(f, "[unknown 0x{id:02x}]"),
}
.fmt(f)
Expand Down Expand Up @@ -360,6 +361,7 @@ impl Protocol {
impl_p!(is_ipv6_icmp, IPV6_ICMP, 58);
impl_p!(is_ipv6_no_next, IPV6_NO_NXT, 59);
impl_p!(is_ipv6_options, IPV6_OPTS, 60);
impl_p!(is_udplite, UDPLITE, 136);
}

#[cfg(test)]
Expand Down
6 changes: 3 additions & 3 deletions quic/s2n-quic-core/src/inet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pub mod ipv6;
pub mod udp;
pub mod unspecified;

pub use datagram::*;
pub use datagram::{AncillaryData, DatagramInfo};
pub use ecn::*;
pub use ip::*;
pub use ipv4::*;
pub use ipv6::*;
pub use ipv4::{IpV4Address, SocketAddressV4};
pub use ipv6::{IpV6Address, SocketAddressV6};
pub use unspecified::*;
47 changes: 36 additions & 11 deletions quic/s2n-quic-core/src/xdp/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,59 @@
use super::{bpf::Decoder, path};
use crate::inet::{
ethernet::{self, EtherType},
ip, ipv4, ipv6, udp, SocketAddress,
ip, ipv4, ipv6, udp, ExplicitCongestionNotification, SocketAddress,
};
use s2n_codec::DecoderError;

type Result<Addr, D> = core::result::Result<Option<(Addr, D)>, DecoderError>;

pub trait Validator {
#[inline(always)]
fn validate_local_port(&self, port: u16) -> bool {
fn validate_local_port(&mut self, port: u16) -> bool {
let _ = port;
true
}

#[inline(always)]
fn validate_local_ipv4(&self, ip: &ipv4::IpV4Address) -> bool {
fn validate_local_ipv4(&mut self, ip: &ipv4::IpV4Address) -> bool {
let _ = ip;
true
}

#[inline(always)]
fn validate_local_ipv6(&self, ip: &ipv6::IpV6Address) -> bool {
fn validate_local_ipv6(&mut self, ip: &ipv6::IpV6Address) -> bool {
let _ = ip;
true
}

#[inline(always)]
fn validate_ecn(&mut self, ecn: ExplicitCongestionNotification) -> bool {
let _ = ecn;
true
}
}

impl Validator for () {}

impl Validator for ExplicitCongestionNotification {
#[inline(always)]
fn validate_ecn(&mut self, ecn: Self) -> bool {
*self = ecn;
true
}
}

/// Decodes a path tuple and payload from a raw packet
#[inline(always)]
pub fn decode_packet<'a, D: Decoder<'a>>(buffer: D) -> Result<path::Tuple, D> {
decode_packet_validator(buffer, &())
decode_packet_validator(buffer, &mut ())
}

/// Decodes a path tuple and payload from a raw packet
#[inline(always)]
pub fn decode_packet_validator<'a, D: Decoder<'a>, V: Validator>(
buffer: D,
validator: &V,
validator: &mut V,
) -> Result<path::Tuple, D> {
let (header, buffer) = buffer.decode::<&ethernet::Header>()?;

Expand Down Expand Up @@ -75,7 +89,7 @@ pub fn decode_packet_validator<'a, D: Decoder<'a>, V: Validator>(
#[inline(always)]
fn decode_ipv4<'a, D: Decoder<'a>, V: Validator>(
buffer: D,
validator: &V,
validator: &mut V,
) -> Result<Tuple<SocketAddress>, D> {
let (header, buffer) = buffer.decode::<&ipv4::Header>()?;

Expand Down Expand Up @@ -103,6 +117,10 @@ fn decode_ipv4<'a, D: Decoder<'a>, V: Validator>(
let options_len = count_without_header as usize * (32 / 8);
let (_options, buffer) = buffer.decode_slice(options_len)?;

if !validator.validate_ecn(header.tos().ecn()) {
return Ok(None);
}

Ok(
parse_ip_protocol(protocol, buffer, validator)?.map(|(ports, buffer)| {
let source = header.source().with_port(ports.source).into();
Expand All @@ -119,14 +137,18 @@ fn decode_ipv4<'a, D: Decoder<'a>, V: Validator>(
#[inline(always)]
fn decode_ipv6<'a, D: Decoder<'a>, V: Validator>(
buffer: D,
validator: &V,
validator: &mut V,
) -> Result<Tuple<SocketAddress>, D> {
let (header, buffer) = buffer.decode::<&ipv6::Header>()?;

if !validator.validate_local_ipv6(header.destination()) {
return Ok(None);
}

if !validator.validate_ecn(header.vtcfl().ecn()) {
return Ok(None);
}

let protocol = header.next_header();

// TODO parse Hop-by-hop/Options headers, for now we'll just forward the packet on to the OS
Expand All @@ -148,17 +170,20 @@ fn decode_ipv6<'a, D: Decoder<'a>, V: Validator>(
fn parse_ip_protocol<'a, D: Decoder<'a>, V: Validator>(
protocol: &ip::Protocol,
buffer: D,
validator: &V,
validator: &mut V,
) -> Result<Tuple<u16>, D> {
match *protocol {
ip::Protocol::UDP => parse_udp(buffer, validator),
ip::Protocol::UDP | ip::Protocol::UDPLITE => parse_udp(buffer, validator),
// pass the packet on to the OS network stack if we don't understand it
_ => Ok(None),
}
}

#[inline(always)]
fn parse_udp<'a, D: Decoder<'a>, V: Validator>(buffer: D, validator: &V) -> Result<Tuple<u16>, D> {
fn parse_udp<'a, D: Decoder<'a>, V: Validator>(
buffer: D,
validator: &mut V,
) -> Result<Tuple<u16>, D> {
let (header, buffer) = buffer.decode::<&udp::Header>()?;

// NOTE: duvet doesn't know how to parse this RFC since it doesn't follow more modern formatting
Expand Down
2 changes: 1 addition & 1 deletion tools/xdp/ebpf/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[toolchain]
# pin the version to prevent random breakage
channel = "nightly-2023-03-12"
channel = "nightly-2023-04-28"
# The source code of rustc, provided by the rust-src component, is needed for
# building eBPF programs.
components = [ "rustc", "cargo", "rust-src" ]
4 changes: 2 additions & 2 deletions tools/xdp/ebpf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn handle_packet(ctx: &XdpContext) -> u32 {
// Safety: start and end come from the caller and have been validated
DecoderBufferMut::new(start, end)
};
match decode_packet_validator(buffer, &PortValidator) {
match decode_packet_validator(buffer, &mut PortValidator) {
Ok(Some((_tuple, payload))) => {
// if the payload is empty there isn't much we can do with it
if payload.is_empty() {
Expand All @@ -71,7 +71,7 @@ struct PortValidator;

impl Validator for PortValidator {
#[inline(always)]
fn validate_local_port(&self, port: u16) -> bool {
fn validate_local_port(&mut self, port: u16) -> bool {
// Make sure the port is in the port map. Otherwise, forward the packet to the OS.
PORTS.get_ptr(&port).is_some()
}
Expand Down
4 changes: 2 additions & 2 deletions tools/xdp/lib/s2n-quic-xdp-bpfeb-trace.ebpf
Git LFS file not shown
4 changes: 2 additions & 2 deletions tools/xdp/lib/s2n-quic-xdp-bpfeb.ebpf
Git LFS file not shown
4 changes: 2 additions & 2 deletions tools/xdp/lib/s2n-quic-xdp-bpfel-trace.ebpf
Git LFS file not shown
4 changes: 2 additions & 2 deletions tools/xdp/lib/s2n-quic-xdp-bpfel.ebpf
Git LFS file not shown

0 comments on commit 56748c1

Please sign in to comment.