Ништа у VpnService не пресреће саобраћај само по себи. Он тражи од система tun интерфејс, предаје том интерфејсу скуп адреса и рута, и од тог тренутка језгро копира сваки пакет који одговара тим рутама у датотечни дескриптор који апликација може да чита. Код апликације никада не види пакете које руте нису изабрале, па је обухват тунела питање табеле рутирања пре него што постане питање програмирања. Зато иста имплементација услуге може да прогута сваки ток на једном телефону, а на другом само део, и зато додавање руте понекад поправи тунел који је изгледао покварен. Добијате оно што сте објавили, минус све што је градитељ тихо одбио.

Руте одлучују који пакети пристижу

Builder.addRoute(prefix, prefixLength) поставља префикс у табелу рутирања VPN мреже. Систем третира ту мрежу као подразумевану за апликације у обухвату, па се њени записи гледају пре подразумеване руте физичке мреже и одговарајући префикс је довољан да преусмери ток. Зато рута за 0.0.0.0/0 узима IPv4 у целини, укључујући штампач, cast циљ и административну страницу рутера.

Клијенти који желе тај обухват без брисања физичког подразумеваног записа користе уместо тога пар 0.0.0.0/1 и 128.0.0.0/1. Две специфичније руте побеђују подразумевану руту, заједно покривају цео адресни простор, а сопствени подразумевани запис телефона остаје у табели за сокете који су још везани за физичку мрежу.

Адресе и DNS записи долазе од истог градитеља и одговарају на друго питање. addAddress даје интерфејсу адресу са дужином префикса, а addDnsServer поставља резолвер који ће апликације у обухвату питати кроз интерфејс. Обе фамилије могу да стоје на једном интерфејсу, па је скуп рута само за IPv4 поред адресе IPv6 конфигурација која се чита исправно и ипак цури. Ако рутирате фамилију у којој интерфејс нема адресу, језгро нема изворну адресу за те пакете и они се никада не појављују код дескриптора.

Шта враћа establish()

establish() враћа ParcelFileDescriptor, или null када апликација није овлашћена. Null је стање сагласности, а не мрежна грешка: VpnService.prepare() је већ морао да врати null, или је корисник морао да одобри системски дијалог пре позива. Сагласност може да се повуче док услуга ради, и интерфејс се затвара под вама без икаквог повратног позива у вашу петљу читања.

Дескриптор не блокира осим ако не кажете другачије, па читање враћа одмах без података уместо да паркира нит. Петља написана за блокирајући сокет сагорева једно језгро, а петља која празно читање сматра фаталним кида тунел у једној неактивној секунди. Builder.setBlocking(true) мења то понашање, а цена је нит која чека унутар читања.

Аргументи руте и адресе проверавају се као вредности LinkAddress, а multicast одредишта као 224.0.0.0/4 одбијају се одмах. Интерфејс tun од тачке до тачке такође нема домен емитовања, па откривање multicast преко њега захтева другачији дизајн, а не још једну руту. Референца VpnService.Builder је листа коју треба проверити пре него што закључите да је позив игнорисан.

Обухват по апликацији сужава скуп, не тунел

Од API 21 градитељ прихвата addAllowedApplication и addDisallowedApplication, и обе листе се међусобно искључују у једном градитељу. Листа дозвољених чини тунел постојећим за те пакете, док све остало задржава физичку мрежу; листа забрањених хвата све осим именованих пакета, што клијент нуди као подељено тунелирање. Пре API 21 нема никаквог платформског механизма, а понашање по апликацији долазило је од клијената трећих страна који су филтрирали по UID у свом преусмеравачу. Разлика је важна при отклањању грешака, јер тунел по апликацији са листом дозвољених од једног пакета готово не производи саобраћај и то је исправно понашање, а не покварена рута.

Какав год да је обухват, сокет који ваша апликација отвара ка VPN серверу не сме да путује кроз тунел који гради. VpnService.protect(socket) везује тај сокет за основну мрежу да би његови пакети заобишли tun уређај. Ако прескочите позив, руковање гута интерфејс који је требало да га подигне.

VpnService.setUnderlyingNetworks затвара петљу рекавши систему која физичка мрежа носи тунел. Та вредност одређује како VPN извештава о повезивању другим апликацијама, а null или застарела вредност је чест узрок повезаног тунела који остатак телефона сматра офлајн. Ажурирајте је када се основна мрежа промени, не само при покретању.

Контролне суме и MTU: где и исправна рута ипак не успева

Када пакети пристигну, два детаља на нивоу пакета одлучују да ли нешто ради од краја до краја. Први је изворна адреса: са неколико адреса интерфејса језгро може да шаље пакете који носе прву, па преусмеравач који преписује заглавља мора да исправи адресу и контролну суму која је покрива.

Други је обухват контролне суме. Када апликација сама гради TCP или UDP пакете, контролна сума покрива псеудозаглавље са изворном адресом, одредишном адресом, бројем протокола и дужином транспорта, а изостављање било чега од тога даје пакете који пристижу и бивају одбачени. Тај неуспех изгледа као истек времена после одговора који никада не долази, што већину људи шаље назад ка рутама. setMtu фиксира MTU интерфејса, и свако читање враћа тачно један цео IP пакет, па ће бафер написан за ток бајтова фрагментирати ваше рашчлањивање, а не пакет.

Проверни пролаз који раздваја слојеве

Да бисте нашли где је обухват отишао наопако, тестирајте слојеве по реду. Питајте уређај које руте VPN мрежа заиста држи, затим проверите да ли се ток ка познатој адреси појављује као читање код дескриптора, затим проверите да ли је пакет који ваш преусмеравач шаље добро формиран. Проблем руте не показује ништа код дескриптора, док проблем контролне суме показује пакет који пристиже и нестаје.

Ако ништа не стигне до дескриптора, сумњиво је скуп рута, а не читач: потврдите префиксе, потврдите да постоји адреса у истој фамилији и потврдите да је апликација у обухвату који сте поставили. Ако пакети стигну до дескриптора али ништа се не враћа, прегледајте protect() и вредност основне мреже пре него што поново дотакнете руту. Ако далека страна пријављује оштећене пакете, контролна сума и MTU су места где треба погледати. Једна провера по слоју штеди једно поподне нагађања.

Повезани чланци