Назад | Перейти на главную страницу

Linux не может интерпретировать ACK, продолжает повторно отправлять SYN + ACK

Ниже приведен дамп программы wirehark, в котором IP-адреса заменены на «клиент» и «сервер»:

4414.229553  client -> server TCP 62464 > http [SYN] Seq=0 Win=65535 Len=0 MSS=1452 WS=3 TSV=116730231 TSER=0
4414.229633 server -> client  TCP http > 62464 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=2406364374 TSER=116730231 WS=6
4414.263330  client -> server TCP 62464 > http [ACK] Seq=1 Ack=1 Win=524280 Len=0 TSV=116730231 TSER=2406364374
4418.812859 server -> client  TCP http > 62464 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=2406365520 TSER=116730231 WS=6
4418.892176  client -> server TCP [TCP Dup ACK 778#1] 62464 > http [ACK] Seq=1 Ack=1 Win=524280 Len=0 TSV=116730278 TSER=2406365520
4424.812864 server -> client  TCP http > 62464 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=2406367020 TSER=116730278 WS=6
4424.891240  client -> server TCP [TCP Dup ACK 778#2] 62464 > http [ACK] Seq=1 Ack=1 Win=524280 Len=0 TSV=116730337 TSER=2406367020

Таким образом, обычная последовательность SYN, SYN + ACK, ACK, кажется, имеет место, за исключением того, что сервер, похоже, не интерпретирует ACK. Вместо этого он продолжает повторно отправлять SYN + ACK, на который клиент постоянно отвечает дубликатом предыдущего ACK. Я не понимаю, как это могло случиться.

Я заметил проблему, потому что отслеживание соединений iptables считает эти соединения установленными и сохраняет их в памяти на полный 120-часовой тайм-аут. У меня есть некоторые правила брандмауэра, чтобы предотвратить большое количество одновременных подключений, пределы которых были достигнуты людьми, без фактического наличия такого количества активных подключений. В netstat команда не показывает эти фантомные соединения.

Другая информация:

Сервер представляет собой стандартную систему debian lenny со стандартным ядром:

Linux tb 2.6.26-2-686 #1 SMP Wed Aug 19 06:06:52 UTC 2009 i686 GNU/Linux

Бег:

Apache/2.2.9 (Debian) mod_ssl/2.2.9 OpenSSL/0.9.8g

У меня нет всей информации о клиенте (я не могу воспроизвести локально), но это Mac с браузером Chrome.

У меня нет правил брандмауэра, влияющих на ACK-пакеты. В основном я фильтрую только SYN-пакеты, все остальные TCP-пакеты пропускаются. Поэтому я на самом деле не использую отслеживание соединений для брандмауэра, кроме подсчета одновременных соединений и некоторого графического отображения установленных TCP-пакетов по сравнению с другими типами пакетов.

Изменить: мои правила iptables, относящиеся к TCP-порту 80:

iptables -P INPUT ACCEPT
iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 50 -j LOGDROP-CONN
iptables -A INPUT -p tcp --syn -m multiport --dports 80,443 -j ACCEPT
iptables -A INPUT -p tcp --syn -j REJECT --reject-with tcp-reset
iptables -A LOGDROP-CONN -m limit --limit 1/minute --limit-burst 1 -j LOG --log-prefix "ConConn "
iptables -A LOGDROP-CONN -j DROP

Изменить 2: еще один дамп, на этот раз с использованием tcpdump -vv:

16:05:52.999525 IP (tos 0x0, ttl 55, id 46466, offset 0, flags [DF], proto TCP (6), length 64) client.50538 > server.www: S, cksum 0x4429 (correct), 38417001:38417001(0) win 65535 <mss 1452,nop,wscale 3,nop,nop,timestamp 117224762 0,sackOK,eol>
16:05:52.999580 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60) server.www > client.50538: S, cksum 0xa2ab (correct), 3062713115:3062713115(0) ack 38417002 win 5792 <mss 1460,sackOK,timestamp 2418739698 117224762,nop,wscale 6>
16:05:53.321788 IP (tos 0x0, ttl 55, id 24299, offset 0, flags [DF], proto TCP (6), length 52) client.50538 > server.www: ., cksum 0xe813 (correct), 1:1(0) ack 1 win 65535 <nop,nop,timestamp 117224765 2418739698>
16:05:56.252697 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60) server.www > client.50538: S, cksum 0x9f7a (correct), 3062713115:3062713115(0) ack 38417002 win 5792 <mss 1460,sackOK,timestamp 2418740512 117224765,nop,wscale 6>
16:05:56.277250 IP (tos 0x0, ttl 55, id 15533, offset 0, flags [DF], proto TCP (6), length 52) client.50538 > server.www: ., cksum 0xe4c4 (correct), 1:1(0) ack 1 win 65535 <nop,nop,timestamp 117224798 2418740512>

Я бы использовал грубую силу. Сначала я бы попробовал, работает ли он с остановленным iptables. Если да, то это что-то в iptables.

Затем я добавлял правила одно за другим и смотрел, какое из них вызывает сбой соединения. Затем я играл с этим правилом до тех пор, пока он не делал то, что я хотел, не нарушая полностью трафик.

Если бы он не работал с остановленным iptables, это было бы действительно странно.

Хорошо, у меня нет полного ответа, но я многому научился с тех пор, как впервые возникла эта проблема. Я поделюсь своими мыслями здесь.

  • Во-первых, проблема была вызвана тем, что Google Chrome открыл несколько сокетов (6 в моих тестах) на любом веб-сайте, который вы открываете с его помощью. Это делается для того, чтобы начать параллельную загрузку различных элементов веб-сайта. Для простых веб-сайтов, на которых не так много элементов, как у меня, некоторые из этих предварительно открытых сокетов будут бездействовать. Я читал, что это делают и другие современные браузеры.
  • У клиента, запускающего проблему отслеживания соединений, вероятно, сломался домашний маршрутизатор или что-то в этом роде, потому что незанятые сокеты имеют тенденцию исчезать без отправки каких-либо пакетов FIN или RST для их разрыва.
  • Повторение пакетов SYN + ACK кажется нормальным поведением TCP, я смог наблюдать это на различных сторонних веб-серверах, запустив дамп пакета, а затем выполнив что-то вроде telnet www.website.com 80 и не отправлять никаких данных. Это все еще может быть особенностью Linux, поскольку все серверы, которые я тестировал, вполне могут работать под управлением Linux. Такое поведение также происходило без загруженных iptables-rules, так что это действительно связано с ядром.
  • После отправки нескольких пакетов SYN + ACK без ответа ядро ​​Linux разорвет свою сторону соединения. iptables отслеживание соединений, похоже, не разделяет эту логику, поэтому соединение останется в ESTABLISHED состояние до истечения времени ожидания. По умолчанию время ожидания составляет 5 дней (!). Я планирую сократить это до чего-то более разумного, например, до нескольких часов.
  • Повторение SYN + ACK после получения ACK не является полностью стандартным поведением, поскольку я не видел этого, когда открывал порт прослушивания с помощью nc -l ### и связан с этим. Так что это может быть определенный параметр TCP, который Apache устанавливает для своего прослушивающего сокета. Или это может быть что-то совершенно уникальное для Apache. Большинство других демонов объявляют о себе сразу после подключения, поэтому они не подходят для этого. Соединение должно выполнить трехстороннее рукопожатие, а затем немедленно перейти в режим ожидания.

Тем не менее, спасибо за идеи, данные выше, они помогли разобраться в этом.