Я ищу способ изучить только первый пакет вновь установленного TCP-соединения (то есть первого пакета с фактической полезной нагрузкой). Есть ли способ сделать это с помощью iptables? Соответствующие пакеты ESTABLISHED будут соответствовать всем пакетам соединения после рукопожатия, верно?
Вы можете достичь своей цели, используя (злоупотребляя) iptables
, чтобы быть более конкретным: connbytes
матч и NFQUEUE
цель. connbytes
позволяет сопоставить N-й пакет в соединении и NFQUEUE
это механизм для передачи пакетов, соответствующих правилу iptables, в программу пользовательского пространства. Более того: вам придется использовать какую-то программу, которая будет получать соответствующие пакеты от ядра и обрабатывать их.
Я предполагаю, что вы заинтересованы в захвате пакетов на стороне сервера (это можно изменить, если вы заинтересованы в захвате на стороне клиента). В этом случае нам нужно захватить 3-й входящий пакет для каждого соединения (т.е. первый входящий пакет после трехстороннего рукопожатия) и помещает пакет в очередь netfilter (в данном случае очередь # 1).
iptables -I INPUT -p tcp -m tcp --dport 12345 -m connbytes --connbytes-mode packets --connbytes-dir original --connbytes 3:3 -j NFQUEUE --queue-num 1
Как только пакет соответствует этому правилу, он будет передан программе пользовательского пространства, привязанной к очереди №1. Затем программа может проверить пакет и впоследствии решить принять его или отбросить.
Вам понадобится программа, которая будет получать пакеты в пользовательском пространстве, используя libnetfilter_queue
библиотека. Привязки для библиотеки доступны на разных языках. Ниже приводится пример программы, написанной на Python:
import struct
from netfilterqueue import NetfilterQueue
def ip_to_string(ip):
return ".".join(map(lambda n: str(ip>>n & 0xff), [24,16,8,0]))
def print_and_accept(pkt):
pl = pkt.get_payload()
src_ip = struct.unpack('>I', pl[12:16])[0]
tcp_offset = (struct.unpack('>B', pl[0:1])[0] & 0xf) * 4
tmp = struct.unpack('>B', pl[tcp_offset+12:tcp_offset+13])[0]
data_offset = ((tmp & 0xf0) >> 4) * 4
src_port = struct.unpack('>H', pl[tcp_offset+0:tcp_offset+2])[0]
data = pl[tcp_offset + data_offset:]
print 'from {}:{}, "{}"'.format(ip_to_string(src_ip), src_port, data)
pkt.accept()
nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
try:
nfqueue.run()
except KeyboardInterrupt:
print
Программа предполагает, что пакеты в очереди будут пакетами IPv4 TCP, и печатает исходную пару ip: порт и полезную нагрузку TCP пакета.
NFQUEUE
target: если программа пользовательского пространства, привязанная к очереди, зависает, дает сбой или медленно обрабатывает пакеты, они будут отброшены / зависнут, а служба, привязанная к указанному порту, станет недоступной. Вы можете пройти --queue-bypass
вариант для NFQUEUE
нацеливаться на ACCEPT
совпадающие пакеты, если никакая программа пользовательского пространства не привязана к указанной очереди: это должно помочь с проблемой сбоя программы, но не поможет с зависшей или медленной программой.Вы не можете делать то, что ищете, на складе iptables
. Вам нужно будет написать контрольный код уровня 7.
Альтернативой, если вы готовы смириться с некоторой пост-обработкой, было бы захват трафика с помощью tcpdump
в файлы PCAP, проанализируйте их на наличие пакетов, которые вы ищете, и выбросите остальные. Я знаю, что диссектор SSL и фильтр отображения Wireshark могут получить то, что вы ищете.
Если бы вы не возражали, что ваши снимки будут немного отставать от реального времени, вы могли бы легко использовать tcpdump
для захвата трафика во вращающиеся файлы PCAP. Вы можете запустить сценарий, который отслеживает завершенные файлы PCAP и анализирует их с помощью tshark
и фильтр отображения, позволяющий записывать в новые файлы PCAP только то, что вы ищете.
Соответствующие пакеты ESTABLISHED будут соответствовать всем пакетам соединения после рукопожатия, верно?
Правильно !
Не уверен, что вы имеете в виду под «захватом». iptables
это вещь, захват сетевых пакетов (tcpdump
) другое дело.
Предполагая, что я понимаю «регистрировать только новые соединения», вам нужно будет регистрировать только правила, соответствующие NEW
штат.
Давайте воспользуемся примером, когда мы хотим регистрировать только новые соединения для запроса icmp (ping):
# Define LOG settings
iptables -N LOG_ACCEPT
iptables -A LOG_ACCEPT -j LOG --log-prefix '[IPTABLES ACCEPT] :'
iptables -A LOG_ACCEPT -j ACCEPT
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state NEW -j LOG_ACCEPT
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state ESTABLISHED,RELATED -j ACCEPT
Здесь мы просто LOG
когда NEW
соединение установлено. Таким образом, мы можем отправить более 1000 ping (с того же исходного хоста), только первый пакет будет зарегистрирован:
Apr 4 21:28:17 UBN-1 kernel: [78512.613705] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=32571 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47088
Если вы положите LOG_ACCEPT
в ESTABLISHED,RELATED
правило:
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state NEW -j LOG_ACCEPT
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT
тогда каждый запрос ping будет регистрироваться. Таким образом, 1000+ ping (с того же исходного хоста) сгенерируют более 1000 записей в журнале:
Apr 4 21:33:49 UBN-1 kernel: [78844.130615] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=244 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47092
Apr 4 21:33:50 UBN-1 kernel: [78845.130551] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=247 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47093
Apr 4 21:33:51 UBN-1 kernel: [78846.131295] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=250 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47094
Apr 4 21:33:52 UBN-1 kernel: [78847.132160] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=252 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47095
......
......
Надеюсь, я понял вопрос!