Как стать автором
Обновить

Что бывает, если MTU выше нормы

Написать о не очевидной сетевой проблеме, проявляющейся по-разному, но имеющей один корень, меня побудил один интересный случай.
Совсем недавно одному удаленному бухгалтеру понадобилось подключаться по RDP к нашему терминальному серверу для работы с 1C. Виндовый админ выделил нужные права, завел учетку, с моей стороны тоже проблем нет: IP клиента статический, а RDP использует TCP порт 3389, который сразу и был проброшен на терминальный сервер:

iptables -t nat -A PREROUTING -p tcp -s $ext_term_access -d $INET_IP --dport 3389 -j DNAT --to-destination $SRV1C
iptables -A FORWARD -p TCP -s $ext_term_access --dport 3389 -d $SRV1C -j ACCEPT


Проверили конфигурацию с внешнего компьютера, успешно соединились, клиенту сразу был дан ответ «Готово, подключайтесь». Ко всеобщему удивлению клиент не смог соединиться, с его стороны соединение «зависало», не выдавая ошибок. Мы проверили конфигурацию с еще нескольких внешних компьютеров, и везде соединение шло гладко. Пришлось списаться с их сисадмином, который, вместо того, чтобы подумать вместе, заявил, что проблема явно у нас, и что его клиенты подключаются таким образом ко многим серверам, а его ISA (это, вроде, такой виндовый фаерволл) и вовсе безупречна. Несколько дней я ломал голову над этой проблемой, даже временно частично или полностью отключал фаерволл, после чего выяснилось, что консольный-то доступ к терминалу у клиента работает. Подозрение сразу пало на некорректный MTU, так как присутствовал самый явный симптом: текстовая информация и прочая мелочь размером до полутора килобайт передавалась успешно, а с более весомой графикой возникали проблемы.

Здесь хотелось бы подробнее остановиться на замечательной технологии определения максимального размера передаваемого сегмента в протоколе TCP — Path Maximum Transmission Unit Discovery или просто PMTUD. Два компьютера (клиент и сервер) начинают сессию передачи данных с SYN и SYN-ACK пакетов, которые содержат информацию о MSS — Maximum Segment Size — это характеристика протокола TCP, сообщающая максимально допустимый размер передаваемого сегмента. Так это выглядит:

IP (0x64, 44702, [DF], TCP, 52) client.com.55679 > server.com.http: S, cksum 0x13ea (correct), 3230584989:3230584989(0) win 5840 <mss 1452,nop,nop,sackOK,nop,wscale 4>
IP (0x63, [DF], TCP, 52) server.com.http > client.com.55679: S, cksum 0xb43b (correct), 3776478921:3776478921(0) ack 3230584990 win 5808 <mss 1412,nop,nop,sackOK,nop,wscale 7>


Хорошо видно, что для одного компьютера MSS равен 1452, для другого 1412. Из этих двух значений выбирается наименьшее. В этой сессии размер любого сегмента обязательно будет меньше либо равен 1412 байт:

IP (0x64, 44703, [DF], TCP, 40) client.com.55679 > server.com.http: ., cksum 0x0a21 (correct), 1:1(0) ack 1 win 365
IP (0x64, 44704, [DF], TCP, 857) client.com.55679 > server.com.http: P 1:818(817) ack 1 win 365
IP (0x63, 63163, [DF], TCP, 40) server.com.http > client.com.55679: ., cksum 0x0822 (correct), 1:1(0) ack 818 win 59
IP (0x63, 63164, [DF], TCP, 1452) server.com.http > client.com.55679: . 1:1413(1412) ack 818 win 59
IP (0x64, 44705, [DF], TCP, 40) client.com.55679 > server.com.http: ., cksum 0x00b5 (correct), 818:818(0) ack 1413 win 548
IP (0x63, 63165, [DF], TCP, 1452) server.com.http > client.com.55679: . 1413:2825(1412) ack 818 win 59
IP (0x64, 44706, [DF], TCP, 40) client.com.55679 > server.com.http: ., cksum 0xfa7a (correct), 818:818(0) ack 2825 win 730
IP (0x63, 63166, [DF], TCP, 1452) server.com.http > client.com.55679: P 2825:4237(1412) ack 818 win 59
...


Путать MSS и MTU не стоит, последний — характеристика сетевого интерфейса, а не протокола. Ну, и конечно, MSS напрямую зависит от MTU и вычисляется как MTU — 20 (размер IP-заголовка) — 20 (размер TCP заголовка).
Таким образом, клиент и сервер могут обмениваться между собой информацией, посылая TCP-пакеты определенного размера с установленным битом «DF: Don't Fragment». Но если на пути между ними обнаружится узел, не способный передать пакет размером 1452 байта (1412+40), этот узел отбросит пакет и сформирует отправителю специальный ICMP пакет с типом 3 (Destination Unreachable) и кодом 4 (fragmentation needed and DF set), который, кроме самой ошибки, будет содержать информацию о максимально допустимом сегменте передачи. Отправитель получает его, меняет свой MSS на еще более меньший и передача успешно продолжается. Это теория.
На практике же где-то между мной и клиентом эти столь необходимые ICMP-пакеты с информацией о фрагментации просто терялись. И передающая сторона, не получая ошибку, повторяла попытки передачи снова и снова, ожидая подтверждения приемной. Эту проблему помог решить iptables. В таблице mangle он умеет изменять то самое передаваемое значение mss в SYN и SYN-ACK пакетах при помощи цели TCPMSS:

iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -s $ext_term_access -j TCPMSS --set-mss 1452

Это правило берет SYN-пакет, отправленный хостом клиента, и меняет его значение mss на 1452. После того, как правило было прописано, у клиента все заработало, и все остались довольны.

Случай второй. Наши плановики начали работать со специальной вебмордой на удаленном сервере. Оформление этого сайта не содержало каких-либо тяжелых объектов или графики, навигация по сайту работала прекрасно. Но среди используемых ими функций была «Формирование отчета в excel», которая просто «зависала» в ожидании ответа от сервера. На удаленной стороне ответили «У всех все работает» и предложили искать проблему у себя. После некоторых размышлений, решил просто пропинговать их сервер, чтобы проверить присутствие icmp — и да, пинг не шел. Точно таким же образом, в iptables для серверного хоста был прописан еще меньший mtu — 1380, после чего плановики смогли работать нормально.

Случай третий. Заметил недавно, что в очереди писем postfix на протяжении нескольких дней стали скапливаться письма на один и тот же домен, с одной о той же необычной причиной отсрочки:

ADC2F308358: from=<user@client.com>, size=33628, nrcpt=2 (queue active)
ADC2F308358: enabling PIX workarounds: disable_esmtp delay_dotcrlf for mailserver.com[22.22.22.22]:25
ADC2F308358: to=<user@mailserver.com>, relay=mailserver.com[22.22.22.22]:25, delay=333009, delays=332827/0.02/1.5/181, dsn=4.4.2, status=deferred (conversation with mailserver.com[22.22.22.22] timed out while sending message body)


Жизнь, что называется, научила, поэтому первым делом проверил пинг до сервера. И пинга не оказалось. Выставил наугад значение mss в 1380 байт, флашнул очередь — не помогло. Значения 1024, 768 и даже 512 тоже результата не дали О.о Началось активное гугление по ругани почтовика, и даже нашелся аналогичный случай, решением которого стало отключение tcp_sack (выборочный ACK на пакеты из очереди TCP). Но нашему серверу не помогло. И вот уже, готовый списаться с админами этого почтового сервера, я зачем-то в последний раз попробовал значение mss в 256 байт и это сработало. Я до сих пор не могу понять, какие же узлы могут находиться между нами, и какими извращенными способами где-то происходит инкапсуляция трафика, из-за чего mss в шесть раз ниже нормы.
И точно так же не понимаю, по каким причинам нужно глушить весь ICMP. Да, существуют легко реализуемые атаки, с его использованием, но вред от них не ахти какой, и уж тем более, это не повод зарезать целый протокол. Без ICMP нельзя, а одминам, которые этого не знают, я желаю рака яичек.
На прошлой неделе списался снова с админом той конторы, откуда подключался клиент RDP:

<Я> а ты сам глушишь icmp или кто-то между нами это делает?
<Админ> у меня isa глушит по-умолчанию
<Я> а мб откроешь? там же icmp type 3 code 4 - необходима фрагментация
<Админ> только с вами, это вообще умолчательная настройка, с нею весь мир живёт, так что не в ней дело


А тем временем весь мир пингуется.
Админы, плохо знающие сети никуда не денутся, поэтому, дорогой читатель, если ты еще не сталкивался с рядом таких проблем, то обязательно столкнешься, и пусть случаи, описанные здесь, помогут быстрее найти и исправить проблему.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.