use bytes::{Bytes, BytesMut};
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use std::hint::black_box;
use rayfish::firewall::{
self, Action, Direction, FirewallConfig, FirewallRule, PeerFilter, PortRange, Protocol,
RuleOrigin, SharedFirewall,
};
const SIZES: &[usize] = &[64, 1280];
const POOL_CHUNK: usize = 64 * 1024;
const MAX_DATAGRAM: usize = 1500;
fn ipv4_tcp_packet(len: usize, dst_port: u16) -> Vec<u8> {
let mut p = vec![0u8; len.max(24)];
p[0] = 0x45; p[9] = 6; p[16..20].copy_from_slice(&[100, 64, 0, 3]); p[20] = 0;
p[21] = 80; p[22] = (dst_port >> 8) as u8;
p[23] = dst_port as u8;
p.truncate(len.max(24));
p
}
fn bench_handoff(c: &mut Criterion) {
let mut group = c.benchmark_group("handoff");
for &size in SIZES {
let packet = ipv4_tcp_packet(size, 443);
group.throughput(Throughput::Bytes(size as u64));
group.bench_with_input(BenchmarkId::new("tx_copy", size), &packet, |b, pkt| {
b.iter(|| {
let owned = Bytes::copy_from_slice(black_box(&pkt[..]));
black_box(owned)
});
});
group.bench_with_input(BenchmarkId::new("tx_zerocopy", size), &packet, |b, pkt| {
let mut pool = BytesMut::with_capacity(POOL_CHUNK);
b.iter(|| {
if pool.capacity() < MAX_DATAGRAM {
pool.reserve(POOL_CHUNK);
}
pool.extend_from_slice(black_box(&pkt[..]));
let out = pool.split_to(pkt.len()).freeze();
black_box(out)
});
});
let datagram = Bytes::copy_from_slice(&packet);
group.bench_with_input(BenchmarkId::new("rx_copy", size), &datagram, |b, dg| {
b.iter(|| {
let v = black_box(dg).to_vec();
black_box(v)
});
});
group.bench_with_input(BenchmarkId::new("rx_zerocopy", size), &datagram, |b, dg| {
b.iter(|| {
let cloned = black_box(dg).clone();
black_box(cloned)
});
});
}
group.finish();
}
fn bench_firewall(c: &mut Criterion) {
let peer = iroh::SecretKey::generate().public();
let net = "bench-net";
let allow_all = SharedFirewall::new(FirewallConfig::default());
let whitelist = SharedFirewall::new(FirewallConfig {
default_inbound: Action::Allow,
default_outbound: Action::Allow,
rules: vec![
rule(Direction::In, Action::Allow, Protocol::Tcp, Some((22, 22))),
rule(Direction::In, Action::Allow, Protocol::Tcp, Some((80, 80))),
rule(
Direction::In,
Action::Allow,
Protocol::Tcp,
Some((443, 443)),
),
rule(Direction::In, Action::Deny, Protocol::Any, None),
],
});
let packet = ipv4_tcp_packet(1280, 443);
let mut group = c.benchmark_group("firewall");
group.throughput(Throughput::Elements(1));
group.bench_function("parse_only", |b| {
b.iter(|| black_box(firewall::parse_packet_info(black_box(&packet))));
});
group.bench_function("parse_eval_out_allow", |b| {
b.iter(|| {
let info = firewall::parse_packet_info(black_box(&packet)).unwrap();
black_box(allow_all.evaluate_packet(Direction::Out, &info, &peer, Some(net)))
});
});
group.bench_function("parse_eval_in_whitelist", |b| {
b.iter(|| {
let info = firewall::parse_packet_info(black_box(&packet)).unwrap();
black_box(whitelist.evaluate_packet(Direction::In, &info, &peer, Some(net)))
});
});
group.finish();
}
fn rule(
direction: Direction,
action: Action,
protocol: Protocol,
port: Option<(u16, u16)>,
) -> FirewallRule {
FirewallRule {
direction,
action,
protocol,
port: port.map(|(start, end)| PortRange { start, end }),
peer: PeerFilter::Any,
network: None,
origin: RuleOrigin::Local,
}
}
criterion_group!(benches, bench_handoff, bench_firewall);
criterion_main!(benches);