Egy kis LEDE, MPTCP, több WAN, Wi-Fi hídkapcsolatok és sok tákolás

Videón a végeredmény: https://www.youtube.com/embed/1xQQuT9BGuI
Akit érdekel a projekt, lentebb megtalálja a részleteket.

A Google Summer of Code egy kis program olyan diákoknak, akik valami open source vagy free software projektbe való besegítéssel szeretnék tölteni a nyarat. Röviden annyi történik, hogy tetszőleges projektek képviselői megírják a projekt ötleteiket a GSoC szervezésének, azok kiválogatják a legjobbakat (vagy inkább legszimpatikusabbakat) amiket támogatni tudnak nyáron. Ezeket meghirdetik a projekt oldalán, majd várják rájuk a jelentkezéseket, kisebb önéletrajzzal, motivációs levéllel. Idén nyáron én is részt vettem benne, ebben a blog bejegyzésben igyekszem nem túl személyes hangvétellel ismertetni a projekt lényegét, fontosabb részeit és eredményét. Röviden: a végberendezések számára transzparens, több hálózati útvonal kapacitását MPTCP segítségével kihasználó proxy, OpenWRT/LEDE környezetben implementálva.

Kezdeném egy kisebb és személyesebb résszel, hogy minél hamarabb rátérhessek a lényegre. Hallottam már korábban is a GSoC-ról de sosem szántam rá magam, hogy jelentkezzek, mindig volt mivel elfoglalni magam nyaranta. Most is nagyjából egy hónapnyi idő volt adva a meghirdetett projektek átböngészésére, az egyetlen ami érdekelt, azt egy hét után leszedték (pontosabban az izgalmasabb alprojekteket). Utána a Matrix.org-al kezdtem szemezni, itt kísérleti transzport protokollokat kellet volna próbálgatni teljesítmény elemzési szempontból, viszont a projektet még kompromisszumokkal is nagynak éreztem. Még három nap maradt a jelentkezésből, mikor teljesen véletlenül megnéztem a Freifunk projekt oldalát. Egész jó, hálózatos alprojektek voltak, utolsóként pedig megláttam a jackpotot: “Add MPTCP support in LEDE/OpenWRT trunk”. A Debreceni Egyetemen hálózatok specializációt végeztem, BSc szakdolgozatomban meg később az MSc-s TDK dolgozatomban is a többutas hálózatokkal foglalkoztam, nem volt ismeretlen az MPTCP sem. Fel is vettem a kapcsolatot a projektet meghirdető későbbi mentorommal, aki mondta, hogy nyugodtan jöhetek, még senki nem jelentkezett. Megírtam a jelentkezést, feltöltöttem a diákomról két képet majd vártam a visszajelzést a GSoC szervezőitől. Egy hónappal később jött is, a projekt elfogadásra került, kezdődhet a munka.

Az MPTCP (RFC 6824) egy viszonylag új hálózati protokoll. Elég régóta kísérleteztek a kutatók azzal, hogyan lehetne több hálózati interfészen kiépíteni egy TCP folyamot. A fő problémára legjobb tudomásom szerint 2009-ig nem született matematikai modell mint megoldás: hogyan lehetne úgy felosztani interfészekre (például két ISP, vagy mobil eszközöknél Wi-Fi + LTE) egy TCP folyamot, hogy az végig fair maradjon a többiekkel. Például van egy TCP viszonyunk, ami két 100 Mbit/s kapcsolatot használ, ezek a távolban befutnak egy router 100 Mbit/s átbocsátóképességű interfészébe, ekkor fair módon jut 50-50 Mbit/s a TCP folyamoknak. Ha jön egy másik gép, elindíjta a maga TCP-s letöltését, az fog történni, hogy 33 Mbit/s jut neki, nekünk pedig 2x33=66 Mbit/s, vagyis unfairek leszünk vele szemben. Egy még trükkösebb probléma: hogyan ismerhető fel, hogy saját magunkkal versenyzünk a sávszélességért? Nem-e egy harmadik interfészünkön futó TCP alfolyam akivel próbálunk előzékenyek lenni másik kliensként érzékelve és 25-25-50 Mbit/s-re hasogatva a sávszélességet? A lényeg, hogy ezek már csak a múltban problémák, az MPTCP torlódásszabályozó algoritmusai megoldották őket, ma már egészen stabil kernel implementációval is rendelkezik az MPTCP.

Egyelőre csak iOS 11-en működik, nem támogatja sem Windows, sem már oprendszer. Androidon talán NUSE-n keresztül be lehet izzítani különösebb kernel módosítgatás nélkül, de ez pár napos hír, részleteket nem tudok. Minden esetre így elég problémás a több útvonal használata, ha nincs hozzá támogatás. Viszont, ha szerencsés a helyzet, a szűk keresztmetszet olyan helyen van, hogy megfelelő helyre egy TCP-MPTCP proxy-t beiktatva nyerhetünk némi sebességet vagy robosztusságot. A legizgalmasabb ugyanis, hogy az MPTCP nagyon ügyesen összegzi az egyes interfészek sebességét, közel 100%-os hatékonysággal. Sőt, ha egyik interfészen megszakad a kapcsolat - mondjuk ledob a Wi-Fi, más interfészen megy tovább a kommunikáció háborítatlanul, csak annyival lassabban, amennyi a Wi-Fi átbocsátóképessége volt. Hogyan kapcsolódik ide a Freifunk? Lényegében egy “open source” Wi-Fi mesh hálózat az egész. Bárki bekapcsolódhat saját AP-kkal. Magát a backbone-t is point-to-point irányított Wi-Fi hidak alkotják. Egy ágas-bogas mesh hálózatban nem nehéz elképzelni, hogy több útvonal is van két állomás között. Ezek közül meghatározásra kerül, hogy kinek merre van a default gateway, valamekkora loadblancing lehet van, de sok erőforrás így is állhat ott kihasználatlanul. Itt jó lenne az “üres” utakon egy MPTCP alfolyamként átterelni a forgalom egy részét. MPTCP alfolyam az egy szabályos TCP kapcsolat interfészenként, melyek között az MPTCP ütemezője osztja fel a forgalmat. Torlódás esetén újraküldhet kiesett részeket egy másik interfészen, de olyan ütemező is van, ami rendundánsan küldi minden interfészen ugyan azt az adatot.

A cél így az lett, hogy kicsiben ki lehetne próbálni, mi lenne ha Wi-Fi hídkapcsolatok sebességét aggregálnánk a kliensek tudta nélkül. Így egész sok sávszélességet nyerhetünk, mondjuk ha a kliens Gbit/s kapcsolattal éri el a routert, ott esélyesen nem lesz olyan Wi-Fi link, ami azt ki bírja hajtani. Hogy könnyebb legyen esetlegesen telepíteni, tesztelni a megoldást, minden módosításnak a routerekben kell, hogy legyen. Klienseknél ne kelljen lehetőleg semmit konfigurálni. Másik fontos dolog, hogy minél kevesebb erőforrás és viszonylag könnyű menedzsmentet igényeljen csak a dolog. Ezért lett LEDE az alaprendszer amin összeraktam a dolgokat: naprakész, friss kernel verziók rendszeresen frissítve, sok csomag, minimális ROM és RAM használat meg sok eszközt támogat. A munka három hónapig tartott, most havi bontásban röviden leírom miket csináltam.

Június. Itt kezdtem ismerkedni a LEDE-vel. Sosem fordítottam még, kipróbálni is csak néhány alkalommal, mások routerén próbáltam. Szerencsére nagyon jó manualok vannak az oldalon, ki lehet RTFM-ozni mindent ami kell. Ugyan úgy Buildroot rendszert használ mint az OpenWRT, így a régebbi leírások és doksik is működnek. Lényegében feedeket kell megadni innen húzza le, mik érhetőek el forrásból, menuconfiggal lehet a csomagokat kiválasztani. Ugyan így lehet a szokásos módon piszkálni a kernel konfigurációját is. Tudni kell, hogy a LEDE is az alap Linux kernelt használja, a közösség ehhez készíti a patch-eket, amiket a konfigurálás előtt alkalmaz az alap kódra a build környezet. Innentől egyértelmű, hogy a teljes MPTCP támogatás is egy patch kell, hogy legyen a munkám végére. A fordítás kimenetele valamilyen betölthető/flashelhető image lesz, vagy akár VirtualBox-ba betölthető VDI fájl. Én ez utóbbival gyorsan leteszteltem, az frissen klónozott LEDE fordul-e, szerencsére gond nélkül fordult és bebootolt VirtualBox-ban. Ezt a szálat ennyivel elvarrtam erre a hónapra.
A másik szál már arra az optimális esetre készült, hogy vannak MPTCP képes eszközeim: hogy lesz megoldva a szoftveres támogatás? Egy Dél-Koreai mobilszolgáltató egyes készülékeit MPTCP támogatással adja. Ők SOCKS5 proxy-val oldják meg, hogy LTE és Wi-Fi együttműködve menjen: telefon SOCKS5-el MPTCP-n kereszül éri el a szolgáltató proxy-ját ami szintén MPTCP képes, és ezen keresztül kapcsolódik ki az internetre. Valóban, ez egy “industrial strength” megoldás, csak van egy kis signaling overhead, ami nagy késleltetés esetén bosszantó lehet. Elkezdtem keresgélni valami egyszerűbb megoldás után, ami csak annyit csinál, hogy a bejövő TCP kapcsolatot terminálja, nyit egy újat (ha lehet MPTCP-t) és másolja a rakrészt. Szerencsére létezik ilyen, shadowsocks-libev a szoftver neve. Ez is kliens-szerver felépítésű, ss-redir és ss-server kombináció pont azt tudja, ami nekem kell. Mint kiderült, ez a szoftver kell vagy sem - titkosít. Teszi ezt úgy, hogy egy szálat használ összesen, így még több magos CPU-val rendelkező routereken is ha kihajt egy magot, onnan nem megy tovább. Próbaképp megnéztem a Raspberry 2 gépemen: loopbacken, a legkisebb kulcshosszú AES esetén 15 Mbit/s-t bírt el. Ez elég lehangoló volt. Végig teszteltem az összes titkosítási módot loopbacken, a leggyorsabb sem volt túl bíztató: https://pastebin.com/GaAwQtgu. Innen lenne szép nyerni.

Július. Úgy döntöttem, hogy a lehető legkevesebb rádolgozással próbálom előbb kitesztelni a szoftveres részt. Felhúztam négy virtuális gépet, mindegyiken friss Ubuntu. Kettőt kineveztem routernek (ip_forward=1) kettő meg sima végberendezés maradt. A routereknek volt saját LAN-juk, meg volt két “WAN” kapcsolat közöttük. Egyelőre minden cím statikusan lett bekonfigurálva. Nyilván itt az volt a cél első körben, hogy a routereknek kinevezett két gép egymást a két WAN kapcsolatot szimultán használva lássa, MPTCP-n keresztül. Leshapeltem 10 Mbit/s-re mindkettőt, feltelepítettem az aktuális MPTCP kernelt és csináltam egy tesztet: iperf3 20 Mbit/s-t jelzett, vagyis ez a rész működik. Ezt követően az egyik routerre felraktam és bekonfiguráltam az ss-redir-t, a másikra az ss-server-t. Alapból az ss-redir fut, de mindaddig nem történik semmi, míg nem kap bejövő TCP kapcsolatokat a saját portjára. Csak úgy lesz munkája, ha a TCP kapcsolatokat átirányítjuk neki (iptables REDIRECT extension-je). Az nem mindegy, mit adunk oda neki, a cél, hogy ne a saját TCP kapcsolatai (iptables OUTPUT lánc) hanem a LAN-ból érkező kapcsolatok legyenek átirányítva az ss-redir helyi portjára. Ehhez a REDIRECT szabályt az iptables “nat” táblájának a PREROUTING láncába kell rakni. Ez hozza az elvárt működést, az átmenő TCP kapcsolatokat terminálja és átirányítja az ss-redir számára. A túlsó routeren megint terminálásra kerülnek a már MPTCP kapcsolatok az ss-redir által és mint sima TCP kapcsolatokként folytatják az utat a cél (a másik végberendezés) felé. A műtét sikeres volt, a két kliens gép 20 Mbit/s sebességgel kommunikált egymással, mit sem sejtve a közöttük lévő MPTCP-s router proxy gépekről. Ezt a szálat erre a hónapra itt varrtam el.
A másik szál csak annyi lett volna, hogy MPTCP-sítsem a LEDE-t. Ettől eléggé féltem, sajnos a git-et eddig maximum github-ról való klónozásra használtam. Ráadásul sem a LEDE, sem a Linux kernel forrása nem ismerős, az MPTCP-s változaté a legkevésbé. Elkezdtem nézegetni a doksikat, tanulmányoztam a repository-k felépítését, felraktam a GitKraken nevű GUI-t, ami mutatja grafikusan az egyes git branch-eket és commitokat. Hamar kiderült végül, nagyjából mi az amit csinálni kell. Az MPTCP kernel akkori verziója a 4.4.70-es sima kernelre épült. A LEDE path-ei a 4.4.74-es kernelhez voltak. Naiv approach: git diff a sima és az MPTCP-vel felokosított 4.4.70-es kernel között, ezt bemásolni LEDE-be és remélni a legjobbakat. Na ez nem működött, egyes sorok nem stimmeltek a patch-elésnél, szépen leállt errorral a build. Ami sikerre vezetett, az annyi volt, hogy beállítottam az MPTCP kernel leklónozott git repojában upsteram-nek az eredeti kernelt git repoját. Ez után git fetch lehúzta az újabb kernel verziókat, innen nekem a 4.4.74-es tag volt érdekes. Az MPTCP branch-et mergeltem ebbe a tag-be, szerencsére nem volt egy ütközés sem. Ez után git diff ment az eredeti 4.4.74-es és az MPTCP-s 4.4.74-es tag-ek között. Ezt már elfogadta a LEDE is, a fordítás után belépve “sysctl -a | grep mptcp” mutatta, hogy itt bizony az MPTCP már konfigurálható.

Augusztus. Itt kezdtek érdekessé válni a dolgok. Még korábban rendeltem pár Ubiquiti NanoStation-t (2xM5 és 2xLoco M5). Használtan, 26500 forint volt a négy. Ezek régebbi verziók, nem támogatják még a 802.11ac-t, de tesztelni igazából ez nem is volt szükséges. Suliból elkértem nyári használatra egy Netgear R7000-es routert, ebben van egy egész jó két magos, 1.4 GHz-s ARM CPU. E mellé vettem még egy Netgear R7800-ast, szerintem ez a ma kapható egyik legjobb router (újabb architektúrájú 2 magos 1.7 GHz-s CPU, közel háromszor gyorsabb mint az R7000-esben van, ath10k Wi-Fi driver tehát LEDE által is támogatott, kézműves spektrum analizátornak is használható, sok RAM és flash memória). Buildeltem a saját LEDE verziómból rájuk egy-egy lemezképet, flasheltem (R7800-nál voltak gondok, párszor bootloopba került, de szerencsére pofon egyszerű TFTP-vel flashelni recovery módban) majd indítottam a routereket. Szerencsére probléma nélkül bekapcsolatk és mutatták MPTCP jeleit procfs-ben.
Korábban említettem, hogy a shadowsocks-libev titkosítási teljesítményével voltak problémák, ezért csináltam belőle egy olyan forkot, amiben a titkosítás opcionális. Pár sort és a doksit kellett csak módosítani hozzá, a lényeg, hogy “none”-t megadva cipher-nek nem titkosít. RaspberryPi 2-n ezzel az AES-es 15 Mbit/s-ről 285 Mbit/s-re nőtt a throughputja loopbacken. Ezt a módosított verziót el kellett juttatnom LEDE-re is. Azt csináltam, hogy csináltam a saját LEDE verziómnak egy feed-et, amiben egyetlen csomag van csak egyelőre, a módosított shadowsocks-libev programom. Menuconfig-ban így az eredeti ss-* csomagok mellett ott van az én változatom is ss-*-nocrypto néven. Buildeltem új LEDE lemezképeket, most már ezekkel a módosított ss-redir és ss-server verziókkal.
A képen látszik nagyjából milyen topológiát használtam. Kliens gép bekötve egyik routerhez, annak a LAN VLAN-jába, van két másik VLAN, WAN1 és WAN2 ezekbe egy-egy Wi-Fi bridge megy. A túloldal szimmetrikusan ugyan ez. Egyik router (amin az ss-redir fut) valamint a négy Wi-Fi bridge DHCP-vel kap IP-t attól a routertől, amin az ss-server is fut. Nem volt egyszerű mindent összerakni megfelelően, hektikus PoE injektorok, egy kontakt hibás Wi-Fi bridge RJ-45 port, routing furcsaságok, iptables és switch portonkénti VLAN kavarodások nehezítették a dolgot. Végül beröffent és szépen megjelent Wi-Fi hídkapcsolatok összegzett sebessége a LAN-okban lévő kliensek között futó iperf3 mérésen. Elég jó érzés volt, gondolom nem kell részleteznem a fentiek fényében :-)

Jövőbeli tervek. Jócskán van még munka. A Wi-Fi hídkapcsolatok különböző frekvenciasávot használnak, érdekes mérés lenne megnézni egy frekvenciasávon mennyivel romlik a helyzet. El tudom képzelni, hogy a sebesség elég nagy részét lenyesi a közeghozzáférési protokoll signaling-je, ezért a tényleges átvitel azonos frekvenciákon is tudnak valamekkora aggregációt mutatni. A másik az UDP forgalom. Terveim között van bekapcsolódni MPUDP-s kutatásokba - bármennyire is banálisan hangzik, egyelőre egy kísérleti hack implementáció létezik csak belőle, mert van benne pár megoldhatatlan probléma. Userspace-ben léteznek többutas VPN-ek, egynek a fejlesztésében én is részt vettem. Ezek egy IP tunnel kapcsolat forgalmát osztják fel interfészek között, így mivel hálózati rétegben mennek, bármilyen transzport protokoll átmegy rajtuk. Érdekes megfigyelés volt még, hogy a CPU erősen limitálja a shadowsocks sebsségét. Úgy tűnik, egy magot használ fel csak. Jó lenne elkészíteni egy több szálat használó verziót - már amennyire ez lehetséges. Szóval munka az akad még bőven, ezek csak a legérdekesebb irányok.

Köszönöm mindenkinek, aki elolvasta, kommentben nagyon szívesen válaszolok az esetleges kérdésekre. Egy rövidebb tutorial jön még fizikai eszközökön való reprodukcióhoz, konfigurációs fájlokkal együtt, ekkor majd frissítem a posztot, valamit várhatóan a blogomra lesz a napokban feltéve (spyff.github.io). A poszt tetején lévő YouTube videón a működés közben lévő tákolmány. Ami miatt még hasznos volt az egész, az az hogy kicsit bele láttam, hogy működik és épülget a LEDE a közösségi munkával és nagyon remélem, hogy tudok majd én is segédkezni a projektben a későbbiekben.

Linkek:
Freifunk blog posztjaim: https://blog.freifunk.net/tag/mptcp/
MPTCP LEDE github: https://github.com/spyff/lede-mptcp
Shadowsocks-libev-nocrypto github: https://github.com/spyff/shadowsocks-libev-nocrypto

update: Konfigurációs részletek lépésről lépésre: https://spyff.github.io/mptcp/2017/08/27/transparent-mptcp-proxy/

Hozzászólások

Szép munka gratulálok, vagy egy hónapja olvasgattam én is az MPTCP-ről valahogy felmerült LTE kapcsán, akkor olvastam, hogy a DE igencsak benne van a projektben.

Fedora 25, Thinkpad x220

Elvileg a magenta szolgáltatónál van olyan kütyü, amelyikben van LTE SIM, és megy bele kábel.
Azon ezek szerint még nem MPTCP van?

Azt ígérik, hogy ott is összeadódhat a két link kapacitása.
Erről tudsz valamit?

Amúgy részemről is gratulálok, szép munka!

jo munka, csak igy tovabb!

a GSoC-bol arra kovetkeztetek hogy egyetemista vagy; ha BSc/MSc diplomamunka erdekelne jo nevu kutatointezetnel OpenStack halozat + L3 kiepites temaban, szolj, tok regota keresek embert erre.

Nagyszerű írás, érdekes téma, jó volt olvasni.

ha jol ertem akkor a tcp kapcsolatnak "csak" egy setsockopt-al kell beallitani hogy az mostantol mptcp-s legyen?

ekkor viszonylag "egyszeru" lenne az openvpn-t is mptcp-siteni ("nativ" support, tehat nem openvpn-over-ssredir!)

--
A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!

Igen, vagyis MPTCP-s kernellel alapból minden socket MPTCP-s, ha ez ki van kapcsolva, akkor kell a setsockopt-al explicit bekapcsolni az adott socketen. OpenVPN esetében - most ezt úgy mondom, hogy nem ismerem teljesen az architektúráját - azzal lehet probléma, hogy TCP over TCP viselkedés miatt dadogós lesz az átvitel. Régebben olvasgattam, hogy ahol lehet mindenütt kerülik a TCP over TCP viselkedést, mert exponenciálisan felugranak a belső TCP számlálói és rosszul becsüli az erőforrásokat. Ettől függetlenül gyakorlatban, mondjuk webezéshez lehet teljesen okés és érdemes tenni vele egy próbát!

Nagyon szep munka, elvezet volt olvasni.
Varjuk a tovabbiakat is. :)