Android VPN: VpnService дескрипторы және root tun құрылғысы
Android қолданбалары tun0-ды өздері жасай алмайды: establish() не қайтарады, Binder қайда тоқтайды және JNI қайда басталады, root жасаған құрылғы қашан жеңеді.
Екі жол 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() нәтижесін қашықтағы сессия күйінен бөлек жазып ал және әр бағытта бір пакетті раста. Содан кейін ғана жетіспейтін мүмкіндік қолдау көрсетілетін жолдан кетуді ақтай ма, соны шеш.