Android VPN-এ IPv6 কাজ করে, তবু ব্রাউজার এখনও IPv4 বেছে নেয়
একটি ব্যক্তিগত IPv6 ঠিকানা হারিয়ে যাওয়ার চারটি ধাপ অনুসরণ করুন: রিজলভার, VpnService ঘোষণা, ব্রাউজারের প্রার্থী যাচাই এবং রুট বিতরণ।
VPN চালু আছে, ব্যক্তিগত ওয়েব UI ল্যাপটপ থেকে 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-এর উল্লেখই থাকে না।
আর কিছু বদলানোর আগে টানেলের রিজলভারকে সরাসরি জিজ্ঞাসা করুন। প্রথম দুটি কমান্ড দেখায় Android সক্রিয় নেটওয়ার্কে কোন DNS সার্ভারগুলো যুক্ত করেছে এবং 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 দিয়ে রুট, addDnsServer দিয়ে DNS সার্ভার, এবং allowFamily দিয়ে পুরো টানেলকে একটি অ্যাড্রেস ফ্যামিলিতে সীমাবদ্ধ করতে পারে। যে বিল্ডার allowFamily(AF_INET) ডেকে পরে একটি IPv6 রুট যোগ করেছে, সে সিস্টেমকে দুটি পরস্পরবিরোধী কথা বলেছে, আর সিস্টেম যে সীমাবদ্ধতা ধরে রাখে সেটি হলো ফ্যামিলি সীমাবদ্ধতা। fd00:61::/64 কভার করা একটি রুটের সঙ্গে ভিন্ন প্রিফিক্সে থাকা ইন্টারফেস ঠিকানা মিললে একই ধরনের কারণে ব্যর্থ হয়: ঠিকানাটি আছে, কিন্তু সেই রুটের জন্য এটি on-link নয়।
অ্যাপের টগল বিশ্বাস না করে ঘোষণাটি পড়ুন। dumpsys netd tun ইন্টারফেসে যুক্ত ঠিকানা ও রুট প্রিন্ট করে, আর dumpsys connectivity-এর ভেতরে প্রতি-নেটওয়ার্ক LinkProperties নেটওয়ার্ক যে ফ্যামিলিগুলোর দাবি করে তার পাশে 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 ক্যাশে করা ফলাফল দেখায়। আপনার নিজের রিজলভারে AAAA আছে এমন নামের জন্য কেবল A রেকর্ড ফেরানো রিজলিউশন সরাসরি এই ধাপটিই দেখায়।
ধাপ 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 উত্তরটি সেই রিজলভার থেকে আসছে যার কাছে জোনটি আছে, tun ইন্টারফেসের জন্য VpnService ও netd অবস্থা পড়ুন, Chromium প্রার্থীটি বাদ দিয়েছে কি না দেখতে একটি net-export ট্রেস নিন, তারপর ip -6 route get দিয়ে যাওয়া-আসা পরীক্ষা করুন। যে স্তরটি সত্যিই ব্যর্থ হয়েছে সেটি ঠিক করুন এবং স্প্লিট পলিসি অপরিবর্তিত রাখুন। ব্যক্তিগত নামের জন্য পাবলিক নামের চেয়ে ভিন্ন উত্তর দরকার হলে split tunneling ঠিক তার জন্যই।
ফোনের বিল্ড, ব্রাউজারের সংস্করণ এবং যে রিজলভারকে আপনি কোয়েরি করেছেন তা লিখে রাখুন। এই তিনটি তথ্য "IPv6 is broken"-কে পরবর্তী ব্যক্তির পুনরুৎপাদনযোগ্য কিছুতে বদলে দেয়।