mirror of
				https://github.com/dndx/phantun.git
				synced 2025-11-04 11:55:34 +08:00 
			
		
		
		
	feat(*) IPv6 support
This commit is contained in:
		@@ -2,7 +2,7 @@ use clap::{crate_version, Arg, Command};
 | 
			
		||||
use fake_tcp::packet::MAX_PACKET_LEN;
 | 
			
		||||
use fake_tcp::{Socket, Stack};
 | 
			
		||||
use log::{debug, error, info};
 | 
			
		||||
use phantun::utils::new_udp_reuseport;
 | 
			
		||||
use phantun::utils::{assign_ipv6_address, new_udp_reuseport};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::net::{Ipv4Addr, SocketAddr};
 | 
			
		||||
@@ -36,7 +36,7 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
                .long("remote")
 | 
			
		||||
                .required(true)
 | 
			
		||||
                .value_name("IP or HOST NAME:PORT")
 | 
			
		||||
                .help("Sets the address or host name 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, IPv6 address need to be specified as: \"[IPv6]:PORT\"")
 | 
			
		||||
                .takes_value(true),
 | 
			
		||||
        )
 | 
			
		||||
        .arg(
 | 
			
		||||
@@ -53,7 +53,7 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
                .long("tun-local")
 | 
			
		||||
                .required(false)
 | 
			
		||||
                .value_name("IP")
 | 
			
		||||
                .help("Sets the Tun interface local address (O/S's end)")
 | 
			
		||||
                .help("Sets the Tun interface IPv4 local address (O/S's end)")
 | 
			
		||||
                .default_value("192.168.200.1")
 | 
			
		||||
                .takes_value(true),
 | 
			
		||||
        )
 | 
			
		||||
@@ -62,12 +62,41 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
                .long("tun-peer")
 | 
			
		||||
                .required(false)
 | 
			
		||||
                .value_name("IP")
 | 
			
		||||
                .help("Sets the Tun interface destination (peer) address (Phantun Client's end). \
 | 
			
		||||
                .help("Sets the Tun interface IPv4 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),
 | 
			
		||||
        )
 | 
			
		||||
        .arg(
 | 
			
		||||
            Arg::new("ipv4_only")
 | 
			
		||||
                .long("ipv4-only")
 | 
			
		||||
                .short('4')
 | 
			
		||||
                .required(false)
 | 
			
		||||
                .help("Do not assign IPv6 addresses to Tun interface")
 | 
			
		||||
                .takes_value(false)
 | 
			
		||||
                .conflicts_with_all(&["tun_local6", "tun_peer6"]),
 | 
			
		||||
        )
 | 
			
		||||
        .arg(
 | 
			
		||||
            Arg::new("tun_local6")
 | 
			
		||||
                .long("tun-local6")
 | 
			
		||||
                .required(false)
 | 
			
		||||
                .value_name("IP")
 | 
			
		||||
                .help("Sets the Tun interface IPv6 local address (O/S's end)")
 | 
			
		||||
                .default_value("fec8::1")
 | 
			
		||||
                .takes_value(true),
 | 
			
		||||
        )
 | 
			
		||||
        .arg(
 | 
			
		||||
            Arg::new("tun_peer6")
 | 
			
		||||
                .long("tun-peer6")
 | 
			
		||||
                .required(false)
 | 
			
		||||
                .value_name("IP")
 | 
			
		||||
                .help("Sets the Tun interface IPv6 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("fec8::2")
 | 
			
		||||
                .takes_value(true),
 | 
			
		||||
        )
 | 
			
		||||
        .get_matches();
 | 
			
		||||
 | 
			
		||||
    let local_addr: SocketAddr = matches
 | 
			
		||||
@@ -76,16 +105,13 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
        .parse()
 | 
			
		||||
        .expect("bad local address");
 | 
			
		||||
 | 
			
		||||
    let ipv4_only = matches.is_present("ipv4_only");
 | 
			
		||||
 | 
			
		||||
    let remote_addr = tokio::net::lookup_host(matches.value_of("remote").unwrap())
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("bad remote address or host")
 | 
			
		||||
        .find(|addr| addr.is_ipv4())
 | 
			
		||||
        .expect("unable to resolve remote host name or no valid A record was returned");
 | 
			
		||||
    let remote_addr = if let SocketAddr::V4(addr) = remote_addr {
 | 
			
		||||
        addr
 | 
			
		||||
    } else {
 | 
			
		||||
        unreachable!();
 | 
			
		||||
    };
 | 
			
		||||
        .find(|addr| !ipv4_only || addr.is_ipv4())
 | 
			
		||||
        .expect("unable to resolve remote host name");
 | 
			
		||||
    info!("Remote address is: {}", remote_addr);
 | 
			
		||||
 | 
			
		||||
    let tun_local: Ipv4Addr = matches
 | 
			
		||||
@@ -99,11 +125,26 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
        .parse()
 | 
			
		||||
        .expect("bad peer address for Tun interface");
 | 
			
		||||
 | 
			
		||||
    let (tun_local6, tun_peer6) = if ipv4_only {
 | 
			
		||||
        (None, None)
 | 
			
		||||
    } else {
 | 
			
		||||
        (
 | 
			
		||||
            matches
 | 
			
		||||
                .value_of("tun_local6")
 | 
			
		||||
                .map(|v| v.parse().expect("bad local address for Tun interface")),
 | 
			
		||||
            matches
 | 
			
		||||
                .value_of("tun_peer6")
 | 
			
		||||
                .map(|v| v.parse().expect("bad peer address for Tun interface")),
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let tun_name = matches.value_of("tun").unwrap();
 | 
			
		||||
 | 
			
		||||
    let num_cpus = num_cpus::get();
 | 
			
		||||
    info!("{} cores available", num_cpus);
 | 
			
		||||
 | 
			
		||||
    let tun = TunBuilder::new()
 | 
			
		||||
        .name(matches.value_of("tun").unwrap()) // if name is empty, then it is set by kernel.
 | 
			
		||||
        .name(tun_name) // 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`.
 | 
			
		||||
@@ -112,12 +153,16 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
        .try_build_mq(num_cpus)
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    if remote_addr.is_ipv6() {
 | 
			
		||||
        assign_ipv6_address(tun[0].name(), tun_local6.unwrap(), tun_peer6.unwrap());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    info!("Created TUN device {}", tun[0].name());
 | 
			
		||||
 | 
			
		||||
    let udp_sock = Arc::new(new_udp_reuseport(local_addr));
 | 
			
		||||
    let connections = Arc::new(RwLock::new(HashMap::<SocketAddr, Arc<Socket>>::new()));
 | 
			
		||||
 | 
			
		||||
    let mut stack = Stack::new(tun);
 | 
			
		||||
    let mut stack = Stack::new(tun, tun_peer, tun_peer6);
 | 
			
		||||
 | 
			
		||||
    let main_loop = tokio::spawn(async move {
 | 
			
		||||
        let mut buf_r = [0u8; MAX_PACKET_LEN];
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ use clap::{crate_version, Arg, Command};
 | 
			
		||||
use fake_tcp::packet::MAX_PACKET_LEN;
 | 
			
		||||
use fake_tcp::Stack;
 | 
			
		||||
use log::{debug, error, info};
 | 
			
		||||
use phantun::utils::new_udp_reuseport;
 | 
			
		||||
use phantun::utils::{assign_ipv6_address, new_udp_reuseport};
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::net::Ipv4Addr;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
@@ -68,6 +68,35 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
                .default_value("192.168.201.2")
 | 
			
		||||
                .takes_value(true),
 | 
			
		||||
        )
 | 
			
		||||
        .arg(
 | 
			
		||||
            Arg::new("ipv4_only")
 | 
			
		||||
                .long("ipv4-only")
 | 
			
		||||
                .short('4')
 | 
			
		||||
                .required(false)
 | 
			
		||||
                .help("Do not assign IPv6 addresses to Tun interface")
 | 
			
		||||
                .takes_value(false)
 | 
			
		||||
                .conflicts_with_all(&["tun_local6", "tun_peer6"]),
 | 
			
		||||
        )
 | 
			
		||||
        .arg(
 | 
			
		||||
            Arg::new("tun_local6")
 | 
			
		||||
                .long("tun-local6")
 | 
			
		||||
                .required(false)
 | 
			
		||||
                .value_name("IP")
 | 
			
		||||
                .help("Sets the Tun interface IPv6 local address (O/S's end)")
 | 
			
		||||
                .default_value("fec9::1")
 | 
			
		||||
                .takes_value(true),
 | 
			
		||||
        )
 | 
			
		||||
        .arg(
 | 
			
		||||
            Arg::new("tun_peer6")
 | 
			
		||||
                .long("tun-peer6")
 | 
			
		||||
                .required(false)
 | 
			
		||||
                .value_name("IP")
 | 
			
		||||
                .help("Sets the Tun interface IPv6 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("fec9::2")
 | 
			
		||||
                .takes_value(true),
 | 
			
		||||
        )
 | 
			
		||||
        .get_matches();
 | 
			
		||||
 | 
			
		||||
    let local_port: u16 = matches
 | 
			
		||||
@@ -94,11 +123,26 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
        .parse()
 | 
			
		||||
        .expect("bad peer address for Tun interface");
 | 
			
		||||
 | 
			
		||||
    let (tun_local6, tun_peer6) = if matches.is_present("ipv4_only") {
 | 
			
		||||
        (None, None)
 | 
			
		||||
    } else {
 | 
			
		||||
        (
 | 
			
		||||
            matches
 | 
			
		||||
                .value_of("tun_local6")
 | 
			
		||||
                .map(|v| v.parse().expect("bad local address for Tun interface")),
 | 
			
		||||
            matches
 | 
			
		||||
                .value_of("tun_peer6")
 | 
			
		||||
                .map(|v| v.parse().expect("bad peer address for Tun interface")),
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let tun_name = matches.value_of("tun").unwrap();
 | 
			
		||||
 | 
			
		||||
    let num_cpus = num_cpus::get();
 | 
			
		||||
    info!("{} cores available", num_cpus);
 | 
			
		||||
 | 
			
		||||
    let tun = TunBuilder::new()
 | 
			
		||||
        .name(matches.value_of("tun").unwrap()) // if name is empty, then it is set by kernel.
 | 
			
		||||
        .name(tun_name) // 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`.
 | 
			
		||||
@@ -107,10 +151,14 @@ async fn main() -> io::Result<()> {
 | 
			
		||||
        .try_build_mq(num_cpus)
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    if let (Some(tun_local6), Some(tun_peer6)) = (tun_local6, tun_peer6) {
 | 
			
		||||
        assign_ipv6_address(tun[0].name(), tun_local6, tun_peer6);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    info!("Created TUN device {}", tun[0].name());
 | 
			
		||||
 | 
			
		||||
    //thread::sleep(time::Duration::from_secs(5));
 | 
			
		||||
    let mut stack = Stack::new(tun);
 | 
			
		||||
    let mut stack = Stack::new(tun, tun_local, tun_local6);
 | 
			
		||||
    stack.listen(local_port);
 | 
			
		||||
    info!("Listening on {}", local_port);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,15 @@
 | 
			
		||||
use std::net::SocketAddr;
 | 
			
		||||
use neli::{
 | 
			
		||||
    consts::{
 | 
			
		||||
        nl::{NlmF, NlmFFlags},
 | 
			
		||||
        rtnl::{Ifa, IfaFFlags, RtAddrFamily, Rtm},
 | 
			
		||||
        socket::NlFamily,
 | 
			
		||||
    },
 | 
			
		||||
    nl::{NlPayload, Nlmsghdr},
 | 
			
		||||
    rtnl::{Ifaddrmsg, Rtattr},
 | 
			
		||||
    socket::NlSocketHandle,
 | 
			
		||||
    types::RtBuffer,
 | 
			
		||||
};
 | 
			
		||||
use std::net::{Ipv6Addr, SocketAddr};
 | 
			
		||||
use tokio::net::UdpSocket;
 | 
			
		||||
 | 
			
		||||
pub fn new_udp_reuseport(local_addr: SocketAddr) -> UdpSocket {
 | 
			
		||||
@@ -20,3 +31,30 @@ pub fn new_udp_reuseport(local_addr: SocketAddr) -> UdpSocket {
 | 
			
		||||
    let udp_sock: std::net::UdpSocket = udp_sock.into();
 | 
			
		||||
    udp_sock.try_into().unwrap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn assign_ipv6_address(device_name: &str, local: Ipv6Addr, peer: Ipv6Addr) {
 | 
			
		||||
    let index = nix::net::if_::if_nametoindex(device_name).unwrap();
 | 
			
		||||
 | 
			
		||||
    let mut rtnl = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
 | 
			
		||||
    let mut rtattrs = RtBuffer::new();
 | 
			
		||||
    rtattrs.push(Rtattr::new(None, Ifa::Local, &local.octets()[..]).unwrap());
 | 
			
		||||
    rtattrs.push(Rtattr::new(None, Ifa::Address, &peer.octets()[..]).unwrap());
 | 
			
		||||
 | 
			
		||||
    let ifaddrmsg = Ifaddrmsg {
 | 
			
		||||
        ifa_family: RtAddrFamily::Inet6,
 | 
			
		||||
        ifa_prefixlen: 128,
 | 
			
		||||
        ifa_flags: IfaFFlags::empty(),
 | 
			
		||||
        ifa_scope: 0,
 | 
			
		||||
        ifa_index: index as i32,
 | 
			
		||||
        rtattrs,
 | 
			
		||||
    };
 | 
			
		||||
    let nl_header = Nlmsghdr::new(
 | 
			
		||||
        None,
 | 
			
		||||
        Rtm::Newaddr,
 | 
			
		||||
        NlmFFlags::new(&[NlmF::Request]),
 | 
			
		||||
        None,
 | 
			
		||||
        None,
 | 
			
		||||
        NlPayload::Payload(ifaddrmsg),
 | 
			
		||||
    );
 | 
			
		||||
    rtnl.send(nl_header).unwrap();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user