Jak MTU wynosi 65535 w UDP, ale Ethernet nie pozwala na rozmiar ramki większy niż 1500 bajtów

11

Korzystam z szybkiego Ethernetu 100 Mbps, którego rozmiar ramki jest mniejszy niż 1500 bajtów (1472 bajtów dla ładunku zgodnie z moim podręcznikiem). Dzięki temu mogłem wysyłać i odbierać pakiet UDP o rozmiarze wiadomości 65507 bajtów, co oznacza, że ​​rozmiar pakietu wynosił 65507 + 20 (nagłówek IP) + 8 (nagłówek UDP) = 65535.

Jeśli rozmiar samej ramki wynosi maksymalnie 1472 bajtów (jak w moim podręczniku), w jaki sposób rozmiar pakietu IP może być większy niż tutaj, który wynosi 65535?

Użyłem kodu nadawcy jako

char buffer[100000];
for (int i = 1; i < 100000; i++)
{
    int len = send (socket_id, buffer, i);
    printf("%d\n", len);
}

Kod odbiorcy jako

while (len = recv (socket_id, buffer, 100000))
{
     printf("%d\n". len);
}

I zaobserwowano, że send returns -1na i > 65507i recvnadrukami lub odbiera pakiet maximum of length 65507.

sysadmin1138
źródło

Odpowiedzi:

11

Datagramy UDP mają niewiele wspólnego z rozmiarem MTU, możesz je ustawić tak duże, jak chcesz, aż do 64K, jak wspomniano powyżej. Możesz nawet wysłać jeden z nich w całym pakiecie, o ile używasz dużych ramek o rozmiarze większym niż duży datagram.

Jednak ramki typu jumbo muszą być obsługiwane przez cały sprzęt, który będzie pomijany, co stanowi problem. Dla celów praktycznych ramki Ethernet są najczęstszym rozmiarem transportu, MTU dla nich wynosi około 1500 bajtów, powiem 1500 w przyszłości, ale nie zawsze. Gdy utworzysz datagram UDP większy niż podstawowa jednostka MTU (która, jak wskazano, to najczęściej ethernet), zostanie on po cichu podzielony na 1500 ramek bajtowych. Jeśli tcpdump ten ruch, zobaczysz liczbę pakietów uszkodzonych na granicy MTU, które będą miały ustawione więcej flag fragmentów wraz z liczbą fragmentów. Pierwszy pakiet będzie miał numer fragmentu 0, a więcej ustawionych fragmentów, a ostatni będzie miał niezerową liczbę fragmentów i więcej nie ustawionych fragmentów.

Dlaczego więc to obchodzi? Szczegóły dotyczące implementacji mają znaczenie. Fragmentacja może zaszkodzić wydajności w sieci, nie jest to już duży problem, ale jeden, o którym należy pamiętać. Jeśli użyje się ogromnego rozmiaru datagramu, to w przypadku utraty jakiegokolwiek fragmentu całe datagramy będą musiały zostać wysłane ponownie. Równie przy dużych ilościach, a dziś są to objętości doskonale osiągalne, wówczas możliwe jest błędne skojarzenie ram przy ponownym montażu. Mogą również wystąpić problemy z uzyskaniem fragmentów pakietów UDP w celu przejścia przez konfiguracje zapory korporacyjnej, w których moduły równoważące obciążenie rozłożą pakiety, jeśli jeden fragment znajduje się na jednej zaporze, a drugi na innym, ruch zostanie odrzucony jako niekompletny.

Nie twórz więc datagramów UDP większych niż fragmentacja wielkości MTU, chyba że musisz i musisz określić, że infrastruktura, która jest komunikowana między nimi, jest zamknięta (ta sama podsieć zamknięta), w którym to momencie jumbo ramki byłyby prawdopodobnie dobrą opcją.

Martyn A.
źródło
Dobra informacja na temat flagi „więcej fragmentów”. Czy to flaga w nagłówku UDP czy w nagłówku IP?
Jan Jezus
Uwaga: niektóre systemy operacyjne NIE będą transmitować UDP, jeśli dane zostaną pofragmentowane. By default, Linux UDP does path MTU (Maximum Transmission Unit) discovery. This means the kernel will keep track of the MTU to a specific target IP address and return EMSGSIZE when a UDP packet write exceeds it.
Dokumentacja
2

Warstwa IP podzieli twój pakiet na końcu wysyłającym, a następnie ponownie złoży go z powrotem na końcu odbierającym, przed przekazaniem go do UDP. Z warstwy UDP nie można tak naprawdę stwierdzić, że pakiet został pofragmentowany. Jeśli używasz narzędzia do przechwytywania pakietów, takiego jak Wireshark , powinieneś zobaczyć, że twój komputer odbiera pakiety IP ograniczone do MTU.

Jeff
źródło
1

Okazuje się, że zezwolenie stosowi TCP / IP na fragmentowanie pakietów w razie potrzeby jest znacznie niższe narzut niż wysyłanie pojedynczych pakietów.

geekozaur
źródło
1
Czy masz na myśli to, że TCP / IP ulega fragmentacji i ponownemu składaniu? Jeśli tak, to dlaczego ludzie cały czas mówią, że kod powinien zająć się ponownym montażem na końcu odbiornika. Na razie nie obserwowałem fragmentacji, ale widziałem wiele forów, które to mówią, a nawet ludzi to akceptujących.
Dla tych z nas, którzy są zakwestionowani przez model OSI, czy mógłbyś dodać nieco więcej szczegółów do swojej odpowiedzi?
Robert Harvey
Byłem trochę klatkowy, ponieważ nie wiem, czy to zadanie domowe, czy nie. Jest to kompromis: ponieważ UDP nie daje gwarancji dostarczenia, jeśli jakiś fragment pakietu zostanie upuszczony, cały pakiet zostanie utracony. Jeśli chcesz mieć niezawodny transport na szczycie UDP, musisz sobie z tym wszystkim poradzić; ale jeśli robisz (powiedzmy) protokoły przesyłania strumieniowego (lub NFS przez UDP, który obrał ścieżkę podobną do przesyłania strumieniowego), to jest to mniejszy narzut, aby po prostu upuścić te pakiety lub retransmitować większy pakiet po długim opóźnieniu, jeśli to konieczne. Musisz zrównoważyć swoje potrzeby z funkcjami protokołu i narzutami protokołu.
geekozaur
1

UDP nic nie wie o MTU. Pakiety UDP mogą mieć dowolny rozmiar od 8 do 65535 bajtów. Warstwy protokołu poniżej UDP albo mogą wysłać pakiet o określonym rozmiarze, albo odrzucą wysyłanie tego pakietu z błędem, jeśli jest zbyt duży.

Warstwa poniżej UDP to zwykle IP, IPv4 lub IPv6. A pakiet IP może mieć dowolny rozmiar od 20 (IPv4) / 40 (IPv6) do 65535 bajtów, czyli tyle samo co UDP. Jednak IP obsługuje mechanizm zwany fragmentacją . Jeśli pakiet IP ma większy rozmiar niż ten, który może transportować poniższa warstwa, IP może podzielić pojedynczy pakiet na wiele pakietów zwanych fragmentami. Każdy fragment jest w rzeczywistości własnym pakietem IP (ma własny nagłówek IP) i jest również wysyłany samodzielnie do miejsca docelowego; zadaniem miejsca docelowego jest zebranie wszystkich fragmentów i odbudowanie z nich pełnego pakietu przed przekazaniem odebranych danych na następnej wyższej warstwie (np. UDP).

Protokół Ethernet może transportować tylko ramki o ładunku między 46 a 1500 bajtów (są wyjątki, ale wykracza to poza zakres tej odpowiedzi). Jeśli dane ładunku są mniejsze niż 46 bajtów, są dopełniane, aby mieć dokładnie 46 bajtów. Jeśli dane ładunku przekraczają 1500 bajtów, interfejs odmówi ich przyjęcia. Jeśli tak się stanie, to od warstwy IP zależy teraz fragmentowanie pakietu, aby żaden fragment nie był większy niż 1500 bajtów, lub zgłoszenie błędu do następnej wyższej warstwy, jeśli fragmentacja została wyłączona lub zabroniona dla tego konkretnego połączenia.

Na ogół należy unikać fragmentacji, jak

  • marnuje zasoby po stronie nadawcy.
  • marnuje zasoby po stronie odbiornika.
  • zwiększa obciążenie protokołu dla tej samej ilości danych ładunku.
  • jeśli pojedynczy fragment zostanie utracony, cały pakiet zostanie utracony.
  • jeśli pojedynczy fragment jest uszkodzony, cały pakiet jest uszkodzony.
  • w przypadku ponownego wysłania wszystkie fragmenty muszą zostać wysłane ponownie.

Właśnie dlatego TCP inteligentnie przyjmuje rozmiar ramki, aby pakiety nigdy nie wymagały IP do ich fragmentacji. Można tego dokonać zakazując fragmentacji pakietów IP, a jeśli IP zgłasza, że ​​pakiet jest zbyt duży, aby go wysłać, TCP zmniejsza rozmiar ramki i próbuje ponownie, dopóki nie zostanie zgłoszony żaden błąd.

Jednak w przypadku UDP byłoby to zadaniem samej aplikacji, ponieważ UDP jest „głupim” protokołem, nie ma własnej logiki zarządzania, co czyni go bardzo elastycznym, szybkim i prostym.

Jedyny rozmiar UDP, na którym można zawsze polegać na transporcie, to 576 minus 8 bajtów nagłówka UDP i minus 20 (v4) / 40 (v6) bajtów nagłówka IP, ponieważ standard IP wymaga, aby każdy host IP mógł odbierać pakiety IP z całkowity rozmiar 576 bajtów. Implementacja protokołu nie będzie zgodna ze standardem, jeśli nie będzie akceptować pakietów co najmniej tego rozmiaru. Zauważ jednak, że standard nie mówi 576 bez fragmentacji, więc nawet pakiet 576 bajtów może zostać podzielony między dwa hosty.

Jedyny rozmiar pakietu, na którym można polegać, że można go przenosić bez fragmentacji, to 24 bajty dla IPv4 i 56 bajtów IPv6, ponieważ najmniejsze nagłówki IP dla fragmentu to 20/48 bajtów (v4 / v6), a fragment musi mieć co najmniej 4/8 dane ładunku bajtów (v4 / v6). Zatem system transportowy poniżej warstwy IP, który nie może transportować co najmniej pakietów o tych rozmiarach, nie może być wykorzystywany do transportu ruchu IP.

I zanim ktokolwiek komentuje, że nagłówek IPv6 ma tylko 40 bajtów: To prawda, ale w przeciwieństwie do nagłówka IPv4, standardowy nagłówek IPv6 nie ma pól nagłówka do fragmentacji. Jeśli pakiet musi zostać pofragmentowany, nagłówek rozszerzenia fragmentacji należy dodać poniżej nagłówka podstawowego IPv6, a ten nagłówek rozszerzenia ma 8 bajtów. Również w przeciwieństwie do IPv4, przesunięcia fragmentacji w IPv6 są liczone w 8 bajtach, a nie w 4 bajtach, dlatego fragment może przenosić tylko ładunek będący wielokrotnością 8 bajtów w przypadku IPv6.

Mecki
źródło
0

Aby odpowiedzieć na twoje pytanie: „Jeśli rozmiar samej ramki wynosi maksymalnie 1472 bajtów (jak w moim podręczniku), w jaki sposób rozmiar pakietu IP może być większy niż ten, który jest tutaj 65535?”

Wynika to z funkcji odciążania zwanej UFO (UDP Fragmentation Offload). Proszę odnieść się do tego linku.

Możesz weryfikować i przełączać funkcje rozładowywania za pomocą odpowiednio ethtool -k ethX i ethtool -K ethX.

Nehal Dattani
źródło
0

Jeśli monitorujesz wychodzące ramki, możliwe, że karta sieciowa obsługuje odciążanie segmentacji i jest włączona. Po włączeniu odciążania segmentacji sama karta sieciowa obsługuje segmentację pakietu / ramki do odpowiedniego rozmiaru, a nie stosu sieciowego. To zwalnia procesor komputera do wykonywania innych zadań, poprawiając wydajność. W systemie Linux „ethtool -k [device]” pokaże flagi odciążania.

Andrew Bowers
źródło