14 Commits

Author SHA1 Message Date
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
Datong Sun
494abf37c5 docs(readme) add IPv6 support info 2021-10-30 09:21:23 -07:00
Datong Sun
cab87bd75b chore(cargo) bump Phantun to v0.2.2 2021-10-30 09:15:23 -07:00
Datong Sun
042f5af49f feat(phantun) add support for connecting to/from IPv6 based UDP endpoint 2021-10-31 00:09:02 +08:00
Datong Sun
f667f56747 chore(actions) always use the latest stable toolchain 2021-10-21 03:47:57 -07:00
Datong Sun
49665b906f chore(cargo) use Rust edition 2021 2021-10-21 03:41:42 -07:00
Datong Sun
e9cde27923 ci(dependabot) add Dependabot checks 2021-10-20 02:07:05 -07:00
8 changed files with 102 additions and 76 deletions

12
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "daily"

View File

@@ -12,6 +12,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Run lint
run: cargo clippy --verbose
- name: Build

View File

@@ -31,7 +31,7 @@ Table of Contents
# Latest release
[v0.2.1](https://github.com/dndx/phantun/releases/tag/v0.2.1)
[v0.2.2](https://github.com/dndx/phantun/releases/tag/v0.2.2)
# Overview
@@ -84,6 +84,10 @@ and change the destination IP address to where the server is listening for incom
In those cases, the machine/iptables running Phantun acts as the "router" that allows Phantun
to communicate with outside using it's private IP addresses.
As of Phantun v0.2.2, IPv6 support for UDP endpoints has been added, however Fake TCP IPv6 support
has not been finished yet. To specify an IPv6 address, use the following format: `[::1]:1234` with
the command line options.
[Back to TOC](#table-of-contents)
## 1. Enable Kernel IP forwarding
@@ -250,7 +254,7 @@ for tunneling TCP/UDP traffic between two test instances and MTU has been tuned
# Future plans
* IPv6 support
* IPv6 support for fake-tcp
* Load balancing a single UDP stream into multiple TCP streams
* Integration tests
* Auto insertion/removal of required firewall rules
@@ -278,7 +282,7 @@ Here is a quick overview of comparison between those two to help you choose:
| Tunneling MTU overhead | 12 bytes | 44 bytes |
| Seprate TCP connections for each UDP connection | Client/Server | Server only |
| Anti-replay, encryption | ❌ | ✅ |
| IPv6 | Planned | ✅ |
| IPv6 | UDP only | ✅ |
[Back to TOC](#table-of-contents)

View File

@@ -1,7 +1,7 @@
[package]
name = "fake-tcp"
version = "0.1.2"
edition = "2018"
version = "0.2.0"
edition = "2021"
authors = ["Datong Sun <dndx@idndx.com>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/dndx/phantun"
@@ -21,4 +21,4 @@ tokio = { version = "1.12.0", features = ["full"] }
rand = { version = "0.8.4", features = ["small_rng"] }
log = "0.4"
internet-checksum = "0.2.0"
dndx-fork-tokio-tun = "0.3.16"
dndx-fork-tokio-tun = "0.4.0"

View File

@@ -15,7 +15,6 @@ use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, RwLock};
use tokio::sync::broadcast;
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::sync::watch;
use tokio::sync::Mutex as AsyncMutex;
use tokio::time;
use tokio_tun::Tun;
@@ -58,6 +57,7 @@ pub enum State {
SynSent,
SynReceived,
Established,
Closed,
}
pub struct Socket {
@@ -69,8 +69,6 @@ pub struct Socket {
seq: AtomicU32,
ack: AtomicU32,
state: State,
closing_tx: watch::Sender<()>,
closing_rx: watch::Receiver<()>,
}
impl Socket {
@@ -83,7 +81,6 @@ impl Socket {
state: State,
) -> (Socket, Sender<Bytes>) {
let (incoming_tx, incoming_rx) = mpsc::channel(MPSC_BUFFER_LEN);
let (closing_tx, closing_rx) = watch::channel(());
(
Socket {
@@ -95,8 +92,6 @@ impl Socket {
seq: AtomicU32::new(0),
ack: AtomicU32::new(ack.unwrap_or(0)),
state,
closing_tx,
closing_rx,
},
incoming_tx,
)
@@ -114,8 +109,6 @@ impl Socket {
}
pub async fn send(&self, payload: &[u8]) -> Option<()> {
let mut closing = self.closing_rx.clone();
match self.state {
State::Established => {
let buf = self.build_tcp_packet(tcp::TcpFlags::ACK, Some(payload));
@@ -126,28 +119,22 @@ impl Socket {
res.unwrap();
Some(())
},
_ = closing.changed() => {
None
}
}
}
State::Closed => None,
_ => unreachable!(),
}
}
pub async fn recv(&self, buf: &mut [u8]) -> Option<usize> {
let mut closing = self.closing_rx.clone();
match self.state {
State::Established => {
let mut incoming = self.incoming.lock().await;
tokio::select! {
Some(raw_buf) = incoming.recv() => {
incoming.recv().await.and_then(|raw_buf| {
let (_v4_packet, tcp_packet) = parse_ipv4_packet(&raw_buf);
if (tcp_packet.get_flags() & tcp::TcpFlags::RST) != 0 {
info!("Connection {} reset by peer", self);
self.close();
return None;
}
@@ -159,20 +146,13 @@ impl Socket {
buf[..payload.len()].copy_from_slice(payload);
Some(payload.len())
},
_ = closing.changed() => {
None
}
}
})
}
State::Closed => None,
_ => unreachable!(),
}
}
pub fn close(&self) {
self.closing_tx.send(()).unwrap();
}
async fn accept(mut self) {
for _ in 0..RETRIES {
match self.state {
@@ -272,6 +252,8 @@ impl Socket {
impl Drop for Socket {
fn drop(&mut self) {
self.state = State::Closed;
let tuple = AddrTuple::new(self.local_addr, self.remote_addr);
// dissociates ourself from the dispatch map
assert!(self.shared.tuples.write().unwrap().remove(&tuple).is_some());
@@ -282,7 +264,7 @@ impl Drop for Socket {
if let Err(e) = self.tun.try_send(&buf) {
warn!("Unable to send RST to remote end: {}", e);
}
self.close();
info!("Fake TCP connection to {} closed", self);
}
}
@@ -396,11 +378,10 @@ impl Stack {
} else {
trace!("Cache miss, checking the shared tuples table for connection");
let sender;
{
let sender = {
let tuples = shared.tuples.read().unwrap();
sender = tuples.get(&tuple).cloned();
}
tuples.get(&tuple).cloned()
};
if let Some(c) = sender {
trace!("Storing connection information into local tuples");

View File

@@ -1,7 +1,7 @@
[package]
name = "phantun"
version = "0.2.1"
edition = "2018"
version = "0.2.3"
edition = "2021"
authors = ["Datong Sun <dndx@idndx.com>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/dndx/phantun"
@@ -13,9 +13,9 @@ Layer 3 & Layer 4 (NAPT) firewalls/NATs.
[dependencies]
clap = "2.33.3"
socket2 = { version = "0.4.2", features = ["all"] }
fake-tcp = "0.1.2"
fake-tcp = "0.2.0"
tokio = { version = "1.12.0", features = ["full"] }
log = "0.4"
pretty_env_logger = "0.4.0"
dndx-fork-tokio-tun = "0.3.16"
dndx-fork-tokio-tun = "0.4.0"
num_cpus = "1.13.0"

View File

@@ -6,7 +6,7 @@ use fake_tcp::{Socket, Stack};
use log::{debug, error, info};
use std::collections::HashMap;
use std::convert::TryInto;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::time::Duration;
use tokio::net::UdpSocket;
@@ -16,8 +16,17 @@ use tokio_tun::TunBuilder;
const UDP_TTL: Duration = Duration::from_secs(180);
fn new_udp_reuseport(addr: SocketAddrV4) -> UdpSocket {
let udp_sock = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None).unwrap();
fn new_udp_reuseport(addr: SocketAddr) -> UdpSocket {
let udp_sock = socket2::Socket::new(
if addr.is_ipv4() {
socket2::Domain::IPV4
} else {
socket2::Domain::IPV6
},
socket2::Type::DGRAM,
None,
)
.unwrap();
udp_sock.set_reuse_port(true).unwrap();
// from tokio-rs/mio/blob/master/src/sys/unix/net.rs
udp_sock.set_cloexec(true).unwrap();
@@ -40,7 +49,7 @@ async fn main() {
.long("local")
.required(true)
.value_name("IP:PORT")
.help("Sets the IP and port where Phantun Client listens for incoming UDP datagrams")
.help("Sets the IP and port where Phantun Client listens for incoming UDP datagrams, IPv6 address need to be specified as: \"[IPv6]:PORT\"")
.takes_value(true),
)
.arg(
@@ -48,8 +57,8 @@ async fn main() {
.short("r")
.long("remote")
.required(true)
.value_name("IP:PORT")
.help("Sets the address and port where Phantun Client connects to Phantun Server")
.value_name("IP or HOST NAME:PORT")
.help("Sets the address or host name and port where Phantun Client connects to Phantun Server")
.takes_value(true),
)
.arg(
@@ -83,16 +92,24 @@ async fn main() {
)
.get_matches();
let local_addr: SocketAddrV4 = matches
let local_addr: SocketAddr = matches
.value_of("local")
.unwrap()
.parse()
.expect("bad local address");
let remote_addr: SocketAddrV4 = matches
.value_of("remote")
.unwrap()
.parse()
.expect("bad remote address");
let remote_addr = tokio::net::lookup_host(matches.value_of("remote").unwrap())
.await
.expect("bad remote address or host")
.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
.value_of("tun_local")
.unwrap()
@@ -117,7 +134,7 @@ async fn main() {
info!("Created TUN device {}", tun[0].name());
let udp_sock = Arc::new(new_udp_reuseport(local_addr));
let connections = Arc::new(RwLock::new(HashMap::<SocketAddrV4, Arc<Socket>>::new()));
let connections = Arc::new(RwLock::new(HashMap::<SocketAddr, Arc<Socket>>::new()));
let mut stack = Stack::new(tun);
@@ -126,7 +143,7 @@ async fn main() {
loop {
tokio::select! {
Ok((size, SocketAddr::V4(addr))) = udp_sock.recv_from(&mut buf_r) => {
Ok((size, addr)) = udp_sock.recv_from(&mut buf_r) => {
// seen UDP packet to listening socket, this means:
// 1. It is a new UDP connection, or
// 2. It is some extra packets not filtered by more specific

View File

@@ -4,7 +4,7 @@ use clap::{crate_version, App, Arg};
use fake_tcp::packet::MAX_PACKET_LEN;
use fake_tcp::Stack;
use log::{error, info};
use std::net::{Ipv4Addr, SocketAddrV4};
use std::net::Ipv4Addr;
use tokio::net::UdpSocket;
use tokio::time::{self, Duration};
use tokio_tun::TunBuilder;
@@ -31,8 +31,8 @@ async fn main() {
.short("r")
.long("remote")
.required(true)
.value_name("IP:PORT")
.help("Sets the address and port where Phantun Server forwards UDP packets to")
.value_name("IP or HOST NAME: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),
)
.arg(
@@ -71,11 +71,14 @@ async fn main() {
.unwrap()
.parse()
.expect("bad local port");
let remote_addr: SocketAddrV4 = matches
.value_of("remote")
.unwrap()
.parse()
.expect("bad remote address");
let remote_addr = tokio::net::lookup_host(matches.value_of("remote").unwrap())
.await
.expect("bad remote address or host")
.next()
.expect("unable to resolve remote host name");
info!("Remote address is: {}", remote_addr);
let tun_local: Ipv4Addr = matches
.value_of("tun_local")
.unwrap()
@@ -113,7 +116,13 @@ async fn main() {
info!("New connection: {}", sock);
tokio::spawn(async move {
let udp_sock = UdpSocket::bind("0.0.0.0:0").await.unwrap();
let udp_sock = UdpSocket::bind(if remote_addr.is_ipv4() {
"0.0.0.0:0"
} else {
"[::]:0"
})
.await
.unwrap();
udp_sock.connect(remote_addr).await.unwrap();
loop {