Wolny start połączeń w protokole TCP

Spis treści

Wolny start jest wynikiem braku zaufania maszyny nadawczej do utworzonego kanału przesyłowego -- połączenia. Nie wie ona czy to łącze jest bowiem w stanie obsłużyć taką porcję danych, którą ma zamiar przesłać bez czekania na pakiet ACK . W przypadku bufora odbiorczego, wszystko jest proste, bo dane dotyczące wielkości okien są zdefiniowane w nagłówku pakietów, do których ma wgląd druga ze stron. W przypadku gdy nadawca przesyła dane, prędkość z jaką to robi zależy głównie od niego.

Wolny start -- zasada działania

Po ustanowieniu połączenia, maszyny na dwóch jego krańcach próbują zwiększyć rozmiar okna TCP, tak by dane były przesyłane z prędkością obsługiwaną przez co najmniej jedną ze stron. Podczas tego procesu, połączenie przechodzi w fazę wykładniczego wzrostu (exponential growth). Rozmiar okna TCP jest zwiększany z każdym otrzymanym pakietem ACK o liczbę zaakceptowanych segmentów (dwukrotnie co kolejny RTT). Trwa to do chwili gdy jakieś potwierdzenie nie zostanie dostarczone lub gdy zostanie osiągnięty próg wielkości okna TCP. Gdy to nastąpi, połączenie przechodzi w fazę liniowego wzrostu (linear growth), podczas której wielkość okna jest zwiększana o rozmiar jednego segmentu z każdym RTT i trwa to do momentu utraty potwierdzenia. Cały ten proces od ustanowienia połączenia do końca drugiej fazy nazywa się wolnym startem.

Kluczowym elementem wolnego startu jest CWND (Congestion Window Size), czyli bufor nadawczy hosta wysyłającego dane. Odpowiada on za ilość segmentów składających się na początkowe okno TCP. Domyślnie ten parametr ma różną wartość i zależy od konkretnego systemu operacyjnego. Na linuxach było to z początku 4*MSS (Maximum Segment Size). Obecnie jest to wartość 10*MSS. Jako, że MSS to zwykle 1460 bajtów, to rozmiar okna, który jest zgłaszany w pakietach SYN wynosi 14600 bajtów.

Zmiana wielkości okna początkowego

Wraz ze wzrostem przepustowości łącz oraz przez to, że połączenia są zwykle krótsze niż kiedyś, sporo z nich nigdy nie rozwija pełnej prędkości, właśnie ze względu na procedurę wolnego startu, co pociąga za sobą zwiększenie opóźnień. Na dobrą sprawę, optymalny rozmiar początkowego okna to około 16 do 20 pakietów. Nie ma jednak prostego sposobu, by zweryfikować jaki rozmiar okna jest ustawiony na danym linuxie. No chyba, że wywnioskujemy to ze sniffera pakietów lub zajrzymy w źródła kernela.

Jeśli ktoś chce odszukać wartość w kodzie kernela, to wystarczy pobrać pakiet linux-source i wypakować paczkę .xz , która zostanie wgrana do katalogu /usr/src/ . Następnie, posługując się poniższym poleceniem, zostanie nam objawiona poszukiwana wartość:

# grep -A 2 initcwnd `find /usr/src/linux/include -type f -iname '*h'`
...
/usr/src/linux/include/net/tcp.h-#define TCP_INIT_CWND          10

Zatem jest to 10 pakietów. Możemy zwiększyć tę wartość do 20, np. przez dopisanie do pliku /etc/network/interfaces poniższej zawartości:

post-up ip route change default via 192.168.1.1 dev eth0 initcwnd 20 initrwnd 20

Jeśli teraz odpalimy sniffer sieciowy, np. wireshark, to powinniśmy ujrzeć wartość 29200 w opcjach okna TCP w pakietach SYN , poniżej fotka:

wolny-start-okno-poczatkowe-wireshark

Zagubione pakiety oraz połączenia IDLE

Faza wolnego startu może pojawić się w kilku miejscach połączenia, pomijając oczywiście jej występowanie na samym początku transferu danych. Jedną z takich sytuacji jest przypadek, w którym następuje retransmisja pakietu, który uległ zagubieniu. Ogólnie rzecz biorąc, chodzi o to, że gdy nadawca przesyła pakiet do odbiorcy, to ma przy tym pewien przedział czasu, w obrębie którego odbiorca musi odesłać pakiet ACK potwierdzający odbiór danych. W chwili gdy upływa ten czas i nadawca nie otrzymał takiego potwierdzenia, retransmituje pakiet. Z chwilą gdy to robi połączenie przechodzi w stan wolnego startu w celu ustalenia optymalnej prędkości przesyłu danych. Z tym, że w zależności od zastosowanego algorytmu, ten proces może przebiegać nieco inaczej.

Inną sytuacją jest stan bezczynności połączenia. To taki stan, gdzie sesja jest aktywna ale przez dłuższy okres czasu nie zostały przesłane żadne dane za wyjątkiem czegoś co się nazywa pakiety keepalive, np. połączenia SSL/TLS. Gdy dochodzi do ponownej transmisji danych, trzeba na nowo ustalić dla nich rozmiar okna, czyli połączenie znowu przechodzi w stan wolnego startu. W tym przypadku mamy jednak nieco większe pole manewru i jesteśmy w stanie zadecydować czy wolny start w przypadku połączeń IDLE ma mieć miejsce czy też nie. Jeśli zdecydujemy się wyłączyć tę opcję, to po wznowieniu transmisji dane będą przesyłane w oknach o rozmiarach ustalonych wcześniej, zanim połączenie weszło stan IDLE.

Poniżej jest parametr wyłączający wolny start dla połączeń IDLE. Trzeba go dopisać do pliku /etc/sysctl.conf :

net.ipv4.tcp_slow_start_after_idle = 0

By zmiany weszły w życie, wydajemy poniższe polecenie:

# sysctl -p
Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.