Android VPN एट्रिब्यूशन: getConnectionOwnerUid बनाम /proc/net
Android 10 ऐप्स से /proc/net छिपा देता है। VPN को पैकेट अब भी मिलते हैं, पर एट्रिब्यूशन getConnectionOwnerUid पर चला जाता है, अपने tuple और अनुमति नियमों के साथ।
Android 10 और उसके बाद, /proc/net/tcp खोलने वाले ऐप को socket तालिका के बजाय पहुँच-अस्वीकृति मिलती है। VPN इंटरफ़ेस को इससे फ़र्क नहीं पड़ता: पैकेट अब भी आते हैं, क्योंकि टनल रूटिंग का निर्णय है, फ़ाइल अनुमति नहीं। जो बदला है वह एक संकरे सवाल का जवाब है: “कौन सा ऐप इस कनेक्शन का मालिक है?” इसका विकल्प ConnectivityManager.getConnectionOwnerUid है, समर्थित प्रति-कनेक्शन क्वेरी, और पुरानी आदत तथा नई API के बीच चुनाव इस पर निर्भर करता है कि आपके पास वास्तव में तीन कामों में से कौन सा है: पैकेट प्राप्त करना, फ़्लो का एट्रिब्यूशन, या उपयोग का योग।
मालिक के सवाल के दो जवाब
API 29 से पहले, एक विशेषाधिकार प्राप्त या root प्रक्रिया kernel की socket तालिकाएँ पढ़ सकती थी, चौकड़ी का मिलान कर सकती थी और मालिक UID जान सकती थी। इससे एक ही फ़ाइल में पूरे डिवाइस की सूची मिलती थी। API 29 से फ़ाइलसिस्टम दृश्य सीमित है, और समर्थित रास्ता प्रोटोकॉल, स्थानीय पता और दूरस्थ पते के साथ प्रति-कनेक्शन क्वेरी है।
| Question | Legacy socket tables | getConnectionOwnerUid |
|---|---|---|
| Who owns this flow? | Scan and match the tuple | Ask once per tuple |
| Device-wide inventory? | Yes, when readable | No, one flow at a time |
| Historical usage? | No | No; that is NetworkStatsManager |
| Works without root? | No on modern releases | Yes, for authorized callers |
तुलना “पुराना बनाम नया” नहीं है। ये तीन अलग काम हैं जो पहले एक पढ़ने योग्य फ़ाइल से पूरे होते थे और अब तीन अलग इंटरफ़ेस तीन अलग अनुबंधों के साथ पूरे करते हैं।
स्थानीय पता ही ऐप का सिरा है
विधि का हस्ताक्षर प्रोटोकॉल, स्थानीय socket पता और दूरस्थ socket पता लेता है। “स्थानीय” का अर्थ कनेक्शन में ऐप का पक्ष है, न कि वह सिरा जो जाँच के क्षण पैकेट का स्रोत हो। दूरस्थ सर्वर से ऐप की ओर लौटते पैकेट का स्रोत और गंतव्य इस परिभाषा के सापेक्ष उलटा होता है।
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
int uid = cm.getConnectionOwnerUid(
OsConstants.IPPROTO_TCP, appLocalAddress, appRemoteAddress);यह दिशा-गलती सबसे आम कारण है कि काम करने वाली क्वेरी ग़लत जवाब देती है, या कोई नहीं। कॉल से पहले दोनों सिरों को लॉग करें और जोड़ी को उस कनेक्शन से मिलाएँ जो ऐप ने वास्तव में खोला था। पता परिवार भी मायने रखता है: IPv4-मैप्ड पता और देशी IPv6 पता अलग tuple हैं, और उन्हीं पोर्टों पर TCP तथा UDP फ़्लो भी।
अधिकृति और अज्ञात परिणाम अनुबंध का हिस्सा हैं
क्वेरी अधिकृत कॉलरों तक सीमित है, जिसमें संबंधित उपयोगकर्ता का सक्रिय VPN ऐप भी शामिल है। असंबंधित निगरानी ऐप यह नहीं मान सकता कि वह किसी अन्य प्रोफ़ाइल के कनेक्शन गिन सकता है, और अनधिकृत कॉल मैत्रीपूर्ण त्रुटि के बजाय सुरक्षा अपवाद फेंक सकता है। दोनों परिणामों को स्पष्ट रूप से संभालें।
अनुपस्थित मालिक पैकेज नाम नहीं है। फ़्लो पैकेट और क्वेरी के बीच बंद हो सकता है, tuple अधूरा हो सकता है, या कॉलर को जानने की अनुमति ही न हो। पहला सफल जवाब कैश करके बाद में दोबारा उपयोग करना नए कनेक्शन को पुराने ऐप से जोड़ देता है, जो अज्ञात बताने से बुरा है।
जब एट्रिब्यूशन विफल हो, तब भी ट्रैफ़िक को निर्णय चाहिए: जहाँ नीति कहती है वहाँ उसे ब्लॉक करें, केवल उतना दायरा अनुमत करें जो आप समझते हैं, या अंतराल दिखाएँ। वह फ़ॉलबैक डिज़ाइन का हिस्सा है, टिप्पणी का नहीं।
लेखा एक अलग सवाल है
NetworkStatsManager अपने स्वयं के पहुँच नियमों के अंतर्गत उपयोग बताता है। यह बता सकता है कि किसी अवधि में UID ने कितना स्थानांतरित किया; यह किसी मनमाने जीवित पैकेट का मालिक नहीं बता सकता। नेटवर्क क्षमताएँ नेटवर्क के गुण बताती हैं, socket की निर्देशिका नहीं। तीनों को मिलाने से ऐसे डैशबोर्ड बनते हैं जो सुसंगत दिखते हैं और होते नहीं।
यह तय करने के सरल मामले के लिए कि कौन से ऐप टनल का उपयोग करते हैं, Android की समर्थित अनुमत और निषिद्ध ऐप सूचियाँ बिना किसी स्क्रैपिंग के नीति व्यक्त करती हैं। प्रति-फ़्लो एट्रिब्यूशन उन मामलों के लिए रखें जिन्हें वास्तव में इसकी आवश्यकता है, और ऐसे अनुरक्षित कार्यान्वयन के लिए बजट रखें जो दिशा, अनुमतियाँ और अज्ञात स्थितियाँ एक साथ संभाले।
काम के अनुसार उपकरण चुनें, फिर किनारे जाँचें
यदि आवश्यकता टनल चयन है, तो प्लेटफ़ॉर्म सूची का उपयोग करें। यदि लाइव एट्रिब्यूशन है, तो मालिक क्वेरी का उपयोग करें और हर अनुत्तरित tuple को अज्ञात मानें। यदि ऐतिहासिक योग हैं, तो stats API का उपयोग करें। फिर वे तीन किनारे जाँचें जो भोले कार्यान्वयन तोड़ते हैं: विपरीत दिशा का पैकेट, क्वेरी से पहले बंद होने वाला फ़्लो, और ऐसा कॉलर जो सक्रिय VPN नहीं है।
इनमें से कुछ भी SELinux बंद करने, फ़ोन को root करने या kernel तालिकाओं को वैश्विक रूप से पठनीय बनाने की माँग नहीं करता। सीमा ही मुद्दा है: प्लेटफ़ॉर्म ने एट्रिब्यूशन को एक अनुबंध के पीछे कर दिया ताकि जवाब उस घटक को उपलब्ध हो जिसे वैध रूप से इसकी आवश्यकता है, और किसी और को नहीं।