Как установить флаг «Не фрагментировать» (DF) на сокете?

Я пытаюсь установить флаг DF (не фрагментировать) для отправки пакетов с использованием UDP.

Глядя на книгу Ричарда Стивена Том 1 Сетевое программирование Unix; Sockets Networking API, я не могу найти, как это установить.

Я подозреваю, что сделал бы это с помощью setsockopt(), но не могу найти его в таблице на странице 193.

Пожалуйста, предложите, как это делается.


person WilliamKF    schedule 10.06.2009    source источник


Ответы (3)


Вы делаете это с помощью вызова setsockopt(), используя опцию IP_DONTFRAG:

int val = 1;
setsockopt(sd, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val));

Вот страница, объясняющая это более подробно .

Похоже, что для Linux вы должны использовать параметр IP_MTU_DISCOVER со значением IP_PMTUDISC_DO (или IP_PMTUDISC_DONT, чтобы отключить его):

int val = IP_PMTUDISC_DO;
setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));

Я не проверял это, просто просмотрел файлы заголовков и немного поиска в Интернете, так что вам нужно это проверить.

Что касается того, есть ли другой способ установить флаг DF:

Я нигде не нахожу в своей программе, где установлен флаг принудительного DF, но tcpdump предполагает, что это так. Есть ли другой способ установить это?

На этой отличной странице здесь:

IP_MTU_DISCOVER: Устанавливает или получает параметр обнаружения MTU пути для сокета. Если этот параметр включен, Linux будет выполнять обнаружение Path MTU, как определено в RFC 1191, для этого сокета. Флаг «не фрагментировать» устанавливается для всех исходящих дейтаграмм. Общесистемное значение по умолчанию контролируется ip_no_pmtu_disc sysctl для SOCK_STREAM сокетов и отключено для всех остальных. Для сокетов, отличных от SOCK_STREAM, пользователь несет ответственность за пакетирование данных в куски размером с MTU и, при необходимости, за повторную передачу. Ядро будет отклонять пакеты, превышающие MTU известного пути, если этот флаг установлен (с EMSGSIZE).

Мне кажется, что вы можете установить общесистемное значение по умолчанию, используя sysctl:

    sysctl ip_no_pmtu_disc

возвращает "error: "ip_no_pmtu_disc" is an unknown key" в моей системе, но может быть установлено в вашей. Кроме этого, я не знаю ничего другого (кроме setsockopt(), как упоминалось ранее), что может повлиять на настройку.

person paxdiablo    schedule 10.06.2009
comment
Какой это уровень, IPPROTO_IP? - person WilliamKF; 10.06.2009
comment
Есть ли другой способ установить это? Я предполагаю, что по умолчанию DF выключен, верно? - person WilliamKF; 10.06.2009
comment
Я думаю, что по умолчанию он отключен, но вы можете использовать getsockopt (как ни странно :-), чтобы получить его текущее значение. - person paxdiablo; 10.06.2009
comment
IP_DONTFRAG не поддерживается для многоадресной рассылки, и я думаю, что он плохо поддерживается для сокетов UDP вне реализации BSD. Если вы еще не получили сообщение об ошибке EOPNOTSUPP, вы можете проверить с помощью getsockopt после setsockopt, чтобы подтвердить, что все сделано. - person nik; 10.06.2009
comment
IP_DONTFRAG не определен (ошибка компилятора) для моей системы Centos 4. Кроме того, я нигде не нахожу в своей программе, где он установлен, но tcpdump() предполагает, что он установлен, и я получил сообщение об ошибке EMSGSIZE «слишком длинное сообщение» (90), предполагающее, что он тоже включен, и пакет был слишком большим, предполагая, что маршрутизация изменилась в середине передачи после многих успешных пакетов на маршрут с меньшим MTU. - person WilliamKF; 10.06.2009
comment
EMSGSIZE в этом состоянии означает, что вы пытаетесь отправить пакет, размер которого больше вашего MTU, с установленным битом «не фрагментировать». Убедитесь, что размер вашего пакета находится в пределах MTU. - person nik; 10.06.2009
comment
Обратите внимание, что фрагментация запускается при превышении размера MTU. В это время, если установлен DF, вы обычно получаете EMSGSIZE. - person nik; 10.06.2009
comment
Он должен поддерживаться в BSD и Unix98, но я не уверен, заботился ли Линус о соблюдении стандартов :-) Я также не могу найти его в своей системе Linux, несмотря на наличие других вариантов. - person paxdiablo; 10.06.2009
comment
На Centos 4, выполняя «sysctl ip_no_pmtu_disc», я получаю: ошибка: «ip_no_pmtu_disc» — неизвестный ключ - person WilliamKF; 13.06.2009
comment
Тогда я не знаю, почему он установлен по умолчанию. Я предлагаю отключить его с помощью setsockopt(), показанного в ответе, перед использованием сокета. - person paxdiablo; 13.06.2009
comment
Примечание: для Windows (XP и более поздних версий) это значение называется IP_DONTFRAGMENT. См. nil.si/ipcorner/IP_Fragmentation. - person Suma; 21.11.2011
comment
Глупый вопрос; Где вы устанавливаете опцию IP_DONTFRAG? Я использую Ubuntu и предполагаю, что это параметр в файле. Я не знаю, где файл. - person Sablefoste; 27.02.2015
comment
К вашему сведению, правильная проверка sysctl: sysctl net.ipv4.ip_no_pmtu_disc. - person jschultz410; 16.11.2017

Если вы работаете в пользовательской среде с намерением обойти сетевой стек ядра и, таким образом, создать свои собственные пакеты и заголовки и передать их пользовательскому модулю ядра, есть лучший вариант, чем setsockopt().

На самом деле вы можете установить флаг DF точно так же, как любое другое поле struct iphdr, определенное в linux/ip.h. 3-битные флаги IP фактически являются частью члена структуры frag_off (смещение фрагмента).

Когда вы думаете об этом, имеет смысл сгруппировать эти две вещи, поскольку флаги связаны с фрагментацией. Согласно RFC-791, в разделе, описывающем структуру заголовка IP, указано, что смещение фрагмента 13-битная длина и три 1-битных флага. Элемент frag_off имеет тип __be16, который может содержать 13 + 3 бита.

Короче, вот решение:

struct iphdr ip;
ip.frag_off |= ntohs(IP_DF);

Здесь мы точно устанавливаем бит DF, используя маску IP_DF, предназначенную для этой конкретной цели.

IP_DF определяется в net/ip.h (конечно, заголовки ядра), тогда как struct iphdr определяется в linux/ip.h.

person Asblarf    schedule 01.02.2012

Я согласен с ответом paxdiablo.

  • setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))

где val является одним из:

#define IP_PMTUDISC_DONT   0    /* Never send DF frames.  */
#define IP_PMTUDISC_WANT   1    /* Use per route hints.  */
#define IP_PMTUDISC_DO     2    /* Always DF.  */
#define IP_PMTUDISC_PROBE  3    /* Ignore dst pmtu.  */
  • ip_no_pmtu_disc в исходниках ядра:
if (ipv4_config.no_pmtu_disc)
    inet->pmtudisc = IP_PMTUDISC_DONT;
else
    inet->pmtudisc = IP_PMTUDISC_WANT;
person EdwardLewis    schedule 26.12.2013
comment
Итак, IP_PMTUDISC_DO означает всегда не фрагментировать? - person pellucidcoder; 11.05.2020