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.

гуглить привело к этот вопрос, С ответом, предлагающим два подхода: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

хорошо, теперь давайте напишем другой сценарий для создания тестового пакета, чтобы увидеть, если что-нибудь произойдет:

from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.connect(('192.168.1.1', 9001))
s.send('hello')

но потом ничего не происходит на принимающей стороне. The recv вызов, кажется, зависает, не получая никаких данных.

Итак, общий вопрос либо:

  • есть ли что-то неправильное в коде для получения данных от TPROXY правило?

или

  • есть ли другой способ достичь этого (перенаправить все исходящие UDP-пакеты в локальный сокет, чтобы получить информацию о цели)?

редактировать: я должен настаивать, что я хотел бы редирект (поэтому перехватывайте) пакеты, а не просто проверяйте их по мере их прохождения через.

3 ответов


Я нашел ваш вопрос интересным.

следующее решение основано на маркировке 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));

теперь вы должны иметь возможность читать из сокета. Совет форма Этьен перо: для принятия всего UDP-трафика, привязка к 0.0.0.0.

то, что я нашел здесь очень интересно, является ли локально генерируемый трафик (а не маршрутизируемый) может быть классифицирован и перенаправлен с использованием iptables и правил маршрута.

надеюсь, что это помогает.


вы можете использовать устройство 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, используйте этой, а для mac os используйте этой

изменить 1 Примечание здесь:

разницу между активным интерфейсом и tun-интерфейс это интерфейс крана выходы (и должны) полный Ethernet-кадры, а tun-интерфейс выходы (и должны) сырые IP пакеты (и нет Ethernet заголовки добавляются к ядру). Ли интерфейс функционирует как интерфейс tun или как интерфейс tap указывается с флагом при создании интерфейса.

для поиска в заголовках пакетов информация(например, 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

вы контролируете хозяин? Если это так, вы можете использовать открыть vSwitch чтобы написать правило, которое охватывает только рассматриваемый поток, сохраняя всю информацию IP в порту крана (а затем просто привязать прослушиватель к порту крана).

(ОВС может делать всевозможные гораздо более сложные вещи, но это относительно простая задача)