Два шляхі могуць паставіць IP-інтэрфейс перад Android-праграмай, і абодва сканчаюцца адным і тым жа тыпам файлавага дэскрыптара з вельмі рознымі ўладальнікамі. Адзін ідзе праз VpnService і яго будаўнік, другі — праз root-абалонку і /dev/net/tun. Кожнае чытанне з гэтага дэскрыптара — гэта зыходны пакет, а кожны запіс — уваходны. Шляхі адрозніваюцца тым, хто трымае палітыку, хто перажывае абнаўленне АС і колькі табліцы маршрутызацыі вам дазволена чапаць.

Ідзіце шляхам платформы, калі толькі не можаце назваць канкрэтную рэч, ад якой яна адмаўляецца. Гэтая адмова звычайна — TAP, захоп па-за вашым наборам праграм або правіла маршрутызацыі, якога фрэймворк ніколі не раскрывае.

На шляху платформы вы заяўляеце намер, а не каманды. Вы даяце як мінімум адзін адрас, патрэбныя маршруты, DNS-серверы і неабавязковы спіс праграм, пасля чаго сістэмная служба Android выконвае прывілеяваную наладку пад сваёй ідэнтычнасцю. Вы ніколі не даведаецеся імя інтэрфейса загадзя і ніколі не выдаяце ioctl, які яго стварае, — у гэтым і сутнасць.

Два шляхі атрымання IP-інтэрфейса

Шлях root — гэта старэйшы рэцэпт. Вы адкрываеце /dev/net/tun, выклікаеце TUNSETIFF з IFF_TUN, уздымаеце інтэрфейс праз ip link і дадаяце адрасы ды маршруты ўручную. Кожны крок трэба зрабіць правільна самому, уключаючы тыя, якія фрэймворк зрабіў бы моўчкі. Адзін прапушчаны маршрут дае карыстальніку тую ж справаздачу, што і мёртвы сервер.

Адладка адрозніваецца не менш за наладку. На шляху root інтэрфейс або існуе, або ваш ioctl вярнуў памылку, якую можна прачытаць напрамую. На шляху платформы інтэрфейс існуе толькі пасля поспеху establish(), таму збой прыходзіць як выключэнне або null-дэскрыптар, а не як вузел прылады, у які можна патыкаць.

Што насамрэч вяртае establish()

Builder.establish() мае тры вынікі, і кожны патрабуе свайго адказу. Дэскрыптар азначае, што інтэрфейс настроены і ваш для выкарыстання, null азначае, што запыт не прайшоў, а выкінутае выключэнне — што сістэмная служба яго адхіліла. Звядзенне null і поспеху ў адзін boolean — найбольш распаўсюджаны спосаб, якім праграма паведамляе пра працоўны тунель, што ніколі не нясе пакетаў. Даведачнік Builder паказвае патрабаванне да адраса, і гэта варта прачытаць, перш чым вінаваціць сервер.

Кірункі вызначае дэскрыптар, а не ваш пратакол. Чытанні даюць зыходныя пакеты, ужо адабраныя для VPN, а запісы ўпрыскваюць уваходныя пакеты назад у інтэрфейс. Ваш уласны транспарт шыфруе, аўтэнтыфікуе і фармуе гэтыя пакеты, бо платформа спыняецца на мяжы пакета.

Binder — адзін крок, JNI — іншая вось

Запыт будаўніка перасякае мяжу сістэмы праз Binder, і дзесьці ніжэй платформа выклікае натыўны код, каб наладзіць прыладу. Гэта два розныя механізмы, укладзеныя ў адзін выклік. Binder — гэта міжпрацэсная камунікацыя паміж праграмай і сістэмнай службай, а JNI — мост з Java у натыўны код унутры працэсу. Чытанне дрэва AOSP Android 9 робіць стос выглядаць літаральна, і варта памятаць, што ўнутраныя імёны класаў і шляхі да файлаў змяняюцца ад выпуску да выпуску.

Нязменным застаецца кантракт, на які можна абаперціся: будаўнік, дэскрыптар, які ён вяртае, і жыццёвы цыкл службы вакол яго. Ніводная праграма не павінна выклікаць унутраную транзакцыю службы або ствараць вузел прылады нанова, каб атрымаць інтэрфейс. Калі ваш код залежыць ад імя класа AOSP, ён зламаецца на выпуску, які вы не тэставалі.

TUN нясе IP, TAP — Ethernet

TUN — гэта IP-інтэрфейс. TAP — гэта Ethernet-інтэрфейс, і гэтая розніца вырашае, ці магчыма маставанне наогул. У прылады TUN няма MAC-адраса і няма загалоўка Ethernet для перасылкі, таму кліент TUN не можа ўвайсці ў маст layer-2, як бы ні былі запісаныя маршруты.

VpnService дае вам IP-варыянт, і менавіта ён патрэбны VPN узроўню праграмы і падтрымліваецца без сырога стосу Ethernet. Калі ваш дызайн патрабуе маставання, шлях платформы вас туды не давядзе, а шлях root давядзе толькі тады, калі ядро і дазволы прылады супрацуюць.

Дзе шлях root усё яшчэ перамагае

Тры задачы штурхаюць людзей з шляху платформы. Маставанне layer-2 патрабуе TAP. Захоп трафіку праграм па-за межамі вашага VPN патрабуе бачнасці, якую фрэймворк не дае. Палітычная маршрутызацыя, якую будаўнік ніколі не раскрывае, напрыклад асобная табліца для аднаго прызначэння, патрабуе правілаў, якія вы ўсталёўваеце самі.

Выдаткі прыходзяць пазней: разблакаваны bootloader або root-вобраз, палітыка SELinux, якая змяняецца ў вас пад нагамі, і прылада, што перастае паводзіць сябе правільна пасля OTA. Root-рэцэпт таксама нельга паставіць праз звычайнае распаўсюджванне праграм, таму большасць каманд трымае яго як унутраную дыягнастычную сцяжынку, а не як прадукт.

Выбар паміж імі

Пачынайце з інтэрфейса, які вам патрэбны, а не з кода, які ў вас ёсць. Калі адказ — прылада TUN з маршрутамі ў межах праграм, шлях платформы карацейшы і перажывае абнаўленні. Калі адказ уключае кадры Ethernet, захоп іншых праграм або правілы маршрутызацыі, якія будаўнік адхіляе, вы на тэрыторыі root і павінны закласці гэта як аперацыйныя выдаткі.

Праверка, якая здымае большасць спрэчак: спачатку пабудуйце шлях платформы, запішыце вынік establish() асобна ад стану аддаленай сесіі і пацвердзіце адзін пакет у кожным кірунку. Толькі пасля гэтага вырашайце, ці апраўдвае адсутная магчымасць выйсце з падтрымліванага шляху.

Падобныя артыкулы