Skip to content

Commit 1d68c8d

Browse files
committed
attach bpf filter to icmp socket on polls
this will improve performance when having lots of forwarder instances with icmp remote on same server
1 parent 3f41b35 commit 1d68c8d

5 files changed

Lines changed: 33 additions & 10 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

forwarder/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ etherparse = "0.13.0"
1010
socket2 = { version = "0.5.5", features = ["all"] }
1111
mio = { version = "1.0.2", features = ["net", "os-poll"] }
1212
parking_lot = "0.12.3"
13+
libc = "0.2.158"

forwarder/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ pub fn run(listen_uri: Uri, remote_uri: Uri, passphrase: Option<String>) -> anyh
3434
let socket = Arc::new(socket);
3535
log::info!("listen on '{listen_addr}'");
3636

37-
let poll = poll::new(remote_uri.protocol, remote_uri.addr.is_ipv6())
38-
.with_context(|| "couldn't create poll")?;
37+
let poll =
38+
poll::new(remote_uri.protocol, remote_uri.addr).with_context(|| "couldn't create poll")?;
3939
let registry = poll
4040
.get_registry()
4141
.with_context(|| "couldn't get registry of poll")?;

forwarder/src/poll.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
uri::Protocol,
55
};
66
use parking_lot::RwLock;
7-
use std::sync::Arc;
7+
use std::{net::SocketAddr, sync::Arc};
88

99
type OnPeerRecvCallback = dyn Fn(&Peer, &mut [u8]);
1010

@@ -32,9 +32,9 @@ pub trait Registry: Send + Sync {
3232
mod icmp;
3333
mod udp;
3434

35-
pub fn new(protocol: Protocol, is_ipv6: bool) -> anyhow::Result<Box<dyn Poll>> {
35+
pub fn new(protocol: Protocol, remote_addr: SocketAddr) -> anyhow::Result<Box<dyn Poll>> {
3636
Ok(match protocol {
3737
Protocol::Udp => Box::new(udp::UdpPoll(mio::Poll::new()?)),
38-
Protocol::Icmp => Box::new(icmp::IcmpPoll { is_ipv6 }),
38+
Protocol::Icmp => Box::new(icmp::IcmpPoll { remote_addr }),
3939
})
4040
}

forwarder/src/poll/icmp.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ use crate::{
55
MAX_PACKET_SIZE,
66
};
77
use parking_lot::RwLock;
8-
use std::{mem::MaybeUninit, sync::Arc};
8+
use std::{mem::MaybeUninit, net::SocketAddr, sync::Arc};
99

1010
#[derive(Debug)]
1111
pub struct IcmpPoll {
12-
pub is_ipv6: bool,
12+
pub remote_addr: SocketAddr,
1313
}
1414

1515
impl Poll for IcmpPoll {
@@ -22,18 +22,28 @@ impl Poll for IcmpPoll {
2222
peers: Arc<RwLock<PeerManager>>,
2323
on_peer_recv: Box<dyn Fn(&Peer, &mut [u8])>,
2424
) -> anyhow::Result<()> {
25-
let listen_addr = crate::peer::create_any_addr(self.is_ipv6);
25+
let is_ipv6 = self.remote_addr.is_ipv6();
26+
let listen_addr = crate::peer::create_any_addr(is_ipv6);
2627
let socket: socket2::Socket = IcmpSocket::inner_bind(listen_addr)?;
2728
let mut buffer = [0u8; MAX_PACKET_SIZE];
2829

30+
#[cfg(target_os = "linux")]
31+
if !is_ipv6 {
32+
let filter = create_bpf_filter(self.remote_addr.port());
33+
if let Err(error) = socket.attach_filter(&filter) {
34+
// filter is not required so continue if it errors
35+
log::warn!("couldn't attach bpf filter to socket: {error:?}");
36+
}
37+
}
38+
2939
loop {
3040
let Ok(size) =
3141
socket.recv(unsafe { &mut *(&mut buffer as *mut [u8] as *mut [MaybeUninit<u8>]) })
3242
else {
3343
continue;
3444
};
3545
let Some(icmp_packet) =
36-
crate::socket::icmp::parse_icmp_packet(&mut buffer[..size], self.is_ipv6)
46+
crate::socket::icmp::parse_icmp_packet(&mut buffer[..size], is_ipv6)
3747
else {
3848
continue;
3949
};
@@ -47,6 +57,17 @@ impl Poll for IcmpPoll {
4757
}
4858
}
4959

60+
#[cfg(target_os = "linux")]
61+
fn create_bpf_filter(remote_port: u16) -> [libc::sock_filter; 4] {
62+
[
63+
(0x28, 0, 0, 0x0000001a), // ldh [26] ; icmp sequence
64+
(0x15, 0, 1, remote_port as u32), // jne #port, drop
65+
(0x06, 0, 0, 0xffffffff), // ret #-1
66+
(0x06, 0, 0, 0000000000), // drop: ret #0
67+
]
68+
.map(|(code, jt, jf, k)| libc::sock_filter { code, jt, jf, k })
69+
}
70+
5071
#[derive(Debug)]
5172
pub struct IcmpRegistry;
5273
// icmp doesn't need a registry because we manage it's poll ourself

0 commit comments

Comments
 (0)