الـ VPN يعمل، وواجهة الويب الخاصة تستجيب عبر IPv6 من حاسوب محمول، وما زال هاتف Android يفتح الصفحة نفسها عبر IPv4 أو يرفض تحميلها كليًا. سجل AAAA موجود. والنفق يعلن عنوان IPv6. ولا شيء في العميل يبدو خطأً. أربعة قرارات منفصلة تحدد النتيجة: أي مُحلِّل أجاب، وما الذي أعلنه VpnService إلى Android، وأي عائلة عناوين كان المتصفح مستعدًا لتجربتها، وهل تستطيع الحزمة إتمام الرحلة ذهابًا وإيابًا. عالجها بهذا الترتيب بدلًا من إضافة مسار افتراضي والأمل بأن ينتقل العَرَض.

الخطوة 1: أثبت أي مُحلِّل أجاب على استعلام AAAA

لا يملك Android مسار DNS واحدًا. يمكن لـ VPN أن يدفع خادم DNS لشبكته، وقد يستخدم النظام Private DNS لأسماء المضيفين التي تنطبق عليها، ويمكن لـ Chrome تشغيل مُحلِّله المشفَّر الخاص من chrome://settings/security. على هاتف يدفع فيه النفق 10.61.0.1 بينما يشير Private DNS إلى مزوّد عام، فإن الاسم داخل منطقتك الخاصة لا يُحلّ بواسطة الجهاز الذي يملك المنطقة. يحصل المتصفح على إجابة من مُحلِّل لا سلطة له على ذلك الاسم فيعتبرها مفقودة، ونص الخطأ لا يذكر DNS أبدًا.

اسأل مُحلِّل النفق مباشرة قبل أن تغيّر أي شيء آخر. يُظهر الأمران الأولان خوادم DNS التي ربطها Android بالشبكة النشطة، وهل Private DNS في الوضع التلقائي أو الصارم؛ ويُشغَّل الثالث من أي عميل داخل النفق، لا من الهاتف.

adb shell dumpsys connectivity | grep -A3 "DnsAddresses"
adb shell settings get global private_dns_mode
nslookup -type=AAAA nas.lan 10.61.0.1

الخطوة 2: اقرأ ما أعلنه VpnService فعليًا

جانب Android صريح لا مُستنتَج. يضيف VpnService.Builder العناوين بـ addAddress، والمسارات بـ addRoute، وخوادم DNS بـ addDnsServer، ويمكنه حصر النفق كله في عائلة عناوين واحدة بـ allowFamily. الباني الذي استدعى allowFamily(AF_INET) ثم أضاف لاحقًا مسار IPv6 يكون قد أخبر النظام بأمرين متناقضين، والقيد على العائلة هو ما يحتفظ به النظام. ومسار يغطي fd00:61::/64 مع عنوان واجهة في بادئة مختلفة يفشل للسبب نفسه: العنوان موجود، لكنه ليس على الرابط (on-link) لذلك المسار.

اقرأ الإعلان بدلًا من الوثوق بالمفتاح في التطبيق. يطبع dumpsys netd العناوين والمسارات المرتبطة بواجهة tun، ويُظهر LinkProperties لكل شبكة داخل dumpsys connectivity قائمة DNS إلى جانب العائلات التي تدّعيها الشبكة.

adb shell ip -6 addr show tun0
adb shell ip -6 route show table all | grep tun0
adb shell dumpsys connectivity | grep -E "LinkAddresses|Routes"

إذا كانت واجهة tun تحمل عنوان IPv6 لكن لا مسار يطابق بادئة الخدمة، فإن تسليم IPv6 معطوب أصلًا قبل أن يدخل المتصفح في الصورة. هذا خطأ مختلف عن متصفح يرفض المحاولة، ويحتاج إلى إصلاح مختلف.

الخطوة 3: المتصفح هو من يقرر هل يجرب IPv6 أصلًا

لا يستخدم Chromium كل سجل AAAA يصله. فمُحلِّل المضيفين لديه فحوصات وصول لـ IPv6 وتصفية للمرشحين قد تُسقِط IPv6 أو تخفض أولويته عندما يُحكَم على الشبكة بأنها غير قادرة على حمله، ويُظهر مصدر المُحلِّل ذلك الفحص إلى جانب الشروط المحيطة به. المنطق يعتمد على الإصدار والأعلام، ولهذا بالضبط قد يتصرف هاتفان على النفق نفسه تصرفًا مختلفًا. وبمجرد وجود المرشحين، يجري Happy Eyeballs سباقًا بين العائلتين لكل اتصال، فمسار IPv6 يجيب ببطء قد يخسر أمام IPv4 يجيب بسرعة.

راقب القرار بدلًا من استنتاجه. الجلسة المسجَّلة عبر chrome://net-export، والتي تُعاد في عارض netlog، تُظهر كل عملية تحليل والعائلة المختارة؛ ويعرض chrome://net-internals/#dns النتائج المخزَّنة. وعملية تحليل تُرجع سجلات A فقط لاسم لديه AAAA عند مُحلِّلك أنت تشير مباشرة إلى هذه المرحلة.

الخطوة 4: تحقق من أن الحزمة تستطيع إتمام الرحلة ذهابًا وإيابًا

اختيار العنوان ليس تسليمًا. يحتفظ Android بجداول توجيه وقواعد سياسة لكل شبكة تختار جدولًا بحسب fwmark، فمسار ظاهر في الجدول الرئيسي ليس تلقائيًا هو المسار الذي يستخدمه المقبس. وقراءة القواعد وطلب تحليل وجهة من النواة لعنوان مصدر يفصل الأمرين.

adb shell ip -6 rule show
adb shell ip -6 route get fd00:61::20 from fd00:61::10

مخرجات route get هي التي تجيب على السؤال. فإن ذكرت tun0 وما زالت الحزم تموت، فانتقل إلى MTU النفق وتحويل IPv6 على البوابة بدلًا من العودة إلى الهاتف.

لماذا يجعل إضافة ::/0 الأمر أسوأ عادةً

إضافة ::/0 تغيّر مطالبة النفق من بادئة واحدة إلى كل وجهة IPv6. على الهاتف قد يرضي ذلك فحص وصول، لأن عنوان اختبار خارج بادئتك يبدو الآن قابلًا للتوجيه. لكنه لا يخلق سعة IPv6 صاعدة على البوابة، وكثير من بوابات المنازل ليس لديها وصلة IPv6 عاملة أصلًا. النتيجة ثقب أسود لـ IPv6 العام: أسماء كانت تُرتد إلى IPv4 صارت تتعلق، وقد تبدو الصفحة الخاصة أفضل فقط لأن الفحص يقيس شيئًا مختلفًا.

وهناك كلفة سياسات أيضًا. مع ::/0 داخل النفق، تخرج كل حركة IPv6 عبر VPN، بما فيها حركة لم يوافق مالك الشبكة قط على حملها. اعتبر ذلك قرار نشر بمالك مسمّى، لا خطوة تنقيح تتخذها في الساعة 1 بعد منتصف الليل.

ترتيب العمل الذي يُنهي هذا

تأكد من أن إجابة AAAA تأتي من المُحلِّل الذي يملك المنطقة، واقرأ حالة VpnService وnetd لواجهة tun، والتقط أثر net-export لترى هل أسقط Chromium المرشح، ثم اختبر الرحلة ذهابًا وإيابًا بـ ip -6 route get. أصلح الطبقة التي فشلت فعلًا واترك سياسة التقسيم كما هي. وإذا كانت الأسماء الخاصة تحتاج إجابات مختلفة عن الأسماء العامة، فلهذا وُجد split tunneling.

دوّن بنية الهاتف وإصدار المتصفح والمُحلِّل الذي استعلمت عنه. تلك الحقائق الثلاث تحوّل "IPv6 is broken" إلى شيء يستطيع الشخص التالي إعادة إنتاجه.

مقالات ذات صلة