Ништо во 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 се местата каде што треба да се погледне. Една проверка по слој заштедува едно попладне на претпоставки.

Поврзани статии