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 प्रवाह नियन्त्रणको होइन, रुटिङको समस्या हो।