diff --git a/fake-tcp/src/lib.rs b/fake-tcp/src/lib.rs index 27e4805..db5534b 100644 --- a/fake-tcp/src/lib.rs +++ b/fake-tcp/src/lib.rs @@ -49,7 +49,7 @@ use pnet::packet::{tcp, Packet}; use rand::prelude::*; use std::collections::{HashMap, HashSet}; use std::fmt; -use std::net::{Ipv4Addr, SocketAddrV4}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, RwLock}; use tokio::sync::broadcast; @@ -65,12 +65,12 @@ const MAX_UNACKED_LEN: u32 = 128 * 1024 * 1024; // 128MB #[derive(Hash, Eq, PartialEq, Clone, Debug)] struct AddrTuple { - local_addr: SocketAddrV4, - remote_addr: SocketAddrV4, + local_addr: SocketAddr, + remote_addr: SocketAddr, } impl AddrTuple { - fn new(local_addr: SocketAddrV4, remote_addr: SocketAddrV4) -> AddrTuple { + fn new(local_addr: SocketAddr, remote_addr: SocketAddr) -> AddrTuple { AddrTuple { local_addr, remote_addr, @@ -89,6 +89,7 @@ struct Shared { pub struct Stack { shared: Arc, local_ip: Ipv4Addr, + local_ip6: Option, ready: mpsc::Receiver, } @@ -103,8 +104,8 @@ pub struct Socket { shared: Arc, tun: Arc, incoming: flume::Receiver, - local_addr: SocketAddrV4, - remote_addr: SocketAddrV4, + local_addr: SocketAddr, + remote_addr: SocketAddr, seq: AtomicU32, ack: AtomicU32, last_ack: AtomicU32, @@ -122,8 +123,8 @@ impl Socket { fn new( shared: Arc, tun: Arc, - local_addr: SocketAddrV4, - remote_addr: SocketAddrV4, + local_addr: SocketAddr, + remote_addr: SocketAddr, ack: Option, state: State, ) -> (Socket, flume::Sender) { @@ -188,7 +189,7 @@ impl Socket { match self.state { State::Established => { self.incoming.recv_async().await.ok().and_then(|raw_buf| { - let (_v4_packet, tcp_packet) = parse_ipv4_packet(&raw_buf); + let (_v4_packet, tcp_packet) = parse_ip_packet(&raw_buf).unwrap(); if (tcp_packet.get_flags() & tcp::TcpFlags::RST) != 0 { info!("Connection {} reset by peer", self); @@ -233,7 +234,7 @@ impl Socket { let res = time::timeout(TIMEOUT, self.incoming.recv_async()).await; if let Ok(buf) = res { let buf = buf.unwrap(); - let (_v4_packet, tcp_packet) = parse_ipv4_packet(&buf); + let (_v4_packet, tcp_packet) = parse_ip_packet(&buf).unwrap(); if (tcp_packet.get_flags() & tcp::TcpFlags::RST) != 0 { return; @@ -277,7 +278,7 @@ impl Socket { match time::timeout(TIMEOUT, self.incoming.recv_async()).await { Ok(buf) => { let buf = buf.unwrap(); - let (_v4_packet, tcp_packet) = parse_ipv4_packet(&buf); + let (_v4_packet, tcp_packet) = parse_ip_packet(&buf).unwrap(); if (tcp_packet.get_flags() & tcp::TcpFlags::RST) != 0 { return None; @@ -358,7 +359,7 @@ impl Stack { /// When more than one [`Tun`](tokio_tun::Tun) object is passed in, same amount /// of reader will be spawned later. This allows user to utilize the performance /// benefit of Multiqueue Tun support on machines with SMP. - pub fn new(tun: Vec) -> Stack { + pub fn new(tun: Vec, local_ip: Ipv4Addr, local_ip6: Option) -> Stack { let tun: Vec> = tun.into_iter().map(Arc::new).collect(); let (ready_tx, ready_rx) = mpsc::channel(MPSC_BUFFER_LEN); let (tuples_purge_tx, _tuples_purge_rx) = broadcast::channel(16); @@ -369,7 +370,6 @@ impl Stack { ready: ready_tx, tuples_purge: tuples_purge_tx.clone(), }); - let local_ip = tun[0].destination().unwrap(); for t in tun { tokio::spawn(Stack::reader_task( @@ -382,6 +382,7 @@ impl Stack { Stack { shared, local_ip, + local_ip6, ready: ready_rx, } } @@ -398,10 +399,17 @@ impl Stack { /// Connects to the remote end. `None` returned means /// the connection attempt failed. - pub async fn connect(&mut self, addr: SocketAddrV4) -> Option { + pub async fn connect(&mut self, addr: SocketAddr) -> Option { let mut rng = SmallRng::from_entropy(); let local_port: u16 = rng.gen_range(1024..65535); - let local_addr = SocketAddrV4::new(self.local_ip, local_port); + let local_addr = SocketAddr::new( + if addr.is_ipv4() { + IpAddr::V4(self.local_ip) + } else { + IpAddr::V6(self.local_ip6.expect("IPv6 local address undefined")) + }, + local_port, + ); let tuple = AddrTuple::new(local_addr, addr); let (mut sock, incoming) = Socket::new( self.shared.clone(), @@ -437,90 +445,90 @@ impl Stack { buf.truncate(size); let buf = buf.freeze(); - if buf[0] >> 4 != 4 { - // not an IPv4 packet - continue; - } + match parse_ip_packet(&buf) { + Some((ip_packet, tcp_packet)) => { + let local_addr = + SocketAddr::new(ip_packet.get_destination(), tcp_packet.get_destination()); + let remote_addr = SocketAddr::new(ip_packet.get_source(), tcp_packet.get_source()); - let (ip_packet, tcp_packet) = parse_ipv4_packet(&buf); - let local_addr = - SocketAddrV4::new(ip_packet.get_destination(), tcp_packet.get_destination()); - let remote_addr = SocketAddrV4::new(ip_packet.get_source(), tcp_packet.get_source()); + let tuple = AddrTuple::new(local_addr, remote_addr); + if let Some(c) = tuples.get(&tuple) { + if c.send_async(buf).await.is_err() { + trace!("Cache hit, but receiver already closed, dropping packet"); + } - let tuple = AddrTuple::new(local_addr, remote_addr); - if let Some(c) = tuples.get(&tuple) { - if c.send_async(buf).await.is_err() { - trace!("Cache hit, but receiver already closed, dropping packet"); + continue; + + // If not Ok, receiver has been closed and just fall through to the slow + // path below + } else { + trace!("Cache miss, checking the shared tuples table for connection"); + let sender = { + let tuples = shared.tuples.read().unwrap(); + tuples.get(&tuple).cloned() + }; + + if let Some(c) = sender { + trace!("Storing connection information into local tuples"); + tuples.insert(tuple, c.clone()); + c.send_async(buf).await.unwrap(); + continue; + } + } + + if tcp_packet.get_flags() == tcp::TcpFlags::SYN + && shared + .listening + .read() + .unwrap() + .contains(&tcp_packet.get_destination()) + { + // SYN seen on listening socket + if tcp_packet.get_sequence() == 0 { + let (sock, incoming) = Socket::new( + shared.clone(), + tun.clone(), + local_addr, + remote_addr, + Some(tcp_packet.get_sequence() + 1), + State::Idle, + ); + assert!(shared + .tuples + .write() + .unwrap() + .insert(tuple, incoming) + .is_none()); + tokio::spawn(sock.accept()); + } else { + trace!("Bad TCP SYN packet from {}, sending RST", remote_addr); + let buf = build_tcp_packet( + local_addr, + remote_addr, + 0, + tcp_packet.get_sequence() + tcp_packet.payload().len() as u32 + 1, // +1 because of SYN flag set + tcp::TcpFlags::RST | tcp::TcpFlags::ACK, + None, + ); + shared.tun[0].try_send(&buf).unwrap(); + } + } else if (tcp_packet.get_flags() & tcp::TcpFlags::RST) == 0 { + info!("Unknown TCP packet from {}, sending RST", remote_addr); + let buf = build_tcp_packet( + local_addr, + remote_addr, + tcp_packet.get_acknowledgement(), + tcp_packet.get_sequence() + tcp_packet.payload().len() as u32, + tcp::TcpFlags::RST | tcp::TcpFlags::ACK, + None, + ); + shared.tun[0].try_send(&buf).unwrap(); + } } - - continue; - - // If not Ok, receiver has been closed and just fall through to the slow - // path below - - } else { - trace!("Cache miss, checking the shared tuples table for connection"); - let sender = { - let tuples = shared.tuples.read().unwrap(); - tuples.get(&tuple).cloned() - }; - - if let Some(c) = sender { - trace!("Storing connection information into local tuples"); - tuples.insert(tuple, c.clone()); - c.send_async(buf).await.unwrap(); + None => { continue; } } - - if tcp_packet.get_flags() == tcp::TcpFlags::SYN - && shared - .listening - .read() - .unwrap() - .contains(&tcp_packet.get_destination()) - { - // SYN seen on listening socket - if tcp_packet.get_sequence() == 0 { - let (sock, incoming) = Socket::new( - shared.clone(), - tun.clone(), - local_addr, - remote_addr, - Some(tcp_packet.get_sequence() + 1), - State::Idle, - ); - assert!(shared - .tuples - .write() - .unwrap() - .insert(tuple, incoming) - .is_none()); - tokio::spawn(sock.accept()); - } else { - trace!("Bad TCP SYN packet from {}, sending RST", remote_addr); - let buf = build_tcp_packet( - local_addr, - remote_addr, - 0, - tcp_packet.get_sequence() + tcp_packet.payload().len() as u32 + 1, // +1 because of SYN flag set - tcp::TcpFlags::RST | tcp::TcpFlags::ACK, - None, - ); - shared.tun[0].try_send(&buf).unwrap(); - } - } else if (tcp_packet.get_flags() & tcp::TcpFlags::RST) == 0 { - info!("Unknown TCP packet from {}, sending RST", remote_addr); - let buf = build_tcp_packet( - local_addr, - remote_addr, - tcp_packet.get_acknowledgement(), - tcp_packet.get_sequence() + tcp_packet.payload().len() as u32, - tcp::TcpFlags::RST | tcp::TcpFlags::ACK, - None, - ); - shared.tun[0].try_send(&buf).unwrap(); - } }, tuple = tuples_purge.recv() => { let tuple = tuple.unwrap(); diff --git a/fake-tcp/src/packet.rs b/fake-tcp/src/packet.rs index 16d01ea..e7dd422 100644 --- a/fake-tcp/src/packet.rs +++ b/fake-tcp/src/packet.rs @@ -1,45 +1,85 @@ use bytes::{Bytes, BytesMut}; use internet_checksum::Checksum; use pnet::packet::Packet; -use pnet::packet::{ip, ipv4, tcp}; +use pnet::packet::{ip, ipv4, ipv6, tcp}; use std::convert::TryInto; -use std::net::SocketAddrV4; +use std::net::{IpAddr, SocketAddr}; const IPV4_HEADER_LEN: usize = 20; +const IPV6_HEADER_LEN: usize = 40; const TCP_HEADER_LEN: usize = 20; pub const MAX_PACKET_LEN: usize = 1500; +pub enum IPPacket<'p> { + V4(ipv4::Ipv4Packet<'p>), + V6(ipv6::Ipv6Packet<'p>), +} + +impl<'a> IPPacket<'a> { + pub fn get_source(&self) -> IpAddr { + match self { + IPPacket::V4(p) => IpAddr::V4(p.get_source()), + IPPacket::V6(p) => IpAddr::V6(p.get_source()), + } + } + + pub fn get_destination(&self) -> IpAddr { + match self { + IPPacket::V4(p) => IpAddr::V4(p.get_destination()), + IPPacket::V6(p) => IpAddr::V6(p.get_destination()), + } + } +} + pub fn build_tcp_packet( - local_addr: SocketAddrV4, - remote_addr: SocketAddrV4, + local_addr: SocketAddr, + remote_addr: SocketAddr, seq: u32, ack: u32, flags: u16, payload: Option<&[u8]>, ) -> Bytes { + let ip_header_len = match local_addr { + SocketAddr::V4(_) => IPV4_HEADER_LEN, + SocketAddr::V6(_) => IPV6_HEADER_LEN, + }; let wscale = (flags & tcp::TcpFlags::SYN) != 0; let tcp_header_len = TCP_HEADER_LEN + if wscale { 4 } else { 0 }; // nop + wscale let tcp_total_len = tcp_header_len + payload.map_or(0, |payload| payload.len()); - let total_len = IPV4_HEADER_LEN + tcp_total_len; + let total_len = ip_header_len + tcp_total_len; let mut buf = BytesMut::with_capacity(total_len); buf.resize(total_len, 0); - let mut v4_buf = buf.split_to(IPV4_HEADER_LEN); + let mut ip_buf = buf.split_to(ip_header_len); let mut tcp_buf = buf.split_to(tcp_total_len); assert_eq!(0, buf.len()); - let mut v4 = ipv4::MutableIpv4Packet::new(&mut v4_buf).unwrap(); - v4.set_version(4); - v4.set_header_length(IPV4_HEADER_LEN as u8 / 4); - v4.set_next_level_protocol(ip::IpNextHeaderProtocols::Tcp); - v4.set_ttl(64); - v4.set_source(*local_addr.ip()); - v4.set_destination(*remote_addr.ip()); - v4.set_total_length(total_len.try_into().unwrap()); - v4.set_flags(ipv4::Ipv4Flags::DontFragment); - let mut cksm = Checksum::new(); - cksm.add_bytes(v4.packet()); - v4.set_checksum(u16::from_be_bytes(cksm.checksum())); + match (local_addr, remote_addr) { + (SocketAddr::V4(local), SocketAddr::V4(remote)) => { + let mut v4 = ipv4::MutableIpv4Packet::new(&mut ip_buf).unwrap(); + v4.set_version(4); + v4.set_header_length(IPV4_HEADER_LEN as u8 / 4); + v4.set_next_level_protocol(ip::IpNextHeaderProtocols::Tcp); + v4.set_ttl(64); + v4.set_source(*local.ip()); + v4.set_destination(*remote.ip()); + v4.set_total_length(total_len.try_into().unwrap()); + v4.set_flags(ipv4::Ipv4Flags::DontFragment); + let mut cksm = Checksum::new(); + cksm.add_bytes(v4.packet()); + v4.set_checksum(u16::from_be_bytes(cksm.checksum())); + } + (SocketAddr::V6(local), SocketAddr::V6(remote)) => { + let mut v6 = ipv6::MutableIpv6Packet::new(&mut ip_buf).unwrap(); + v6.set_version(6); + v6.set_payload_length(tcp_total_len.try_into().unwrap()); + v6.set_next_header(ip::IpNextHeaderProtocols::Tcp); + v6.set_hop_limit(64); + v6.set_source(*local.ip()); + v6.set_destination(*remote.ip()); + } + _ => unreachable!(), + }; let mut tcp = tcp::MutableTcpPacket::new(&mut tcp_buf).unwrap(); tcp.set_window(0xffff); @@ -59,24 +99,55 @@ pub fn build_tcp_packet( } let mut cksm = Checksum::new(); - cksm.add_bytes(&local_addr.ip().octets()); - cksm.add_bytes(&remote_addr.ip().octets()); let ip::IpNextHeaderProtocol(tcp_protocol) = ip::IpNextHeaderProtocols::Tcp; - let mut pseudo = [0u8, tcp_protocol, 0, 0]; - pseudo[2..].copy_from_slice(&(tcp_total_len as u16).to_be_bytes()); - cksm.add_bytes(&pseudo); + + match (local_addr, remote_addr) { + (SocketAddr::V4(local), SocketAddr::V4(remote)) => { + cksm.add_bytes(&local.ip().octets()); + cksm.add_bytes(&remote.ip().octets()); + + let mut pseudo = [0u8, tcp_protocol, 0, 0]; + pseudo[2..].copy_from_slice(&(tcp_total_len as u16).to_be_bytes()); + cksm.add_bytes(&pseudo); + } + (SocketAddr::V6(local), SocketAddr::V6(remote)) => { + cksm.add_bytes(&local.ip().octets()); + cksm.add_bytes(&remote.ip().octets()); + + let mut pseudo = [0u8, 0, 0, 0, 0, 0, 0, tcp_protocol]; + pseudo[0..4].copy_from_slice(&(tcp_total_len as u32).to_be_bytes()); + cksm.add_bytes(&pseudo); + } + _ => unreachable!(), + }; + cksm.add_bytes(tcp.packet()); tcp.set_checksum(u16::from_be_bytes(cksm.checksum())); - v4_buf.unsplit(tcp_buf); - v4_buf.freeze() + ip_buf.unsplit(tcp_buf); + ip_buf.freeze() } -pub fn parse_ipv4_packet(buf: &Bytes) -> (ipv4::Ipv4Packet, tcp::TcpPacket) { - let v4 = ipv4::Ipv4Packet::new(buf).unwrap(); - let tcp = tcp::TcpPacket::new(&buf[IPV4_HEADER_LEN..]).unwrap(); +pub fn parse_ip_packet(buf: &Bytes) -> Option<(IPPacket, tcp::TcpPacket)> { + if buf[0] >> 4 == 4 { + let v4 = ipv4::Ipv4Packet::new(buf).unwrap(); + if v4.get_next_level_protocol() != ip::IpNextHeaderProtocols::Tcp { + return None; + } - (v4, tcp) + let tcp = tcp::TcpPacket::new(&buf[IPV4_HEADER_LEN..]).unwrap(); + Some((IPPacket::V4(v4), tcp)) + } else if buf[0] >> 4 == 6 { + let v6 = ipv6::Ipv6Packet::new(buf).unwrap(); + if v6.get_next_header() != ip::IpNextHeaderProtocols::Tcp { + return None; + } + + let tcp = tcp::TcpPacket::new(&buf[IPV6_HEADER_LEN..]).unwrap(); + Some((IPPacket::V6(v6), tcp)) + } else { + None + } } #[cfg(all(test, feature = "benchmark"))] diff --git a/phantun/Cargo.toml b/phantun/Cargo.toml index 930f3ba..a48446b 100644 --- a/phantun/Cargo.toml +++ b/phantun/Cargo.toml @@ -20,3 +20,5 @@ log = "0.4" pretty_env_logger = "0.4" tokio-tun = "0.5" num_cpus = "1.13" +neli = "0.6" +nix = "0.23" diff --git a/phantun/src/bin/client.rs b/phantun/src/bin/client.rs index 816e6da..dab05b1 100644 --- a/phantun/src/bin/client.rs +++ b/phantun/src/bin/client.rs @@ -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 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::>::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]; diff --git a/phantun/src/bin/server.rs b/phantun/src/bin/server.rs index 5b8f4c6..26eedea 100644 --- a/phantun/src/bin/server.rs +++ b/phantun/src/bin/server.rs @@ -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 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); diff --git a/phantun/src/utils.rs b/phantun/src/utils.rs index ff35ddf..b8630e7 100644 --- a/phantun/src/utils.rs +++ b/phantun/src/utils.rs @@ -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(); +}