Android VPN: VpnService डिस्क्रिप्टर विरुद्ध root tun डिव्हाइस
Android अॅप्स स्वतः tun0 तयार करू शकत नाहीत: establish() काय परत देतो, Binder कुठे थांबतो आणि JNI कुठे सुरू होते, आणि root ने तयार केलेले डिव्हाइस कधी जिंकते.
दोन मार्ग Android अॅपच्या समोर IP इंटरफेस ठेवू शकतात, आणि दोन्ही खूप वेगळ्या मालकांच्या एकाच प्रकारच्या फाइल डिस्क्रिप्टरवर संपतात। एक VpnService आणि त्याच्या बिल्डरमधून जातो, दुसरा root शेल आणि /dev/net/tun मधून। त्या डिस्क्रिप्टरवरील प्रत्येक वाचन बाहेर जाणारे पॅकेट आहे आणि प्रत्येक लेखन आत येणारे। मार्ग वेगळे करणारे म्हणजे धोरण कोणाकडे आहे, OS अपग्रेड कोण टिकून राहतो, आणि राउटिंग टेबलपैकी किती तुम्ही स्पर्श करू शकता।
प्लॅटफॉर्मचा मार्ग घ्या, जोपर्यंत तो करण्यास नकार देतो ती विशिष्ट गोष्ट तुम्ही नावाने सांगू शकत नाही। तो नकार सामान्यतः TAP, तुमच्या स्वतःच्या अॅप संचाबाहेर कॅप्चर, किंवा फ्रेमवर्क कधीही उघड न करणारा राउटिंग नियम असतो।
प्लॅटफॉर्मच्या मार्गावर तुम्ही आदेशांऐवजी हेतू जाहीर करता। तुम्ही किमान एक पत्ता, हवे असलेले मार्ग, DNS सर्व्हर आणि ऐच्छिक अॅप यादी देता, मग Android ची सिस्टम सेवा स्वतःच्या ओळखीने विशेषाधिकारित सेटअप करते। तुम्ही इंटरफेसचे नाव आधी कधीही जाणत नाही, आणि ते तयार करणारा ioctl कधीही बाहेर काढत नाही, आणि हाच मुद्दा आहे।
IP इंटरफेस मिळवण्याचे दोन मार्ग
root मार्ग जुनी पद्धत आहे। तुम्ही /dev/net/tun उघडता, IFF_TUN सह TUNSETIFF कॉल करता, 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() चा निकाल दूरस्थ सत्राच्या स्थितीपासून वेगळा नोंदवा, आणि प्रत्येक दिशेने एक पॅकेट पुष्टी करा। त्यानंतरच गायब क्षमता समर्थित मार्ग सोडण्याचे समर्थन करते का ते ठरवा।