Как да задам флага за не фрагментиране (DF) на сокет?

Опитвам се да задам DF (флаг за не фрагментиране) за изпращане на пакети чрез UDP.

Разглеждайки книгата на Ричард Стивън, том 1 Unix Network Programming; 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: Задава или получава настройката Path MTU Discovery за сокет. Когато е активиран, Linux ще извърши Path MTU Discovery, както е дефинирано в 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 след setockopt, за да потвърдите, че е готово. - person nik; 10.06.2009
comment
IP_DONTFRAG не е дефиниран (грешка на компилатора) за моята система Centos 4. Освен това не намирам никъде в програмата си, където е зададено, но tcpdump() предполага, че е зададено, и получих „съобщение твърде дълго“ (90) EMSGSIZE грешка, което предполага, че също е включено и пакетът е твърде голям, което предполага, че маршрутизирането се промени по средата на трансфера след много успешни пакети към маршрут с по-малък 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

Ако работите в Userland с намерението да заобиколите мрежовия стек на ядрото и по този начин да изградите свои собствени пакети и заглавки и да ги предадете на персонализиран модул на ядрото, има по-добър вариант от setsockopt().

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

Като се замислите, има смисъл да групирате тези две неща, тъй като флаговете са свързани с фрагментацията. Съгласно 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