Android VPN: VpnService डिस्क्रिप्टर र root tun यन्त्र
Android अनुप्रयोगहरूले आफै tun0 बनाउन सक्दैनन्: establish() ले के फर्काउँछ, Binder कहाँ रोकिन्छ र JNI कहाँ सुरु हुन्छ, र root ले बनाएको यन्त्र कहिले जित्छ।
दुई बाटोले Android अनुप्रयोगको अगाडि IP इन्टरफेस राख्न सक्छन्, र दुवै धेरै फरक मालिक भएको एउटै किसिमको फाइल डिस्क्रिप्टरमा टुंगिन्छन्। एउटा VpnService र त्यसको builder हुँदै जान्छ, अर्को 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() को नतिजा टाढाको सत्रको अवस्थाबाट अलग रेकर्ड गर्नुहोस्, र हरेक दिशामा एउटा प्याकेट पुष्टि गर्नुहोस्। त्यसपछि मात्र छुटेको क्षमताले समर्थित बाटो छोड्नु जायज ठहराउँछ कि भन्ने निर्णय गर्नुहोस्।