"HUP bug": TCP window scaling és a hibás routerek

Címkék

Mert már többen kérdezték...

A 2.6.7-es kernelben az alapértelmezett TCP window scaling értéke 0. Linus BK fájában, a legfrissebb -mm kernelekben és a 2.6.8-rc1-es kernelben ez az érték 7. Ennek a változtatásnak köszönhetően előjött egy ún. HUP bug, amelyet jeleztem a kernel fejlesztőknek.
A jelenséget zleho tagunk fedezte fel. A jelenség az volt, hogy a legújabb 2.6.7-mm1 - 2.6.7-mm4 kernelekkel nem működött a HUP. Miután leteszteltem a bug reportot és valóban azt tapasztaltam, amit zleho írt, jelentettem a furcsa viselkedést az LKML-en.

A levélre Stephen Hemminger azt válaszolta, hogy David S. Miller a legújabb kernelekben engedélyezte a TCP window scaling opciót, amely okozhatja ezt a hibát. Közben nekem privátban megírták, hogy nem csak a HUP nem megy ezekkel a kernelekkel, hanem gondok vannak a packages.gentoo.org oldallal is. Folyt a debug-olás, mikor John Heffner jelezte, hogy neki volt már egy évvel ezelőtt hasonló tapasztalata. Akkor a gondot a távoli gép előtt levő régi OpenBSD csomagszűrő okozta, amely megcsonkította a TCP csomagokban levő window scale opciót. Ekkor terelődött a gyanú a HUP előtt levő OpenBSD csomagszűrőre. Elképzelhető, hogy az nem tudja kezeli a TCP window scaling-et. Ekkor kiderült, hogy a Gentoo oldal is egy meglehetősen régi (2.4.20-as) Linux kernelt futtat, amely szintén gyanúsítható azzal, hogy rosszul kezeli ezt a TCP opciót.
Mivel nem csak ez a két gép érintett ebben a problémában (hiszen számos más tűzfal terméket használnak az emberek az interneten), Stephen Hemminger elkészített egy patchet, amely orvosolhatja a hibát. A patch kiszámítja azt a TCP window scaling értéket, amely a lehetséges legnagyobb TCP ablakot adja úgy, hogy a hálózat még működik. A legtöbb esetben a patch a net.ipv4.tcp_default_win_scale értékét 2-re állítja, amely még működik a hibás routerekkel, tűzfalakkal. Sajnos a patch David S. Miller és a TCP alrendszer fejlesztői által elvetésre kerültek (thread). A fejlesztők álláspontja az, hogy a hibás tűzfalak működését kompenzáló workaround hozzáadása a Linux kernelhez hiba lenne. Szerintük a megoldás az, hogy nyomást kell gyakorolni a router gyártókra, hogy azok javítsák ki a hibás ``termékeiket''. Úgy gondolják, hogy a Linuxnak van akkora súlya már a hálózati világban, hogy képes hatni a gyártókra. Egyébként a TCP window scaling nem új találmány. Minden TCP csomag fejlécében található egy window mező. A window egy olyan flow control a TCP-ben, amely meghatározza, hogy mekkora a maximálisan átvihető adat mérete a kommunikációban részt vevő hostok között, mekkora a maximális adat méret, amelyet a ``másik oldal'' fogadni képes. A TCP gyerekkorában ez a window érték meglehetősen kicsi volt, mert a gépek limitált memória mérettel rendelkeztek (nem voltak képesek nagy mennyiségű adatot pufferelni), és a hálózati eszközök sem voltak olyan kifinomultak és nagy teljesítményűek, mint manapság. A modern eszközök már képesek a nagyobb TCP csomagok fogadására és hatékony kezelésére, ezért a nagyobb teljesítmény elérése érdekében nagyobb TCP window értéket is használhatunk.
A TCP window mező szélessége 16 bitnyi, így a maximális window méret 64 kbyte lehet. A TCP dizájnerek soha nem gondolták, hogy bárkinek is szüksége lehet ennél nagyobb értékre. A 64 kbyte pedig úgy tűnik nem elég napjainkban, valahogy emleni kellene ezt az értéket. A megoldást a TCP window scaling jelenti, amely már 1992 óta létező dolog. A TCP window scaling leírását az RFC1323 fogja össze. Ezzel az eljárással a kommunkációban levő felek nagyobb adatmennyiséget is átvihetnek egymás közt, akkor ha ezt ``megbeszélik''.
Annak érdekében, hogy a TCP window scaling-et nem ismerő gépek TCP rendszerében ez ne okozzon hibát, a kommunikáció kezdeti szakaszában egy SYN csomagban elküldésre kerül a TCP window scaling opció, és csak akkor kerül alkalmazásra, ha a válaszként visszajövő SYN+ACK csomag szintén tartalmazza ezt az opciót. Maga a scale faktor (hogy mekkora legyen a windows scaling) megbeszélése a setup handshake része, az a későbbiekben nem kerül módosításra.

A probléma ott kezdődik, mikor a kommunikációban részt vevő felek között elhelyezkedik egy hibás router. Vannak olyan routerek, amelyek megváltoztatják a rajtuk keresztül haladó window scale TCP opciót a SYN csomagokban. Az eredmény: a két host között káosz lesz, mert nem tudják egyeztetni a window scaling mértékét. A feltevés az, hogy a hibás router kicserélni a küldött windows scaling értéket 0-ra, de az opciót a helyén hagyja. A ``vevő'' gép érzékeli, hogy van window scaling, el is küldi a saját értékét. A ``küldő'' gép látja, hogy a window scaling elfogadásra került, meg is kezdi a window scaling-et ennek megfelelően. Mivel a ``vevő'' gép azt hiszi, hogy a scaling 0 (mert a router kicserélte az eredeti értéket), az egészből egy össze-visszaság alakul ki.

Térjünk vissza az elejére. Tehát a Linux kernel újabb verzióiban a window scaling értéke 7. A te gépet ezt az értéket küldi a SYN csomagban, (valószínűleg) a HUP előtti csomagszűrő ezt átírja 0-ra, de meghagyja az beállított értéket. A HUP azt hiszi, hogy van window scaling, elküldi a saját scaling faktorát. A te géped azt hiszi, hogy minden OK, el is kezdi a window scaling-et. A probléma az, hogy a HUP azt hiszi, hogy 0 a window scaling, és ennek megfelelően viselkedik.
A megoldás, frissíteni kell a HUP előtti csomagszűrőt (ami meg is fog történni a közeljövőben).

Workaround: ki kell kapcsolni a problémás kernelekben a window scaling opciót. Ezt a net.ipv4.tcp_default_win_scale érték megváltoztatásával lehet elérni az alábbi módon (root-ként természetesen):

sysctl -w net.ipv4.tcp_default_win_scale=0
(tapasztalat szerint az 1 és 2 értékkel még működik)

Maradandó beállítást a /etc/sysctl.conf-ban lehet elkövetni.

A dologgal foglalkozik az LWN-net is itt.

Hozzászólások

Ez csak az en velemenyem ...de szerintem ha 64 kilobyte nal nagyobb csomagot kuldesz hasznalj udp t ...

Pl jatek adatfolyam tcp kommunikaciojanalkifejezetten hatrany a nagymeretu adatcsomag... Nincsezmaskeppahttp eseten is hisz a legtobb file nem eri el a 64 k t. De a scaling nem minden adatcsomagot kuld mas meretekkel igy a savterhelesed nagyobb lehett...hisza16bit meg marad... Baar lehett megint hulyevagyok...

"de szerintem ha 64 kilobyte nal nagyobb csomagot kuldesz hasznalj udp"

A max csomagmeret 64k, de gyakorlatban nem mennek 1.5k fole.

Mindegy, ez az egesz egyebkent sem a csomagmeretekol szol.

Trey: ez a hiba a 3.5-osben is benne van? Reportolta valaki az OpenBSD-seknek?

netchan

Hat egyelore nem tudom, hogy az OpenBSD-ben van-e a hiba (csak minden jel arra utal), mert en nem ferek hozza (Bp-en van IIRC az Axelero adatparkban, ahova nem konnyu bejutni (elotte idopontot kell kerni, stb.)). A csomagszurot mas tartja karban, nem csak a HUP-nak szur. A verziojat sem ismerem, de gyanitom, hogy nem a legujabb kiadasok egyike. Azt hogy a 3.5-ben benne van-e, azt nem tudom, de szandekomban all kiprobalni meg ma. Elotte azonban kellene a csomagszuro pf konfja, amit el kell kernem az illetekstol. Ha benne van a hiba az OBSD 3.5-ben is, akkor bugrepotolok majd a listara. Addig nem tudok mit tenni.

A 3.5-os OpenBSD-ben van egy net.inet.tcp.rfc1323 nevű sysctl. A HUP előtt lévő OpenBSD-ben nincs ilyen? Nem lenne elég ennek az átállítása?

Igen, ezt mar neztem en is. Ennek az erteke alapertelmezes szerint 1 az OpenBSD 3.5-ben. Sajnos a csomagszurohoz nem ferek hozza, igy nem ismerem a verziojat... De ha lenne benne akkor ott is 1 lenne szerintem az erteke... Egy upgrade minden esetre esedekes ugyis...

Mivel nem csak ez a két gép érintett ebben a problémában (hiszen számos más tűzfal terméket használnak az emberek az interneten)

Hát nem tudom...Emiatt szerintem elég gáz az a hozzáállás, hogy

A fejlesztők álláspontja az, hogy a hibás tűzfalak működését kompenzáló workaround hozzáadása a Linux kernelhez hiba lenne.

Szerintem korai volt ez a változtatás, nem fogják az egyébként jól működő csomagszűrő rendszereket

a 2.6.8+-os linux kernel kedvéért átírni. Nem lennék meglepve ha a patch még a végleges kiadás elött bekerülne a kernelbe.

Főleg azért, mert még behatárolni sem lehet, hogy ez az inkompatiblitás pontosan mit is érint. Régebbi linux kernelt, régebbi openbsd-t, "routereket", de még ki tudja mit érint most, vagy fog a közeljövőben.

Lehet, hogy megérné így alkalmazni a tcp window scalinget, ahogyan a 2.6.8 szeretné de nem biztos, hogy a "hálózati világ" ma még erre a "módosításra" készen áll.

Ha cinikus lennék, most azt mondanám, hogy az RFC sokszor csak egy dolog, a "gyakorlat" meg egy gyakran egészen más dolog. De ugye nem vagyok az, úgyhogy nem mondom. -)

De szerintem is meg kéne hagyni a status quo-t...

Viszont túl sok "energiát" feccöltek már bele, úgyhogy szerintem már "csak azért sem" fognak a fejlesztők visszatáncolni, ezért gondolom hogy végül a nyomásra mégiscsak berakják a patchet, de nem fognak vissztérni a 0-hoz.

Szerintem a legnagyobb baromság volna berakni a hibás eszközök támogatásáára mindenféle workaroundot, ez az, ami a számítástechnika legkülönbözőbb helyeiben (lásd pl. html) inkompatibilitáshoz, széthúzáshoz, szabványtól eltéréshez, végső soron káoszhoz vezet. Ha egy eszköz hibás, akkor _azt_ kell megjavítani, nem a környékét hozzáigazítani.

Ettől függetlenül lehet hogy praktikus okok miatt maradhatna még egy darabig 0-n a default érték.

Lehet hogy 12 éves a window scaling feature, de a cikk írta, hogy vszleg a 2.4.20-as Linux kernel is hibás, márpedig az azért még nem ilyen régi.

Egyébként köszi Trey az összefoglaló cikkért, érdemes volt elolvasni, a korábbi apró részletek alapján nem állt össze számomra a kép (vagy inkább nem is érdekelt?) hogy miről van szó, de így már tiszta.

Kitettem egy tesztfile-t ide:

http://cape.vanth.org/dltest/dltest.bin

Ez egy 1 megas 0-kbol allo binaris file, OpenBSD-s szerveren egy masik OpenBSD csomagszuro mogott (mindketto 3.5).

Akinek erintett kernele van, tenne egy probat?

Van egy buta kerdesem, es elorebocsjtom, h nem ertek a TCP/IP-hez!!!

Amit trey leirt az szamomra ertheto volt, feldolgoztam az infokat es megnyugodtam, beraktam 1 init scriptbe a kivant sort. Szoval minden OK, de visszagondolva amikor a megoldas meg nem volt ismert szamomra, akkor is bejott az oldal, de nem teljesen.

Szoval a kerdesem, h addig miert jutott el a betoltes? Akkor nem kellett volna mar az elejen osszeakadnia?

1 otlet volt szamomra, h veletlen, h mikor bukik ki a csomag meret egyeztetes, de elvetettem mert hat mindig es mindenkinek ugyanott allt meg.

Remelem nem zavartam fel evvel nagyon a kedelyeket, de tenyleg nem ertem:))

Csak egy kérdés:

Az érték, amiről beszélünk, hol található meg a kernelben? (Azaz melyik fájl).

(Épp egy RedHat 9 elött ülök, s a sysctl.conf-ban ez speciel nincs benne... Amúgy csak azért érdekel, mert épp most foglalkozok egyenlőre alap szinten a TCP/IP működésével, s jó lenne ilyet kódként is látni...)