Python / iptables:捕获所有UDP数据包及其原始目标

时间:2012-04-06 03:16:50

标签: python sockets udp iptables packet-capture

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

谷歌搜索导致this question,答案提示两种方法:TPROXYSO_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数据包重定向到本地套接字)?

修改:我应该坚持要重定向(因此拦截)数据包,而不仅仅是检查它们。

3 个答案:

答案 0 :(得分:9)

我发现你的问题很有趣。

以下解决方案基于标记主机生成的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和路由规则对本地生成的流量(而不是路由流量)进行分类和重新路由。

希望这有帮助。

答案 1 :(得分:4)

你可以使用tun / tap设备,只需从python中读取它,例如:

tunnel.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”或打印的非界面名称。

linux发行版不需要安装任何东西,但是如果你在Windows上使用this,并且对于mac os使用this

编辑1 来自here的注释:

  

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

用于查找包头信息(如src,dst& etc ......),您可以使用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

答案 2 :(得分:1)

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

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