Skip to content

Commit d03633d

Browse files
committed
feat(s2n-quic-platform): add ancillary instructions for bpf assembler
1 parent 25e2706 commit d03633d

File tree

39 files changed

+1361
-100
lines changed

39 files changed

+1361
-100
lines changed

dc/s2n-quic-dc/src/socket/bpf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{io, net::UdpSocket};
1111
/// High-level algorithm with asm is available here: https://godbolt.org/z/crxT4d53j
1212
pub static ROUTER: Program = Program::new(&[
1313
// load the first byte of the packet
14-
ldb(0),
14+
ldb(abs(0)),
1515
// mask off the LSBs
1616
and(!stream::Tag::MAX as _),
1717
// IF:

dc/s2n-quic-dc/src/socket/snapshots/s2n_quic_dc__socket__bpf__tests__snapshot_test.snap

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
source: dc/s2n-quic-dc/src/socket/bpf.rs
33
expression: ROUTER
44
---
5-
LDB [0]
6-
AND #192
7-
JEQ #64,0,1
8-
RET #0
9-
RET #1
5+
l0 : LDB [0]
6+
l1 : AND #192
7+
l2 : JEQ #64,l3,l4
8+
l3 : RET #0
9+
l4 : RET #1

quic/s2n-quic-platform/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ bolero-generator = "0.13"
4242
futures = { version = "0.3", features = ["std"] }
4343
insta = { version = "1", features = ["json"] }
4444
s2n-quic-core = { path = "../s2n-quic-core", features = ["testing"] }
45+
tempfile = "3"
4546
tokio = { version = "1", features = ["full"] }
4647
tracing = { version = "0.1" }
4748

quic/s2n-quic-platform/src/bpf.rs

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
pub mod instruction;
66
#[macro_use]
77
mod common;
8+
#[macro_use]
9+
pub mod ancillary;
810

911
pub mod cbpf;
1012
pub mod ebpf;
+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Ancillary data extensions are used to look up additional information about a packet
5+
//!
6+
//! See <https://docs.kernel.org/networking/filter.html#bpf-engine-and-instruction-set>
7+
8+
macro_rules! skf_value {
9+
($name:ident) => {
10+
(libc::SKF_AD_OFF + libc::$name) as _
11+
};
12+
}
13+
14+
#[derive(Clone, Copy, Debug)]
15+
pub struct Info {
16+
/// The special extension name used in `bpf_asm`
17+
pub extension: &'static str,
18+
/// The `C` interface
19+
pub capi: &'static str,
20+
}
21+
22+
/// Returns the [`Info`] for a given offset
23+
pub const fn lookup(offset: u32) -> Option<Info> {
24+
macro_rules! map {
25+
($(($value:expr, $extension:expr, $capi:expr),)*) => {
26+
match offset {
27+
$(_ if offset == $value => Some(Info { extension: $extension, capi: $capi }),)*
28+
_ => None,
29+
}
30+
};
31+
}
32+
33+
map!(
34+
(skb::protocol(), "proto", "skb->protocol"),
35+
(skb::pkt_type(), "type", "skb->pkt_type"),
36+
(skb::ifindex(), "ifidx", "skb->dev->ifindex"),
37+
(skb::mark(), "mark", "skb->mark"),
38+
(skb::queue_mapping(), "queue", "skb->queue_mapping"),
39+
(skb::dev_type(), "hatype", "skb->dev->type"),
40+
(skb::hash(), "rxhash", "skb->hash"),
41+
(skb::vlan_tci(), "vlan_tci", "skb_vlan_tag_get(skb)"),
42+
(skb::vlan_avail(), "vlan_avail", "skb_vlan_tag_present(skb)"),
43+
(skb::vlan_proto(), "vlan_tpid", "skb->vlan_tproto"),
44+
(payload_offset(), "poff", "payload_offset()"),
45+
(raw_smp_processor_id(), "cpu", "raw_smp_processor_id()"),
46+
(get_random_u32(), "rand", "get_random_u32()"),
47+
)
48+
}
49+
50+
pub mod skb {
51+
macro_rules! skb_value {
52+
($name:ident, $value:ident) => {
53+
#[inline]
54+
pub const fn $name() -> u32 {
55+
skf_value!($value)
56+
}
57+
};
58+
}
59+
60+
skb_value!(protocol, SKF_AD_PROTOCOL);
61+
skb_value!(pkt_type, SKF_AD_PKTTYPE);
62+
skb_value!(ifindex, SKF_AD_IFINDEX);
63+
skb_value!(mark, SKF_AD_MARK);
64+
skb_value!(queue_mapping, SKF_AD_QUEUE);
65+
skb_value!(dev_type, SKF_AD_HATYPE);
66+
skb_value!(hash, SKF_AD_RXHASH);
67+
skb_value!(vlan_tci, SKF_AD_VLAN_TAG);
68+
skb_value!(vlan_avail, SKF_AD_VLAN_TAG_PRESENT);
69+
skb_value!(vlan_proto, SKF_AD_VLAN_TPID);
70+
}
71+
72+
#[inline]
73+
pub const fn payload_offset() -> u32 {
74+
skf_value!(SKF_AD_PAY_OFFSET)
75+
}
76+
77+
#[inline]
78+
pub const fn raw_smp_processor_id() -> u32 {
79+
skf_value!(SKF_AD_CPU)
80+
}
81+
82+
#[inline]
83+
pub const fn get_random_u32() -> u32 {
84+
skf_value!(SKF_AD_RANDOM)
85+
}
86+
87+
macro_rules! impl_ancillary {
88+
() => {
89+
/// Ancillary data extensions are used to look up additional information about a packet
90+
///
91+
/// See <https://docs.kernel.org/networking/filter.html#bpf-engine-and-instruction-set>
92+
pub mod ancillary {
93+
use super::{super::ancillary, *};
94+
95+
/// Data associated with the socket buffer (skb)
96+
pub mod skb {
97+
use super::{ancillary::skb, *};
98+
99+
/// Loads the `skb->len` into the `A` register
100+
pub const fn len() -> K {
101+
// use the dialect-specific instruction to load the skb len
102+
//
103+
// in the case of CBPF, there is a single `Mode` for `LEN`
104+
super::super::len()
105+
}
106+
107+
/// Loads the `skb->protocol` into the `A` register
108+
pub const fn protocol() -> K {
109+
abs(skb::protocol())
110+
}
111+
112+
/// Loads the `skb->pkt_type` into the `A` register
113+
pub const fn pkt_type() -> K {
114+
abs(skb::pkt_type())
115+
}
116+
117+
/// Loads the `skb->ifindex` into the `A` register
118+
pub const fn ifindex() -> K {
119+
abs(skb::ifindex())
120+
}
121+
122+
/// Loads the `skb->mark` into the `A` register
123+
pub const fn mark() -> K {
124+
abs(skb::mark())
125+
}
126+
127+
/// Loads the `skb->queue_mapping` into the `A` register
128+
pub const fn queue_mapping() -> K {
129+
abs(skb::queue_mapping())
130+
}
131+
132+
/// Loads the `skb->dev->type` into the `A` register
133+
pub const fn dev_type() -> K {
134+
abs(skb::dev_type())
135+
}
136+
137+
/// Loads the `skb->hash` into the `A` register
138+
pub const fn hash() -> K {
139+
abs(skb::hash())
140+
}
141+
142+
/// Loads the VLAN Tag value into the `A` register
143+
pub const fn vlan_tci() -> K {
144+
abs(skb::vlan_tci())
145+
}
146+
147+
/// Loads the VLAN Tag value into the `A` register
148+
///
149+
/// This is used for compatibility with the C API
150+
pub const fn vlan_tag_get() -> K {
151+
vlan_tci()
152+
}
153+
154+
/// Loads if the VLAN Tag is present into the `A` register
155+
pub const fn vlan_avail() -> K {
156+
abs(skb::vlan_avail())
157+
}
158+
159+
/// Loads if the VLAN Tag is present into the `A` register
160+
///
161+
/// This is used for compatibility with the C API
162+
pub const fn vlan_tag_present() -> K {
163+
vlan_avail()
164+
}
165+
166+
/// Loads the `skb->vlan_proto` (VLAN Protocol) into the `A` register
167+
pub const fn vlan_proto() -> K {
168+
abs(skb::vlan_proto())
169+
}
170+
}
171+
172+
/// Loads the payload offset into the `A` register
173+
pub const fn payload_offset() -> K {
174+
abs(ancillary::payload_offset())
175+
}
176+
177+
/// Loads the CPU ID into the `A` register
178+
pub const fn raw_smp_processor_id() -> K {
179+
abs(ancillary::raw_smp_processor_id())
180+
}
181+
182+
/// Loads a random `u32` into the `A` register
183+
pub const fn get_random_u32() -> K {
184+
abs(ancillary::get_random_u32())
185+
}
186+
}
187+
};
188+
}

quic/s2n-quic-platform/src/bpf/cbpf.rs

+70-43
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
use core::fmt;
55

6+
#[cfg(all(test, not(miri)))]
7+
mod tests;
8+
69
pub use super::common::*;
710
pub type Instruction = super::Instruction<Cbpf>;
811
pub type Program<'a> = super::Program<'a, Cbpf>;
@@ -53,15 +56,20 @@ impl super::instruction::Dialect for Cbpf {
5356
f.field("jf", &jf);
5457
}
5558

56-
f.field("k", &k).finish()
59+
let prefix = if k == 0 { "" } else { "0x" };
60+
f.field("k", &format_args!("{prefix}{k:x}")).finish()
5761
}
5862

59-
fn display(i: &Instruction, f: &mut fmt::Formatter) -> fmt::Result {
63+
fn display(i: &Instruction, f: &mut fmt::Formatter, line: Option<usize>) -> fmt::Result {
6064
let code = i.code;
6165
let k = i.k;
6266
let jt = i.jt;
6367
let jf = i.jf;
6468

69+
if let Some(line) = line {
70+
write!(f, "l{line:<4}: ")?;
71+
}
72+
6573
let class = Class::decode(code);
6674

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

7280
match mode {
7381
Mode::IMM => return write!(f, "{class}{size} #{k}"),
74-
Mode::ABS => return write!(f, "{class}{size} [{k}]"),
82+
Mode::ABS => {
83+
let prefix = if k == 0 { "" } else { "0x" };
84+
return if let Some(info) = super::ancillary::lookup(k) {
85+
write!(
86+
f,
87+
"{class}{size} {} ; [{prefix}{k:x}] // {}",
88+
info.extension, info.capi
89+
)
90+
} else {
91+
write!(f, "{class}{size} [{prefix}{k:x}]")
92+
};
93+
}
94+
Mode::LEN => return write!(f, "{class}{size} len"),
95+
Mode::IND => return write!(f, "{class}{size} [x + {k}]"),
7596
_ => {}
7697
}
7798
}
@@ -88,17 +109,37 @@ impl super::instruction::Dialect for Cbpf {
88109
let op = Jump::decode(code);
89110
let source = Source::decode(code);
90111

91-
return match source {
92-
Source::K => write!(f, "{op} #{k},{jt},{jf}"),
93-
Source::X => write!(f, "{op} x,{jt},{jf}"),
94-
};
112+
match source {
113+
Source::K => write!(f, "{op} #{k}")?,
114+
Source::X => write!(f, "{op} x")?,
115+
}
116+
117+
if let Some(line) = line {
118+
let line = line + 1;
119+
let jt = line + jt as usize;
120+
let jf = line + jf as usize;
121+
write!(f, ",l{jt},l{jf}")?
122+
} else {
123+
write!(f, ",{jt},{jf}")?
124+
}
125+
126+
return Ok(());
95127
}
96128
Class::RET => {
97129
let source = Source::decode(code);
130+
let size = Size::decode(code);
98131

99-
return match source {
100-
Source::K => write!(f, "{class} #{k}"),
101-
Source::X => write!(f, "{class} x"),
132+
return match (source, size) {
133+
(Source::K, Size::B) if k == 0 => write!(f, "{class} %a"),
134+
(Source::K, _) => write!(f, "{class} #{k}"),
135+
(Source::X, _) => write!(f, "{class} %x"),
136+
};
137+
}
138+
Class::MISC => {
139+
let misc = Misc::decode(code);
140+
return match misc {
141+
Misc::TAX => write!(f, "tax"),
142+
Misc::TXA => write!(f, "txa"),
102143
};
103144
}
104145
_ => {}
@@ -189,42 +230,28 @@ define!(
189230
}
190231
);
191232

233+
define!(
234+
#[mask(0xf0)]
235+
pub enum Misc {
236+
TAX = 0x00,
237+
TXA = 0x80,
238+
}
239+
);
240+
192241
impl_ops!();
193242
impl_ret!();
194243

195-
#[cfg(test)]
196-
mod tests {
197-
use crate::bpf::cbpf::*;
198-
199-
static PROGRAM: Program = {
200-
const MAX: u8 = 0b0011_1111;
201-
202-
Program::new(&[
203-
// load the first byte of the packet
204-
ldb(0),
205-
// mask off the LSBs
206-
and(!MAX as _),
207-
// IF:
208-
// the control bit is set
209-
jneq(MAX as u32 + 1, 1, 0),
210-
// THEN:
211-
// return a 0 indicating we want to route to the writer socket
212-
ret(0),
213-
// ELSE:
214-
// return a 1 indicating we want to route to the reader socket
215-
ret(1),
216-
])
217-
};
218-
219-
#[test]
220-
#[cfg_attr(miri, ignore)]
221-
fn static_program_debug() {
222-
insta::assert_debug_snapshot!(PROGRAM);
244+
pub const fn len() -> K {
245+
K {
246+
mode: Mode::LEN,
247+
value: 0,
223248
}
249+
}
224250

225-
#[test]
226-
#[cfg_attr(miri, ignore)]
227-
fn static_program_display() {
228-
insta::assert_snapshot!(PROGRAM);
229-
}
251+
pub const fn tax() -> Instruction {
252+
Instruction::raw(Class::MISC as u16 | Misc::TAX as u16, 0, 0, 0)
253+
}
254+
255+
pub const fn txa() -> Instruction {
256+
Instruction::raw(Class::MISC as u16 | Misc::TXA as u16, 0, 0, 0)
230257
}

0 commit comments

Comments
 (0)