Android VpnService: TUN پڑھائی کا رکنا بمقابلہ ENOBUFS لکھائی کی غلطیاں
کبھی واپس نہ آنے والی پڑھائی اور ENOBUFS کے ساتھ ناکام ہونے والی لکھائی کی وجوہات مخالف ہیں۔ درست اصلاح چننے کے لیے اعلان شدہ ونڈو اور پیکٹ کی لمبائی ناپیں۔
ایک ہی VpnService لوپ میں دو علامات ظاہر ہوتی ہیں اور دونوں پر ایک ہی چیز کا الزام لگایا جاتا ہے: ایک قاری جو کبھی واپس نہیں آتا، اور ایک لکھائی جو ENOBUFS کے ساتھ ناکام ہوتی ہے۔ یہ ایک ہی بگ کی اقسام نہیں ہیں۔ رکی ہوئی پڑھائی ہم منصب کے ساتھ بہاؤ کے کنٹرول پر اختلاف ہے؛ ناکام لکھائی وہ پیکٹ ہے جسے انٹرفیس قبول نہیں کرے گا۔ اگر آپ انہیں ایک مسئلہ سمجھیں گے تو ایسے بفر ترتیب دینے میں ایک دن گزاریں گے جو کبھی شامل ہی نہیں تھے۔ نیچے دی تقسیم یہ ہے کہ میں لوپ کا کون سا نصف پہلے ناپوں۔
ایک لوپ، دو خودمختار ناکامی کے مقامات
آپ کی سروس TUN فائل ڈسکرپٹر سے IP پیکٹ پڑھتی ہے، اسے دیکھتی ہے اور اسی ڈسکرپٹر میں جواب لکھتی ہے۔ اس راستے میں جو کچھ ٹوٹتا ہے وہ ان دو کالوں میں سے ایک پر ظاہر ہوتا ہے، اسی لیے دونوں کو ٹنل ٹوٹا ہوا بتایا جاتا ہے۔ کالوں کے معاہدے مختلف ہیں۔ پڑھائی انتظار کرتی ہے کہ کرنل آپ کو پیکٹ دے، اور لکھائی کرنل کو ایسا پیکٹ دیتی ہے جسے مسترد کرنے کا اسے حق ہے۔
پڑھائی سوچنے کے لیے آسان نصف ہے۔ جو پڑھائی کچھ واپس نہیں دیتی وہ یا کبھی نہ آنے والی ٹریفک کا انتظار کرتی ہے یا اس لیے انتظار کرتی ہے کہ ہم منصب نے بھیجنا بند کر دیا۔
رکی ہوئی پڑھائی بہاؤ کے کنٹرول پر اختلاف ہے
TUN ڈیوائس پڑھنا پیکٹ دستیاب ہونے تک بلاک کرتا ہے۔ یہ ڈیزائن کیا گیا رویہ ہے، اور اسی لیے read کے اندر کھڑا تھریڈ غیر فعال لنک پر معمول کا لگتا ہے۔ یہ ہینگ اس وقت بنتا ہے جب آپ کا اپنا TCP اسٹیک مقامی ایپ سے ڈیٹا لیتا رہتا ہے لیکن دور کے ہم منصب کو مزید بھیجنے کے لیے کافی ونڈو کبھی اعلان نہیں کرتا۔ ہم منصب آپ کی اعلان کردہ ونڈو بھرتا ہے، ACK اور ونڈو اپ ڈیٹ کا انتظار کرتا ہے، اور رک جاتا ہے۔ پھر آپ کے قاری کے پاس واپس دینے کو کچھ نہیں ہوتا، اور ایپ کو کھلا مگر خاموش کنکشن نظر آتا ہے۔
عام وجوہات وہ اسٹیک ہیں جو اعلان شدہ ونڈو کو ٹریک کیے بغیر ساکٹ میں لکھتا ہے، اور دوسرا جو اپنا بفر خالی کرنے کے بعد ونڈو اپ ڈیٹ بھیجنا بھول جاتا ہے۔ باہر سے دونوں ایک جیسے دکھتے ہیں: بائٹس باہر جاتے ہیں، کچھ واپس نہیں آتا، کوئی غلطی نہیں اٹھتی۔ پڑھائی کے تھریڈ کو پارسنگ، DNS حل یا ڈسک I/O پر بلاک کرنا غیر متعلقہ وجہ سے اسی شکل کی علامت پیدا کرتا ہے، اس لیے بہاؤ کے کنٹرول کو الزام دینے سے پہلے یقینی بنائیں کہ تھریڈ واقعی read کے اندر ہے۔
ہم منصب کی بھیجنے کی قطار اور وہ ونڈو جو اس نے آخری بار دیکھی، چیک کریں۔ اگر آپ کی ونڈو صفر ہونے کی وجہ سے ہم منصب ڈیٹا روکے ہوئے ہے تو اصلاح آپ کے ACK راستے میں ہے، TUN پڑھائی میں نہیں۔
ENOBUFS حجم کا مسئلہ ہے، بفر کی کمی نہیں
TUN انٹرفیس پر ENOBUFS واپس کرنے والی لکھائی عام طور پر پیکٹ کی لمبائی مسترد کرتی ہے۔ انٹرفیس کا MTU ہوتا ہے، اس سے بڑا پیکٹ قطار میں نہیں ڈالا جا سکتا، اور وہی پیکٹ دوبارہ آزمانے پر ہر بار اسی طرح ناکام ہوتا ہے۔ یہی قطعیت اشارہ ہے۔ حقیقی بفر کی کمی دباؤ کم ہونے پر ختم ہو جاتی ہے، جبکہ حجم کی نامنظوری اس بات کی پروا نہیں کرتی کہ آپ کتنی دیر انتظار کریں۔
TCP کے پاس اسی کے لیے ایک طریقہ کار ہے، اور یہی وہ قدم ہے جو لوگ چھوڑ دیتے ہیں: MSS کلیمپ۔ جب آپ صارف کی فضا میں سیگمنٹ تیار کرتے ہیں تو پے لوڈ کا حجم آپ خود چنتے ہیں، اور اسے ٹنل کے MTU کے بجائے مقامی انٹرفیس کے MTU سے لینا ایسے سیگمنٹ خارج کرتا ہے جو ٹنل اٹھا نہیں سکتی۔ دونوں میں سے چھوٹے تک محدود کریں۔ کلیمپ صرف TCP کو ڈھکتا ہے، اس لیے بہت بڑا UDP ڈیٹا گرام آپ کے کوڈ کو ٹکڑے کرنا یا چھوڑنا ہوگا۔
ڈیبگ کرتے وقت آپ جو بھی پیکٹ لکھیں اس کی لمبائی ریکارڈ کریں۔ جو لمبائی ناکام ہوتی ہے وہی جواب ہے، اور کوششوں کے درمیان وہ نہیں بدلے گی۔
تین پیمائشیں جو کیس الگ کرتی ہیں
دونوں میں سے کسی ناکامی کے لیے آپ کو ڈیبگر کی ضرورت نہیں۔ صحیح حصے تک پہنچانے کے لیے تین مشاہدے کافی ہیں، اور وہ دوبارہ تعمیر سے کم وقت لیتے ہیں۔
| What you see | Where to look |
|---|---|
| Reader parked, peer send queue empty, last advertised window zero | Window updates in your ACK path |
| Write fails on one length and succeeds on a smaller one | MSS clamp and tunnel MTU |
| Both directions look fine, app still sees no data | Protected socket and routing loop |
اگر تیسری سطر مطابقت رکھتی ہے تو لوپ خود ٹھیک ہے اور مسئلہ وہاں ہے جہاں ٹنل اسے دیکھ نہیں سکتی: ایک غیر محفوظ ٹرانسپورٹ ساکٹ جو آپ کی اپنی VPN ٹریفک کو واپس ٹنل میں لے جاتا ہے، جو عام طور پر ایسے کنکشن کی صورت میں ظاہر ہوتا ہے جو کامیاب ہو کر پھر کچھ نہیں پہنچاتا۔
وہ پلمبنگ ٹھیک کریں جس پر دونوں راستے منحصر ہیں
انٹرفیس کے درست بننے تک کسی علامت کا پیچھا کرنا بے سود ہے۔ اہم builder کالز پتہ، راستہ، MTU اور رضامندی ہیں، اسی ترتیب میں:
Builder()
.addAddress(10.8.0.2, 32)
.addRoute(0.0.0.0, 0)
.setMtu(1400)
.establish()MTU کو نمونے کے استعمال کردہ قدر پر چھوڑنے کے بجائے ٹنل کی حقیقی قدر پر مقرر کریں۔ آگے آپ جو کچھ لکھیں گے وہ اسی عدد سے محدود ہے، اور آپ کے مقرر کردہ MTU اور جس MSS تک محدود کرتے ہیں ان کے درمیان تفاوت اصلاح کے بعد لکھائی کی ناکامی واپس لانے کا سب سے عام طریقہ ہے۔ پھر اس ساکٹ کی حفاظت کریں جو ٹریفک خود VPN سرور تک لے جاتا ہے۔ protect کے بغیر وہ پیکٹ آپ ہی کی ٹنل میں واپس روٹ ہو جاتے ہیں اور کنکشن کبھی مکمل نہیں ہوتا، جو رکی ہوئی پڑھائی جیسا لگتا ہے مگر روٹنگ لوپ ہے۔
رضامندی سے بچا نہیں جا سکتا۔ prepare کے null واپس نہ کرنے پر شروع کی گئی سروس کو انٹرفیس نہیں ملتا، اور establish پھینکنے کے بجائے null واپس کرتا ہے، اس لیے لوپ شروع کرنے سے پہلے ڈسکرپٹر جانچیں۔ VpnService حوالہ وہ جگہ ہے جہاں نمونہ نقل کرنے کے بجائے builder معاہدہ کی تصدیق کریں۔
ایک وقت میں ایک سمت کی تصدیق کریں
TCP کو چھونے سے پہلے ٹنل سے UDP گونج بھیجیں اور پیکٹ کی لمبائیاں دیکھیں۔ پھر مصافحہ کھولیں اور تصدیق کریں کہ سیگمنٹ آپ کے حساب کردہ MSS سے نیچے رہتے ہیں۔ کنکشن کے بجائے پیکٹ گنیں، تاکہ رکاوٹ لٹکے ہوئے ساکٹ کے بجائے کاؤنٹر میں خلا کے طور پر نظر آئے۔ لکھائیوں کے جھونکے کے بعد پڑھائی رک جائے تو ونڈو مشتبہ ہے؛ لکھائیاں فوراً ناکام ہوں تو لمبائی۔
پھر ڈیوائس ری بوٹ کریں اور ایک بار دہرائیں، کیونکہ جو VpnService صرف پہلی نیٹ ورک تبدیلی تک کام کرے وہ بہاؤ کے کنٹرول کا نہیں، روٹنگ کا مسئلہ ہے۔