چرا Android VpnService بخشی از ترافیک را میگیرد و نه همه را
مسیرها، ورودیهای DNS و دامنهٔ هر برنامه تعیین میکنند کدام بستهها به دستگاه tun میرسند، چرا دستگاههای محلی ناپدید میشوند و سازنده از چه چیزی سر باز میزند.
هیچ چیز در VpnService بهخودیخود ترافیک را رهگیری نمیکند. از سیستم یک واسط tun میخواهد، مجموعهای از نشانیها و مسیرها را به آن واسط میدهد و از همان لحظه هسته هر بستهای را که با آن مسیرها مطابقت دارد در توصیفگر فایلی که برنامه میتواند بخواند کپی میکند. کد برنامه هرگز بستههایی را که مسیرها انتخاب نکردهاند نمیبیند، پس دامنهٔ یک تونل پیش از آنکه پرسشی برنامهنویسی باشد، پرسشی دربارهٔ جدول مسیریابی است. به همین دلیل همان پیادهسازی سرویس میتواند روی یک گوشی همهٔ جریانها را ببلعد و روی گوشی دیگر فقط بخشی را، و به همین دلیل افزودن یک مسیر گاهی تونلی را که خراب به نظر میرسید تعمیر میکند. آنچه میگیرید همان است که اعلام کردهاید، منهای هر چیزی که سازنده بیصدا رد کرد.
مسیرها تعیین میکنند کدام بستهها میرسند
Builder.addRoute(prefix, prefixLength) یک پیشوند را در جدول مسیریابی شبکهٔ VPN نصب میکند. سیستم آن شبکه را برای برنامههای درون دامنه پیشفرض میداند، پس ورودیهای آن پیش از مسیر پیشفرض شبکهٔ فیزیکی بررسی میشود و یک پیشوند منطبق برای منحرفکردن یک جریان کافی است. بنابراین مسیر 0.0.0.0/0 کل IPv4 را میگیرد، از جمله چاپگر، هدف cast و صفحهٔ مدیریت روتر.
کارخواهانی که این پوشش را بدون حذف ورودی پیشفرض فیزیکی میخواهند، در عوض از جفت 0.0.0.0/1 و 128.0.0.0/1 استفاده میکنند. دو مسیر مشخصتر بر مسیر پیشفرض پیروز میشوند، با هم تمام فضای نشانی را میپوشانند و ورودی پیشفرض خود گوشی برای سوکتهایی که هنوز به شبکهٔ فیزیکی بستهاند در جدول میماند.
نشانیها و ورودیهای DNS از همان سازنده میآیند و به پرسش دیگری پاسخ میدهند. addAddress به واسط نشانیای با طول پیشوند میدهد و addDnsServer حلکنندهای نصب میکند که برنامههای درون دامنه از طریق واسط از آن میپرسند. هر دو خانواده میتوانند روی یک واسط باشند، پس مجموعهٔ مسیرهای فقط IPv4 در کنار نشانی IPv6 پیکربندیای است که درست خوانده میشود و باز هم نشت میکند. اگر خانوادهای را مسیریابی کنید که واسط در آن نشانی ندارد، هسته برای آن بستهها نشانی مبدأ ندارد و آنها هرگز در توصیفگر ظاهر نمیشوند.
establish() چه برمیگرداند
establish() یک ParcelFileDescriptor برمیگرداند، یا null وقتی برنامه مجاز نیست. null وضعیت رضایت است نه خطای شبکه: VpnService.prepare() باید از قبل null برگردانده باشد یا کاربر باید پیش از فراخوانی گفتوگوی سیستم را تأیید کرده باشد. رضایت میتواند در حین اجرای سرویس پس گرفته شود و واسط بدون هیچ فراخوانی بازگشتی به حلقهٔ خواندن شما زیر پایتان بسته میشود.
توصیفگر غیرمسدودکننده است مگر اینکه خلافش را بگویید، پس خواندن بیدرنگ و بدون داده برمیگردد بهجای آنکه رشته را پارک کند. حلقهای که برای سوکت مسدودکننده نوشته شده یک هسته را میسوزاند و حلقهای که خواندن خالی را مرگبار میداند تونل را در یک ثانیهٔ بیکار پاره میکند. Builder.setBlocking(true) آن رفتار را تغییر میدهد و بهایش رشتهای است که درون خواندن منتظر میماند.
آرگومانهای مسیر و نشانی بهعنوان مقادیر LinkAddress اعتبارسنجی میشوند و مقصدهای multicast مانند 224.0.0.0/4 یکسره رد میشوند. واسط tun نقطهبهنقطه هم دامنهٔ پخش ندارد، پس کشف multicast از طریق آن طراحی دیگری میخواهد نه یک مسیر بیشتر. مرجع VpnService.Builder فهرستی است که باید پیش از نتیجهگیری دربارهٔ نادیدهگرفتهشدن یک فراخوانی بررسی کنید.
دامنهٔ هر برنامه مجموعه را تنگ میکند، نه تونل را
از API 21 سازنده addAllowedApplication و addDisallowedApplication را میپذیرد و این دو فهرست درون یک سازنده ناسازگارند. فهرست مجاز تونل را برای آن بستهها موجود میکند در حالی که بقیه شبکهٔ فیزیکی را نگه میدارند؛ فهرست غیرمجاز همهچیز جز بستههای نامبرده را میگیرد، که کارخواه آن را بهعنوان تونلسازی تفکیکشده عرضه میکند. پیش از API 21 هیچ سازوکار پلتفرمی وجود ندارد و رفتار هر برنامه از کارخواهان شخص ثالث میآمد که درون ارسالکنندهٔ خود بر اساس UID پالایش میکردند. این تفاوت هنگام اشکالزدایی مهم است، چون تونل هر برنامه با فهرست مجاز یک بسته تقریباً هیچ ترافیکی تولید نمیکند و این رفتار درست است، نه مسیر خراب.
دامنه هر چه باشد، سوکتی که برنامهٔ شما به سرور VPN باز میکند نباید از تونلی که میسازد بگذرد. VpnService.protect(socket) آن سوکت را به شبکهٔ زیرین میبندد تا بستههایش از دستگاه tun بگذرند. اگر فراخوانی را جا بیندازید، دستدادن توسط واسطی بلعیده میشود که قرار بود آن را بالا بیاورد.
VpnService.setUnderlyingNetworks حلقه را با گفتن اینکه کدام شبکهٔ فیزیکی تونل را حمل میکند میبندد. آن مقدار تعیین میکند VPN چگونه اتصال را به برنامههای دیگر گزارش میکند و مقدار null یا کهنه علت رایج تونلی متصل است که بقیهٔ گوشی آن را آفلاین میداند. آن را هنگام تغییر شبکهٔ زیرین بهروز کنید، نه فقط هنگام راهاندازی.
مجموعهای بررسی و MTU: جایی که مسیر درست باز هم شکست میخورد
وقتی بستهها میرسند، دو جزئیات در سطح بسته تعیین میکند آیا چیزی سرتاسر کار میکند. نخستین، نشانی مبدأ است: با چند نشانی واسط ممکن است هسته بستههای حامل نشانی نخست را بفرستد، پس ارسالکنندهای که سرآیندها را بازنویسی میکند باید نشانی و مجموع بررسی پوشانندهٔ آن را درست کند.
دومین، دامنهٔ مجموع بررسی است. وقتی برنامه خودش بستههای TCP یا UDP میسازد، مجموع بررسی یک شبهسرآیند شامل نشانی مبدأ، نشانی مقصد، شمارهٔ پروتکل و طول انتقال را میپوشاند و حذف هر یک از آنها بستههایی میدهد که میرسند و دور ریخته میشوند. آن شکست مانند پایان مهلت پس از پاسخی است که هرگز نمیآید، و این بیشتر مردم را به مسیرها برمیگرداند. setMtu MTU واسط را ثابت میکند و هر خواندن دقیقاً یک بستهٔ کامل IP برمیگرداند، پس بافری که برای جریان بایتی نوشته شده تجزیهٔ شما را قطعهقطعه میکند نه بسته را.
یک گذر تأیید که لایهها را جدا میکند
برای یافتن اینکه دامنه کجا اشتباه رفت، لایهها را به ترتیب بیازمایید. از دستگاه بپرسید شبکهٔ VPN واقعاً کدام مسیرها را دارد، سپس بررسی کنید جریان به یک نشانی شناختهشده بهصورت خواندن در توصیفگر ظاهر میشود یا نه، سپس بررسی کنید بستهای که ارسالکنندهٔ شما میفرستد درست شکل گرفته است یا نه. مشکل مسیر چیزی در توصیفگر نشان نمیدهد، در حالی که مشکل مجموع بررسی بستهای را نشان میدهد که میرسد و ناپدید میشود.
اگر چیزی به توصیفگر نمیرسد، مظنون مجموعهٔ مسیرهاست نه خواننده: پیشوندها را تأیید کنید، تأیید کنید نشانیای در همان خانواده وجود دارد و تأیید کنید برنامه درون دامنهای است که تعیین کردهاید. اگر بستهها به توصیفگر میرسند اما چیزی برنمیگردد، پیش از دستزدن دوباره به یک مسیر protect() و مقدار شبکهٔ زیرین را بررسی کنید. اگر طرف دور بستههای بدشکل گزارش میکند، مجموع بررسی و MTU جاهایی هستند که باید نگاه کنید. یک کاوش در هر لایه یک بعدازظهر حدسزدن را ذخیره میکند.