簡體   English   中英

Python/iptables: 捕獲所有 UDP 個數據包及其原始目的地

[英]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重定向之前的主機/端口。

谷歌搜索導致了這個問題,答案提出了兩種方法: TPROXYSO_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規則接收數據的代碼有問題嗎?

要么

  • 有沒有另一種方法來實現這一點(將所有傳出的 UDP 數據包重定向到本地套接字,以獲取目標信息)?

編輯:我應該堅持我想重定向(因此攔截)數據包,而不僅僅是在它們 go 通過時檢查它們。

我發現你的問題很有趣。

以下解決方案是基於標記主機產生的 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 和路由規則進行分類和重新路由。

希望這可以幫助。

你可以使用 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM