Skip to content

Commit

Permalink
tun: disable rp_filter
Browse files Browse the repository at this point in the history
  • Loading branch information
mhils committed Oct 28, 2024
1 parent f9ae51e commit 519147a
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Unreleased: mitmproxy_rs next

- tun mode: disable rp_filter, remove debug logging.

## 28 October 2024: mitmproxy_rs 0.10.6

Expand Down
30 changes: 14 additions & 16 deletions src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,23 +121,21 @@ pub enum SmolPacket {

impl fmt::Debug for SmolPacket {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {

match TryInto::<InternetPacket>::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(),
}
}
}
Expand Down
74 changes: 57 additions & 17 deletions src/packet_sources/tun.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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)?;

Expand Down Expand Up @@ -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,
Expand All @@ -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::<InternetPacket>::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();
}
}
}
Expand All @@ -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(())
}

0 comments on commit 519147a

Please sign in to comment.