6 Commits

Author SHA1 Message Date
Datong Sun
ef96a5161d chore(phantun) release v0.2.0 2021-09-28 23:35:37 -07:00
Datong Sun
2b7588adfe docs(phantun) add -h option information 2021-09-28 23:31:07 -07:00
Datong Sun
6285efd0d7 feat(phantun) make Tun interface name and address configurable. Improved
documentations of Clap CLI help

Closes #8
2021-09-29 14:27:00 +08:00
Datong Sun
5a6ebf52ea fix(phantun) better UDP error handling
When UDP send fails, print a proper error message and close the
connection, instead of `panic`ing.
2021-09-28 00:53:52 +08:00
Datong Sun
e3e50f8a9e docs(readme) add more network topology explanation 2021-09-24 08:44:42 -07:00
Datong Sun
e97a2d1cad docs(readme) update latest release version 2021-09-22 22:23:00 -07:00
4 changed files with 136 additions and 25 deletions

View File

@@ -31,7 +31,7 @@ Table of Contents
# Latest release
[v0.1.0](https://github.com/dndx/phantun/releases/tag/v0.1.0)
[v0.1.1](https://github.com/dndx/phantun/releases/tag/v0.1.1)
# Overview
@@ -55,11 +55,35 @@ to make it pass through stateful firewall/NATs as TCP packets.
# Usage
For the example below, it is assumed that **Phantun Server** listens for incoming Phantun Client connections at
port `4567` (the `--local` option for server), and it forwards UDP packets to UDP server at `127.0.0.1:1234`
(the `--remote` option for server).
It is also assumed that **Phantun Client** listens for incoming UDP packets at
`127.0.0.1:1234` (the `--local` option for client) and connects to Phantun Server at `10.0.0.1:4567`
(the `--remote` option for client).
Phantun creates TUN interface for both the Client and Server. For Client, Phantun assigns itself the IP address
`192.168.200.2` and for Server, it assigns `192.168.201.2`. Therefore, your Kernel must have
`192.168.200.2` by default and for Server, it assigns `192.168.201.2` by default. Therefore, your Kernel must have
`net.ipv4.ip_forward` enabled and setup appropriate iptables rules for NAT between your physical
NIC address and Phantun's TUN interface address.
You may customize the name of Tun interface created by Phantun and the assigned addresses. Please
run the executable with `-h` options to see how to change them.
Another way to help understand this network topology:
Phantun Client is like a machine with private IP address (`192.168.200.2`) behind a router.
In order for it to reach the Internet, you will need to SNAT the private IP address before it's traffic
leaves the NIC.
Phantun Server is like a server with private IP address (`192.168.201.2`) behind a router.
In order to access it from the Internet, you need to `DNAT` it's listening port on the router
and change the destination IP address to where the server is listening for incoming connections.
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.
[Back to TOC](#table-of-contents)
## 1. Enable Kernel IP forwarding
@@ -96,7 +120,7 @@ table inet nat {
#### Using iptables
```
iptables -t nat -A POSTROUTING -i tun0 -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
```
[Back to TOC](#table-of-contents)
@@ -146,6 +170,8 @@ sudo setcap cap_net_admin=+pe phantun_client
## 4. Start Phantun daemon
**Note:** Run Phantun executable with `-h` option to see full detailed options.
### Server
Note: `4567` is the TCP port Phantun should listen on and must corresponds to the DNAT

View File

@@ -1,6 +1,6 @@
[package]
name = "phantun"
version = "0.1.1"
version = "0.2.0"
edition = "2018"
authors = ["Datong Sun <dndx@idndx.com>"]
license = "MIT OR Apache-2.0"

View File

@@ -1,12 +1,12 @@
extern crate dndx_fork_tokio_tun as tokio_tun;
use clap::{App, Arg};
use clap::{crate_version, App, Arg};
use fake_tcp::packet::MAX_PACKET_LEN;
use fake_tcp::{Socket, Stack};
use log::{debug, error, info};
use std::collections::HashMap;
use std::convert::TryInto;
use std::net::{SocketAddr, SocketAddrV4};
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::sync::Arc;
use std::time::Duration;
use tokio::net::UdpSocket;
@@ -32,15 +32,15 @@ async fn main() {
pretty_env_logger::init();
let matches = App::new("Phantun Client")
.version("1.0")
.author("dndx@GitHub")
.version(crate_version!())
.author("Datong Sun (github.com/dndx)")
.arg(
Arg::with_name("local")
.short("l")
.long("local")
.required(true)
.value_name("IP:PORT")
.help("Sets the listening socket address")
.help("Sets the IP and port where Phantun Client listens for incoming UDP datagrams")
.takes_value(true),
)
.arg(
@@ -49,7 +49,36 @@ async fn main() {
.long("remote")
.required(true)
.value_name("IP:PORT")
.help("Sets the connecting socket address")
.help("Sets the address and port where Phantun Client connects to Phantun Server")
.takes_value(true),
)
.arg(
Arg::with_name("tun")
.long("tun")
.required(false)
.value_name("tunX")
.help("Sets the Tun interface name, if absent, pick the next available name")
.default_value("")
.takes_value(true),
)
.arg(
Arg::with_name("tun_local")
.long("tun-local")
.required(false)
.value_name("IP")
.help("Sets the Tun interface local address (O/S's end)")
.default_value("192.168.200.1")
.takes_value(true),
)
.arg(
Arg::with_name("tun_peer")
.long("tun-peer")
.required(false)
.value_name("IP")
.help("Sets the Tun interface destination (peer) address (Phantun Client's end). \
You will need to setup SNAT/MASQUERADE rules on your Internet facing interface \
in order for Phantun Client to connect to Phantun Server")
.default_value("192.168.200.2")
.takes_value(true),
)
.get_matches();
@@ -64,14 +93,24 @@ async fn main() {
.unwrap()
.parse()
.expect("bad remote address");
let tun_local: Ipv4Addr = matches
.value_of("tun_local")
.unwrap()
.parse()
.expect("bad local address for Tun interface");
let tun_peer: Ipv4Addr = matches
.value_of("tun_peer")
.unwrap()
.parse()
.expect("bad peer address for Tun interface");
let tun = TunBuilder::new()
.name("") // if name is empty, then it is set by kernel.
.name(matches.value_of("tun").unwrap()) // if name is empty, then it is set by kernel.
.tap(false) // false (default): TUN, true: TAP.
.packet_info(false) // false: IFF_NO_PI, default is true.
.up() // or set it up manually using `sudo ip link set <tun-name> up`.
.address("192.168.200.1".parse().unwrap())
.destination("192.168.200.2".parse().unwrap())
.address(tun_local)
.destination(tun_peer)
.try_build_mq(num_cpus::get())
.unwrap();
@@ -139,7 +178,11 @@ async fn main() {
match res {
Some(size) => {
if size > 0 {
udp_sock.send(&buf_tcp[..size]).await.unwrap();
if let Err(e) = udp_sock.send(&buf_tcp[..size]).await {
connections.write().await.remove(&addr);
error!("Unable to send UDP packet to {}: {}, closing connection", e, addr);
return;
}
}
},
None => {

View File

@@ -1,10 +1,10 @@
extern crate dndx_fork_tokio_tun as tokio_tun;
use clap::{App, Arg};
use clap::{crate_version, App, Arg};
use fake_tcp::packet::MAX_PACKET_LEN;
use fake_tcp::Stack;
use log::info;
use std::net::SocketAddrV4;
use log::{error, info};
use std::net::{Ipv4Addr, SocketAddrV4};
use tokio::net::UdpSocket;
use tokio::time::{self, Duration};
use tokio_tun::TunBuilder;
@@ -15,15 +15,15 @@ async fn main() {
pretty_env_logger::init();
let matches = App::new("Phantun Server")
.version("1.0")
.author("dndx@GitHub")
.version(crate_version!())
.author("Datong Sun (github.com/dndx)")
.arg(
Arg::with_name("local")
.short("l")
.long("local")
.required(true)
.value_name("PORT")
.help("Sets the listening port")
.help("Sets the port where Phantun Server listens for incoming Phantun Client TCP connections")
.takes_value(true),
)
.arg(
@@ -32,7 +32,36 @@ async fn main() {
.long("remote")
.required(true)
.value_name("IP:PORT")
.help("Sets the connecting socket address")
.help("Sets the address and port where Phantun Server forwards UDP packets to")
.takes_value(true),
)
.arg(
Arg::with_name("tun")
.long("tun")
.required(false)
.value_name("tunX")
.help("Sets the Tun interface name, if absent, pick the next available name")
.default_value("")
.takes_value(true),
)
.arg(
Arg::with_name("tun_local")
.long("tun-local")
.required(false)
.value_name("IP")
.help("Sets the Tun interface local address (O/S's end)")
.default_value("192.168.201.1")
.takes_value(true),
)
.arg(
Arg::with_name("tun_peer")
.long("tun-peer")
.required(false)
.value_name("IP")
.help("Sets the Tun interface destination (peer) address (Phantun Server's end). \
You will need to setup DNAT rules to this address in order for Phantun Server \
to accept TCP traffic from Phantun Client")
.default_value("192.168.201.2")
.takes_value(true),
)
.get_matches();
@@ -47,14 +76,24 @@ async fn main() {
.unwrap()
.parse()
.expect("bad remote address");
let tun_local: Ipv4Addr = matches
.value_of("tun_local")
.unwrap()
.parse()
.expect("bad local address for Tun interface");
let tun_peer: Ipv4Addr = matches
.value_of("tun_peer")
.unwrap()
.parse()
.expect("bad peer address for Tun interface");
let tun = TunBuilder::new()
.name("") // if name is empty, then it is set by kernel.
.name(matches.value_of("tun").unwrap()) // if name is empty, then it is set by kernel.
.tap(false) // false (default): TUN, true: TAP.
.packet_info(false) // false: IFF_NO_PI, default is true.
.up() // or set it up manually using `sudo ip link set <tun-name> up`.
.address("192.168.201.1".parse().unwrap())
.destination("192.168.201.2".parse().unwrap())
.address(tun_local)
.destination(tun_peer)
.try_build_mq(num_cpus::get())
.unwrap();
@@ -90,7 +129,10 @@ async fn main() {
match res {
Some(size) => {
if size > 0 {
udp_sock.send(&buf_tcp[..size]).await.unwrap();
if let Err(e) = udp_sock.send(&buf_tcp[..size]).await {
error!("Unable to send UDP packet to {}: {}, closing connection", e, remote_addr);
return;
}
}
},
None => { return; },