12

我正在尝试编写一个iptables规则,将所有传出的 UDP 数据包重定向到本地套接字,但我还需要目标信息。我从

sudo iptables -t nat -A sshuttle-12300 -j RETURN   --dest 127.0.0.0/8 -p udp
sudo iptables -t nat -A sshuttle-12300 -j REDIRECT --dest 0.0.0.0/0   -p udp --to-ports 15000

太好了,现在我可以通过使用端口 15000 上的套接字来获取所有传出的 UDP 数据包。

现在,我需要目标信息(目标主机和端口号),所以一个简单的 UDP 套接字是不够的;需要一个原始套接字,以便获得完整的 IP 标头。

然而,事实证明,收到的数据包似乎是针对localhost:15000. 这是有道理的,因为那是套接字所在的位置,但这不是我想要的;我想要在数据包被重定向之前的主机/端口iptables

谷歌搜索导致了这个问题,答案提出了两种方法:TPROXY并且SO_ORIGINAL_DST,推荐前者,所以这就是我试图采用的方法。

添加了以下iptables规则TPROXY

sudo iptables -t mangle -A PREROUTING -j TPROXY --dest 0.0.0.0/0 -p udp --on-port 15000

tproxy.txt读取,我们需要创建一个带有IP_TRANSPARENT选项的监听套接字(这是作为 root 完成的):

from socket import *
s = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)
# The IP_TRANSPARENT option isn't defined in the socket module.
# Took the value (19) from the patch in http://bugs.python.org/issue12809
s.setsockopt(SOL_IP, 19, 1)
s.bind(('0.0.0.0', 15000))
s.recv(4096) # Will hang until it receives a packet

好的,现在让我们编写另一个脚本来生成一个测试包,看看是否有任何事情发生:

from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.connect(('192.168.1.1', 9001))
s.send('hello')

但随后接收方没有任何反应。recv通话似乎挂起,没有收到任何数据。

因此,总体问题是:

  • TPROXY规则接收数据的代码有问题吗?

或者

  • 是否有另一种方法来实现这一点(将所有传出的 UDP 数据包重定向到本地套接字以获取目标信息)?

编辑:我应该坚持我想重定向(因此拦截)数据包,而不仅仅是在它们通过时检查它们。

4

3 回答 3

10

我发现你的问题很有趣。

以下解决方案基于标记主机生成的 UDP 流量并将其重新路由回本地主机应用程序。在应用程序中,应该使用 UDP 套接字来读取数据,即使是不是发往主机本身的数据(见下文如何)。

网络设置:

  • 标记退出主机的 UDP 流量
  • 标记为 1 的流量,传递到路由表 100 进行处理
  • 将流量路由到应用程序
iptables -A OUTPUT -t mangle -p udp -j MARK --set-mark 1
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

套接字设置:

  • 创建 UDP 套接字(常规)
  • 为非本地地址启用绑定/读取
#ifndef IP_TRANSPARENT
#define IP_TRANSPARENT 19
#endif

int val = 1; 
setsockopt(sockfd, SOL_IP, IP_TRANSPARENT, &val, sizeof(val));

您现在应该可以从套接字读取。来自 Etienne Perot 的提示:要接受所有 UDP 流量,请绑定到 0.0.0.0。

我在这里发现非常有趣的是,本地生成的流量(而不是路由流量)可以使用 iptables 和路由规则进行分类和重新路由。

希望这可以帮助。

于 2012-04-10T07:25:40.467 回答
4

您可以使用 tun/tap 设备,可以简单地从 python 中读取它,例如:

隧道.py

import os
from fcntl import ioctl
from select import select
import struct
import subprocess

TUNSETIFF = 0x400454ca
TUNMODE = 0x0001

tunFile = os.open("/dev/net/tun", os.O_RDWR)
ifs = ioctl(f, TUNSETIFF, struct.pack("16sH", "tun%d", TUNMODE))
ifname = ifs[:16].strip("\x00")
print "Allocated interface %s. Configure it and use it" % ifname
subprocess.call("ifconfig %s 192.168.13.1" % ifname,shell=True)
# Reading
def read():
    r = select([tunFile], [], [])[0][0]
    if r == tunFile:
        return os.read(tunFile, 1600)
    return None

# Writing
def write(buf):
    os.write(tunFile, buf)

完整的例子可以在这里找到

并将您的数据包路由到接口“tun0”或打印的 inetrface 名称。

linux发行版不需要安装任何东西,但如果你在windows上使用这个,对于mac os使用这个

编辑 1注意

tap 接口和 tun 接口之间的区别在于,tap 接口输出(并且必须给出)完整的以太网帧,而 tun 接口输出(并且必须给出)原始 IP 数据包(并且内核不添加以太网头) )。接口的功能是像 tun 接口还是像 tap 接口,在创建接口时用标志指定。

要查找数据包头信息(如 src、dst 等...),您可以使用dpkt

from dpkt import ip 
from tunnel import read,write # tunnel.py
while 1:
    data = read()
    if data:
        ipObj = ip.IP(data[4:])
        print ipObj.src, ipObj.dst
于 2012-04-08T19:36:56.187 回答
1

你控制主机?如果是这样,您可以使用Open vSwitch编写一个仅涵盖相关流的规则,将所有 IP 信息保留到分接端口(然后只需将侦听器绑定到分接端口)。

(OVS 可以做各种复杂得多的事情,但这是一个相对简单的任务)

于 2012-04-06T04:37:46.213 回答