انتساب VPN در اندروید: getConnectionOwnerUid در برابر /proc/net
اندروید 10 مسیر /proc/net را از برنامهها پنهان میکند. VPN همچنان بسته میگیرد، اما انتساب به getConnectionOwnerUid منتقل میشود، با قواعد خودش برای tuple و مجوزها.
در اندروید 10 و بعد از آن، برنامهای که /proc/net/tcp را باز میکند بهجای جدول socket پاسخ «دسترسی رد شد» میگیرد. رابط VPN اهمیتی نمیدهد: بستهها همچنان میرسند، چون تونل یک تصمیم مسیریابی است، نه مجوز فایل. آنچه عوض شده پاسخ به پرسشی باریکتر است: «کدام برنامه صاحب این اتصال است؟» جایگزین ConnectivityManager.getConnectionOwnerUid است، پرسوجوی پشتیبانیشده برای هر اتصال، و انتخاب میان عادت قدیمی و API جدید بستگی دارد به اینکه واقعاً کدام یک از سه کار را دارید: دریافت بسته، انتساب یک جریان، یا جمعزدن مصرف.
دو راه برای پاسخ به پرسش مالک
پیش از API 29، یک فرایند دارای امتیاز یا root میتوانست جدولهای 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ها. آمیختن این سه، داشبوردهایی میسازد که سازگار به نظر میرسند و نیستند.
برای حالت ساده تصمیمگیری اینکه کدام برنامهها از تونل استفاده میکنند، فهرستهای پشتیبانیشده برنامههای مجاز و غیرمجاز اندروید از قبل سیاست را بیان میکنند، بدون هیچ scraping. انتساب بهازای هر جریان را برای مواردی نگه دارید که واقعاً به آن نیاز دارند، و برای پیادهسازی نگهداشتنیای بودجه بگذارید که جهت، مجوزها و حالتهای ناشناخته را با هم مدیریت کند.
ابزار را بر اساس کار انتخاب کنید، سپس لبهها را بیازمایید
اگر نیاز انتخاب تونل است، از فهرست پلتفرم استفاده کنید. اگر انتساب زنده است، از پرسوجوی مالک استفاده کنید و هر tuple بیپاسخ را ناشناخته بگیرید. اگر جمعهای تاریخی است، از stats API استفاده کنید. سپس سه لبهای را بیازمایید که پیادهسازیهای سادهلوحانه را میشکنند: بستهای در جهت معکوس، جریانی که پیش از پرسوجو بسته میشود، و فراخوانی که VPN فعال نیست.
هیچکدام از اینها به غیرفعالکردن SELinux، root کردن گوشی یا خوانا کردن جدولهای هسته برای همه نیاز ندارد. محدودیت خودش هدف است: پلتفرم انتساب را پشت یک قرارداد برد تا پاسخ در دسترس مؤلفهای باشد که بهطور مشروع به آن نیاز دارد، و نه هیچکس دیگر.