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 ફક્ત પ્રથમ નેટવર્ક પરિવર્તન સુધી જ કામ કરે છે તે પ્રવાહ નિયંત્રણની નહીં, રૂટિંગની સમસ્યા છે.