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 এমন একটি রিজলভার বসায় যা পরিধির অ্যাপগুলো ইন্টারফেস দিয়ে জিজ্ঞাসা করবে। দুই পরিবারই একটি ইন্টারফেসে থাকতে পারে, তাই IPv6 ঠিকানার পাশে শুধু IPv4 রুটের সেট এমন কনফিগারেশন যা ঠিকঠাক পড়া যায় এবং তবুও লিক করে। ইন্টারফেসে যে পরিবারের ঠিকানা নেই এমন পরিবার রুট করলে কার্নেলের ওই প্যাকেটগুলোর জন্য উৎস ঠিকানা থাকে না এবং সেগুলো ডেসক্রিপ্টরে কখনো দেখা যায় না।

establish() কী ফিরিয়ে দেয়

establish() একটি ParcelFileDescriptor ফেরায়, বা null যখন অ্যাপ অনুমোদিত নয়। null নেটওয়ার্ক ত্রুটি নয়, সম্মতির অবস্থা: VpnService.prepare() আগেই null ফেরাতে হবে, নয়তো ব্যবহারকারীকে কলের আগে সিস্টেম ডায়ালগ অনুমোদন করতে হবে। সার্ভিস চলার সময় সম্মতি প্রত্যাহার হতে পারে, আর আপনার পড়ার লুপে কোনো কলব্যাক না দিয়েই ইন্টারফেস আপনার নিচ থেকে বন্ধ হয়ে যায়।

ডেসক্রিপ্টরটি ব্লক করে না যদি আপনি অন্যথা না বলেন, তাই পড়া থ্রেড আটকে না রেখে সাথে সাথে কোনো ডেটা ছাড়াই ফেরে। ব্লকিং সকেটের জন্য লেখা লুপ একটি কোর পুড়িয়ে দেয়, আর খালি পড়াকে মারাত্মক ধরা লুপ নিষ্ক্রিয় এক সেকেন্ডে টানেল ছিঁড়ে ফেলে। Builder.setBlocking(true) সেই আচরণ বদলায়, আর দাম হলো পড়ার ভিতরে অপেক্ষা করা একটি থ্রেড।

রুট ও ঠিকানা আর্গুমেন্ট LinkAddress মান হিসেবে যাচাই হয়, আর 224.0.0.0/4-এর মতো মাল্টিকাস্ট গন্তব্য সরাসরি প্রত্যাখ্যাত হয়। পয়েন্ট-টু-পয়েন্ট tun ইন্টারফেসেরও ব্রডকাস্ট ডোমেইন নেই, তাই এর ওপর মাল্টিকাস্ট আবিষ্কারের জন্য আরেকটি রুট নয়, ভিন্ন ডিজাইন দরকার। 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-ই দেখার জায়গা। প্রতি স্তরে একটি পরীক্ষা এক দুপুর অনুমানের খরচ বাঁচায়।

সম্পর্কিত নিবন্ধ