Эки жол Android колдонмосунун алдына IP интерфейсин коё алат жана экөө тең ээлери абдан башка болгон бирдей типтеги файл дескрипторуна бүтөт. Бирөө VpnService жана анын builder аркылуу өтөт, экинчиси root shell жана /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 — башка ог

builder өтүнүчү Binder аркылуу системага кирет, ал эми анын астында бир жерде платформа түзмөнү жөндөө үчүн нативдик кодду чакырат. Бул бир чакыруунун ичинде катмарланган эки башка механизм. Binder — колдонмоң менен системалык кызматтын ортосундагы процесстер аралык байланыш, ал эми JNI — процесс ичиндеги Javaдан нативдик кодго көпүрө. Android 9 AOSP дарагын окуу стекти сөзмө-сөз көрсөтөт жана ички класс аттары менен файл жолдору чыгарылыштардын ортосунда жылат деп эстен чыгарба.

Жылбаган нерсе — ишенүүгө боло турган келишим: builder, ал кайтарган дескриптор жана анын айланасындагы кызматтын жашоо циклү. Эч бир колдонмо интерфейс алуу үчүн ички кызмат транзакциясын чакырбашы же түзмөк түйүнүн кайра түзбөшү керек. Кодүң 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 аймагыңдын сыртындагы колдонмолордун трафигин кармоо үчүн фреймворк бербеген көрүнүүчүлүк керек. builder эч качан ачпаган саясаттык маршруттоо, мисалы бир бараан жер үчүн өзүнчө таблица, өзүң орноткон эрежелерди талап кылат.

Чыгымдар кийин келет: ачылган bootloader же root жасалган образ, астыңда жылган SELinux саясаты жана OTAдан кийин туура жүрүм-турумду токтоткон түзмөк. root жасалган рецептти кадимки колдонмо таратуу аркылуу да чыгарууга болбойт, ошондуктан көп команда аны продукт катары эмес, ички диагностикалык жол катары кармайт.

Экөөнүн ортосунда тандоо

Колуңдагы коддон эмес, сага керектүү интерфейстен башта. Жооп колдонмо менен чектелген маршруттары бар TUN түзмөгү болсо, платформа жолу кыскараак жана жаңыртуулардан аман калат. Жооп Ethernet кадрларын, башка колдонмолорду кармоону же builder четке каккан маршруттоо эрежелерин камтыса, сен root аймагындасың жана муну операциялык чыгым катары пландаштырышың керек.

Көп талаш-тартыштарды чечкен текшерүү: адегенде платформа жолун кур, establish() натыйжасын алыскы сессиянын абалынан өзүнчө жазып ал жана ар багытта бир пакетти ыраста. Ошондон кийин гана жетишпеген мүмкүнчүлүк колдолуучу жолдон кетүүнү актайбы, аны чеч.

Тектеш макалалар