ping کردن 192.0.2.10 فقط می‌گوید که یک نشانی به پژواک ICMP پاسخ داده است و بس. در شبکه‌ای بدون تونل ممکن است باز هم موفق شود، چون نشانی می‌تواند در زیرشبکه محلی باشد؛ با تونلی سالم ممکن است شکست بخورد، چون سرور ICMP را رها می‌کند در حالی که HTTPS سرویس می‌دهد. بررسی‌ای که اسکریپت شما لازم دارد، یک پرس‌وجوی وضعیت پلتفرم به‌همراه آزمون مسیر برای مقصدی است که برنامه استفاده می‌کند. این دو را دو فیلد جداگانه بدانید، هرگز یک boolean واحد نه.

پاسخ پژواک واقعاً چه چیزی را ثابت می‌کند

ICMP یک پروتکل دسترس‌پذیری است که هیچ تصوری از رابط‌ها یا تونل‌ها ندارد. وقتی پاسخی می‌رسد، هسته گزارش می‌دهد که میزبانی پاسخ داده است؛ گزارش نمی‌دهد که کدام مسیر درخواست یا پاسخ را حمل کرده است. فیلتر کردن سیگنال را شلوغ‌تر هم می‌کند، چون دیوار آتشی که درخواست‌های پژواک را مسدود می‌کند چیزی درباره پورت‌های پشت آن نمی‌گوید. و یک نمایه با تونل تقسیم‌شده می‌تواند نشانی آزمون‌شده را عمداً کنار بگذارد در حالی که هر مقصد دیگری کار می‌کند.

آنچه واقعاً می‌خواهید بدانید این است که آیا سرویس مشخصی روی مسیری که برنامه انتخاب می‌کند قابل استفاده است. میزبان و پورتی را که برنامه فراخوانی می‌کند انتخاب کنید، سپس پاسخی در لایه کاربرد بخواهید.

دسترس‌پذیری و وضعیت تونل دو واقعیت متفاوت‌اند و هر یک می‌تواند درست باشد در حالی که دیگری نادرست است. جدا نگه داشتن آن‌ها می‌گوید شکست از کیست. ادغام آن‌ها در یک بررسی، تیکتی می‌سازد که هیچ‌کس نمی‌تواند مسیردهی کند.

از Android انتقال را بخواهید، نه ping را

Android به پرسش وضعیت مستقیماً از طریق ConnectivityManager پاسخ می‌دهد. توانایی‌های شبکه فعال یک پرچم انتقال VPN دارند که وقتی ترافیک از یک رابط VPN مسیردهی می‌شود تنظیم می‌شود. فراخوانی محلی است، ارزان است و به پاسخ دادن هیچ میزبان دوری بستگی ندارد.

این نسخه Java وقتی شبکه فعالی وجود ندارد، به‌جای پرتاب استثنا مقدار false برمی‌گرداند. چون شبکه فعال را می‌خواند، نمایهٔ انتخابی کاربر در تنظیمات را بازتاب می‌دهد.

ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkCapabilities caps = cm.getNetworkCapabilities(cm.getActiveNetwork());
boolean tunnel = caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);

برای اعلان‌های تغییر، یک NetworkCallback ثبت کنید نه اینکه با زمان‌سنج نظرسنجی کنید. این را با VpnService.prepare اشتباه نگیرید، که فقط گزارش می‌دهد آیا کاربر اجازهٔ VPN داده است، نه اینکه بسته‌ها جریان دارند یا نه. لغو از طریق VpnService.onRevoke می‌آید، اما فراموش کردن یک نمایه در تنظیمات هیچ پخشی تولید نمی‌کند.

iOS: نام رابط‌ها یک هیوریستیک است، نه قرارداد

بسیاری از قطعه‌کدهای iOS تنظیمات پروکسی را بررسی می‌کنند، واژه‌نامهٔ __SCOPED__ را پیدا می‌کنند و کلیدهای رابط مانند tap، tun، utun2، ppp یا ipsec را می‌جویند. این رویکرد یک قرارداد نام‌گذاری است، نه تضمین مستند، بنابراین به‌روزرسانی سیستم‌عاملی که یک رابط را تغییر نام دهد می‌تواند آن را بشکند. همچنین گرایش دارد برای ورودی‌های کهنه‌ای که از تونل جان سالم به در می‌برند درست گزارش دهد.

وقتی برنامهٔ شما مالک پیکربندی است، NEVPNManager وضعیت واقعی می‌دهد. تونلی را توصیف می‌کند که برنامهٔ شما آغاز کرده است؛ با VPN هربرنامه‌ای، برنامه‌های دیگر ممکن است کاملاً بیرون از آن تونل باشند.

پیش از خواندن وضعیت، ترجیحات را دوباره بارگذاری کنید، چون مقدار با آمدن و رفتن تونل تغییر می‌کند:

let manager = NEVPNManager.shared()
manager.loadFromPreferences { error in
    let up = manager.connection.status == .connected
    print(up)
}

پرچم وضعیت پلتفرم باز هم ثابت نمی‌کند که ترافیک به همتا می‌رسد. همین شکاف را آزمون مسیر می‌پوشاند و به همین دلیل یک boolean برای پشتیبانی کافی نیست.

نمایه‌های Windows یک فیلد وضعیت واقعی ارائه می‌دهند

Windows نمایه‌های VPN را جدا از آداپترها ردیابی می‌کند و Get-VpnConnection برای هر نمایهٔ شناخته‌شده یک ConnectionStatus ارائه می‌دهد. نمایه‌هایی که از طریق کلاینت Windows یا بستهٔ تأمین ساخته شده‌اند اینجا دیده می‌شوند. این cmdlet یک آشکارساز عمومی تونل نیست.

فیلد را برای یک نمایهٔ نام‌دار بخوانید و وقتی نمایه به همهٔ کاربران تعلق دارد -AllUserConnection را اضافه کنید:

Get-VpnConnection -Name "Fabrikam" | Select-Object Name, ConnectionStatus, ServerAddress

کلاینت‌های شخص ثالث اغلب حتی وقتی تونلشان فعال است برای این cmdlet نامرئی می‌مانند، بنابراین نتیجهٔ خالی یعنی نامعلوم، نه قطع‌شده. فهرست آداپترها هم به همان اندازه گمراه‌کننده است: آداپتر یک فروشنده می‌تواند در حالت بیکار باقی بماند و بعضی آداپترها فقط در طول یک نشست وجود دارند. برای نمایه‌ای که Windows مالک آن است، rasdial یک کد خروج گزارش می‌کند که می‌توانید بر آن شاخه بزنید. همهٔ این‌ها باور محلی را توصیف می‌کنند، نه دسترس‌پذیری همتا.

وضعیت به‌همراه مسیر: بررسی دوبخشی

نمایهٔ متصل چیزی دربارهٔ اینکه بسته‌ها کجا می‌روند نمی‌گوید. تونل تقسیم‌شده می‌تواند تونل را بالا نگه دارد در حالی که مقصدی را که برایتان مهم است کنار می‌گذارد. نمایه‌های always-on با سروری مرده متصل می‌مانند و یک kill switch وقتی تونل می‌افتد عمداً ترافیک را مسدود می‌کند.

هر دو لایه را بیازمایید: تأیید کنید که یک انتقال VPN یا نمایه فعال است، سپس تأیید کنید که مقصد تفکیک می‌شود و از همان مسیر پاسخ می‌دهد. در Linux از هسته بپرسید از کدام رابط استفاده می‌کند:

ip route get 192.0.2.10
resolvectl query intranet.example.test

وقتی کلاینت بیرون از کنترل شماست، هر دو نتیجه را ثبت کنید و بگذارید الگو به لایه اشاره کند. متصل به‌همراه آزمون مسیر ناموفق به تونل تقسیم‌شده، DNS یا سیاست اشاره دارد. قطع‌شده به‌همراه آزمون مسیر موفق یعنی مسیر دیگری ترافیک را حمل می‌کند.

آزمونی که می‌توانید به ترتیب اجرا کنید

اول وضعیت پلتفرم را ثبت کنید، پیش از آنکه هر وضعیت شبکه را تغییر دهید. نام میزبان را تفکیک کنید و خانوادهٔ نشانی را یادداشت کنید، چون یک رکورد AAAA می‌تواند مسیری متفاوت از رکورد A بگیرد. پورت سرویس را باز کنید و به‌جای دست‌دادن صرف، پاسخ کاربرد بخواهید. هر مشاهده را در فیلد گزارش خودش نگه دارید.

روی یک ماشین Linux با dig و curl، سه فرمان تفکیک، مسیر و خود سرویس را پوشش می‌دهند. آن‌ها را با تونل فعال و دیگربار با تونل افتاده اجرا کنید:

dig +short intranet.example.test
ip route get 198.51.100.20
curl -sS -o /dev/null -w "%{http_code}" --max-time 8 https://intranet.example.test/health

چهار فیلد جدا از یک boolean پیشی می‌گیرند، چون وضعیت، DNS، مسیر و سیاست مستقل از هم شکست می‌خورند و به تیم‌های مختلف می‌رسند. ping در ترمینال یک بررسی دستی خوب برای سلامت می‌ماند. اما تعریف ضعیفی از اتصال برای خودکارسازی است، جایی که API پلتفرم و آزمون مسیر به پرسش واقعی پاسخ می‌دهند.

مقالات مرتبط