diff --git a/CHANGELOG.md b/CHANGELOG.md index c19e49db..e6f6bbe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## Unreleased: mitmproxy_rs next +- tun mode: disable rp_filter, remove debug logging. ## 28 October 2024: mitmproxy_rs 0.10.6 diff --git a/src/messages.rs b/src/messages.rs index 36687907..d48b8eca 100755 --- a/src/messages.rs +++ b/src/messages.rs @@ -121,23 +121,21 @@ pub enum SmolPacket { impl fmt::Debug for SmolPacket { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match TryInto::::try_into(self.clone()) { - Ok(p) => { - f.debug_struct("SmolPacket") - .field("src", &p.src()) - .field("dst", &p.dst()) - .field("protocol", &p.protocol()) - .field("tcp_flags_str", &p.tcp_flag_str()) - .finish() - } - Err(_) => { - f.debug_struct("SmolPacket") - .field("src_ip", &self.src_ip()) - .field("dst_ip", &self.dst_ip()) - .field("transport_protocol", &self.transport_protocol()) - .finish() - } + Ok(p) => f + .debug_struct("SmolPacket") + .field("src", &p.src()) + .field("dst", &p.dst()) + .field("protocol", &p.protocol()) + .field("tcp_flags_str", &p.tcp_flag_str()) + .field("payload", &String::from_utf8_lossy(p.payload())) + .finish(), + Err(_) => f + .debug_struct("SmolPacket") + .field("src_ip", &self.src_ip()) + .field("dst_ip", &self.dst_ip()) + .field("transport_protocol", &self.transport_protocol()) + .finish(), } } } diff --git a/src/packet_sources/tun.rs b/src/packet_sources/tun.rs index f4cfc538..22156c96 100644 --- a/src/packet_sources/tun.rs +++ b/src/packet_sources/tun.rs @@ -1,10 +1,11 @@ -use anyhow::{Context, Result}; -use internet_packet::InternetPacket; use crate::messages::{ NetworkCommand, NetworkEvent, SmolPacket, TransportCommand, TransportEvent, TunnelInfo, }; use crate::network::{add_network_layer, MAX_PACKET_SIZE}; use crate::packet_sources::{PacketSourceConf, PacketSourceTask}; +use anyhow::{Context, Result}; +use std::cmp::max; +use std::fs; use tokio::sync::mpsc::{Permit, Receiver, UnboundedReceiver}; use tokio::sync::{broadcast, mpsc::Sender}; use tun2::AbstractDevice; @@ -41,6 +42,16 @@ impl PacketSourceConf for TunConf { let device = tun2::create_as_async(&config).context("Failed to create TUN device")?; let tun_name = device.tun_name().context("Failed to get TUN name")?; + if let Err(e) = disable_rp_filter(&tun_name) { + log::error!("failed to set rp_filter: {e}"); + } + if let Err(e) = fs::write( + format!("/proc/sys/net/ipv4/conf/{tun_name}/route_localnet"), + "1", + ) { + log::error!("Failed to enable route_localnet: {e}"); + } + let (network_task_handle, net_tx, net_rx) = add_network_layer(transport_events_tx, transport_commands_rx, shutdown)?; @@ -92,7 +103,6 @@ impl PacketSourceTask for TunTask { log::error!("Skipping invalid packet from tun interface: {:?}", &buf[..len]); continue; }; - dbg!(&packet); permit.take().unwrap().send(NetworkEvent::ReceivePacket { packet, tunnel_info: TunnelInfo::None, @@ -109,20 +119,7 @@ impl PacketSourceTask for TunTask { Some(command) = self.net_rx.recv(), if packet_to_send.is_empty() => { match command { NetworkCommand::SendPacket(packet) => { - dbg!(&packet); - // FIXME: Speculative checksum fix - match TryInto::::try_into(packet.clone()) { - Ok(mut p) => { - p.recalculate_tcp_checksum(); - p.recalculate_udp_checksum(); - p.recalculate_ip_checksum(); - dbg!("checksum fixed"); - packet_to_send = p.inner(); - } - Err(_) => { - packet_to_send = packet.into_inner(); - } - } + packet_to_send = packet.into_inner(); } } } @@ -132,3 +129,46 @@ impl PacketSourceTask for TunTask { Ok(()) } } + +/// Disable reverse path filtering for our tun interface. +/// This is necessary so that the kernel does not drop our injected packets. +fn disable_rp_filter(tun_name: &str) -> Result<()> { + fs::write(format!("/proc/sys/net/ipv4/conf/{tun_name}/rp_filter"), "0") + .context("failed to disable rp_filter on the interface")?; + + // The max value from conf/{all,interface}/rp_filter is used + // when doing source validation on the {interface}. + // So we do a relatively elaborate dance where we upgrade all interfaces to max(all, if) + // so that we can safely downgrade out interface. + + let all_rp_filter = fs::read_to_string("/proc/sys/net/ipv4/conf/all/rp_filter") + .context("failed to read /proc/sys/net/ipv4/conf/all/rp_filter")?; + if all_rp_filter == "0" { + return Ok(()); + } + + let paths = fs::read_dir("/proc/sys/net/ipv4/conf") + .context("failed to read /proc/sys/net/ipv4/conf")?; + for dir_entry in paths { + let mut path = dir_entry + .context("failed to iterate /proc/sys/net/ipv4/conf")? + .path(); + if path.ends_with(tun_name) { + continue; + } + + path.push("rp_filter"); + let interface_rp_filter = fs::read_to_string(&path).unwrap_or_default(); + let combined = max(&all_rp_filter, &interface_rp_filter); + if *combined != interface_rp_filter { + fs::write(&path, combined) + .with_context(|| format!("failed to set {}", path.display()))?; + } + } + + // We've successfully upgraded all individual interfaces, so we can now downgrade `all`. + fs::write("/proc/sys/net/ipv4/conf/all/rp_filter", "0") + .context("failed to disable /proc/sys/net/ipv4/conf/all/rp_filter")?; + log::debug!("Successfully updated rp_filter."); + Ok(()) +}