Skip to content

Commit 98d4d4d

Browse files
committed
implement pushack protocol
1 parent b812ef2 commit 98d4d4d

17 files changed

Lines changed: 491 additions & 40 deletions

File tree

Cargo.lock

Lines changed: 101 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

forwarder-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ clap = { version = "4.4.18", features = ["derive"] }
99
log = "0.4.20"
1010
simple_logger = "4.2.0"
1111
forwarder = { path = "../forwarder" }
12+
ctrlc = { version = "3.5.2", features = ["termination"] }
1213

1314
[build-dependencies]
1415
vergen = { version = "8.2.8", features = ["git", "gitcl"] }

forwarder-cli/src/health_check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::Args;
22
use forwarder::{
33
create_socket_buffer,
44
socket::{
5-
icmp::{create_bfp_filter, IcmpEchoType, IcmpSocket, ICMP_RESERVED_BYTES_LEN},
5+
icmp::{create_bfp_filter, IcmpEchoType, IcmpSocket},
66
SocketTrait,
77
},
88
};

forwarder-cli/src/main.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::{ensure, Context};
1+
use anyhow::{bail, ensure, Context};
22
use clap::Parser;
33
use forwarder::uri::{Protocol, Uri};
44
use log::{info, LevelFilter};
@@ -8,6 +8,7 @@ use std::{
88
};
99

1010
mod health_check;
11+
mod pushack_firewall;
1112

1213
/// Lightweight UDP forwarder and UDP over ICMP
1314
#[derive(Parser)]
@@ -37,7 +38,7 @@ pub struct Args {
3738
#[arg(long, hide = true)]
3839
pub child: bool,
3940

40-
/// Amount of seconds to wait when handshake failed
41+
/// Amount of seconds to wait when icmp handshake failed in reverse mode
4142
#[arg(long, default_value = "2")]
4243
pub handshake_delay: u32,
4344
}
@@ -46,6 +47,23 @@ fn main() -> anyhow::Result<()> {
4647
let cli = Args::parse();
4748
setup_logger().with_context(|| "couldn't setup logger")?;
4849
log_version();
50+
51+
let iptables_guard = pushack_firewall::drop_rst(&cli)
52+
.with_context(|| "couldn't add firewall rule to drop rst")?;
53+
// TODO: this will not get called on panics of forwarder and ...
54+
// maybe launch forwarder on new process and disable panic abort and remove panics in forwarder lib
55+
ctrlc::set_handler(move || {
56+
drop(iptables_guard.clone());
57+
std::process::exit(1);
58+
})
59+
.unwrap();
60+
61+
if cli.listen_uri.addr.is_ipv6() && cli.listen_uri.protocol == Protocol::Pushack
62+
|| cli.remote_uri.addr.is_ipv6() && cli.remote_uri.protocol == Protocol::Pushack
63+
{
64+
bail!("pushack protocol does not support ipv6 yet");
65+
}
66+
4967
if cli.child || !cli.reverse {
5068
forwarder::run(cli.listen_uri, cli.remote_uri, cli.passphrase, cli.reverse)?;
5169
} else {
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use super::Args;
2+
use anyhow::{bail, Context};
3+
use forwarder::uri::Protocol;
4+
use std::process::Command;
5+
6+
#[derive(Default, Clone)]
7+
pub struct IptablesGuard {
8+
filters: Vec<Filter>,
9+
}
10+
11+
impl Drop for IptablesGuard {
12+
fn drop(&mut self) {
13+
for filter in self.filters.drain(..) {
14+
run_iptables_rst_filter(Action::Remove, filter)
15+
.with_context(|| "couldn't remove iptables filter")
16+
.unwrap();
17+
}
18+
}
19+
}
20+
21+
pub fn drop_rst(cli: &Args) -> anyhow::Result<IptablesGuard> {
22+
let mut guard = IptablesGuard::default();
23+
if cli.remote_uri.protocol == Protocol::Pushack {
24+
let filter = Filter::DestPort(cli.remote_uri.addr.port());
25+
run_iptables_rst_filter(Action::Add, filter)
26+
.with_context(|| "couldn't add rst filter for remote")?;
27+
guard.filters.push(filter);
28+
}
29+
if cli.listen_uri.protocol == Protocol::Pushack {
30+
let filter = Filter::SourcePort(cli.listen_uri.addr.port());
31+
run_iptables_rst_filter(Action::Add, filter)
32+
.with_context(|| "couldn't add rst filter for listen")?;
33+
guard.filters.push(filter);
34+
}
35+
Ok(guard)
36+
}
37+
38+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
39+
enum Filter {
40+
SourcePort(u16),
41+
DestPort(u16),
42+
}
43+
44+
#[derive(PartialEq, Eq, Clone, Copy)]
45+
enum Action {
46+
Add,
47+
Remove,
48+
}
49+
50+
fn run_iptables_rst_filter(action: Action, filter: Filter) -> anyhow::Result<()> {
51+
let (filter, value) = match filter {
52+
Filter::DestPort(port) => ("--dport", port.to_string()),
53+
Filter::SourcePort(port) => ("--sport", port.to_string()),
54+
};
55+
run_command(
56+
"iptables",
57+
&[
58+
"-t",
59+
"mangle",
60+
if action == Action::Add { "-I" } else { "-D" },
61+
"POSTROUTING",
62+
"-p",
63+
"tcp",
64+
filter,
65+
&value,
66+
"--tcp-flags",
67+
"RST",
68+
"RST",
69+
"-j",
70+
"DROP",
71+
],
72+
)
73+
.with_context(|| "iptables command failed")?;
74+
Ok(())
75+
}
76+
77+
fn run_command(program: &str, args: &[&str]) -> anyhow::Result<String> {
78+
let output = Command::new(program)
79+
.args(args)
80+
.output()
81+
.with_context(|| format!("couldn't spawn '{program}' program"))?;
82+
let stdout = String::from_utf8(output.stdout)?;
83+
if !output.status.success() {
84+
let stderr = String::from_utf8(output.stderr)?;
85+
bail!("{stdout}\n{stderr}")
86+
}
87+
Ok(stdout)
88+
}

0 commit comments

Comments
 (0)