Python/iptables:捕获所有UDP包及其原始目的地

2024-04-29 15:44:28 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试编写一个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重定向之前的主机/端口。

google找到了this question,答案是两种方法:TPROXYSO_ORIGINAL_DST,推荐前者,所以这就是我试图采用的方法。

TPROXY添加了iptables规则:

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

tproxy.txt读取,我们需要使用IP_TRANSPARENT选项创建一个监听套接字(这是以根用户身份完成的):

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数据包重定向到本地套接字以获取目标信息)?

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


Tags: 方法fromip信息iptables目标规则sudo
3条回答

我觉得你的问题很有趣。

以下解决方案基于标记主机生成的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));

你现在应该可以从插座上读了。 Tip form Etienne Perot:要接受所有UDP通信,请绑定到0.0.0.0。

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

希望这有帮助。

你能控制主人吗?如果是,您可以使用Open vSwitch来编写一个规则,该规则只覆盖所讨论的流,将所有IP信息保留到tap端口(然后将侦听器绑定到tap端口)。

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

您可以使用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)

完整的示例可以找到here

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

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

编辑1 来自here的注释:

The difference between a tap interface and a tun interface is that a tap interface outputs (and must be given) full ethernet frames, while a tun interface outputs (and must be given) raw IP packets (and no ethernet headers are added by the kernel). Whether an interface functions like a tun interface or like a tap interface is specified with a flag when the interface is created.

要查找数据包头信息(如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

相关问题 更多 >