Python/iptables: захват всех UDP-пакетов и их исходного назначения
Я пытаюсь написать правило 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
И это здорово, теперь я могу получить все исходящие UDP-пакеты, используя сокет на порту 15000.
Теперь мне нужна информация о назначении (целевой хост и номер порта), поэтому достаточно простого сокета UDP; нужен сырой сокет, чтобы он получил полный IP-заголовок.
Однако, как оказалось, полученные пакеты, кажется, адресованы для localhost:15000
. Это имеет смысл, потому что там, где есть сокет, но это не то, что я хочу; Я хочу, чтобы хост/порт перед тем, как пакет был перенаправлен на iptables
.
Googling привел к этому вопросу, с ответом, предлагающим два подхода: 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
Хорошо, теперь давайте напишем еще один script, чтобы создать тестовый пакет, чтобы узнать, что произойдет:
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.connect(('192.168.1.1', 9001))
s.send('hello')
Но на принимающей стороне ничего не происходит. Похоже, что вызов recv
зависает, не получая никаких данных.
Итак, общий вопрос заключается в следующем:
- Что-то не так в коде для получения данных из правила
TPROXY
?
или
- Есть ли другой способ достичь этого (перенаправить все исходящие UDP-пакеты в локальный сокет с целью получения информации о назначении)?
Изменить. Я должен настаивать на том, чтобы я хотел перенаправить (следовательно, перехватить) пакеты, а не просто проверять их по мере их прохождения.
Ответы
Ответ 1
Я нашел ваш вопрос интересным.
Следующее решение основано на маркировке трафика 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 и правил маршрута.
Надеюсь, что это поможет.
Ответ 2
вы можете использовать устройство 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)
полный пример можно найти здесь
и направьте ваши пакеты на интерфейс "tun0" или напечатанное имя inetrface.
Linux-дистрибутивы не требуют ничего устанавливать, но если вы используете Windows, this, а для mac os используйте this
ИЗМЕНИТЬ 1
Примечание от здесь:
Разница между интерфейсом крана и интерфейсом туннеля заключается в том, что интерфейс крана выводит (и должен быть предоставлен) полные кадры ethernet, в то время как интерфейс tun выводит (и должен быть предоставлен) необработанные IP-пакеты (и не добавляются заголовки Ethernet по ядру). Независимо от того, какой интерфейс функционирует как интерфейс tun или как интерфейс перехода, с флагом указывается при создании интерфейса.
для поиска информации заголовка пакета (например, 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
Ответ 3
Вы управляете хостом? Если это так, вы можете использовать "Открыть vSwitch" , чтобы написать правило, которое охватывает только данный поток, сохраняя всю информацию о IP-адресе в порт-кран (а затем просто привяжите прослушиватель к порту крана).
(OVS может делать все гораздо более сложные вещи, но это относительно простая задача)