[英]Python/iptables: Capturing all UDP packets and their original destination
我正在嘗試編寫一個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 header。
然而,事實證明,收到的數據包似乎是針對localhost:15000
尋址的。 這是有道理的,因為那是套接字所在的位置,但這不是我想要的; 我想要在數據包被iptables
重定向之前的主機/端口。
谷歌搜索導致了這個問題,答案提出了兩種方法: TPROXY
和SO_ORIGINAL_DST
,推薦前者,這就是我嘗試使用的 go。
為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
選項的監聽套接字(這是以 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
規則接收數據的代碼有問題嗎?要么
編輯:我應該堅持我想重定向(因此攔截)數據包,而不僅僅是在它們 go 通過時檢查它們。
我發現你的問題很有趣。
以下解決方案是基於標記主機產生的 UDP 流量並將其重新路由回本地主機應用程序。 在應用程序中,應該使用一個 UDP 套接字來讀取數據,即使不是指定給主機本身的套接字(見下文)。
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
#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 和路由規則進行分類和重新路由。
希望這可以幫助。
你可以使用 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”或打印的 .netrface 名稱。
linux 發行版不需要安裝任何東西,但是如果你在 windows 上使用這個,對於 mac os 使用這個
編輯 1 從 這里注意:
tap 接口和 tun 接口之間的區別在於 tap 接口輸出(並且必須提供)完整的 ethe.net 幀,而 tun 接口輸出(並且必須提供)原始 IP 數據包(並且沒有 ethe.net 標頭)由內核添加)。 在創建接口時,接口的功能是像 tun 接口還是像 tap 接口一樣用標志指定。
要查找數據包 header 信息(如 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
你控制主機嗎? 如果是這樣,您可以使用Open vSwitch編寫一個僅跨越相關流的規則,將所有 IP 信息保留到 tap 端口(然后將偵聽器綁定到 tap 端口)。
(OVS 可以做各種更復雜的事情,但這是一個相對簡單的任務)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.