কীভাবে Android VPN টানেল থেকে একটি সাবনেট বাদ দেবেন
VpnService কোনো রুট বাদ দিতে পারে না, তাই আপনি যোগ করেন পরিপূরক: /8-এর জন্য আটটি উৎপন্ন প্রিফিক্স, সাথে IPv6, DNS ও লুকআপ পরীক্ষা যা বাকিটা ধরে।
Android-এর VpnService-এ কোনো বাদ দেওয়ার রুট নেই, এবং API পড়ার কোনো পরিমাণই একটি তৈরি করবে না। Builder.addRoute() কেবল টানেলে গন্তব্য যোগ করে, তাই একটি LAN সেগমেন্ট স্থানীয় রাখার মানে সেই সেগমেন্ট কখনোই রুট তালিকায় ঢোকে না। বরং আপনি যা যোগ করেন তা হলো সেগমেন্টের পরিপূরক, সাধারণ প্রিফিক্স হিসেবে প্রকাশিত। কাজটি গণিত, এবং এটি কয়েকটি অনুমানযোগ্য জায়গায় ব্যর্থ হয়।
নিচের পরীক্ষাগুলো ক্রমে করুন। প্রতিটি ধরে নেয় আগেরটি টিকেছে, তাই নিচের লক্ষণ প্যাচ করার বদলে প্রথম ব্যর্থতায় থামুন।
টানেল যা দাবি করে তা থেকে শুরু করুন
0.0.0.0/0 কে addRoute()-এ পাঠানো Android-কে বলে যে প্রতিটি IPv4 গন্তব্য টানেলের অন্তর্গত। এজন্যই এত ক্লায়েন্ট স্প্লিট টানেলিংয়ে অক্ষম দেখায়: তারা পুরো অ্যাড্রেস স্পেস দাবি করে, তারপর তা থেকে বিয়োগ করার চেষ্টা করে। বিল্ডারে কিছুই বিয়োগ করে না। আপনি যে নেটওয়ার্কটি স্থানীয় রাখতে চেয়েছিলেন তার জন্য আরও নির্দিষ্ট রুট মানুষ যা আশা করে তার উল্টো করে, কারণ দীর্ঘতম মিলে যাওয়া প্রিফিক্স জেতে এবং সেই নেটওয়ার্ক টানেলের ভিতরে পড়ে।
রুট তালিকাই সম্পূর্ণ নীতি। এর পিছনে প্রতি-গন্তব্য ভেটো নেই, এবং Builder রেফারেন্স এমন কোনো পদ্ধতি তালিকাভুক্ত করে না যা তা দেবে।
প্রিফিক্স গণিত থেকে পরিপূরক তৈরি করুন
বাদ দেওয়া প্রিফিক্সের পরিপূরক হলো অ্যাড্রেস ট্রির মূলে যাওয়ার পথে তার ভাইদের সেট। 1 থেকে বাদ দেওয়া দৈর্ঘ্য পর্যন্ত প্রতিটি দৈর্ঘ্যের জন্য, আপনার প্রিফিক্স ধারণ করা সেই দৈর্ঘ্যের ব্লকটি নিন, প্রিফিক্সের ঠিক নিচের বিটটি উল্টান, এবং ফলাফল বের করুন। এটি চালানোর আগ পর্যন্ত বিমূর্ত শোনায়।
10.0.0.0/8 বাদ দিলে আটটি রুট পাওয়া যায়: 128.0.0.0/1, 64.0.0.0/2, 32.0.0.0/3, 16.0.0.0/4, 0.0.0.0/5, 12.0.0.0/6, 8.0.0.0/7, 11.0.0.0/8। তাদের কোনোটিই 10.0.0.0/8 ধারণ করে না, এবং একসাথে তারা বাকি সব ঢেকে দেয়।
আটটি লাইন যেকোনো প্রিফিক্সের জন্য সেই তালিকা তৈরি করে:
import ipaddress
def complement(addr, length):
base = int(ipaddress.IPv4Address(addr))
out = []
for k in range(1, length + 1):
block = base & (0xFFFFFFFF << (32 - k))
sibling = block ^ (1 << (32 - k))
out.append(f"{ipaddress.IPv4Address(sibling)}/{k}")
return out
print(complement("10.0.0.0", 8))
রুট সংখ্যা প্রিফিক্স দৈর্ঘ্যের সমান, এবং সেটাই লক্ষ রাখার সংখ্যা। একটি একক /21 বাদ দেওয়া 21টি রুট খরচ করে এবং পড়ার যোগ্য থাকে। দশটি বাদ দেওয়া কয়েকশো খরচ করতে পারে, যার মানে সাধারণত জেনারেটর নয়, নেটওয়ার্ক ডিজাইন বদলাতে হবে।
অ্যাড্রেস পরিবার ও টানেলের পরিধি পরীক্ষা করুন
IPv4 রুট IPv6-এর জন্য কিছুই করে না। যদি টানেল একটি IPv6 অ্যাড্রেস দাবি করে, তবে একটি IPv6 LAN প্রিফিক্সের নিজস্ব পরিপূরক দরকার, 128 বিটের ওপর একই গণিতে তৈরি। কাগজে বাদ দেওয়া কাজ করছে অথচ স্থানীয় ডিভাইস পৌঁছানোর বাইরে থাকছে এমন একটি সাধারণ কারণ এই: হোস্টে পৌঁছানো হয়েছিল IPv6 দিয়ে, আর শুধু IPv4 কেটে বাদ দেওয়া হয়েছিল। গণিত ভুল বলে সিদ্ধান্তে আসার আগে দুই পরিবারই পরীক্ষা করুন।
পরিধি হলো পরীক্ষার অন্য অর্ধেক। Android নেটওয়ার্ক ও অ্যাপ অনুযায়ী রাউটিং ট্র্যাক করে, তাই একই প্রিফিক্স VPN কীভাবে ঘোষণা করা হয়েছে তার ওপর নির্ভর করে এক অ্যাপের জন্য টানেল হয়ে এবং অন্য অ্যাপের জন্য সরাসরি হতে পারে।
DNS ও প্রতি-অ্যাপ নিয়ম সঠিক জায়গায় রাখুন
রুট বাদ দেওয়া রিজলভার বদলায় না। একটি স্থানীয় হোস্টনেম এখনো টানেলের DNS সার্ভার দ্বারা উত্তর পেতে পারে, অথচ তার অ্যাড্রেস স্থানীয়ভাবে রাউট হয়, এবং অমিলটি দেখা যায় এমন নাম হিসেবে যা এমন কিছুতে সমাধান হয় যা LAN পৌঁছাতে পারে না। রিজলভার নীতি সচেতনভাবে নির্ধারণ করুন, একটি সার্চ ডোমেইন বা স্প্লিট-DNS নিয়ম দিয়ে। ধরে নেবেন না যে রুটগুলো আপনার হয়ে এটি ঠিক করেছে।
addDisallowedApplication() একটি অ্যাপকে টানেলের বাইরে রাখে; ভিতরে থাকা অ্যাপগুলোর জন্য এটি কোনো গন্তব্য বাইরে রাখে না। protect() আপনার খোলা সকেটকে টানেল এড়িয়ে যেতে দেয়, এভাবেই একটি টানেল নিজের ট্রান্সপোর্ট ধরা থেকে বাঁচে। দুটির কোনোটিই পরিপূরকের বিকল্প নয়।
নিজের তালিকা নয়, লুকআপ দিয়ে যাচাই করুন
টানেল চালু হলে, আপনি যে তালিকা বানিয়েছেন তা আবার পড়ার বদলে ডিভাইসকে জিজ্ঞাসা করুন একটি অ্যাড্রেস আসলে কোথায় যায়। iproute2 সহ আসা একটি বিল্ডে, adb shell ip route get 10.1.2.3 কার্নেল যে ইন্টারফেস বেছেছে তার নাম দেয়। একটি পাবলিক অ্যাড্রেসের জন্য একই কমান্ড চালান এবং নিশ্চিত করুন এটি টানেলের নাম দেয়।
আরও দুটি পরীক্ষা চক্রটি বন্ধ করে। আগে একটি স্থানীয় নাম সমাধান করুন, তারপর সমাধান হওয়া অ্যাড্রেসে adb shell ip route get চালান এবং এমন ইন্টারফেস দাবি করুন যা টানেল নয়, কারণ সফল adb shell ping -c 1 host.lan পৌঁছানো প্রমাণ করে, পথ নয়। টানেল একটি দাবি করলে IPv6-এর ওপর অ্যাড্রেস-পরিবার পরীক্ষাটি পুনরাবৃত্তি করুন। আপনি যে API স্তর শিপ করেন সেখানেই পরীক্ষা করুন, কারণ VPN নেটওয়ার্ক কীভাবে রাউটিং টেবিলে প্রকাশ পায় তা রিলিজের পর রিলিজে বদলেছে। কাগজে সঠিক দেখানো রুট তালিকা আর ভুল ইন্টারফেস বেছে নেওয়া লুকআপ ভিন্ন দুটি বাগ, এবং কেবল দ্বিতীয়টি ব্যবহারকারীর কাছে পৌঁছায়।
যদি বাদ দেওয়ার তালিকা বাড়তেই থাকে, রুট তৈরি করা থামান এবং দূরবর্তী নেটওয়ার্ক পুনঃনম্বর দিন বা টানেল সংকুচিত করুন। জেনারেটর অনুপস্থিত API-এর জন্য একটি উপায়, ডিজাইন টুল নয়।