Каково значение SO_REUSEADDR (setsockopt опция) - Linux? [дубликат]

57
задан Nawaz 19 August 2016 в 05:41
поделиться

3 ответа

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

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

From unixguide.net

27
ответ дан 24 November 2019 в 19:18
поделиться

Основная цель разработки 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 sockets - по сути, все Unix и 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-tuple, так что во время состояния TIME_WAIT данный удаленный узел не может повторно соединиться с тем же удаленным эфемерным портом, но я не знаю ни одного стека TCP с таким уточнением.

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

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

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

Самое безопасное, что можно сделать, это переждать период TIME_WAIT.

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

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

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

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

129
ответ дан 24 November 2019 в 19:18
поделиться

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

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

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

8
ответ дан 24 November 2019 в 19:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: