Pull to refresh

Что бывает, если 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 - необходима фрагментация
<Админ> только с вами, это вообще умолчательная настройка, с нею весь мир живёт, так что не в ней дело


А тем временем весь мир пингуется.
Админы, плохо знающие сети никуда не денутся, поэтому, дорогой читатель, если ты еще не сталкивался с рядом таких проблем, то обязательно столкнешься, и пусть случаи, описанные здесь, помогут быстрее найти и исправить проблему.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.