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 может делать все гораздо более сложные вещи, но это относительно простая задача)