如何在 Linux 中让所有流量通过一个接口

ily*_*rov 13 linux ip iptables route

我有一个自己编写的接口 tun0(基于TUN/TAP)输出它接收到的内容。
我需要系统的所有流量都流经这个接口。
接口的作用是:

  1. 找出可能被审查的数据包并通过隧道传输它们。
  2. 原封不动地通过所有其他交通。

正如您猜想的那样,我正在尝试构建一个反审查工具。
关于隧道的决定必须在 tun0 进程中进行,
因为只有在那里我们才能使用受信任的 DNS。

我需要你的帮助来告诉我如何让所有流量都通过一个自写的接口 tun0。如果 tun0 需要更改,我会要求您提供此类更改。

以下是我尝试使所有流量通过 tun0 并失败(ping 失败)的方法。

编译

  1. gcc tun0.c
  2. sudo ./a.out

配置

  1. sudo ip addr add 10.0.0.1/24 dev tun0
  2. 创建表约翰

    $ cat /etc/iproute2/rt_tables 
    #
    # reserved values
    #
    255     local
    254     main
    253     default
    0       unspec
    #
    # local
    #
    #1      inr.ruhep
    
    200 John
    
    Run Code Online (Sandbox Code Playgroud)

顺序很重要:

  1. sudo ip rule add from all lookup John
  2. sudo ip route add default dev tun0 table John
  3. sudo ip rule add iif tun0 lookup main priority 500

    $ ip rule
    0:      from all lookup local 
    500:    from all iif tun0 lookup main 
    32765:  from all lookup John 
    32766:  from all lookup main 
    35000:  from all lookup default 
    
    Run Code Online (Sandbox Code Playgroud)

故障排除

  1. sudo tcpdump -i wlp2s0 -qtln icmp然后ping -I tun0 8.8.8.8显示没有捕获到数据包,这意味着没有数据包通过iif tun0 lookup main规则从 tun0 传输到 wlp2s0 。

  2. 当我替换tun0lo无处不在时,它对我有用。

也试过

  1. 关闭反向路径过滤,rp_filter=0/etc/sysctl.conf

解答疑难解答

iptables -I FORWARD -j LOG --log-prefix "filter/FORWARD " 
iptables -t nat -I OUTPUT -j LOG --log-prefix "nat/OUTPUT " 
iptables -t nat -I PREROUTING -j LOG --log-prefix "nat/PREROUTING " 
iptables -t nat -I POSTROUTING -j LOG --log-prefix "nat/POSTROUTNG "
tail -f /var/log/syslog
Run Code Online (Sandbox Code Playgroud)

来自答案的修改来源也在这里

tif*_*oft 10

因此,在您的配置中,您尝试发送到网络的所有数据包最初来自10.0.0.1(因为它们通过tun0接口并且其本地地址是10.0.0.1)。您捕获数据包,到目前为止一切都很好。
现在,tun0进一步发送数据包。源地址10.0.0.1并且您希望数据包通过不同的接口离开(wlp2s0在您的情况下)。那是路由,所以让我们先启用路由:

sysctl -w net.ipv4.ip_forward=1
Run Code Online (Sandbox Code Playgroud)

在此之后,如果你看tcpdumpwlp2s0,你可以看到的数据包离开与源地址10.0.0.1,而不是与WLAN接口的源地址(你期望我猜的)。所以我们需要改变源地址,它被称为源 NAT。在 linux 中,借助netfilter/iptables很容易:

iptables -t nat -A POSTROUTING -o wlp2s0 -s 10.0.0.1 -j MASQUERADE
Run Code Online (Sandbox Code Playgroud)

另请检查您的FORWARD连锁店是否有ACCEPT政策,否则您需要允许使用以下内容进行转发

iptables -A FORWARD -i tun0 -o wlp2s0 -s 10.0.0.1 -j ACCEPT
iptables -A FORWARD -i wlp2s0 -o tun0 -d 10.0.0.1 -j ACCEPT
Run Code Online (Sandbox Code Playgroud)

现在一切正常:Linux 内核执行路由,它将数据包从tun0接口移动到wlp2s0. netfilter应该将源 IP 更改10.0.0.1为您wlp2s0为输出数据包分配的接口地址。它记住所有连接,当回复数据包返回时(如果它们),它将wlp2s0接口分配地址的目标地址更改为10.0.0.1(“conntrack”功能)。
嗯,它应该但它没有。看起来,netfilter对这种复杂的路由配置以及同一个数据包首先通过OUTPUT链然后被路由并到达PREROUTING链的事实感到困惑。至少在 Debian 8 机器上它不起作用。
netfilter进行故障排除的最佳方法是以下TRACE功能:

modprobe ipt_LOG
iptables -t raw -A OUTPUT -p icmp -j TRACE
iptables -t raw -A PREROUTING -p icmp -j TRACE
Run Code Online (Sandbox Code Playgroud)

我只对ICMP包启用跟踪,您可以使用其他过滤器进行调试。
它将显示数据包经过哪些表和链。而且我可以看到数据包没有在FORWARD链上走得更远(并且它没有被nat/POSTROUTING实际执行的链捕获SNAT)。
以下是实现这项工作的几种方法。

方法#1

消除netfilter混淆的最佳方法是更改tun0.c应用程序中数据包的源 IP 地址。这也是最自然的方式。我们需要在向外的途中将 10.0.0.1 更改为 10.0.0.2,在返回的途中将 10.0.0.2 更改为 10.0.0.1
我已经修改tun0.c了源地址更改代码。这是新文件这是您的tun0.c. 对 IP 标头的更改还涉及校验和校正,因此我从OpenVPN 项目中获取了一些代码。这是我在干净重启和tun0_changeip.c启动后执行的命令的完整列表 :

ifconfig tun0 inet 10.0.0.1/30 up
sysctl -w net.ipv4.ip_forward=1
ip route add default dev tun0 table John
ip rule add from all lookup John
ip rule add from 10.0.0.2 lookup main priority 500
iptables -t nat -A POSTROUTING -o wlp2s0 -s 10.0.0.2 -j MASQUERADE
Run Code Online (Sandbox Code Playgroud)

请注意,在这种情况下您不需要关闭反向路径过滤,因为一切都是合法的 -tun0只接收和发送属于其子网的数据包。您也可以进行基于源的路由而不是基于接口的路由。

方法#2

可以SNAT在数据包到达tun0接口之前进行。不过也不是很正确。在这种情况下,您肯定需要关闭反向路径过滤

sysctl -w net.ipv4.conf.tun0.rp_filter=0
# It won't work without also changing the "all" value
sysctl -w net.ipv4.conf.all.rp_filter=0
Run Code Online (Sandbox Code Playgroud)

现在,执行SNAT: iptables -t nat -A POSTROUTING -o tun0 -s 10.0.0.1 -j SNAT --to-source ip.address.of.your.wlan.interface

在这里,我们在数据包到达设备之前更改源地址tun0tun0.c代码“按原样”重新发送这些数据包(具有更改的源地址),并且它们通过 wlan 接口成功路由。但是您可能在 wlan 接口上有一个动态 IP 并且想要使用MASQUERADE(为了不明确指定接口地址)。以下是您可以使用的方法MASQUERADE

iptables -t nat -A POSTROUTING -o tun0 -s 10.0.0.1 -j SNAT --to-source 10.0.55.1
iptables -t nat -A POSTROUTING -o wlp2s0 -s 10.0.55.1 -j MASQUERADE
Run Code Online (Sandbox Code Playgroud)

请注意“ 10.0.55.1” IP 地址 - 它是不同的。你可以在这里使用任何IP,没关系。如果我们之前更改源 IP,数据包会到达接口上的nat/POSTROUTINGwlp2s0。现在它不依赖于 wlan 接口的静态 IP。

方法#3

您也可以使用fwmark. 这样你就不需要了,SNAT但你只会捕获传出的数据包:
首先,我们需要禁用反向路径过滤tun0因为它会转发属于另一个网络的数据包:

sysctl -w net.ipv4.conf.tun0.rp_filter=0
# It won't work without also changing the "all" value
sysctl -w net.ipv4.conf.all.rp_filter=0

Now let's alter the routing rules a bit:
# Delete old rules
ip rule del iif tun0 lookup main
ip rule del from all lookup John

# Packets will start going from wlan interface so they will have source address of it
iptables -t mangle -A OUTPUT -o wlp2s0 -j MARK --set-mark 1
ip rule add fwmark 0x1 lookup John
Run Code Online (Sandbox Code Playgroud)

这是在我的 Debian 8 机器上工作的路由netfilter的另一个“黑客” ,但我仍然建议采用第一种方法,因为它更自然并且不使用任何黑客。


您还可以考虑将您的应用程序构建为透明代理。我认为比分析来自 tun 设备的数据包要容易得多。