17 Commits

Author SHA1 Message Date
Datong Sun
b3c781cdc5 chore(phantun) bump phantun to v0.2.4 2021-12-05 07:10:42 -08:00
Datong Sun
d5e30c113f chore(phantun) bump clap and tokio dependencies to latest 2021-12-05 07:01:58 -08:00
Datong Sun
e2a9194f6f chore(fake-tcp) bump to v0.2.2 2021-12-05 06:57:23 -08:00
Datong Sun
d0eaefe5d0 chore(phantun) specify exact version for fake-tcp dependency 2021-12-05 06:49:35 -08:00
Datong Sun
299646a54f chore(fake-tcp) bump to v0.2.1 2021-12-05 06:43:18 -08:00
Datong Sun
8b28cdc6c2 chore(crates) do not include bugfix version in dependency as it prevents
auto update from Dependabot
2021-12-05 22:41:05 +08:00
Datong Sun
a8ad203754 docs(readme) fixed some typos 2021-12-01 15:10:52 +08:00
Datong Sun
33e510e7ba fix(fake-tcp) remove unneeded State::Closed as it is not in a
reachable code path
2021-11-19 10:45:13 -08:00
Datong Sun
521a3f1a01 docs(readme) bump release version and add host name support info for
`--remote`
2021-11-18 20:48:53 -08:00
Datong Sun
c5a5116808 chore(phantun) bump to v0.2.3, bump fake-tcp dependency to v0.2.0 2021-11-18 20:37:28 -08:00
Datong Sun
e8f2457cb5 chore(fake-tcp) bump to v0.2.0 2021-11-18 20:36:05 -08:00
Datong Sun
583cdbe300 perf(fake-tcp) reduce the number of clone() calls in hot path 2021-11-19 12:35:21 +08:00
Datong Sun
91988520e5 feat(*) add DNS name support for --remote argument in both Client and
Server
2021-11-19 12:30:47 +08:00
Datong Sun
49cc6a6865 chore(phantun) update fake-tcp dependency version to v0.1.3 2021-11-02 18:29:53 +08:00
Datong Sun
7390d4bf27 chore(fake-tcp) release version v0.1.3 2021-11-02 18:29:53 +08:00
dependabot[bot]
95e762f5fd chore(deps): update dndx-fork-tokio-tun requirement from 0.3.16 to 0.4.0
Updates the requirements on [dndx-fork-tokio-tun](https://github.com/yaa110/tokio-tun) to permit the latest version.
- [Release notes](https://github.com/yaa110/tokio-tun/releases)
- [Commits](https://github.com/yaa110/tokio-tun/compare/0.3.16...0.4.0)

---
updated-dependencies:
- dependency-name: dndx-fork-tokio-tun
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-02 18:29:53 +08:00
Datong Sun
c9043015f2 docs(readme) update latest release to v0.2.2 2021-10-30 09:32:14 -07:00
6 changed files with 78 additions and 80 deletions

View File

@@ -31,16 +31,16 @@ Table of Contents
# Latest release # Latest release
[v0.2.1](https://github.com/dndx/phantun/releases/tag/v0.2.1) [v0.2.3](https://github.com/dndx/phantun/releases/tag/v0.2.3)
# Overview # Overview
Phanton is a project that obfuscated UDP packets into TCP connections. It aims to Phantun is a project that obfuscated UDP packets into TCP connections. It aims to
achieve maximum performance with minimum processing and encapsulation overhead. achieve maximum performance with minimum processing and encapsulation overhead.
It is commonly used in environments where UDP is blocked/throttled but TCP is allowed through. It is commonly used in environments where UDP is blocked/throttled but TCP is allowed through.
Phanton simply converts a stream of UDP packets into obfuscated TCP stream packets. The TCP stack Phantun simply converts a stream of UDP packets into obfuscated TCP stream packets. The TCP stack
used by Phantun is designed to pass through most L3/L4 stateful/stateless firewalls/NAT used by Phantun is designed to pass through most L3/L4 stateful/stateless firewalls/NAT
devices. It will **not** be able to pass through L7 proxies. devices. It will **not** be able to pass through L7 proxies.
However, the advantage of this approach is that none of the common UDP over TCP performance killer However, the advantage of this approach is that none of the common UDP over TCP performance killer
@@ -134,7 +134,7 @@ iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Server needs to DNAT the TCP listening port to Phantun's TUN interface address. Server needs to DNAT the TCP listening port to Phantun's TUN interface address.
Note: change `eth0` to whatever actual physical interface name is and `4567` to Note: change `eth0` to whatever actual physical interface name is and `4567` to
actual TCP port number used by Phanton server actual TCP port number used by Phantun server
[Back to TOC](#table-of-contents) [Back to TOC](#table-of-contents)
@@ -185,6 +185,12 @@ rule specified above. `127.0.0.1:1234` is the UDP Server to connect to for new c
RUST_LOG=info /usr/local/bin/phantun_server --local 4567 --remote 127.0.0.1:1234 RUST_LOG=info /usr/local/bin/phantun_server --local 4567 --remote 127.0.0.1:1234
``` ```
Or use host name with `--remote`:
```
RUST_LOG=info /usr/local/bin/phantun_server --local 4567 --remote example.com:1234
```
[Back to TOC](#table-of-contents) [Back to TOC](#table-of-contents)
### Client ### Client
@@ -196,6 +202,12 @@ the Phantun Server to connect.
RUST_LOG=info /usr/local/bin/phantun_client --local 127.0.0.1:1234 --remote 10.0.0.1:4567 RUST_LOG=info /usr/local/bin/phantun_client --local 127.0.0.1:1234 --remote 10.0.0.1:4567
``` ```
Or use host name with `--remote`:
```
RUST_LOG=info /usr/local/bin/phantun_client --local 127.0.0.1:1234 --remote example.com:4567
```
[Back to TOC](#table-of-contents) [Back to TOC](#table-of-contents)
# MTU overhead # MTU overhead
@@ -264,7 +276,7 @@ for tunneling TCP/UDP traffic between two test instances and MTU has been tuned
# Compariation to udp2raw # Compariation to udp2raw
[udp2raw](https://github.com/wangyu-/udp2raw-tunnel) is another popular project by [@wangyu-](https://github.com/wangyu-) [udp2raw](https://github.com/wangyu-/udp2raw-tunnel) is another popular project by [@wangyu-](https://github.com/wangyu-)
that is very similar to what Phantun can do. In fact I took inspirations of Phantun from udp2raw. The biggest reason for that is very similar to what Phantun can do. In fact I took inspirations of Phantun from udp2raw. The biggest reason for
developing Phanton is because of lack of performance when running udp2raw (especially on multi-core systems such as Raspberry Pi). developing Phantun is because of lack of performance when running udp2raw (especially on multi-core systems such as Raspberry Pi).
However, the goal is never to be as feature complete as udp2raw and only support the most common use cases. Most notably, UDP over ICMP However, the goal is never to be as feature complete as udp2raw and only support the most common use cases. Most notably, UDP over ICMP
and UDP over UDP mode are not supported and there is no anti-replay nor encryption support. The benefit of this is much better and UDP over UDP mode are not supported and there is no anti-replay nor encryption support. The benefit of this is much better
performance overall and less MTU overhead because lack of additional headers inside the TCP payload. performance overall and less MTU overhead because lack of additional headers inside the TCP payload.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "fake-tcp" name = "fake-tcp"
version = "0.1.2" version = "0.2.2"
edition = "2021" edition = "2021"
authors = ["Datong Sun <dndx@idndx.com>"] authors = ["Datong Sun <dndx@idndx.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@@ -16,9 +16,9 @@ benchmark = []
[dependencies] [dependencies]
bytes = "1" bytes = "1"
pnet = "0.28.0" pnet = "0.28"
tokio = { version = "1.12.0", features = ["full"] } tokio = { version = "1.14", features = ["full"] }
rand = { version = "0.8.4", features = ["small_rng"] } rand = { version = "0.8", features = ["small_rng"] }
log = "0.4" log = "0.4"
internet-checksum = "0.2.0" internet-checksum = "0.2"
dndx-fork-tokio-tun = "0.3.16" dndx-fork-tokio-tun = "0.4"

View File

@@ -15,7 +15,6 @@ use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tokio::sync::mpsc::{self, Receiver, Sender}; use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::sync::watch;
use tokio::sync::Mutex as AsyncMutex; use tokio::sync::Mutex as AsyncMutex;
use tokio::time; use tokio::time;
use tokio_tun::Tun; use tokio_tun::Tun;
@@ -69,8 +68,6 @@ pub struct Socket {
seq: AtomicU32, seq: AtomicU32,
ack: AtomicU32, ack: AtomicU32,
state: State, state: State,
closing_tx: watch::Sender<()>,
closing_rx: watch::Receiver<()>,
} }
impl Socket { impl Socket {
@@ -83,7 +80,6 @@ impl Socket {
state: State, state: State,
) -> (Socket, Sender<Bytes>) { ) -> (Socket, Sender<Bytes>) {
let (incoming_tx, incoming_rx) = mpsc::channel(MPSC_BUFFER_LEN); let (incoming_tx, incoming_rx) = mpsc::channel(MPSC_BUFFER_LEN);
let (closing_tx, closing_rx) = watch::channel(());
( (
Socket { Socket {
@@ -95,8 +91,6 @@ impl Socket {
seq: AtomicU32::new(0), seq: AtomicU32::new(0),
ack: AtomicU32::new(ack.unwrap_or(0)), ack: AtomicU32::new(ack.unwrap_or(0)),
state, state,
closing_tx,
closing_rx,
}, },
incoming_tx, incoming_tx,
) )
@@ -114,8 +108,6 @@ impl Socket {
} }
pub async fn send(&self, payload: &[u8]) -> Option<()> { pub async fn send(&self, payload: &[u8]) -> Option<()> {
let mut closing = self.closing_rx.clone();
match self.state { match self.state {
State::Established => { State::Established => {
let buf = self.build_tcp_packet(tcp::TcpFlags::ACK, Some(payload)); let buf = self.build_tcp_packet(tcp::TcpFlags::ACK, Some(payload));
@@ -123,12 +115,8 @@ impl Socket {
tokio::select! { tokio::select! {
res = self.tun.send(&buf) => { res = self.tun.send(&buf) => {
res.unwrap(); res.ok().and(Some(()))
Some(())
}, },
_ = closing.changed() => {
None
}
} }
} }
_ => unreachable!(), _ => unreachable!(),
@@ -136,43 +124,31 @@ impl Socket {
} }
pub async fn recv(&self, buf: &mut [u8]) -> Option<usize> { pub async fn recv(&self, buf: &mut [u8]) -> Option<usize> {
let mut closing = self.closing_rx.clone();
match self.state { match self.state {
State::Established => { State::Established => {
let mut incoming = self.incoming.lock().await; let mut incoming = self.incoming.lock().await;
tokio::select! { incoming.recv().await.and_then(|raw_buf| {
Some(raw_buf) = incoming.recv() => { let (_v4_packet, tcp_packet) = parse_ipv4_packet(&raw_buf);
let (_v4_packet, tcp_packet) = parse_ipv4_packet(&raw_buf);
if (tcp_packet.get_flags() & tcp::TcpFlags::RST) != 0 { if (tcp_packet.get_flags() & tcp::TcpFlags::RST) != 0 {
info!("Connection {} reset by peer", self); info!("Connection {} reset by peer", self);
self.close(); return None;
return None;
}
let payload = tcp_packet.payload();
self.ack
.store(tcp_packet.get_sequence().wrapping_add(1), Ordering::Relaxed);
buf[..payload.len()].copy_from_slice(payload);
Some(payload.len())
},
_ = closing.changed() => {
None
} }
}
let payload = tcp_packet.payload();
self.ack
.store(tcp_packet.get_sequence().wrapping_add(1), Ordering::Relaxed);
buf[..payload.len()].copy_from_slice(payload);
Some(payload.len())
})
} }
_ => unreachable!(), _ => unreachable!(),
} }
} }
pub fn close(&self) {
self.closing_tx.send(()).unwrap();
}
async fn accept(mut self) { async fn accept(mut self) {
for _ in 0..RETRIES { for _ in 0..RETRIES {
match self.state { match self.state {
@@ -282,7 +258,7 @@ impl Drop for Socket {
if let Err(e) = self.tun.try_send(&buf) { if let Err(e) = self.tun.try_send(&buf) {
warn!("Unable to send RST to remote end: {}", e); warn!("Unable to send RST to remote end: {}", e);
} }
self.close();
info!("Fake TCP connection to {} closed", self); info!("Fake TCP connection to {} closed", self);
} }
} }
@@ -396,11 +372,10 @@ impl Stack {
} else { } else {
trace!("Cache miss, checking the shared tuples table for connection"); trace!("Cache miss, checking the shared tuples table for connection");
let sender; let sender = {
{
let tuples = shared.tuples.read().unwrap(); let tuples = shared.tuples.read().unwrap();
sender = tuples.get(&tuple).cloned(); tuples.get(&tuple).cloned()
} };
if let Some(c) = sender { if let Some(c) = sender {
trace!("Storing connection information into local tuples"); trace!("Storing connection information into local tuples");

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "phantun" name = "phantun"
version = "0.2.2" version = "0.2.4"
edition = "2021" edition = "2021"
authors = ["Datong Sun <dndx@idndx.com>"] authors = ["Datong Sun <dndx@idndx.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@@ -11,11 +11,11 @@ Transforms UDP stream into (fake) TCP streams that can go through
Layer 3 & Layer 4 (NAPT) firewalls/NATs. Layer 3 & Layer 4 (NAPT) firewalls/NATs.
""" """
[dependencies] [dependencies]
clap = "2.33.3" clap = "2.34"
socket2 = { version = "0.4.2", features = ["all"] } socket2 = { version = "0.4", features = ["all"] }
fake-tcp = "0.1.2" fake-tcp = "0.2.2"
tokio = { version = "1.12.0", features = ["full"] } tokio = { version = "1.14", features = ["full"] }
log = "0.4" log = "0.4"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4"
dndx-fork-tokio-tun = "0.3.16" dndx-fork-tokio-tun = "0.4"
num_cpus = "1.13.0" num_cpus = "1.13"

View File

@@ -6,7 +6,7 @@ use fake_tcp::{Socket, Stack};
use log::{debug, error, info}; use log::{debug, error, info};
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
@@ -57,8 +57,8 @@ async fn main() {
.short("r") .short("r")
.long("remote") .long("remote")
.required(true) .required(true)
.value_name("IP:PORT") .value_name("IP or HOST NAME:PORT")
.help("Sets the address and port where Phantun Client connects to Phantun Server") .help("Sets the address or host name and port where Phantun Client connects to Phantun Server")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@@ -97,11 +97,19 @@ async fn main() {
.unwrap() .unwrap()
.parse() .parse()
.expect("bad local address"); .expect("bad local address");
let remote_addr: SocketAddrV4 = matches
.value_of("remote") let remote_addr = tokio::net::lookup_host(matches.value_of("remote").unwrap())
.unwrap() .await
.parse() .expect("bad remote address or host")
.expect("bad remote address"); .next()
.expect("unable to resolve remote host name");
let remote_addr = if let SocketAddr::V4(addr) = remote_addr {
addr
} else {
panic!("only IPv4 remote address is supported");
};
info!("Remote address is: {}", remote_addr);
let tun_local: Ipv4Addr = matches let tun_local: Ipv4Addr = matches
.value_of("tun_local") .value_of("tun_local")
.unwrap() .unwrap()

View File

@@ -4,7 +4,7 @@ use clap::{crate_version, App, Arg};
use fake_tcp::packet::MAX_PACKET_LEN; use fake_tcp::packet::MAX_PACKET_LEN;
use fake_tcp::Stack; use fake_tcp::Stack;
use log::{error, info}; use log::{error, info};
use std::net::{Ipv4Addr, SocketAddr}; use std::net::Ipv4Addr;
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
use tokio::time::{self, Duration}; use tokio::time::{self, Duration};
use tokio_tun::TunBuilder; use tokio_tun::TunBuilder;
@@ -31,8 +31,8 @@ async fn main() {
.short("r") .short("r")
.long("remote") .long("remote")
.required(true) .required(true)
.value_name("IP:PORT") .value_name("IP or HOST NAME:PORT")
.help("Sets the address and port where Phantun Server forwards UDP packets to, IPv6 address need to be specified as: \"[IPv6]:PORT\"") .help("Sets the address or host name and port where Phantun Server forwards UDP packets to, IPv6 address need to be specified as: \"[IPv6]:PORT\"")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@@ -71,11 +71,14 @@ async fn main() {
.unwrap() .unwrap()
.parse() .parse()
.expect("bad local port"); .expect("bad local port");
let remote_addr: SocketAddr = matches
.value_of("remote") let remote_addr = tokio::net::lookup_host(matches.value_of("remote").unwrap())
.unwrap() .await
.parse() .expect("bad remote address or host")
.expect("bad remote address"); .next()
.expect("unable to resolve remote host name");
info!("Remote address is: {}", remote_addr);
let tun_local: Ipv4Addr = matches let tun_local: Ipv4Addr = matches
.value_of("tun_local") .value_of("tun_local")
.unwrap() .unwrap()