Bumangon ang tunnel, lumitaw ang icon ng susi, at bawat kahilingan sa browser ay nag-time out pa rin. Sa isang VpnService app ang kumbinasyong iyon ay karaniwang nangangahulugan na buhay ang read loop at sira ang kalahati ng output, hindi na tinanggihan ng server ang handshake. Nagawa na ng Android ang interface, inilipat ang trapiko ng app papasok dito at binigyan ang proseso mo ng file descriptor. Lahat ng pagkatapos ng descriptor na iyon ay code mo, at isang nawawalang tawag ay sapat na para lunukin ang buong stream.

Ano ang ibinibigay ng system at ano ang hindi

Nagbabalik ang establish() ng ParcelFileDescriptor para sa isang tun interface. Isinusulat ng system ang papalabas na IP packet doon at inaasahang babasahin ito ng proseso mo, magpapasya kung saan pupunta at ipapadala sa pamamagitan ng ordinaryong socket. Walang kernel forwarding sa ilalim, kaya kapag walang nagbabasa ay itinatapon ang mga packet at hindi gumagalaw ang mga counter ng interface.

Magsimula sa pagbilang kaysa paghula. Dagdagan ang counter sa bawat matagumpay na pagbasa at itala ang destinasyon at protocol ng unang mga packet. Kung mananatili ang counter sa zero habang may trapikong nalilikha, ang depekto ay nasa itaas ng forwarding code mo, na nagpapaliit sa paghahanap sa builder configuration at read mode. Kung gumagalaw ang counter at nabigo pa rin ang browser, ang depekto ay nasa daan ng pagpapadala o sa daan pabalik.

Ang blocking na pagbasa ang nagpapasya kung gumagalaw ang loop

Inilalagay ng setBlocking(true) ang descriptor sa blocking mode at umiiral ito mula noong API 21. Sa lumang mga release, ang solusyon ay maliit na JNI helper na naglilinis ng O_NONBLOCK gamit ang fcntl sa raw descriptor. Mahalaga ang mode dahil ito ang nagpapasya kung ano ang maaaring gawin ng pagbasa na walang naghihintay na packet.

Sa non-blocking mode, agad na bumabalik ang pagbasang iyon na may negatibong halaga, at ang loop na hindi pinapansin ang resulta ay umiikot ng core nang walang trapikong naigagalaw. Sa blocking mode, naghihintay ang pagbasa, na siyang gusto ng forwarding thread, pero hindi na maaaring abalahin ang thread. Ang pagsasara ng descriptor ang sinusuportahang paraan para pabagsakin ang pagbasa upang makalabas ang loop. Ang pagpatay sa proseso o pag-abala sa thread ay maaaring mag-iwan sa susunod na simula ng descriptor na hindi na tugma sa interface.

Magbasa ng isang packet bawat tawag papasok sa buffer na kasinglaki ng MTU ng interface, at ituring ang zero o negatibong return na error, hindi blangkong packet.

protect() bago ang unang pagpapadala, hindi pagkatapos

Bawat socket na nagdadala ng encapsulated packet ay dapat protektahan muna. Ang socket na walang proteksyon ay sumusunod sa parehong routing rules na kalalagay mo lang, kaya bumalik ang mga packet nito sa tunnel at kinukuha muli ng sarili mong read loop. Pagkatapos ay walang katapusang ipinapasa ng tunnel ang sarili nitong output, at mula sa labas ay mukhang koneksyong bumubukas at hindi kailanman sumasagot.

Ang pagkakasunod-sunod ay protect, pagkatapos connect o bind, pagkatapos send. Ang pagtawag ng protect pagkatapos ng connect ay maaaring mag-iwan ng dating desisyon sa ruta, kaya patuloy na ginagamit ng socket ang maling daan kahit nagbalik ng true ang tawag. Suriin ang boolean na ibinabalik at mabigo nang malakas kapag false, sa halip na pumasok sa pagpapadalang hindi maaaring gumana.

Ang error sa permiso mula sa sendto sa socket na hindi kailanman humawak sa protect() ang karaniwang senyales ng pagkakamaling ito.

Ang halimbawang UDP ay hindi nagdadala ng HTTPS

Ang halimbawa ng forwarding na kinokopya ng karamihan ng tutorial ay humahawak lamang ng UDP. Ang TCP ay nangangailangan ng handshake, sequence numbers, acknowledgements at retransmission, at ang loop na kumokopya ng payload sa pagitan ng tun descriptor at socket ay wala ni isa sa mga iyon. Iyon ang dahilan kung bakit nakakapasok ang UDP DNS query habang bawat HTTPS request ay natigil. Magpatakbo ng TCP stack sa user space o ibigay ang mga koneksyon sa lokal na proxy na nagtatapos ng TCP sa device at nagpapasa ng stream.

Subukan ang paghahati bago muling isulat ang anuman: magpadala ng isang UDP query at magbukas ng isang TCP connection sa parehong host. Kung gumagana ang UDP path at natigil ang TCP path sa unang SYN, ang forwarding code ang problema at hindi ang server. Ang isang paghahambing na iyon ay nakakatipid ng maraming oras na ginugugol sa pagbasa ng server logs na walang laman na kapaki-pakinabang.

Ang listahan ng ruta at mga filter bawat app ay naglilimita sa nakikita mo

Ang addAddress, addRoute at addDns ang nagtatakda kung ano ang ipinapadala ng system sa tunnel. Ang destinasyong labas ng listahan ng ruta ay hindi kailanman umaabot sa descriptor mo, kaya walang dami ng forwarding logic ang makakapag-ayos nito. I-log ang mga builder call na ginagawa mo at ihambing sa mga address na talagang sinusubukang maabot ng app.

Ang pagpili bawat app ay may sariling sahig: ang addAllowedApplication at addDisallowedApplication ay nangangailangan ng API 21, at sa lumang mga release ay wala talaga ang mga method na iyon. Ang disenyong nagfi-filter ayon sa packet ay hindi talaga makakatakbo doon.

Nag-iiwan ng lumang descriptor ang muling pag-set up

Ang bagong tawag sa establish() ay nagbabalik ng bagong descriptor at nagsasara ng dating interface. Hindi na valid ang lumang descriptor, kaya ang thread na nakablock pa rin doon ay nakakakuha ng error, at ang mga socket na ginawa para sa lumang daan ay maaaring kailangang palitan. Ang onRevoke ay ang callback para sa tunnel na kinuha ng user o system. Ang pag-iingat ng lifecycle sa isang may-ari, na humihinto sa reader bago muling mag-set up, ay pumipigil sa karamihan ng naka-konekta-pero-patay na estado.

Basahin ang bawat error ayon sa hakbang na kinabibilangan nito. Ang connection reset mula sa recvfrom ay nangangahulugan na nagsara ang malayong dulo habang nagbabasa pa ang code mo. Ang error sa maling address ay tumuturo sa header parsing o offset bug, hindi sa network.

Ayos ng trabaho: blocking mode on, isang packet bawat pagbasa, na-parse ang header, protektado ang socket, naipadala ang payload, malinis na pagsasara sa paghahatid at listahan ng ruta na sumasaklaw sa destinasyon.

Kaugnay na mga artikulo