How to Adjust the TCP Window Size Limit on Linux
The maximum bandwidth that iPerf can push between a source and a destination depends on many factors. To name a few:
- Interface: if you are using a Raspberry Pi that has a 10/100 interface, then the upper limit is 100 Mbps.
- CPU capabilities: pushing several Gbps of traffic needs quite a bit of CPU performance.
- Latency: the greater the latency between source and destination, the larger the TCP window size needs to be.
The interface and the CPU put hard limits on the maximum bandwidth you can achieve. You can’t bypass them unless you get new hardware.
However, given your link’s latency, you can adjust the TCP window size in order to achieve the desired bandwidth. But it can be tricky…
How to calculate maximum achievable TCP throughput
The maximum achievable throughput is equal to:
[TCP Window Size in bits] / [Latency in seconds] = [Throughput in bits per second]
The default window size that iPerf uses varies by machine type and operating system. If it’s 64 KBytes, with a modest latency of 10 ms, the maximum bandwidth you can get is 52.43 Mbits/sec. For 500 ms the maximum is 524 Kbits/sec.
The obvious option would be to increase the window size to a larger value and get up to, let’s say, 500 Mbps. For a 50 ms latency, the window size needs to be 3.1 Mbytes.
Indeed, iPerf gives you the option to set the window size, but if you try to set it to 3.1 or 8.0 Mbytes you may get the following warning from iPerf:
“TCP window size: 250 KByte (WARNING: requested 8.00 MByte)”
Oops… Why didn’t iPerf follow your command and used only a 250 Kbyte window?
The devil is in the details!
In this case, the devil is the operating system that has a hard limit on the TCP window size that an application can use. It looks like in this case the limit is 250 Kbytes. These limits exist for good reasons. Larger TCP windows take more system memory and if you have multiple applications running, using large windows they may bog down the system. Or the system may deny TCP connections if it is running out of memory.
In your case, if you have full control of the system and you know what you are doing, you may want to increase the OS limits. As an example, to increase those limits to 8 MBytes, run the following commands as root on a Debian Linux machine:
echo 'net.core.wmem_max=4194304' >> /etc/sysctl.conf echo 'net.core.rmem_max=12582912' >> /etc/sysctl.conf echo 'net.ipv4.tcp_rmem = 4096 87380 4194304' >> /etc/sysctl.conf echo 'net.ipv4.tcp_wmem = 4096 87380 4194304' >> /etc/sysctl.conf sysctl -p
Now if you request iPerf to use a 4 Mbyte TCP window size, you will see that your request will get fulfilled like it’s supposed to!
Spot VPN, ISP, WiFi issues and more with Netbeez
Тонкая настройка сети в Linux
Про то, что Linux изначально разрабатывалась как сетевая операционная система я думаю знают все. Поэтому в этой ОС можно вносить достаточно серьезные изменения в сетевую конфигурацию. Как говорится, в Линуксе можно все, главное знать как. Но прежде, чем начать обсуждение темы тонких настроек совершим небольшой экскурс в историю развития Линукс.
В прежние времена (а точнее до версии ядра 1.3.57) если пользователю требовалось внести какие-то изменения в системные параметры ОС необходимо было перекомпилировать ядро. Естественно, пересборка ядра была не самой тривиальной задачей и при неумелом обращении данная операция вполне могла привести к краху системы. Поэтому в качестве альтернативы разработчики предложили sysctl. Sysctl — это программная утилита, предназначенная для считывания и изменения атрибутов системного ядра, таких как номер версии, максимальные ограничения и параметры безопасности. Она доступна как в виде системного вызова для скомпилированных программ, так и в виде команды администратора для интерактивного использования и написания сценариев.
С помощью sysctl можно изменять различные параметры полный список которых можно получить с помощью команды
Однако, результат вывода этой команды будет содержать много лишней информации, связанной с настройками ядра операционной системы. Но нас в рамках сегодняшней статьи интересуют сетевые параметры, для получения которых можно запустить команду с ключом:
Если нужно посмотреть значение конкретного параметра, то воспользуемся:
Помимо sysctl есть еще один способ узнать значение конкретного параметра
Соответственно для изменения значений параметров мы также можем воспользоваться sysctl:
а можем изменить параметр непосредственно в файле:
echo 1 >/proc/sys/net/ipv4/ip_forward
Однако, значения этих параметров сохранятся в системе только до перезагрузки. Для того, чтобы сохранить их в системе необходимо внести соответствующие правки в файл /etc/sysctl.conf.
Также, для применения изменений необходимо воспользоваться командой sysctl -p [filename]
Но это было лишь вступление, рассказывающее о том, как работать с параметрами ядра, а дальше мы перейдем к непосредственно к тонкой настройке.
Боремся с затоплениями
Начнем нашу настройку мы с внесения изменений в параметры ICMP. Широковещательные ICMP пакеты могут использоваться для реализации атаки с затоплением. В чем заключается ее суть.
Злоумышленник посылает ICMP echo request-пакеты с адресом жертвы в качестве источника, на broadcast-адреса крупных сетей. В результате каждая из машин ответит на этот фальшивый запрос, и машина-отправитель (жертва) получит больше количество ответов. Посылка множество broadcast-echo requests от имени «жертвы» на broadcast-адреса крупных сетей, можно вызвать резкой заполнение канала «жертвы».
Для того, чтобы не принимать участия в подобном “мероприятии” необходимо настроить во первых игнорирование широковещательных запросов и во-вторых поддельные ответы на широковещательные кадры (нарушение RFC1122):
Продолжая тему затоплений не лишним будет вспомнить “старый добрый” SYN flood. Это атака на отказ в обслуживании, которая заключается в отправке большого количества SYN-запросов, то есть запросов на подключение по протоколу TCP в достаточно короткий срок.
Напомним, как устанавливается TCP соединение. Согласно процессу «трёхкратного рукопожатия» TCP, клиент посылает пакет с установленным флагом SYN (synchronize). В ответ на него сервер должен ответить комбинацией флагов SYN+ACK (acknowledges). После этого клиент должен ответить пакетом с флагом ACK, после чего соединение считается установленным.
Соответственно, злоумышленник, посылая SYN-запросы, переполняет на машине жертвы очередь на подключения. При этом он игнорирует SYN+ACK пакеты цели, не высылая ответные пакеты, либо подделывает заголовок пакета таким образом, что ответный SYN+ACK отправляется на несуществующий адрес. В очереди подключений появляются так называемые полуоткрытые соединения, ожидающие подтверждения от клиента. По истечении определенного тайм-аута эти подключения отбрасываются. Задача злоумышленника заключается в том, чтобы поддерживать очередь заполненной таким образом, чтобы не допустить новых подключений. Из-за этого клиенты, не являющиеся злоумышленниками, не могут установить связь, либо устанавливают её с существенными задержками.
Для защиты от SYN-flood в sysctl.conf необходимо включить SYN cookies (мы будем избегать сброса новых соединений, когда очередь TCP-соединений будет переполнена), изменить размер SYN backlog (размер очереди) и уменьшить количество повторных попыток SYN/ACK:
Закручиваем другие гайки
Продолжая тему безопасных сетевых настроек, можно порекомендовать отключить перенаправление ICMP пакетов. Естественно, делать это можно только на узлах, не выполняющих маршрутизацию:
Аналогично можно запретить форвардинг в принципе
Далее нам необходимо отключить прием маршрутов для того, чтобы нам не подсунули неправильные маршруты. Злоумышленник может попытаться подсунуть поддельные маршруты для того, чтобы перенаправить весть трафик через узел, который он контролирует (атака Man In The Middle).
Включим проверку обратного маршрута, чтобы защититься от спуфинга. Однако, если у вас используется несимметричная маршрутизация (да, надо хорошо знать свою сеть), то лучше эти опции не включать:
Ну и вообще неплохо бы логировать пакеты, пришедшие с несуществующих адресов. Хотя здесь есть риск засорения логов ненужными сообщениями, так что лучше проверить опытным путем.
Также включаем аналогичные настройки для IPv6.
А если IPv6 вообще не используется, то его проще отключить. Меньше сервисов — меньше проблем:
Улучшаем производительность
После того, как мы настроили безопасность, самое время поработать над производительностью сетевых компонентов. Начнем с общих настроек потребления памяти для протокола TCP.
При передаче пакетов существует требование фиксированного значения размера окна отправки и получения данных (TCP Window). Это связано с тем, что отправитель должен отслеживать отправленные им байты до тех пор, пока не получит подтверждение обратно. Потому что, если подтверждение не приходит, тогда отправитель должен повторно отправить все байты (если они в буфере, он может отправить их повторно. Следовательно, эти данные буфера не сбрасываются до тех пор, пока не придет подтверждение.)
Для начала включим масштабирование окна TCP, тем самым мы увеличим объем данных, передаваемых получателю:
Однако, даже после включения опции масштабирования окна максимальный объем данных, который может быть отправлен получателю без получения обратного подтверждения, зависит еще от размеров окна приема. Это максимальный объем данных, который получатель может буферизировать перед обработкой принимающим приложением. Следовательно, нам нужно изменить размер окна приема на большее максимальное значение.
Аналогично, нам необходимо указать максимальное значение окна отправки, и эти значения должны совпадать.
И еще несколько переменных, изменение значения которых может повлиять на производительность.
Переменная net.ipv4.tcp_rmem имеет три значения: минимум, по умолчанию, максимум и определяет размер приемного буфера сокетов TCP. Каждый сокет TCP при создании имеет право использовать минимальный объем памяти. Размер минимального буфера по умолчанию составляет 8 Кбайт (8192). Значение по умолчанию определяет количество памяти, допустимое для буфера передачи сокета TCP. И максимум это максимальный размер буфера, который может быть автоматически выделен для приема сокету TCP.
net.ipv4.tcp_rmem = 4096 87380 16777216
Переменная net.ipv4.tcp_wmem также содержит три значения, определяющих минимальное, принятое по умолчанию и максимальное количество памяти, резервируемой для буферов передачи сокета TCP. Здесь аналогично предыдущему при минимуме каждый сокет TCP имеет право использовать память по факту своего создания. Но размер минимального буфера по умолчанию составляет только 4 Кбайт (4096). Размер принятого по умолчанию буфера обычно составляет 16 Кбайт (16384). И максимум это максимальное количество памяти, которое может быть автоматически выделено для буфера передачи сокета TCP.
net.ipv4.tcp_wmem = 4096 65536 16777216
На сильно загруженных серверах имеет смысл уменьшить число неудачных попыток, после которого уничтожается соединение TCP, закрытое на стороне клиента. Это значение можно указать с помощью параметра net.ipv4.tcp_orphan_retries. По умолчанию обычно указывают 7, но если система сильно загружена, имеет смысл уменьшить это значение так как закрытые соединения могут поглощать достаточно много ресурсов.
И напоследок еще один параметр ядра для оптимизации. Если интерфейс получает много пакетов, ядро может обрабатывать их недостаточно быстро. Вы можете увеличить количество пакетов, удерживаемых в очереди (backlog), изменив значение net.core.netdev_max_backlog:
Заключение
С помощью представленных в статье параметров можно усилить защищенность системы и увеличить ее производительность. Однако, многие значения параметров требуют определенного времени для получения оптимальных значений производительности.
Что такое инфраструктура как код? Что значит «сервера снежинки»? Как автоматизировать работу с серверами? Ответы на эти и другие вопросы дадим на бесплатном уроке, после которого вы узнаете как автоматизировать рутинные процессы создания и настройки серверов с помощью инструментов vagrant, terraform, ansible.