Skip to content

Commit

Permalink
feat(s2n-quic-platform): add ancillary instructions for bpf assembler
Browse files Browse the repository at this point in the history
  • Loading branch information
camshaft committed Mar 11, 2025
1 parent 25e2706 commit d03633d
Show file tree
Hide file tree
Showing 39 changed files with 1,361 additions and 100 deletions.
2 changes: 1 addition & 1 deletion dc/s2n-quic-dc/src/socket/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{io, net::UdpSocket};
/// High-level algorithm with asm is available here: https://godbolt.org/z/crxT4d53j
pub static ROUTER: Program = Program::new(&[
// load the first byte of the packet
ldb(0),
ldb(abs(0)),
// mask off the LSBs
and(!stream::Tag::MAX as _),
// IF:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
source: dc/s2n-quic-dc/src/socket/bpf.rs
expression: ROUTER
---
LDB [0]
AND #192
JEQ #64,0,1
RET #0
RET #1
l0 : LDB [0]
l1 : AND #192
l2 : JEQ #64,l3,l4
l3 : RET #0
l4 : RET #1
1 change: 1 addition & 0 deletions quic/s2n-quic-platform/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ bolero-generator = "0.13"
futures = { version = "0.3", features = ["std"] }
insta = { version = "1", features = ["json"] }
s2n-quic-core = { path = "../s2n-quic-core", features = ["testing"] }
tempfile = "3"
tokio = { version = "1", features = ["full"] }
tracing = { version = "0.1" }

Expand Down
2 changes: 2 additions & 0 deletions quic/s2n-quic-platform/src/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
pub mod instruction;
#[macro_use]
mod common;
#[macro_use]
pub mod ancillary;

pub mod cbpf;
pub mod ebpf;
Expand Down
188 changes: 188 additions & 0 deletions quic/s2n-quic-platform/src/bpf/ancillary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//! Ancillary data extensions are used to look up additional information about a packet
//!
//! See <https://docs.kernel.org/networking/filter.html#bpf-engine-and-instruction-set>
macro_rules! skf_value {
($name:ident) => {
(libc::SKF_AD_OFF + libc::$name) as _
};
}

#[derive(Clone, Copy, Debug)]
pub struct Info {
/// The special extension name used in `bpf_asm`
pub extension: &'static str,
/// The `C` interface
pub capi: &'static str,
}

/// Returns the [`Info`] for a given offset
pub const fn lookup(offset: u32) -> Option<Info> {
macro_rules! map {
($(($value:expr, $extension:expr, $capi:expr),)*) => {
match offset {
$(_ if offset == $value => Some(Info { extension: $extension, capi: $capi }),)*
_ => None,
}
};
}

map!(
(skb::protocol(), "proto", "skb->protocol"),
(skb::pkt_type(), "type", "skb->pkt_type"),
(skb::ifindex(), "ifidx", "skb->dev->ifindex"),
(skb::mark(), "mark", "skb->mark"),
(skb::queue_mapping(), "queue", "skb->queue_mapping"),
(skb::dev_type(), "hatype", "skb->dev->type"),
(skb::hash(), "rxhash", "skb->hash"),
(skb::vlan_tci(), "vlan_tci", "skb_vlan_tag_get(skb)"),
(skb::vlan_avail(), "vlan_avail", "skb_vlan_tag_present(skb)"),
(skb::vlan_proto(), "vlan_tpid", "skb->vlan_tproto"),
(payload_offset(), "poff", "payload_offset()"),
(raw_smp_processor_id(), "cpu", "raw_smp_processor_id()"),
(get_random_u32(), "rand", "get_random_u32()"),
)
}

pub mod skb {
macro_rules! skb_value {
($name:ident, $value:ident) => {
#[inline]
pub const fn $name() -> u32 {
skf_value!($value)
}
};
}

skb_value!(protocol, SKF_AD_PROTOCOL);
skb_value!(pkt_type, SKF_AD_PKTTYPE);
skb_value!(ifindex, SKF_AD_IFINDEX);
skb_value!(mark, SKF_AD_MARK);
skb_value!(queue_mapping, SKF_AD_QUEUE);
skb_value!(dev_type, SKF_AD_HATYPE);
skb_value!(hash, SKF_AD_RXHASH);
skb_value!(vlan_tci, SKF_AD_VLAN_TAG);
skb_value!(vlan_avail, SKF_AD_VLAN_TAG_PRESENT);
skb_value!(vlan_proto, SKF_AD_VLAN_TPID);
}

#[inline]
pub const fn payload_offset() -> u32 {
skf_value!(SKF_AD_PAY_OFFSET)
}

#[inline]
pub const fn raw_smp_processor_id() -> u32 {
skf_value!(SKF_AD_CPU)
}

#[inline]
pub const fn get_random_u32() -> u32 {
skf_value!(SKF_AD_RANDOM)
}

macro_rules! impl_ancillary {
() => {
/// Ancillary data extensions are used to look up additional information about a packet
///
/// See <https://docs.kernel.org/networking/filter.html#bpf-engine-and-instruction-set>
pub mod ancillary {
use super::{super::ancillary, *};

/// Data associated with the socket buffer (skb)
pub mod skb {
use super::{ancillary::skb, *};

/// Loads the `skb->len` into the `A` register
pub const fn len() -> K {
// use the dialect-specific instruction to load the skb len
//
// in the case of CBPF, there is a single `Mode` for `LEN`
super::super::len()
}

/// Loads the `skb->protocol` into the `A` register
pub const fn protocol() -> K {
abs(skb::protocol())
}

/// Loads the `skb->pkt_type` into the `A` register
pub const fn pkt_type() -> K {
abs(skb::pkt_type())
}

/// Loads the `skb->ifindex` into the `A` register
pub const fn ifindex() -> K {
abs(skb::ifindex())
}

/// Loads the `skb->mark` into the `A` register
pub const fn mark() -> K {
abs(skb::mark())
}

/// Loads the `skb->queue_mapping` into the `A` register
pub const fn queue_mapping() -> K {
abs(skb::queue_mapping())
}

/// Loads the `skb->dev->type` into the `A` register
pub const fn dev_type() -> K {
abs(skb::dev_type())
}

/// Loads the `skb->hash` into the `A` register
pub const fn hash() -> K {
abs(skb::hash())
}

/// Loads the VLAN Tag value into the `A` register
pub const fn vlan_tci() -> K {
abs(skb::vlan_tci())
}

/// Loads the VLAN Tag value into the `A` register
///
/// This is used for compatibility with the C API
pub const fn vlan_tag_get() -> K {
vlan_tci()
}

/// Loads if the VLAN Tag is present into the `A` register
pub const fn vlan_avail() -> K {
abs(skb::vlan_avail())
}

/// Loads if the VLAN Tag is present into the `A` register
///
/// This is used for compatibility with the C API
pub const fn vlan_tag_present() -> K {
vlan_avail()
}

/// Loads the `skb->vlan_proto` (VLAN Protocol) into the `A` register
pub const fn vlan_proto() -> K {
abs(skb::vlan_proto())
}
}

/// Loads the payload offset into the `A` register
pub const fn payload_offset() -> K {
abs(ancillary::payload_offset())
}

/// Loads the CPU ID into the `A` register
pub const fn raw_smp_processor_id() -> K {
abs(ancillary::raw_smp_processor_id())
}

/// Loads a random `u32` into the `A` register
pub const fn get_random_u32() -> K {
abs(ancillary::get_random_u32())
}
}
};
}
113 changes: 70 additions & 43 deletions quic/s2n-quic-platform/src/bpf/cbpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

use core::fmt;

#[cfg(all(test, not(miri)))]
mod tests;

pub use super::common::*;
pub type Instruction = super::Instruction<Cbpf>;
pub type Program<'a> = super::Program<'a, Cbpf>;
Expand Down Expand Up @@ -53,15 +56,20 @@ impl super::instruction::Dialect for Cbpf {
f.field("jf", &jf);
}

f.field("k", &k).finish()
let prefix = if k == 0 { "" } else { "0x" };
f.field("k", &format_args!("{prefix}{k:x}")).finish()
}

fn display(i: &Instruction, f: &mut fmt::Formatter) -> fmt::Result {
fn display(i: &Instruction, f: &mut fmt::Formatter, line: Option<usize>) -> fmt::Result {
let code = i.code;
let k = i.k;
let jt = i.jt;
let jf = i.jf;

if let Some(line) = line {
write!(f, "l{line:<4}: ")?;
}

let class = Class::decode(code);

match class {
Expand All @@ -71,7 +79,20 @@ impl super::instruction::Dialect for Cbpf {

match mode {
Mode::IMM => return write!(f, "{class}{size} #{k}"),
Mode::ABS => return write!(f, "{class}{size} [{k}]"),
Mode::ABS => {
let prefix = if k == 0 { "" } else { "0x" };
return if let Some(info) = super::ancillary::lookup(k) {
write!(
f,
"{class}{size} {} ; [{prefix}{k:x}] // {}",
info.extension, info.capi
)
} else {
write!(f, "{class}{size} [{prefix}{k:x}]")
};
}
Mode::LEN => return write!(f, "{class}{size} len"),
Mode::IND => return write!(f, "{class}{size} [x + {k}]"),
_ => {}
}
}
Expand All @@ -88,17 +109,37 @@ impl super::instruction::Dialect for Cbpf {
let op = Jump::decode(code);
let source = Source::decode(code);

return match source {
Source::K => write!(f, "{op} #{k},{jt},{jf}"),
Source::X => write!(f, "{op} x,{jt},{jf}"),
};
match source {
Source::K => write!(f, "{op} #{k}")?,
Source::X => write!(f, "{op} x")?,
}

if let Some(line) = line {
let line = line + 1;
let jt = line + jt as usize;
let jf = line + jf as usize;
write!(f, ",l{jt},l{jf}")?
} else {
write!(f, ",{jt},{jf}")?
}

return Ok(());
}
Class::RET => {
let source = Source::decode(code);
let size = Size::decode(code);

return match source {
Source::K => write!(f, "{class} #{k}"),
Source::X => write!(f, "{class} x"),
return match (source, size) {
(Source::K, Size::B) if k == 0 => write!(f, "{class} %a"),
(Source::K, _) => write!(f, "{class} #{k}"),
(Source::X, _) => write!(f, "{class} %x"),
};
}
Class::MISC => {
let misc = Misc::decode(code);
return match misc {
Misc::TAX => write!(f, "tax"),
Misc::TXA => write!(f, "txa"),
};
}
_ => {}
Expand Down Expand Up @@ -189,42 +230,28 @@ define!(
}
);

define!(
#[mask(0xf0)]
pub enum Misc {
TAX = 0x00,
TXA = 0x80,
}
);

impl_ops!();
impl_ret!();

#[cfg(test)]
mod tests {
use crate::bpf::cbpf::*;

static PROGRAM: Program = {
const MAX: u8 = 0b0011_1111;

Program::new(&[
// load the first byte of the packet
ldb(0),
// mask off the LSBs
and(!MAX as _),
// IF:
// the control bit is set
jneq(MAX as u32 + 1, 1, 0),
// THEN:
// return a 0 indicating we want to route to the writer socket
ret(0),
// ELSE:
// return a 1 indicating we want to route to the reader socket
ret(1),
])
};

#[test]
#[cfg_attr(miri, ignore)]
fn static_program_debug() {
insta::assert_debug_snapshot!(PROGRAM);
pub const fn len() -> K {
K {
mode: Mode::LEN,
value: 0,
}
}

#[test]
#[cfg_attr(miri, ignore)]
fn static_program_display() {
insta::assert_snapshot!(PROGRAM);
}
pub const fn tax() -> Instruction {
Instruction::raw(Class::MISC as u16 | Misc::TAX as u16, 0, 0, 0)
}

pub const fn txa() -> Instruction {
Instruction::raw(Class::MISC as u16 | Misc::TXA as u16, 0, 0, 0)
}
Loading

0 comments on commit d03633d

Please sign in to comment.