Как установить флаг не фрагмент (DF) на сокете? - PullRequest
9 голосов
/ 10 июня 2009

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

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

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

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

Ответы [ 3 ]

19 голосов
/ 10 июня 2009

Вы делаете это с помощью вызова 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 другим способом:

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

С этой превосходной страницы здесь :

IP_MTU_DISCOVER: Устанавливает или получает настройку Path MTU Discovery для сокета. Когда включено, Linux будет выполнять Обнаружение 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(), как упоминалось ранее), которое может повлиять на настройку.

3 голосов
/ 01 февраля 2012

Если вы работаете в Userland с намерением обойти сетевой стек ядра и, таким образом, собираете свои собственные пакеты и заголовки и передаете их в пользовательский модуль ядра, есть лучший вариант, чем 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.

2 голосов
/ 26 декабря 2013

Я согласен с ответом Паксдиабло.

  • 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;
...