Подтвердить что ты не робот

В чем смысл SO_REUSEADDR (опция setsockopt) - Linux?

На странице man:

SO_REUSEADDR Указывает, что правила используется для проверки адресов для bind() следует разрешить повторное использование локальных адресов, если это поддерживается протокол. Эта опция принимает int стоимость. Это логическая опция

Когда я должен его использовать? Почему "повторное использование локальных адресов" дает?

4b9b3361

Ответ 1

Основная цель проекта TCP состоит в том, чтобы обеспечить надежную передачу данных в условиях потери пакетов, переупорядочения пакетов и, в данном случае, дублирования пакетов.

Совершенно очевидно, как сетевой стек TCP/IP справляется со всем этим, пока соединение установлено, но есть крайний случай, который происходит сразу после закрытия соединения. Что произойдет, если пакет, отправленный в самом конце разговора, будет продублирован и задержан, так что пакеты с 4-сторонним отключением попадут в приемник раньше, чем пакет с задержкой? Стек покорно закрывает соединение. Затем позже, задержанный дубликат пакета обнаруживается. Что должен делать стек?

Что еще более важно, что делать, если программа с открытыми сокетами на заданном IP-адресе + комбинированный порт TCP закрывает свои сокеты, а затем ненадолго появляется программа и хочет прослушивать тот же IP-адрес и номер порта TCP? (Типичный случай: программа убивается и быстро перезапускается.)

Есть несколько вариантов:

  1. Запретить повторное использование этой комбинации IP/порт как минимум в 2 раза больше максимального времени, в течение которого пакет может находиться в полете. В TCP это обычно называется задержкой 2 × MSL. Иногда вы также видите 2 × RTT, что примерно эквивалентно.

    Это поведение по умолчанию для всех распространенных стеков TCP/IP. 2 × MSL обычно составляет от 30 до 120 секунд, и он отображается в выводе netstat как период TIME_WAIT. По истечении этого времени стек предполагает, что любые мошеннические пакеты были отброшены в пути из-за истекших TTL, так что сокет покидает TIME_WAIT, что позволяет повторно использовать эту комбинацию IP/порт.

  2. Разрешить новой программе выполнить повторную привязку к этому списку IP/портов. В стеках с интерфейсами BSD-сокетов - по существу, во всех Unixes и Unix-подобных системах, а также в Windows через Winsock - вы должны запросить это поведение, установив параметр SO_REUSEADDR помощью setsockopt() прежде чем вызывать bind().

SO_REUSEADDR чаще всего устанавливается в программах сетевого сервера, поскольку обычным способом использования является изменение конфигурации, а затем потребуется перезапустить эту программу, чтобы изменения вступили в силу. Без SO_REUSEADDR вызов bind() в новом экземпляре перезапущенной программы завершится неудачей, если были соединения, открытые к предыдущему экземпляру, когда вы его уничтожили. Эти соединения будут удерживать порт TCP в TIME_WAIT течение 30-120 секунд, поэтому вы попадаете в случай 1 выше.

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

Если вы не видите, что это правда, здесь все стек TCP/IP прослушивающей машины должен работать с каждым соединением, чтобы принять это решение:

  1. Локальный IP: не уникально для каждого соединения. Фактически, в нашем определении проблемы здесь говорится, что мы намеренно повторно используем локальный IP.

  2. Локальный порт TCP: тоже самое.

  3. Удаленный IP-адрес: машина, вызывающая неоднозначность, может повторно подключиться, что не поможет устранить неоднозначность правильного назначения пакета.

  4. Удаленный порт. В хорошо функционирующих сетевых стеках удаленный порт исходящего соединения используется не быстро, а только в 16 битах, поэтому у вас есть 30–120 секунд, чтобы заставить стек пройти несколько десятков тысяч из вариантов и повторно использовать порт. Компьютеры могли работать так быстро еще в 1960-х годах.

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

    Я полагаю, что стек слушателя мог бы выбрать строго запрещать соединения только из TCP 4-кортежа, чтобы во время TIME_WAIT данному удаленному хосту было запрещено повторное соединение с тем же удаленным эфемерным портом, но я не знаю ни одного стека TCP с этим конкретным уточнением.

  5. Локальные и удаленные порядковые номера TCP: они также недостаточно уникальны, чтобы новая удаленная программа не могла получить те же значения.

Если бы мы сегодня перепроектировали TCP, я думаю, что мы бы интегрировали TLS или что-то в этом роде в качестве необязательной функции, одним из эффектов которой является невозможность такого рода непреднамеренного и злонамеренного перехвата соединения, но это требует добавления больших полей. (128 бит и выше), что было совсем не практично в 1981 году, когда был опубликован документ для текущей версии TCP (RFC 793).

Без такого усиления неоднозначность, созданная путем повторной привязки во время TIME_WAIT означает, что вы можете либо a) иметь устаревшие данные, предназначенные для старого слушателя, неправильно доставлять в сокет, принадлежащий новому слушателю, тем самым либо нарушая протокол слушателя, либо неправильно вводя устаревшие данные в связи; или b) новые данные для нового сокета слушателя, ошибочно назначенные старому сокету слушателя и, таким образом, непреднамеренно отброшенные.

Безопасная вещь, чтобы сделать, это подождать период TIME_WAIT.

В конечном итоге все сводится к выбору затрат: переждать период TIME_WAIT или взять на себя риск нежелательной потери данных или непреднамеренного ввода данных.

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

Это не универсальный выбор. Многие программы - даже серверные программы, требующие перезапуска, чтобы применить изменение настроек - вместо этого предпочитают оставить SO_REUSEADDR покое. Программист может знать об этих рисках и предпочитает оставить дефолт в покое, или они могут не знать о проблемах, но получают выгоду от мудрого дефолта.

Некоторые сетевые программы предлагают пользователю выбор из вариантов конфигурации, снимая с себя ответственность перед конечным пользователем или системным администратором.

Ответ 2

SO_REUSEADDR позволяет вашему серверу привязать к адресу, который находится в TIME_WAIT.

Эта опция сокета указывает ядру, что даже если этот порт занят (в состоянии TIME_WAIT), продолжайте и повторно используйте его. Если он занят, но с другим состоянием, вы все равно получите сообщение об ошибке, которое уже используется. Это полезно, если ваш сервер был отключен, а затем сразу же перезапущен, пока сокеты все еще активны на своем порту.

От unixguide.net

Ответ 3

Когда вы создаете сокет, вы действительно не владеете им. ОС (стек TCP) создает его для вас и предоставляет вам дескриптор (файловый дескриптор) для доступа к нему. Когда ваш сокет закрыт, для ОС требуется время "полностью закрыть", пока оно проходит через несколько состояний. Как упоминалось в комментариях EJP, самая длинная задержка обычно происходит из состояния TIME_WAIT. Эта дополнительная задержка требуется для обработки крайних случаев в самом конце последовательности завершения и обеспечения того, чтобы последнее подтверждение завершения было либо пройдено, либо было сброшено с другой стороны из-за таймаута. Здесь вы можете найти дополнительные соображения об этом состоянии. Основные соображения указаны следующим образом:

Помните, что TCP гарантирует, что все переданные данные будут доставлены, если это вообще возможно. Когда вы закрываете сокет, сервер переходит в состояние TIME_WAIT, просто чтобы быть действительно уверенным, что все данные прошли. Когда сокет закрыт, обе стороны соглашаются, отправляя сообщения друг другу, что они не будут отправлять больше данных. Это, мне показалось, было достаточно хорошим, и после того, как рукопожатие сделано, розетка должна быть закрыта. Проблема в два раза. Во-первых, нет никакого способа убедиться, что последний ответ был успешно передан. Во-вторых, в сети могут появляться "блуждающие дубликаты", которые должны быть обработаны, если они будут доставлены.

Если вы попытаетесь создать несколько сокетов с той же самой парой ip: port действительно быстро, вы получите ошибку "адрес уже используется", потому что предыдущий сокет не будет полностью выпущен. Использование SO_REUSEADDR избавится от этой ошибки, поскольку оно переопределит проверки для любого предыдущего экземпляра.