Чаму Android VpnService перахоплівае частку трафіку, а не ўвесь
Маршруты, запісы DNS і ахоп па асобных праграмах вызначаюць, якія пакеты дасягаюць прылады tun, чаму лакальныя прылады знікаюць і ад чаго адмаўляецца будаўнік.
Нішто ў VpnService не перахоплівае трафік само па сабе. Ён просіць у сістэмы інтэрфейс tun, перадае гэтаму інтэрфейсу набор адрасоў і маршрутаў, і з гэтага моманту ядро капіюе кожны пакет, які адпавядае гэтым маршрутам, у дэскрыптар файла, які праграма можа чытаць. Код праграмы ніколі не бачыць пакеты, якія не выбралі маршруты, таму ахоп тунеля — гэта пытанне табліцы маршрутызацыі раней, чым пытанне праграмавання. Вось чаму адна і тая ж рэалізацыя сэрвісу можа праглынуць увесь паток на адным тэлефоне і толькі частку на іншым, і чаму даданне маршруту часам рамантуе тунель, які здаваўся зламаным. Тое, што вы атрымалі, — гэта тое, што вы абвясцілі, мінус усё, ад чаго будаўнік ціха адмовіўся.
Маршруты вызначаюць, якія пакеты прыходзяць
Builder.addRoute(prefix, prefixLength) усталёўвае прэфікс у табліцу маршрутызацыі сеткі VPN. Сістэма лічыць гэтую сетку прадвызначанай для праграм у ахопе, таму яе запісы разглядаюцца раней за прадвызначаны маршрут фізічнай сеткі, і супадальнага прэфікса дастаткова, каб перанакіраваць паток. Таму маршрут для 0.0.0.0/0 цалкам забірае IPv4, уключаючы прынтар, мэту трансляцыі і адмін-старонку роўтара.
Кліенты, якія хочуць такога пакрыцця без выдалення фізічнага прадвызначанага запісу, выкарыстоўваюць замест гэтага пару 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, а мэты множнай рассылкі, такія як 224.0.0.0/4, адхіляюцца цалкам. Кропка-кропкавы інтэрфейс tun таксама не мае шырокавяшчальнага дамена, таму выяўленне множнай рассылкі праз яго патрабуе іншага дызайну, а не яшчэ аднаго маршруту. Даведнік 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 — гэта месцы, дзе шукаць. Адна праверка на ўзровень зберагае паўдзень здагадак.