Фильтрация методов HTTP в пакетах IPv6 с использованием tcpdump

Фильтрация методов HTTP в пакетах IPv6 с использованием tcpdump

Я использую "tcpdump" для захвата трафика и хочу фильтровать по методам HTTP. Когда у меня есть пакеты IPv4, я использую: tcpdump -s 0 -A 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'Для фильтрации пакетов HTTP GET.

К сожалению, tcp[]фильтр не работает с пакетами IPv6. Согласно документации

Арифметические выражения над заголовками транспортного уровня, такие как tcp[0], не работают с пакетами IPv6.

Поэтому я ищу другой способ фильтрации только HTTP-методов для трафика IPv6 с помощью tcpdump.

Я пытался найти решение, но, похоже, они включают использование grep, что не подходит для моих нужд, так как я хочу выводить весь отфильтрованный трафик непосредственно в файл pcap.

решение1

Ограничения tcpdumpIPv6 начиная с версии 4.99.x

Для IPv4, чтобы получить смещение полезной нагрузки в пакете, можно просто более или менее вычислить смещение полезной нагрузки, используяIHL IPv4+ Смещение данных TCPтакже учитывать параметры IPv4 и TCP. Это достаточно просто, чтобы включить вtcpdumpнепосредственно при генерации фильтра байт-кода BPF (некоторая документация BPF доступна, напримертам).

Для IPv6, вместо этого, может быть переменное (возможно, от 0 до 9) количество заголовков расширения между фиксированным заголовком и верхним заголовком. Это означало бы включение кода для, возможно, 10 случаев (0, 1, 2, ... 9 заголовков расширения) для поиска смещения полезной нагрузки в пакете, предполагая, что нет дополнительных особенностей для определенных случаев, или даже попытки иметь защиту от неправильно сформированных пакетов (что может привести к ложному срабатыванию): я могу только предположить, что это не было реализовано из-за сложности и нежелания предоставлять некорректную реализацию, игнорирующую такие заголовки.

Даже такой простой тест, как , ip6 and tcp dst port 80не учитывает заголовки расширений, как показано в сгенерированном коде BPF ниже:

$ tcpdump --version 
tcpdump version 4.99.3
libpcap version 1.10.3 (with TPACKET_V3)
OpenSSL 3.0.9 30 May 2023
$ tcpdump -y EN10MB -d ip6 and tcp dst port 80
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 7
(002) ldb      [20]
(003) jeq      #0x6             jt 4    jf 7
(004) ldh      [56]
(005) jeq      #0x50            jt 6    jf 7
(006) ret      #262144
(007) ret      #0

поэтому может пропустить трафик.

Похоже, что начиная с версии tcpdump 4.99.x единственное место, где реализована адекватная обработка заголовков расширения IPv6, задокументировано в конце страницы руководства:

ip6 protoдолжен отслеживать цепочку заголовков, но в данный момент этого не происходит. ip6 protochainпредоставляется для этого поведения.

Действительно, в настоящее время tcpdump 4.99.x ip6 and tcpэквивалентен ip6 proto 6(они создают тот же байт-код):

$ tcpdump -y EN10MB -d ip6 and tcp
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 8
(002) ldb      [20]
(003) jeq      #0x6             jt 7    jf 4
(004) jeq      #0x2c            jt 5    jf 8
(005) ldb      [54]
(006) jeq      #0x6             jt 7    jf 8
(007) ret      #262144
(008) ret      #0

в то время как ip6 protochain 6выполняет гораздо более тщательную проверку и является тем, чего следует ожидать в любом тесте, включающем верхний уровень (конечный транспортный заголовок), такой как TCP:

$ tcpdump -y EN10MB -d ip6 protochain 6
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 35
(002) ldb      [20]
(003) ldx      #0x28
(004) jeq      #0x6             jt 32   jf 5
(005) jeq      #0x3b            jt 32   jf 6
(006) jeq      #0x0             jt 10   jf 7
(007) jeq      #0x3c            jt 10   jf 8
(008) jeq      #0x2b            jt 10   jf 9
(009) jeq      #0x2c            jt 10   jf 19
(010) ldb      [x + 14]
(011) st       M[0]
(012) ldb      [x + 15]
(013) add      #1
(014) mul      #8
(015) add      x
(016) tax      
(017) ld       M[0]
(018) ja       4
(019) jeq      #0x33            jt 20   jf 32
(020) txa      
(021) ldb      [x + 14]
(022) st       M[0]
(023) txa      
(024) add      #1
(025) tax      
(026) ldb      [x + 14]
(027) add      #2
(028) mul      #4
(029) tax      
(030) ld       M[0]
(031) ja       4
(032) add      #0
(033) jeq      #0x6             jt 34   jf 35
(034) ret      #262144
(035) ret      #0

Выше 4 тестов, включая jt 10и строки между (10) и (018) ja 4цикл по 4 конкретным заголовкам расширения IPv6, чтобы пропустить их, в то время как (19)для (31)работы с IPSec AH. Я не знаю, как сгенерировать в другом месте такой код сtcpdumpязык (а не, скажем, прямойБПФcode). Использование ip6 protochain 6 and ip6 and tcp dst port 80просто начинает все с нуля или еще хуже, вместо того, чтобы извлечь выгоду из только что полученного заголовка TCP.

Аналогичная проблема описана в различных сетевых средствах и инструментах для Linux. Пример:tc u32рассказывает:

icmp_code VAL_MASK_8

Предположим, что используется протокол следующего заголовка icmp или ipv6-icmp и сопоставьте значения полей «Тип» или «Код».Это опасно, как предполагает код.минимальный размер заголовка для IPv4 иотсутствие заголовков расширения для IPv6.

Все это даже не учитывает, что конкретно для HTTP, когда соединение используется повторно, следующий HTTP-запрос может даже не находиться в начале границы пакета, или что такой запрос, встроенный в виде данных, а не запроса, может совпасть, если такие данные появятся в начале границы пакета.

Тест для случая 0 extension-header с использованием tcpdumpверсии 4.99.x

Вот тест с 0 заголовками расширения, т.е. на том же уровне, что иtcpdumpв настоящее время выполняется, с минимальной проверкой:

tcpdump -n -s 0 -A 'ip6[6] == 6 and ( ip6[4:2] - ((ip6[52] & 0xf0) >> 2) >= 4 ) and ip6[40 + ((ip6[52] & 0xf0) >> 2) :4] == 0x47455420'

что можно задокументировать следующим образом (по-прежнему только для случая заголовка расширения 0, где заголовок TCP всегда начинается с позиции 40):

  • Следующий заголовок — TCP

    ip6[6] == 6
    
  • IPv6Длина полезной нагрузки:

    ip6[4:2]
    
  • ТКПсмещение данных (фиксированный размер IPv6 40 + 12 = 52) (и соответствующая корректировка для получения байтов)

    (ip6[52] & 0xf0) >> 2
    
  • Длина полезной нагрузки TCP = длина полезной нагрузки IPv6 - смещение данных TCP

  • Проверьте, что имеется не менее 4 байт полезной нагрузки TCP для длины не менее "GET"

    ip6[4:2] - ((ip6[52] & 0xf0) >> 2)  >= 4
    
  • 4-байтовая строка "GET", закодированная в ASCII/UTF-8, может быть представлена ​​4-байтовым значением 0x47455420

  • Смещение полезной нагрузки TCP равно фиксированной длине заголовка IPv6 (40) + смещение данных TCP

    40 + ((ip6[52] & 0xf0) >> 2)
    
  • проверить, что первое 4-байтовое слово, начинающееся с фиксированной длины заголовка IPv6 (40) + смещение данных, равно значению для строки "GET"

    ip6[40 + ((ip6[52] & 0xf0) >> 2) :4] = 0x47455420
    

nftablesс ядром Linux >= 5.15.54 +tcpdump

Я предлагаю простой альтернативный метод, предназначенный только для Linux, с использованиемnftablesи требуя достаточно нового ядра (версия ядра Linux>= 5.15.54бэкпортировано из5.16) а такжеnftables>= 1.0.1для поддержки@ih(внутренний заголовок/полезная нагрузка) необработанная полезная нагрузка, а не просто @th(транспортный заголовок), потому чтоnftablesсам по себе слишком ограничен, чтобы выполнять произвольную обработку @thполезным образом.

Датаграмма IPv6 уже проанализирована, включая все заголовки расширений IPv6, и указатели @th и @in уже доступны дляnftables: не требуется дальнейшей обработки для получения всех случаев. Я полагаюсь наnftablesпровалить проверку, если пакет слишком короткий, без необходимости вычисления фактического размера полезной нагрузки TCP (в отличие отtcpdumpи БПФ,nftablesне умеет вычитать, поэтому нет возможности правильно проверить размер).

Thenftablesпример ниже пытается имитировать точный пример OP, но не всегда может во всех деталях без некоторой адаптации. Нет направления или порта, захватывает входящие пакеты до NAT и исходящие пакеты после NAT (отсюда и выбранные хуки и приоритеты). Выбранный пакет в конечном итогеотправлено внфлогсредство:

Во второй форме призывания (еслиnflog_groupуказано), ядро ​​Linux передаст пакет в nfnetlink_log, который отправит журнал через сокет netlink в указанную группу. Один процесс пользовательского пространства может подписаться на группу для получения журналов [...]

table ip6 special_log
delete table ip6 special_log

table ip6 special_log {
        chain log_to_nflog {
                log group 4242
        }

        chain ih_filter {
                meta l4proto tcp @ih,0,32 0x47455420 counter jump log_to_nflog
        }

        chain c_ingress {
                type filter hook prerouting priority -150; policy accept;
                jump ih_filter
        }
        chain c_egress {
                type filter hook postrouting priority 150; policy accept;
                jump ih_filter
        }
}

В Linux,нфлогсредство доступно как псевдоинтерфейс для (libpcapи)tcpdump:

# tcpdump -D |grep nflog
12.nflog (Linux netfilter log (NFLOG) interface) [none]

Пакеты, выбранныеnftablesТаким образом, можно отобразить (повторно используя ту же группу NFLOG: 4242):

tcpdump -n -s 0 -A -i nflog:4242

Предупреждение: задержка больше, чем при прямом захвате.

Примечание: правильное обращениеnftablesслучая, когда заголовок расширения был фактически протестирован с UDP (а не TCP) и фрагментированными пакетами (обрабатываемымиЗаголовок фрагмента) с помощью socat(в отдельном сетевом пространстве имен, чтобы предотвратить nf_defrag_ipv6срабатывание), поскольку сгенерировать фрагментированный пакет с помощью UDP гораздо проще, чем с помощью TCP, и я не нашел другого способа разместить где-нибудь заголовок расширения.

Связанный контент