From 2135c943bd45d77fdb14a0394df1cd5c278b89d6 Mon Sep 17 00:00:00 2001 From: Cameron Bytheway Date: Fri, 28 Apr 2023 15:45:48 -0600 Subject: [PATCH] feat(s2n-quic-core): make XDP decoder take mutable validator --- common/s2n-codec/src/lib.rs | 8 +++- quic/s2n-quic-core/src/inet/ip.rs | 2 + quic/s2n-quic-core/src/inet/mod.rs | 5 +-- quic/s2n-quic-core/src/xdp/decoder.rs | 47 ++++++++++++++++----- tools/xdp/ebpf/rust-toolchain.toml | 2 +- tools/xdp/ebpf/src/main.rs | 4 +- tools/xdp/lib/s2n-quic-xdp-bpfeb-trace.ebpf | 4 +- tools/xdp/lib/s2n-quic-xdp-bpfeb.ebpf | 4 +- tools/xdp/lib/s2n-quic-xdp-bpfel-trace.ebpf | 4 +- tools/xdp/lib/s2n-quic-xdp-bpfel.ebpf | 4 +- 10 files changed, 57 insertions(+), 27 deletions(-) diff --git a/common/s2n-codec/src/lib.rs b/common/s2n-codec/src/lib.rs index 97a0c81c99..33018d1c61 100644 --- a/common/s2n-codec/src/lib.rs +++ b/common/s2n-codec/src/lib.rs @@ -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::*; diff --git a/quic/s2n-quic-core/src/inet/ip.rs b/quic/s2n-quic-core/src/inet/ip.rs index dc8616f1fc..75a6137b24 100644 --- a/quic/s2n-quic-core/src/inet/ip.rs +++ b/quic/s2n-quic-core/src/inet/ip.rs @@ -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) @@ -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)] diff --git a/quic/s2n-quic-core/src/inet/mod.rs b/quic/s2n-quic-core/src/inet/mod.rs index e389ac1d5a..18050cd225 100644 --- a/quic/s2n-quic-core/src/inet/mod.rs +++ b/quic/s2n-quic-core/src/inet/mod.rs @@ -14,9 +14,8 @@ pub mod ipv6; pub mod udp; pub mod unspecified; -pub use datagram::*; 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::*; diff --git a/quic/s2n-quic-core/src/xdp/decoder.rs b/quic/s2n-quic-core/src/xdp/decoder.rs index 4a34ae0824..deb05404ad 100644 --- a/quic/s2n-quic-core/src/xdp/decoder.rs +++ b/quic/s2n-quic-core/src/xdp/decoder.rs @@ -4,7 +4,7 @@ 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; @@ -12,37 +12,51 @@ type Result = core::result::Result, 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 { - 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 { let (header, buffer) = buffer.decode::<ðernet::Header>()?; @@ -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, D> { let (header, buffer) = buffer.decode::<&ipv4::Header>()?; @@ -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(); @@ -119,7 +137,7 @@ 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, D> { let (header, buffer) = buffer.decode::<&ipv6::Header>()?; @@ -127,6 +145,10 @@ fn decode_ipv6<'a, D: Decoder<'a>, V: Validator>( 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 @@ -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, 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, D> { +fn parse_udp<'a, D: Decoder<'a>, V: Validator>( + buffer: D, + validator: &mut V, +) -> Result, 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 diff --git a/tools/xdp/ebpf/rust-toolchain.toml b/tools/xdp/ebpf/rust-toolchain.toml index d652c89f56..758887fce6 100644 --- a/tools/xdp/ebpf/rust-toolchain.toml +++ b/tools/xdp/ebpf/rust-toolchain.toml @@ -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" ] diff --git a/tools/xdp/ebpf/src/main.rs b/tools/xdp/ebpf/src/main.rs index 25f27c4a26..bb7ebee62a 100644 --- a/tools/xdp/ebpf/src/main.rs +++ b/tools/xdp/ebpf/src/main.rs @@ -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() { @@ -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() } diff --git a/tools/xdp/lib/s2n-quic-xdp-bpfeb-trace.ebpf b/tools/xdp/lib/s2n-quic-xdp-bpfeb-trace.ebpf index 169beeb2e9..b48b6efffb 100644 --- a/tools/xdp/lib/s2n-quic-xdp-bpfeb-trace.ebpf +++ b/tools/xdp/lib/s2n-quic-xdp-bpfeb-trace.ebpf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79d7fe88eb5ab1117b623c5a4b0b614d3027116e091a4fd56b6d80242f58a004 -size 12112 +oid sha256:19e2b73f5fa37c850891f7b653213d943129e33cb582f314de01d03df98776ca +size 12152 diff --git a/tools/xdp/lib/s2n-quic-xdp-bpfeb.ebpf b/tools/xdp/lib/s2n-quic-xdp-bpfeb.ebpf index 636dd25651..2fd61848c2 100644 --- a/tools/xdp/lib/s2n-quic-xdp-bpfeb.ebpf +++ b/tools/xdp/lib/s2n-quic-xdp-bpfeb.ebpf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22043f019bb4a775a728fcad1b16039913820beb2e974bc9448784c405c39501 -size 2448 +oid sha256:a4528d28df7df772bdf8f2a3bf2a75f8e4aa4662c9dad2f8a86ce6830e4910d9 +size 2408 diff --git a/tools/xdp/lib/s2n-quic-xdp-bpfel-trace.ebpf b/tools/xdp/lib/s2n-quic-xdp-bpfel-trace.ebpf index 956deb87a0..913558bf64 100644 --- a/tools/xdp/lib/s2n-quic-xdp-bpfel-trace.ebpf +++ b/tools/xdp/lib/s2n-quic-xdp-bpfel-trace.ebpf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:641dc1be3f5c765aa0a79272f34d11b8a1edc51a63c741058dacc9ad14a68f86 -size 12128 +oid sha256:31759957edc3eec74590eb8482e6a82a58d1fb51447bf07658ea5c8c48bdd54b +size 12184 diff --git a/tools/xdp/lib/s2n-quic-xdp-bpfel.ebpf b/tools/xdp/lib/s2n-quic-xdp-bpfel.ebpf index a22554640f..203eb31675 100644 --- a/tools/xdp/lib/s2n-quic-xdp-bpfel.ebpf +++ b/tools/xdp/lib/s2n-quic-xdp-bpfel.ebpf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0373bdcc88e9900b3bbb38967ef07fec53ce772c35b40bce30f44a457b47f78e -size 2480 +oid sha256:c73a3236182130a5b8788d302333e5cc1a92fce93a20466c0a2e179cc943abef +size 2440