Skip to content

Commit 6f0fcbd

Browse files
committed
implement icmpv6 on ipv4
1 parent 7471086 commit 6f0fcbd

9 files changed

Lines changed: 141 additions & 51 deletions

File tree

forwarder-cli/src/icmp_rev/health_check.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::Args;
22
use forwarder::{
33
create_socket_buffer,
44
socket::{
5-
icmp::{create_bfp_filter, IcmpEchoType, IcmpSocket},
5+
icmp::{create_bfp_filter, IcmpConfig, IcmpEchoType, IcmpSocket},
66
SocketTrait,
77
},
88
};
@@ -30,7 +30,11 @@ const PACKET_COUNTER_LEN: usize = 100;
3030

3131
pub fn initiate_server(cli: &Args) -> anyhow::Result<Arc<AtomicBool>> {
3232
let listen_addr = SocketAddr::new(cli.listen_uri.addr.ip(), cli.remote_uri.addr.port());
33-
let mut socket = IcmpSocket::bind(&listen_addr, IcmpEchoType::Request, false)?;
33+
let config = IcmpConfig {
34+
echo_type: IcmpEchoType::Request,
35+
force_icmpv6: cli.force_icmpv6,
36+
};
37+
let mut socket = IcmpSocket::bind(&listen_addr, config, false)?;
3438
socket.set_read_timeout(Some(HELLO_TIMEOUT))?;
3539

3640
log::info!("waiting for client handshake...");
@@ -101,7 +105,11 @@ fn spawn_server_health_check(
101105
}
102106

103107
pub fn initiate_client(cli: &Args, reverse_addr: IpAddr) -> anyhow::Result<Arc<AtomicBool>> {
104-
let mut socket = IcmpSocket::bind(&cli.listen_uri.addr, IcmpEchoType::Reply, false)?;
108+
let config = IcmpConfig {
109+
echo_type: IcmpEchoType::Reply,
110+
force_icmpv6: cli.force_icmpv6,
111+
};
112+
let mut socket = IcmpSocket::bind(&cli.listen_uri.addr, config, false)?;
105113
socket.set_read_timeout(Some(HELLO_TIMEOUT))?;
106114
socket.inner_socket().attach_filter(&create_bfp_filter(
107115
false,

forwarder-cli/src/main.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ pub struct Args {
2828
#[arg(short = 'R', long)]
2929
pub reverse: bool,
3030

31+
/// When having one icmp uri this will force using icmpv6 protocol on ipv4
32+
#[arg(short = '6', long)]
33+
pub force_icmpv6: bool,
34+
3135
/// Ip of server you wanna connect to and start the icmp connection in reverse mode
3236
#[arg(long)]
3337
pub reverse_addr: Option<IpAddr>,
@@ -49,7 +53,11 @@ fn main() -> anyhow::Result<()> {
4953
pushack::prepare_firewall(&cli)?;
5054

5155
if cli.child || !cli.reverse {
52-
forwarder::run(cli.listen_uri, cli.remote_uri, cli.passphrase, cli.reverse)?;
56+
let args = forwarder::Args {
57+
reverse_icmp: cli.reverse,
58+
force_icmpv6: cli.force_icmpv6,
59+
};
60+
forwarder::run(cli.listen_uri, cli.remote_uri, cli.passphrase, args)?;
5361
} else {
5462
icmp_rev::run_reverse(cli)?;
5563
}

forwarder/src/lib.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub mod socket;
55
pub mod uri;
66
pub(crate) mod utils;
77

8-
use crate::socket::icmp::IcmpEchoType;
8+
use crate::socket::icmp::{IcmpConfig, IcmpEchoType};
99
use anyhow::Context;
1010
use parking_lot::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard};
1111
use poll::Poll;
@@ -22,6 +22,12 @@ const MAX_PACKET_SIZE: usize = 65535;
2222
/// interval that cleanup happens, lowering this result in lower allowed unused time
2323
const CLEANUP_INTERVAL: Duration = Duration::from_secs(7 * 60);
2424

25+
#[derive(Default, Clone)]
26+
pub struct Args {
27+
pub reverse_icmp: bool,
28+
pub force_icmpv6: bool,
29+
}
30+
2531
/// blocks current thread and runs a forwarder server that listens on `listen_uri` and forwards
2632
/// all incoming packets to `remote_uri` and also forwards all packets returned by `remote_uri`
2733
/// to the client that initiated the connection
@@ -33,19 +39,28 @@ pub fn run(
3339
listen_uri: Uri,
3440
remote_uri: Uri,
3541
passphrase: Option<String>,
36-
reverse_icmp: bool,
42+
args: Args,
3743
) -> anyhow::Result<()> {
3844
let listen_addr = &listen_uri.addr;
39-
let icmp_type = if reverse_icmp {
45+
let force_icmpv6 = args.force_icmpv6;
46+
let icmp_type = if args.reverse_icmp {
4047
IcmpEchoType::Reply
4148
} else {
4249
IcmpEchoType::Request
4350
};
44-
let socket = Socket::bind(listen_uri.protocol, listen_addr, icmp_type)
51+
let icmp_config = IcmpConfig {
52+
echo_type: icmp_type,
53+
force_icmpv6,
54+
};
55+
let socket = Socket::bind(listen_uri.protocol, listen_addr, icmp_config)
4556
.with_context(|| "couldn't create server")?;
4657
log::info!("listen on '{listen_addr}'");
4758

48-
let poll = poll::new(remote_uri.protocol, remote_uri.addr, icmp_type.opposite())
59+
let icmp_config = IcmpConfig {
60+
echo_type: icmp_type.opposite(),
61+
force_icmpv6: args.force_icmpv6,
62+
};
63+
let poll = poll::new(remote_uri.protocol, remote_uri.addr, icmp_config)
4964
.with_context(|| "couldn't create poll")?;
5065
let registry = poll
5166
.get_registry()
@@ -65,6 +80,7 @@ pub fn run(
6580
passphrase,
6681
remote_uri,
6782
icmp_type.opposite(),
83+
force_icmpv6,
6884
listen_uri.addr.port(),
6985
);
7086
Ok(())
@@ -86,6 +102,7 @@ fn run_server(
86102
passphrase: Option<String>,
87103
remote_uri: Uri,
88104
peer_icmp_type: IcmpEchoType,
105+
force_icmpv6: bool,
89106
listening_port: u16,
90107
) {
91108
let buffer = create_socket_buffer!(MAX_PACKET_SIZE);
@@ -112,7 +129,11 @@ fn run_server(
112129
None => {
113130
log::info!("new client '{from_addr}'");
114131
let peers = RwLockUpgradableReadGuard::upgrade(peers);
115-
let peer = match add_new_peer(&remote_uri, from_addr, peers, peer_icmp_type) {
132+
let icmp_config = IcmpConfig {
133+
echo_type: peer_icmp_type,
134+
force_icmpv6,
135+
};
136+
let peer = match add_new_peer(&remote_uri, from_addr, peers, icmp_config) {
116137
Ok(peer) => peer,
117138
Err(error) => {
118139
log::error!("couldn't add new peer: {error:?}");
@@ -132,9 +153,9 @@ fn add_new_peer(
132153
remote_uri: &Uri,
133154
from_addr: SocketAddr,
134155
mut peers: RwLockWriteGuard<PeerManager>,
135-
icmp_echo_type: IcmpEchoType,
156+
icmp_config: IcmpConfig,
136157
) -> anyhow::Result<Arc<Peer>> {
137-
let new_peer = Peer::new(remote_uri, from_addr, icmp_echo_type)?;
158+
let new_peer = Peer::new(remote_uri, from_addr, icmp_config)?;
138159
let peer = peers.add_peer(new_peer)?;
139160
Ok(peer)
140161
}

forwarder/src/peer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::poll::Registry;
2-
use crate::socket::icmp::IcmpEchoType;
2+
use crate::socket::icmp::IcmpConfig;
33
use crate::socket::NonBlockingSocket;
44
use crate::uri::Uri;
55
use std::fmt::Debug;
@@ -22,10 +22,10 @@ impl Peer {
2222
pub fn new(
2323
remote_uri: &Uri,
2424
client_addr: SocketAddr,
25-
icmp_echo_type: IcmpEchoType,
25+
icmp_config: IcmpConfig,
2626
) -> anyhow::Result<Self> {
2727
let addr = create_any_addr(remote_uri.addr.is_ipv6());
28-
let mut socket = NonBlockingSocket::bind(remote_uri.protocol, &addr, icmp_echo_type)?;
28+
let mut socket = NonBlockingSocket::bind(remote_uri.protocol, &addr, icmp_config)?;
2929
socket.connect(&remote_uri.addr)?;
3030
let peer = Self {
3131
socket,

forwarder/src/poll.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
peer::{Peer, PeerManager},
33
poll::pushack::PushackPoll,
4-
socket::{icmp::IcmpEchoType, NonBlockingSocket},
4+
socket::{icmp::IcmpConfig, NonBlockingSocket},
55
uri::Protocol,
66
};
77
use icmp::IcmpPoll;
@@ -39,13 +39,13 @@ mod udp;
3939
pub fn new(
4040
protocol: Protocol,
4141
remote_addr: SocketAddr,
42-
icmp_echo_type: IcmpEchoType,
42+
icmp_config: IcmpConfig,
4343
) -> anyhow::Result<Box<dyn Poll>> {
4444
Ok(match protocol {
4545
Protocol::Udp => Box::new(UdpPoll::new()?),
4646
Protocol::Icmp => Box::new(IcmpPoll {
4747
remote_addr,
48-
echo_type: icmp_echo_type,
48+
config: icmp_config,
4949
}),
5050
Protocol::Pushack => Box::new(PushackPoll { remote_addr }),
5151
})

forwarder/src/poll/icmp.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::Poll;
22
use crate::{
33
peer::{Peer, PeerManager},
4-
socket::icmp::{header_offset, parse_icmp_packet, IcmpEchoType, IcmpSocket, ICMP_HEADER_LEN},
4+
socket::icmp::{header_offset, parse_icmp_packet, IcmpConfig, IcmpSocket, ICMP_HEADER_LEN},
55
utils::cast_maybe_uninit,
66
MAX_PACKET_SIZE,
77
};
@@ -11,7 +11,7 @@ use std::{net::SocketAddr, sync::Arc};
1111
#[derive(Debug)]
1212
pub struct IcmpPoll {
1313
pub remote_addr: SocketAddr,
14-
pub echo_type: IcmpEchoType,
14+
pub config: IcmpConfig,
1515
}
1616

1717
impl Poll for IcmpPoll {
@@ -26,13 +26,14 @@ impl Poll for IcmpPoll {
2626
) -> anyhow::Result<()> {
2727
let is_ipv6 = self.remote_addr.is_ipv6();
2828
let listen_addr = crate::peer::create_any_addr(is_ipv6);
29-
let socket: socket2::Socket = IcmpSocket::inner_bind(listen_addr)?;
29+
let socket: socket2::Socket =
30+
IcmpSocket::inner_bind(listen_addr, self.config.force_icmpv6)?;
3031

3132
#[cfg(target_os = "linux")]
3233
{
3334
let filter = crate::socket::icmp::create_bfp_filter(
3435
is_ipv6,
35-
self.echo_type,
36+
self.config.echo_type,
3637
self.remote_addr.port(),
3738
);
3839
if let Err(error) = socket.attach_filter(&filter) {
@@ -48,7 +49,7 @@ impl Poll for IcmpPoll {
4849
continue;
4950
};
5051
let Some(icmp_packet) =
51-
parse_icmp_packet(&buffer[header_offset..size], is_ipv6, self.echo_type)
52+
parse_icmp_packet(&buffer[header_offset..size], is_ipv6, self.config.echo_type)
5253
else {
5354
continue;
5455
};

forwarder/src/socket.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{socket::icmp::IcmpEchoType, uri::Protocol};
1+
use crate::{socket::icmp::IcmpConfig, uri::Protocol};
22
use std::{
33
io,
44
net::SocketAddr,
@@ -42,11 +42,11 @@ impl Socket {
4242
pub fn bind(
4343
protocol: Protocol,
4444
addr: &SocketAddr,
45-
icmp_type: IcmpEchoType,
45+
icmp_config: IcmpConfig,
4646
) -> io::Result<Self> {
4747
let socket = match protocol {
4848
Protocol::Udp => Socket::Udp(udp::UdpSocket::bind(addr)?),
49-
Protocol::Icmp => Socket::Icmp(icmp::IcmpSocket::bind(addr, icmp_type, true)?),
49+
Protocol::Icmp => Socket::Icmp(icmp::IcmpSocket::bind(addr, icmp_config, true)?),
5050
Protocol::Pushack => Socket::Pushack(pushack::PushackSocket::bind(addr)?),
5151
};
5252
Ok(socket)
@@ -72,11 +72,11 @@ impl NonBlockingSocket {
7272
pub fn bind(
7373
protocol: Protocol,
7474
addr: &SocketAddr,
75-
icmp_echo_type: IcmpEchoType,
75+
icmp_config: IcmpConfig,
7676
) -> io::Result<Self> {
7777
let socket = match protocol {
7878
Protocol::Udp => Self::Udp(udp::NonBlockingUdpSocket::bind(addr)?),
79-
Protocol::Icmp => Self::Icmp(icmp::NonBlockingIcmpSocket::bind(addr, icmp_echo_type)?),
79+
Protocol::Icmp => Self::Icmp(icmp::NonBlockingIcmpSocket::bind(addr, icmp_config)?),
8080
Protocol::Pushack => Self::Pushack(pushack::NonBlockingPushackSocket::bind(addr)?),
8181
};
8282
Ok(socket)

forwarder/src/socket/icmp.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ pub struct IcmpSocket {
2626
read_timeout: Option<Duration>,
2727
}
2828

29+
#[derive(Debug)]
30+
pub struct IcmpConfig {
31+
pub echo_type: IcmpEchoType,
32+
pub force_icmpv6: bool,
33+
}
34+
2935
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
3036
#[repr(u8)]
3137
pub enum IcmpEchoType {
@@ -43,34 +49,35 @@ impl IcmpEchoType {
4349
}
4450

4551
impl IcmpSocket {
46-
pub fn bind(
47-
addr: &SocketAddr,
48-
icmp_echo_type: IcmpEchoType,
49-
check_port: bool,
50-
) -> io::Result<Self> {
52+
pub fn bind(addr: &SocketAddr, config: IcmpConfig, check_port: bool) -> io::Result<Self> {
5153
let (udp_socket, udp_socket_addr) = if check_port {
5254
let udp_socket = UdpSocket::bind(addr)?;
5355
let addr = udp_socket.local_addr()?;
5456
(Some(udp_socket), addr)
5557
} else {
5658
(None, *addr)
5759
};
58-
let socket = IcmpSocket::inner_bind(*addr)?;
60+
let socket = IcmpSocket::inner_bind(*addr, config.force_icmpv6)?;
5961

6062
// TODO: maybe attach a bpf filter here
6163

6264
Ok(IcmpSocket {
6365
_udp_socket: udp_socket,
6466
addr: udp_socket_addr,
6567
socket,
66-
icmp_echo_type,
68+
icmp_echo_type: config.echo_type,
6769
read_timeout: None,
6870
})
6971
}
7072

71-
pub fn inner_bind(addr: SocketAddr) -> io::Result<socket2::Socket> {
73+
pub fn inner_bind(addr: SocketAddr, force_icmpv6: bool) -> io::Result<socket2::Socket> {
7274
let socket = if addr.is_ipv4() {
73-
socket2::Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::ICMPV4))
75+
let protocol = if force_icmpv6 {
76+
Protocol::ICMPV6
77+
} else {
78+
Protocol::ICMPV4
79+
};
80+
socket2::Socket::new(Domain::IPV4, Type::RAW, Some(protocol))
7481
} else {
7582
socket2::Socket::new(Domain::IPV6, Type::RAW, Some(Protocol::ICMPV6))
7683
}?;
@@ -153,17 +160,17 @@ pub struct NonBlockingIcmpSocket {
153160
}
154161

155162
impl NonBlockingIcmpSocket {
156-
pub fn bind(addr: &SocketAddr, echo_type: IcmpEchoType) -> io::Result<Self> {
163+
pub fn bind(addr: &SocketAddr, config: IcmpConfig) -> io::Result<Self> {
157164
let udp_socket = UdpSocket::bind(addr)?;
158165
let addr = udp_socket.local_addr()?;
159-
let socket = IcmpSocket::inner_bind(addr)?;
166+
let socket = IcmpSocket::inner_bind(addr, config.force_icmpv6)?;
160167
socket.set_nonblocking(true)?;
161168
Ok(Self {
162169
socket,
163170
connected_addr: None,
164171
_udp_socket: udp_socket,
165172
addr,
166-
echo_type,
173+
echo_type: config.echo_type,
167174
})
168175
}
169176
}

0 commit comments

Comments
 (0)