You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust UDP透明代理:TPROXY无法接收全部待重定向流量的问题

Rust UDP透明代理TPROXY配置问题

使用的依赖crate

  • socket2:用于创建RAW socket
  • nix::sys::socket:用于调用底层socket API
  • std::net::socket:用于标准socket操作

系统基础配置

代理机器已设为目标设备网关,且启用Linux内核转发能力,配置脚本如下:

#!/bin/sh

# Redirect packets coming to the computer
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1

# Disable ICMP redirection
sysctl -w net.ipv4.conf.all.send_redirects=0

NFTABLES流量重定向规则

配置规则将流量重定向到代理程序,脚本如下:

#!/bin/sh

# Setting up NFTABLES

nft add table filter
nft add chain filter divert "{ type filter hook prerouting priority -150; }"
nft add rule filter divert meta l4proto udp socket transparent 1 meta mark set 1 accept

#nft add table ip nat
#nft -- add chain ip nat prerouting { type nat hook prerouting priority -100 \; }

# Creating a new rule that redirects UDP traffic from P1-P2 to port 35

nft add rule filter divert udp dport 80-65525 tproxy to :35 meta mark set 1 accept

#nft add rule ip nat prerouting udp dport 80-65525 redirect to :35

问题现象

尝试过两种流量重定向方式:

  • NAT PREROUTING:代理监听35端口可接收全部客户端流量,但读取数据包时原目标地址会被覆盖为代理机器IP
  • TPROXY:仅能接收广播流量和目标MAC为代理机器的流量,无法获取所有客户端流量

程序相关实现

已遵循Linux内核tproxy手册配置,程序中设置了IP_TRANSPARENT等选项:

Socket配置代码

use socket2::{Socket, Domain, Type, Protocol};
use std::os::fd::{OwnedFd, RawFd, AsRawFd};
use nix::sys::socket::{
    ...
    sockopt::{IpTransparent, Ipv4OrigDstAddr, Ipv4PacketInfo},
    setsockopt,
    ...
};
...
let client_to_proxy_socket = match Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::UDP)) { ... };
let socket_ref: OwnedFd = client_to_proxy_socket.into();
let active: bool = true;
match setsockopt(&socket_ref, IpTransparent, &active) { ... };
match setsockopt(&socket_ref, Ipv4OrigDstAddr, &active) { ... };
match setsockopt(&socket_ref, Ipv4PacketInfo, &active) { ... };
...

recvmsg处理代码

let target_address: SockaddrIn = match recvmsg::<SockaddrIn>(*socket_clone, &mut iov, Some(&mut cmsg_space!(nix_msghdr)), flags) {
    Ok(t) => {
        let ctrl_msg: Vec<ControlMessageOwned> = t.cmsgs().collect();

        let mut res: SockaddrIn = SockaddrIn::new(0, 0, 0, 0, 0);

        for c in ctrl_msg {
            if let ControlMessageOwned::Ipv4OrigDstAddr(b) = c {
                let addr: [u8; 4] = convert_u32_to_4u8(b.sin_addr.s_addr);
                let port: u16 = b.sin_port;

                let new_port: u16 = ((port & (0xFF << 8)) >> 8) | ((port & 0xFF) << 8);

                res = SockaddrIn::new(addr[0], addr[1], addr[2], addr[3], new_port);
            }

            if let ControlMessageOwned::Ipv4PacketInfo(b) = c {
                debug!("IP_PKTINFO => {:?}", b);
            }
        }

        res
    },
    Err(e) => {
        error!("Could not extract the original destination from the socket ; Error => {}", e);

        std::process::exit(15);
    }
};

注:完整程序达400行,此处仅展示相关代码。

内容的提问来源于stack exchange,提问作者User582016482

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:57:19