Bakit Humuhuli ang Android VpnService ng Bahagi ng Trapiko at Hindi Lahat
Ang mga ruta, entry ng DNS at saklaw bawat app ang nagtatakda kung aling packet ang umaabot sa tun device, bakit naglalaho ang lokal na device at ano ang tinatanggihan ng builder.
Walang anuman sa VpnService ang humuhuli ng trapiko nang mag-isa. Hinihiling nito sa system ang isang tun interface, ibinibigay sa interface na iyon ang isang set ng address at ruta, at mula sa sandaling iyon kinokopya ng kernel ang bawat packet na tumutugma sa mga rutang iyon sa isang file descriptor na mababasa ng app. Hindi kailanman nakikita ng code ng app ang mga packet na hindi pinili ng mga ruta, kaya ang saklaw ng isang tunnel ay tanong ng routing table bago ito maging tanong ng programming. Iyon ang dahilan kung bakit ang parehong implementasyon ng serbisyo ay nakakalunok ng bawat daloy sa isang telepono at bahagi lamang sa isa pa, at iyon ang dahilan kung bakit ang pagdaragdag ng ruta ay kung minsan nag-aayos ng tunnel na mukhang sira. Ang nakukuha mo ay ang idineklara mo, binawasan ang lahat ng tahimik na tinanggihan ng builder.
Ang mga ruta ang nagtatakda kung aling packet ang dumarating
Builder.addRoute(prefix, prefixLength) ay naglalagay ng prefix sa routing table ng VPN network. Tinatrato ng system ang network na iyon bilang default para sa mga app sa saklaw, kaya ang mga entry nito ay tiningnan bago ang default na ruta ng pisikal na network at sapat na ang tugmang prefix upang ilihis ang isang daloy. Kaya ang isang ruta para sa 0.0.0.0/0 ay kumukuha ng buong IPv4, kasama ang printer, ang cast target at ang admin page ng router.
Ang mga client na gustong ang coverage na iyon nang hindi binubura ang pisikal na default na entry ay gumagamit sa halip ng pares na 0.0.0.0/1 at 128.0.0.0/1. Dalawang mas partikular na ruta ang nananalo sa isang default na ruta, magkasama nilang sakop ang buong address space, at ang sariling default na entry ng telepono ay nananatili sa table para sa mga socket na nakatali pa rin sa pisikal na network.
Ang mga address at entry ng DNS ay nagmumula sa parehong builder at sumasagot sa ibang tanong. Ang addAddress ay nagbibigay sa interface ng address na may haba ng prefix, at ang addDnsServer ay naglalagay ng resolver na tatanungin ng mga app sa saklaw sa pamamagitan ng interface. Maaaring manatili ang parehong pamilya sa isang interface, kaya ang set ng ruta na IPv4 lamang sa tabi ng IPv6 address ay isang configuration na tamang binabasa at tumutulo pa rin. Kung iruruta mo ang isang pamilya na walang address ang interface, walang source address ang kernel para sa mga packet na iyon at hindi sila kailanman lumalabas sa descriptor.
Ano ang ibinabalik ng establish()
Ang establish() ay nagbabalik ng ParcelFileDescriptor, o null kapag hindi awtorisado ang app. Ang null ay isang estado ng pahintulot at hindi isang error ng network: ang VpnService.prepare() ay dapat na nakabalik na ng null, o dapat na naaprubahan ng user ang dialog ng system bago ang tawag. Maaaring bawiin ang pahintulot habang tumatakbo ang serbisyo, at ang interface ay nagsasara sa ilalim ng iyong mga paa nang walang anumang callback sa iyong loop ng pagbasa.
Ang descriptor ay hindi nagboblock maliban kung sabihin mong iba, kaya ang pagbasa ay agad na bumabalik nang walang data sa halip na iparada ang thread. Ang isang loop na isinulat para sa isang blocking socket ay nagsusunog ng isang core, at ang isang loop na itinuturing na nakamamatay ang walang laman na pagbasa ay nagsisira ng tunnel sa isang idle na segundo. Binabago ng Builder.setBlocking(true) ang pag-uugali na iyon, at ang halaga ay isang thread na naghihintay sa loob ng pagbasa.
Ang mga argumento ng ruta at address ay binabalido bilang mga value na LinkAddress, at ang mga destinasyong multicast gaya ng 224.0.0.0/4 ay direktang tinatanggihan. Ang isang tun interface na punto-sa-punto ay wala ring broadcast domain, kaya ang pagtuklas ng multicast sa pamamagitan nito ay nangangailangan ng ibang disenyo sa halip na isa pang ruta. Ang listahang dapat suriin ay ang VpnService.Builder, bago mo hatulan na isang tawag ang hindi pinansin.
Pinapaliit ng saklaw bawat app ang set, hindi ang tunnel
Mula sa API 21 tinatanggap ng builder ang addAllowedApplication at addDisallowedApplication, at ang dalawang listahan ay magkasalungat sa loob ng isang builder. Ang listahan ng pinapayagan ay gumagawa ng tunnel para sa mga packet na iyon habang pinapanatili ng lahat ng iba ang pisikal na network; ang listahan ng ipinagbabawal ay humuhuli sa lahat maliban sa mga pinangalanang packet, na siyang iniaalok ng isang client bilang split tunneling. Bago ang API 21 ay wala talagang mekanismo ng platform, at ang pag-uugali bawat app ay nagmula sa mga third-party na client na nag-filter ayon sa UID sa loob ng kanilang sariling forwarder. Mahalaga ang pagkakaiba kapag nagde-debug, dahil ang isang tunnel bawat app na may listahan ng pinapayagan na isang packet ay halos walang trapikong nalilikha at iyon ay tamang pag-uugali, hindi sirang ruta.
Anuman ang saklaw, ang socket na binuksan ng app mo patungo sa server ng VPN ay hindi dapat dumaan sa tunnel na itinatayo nito. Ang VpnService.protect(socket) ay nagbibigkis ng socket na iyon sa pinagbabatayang network upang laktawan ng mga packet nito ang tun device. Kung lalaktawan mo ang tawag, ang handshake ay nilalamon ng interface na dapat sana ang nagtaas nito.
Ang VpnService.setUnderlyingNetworks ay nagsasara ng loop sa pamamagitan ng pagsasabi sa system kung aling pisikal na network ang nagdadala ng tunnel. Ang value na iyon ang nagtatakda kung paano iniuulat ng VPN ang konektibidad sa iba pang app, at ang null o lumang value ay karaniwang dahilan ng isang konektadong tunnel na itinuturing na offline ng natitirang bahagi ng telepono. I-update ito kapag nagbago ang pinagbabatayang network, hindi lamang sa pagsisimula.
Mga checksum at MTU: kung saan nabibigo pa rin ang tamang ruta
Kapag dumating na ang mga packet, dalawang detalye sa antas ng packet ang nagtatakda kung may gumagana mula sa dulo hanggang dulo. Ang una ay ang source address: kapag maraming interface address, maaaring magpadala ang kernel ng mga packet na nagdadala ng una, kaya ang forwarder na nagsusulat muli ng header ay dapat ayusin ang address at ang checksum na sumasaklaw dito.
Ang pangalawa ay ang saklaw ng checksum. Kapag ang app mismo ang gumagawa ng TCP o UDP packet, sinasaklaw ng checksum ang isang pseudo-header na nagdadala ng source address, destination address, numero ng protocol at haba ng transport, at ang pag-alis ng alinman sa mga ito ay nagdudulot ng mga packet na dumarating at itinatapon. Ang pagkabigong iyon ay mukhang timeout pagkatapos ng tugon na hindi kailanman dumarating, na nagpapabalik sa karamihan sa mga ruta. Ang setMtu ay nagtatakda ng MTU ng interface, at ang bawat pagbasa ay nagbabalik ng eksaktong isang kumpletong IP packet, kaya ang buffer na isinulat para sa isang byte stream ay magpuputol-putol ng iyong pag-parse, hindi ang packet.
Isang pag-ikot ng beripikasyon na naghihiwalay sa mga layer
Upang mahanap kung saan nagkamali ang saklaw, subukan ang mga layer nang sunod-sunod. Itanong sa device kung aling mga ruta ang talagang hawak ng VPN network, pagkatapos tingnan kung ang isang daloy patungo sa kilalang address ay lumalabas bilang pagbasa sa descriptor, pagkatapos tingnan kung maayos ang pagkakabuo ng packet na ipinapadala ng iyong forwarder. Walang ipinapakita ang problema sa ruta sa descriptor, samantalang ang problema sa checksum ay nagpapakita ng packet na dumarating at naglalaho.
Kung walang umaabot sa descriptor, ang hinala ay ang set ng ruta at hindi ang nagbabasa: kumpirmahin ang mga prefix, kumpirmahin na may address sa parehong pamilya, at kumpirmahin na ang app ay nasa saklaw na itinakda mo. Kung umaabot ang mga packet sa descriptor pero walang bumabalik, siyasatin ang protect() at ang value ng pinagbabatayang network bago muling hawakan ang isang ruta. Kung nag-uulat ang malayong panig ng mga malformed na packet, ang checksum at MTU ang mga lugar na dapat tingnan. Isang pagsusuri bawat layer ang nakakatipid ng isang hapon ng hula.