Как установить флаг don't fragment (DF) для сокета?
Вопрос
Я пытаюсь установить DF (флаг не фрагментировать) для отправки пакетов с использованием UDP.
Рассматривая книгу Ричарда Стивена Том 1 Сетевое программирование в Unix;Сетевой API сокетов, я не могу найти, как это установить.
Я подозреваю, что я бы сделал это с помощью setsockopt(), но не могу найти его в таблице на странице 193.
Пожалуйста, подскажите, как это делается.
Решение
Вы делаете это с помощью 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:
Устанавливает или получает параметр обнаружения MTU пути для сокета.Когда этот параметр включен, 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()
как упоминалось ранее), которые могут повлиять на настройку.
Другие советы
Если вы работаете в пользовательской среде с намерением обойти сетевой стек ядра и, таким образом, создаете свои собственные пакеты и заголовки и передаете их пользовательскому модулю ядра, есть вариант получше, чем 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
.
Я согласен с ответом paxdiablo's.
- setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &значение, sizeof(значение))
где 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;