Android VPN: descriptor ng VpnService laban sa root tun device
Hindi kayang gumawa ng tun0 ng mga Android app: ano ang ibinabalik ng establish(), saan humihinto ang Binder at saan nagsisimula ang JNI, at kailan nananalo ang device na ginawa ng root.
Dalawang landas ang makakapaglagay ng IP interface sa harap ng isang Android app, at parehong nagtatapos sa parehong uri ng file descriptor na may magkaibang magkaibang may-ari. Ang isa ay dumadaan sa VpnService at sa builder nito, ang isa sa root shell at /dev/net/tun. Bawat pagbasa sa descriptor na iyon ay isang palabas na packet at bawat pagsulat ay isang papasok na packet. Ang naghihiwalay sa mga landas ay kung sino ang may hawak ng patakaran, sino ang nakakaligtas sa isang OS upgrade, at gaano karami sa routing table ang maaari mong hawakan.
Piliin ang landas ng platform maliban kung kaya mong pangalanan ang partikular na bagay na tinatanggihan nitong gawin. Ang pagtangging iyon ay karaniwang TAP, paghuli sa labas ng sarili mong app set, o isang routing rule na hindi kailanman inilalantad ng framework.
Sa landas ng platform, nagdedeklara ka ng layunin sa halip ng mga utos. Nagbibigay ka ng hindi bababa sa isang address, ang mga rutang gusto mo, mga DNS server at isang opsyonal na listahan ng app, pagkatapos ay ginagawa ng system service ng Android ang privileged setup sa ilalim ng sarili nitong pagkakakilanlan. Hindi mo kailanman nalalaman ang pangalan ng interface nang maaga at hindi mo kailanman inilalabas ang ioctl na gumagawa nito, at iyon ang punto.
Dalawang paraan upang makakuha ng IP interface
Ang landas ng root ay ang mas lumang resipe. Binubuksan mo ang /dev/net/tun, tinatawag ang TUNSETIFF kasama ng IFF_TUN, itinataas ang interface gamit ang ip link, at manu-manong nagdaragdag ng mga address at ruta. Bawat hakbang ay sa iyo upang gawin nang tama, kabilang ang mga hahawakan sana ng framework nang tahimik. Isang kulang na ruta ay nagbibigay sa user ng parehong ulat tulad ng isang patay na server.
Ang debugging ay kasing iba ng setup. Sa landas ng root, ang interface ay umiiral o ang iyong ioctl ay nagbalik ng error na mababasa mo nang direkta. Sa landas ng platform, ang interface ay umiiral lamang pagkatapos magtagumpay ang establish(), kaya ang kabiguan ay dumarating bilang exception o null descriptor sa halip na isang device node na maaari mong kalikutin.
Ano ang talagang ibinabalik ng establish()
Ang Builder.establish() ay may tatlong kinalabasan at kailangan nila ng tatlong magkaibang tugon. Ang descriptor ay nangangahulugang ang interface ay naka-configure at sa iyo upang gamitin, ang null ay nangangahulugang hindi nakapasok ang kahilingan, at ang itinapong exception ay nangangahulugang tinanggihan ito ng system service. Ang pagsasama ng null at tagumpay sa isang boolean ay ang pinakakaraniwang paraan ng isang app upang iulat ang gumaganang tunnel na hindi kailanman may dalang packet. Ang Builder reference ay nagsasaad ng pangangailangan sa address, at sulit itong basahin bago mo sisihin ang server.
Ang mga direksyon ay itinatakda ng descriptor, hindi ng iyong protocol. Ang mga pagbasa ay nagbibigay sa iyo ng mga palabas na packet na napili na para sa VPN, at ang mga pagsulat ay naglalagay ng mga papasok na packet pabalik sa interface. Ang sarili mong transport ang nag-ee-encrypt, nag-a-authenticate at nag-frame ng mga packet na iyon, dahil humihinto ang platform sa hangganan ng packet.
Ang Binder ay isang hakbang, ang JNI ay isa pang axis
Ang kahilingan ng builder ay tumatawid sa system sa pamamagitan ng Binder, at saanman sa ilalim nito ay tinatawag ng platform ang native code upang i-configure ang device. Iyon ay dalawang magkaibang mekanismo na nakasalansan sa isang tawag. Ang Binder ay komunikasyon sa pagitan ng mga proseso sa pagitan ng iyong app at isang system service, habang ang JNI ay ang tulay mula Java patungong native sa loob ng isang proseso. Ang pagbasa ng AOSP tree ng Android 9 ay nagpapakita ng stack nang literal, at sulit tandaan na ang mga panloob na pangalan ng klase at landas ng file ay gumagalaw sa pagitan ng mga release.
Ang hindi gumagalaw ay ang kontratang maaari mong asahan: ang builder, ang descriptor na ibinabalik nito, at ang lifecycle ng service sa paligid nito. Walang app ang dapat tumawag ng panloob na service transaction o muling gumawa ng device node upang makakuha ng interface. Kung ang iyong code ay umaasa sa isang pangalan ng klase ng AOSP, masisira ito sa isang release na hindi mo sinubukan.
Ang TUN ay may dalang IP, ang TAP ay may dalang Ethernet
Ang TUN ay isang IP interface. Ang TAP ay isang Ethernet interface, at ang pagkakaibang iyon ang nagtatakda kung posible man ang bridging. Ang TUN device ay walang MAC address at walang Ethernet header na ipasa, kaya ang TUN client ay hindi makakasali sa isang layer-2 bridge, gaano man isulat ang mga ruta.
Ang VpnService ay nagbibigay sa iyo ng IP variant, na siyang kailangan ng VPN sa antas ng app at kayang suportahan nang walang raw Ethernet stack. Kung ang iyong disenyo ay nangangailangan ng bridging, hindi ka dadalhin doon ng landas ng platform, at ang landas ng root ay gagawin lamang iyon kung magtutulungan ang kernel at ang mga pahintulot ng device.
Kung saan nananalo pa rin ang landas ng root
Tatlong trabaho ang nagtutulak sa mga tao palayo sa landas ng platform. Ang bridging sa layer 2 ay nangangailangan ng TAP. Ang paghuli ng trapiko mula sa mga app sa labas ng iyong saklaw ng VPN ay nangangailangan ng visibility na hindi ibinibigay ng framework. Ang policy routing na hindi kailanman inilalantad ng builder, tulad ng hiwalay na table para sa isang destinasyon, ay nangangailangan ng mga panuntunang ikaw mismo ang nag-i-install.
Dumarating ang mga gastos mamaya: isang naka-unlock na bootloader o isang naka-root na image, isang SELinux policy na gumagalaw sa ilalim mo, at isang device na humihinto sa tamang pag-uugali pagkatapos ng OTA. Ang naka-root na resipe ay hindi rin maipapadala sa normal na pamamahagi ng app, kaya karamihan ng mga team ay pinapanatili ito bilang panloob na diagnostic path sa halip na produkto.
Pagpili sa pagitan ng dalawa
Magsimula sa interface na kailangan mo, hindi sa code na mayroon ka. Kung ang sagot ay isang TUN device na may rutang limitado sa app, ang landas ng platform ay mas maikli at nakakaligtas sa mga upgrade. Kung ang sagot ay may kasamang Ethernet frames, paghuli ng ibang app, o mga routing rule na tinatanggihan ng builder, nasa teritoryo ka ng root at dapat mong ibudget iyon bilang operating cost.
Isang pagsusuri na nagtatapos sa karamihan ng pagtatalo: buuin muna ang landas ng platform, itala ang kinalabasan ng establish() nang hiwalay sa estado ng remote session, at kumpirmahin ang isang packet sa bawat direksyon. Pagkatapos lamang niyan magpasya kung ang kulang na kakayahan ay makakatuwiran sa pag-alis sa sinusuportahang landas.