diff --git a/Cargo.lock b/Cargo.lock index 6499e35..a6b91a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,8 @@ dependencies = [ "cfg-if", "defmt", "heapless", + "libc", + "log", "managed", ] diff --git a/Cargo.toml b/Cargo.toml index a69997b..9a0e163 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ rust-version = "1.77" log = { version = "0.4", default-features = false } tokio = { version = "1.38", default-features = false, features = ["rt", "io-util", "signal", "net", "time", "sync"] } tokio-native-tls = { version = "0.3", default-features = false } -smoltcp = { version = "0.11", default-features = false, features = ["std", "medium-ip", "proto-ipv4", "proto-ipv6", "socket-tcp"] } +smoltcp = { version = "0.11", default-features = true, features = ["std", "medium-ip", "proto-ipv4", "proto-ipv4-fragmentation", "proto-ipv6", "proto-ipv6-fragmentation", "socket-tcp"] } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } [profile.release] diff --git a/src/fortivpn.rs b/src/fortivpn.rs index 4672326..b6eac1f 100644 --- a/src/fortivpn.rs +++ b/src/fortivpn.rs @@ -6,18 +6,24 @@ use std::{ }; use log::{debug, warn}; +use rand::Rng; use tokio::net::{TcpListener, TcpStream}; use tokio_native_tls::native_tls; use crate::http::{ build_http_request, read_content, read_http_headers, write_http_response, BufferedTlsStream, }; +use crate::ppp; pub struct Config { pub destination_addr: SocketAddr, pub destination_hostport: String, } +// TODO: allow this to be configured. +// PPP_MTU specifies is the MTU excluding IP, TCP, TLS, FortiVPN and PPP encapsulation headers. +const PPP_MTU: u16 = 1400; //1500 - 20 - 20 - 5 - 6 - 4; + // TODO: check how FortiVPN chooses the listen port - is it fixed or sent as a parameter? const REDIRECT_ADDRESS: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8020); @@ -122,8 +128,9 @@ pub async fn get_oauth_cookie(config: &Config) -> Result { pub struct FortiVPNTunnel { socket: FortiTlsStream, addr: IpAddr, - first_packet: bool, - tunnel_failed: bool, + mtu: usize, + ppp_magic: u32, + ppp_identifier: u8, } impl FortiVPNTunnel { @@ -136,11 +143,14 @@ impl FortiVPNTunnel { let mut socket = FortiVPNTunnel::connect(&config.destination_hostport, domain).await?; let addr = FortiVPNTunnel::request_vpn_allocation(domain, &mut socket, &cookie).await?; FortiVPNTunnel::start_vpn_tunnel(domain, &mut socket, &cookie).await?; + let ppp_magic = FortiVPNTunnel::start_ppp(&mut socket).await?; + FortiVPNTunnel::start_ipcp(&mut socket, addr).await?; Ok(FortiVPNTunnel { socket, addr, - first_packet: true, - tunnel_failed: true, + mtu: PPP_MTU as usize, + ppp_magic, + ppp_identifier: 2, }) } @@ -148,6 +158,10 @@ impl FortiVPNTunnel { self.addr } + pub fn mtu(&self) -> usize { + self.mtu as usize + } + async fn connect(hostport: &str, domain: &str) -> Result { let socket = TcpStream::connect(hostport).await?; let connector = native_tls::TlsConnector::builder().build()?; @@ -197,72 +211,415 @@ impl FortiVPNTunnel { cookie: &str, ) -> Result<(), FortiError> { let req = build_http_request("GET /remote/sslvpn-tunnel", domain, Some(cookie), 0); - println!("Starting VPN is {}?", req); socket.write_all(req.as_bytes()).await?; socket.flush().await?; Ok(()) } - pub async fn send_packet(&mut self, data: &[u8]) -> Result<(), FortiError> { - println!("About to send packet {:?}", data); - // PPP packets are surprisingly basic. - let mut packet_header = [0u8; 6]; - packet_header[..2].copy_from_slice(&(6 + data.len() as u16).to_be_bytes()); - packet_header[2..4].copy_from_slice(&[0x50, 0x50]); - packet_header[4..].copy_from_slice(&(data.len() as u16).to_be_bytes()); + async fn start_ppp(socket: &mut FortiTlsStream) -> Result { + // Open PPP link; 200 bytes should fit any PPP packet. + // This is an oversimplified implementation of the RFC 1661 state machine. + let mut req = [0u8; 20]; + let mut resp = [0u8; 200]; + let identifier = 1; + let magic = rand::thread_rng().gen::(); + let opts = [ + ppp::LcpOptionData::MaximumReceiveUnit(PPP_MTU), + ppp::LcpOptionData::MagicNumber(magic), + ]; + let length = + ppp::encode_lcp_config(&mut req, ppp::LcpCode::CONFIGURE_REQUEST, identifier, &opts) + .map_err(|err| { + debug!("Failed to encode Configure-Request: {}", err); + "Failed to encode Configure-Request" + })?; + { + let mut pack = [0u8; 200]; + pack[..2].copy_from_slice(&ppp::Protocol::LCP.value().to_be_bytes()); + pack[2..length + 2].copy_from_slice(&req[..length]); - // TODO: replace with a version that needs less allocations - /* - let packet_data = Vec::from(data); - let data = match hdlc::encode(&packet_data, hdlc::SpecialChars::default()) { - Ok(encoded) => encoded, - Err(err) => { - debug!("Failed to encode HDLC packet: {}", err); - return Err("Failed to encode HDLC packet".into()); + println!( + "Encoded packet: {:?}", + ppp::Packet::from_bytes(&pack[..length + 2]) + ); + } + FortiVPNTunnel::send_ppp_packet(socket, ppp::Protocol::LCP, &req[..length]).await?; + + let mut local_acked = false; + let mut remote_acked = false; + while !(local_acked && remote_acked) { + let length = FortiVPNTunnel::read_ppp_packet(socket, &mut resp, true).await?; + let response = &resp[..length]; + println!("Read packet: {:?}", response); + let response = ppp::Packet::from_bytes(&resp[..length]).map_err(|err| { + debug!("Failed to decode PPP packet: {}", err); + "Failed to decode PPP packet" + })?; + println!("Decoded packet: {}", response); + let lcp_packet = match response.to_lcp() { + Ok(lcp) => lcp, + Err(err) => { + debug!( + "Received unexpected PPP packet during handshake: {} (error {})", + response, err + ); + continue; + } + }; + match lcp_packet.code() { + ppp::LcpCode::CONFIGURE_ACK => { + let received_opts = lcp_packet + .iter_options() + .collect::, ppp::FormatError>>(); + let options_match = match received_opts { + Ok(received_opts) => &opts == received_opts.as_slice(), + Err(err) => { + debug!("Failed to decode Ack options: {}", err); + return Err("Failed to decode Ack options".into()); + } + }; + if !options_match { + return Err("Configure Ack has unexpected options".into()); + } + local_acked = true; + } + ppp::LcpCode::CONFIGURE_REQUEST => { + let received_opts = lcp_packet + .iter_options() + .map(|opt| { + let opt = match opt { + Ok(opt) => opt, + Err(err) => { + debug!( + "Remote side sent invalid configuration option: {}", + err + ); + return Err( + "Remote side sent invalid configuration option".into() + ); + } + }; + match opt { + ppp::LcpOptionData::MaximumReceiveUnit(mtu) => { + if mtu <= PPP_MTU { + Ok(opt) + } else { + debug!("Remote side sent unacceptable MTU: {}", mtu); + Err("Remote side sent unacceptable MTU".into()) + } + } + ppp::LcpOptionData::MagicNumber(magic) => Ok(opt), + _ => { + debug!( + "Remote side sent unsupported configuration option: {}", + opt + ); + Err("Remote side sent unsupported configuration option".into()) + } + } + }) + .collect::, FortiError>>(); + match received_opts { + Ok(opts) => { + let length = ppp::encode_lcp_data( + &mut req, + ppp::LcpCode::CONFIGURE_ACK, + lcp_packet.identifier(), + lcp_packet.read_options(), + ) + .map_err(|err| { + debug!("Failed to encode Configure-Ack: {}", err); + "Failed to encode Configure-Ack" + })?; + FortiVPNTunnel::send_ppp_packet( + socket, + ppp::Protocol::LCP, + &req[..length], + ) + .await?; + remote_acked = true; + } + Err(err) => { + return Err(err); + } + } + } + ppp::LcpCode::CONFIGURE_NAK => { + debug!("Remote side Nak'd configuration: {}", response); + return Err("Remote side Nak'd configuration".into()); + } + ppp::LcpCode::CONFIGURE_REJECT => { + debug!("Remote side rejected configuration: {}", response); + return Err("Remote side rejected configuration".into()); + } + _ => { + debug!("Received unexpected PPP packet: {}", response); + return Err("Unexpected PPP packet received".into()); + } } + } + + Ok(magic) + } + + async fn start_ipcp(socket: &mut FortiTlsStream, addr: IpAddr) -> Result<(), FortiError> { + // Open IPCP link; 20 bytes should fit any IPCP packet. + // This is an oversimplified implementation of the RFC 1661 state machine. + let mut req = [0u8; 100]; + let mut resp = [0u8; 200]; + let mut identifier = 1; + let addr = match addr { + IpAddr::V4(addr) => addr, + _ => return Ok(()), }; - */ + let opts = [ + ppp::IpcpOptionData::IpAddress(addr), + //ppp::IpcpOptionData::PrimaryDns(Ipv4Addr::UNSPECIFIED), + //ppp::IpcpOptionData::SecondaryDns(Ipv4Addr::UNSPECIFIED), + ]; + let length = + ppp::encode_ipcp_config(&mut req, ppp::LcpCode::CONFIGURE_REQUEST, identifier, &opts) + .map_err(|err| { + debug!("Failed to encode Configure-Request: {}", err); + "Failed to encode Configure-Request" + })?; + { + let mut pack = [0u8; 200]; + pack[..2].copy_from_slice(&ppp::Protocol::IPV4CP.value().to_be_bytes()); + pack[2..length + 2].copy_from_slice(&req[..length]); - self.socket.write_all(&packet_header).await?; - Ok(self.socket.write_all(&data).await?) + println!( + "Encoded packet: {:?}", + ppp::Packet::from_bytes(&pack[..length + 2]) + ); + } + let mut opts = [0u8; 100]; + let mut opts_len = length - 4; + opts[..opts_len].copy_from_slice(&req[4..length]); + FortiVPNTunnel::send_ppp_packet(socket, ppp::Protocol::IPV4CP, &req[..length]).await?; + + let mut local_acked = false; + let mut remote_acked = false; + while !(local_acked && remote_acked) { + let length = FortiVPNTunnel::read_ppp_packet(socket, &mut resp, true).await?; + let response = &resp[..length]; + println!("Read packet: {:?}", response); + let response = ppp::Packet::from_bytes(&resp[..length]).map_err(|err| { + debug!("Failed to decode PPP packet: {}", err); + "Failed to decode PPP packet" + })?; + println!("Decoded packet: {}", response); + let ipcp_packet = match response.to_ipcp() { + Ok(lcp) => lcp, + Err(err) => { + debug!( + "Received unexpected PPP packet during handshake: {} (error {})", + response, err + ); + continue; + } + }; + match ipcp_packet.code() { + ppp::LcpCode::CONFIGURE_ACK => { + if ipcp_packet.read_options() != &opts[..opts_len] { + return Err("Configure Ack has unexpected options".into()); + } + local_acked = true; + } + ppp::LcpCode::CONFIGURE_REQUEST => { + let received_opts = ipcp_packet + .iter_options() + .map(|opt| { + let opt = match opt { + Ok(opt) => opt, + Err(err) => { + debug!( + "Remote side sent invalid configuration option: {}", + err + ); + return Err( + "Remote side sent invalid configuration option".into() + ); + } + }; + match opt { + ppp::IpcpOptionData::IpAddress(ip) => Ok(opt), + ppp::IpcpOptionData::PrimaryDns(ip) => Ok(opt), + ppp::IpcpOptionData::SecondaryDns(ip) => Ok(opt), + _ => { + debug!( + "Remote side sent unsupported configuration option: {}", + opt + ); + Err("Remote side sent unsupported configuration option".into()) + } + } + }) + .collect::, FortiError>>(); + match received_opts { + Ok(_) => { + let length = ppp::encode_lcp_data( + &mut req, + ppp::LcpCode::CONFIGURE_ACK, + ipcp_packet.identifier(), + ipcp_packet.read_options(), + ) + .map_err(|err| { + debug!("Failed to encode Configure-Ack: {}", err); + "Failed to encode Configure-Ack" + })?; + FortiVPNTunnel::send_ppp_packet( + socket, + ppp::Protocol::IPV4CP, + &req[..length], + ) + .await?; + remote_acked = true; + } + Err(err) => { + return Err(err); + } + } + } + ppp::LcpCode::CONFIGURE_NAK => { + debug!("Remote side Nak'd configuration: {}", response); + let received_opts = ipcp_packet + .iter_options() + .map(|opt| { + let opt = match opt { + Ok(opt) => opt, + Err(err) => { + debug!( + "Remote side sent invalid configuration option: {}", + err + ); + return Err( + "Remote side sent invalid configuration option".into() + ); + } + }; + match opt { + ppp::IpcpOptionData::IpAddress(ip) => Ok(opt), + ppp::IpcpOptionData::PrimaryDns(ip) => Ok(opt), + ppp::IpcpOptionData::SecondaryDns(ip) => Ok(opt), + _ => { + debug!( + "Remote side sent unsupported configuration option: {}", + opt + ); + Err("Remote side sent unsupported configuration option".into()) + } + } + }) + .collect::, FortiError>>(); + match received_opts { + Ok(_) => { + opts_len = ipcp_packet.read_options().len(); + opts[..opts_len].copy_from_slice(ipcp_packet.read_options()); + identifier += 1; + + let length = ppp::encode_lcp_data( + &mut req, + ppp::LcpCode::CONFIGURE_REQUEST, + identifier, + &opts[..opts_len], + ) + .map_err(|err| { + debug!("Failed to encode Configure-Request: {}", err); + "Failed to encode Configure-Request" + })?; + FortiVPNTunnel::send_ppp_packet( + socket, + ppp::Protocol::IPV4CP, + &req[..length], + ) + .await?; + } + Err(err) => { + return Err(err); + } + } + } + ppp::LcpCode::CONFIGURE_REJECT => { + debug!("Remote side rejected configuration: {}", response); + return Err("Remote side rejected configuration".into()); + } + _ => { + debug!("Received unexpected PPP packet: {}", response); + return Err("Unexpected PPP packet received".into()); + } + } + } + + Ok(()) } - pub async fn read_packet(&mut self, dest: &mut [u8]) -> Result { - let socket = &mut self.socket; + async fn send_ppp_packet( + socket: &mut FortiTlsStream, + protocol: ppp::Protocol, + ppp_data: &[u8], + ) -> Result<(), FortiError> { + let mut buf = String::new(); + ppp::fmt_slice_hex(ppp_data, &mut buf); + println!("About to send packet {:?}, {}", buf, protocol.value()); + // FortiVPN encapsulation. + let mut packet_header = [0u8; 8]; + let ppp_packet_length = ppp_data.len() + 2; + packet_header[..2].copy_from_slice(&(6 + ppp_packet_length as u16).to_be_bytes()); + packet_header[2..4].copy_from_slice(&[0x50, 0x50]); + packet_header[4..6].copy_from_slice(&(ppp_packet_length as u16).to_be_bytes()); + // PPP encapsulation. + packet_header[6..].copy_from_slice(&protocol.value().to_be_bytes()); + + socket.write_all(&packet_header).await?; + socket.write_all(&ppp_data).await?; + Ok(socket.flush().await?) + } + + async fn read_ppp_packet( + socket: &mut FortiTlsStream, + dest: &mut [u8], + first_packet: bool, + ) -> Result { let mut packet_header = [0u8; 6]; println!("Reading packet"); // If no data is available, this will return immediately. match tokio::time::timeout(Duration::from_millis(100), async { loop { - //println!("stupid loop"); - if let Ok(header) = socket.read_peek(6).await { - if header.len() >= 6 { - println!("Have header {} bytes", header.len()); + match socket.read_peek(packet_header.len()).await { + Ok(header) => { + if header.len() >= packet_header.len() { + println!("Have header {} bytes", header.len()); + return; + } else { + println!("Have {} bytes", header.len()); + } + } + Err(err) => { + println!("Failed to read header {}", err); return; - } else { - println!("Have {} bytes", header.len()); } - } else { - return; - }; + } } }) .await { Ok(_) => {} - Err(_) => return Ok(0), + Err(_) => { + println!("Read timed out"); + return Ok(0); + } } - println!("Packet not ready"); + println!("Packet ready"); - socket.read(&mut packet_header).await?; - if self.first_packet { - self.first_packet = false; - if let Err(err) = FortiVPNTunnel::validate_link(socket, &packet_header).await { - self.tunnel_failed = true; + if first_packet { + if let Err(err) = FortiVPNTunnel::validate_link(socket, &packet_header[..6]).await { return Err(err); } } + + socket.read(&mut packet_header).await?; let mut ppp_size = [0u8; 2]; ppp_size.copy_from_slice(&packet_header[..2]); let ppp_size = u16::from_be_bytes(ppp_size); @@ -300,6 +657,51 @@ impl FortiVPNTunnel { Ok(data_size) } + pub async fn send_echo(&mut self) -> Result<(), FortiError> { + let mut req = [0u8; 8]; + let data = self.ppp_magic.to_be_bytes(); + let length = ppp::encode_lcp_data( + &mut req, + ppp::LcpCode::ECHO_REQUEST, + self.ppp_identifier, + &data, + ) + .map_err(|err| { + debug!("Failed to encode Echo-Request: {}", err); + "Failed to encode Echo-Request" + })?; + self.ppp_identifier += 1; + FortiVPNTunnel::send_ppp_packet(&mut self.socket, ppp::Protocol::LCP, &req[..length]).await + } + + pub async fn send_packet(&mut self, data: &[u8]) -> Result<(), FortiError> { + FortiVPNTunnel::send_ppp_packet(&mut self.socket, ppp::Protocol::IPV4, data).await + } + + pub async fn read_packet(&mut self, dest: &mut [u8]) -> Result { + // TODO: handle async PPP packets. + let length = FortiVPNTunnel::read_ppp_packet(&mut self.socket, dest, false).await?; + if length == 0 { + return Ok(0); + } + let packet = match ppp::Packet::from_bytes(&dest[..length]) { + Ok(packet) => packet, + Err(err) => { + debug!("Failed to decode PPP packet: {}", err); + return Err("Failed to decode PPP packet".into()); + } + }; + println!("Packet= {}", packet); + + if packet.read_protocol() == ppp::Protocol::IPV4 { + // TODO: improve this + dest.copy_within(2..length, 0); + Ok(length - 2) + } else { + Ok(0) + } + } + async fn validate_link( socket: &mut FortiTlsStream, packet_header: &[u8], diff --git a/src/main.rs b/src/main.rs index 4193d64..4666186 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ mod fortivpn; mod http; mod logger; mod network; +mod ppp; mod socks; enum Action { @@ -167,7 +168,7 @@ fn serve(config: Config) -> Result<(), i32> { 1 })?; - let forti_client = rt + let mut forti_client = rt .block_on(fortivpn::FortiVPNTunnel::new( &config.fortivpn, sslvpn_cookie, @@ -176,6 +177,11 @@ fn serve(config: Config) -> Result<(), i32> { eprintln!("Failed to connect to VPN service: {}", err); 1 })?; + // TODO: run this in a timer loop. + rt.block_on(forti_client.send_echo()).map_err(|err| { + eprintln!("Failed to send echo to VPN service: {}", err); + 1 + })?; let mut client = network::Network::new(forti_client).map_err(|err| { eprintln!("Failed to start virtual network interface: {}", err); 1 diff --git a/src/network.rs b/src/network.rs index 699bb31..a9ba27c 100644 --- a/src/network.rs +++ b/src/network.rs @@ -6,19 +6,17 @@ use std::{ }; use log::{debug, warn}; +use rand::Rng; use smoltcp::{iface, phy, socket, wire}; -use tokio::{ - runtime, - sync::{mpsc, oneshot}, -}; +use tokio::sync::{mpsc, oneshot}; use crate::fortivpn::FortiVPNTunnel; -const MTU_SIZE: usize = 1500; +const MAX_MTU_SIZE: usize = 1500; const SOCKET_BUFFER_SIZE: usize = 4096; pub struct Network<'a> { - device: PPPDevice, + device: VpnDevice, iface: iface::Interface, sockets: iface::SocketSet<'a>, bridges: HashMap, @@ -31,9 +29,9 @@ pub struct Network<'a> { impl Network<'_> { pub fn new<'a>(vpn: FortiVPNTunnel) -> Result, NetworkError> { // TODO: how to choose the CIDR? - let ip_cidr = wire::IpCidr::new(vpn.ip_addr().into(), 24); + let ip_cidr = wire::IpCidr::new(vpn.ip_addr().into(), 8); - let mut device = PPPDevice::new(vpn); + let mut device = VpnDevice::new(vpn); let mut config = iface::Config::new(smoltcp::wire::HardwareAddress::Ip); config.random_seed = rand::random(); let mut iface = iface::Interface::new(config, &mut device, smoltcp::time::Instant::now()); @@ -67,8 +65,8 @@ impl Network<'_> { self.copy_all_data(); self.iface .poll(timestamp, &mut self.device, &mut self.sockets); - self.device.receive_data().await?; self.device.send_data().await?; + self.device.receive_data().await?; match self.iface.poll_delay(timestamp, &self.sockets) { Some(poll_delay) => { let timeout_at = tokio::time::Instant::now() @@ -76,7 +74,9 @@ impl Network<'_> { self.process_commands(timeout_at.into()).await; } None => { - self.process_commands(None).await; + // TODO: refactor this to send all events through queue. + let timeout_at = tokio::time::Instant::now() + Duration::from_millis(100); + self.process_commands(timeout_at.into()).await; } } } @@ -173,8 +173,8 @@ impl Network<'_> { let rx_buffer = tcp::SocketBuffer::new(vec![0; SOCKET_BUFFER_SIZE]); let tx_buffer = tcp::SocketBuffer::new(vec![0; SOCKET_BUFFER_SIZE]); let mut socket = tcp::Socket::new(rx_buffer, tx_buffer); - // TODO: choose a random port between 49152 and 65535. - let local_port = 8080; + // TODO: check for collisions. + let local_port = rand::thread_rng().gen_range(49152..=65535); let remote_addr = wire::IpAddress::from(addr.ip()); if let Err(err) = socket.connect(self.iface.context(), (remote_addr, addr.port()), local_port) @@ -232,27 +232,27 @@ pub enum Command { ), } -struct PPPDevice { +struct VpnDevice { vpn: FortiVPNTunnel, - read_packet: [u8; MTU_SIZE], - write_packet: [u8; MTU_SIZE], + read_packet: [u8; MAX_MTU_SIZE], + write_packet: [u8; MAX_MTU_SIZE], read_packet_size: usize, write_packet_size: usize, } -impl PPPDevice { - fn new(vpn: FortiVPNTunnel) -> PPPDevice { - PPPDevice { +impl VpnDevice { + fn new(vpn: FortiVPNTunnel) -> VpnDevice { + VpnDevice { vpn, - read_packet: [0u8; MTU_SIZE], - write_packet: [0u8; MTU_SIZE], + read_packet: [0u8; MAX_MTU_SIZE], + write_packet: [0u8; MAX_MTU_SIZE], read_packet_size: 0, write_packet_size: 0, } } } -impl PPPDevice { +impl VpnDevice { async fn receive_data(&mut self) -> Result<(), NetworkError> { if self.read_packet_size > 0 { // Data is not consumed yet. @@ -276,12 +276,12 @@ impl PPPDevice { } } -impl phy::Device for PPPDevice { - type RxToken<'a> = PPPDeviceRxToken<'a> +impl phy::Device for VpnDevice { + type RxToken<'a> = RxToken<'a> where Self: 'a; - type TxToken<'a> = PPPDeviceTxToken<'a> + type TxToken<'a> = TxToken<'a> where Self: 'a; @@ -292,11 +292,11 @@ impl phy::Device for PPPDevice { if self.read_packet_size == 0 || self.write_packet_size != 0 { return None; } + let read_packet = &mut self.read_packet[..self.read_packet_size]; + self.read_packet_size = 0; Some(( - PPPDeviceRxToken { - read_packet: &mut self.read_packet[..self.read_packet_size], - }, - PPPDeviceTxToken { + RxToken { read_packet }, + TxToken { write_packet: &mut self.write_packet, write_packet_size: &mut self.write_packet_size, }, @@ -307,7 +307,7 @@ impl phy::Device for PPPDevice { if self.write_packet_size != 0 { return None; } - Some(PPPDeviceTxToken { + Some(TxToken { write_packet: &mut self.write_packet, write_packet_size: &mut self.write_packet_size, }) @@ -315,18 +315,23 @@ impl phy::Device for PPPDevice { fn capabilities(&self) -> phy::DeviceCapabilities { let mut caps = phy::DeviceCapabilities::default(); - caps.max_transmission_unit = MTU_SIZE; - caps.max_burst_size = Some(1); + caps.max_transmission_unit = self.vpn.mtu(); + //caps.max_burst_size = Some(16); caps.medium = phy::Medium::Ip; + caps.checksum.ipv4 = phy::Checksum::Both; + caps.checksum.tcp = phy::Checksum::Both; + caps.checksum.udp = phy::Checksum::Both; + caps.checksum.icmpv4 = phy::Checksum::Both; + caps.checksum.icmpv6 = phy::Checksum::Both; caps } } -struct PPPDeviceRxToken<'a> { +struct RxToken<'a> { read_packet: &'a mut [u8], } -impl<'a> phy::RxToken for PPPDeviceRxToken<'a> { +impl<'a> phy::RxToken for RxToken<'a> { fn consume(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -337,12 +342,12 @@ impl<'a> phy::RxToken for PPPDeviceRxToken<'a> { } } -struct PPPDeviceTxToken<'a> { +struct TxToken<'a> { write_packet: &'a mut [u8], write_packet_size: &'a mut usize, } -impl<'a> phy::TxToken for PPPDeviceTxToken<'a> { +impl<'a> phy::TxToken for TxToken<'a> { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/src/ppp.rs b/src/ppp.rs new file mode 100644 index 0000000..2f10fca --- /dev/null +++ b/src/ppp.rs @@ -0,0 +1,740 @@ +use std::{error, fmt, net::Ipv4Addr}; + +use log::debug; + +/* + * PPP constants are defined in https://www.iana.org/assignments/ppp-numbers/ppp-numbers.xhtml + */ + +pub struct Packet<'a> { + data: &'a [u8], +} + +impl Packet<'_> { + pub fn from_bytes(b: &[u8]) -> Result { + if b.len() < 2 { + debug!("Not enough data in PPP packet"); + Err("Not enough data in PPP packet".into()) + } else { + let packet = Packet { data: b }; + packet.validate()?; + Ok(packet) + } + } + + pub fn validate(&self) -> Result<(), FormatError> { + self.read_protocol().validate() + } + + pub fn read_protocol(&self) -> Protocol { + let mut result = [0u8; 2]; + result.copy_from_slice(&self.data[..2]); + Protocol::from_u16(u16::from_be_bytes(result)) + } + + pub fn to_lcp(&self) -> Result { + let protocol = self.read_protocol(); + if protocol == Protocol::LCP { + LcpPacket::from_bytes(&self.data[2..]) + } else { + Err("Protocol type is not LCP".into()) + } + } + + pub fn to_ipcp(&self) -> Result { + let protocol = self.read_protocol(); + if protocol == Protocol::IPV4CP { + IpcpPacket::from_bytes(&self.data[2..]) + } else { + Err("Protocol type is not IPCP".into()) + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Protocol(u16); + +impl Protocol { + pub const IPV4: Protocol = Protocol(0x0021); + pub const IPV6: Protocol = Protocol(0x0057); + pub const LCP: Protocol = Protocol(0xc021); + pub const IPV4CP: Protocol = Protocol(0x8021); + + fn from_u16(value: u16) -> Protocol { + Protocol(value) + } + + pub fn value(&self) -> u16 { + self.0 + } + + fn validate(&self) -> Result<(), FormatError> { + if self.0 & 0x0001 == 0 { + return Err("Protocol must be odd".into()); + } + if self.0 & 0x0100 != 0 { + return Err("Protocol group must be even".into()); + } + Ok(()) + } +} + +impl fmt::Display for Protocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::IPV4 => write!(f, "Internet Protocol version 4")?, + Self::IPV6 => write!(f, "Internet Protocol version 6")?, + Self::LCP => write!(f, "Link Control Protocol")?, + Self::IPV4CP => write!(f, "Internet Protocol Control Protocol")?, + _ => write!(f, "Unknown protocol {:04x}", self.0)?, + } + Ok(()) + } +} + +pub struct LcpPacket<'a> { + code: LcpCode, + identifier: u8, + data: &'a [u8], +} + +impl LcpPacket<'_> { + fn from_bytes(data: &[u8]) -> Result { + if data.len() < 4 { + debug!("Not enough data in LCP packet"); + Err("Not enough in LCP packet".into()) + } else { + let code = LcpCode::from_u8(data[0])?; + let identifier = data[1]; + let mut length = [0u8; 2]; + length.copy_from_slice(&data[2..4]); + let length = u16::from_be_bytes(length) as usize; + if data.len() < length { + debug!( + "LCP data overflow: received {}, length {}", + data.len(), + length + ); + return Err("LCP data overflow".into()); + } + Ok(LcpPacket { + code, + identifier, + data: &data[4..length], + }) + } + } + + pub fn code(&self) -> LcpCode { + self.code + } + + pub fn identifier(&self) -> u8 { + self.identifier + } + + pub fn read_magic(&self) -> Option { + if !(self.code == LcpCode::ECHO_REQUEST || self.code == LcpCode::ECHO_REPLY) + || self.data.len() < 4 + { + return None; + } + let mut magic = [0u8; 4]; + magic.copy_from_slice(&self.data[..4]); + Some(u32::from_be_bytes(magic)) + } + + pub fn read_options(&self) -> &[u8] { + if self.code.has_configure_options() { + self.data + } else { + &[] + } + } + + pub fn iter_options(&self) -> LcpOptionsIter { + LcpOptionsIter { + data: self.read_options(), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct LcpCode(u8); + +impl LcpCode { + pub const CONFIGURE_REQUEST: LcpCode = LcpCode(1); + pub const CONFIGURE_ACK: LcpCode = LcpCode(2); + pub const CONFIGURE_NAK: LcpCode = LcpCode(3); + pub const CONFIGURE_REJECT: LcpCode = LcpCode(4); + pub const TERMINATE_REQUEST: LcpCode = LcpCode(5); + pub const TERMINATE_ACK: LcpCode = LcpCode(6); + pub const CODE_REJECT: LcpCode = LcpCode(7); + pub const PROTOCOL_REJECT: LcpCode = LcpCode(8); + pub const ECHO_REQUEST: LcpCode = LcpCode(9); + pub const ECHO_REPLY: LcpCode = LcpCode(10); + pub const DISCARD_REQUEST: LcpCode = LcpCode(11); + + fn from_u8(value: u8) -> Result { + if (Self::CONFIGURE_REQUEST.0..=Self::DISCARD_REQUEST.0).contains(&value) { + Ok(LcpCode(value)) + } else { + debug!("Unsupported LCP Code: {}", value); + Err("Unsupported LCP Code".into()) + } + } + + fn has_configure_options(&self) -> bool { + (Self::CONFIGURE_REQUEST.0..=LcpCode::CONFIGURE_REJECT.0).contains(&self.0) + } +} + +impl fmt::Display for LcpCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::CONFIGURE_REQUEST => write!(f, "Configure-Request")?, + Self::CONFIGURE_ACK => write!(f, "Configure-Ack")?, + Self::CONFIGURE_NAK => write!(f, "Configure-Nak")?, + Self::CONFIGURE_REJECT => write!(f, "Configure-Reject")?, + Self::TERMINATE_REQUEST => write!(f, "Terminate-Request")?, + Self::TERMINATE_ACK => write!(f, "Terminate-Ack")?, + Self::CODE_REJECT => write!(f, "Code-Reject")?, + Self::PROTOCOL_REJECT => write!(f, "Protocol-Reject")?, + Self::ECHO_REQUEST => write!(f, "Echo-Request")?, + Self::ECHO_REPLY => write!(f, "Echo-Reply")?, + Self::DISCARD_REQUEST => write!(f, "Discard-Request")?, + _ => write!(f, "Unknown LCP code {:02x}", self.0)?, + } + Ok(()) + } +} + +pub struct LcpOptionsIter<'a> { + data: &'a [u8], +} + +impl<'a> Iterator for LcpOptionsIter<'a> { + type Item = Result, FormatError>; + + fn next(&mut self) -> Option { + if self.data.is_empty() { + return None; + } + if self.data.len() < 2 { + debug!("Not enough data in LCP Configuration Option"); + self.data = &[]; + return Some(Err("Not enough data in LCP configuration option".into())); + } + let option_type = self.data[0]; + let length = self.data[1] as usize; + if self.data.len() < length { + debug!( + "LCP option overflow: type {} available {}, length {}", + option_type, + self.data.len(), + length + ); + self.data = &[]; + return Some(Err("LCP option overflow".into())); + } + let data = &self.data[2..length]; + self.data = &self.data[length..]; + Some(LcpOptionData::from_data(option_type, data)) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum LcpOptionData<'a> { + Reserved(), + MaximumReceiveUnit(u16), + AuthenticationProtocol(&'a [u8]), + QualityProtocol(&'a [u8]), + MagicNumber(u32), + ProtocolFieldCompression(), + AddressControlFieldCompression(), + Unknown(u8, &'a [u8]), +} + +impl LcpOptionData<'_> { + fn from_data(option_type: u8, data: &[u8]) -> Result { + let data = match option_type { + 0 => LcpOptionData::Reserved(), + 1 => { + if data.len() != 2 { + return Err("Unexpected Maximum Receive Unit length".into()); + } + let mut mru = [0u8; 2]; + mru.copy_from_slice(data); + LcpOptionData::MaximumReceiveUnit(u16::from_be_bytes(mru)) + } + 3 => { + if data.len() < 2 { + return Err("Unexpected Authentication Protocol length".into()); + } + LcpOptionData::AuthenticationProtocol(data) + } + 4 => { + if data.len() < 2 { + return Err("Unexpected Quality Protocol length".into()); + } + LcpOptionData::QualityProtocol(data) + } + 5 => { + if data.len() != 4 { + return Err("Unexpected Magic Number length".into()); + } + let mut magic = [0u8; 4]; + magic.copy_from_slice(data); + LcpOptionData::MagicNumber(u32::from_be_bytes(magic)) + } + 7 => { + if data.len() != 0 { + return Err("Unexpected Protocol Field Compression length".into()); + } + LcpOptionData::ProtocolFieldCompression() + } + 8 => { + if data.len() != 0 { + return Err("Unexpected Address and Protocol Field Compression length".into()); + } + LcpOptionData::AddressControlFieldCompression() + } + _ => LcpOptionData::Unknown(option_type, data), + }; + Ok(data) + } + + pub fn option_type(&self) -> u8 { + match *self { + Self::Reserved() => 0, + Self::MaximumReceiveUnit(_) => 1, + Self::AuthenticationProtocol(_) => 3, + Self::QualityProtocol(_) => 4, + Self::MagicNumber(_) => 5, + Self::ProtocolFieldCompression() => 7, + Self::AddressControlFieldCompression() => 8, + Self::Unknown(option_type, _) => option_type, + } + } + + fn length(&self) -> usize { + 2 + match *self { + Self::Reserved() => 0, + Self::MaximumReceiveUnit(_) => 2, + Self::AuthenticationProtocol(data) => data.len(), + Self::QualityProtocol(data) => data.len(), + Self::MagicNumber(_) => 4, + Self::ProtocolFieldCompression() => 0, + Self::AddressControlFieldCompression() => 0, + Self::Unknown(_, data) => data.len(), + } + } + + fn encode(&self, dest: &mut [u8]) { + dest[0] = self.option_type(); + dest[1] = self.length() as u8; + let dest = &mut dest[2..]; + match *self { + Self::MaximumReceiveUnit(mru) => dest.copy_from_slice(&mru.to_be_bytes()), + Self::AuthenticationProtocol(data) => dest.copy_from_slice(data), + Self::QualityProtocol(data) => dest.copy_from_slice(data), + Self::MagicNumber(magic) => dest.copy_from_slice(&magic.to_be_bytes()), + Self::Unknown(_, data) => dest.copy_from_slice(data), + _ => {} + } + } +} + +impl fmt::Display for LcpOptionData<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::Reserved() => write!(f, "Reserved"), + Self::MaximumReceiveUnit(mru) => write!(f, "Maximum-Receive-Unit {}", mru), + Self::AuthenticationProtocol(data) => { + write!(f, "Authentication-Protocol ")?; + fmt_slice_hex(data, f) + } + Self::QualityProtocol(data) => { + write!(f, "Quality-Protocol ")?; + fmt_slice_hex(data, f) + } + Self::MagicNumber(magic) => write!(f, "Magic-Number {:08x}", magic), + Self::ProtocolFieldCompression() => write!(f, "Protocol-Field-Compression"), + Self::AddressControlFieldCompression() => { + write!(f, "Address-and-Control-Field-Compression") + } + Self::Unknown(option_type, data) => { + write!(f, "Unknown option type {} data: ", option_type)?; + fmt_slice_hex(data, f) + } + } + } +} + +pub struct IpcpPacket<'a> { + code: LcpCode, + identifier: u8, + data: &'a [u8], +} + +impl IpcpPacket<'_> { + fn from_bytes(data: &[u8]) -> Result { + if data.len() < 4 { + debug!("Not enough data in IPCP packet"); + Err("Not enough in IPCP packet".into()) + } else { + let code = LcpCode::from_u8(data[0])?; + let identifier = data[1]; + let mut length = [0u8; 2]; + length.copy_from_slice(&data[2..4]); + let length = u16::from_be_bytes(length) as usize; + if data.len() < length { + debug!( + "LCP data overflow: received {}, length {}", + data.len(), + length + ); + return Err("LCP data overflow".into()); + } + Ok(IpcpPacket { + code, + identifier, + data: &data[4..length], + }) + } + } + + pub fn code(&self) -> LcpCode { + self.code + } + + pub fn identifier(&self) -> u8 { + self.identifier + } + + pub fn read_options(&self) -> &[u8] { + self.data + } + + pub fn iter_options(&self) -> IpcpOptionsIter<'_> { + IpcpOptionsIter { data: self.data } + } +} + +pub struct IpcpOptionsIter<'a> { + data: &'a [u8], +} + +impl<'a> Iterator for IpcpOptionsIter<'a> { + type Item = Result, FormatError>; + + fn next(&mut self) -> Option { + if self.data.is_empty() { + return None; + } + if self.data.len() < 2 { + debug!("Not enough data in IPCP Configuration Option"); + self.data = &[]; + return Some(Err("Not enough data in IPCP configuration option".into())); + } + let option_type = self.data[0]; + let length = self.data[1] as usize; + if self.data.len() < length { + debug!( + "IPCP option overflow: type {} available {}, length {}", + option_type, + self.data.len(), + length + ); + self.data = &[]; + return Some(Err("IPCP option overflow".into())); + } + let data = &self.data[2..length]; + self.data = &self.data[length..]; + Some(IpcpOptionData::from_data(option_type, data)) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum IpcpOptionData<'a> { + IpAddresses(), + IpCompressionProtocol(&'a [u8]), + IpAddress(Ipv4Addr), + PrimaryDns(Ipv4Addr), + PrimaryNbns(Ipv4Addr), + SecondaryDns(Ipv4Addr), + SecondaryNbns(Ipv4Addr), +} + +impl IpcpOptionData<'_> { + fn from_data(option_type: u8, data: &[u8]) -> Result { + let data = match option_type { + 1 => IpcpOptionData::IpAddresses(), + 2 => { + if data.len() < 2 { + return Err("Unexpected IP Compression Protocol length".into()); + } + IpcpOptionData::IpCompressionProtocol(data) + } + 3 => { + if data.len() != 4 { + return Err("Unexpected IP Address length".into()); + } + let mut ip = [0u8; 4]; + ip.copy_from_slice(data); + let ip = Ipv4Addr::from(ip); + IpcpOptionData::IpAddress(ip) + } + 129 => { + if data.len() != 4 { + return Err("Unexpected Primary DNS Server Address length".into()); + } + let mut ip = [0u8; 4]; + ip.copy_from_slice(data); + let ip = Ipv4Addr::from(ip); + IpcpOptionData::PrimaryDns(ip) + } + 130 => { + if data.len() != 4 { + return Err("Unexpected Primary NBNS Server Address length".into()); + } + let mut ip = [0u8; 4]; + ip.copy_from_slice(data); + let ip = Ipv4Addr::from(ip); + IpcpOptionData::PrimaryNbns(ip) + } + 131 => { + if data.len() != 4 { + return Err("Unexpected Secondary DNS Server Address length".into()); + } + let mut ip = [0u8; 4]; + ip.copy_from_slice(data); + let ip = Ipv4Addr::from(ip); + IpcpOptionData::SecondaryDns(ip) + } + 132 => { + if data.len() != 4 { + return Err("Unexpected Secondary NBNS Server Address length".into()); + } + let mut ip = [0u8; 4]; + ip.copy_from_slice(data); + let ip = Ipv4Addr::from(ip); + IpcpOptionData::SecondaryNbns(ip) + } + _ => return Err("Unexpected IPCP option type".into()), + }; + Ok(data) + } + + pub fn option_type(&self) -> u8 { + match *self { + Self::IpAddresses() => 1, + Self::IpCompressionProtocol(_) => 2, + Self::IpAddress(_) => 3, + Self::PrimaryDns(_) => 129, + Self::PrimaryNbns(_) => 130, + Self::SecondaryDns(_) => 131, + Self::SecondaryNbns(_) => 132, + } + } + + fn length(&self) -> usize { + 2 + match *self { + Self::IpAddresses() => 0, + Self::IpCompressionProtocol(data) => data.len(), + Self::IpAddress(ip) => ip.octets().len(), + Self::PrimaryDns(ip) => ip.octets().len(), + Self::PrimaryNbns(ip) => ip.octets().len(), + Self::SecondaryDns(ip) => ip.octets().len(), + Self::SecondaryNbns(ip) => ip.octets().len(), + } + } + + fn encode(&self, dest: &mut [u8]) { + dest[0] = self.option_type(); + dest[1] = self.length() as u8; + let dest = &mut dest[2..]; + match *self { + Self::IpCompressionProtocol(data) => dest.copy_from_slice(&data), + Self::IpAddress(ip) => dest.copy_from_slice(&ip.octets()), + Self::PrimaryDns(ip) => dest.copy_from_slice(&ip.octets()), + Self::PrimaryNbns(ip) => dest.copy_from_slice(&ip.octets()), + Self::SecondaryDns(ip) => dest.copy_from_slice(&ip.octets()), + Self::SecondaryNbns(ip) => dest.copy_from_slice(&ip.octets()), + _ => {} + } + } +} + +impl fmt::Display for IpcpOptionData<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::IpAddresses() => write!(f, "IP-Addresses"), + Self::IpCompressionProtocol(data) => { + write!(f, "IP-Compression-Protocol ")?; + fmt_slice_hex(data, f) + } + Self::IpAddress(ip) => write!(f, "IP-Address {}", ip), + Self::PrimaryDns(ip) => write!(f, "Primary DNS Server Address {}", ip), + Self::PrimaryNbns(ip) => write!(f, "Primary NBNS Server Address {}", ip), + Self::SecondaryDns(ip) => write!(f, "Secondary DNS Server Address {}", ip), + Self::SecondaryNbns(ip) => write!(f, "Secondary NBNS Server Address {}", ip), + } + } +} + +pub fn encode_lcp_config( + dest: &mut [u8], + code: LcpCode, + identifier: u8, + options: &[LcpOptionData], +) -> Result { + let length = 4 + options.iter().map(|opt| opt.length()).sum::(); + if dest.len() < length { + return Err(NotEnoughSpaceError {}); + } + dest[0] = code.0; + dest[1] = identifier; + dest[2..4].copy_from_slice(&(length as u16).to_be_bytes()); + let mut dest = &mut dest[4..]; + for opt in options.iter() { + let length = opt.length(); + opt.encode(&mut dest[..length]); + dest = &mut dest[length..]; + } + Ok(length) +} + +pub fn encode_lcp_data( + dest: &mut [u8], + code: LcpCode, + identifier: u8, + data: &[u8], +) -> Result { + let length = 4 + data.len(); + if dest.len() < length { + return Err(NotEnoughSpaceError {}); + } + dest[0] = code.0; + dest[1] = identifier; + dest[2..4].copy_from_slice(&(length as u16).to_be_bytes()); + dest[4..length].copy_from_slice(data); + Ok(length) +} + +pub fn encode_ipcp_config( + dest: &mut [u8], + code: LcpCode, + identifier: u8, + options: &[IpcpOptionData], +) -> Result { + let length = 4 + options.iter().map(|opt| opt.length()).sum::(); + if dest.len() < length { + return Err(NotEnoughSpaceError {}); + } + dest[0] = code.0; + dest[1] = identifier; + dest[2..4].copy_from_slice(&(length as u16).to_be_bytes()); + let mut dest = &mut dest[4..]; + for opt in options.iter() { + let length = opt.length(); + opt.encode(&mut dest[..length]); + dest = &mut dest[length..]; + } + Ok(length) +} + +pub fn fmt_slice_hex(data: &[u8], f: &mut dyn std::fmt::Write) -> std::fmt::Result { + for (i, b) in data.iter().enumerate() { + write!(f, "{:02x}", b)?; + if i + 1 < data.len() { + write!(f, " ")?; + } + } + Ok(()) +} + +impl fmt::Display for Packet<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Protocol: {}", self.read_protocol())?; + if let Ok(lcp) = self.to_lcp() { + write!(f, ", LCP code: {} id: {}", lcp.code(), lcp.identifier())?; + for opt in lcp.iter_options() { + match opt { + Ok(opt) => write!(f, " {}", opt)?, + Err(err) => write!(f, " invalid option: {}", err)?, + } + } + if let Some(magic) = lcp.read_magic() { + write!(f, ", magic: {:08x}", magic)?; + } + Ok(()) + } else if let Ok(lcp) = self.to_ipcp() { + write!(f, ", IPCP code: {} id: {}", lcp.code(), lcp.identifier())?; + for opt in lcp.iter_options() { + match opt { + Ok(opt) => write!(f, " {}", opt)?, + Err(err) => write!(f, " invalid option: {}", err)?, + } + } + Ok(()) + } else { + write!(f, ", data: ")?; + fmt_slice_hex(self.data, f) + } + } +} + +impl fmt::Debug for Packet<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +pub struct FormatError { + msg: &'static str, +} + +impl fmt::Display for FormatError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.msg.fmt(f) + } +} + +impl fmt::Debug for FormatError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl error::Error for FormatError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(self) + } +} + +impl From<&'static str> for FormatError { + fn from(msg: &'static str) -> FormatError { + FormatError { msg } + } +} + +pub struct NotEnoughSpaceError {} + +impl fmt::Display for NotEnoughSpaceError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Not enough space in buffer") + } +} + +impl fmt::Debug for NotEnoughSpaceError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl error::Error for NotEnoughSpaceError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(self) + } +}