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 տեղադրում է լուծիչ, որին շրջանակում գտնվող հավելվածները կհարցնեն ինտերֆեյսի միջոցով։ Երկու ընտանիքներն էլ կարող են նստել մեկ ինտերֆեյսի վրա, ուստի միայն IPv4 երթուղիների հավաքածուն IPv6 հասցեի կողքին այնպիսի կոնֆիգուրացիա է, որը ճիշտ է կարդացվում և դեռ արտահոսում է։ Եթե երթուղավորում եք ընտանիք, որում ինտերֆեյսը հասցե չունի, միջուկը այդ փաթեթների համար աղբյուրի հասցե չունի, և դրանք երբեք չեն հայտնվում նկարագրիչում։

Ինչ է վերադարձնում establish()

establish() վերադարձնում է ParcelFileDescriptor, կամ null, երբ հավելվածը լիազորված չէ։ null-ը համաձայնության վիճակ է, ոչ թե ցանցային սխալ. VpnService.prepare() պետք է արդեն վերադարձրած լիներ null, կամ օգտատերը պետք է հաստատած լիներ համակարգի երկխոսությունը մինչև կանչը։ Համաձայնությունը կարող է չեղարկվել ծառայության աշխատանքի ընթացքում, և ինտերֆեյսը փակվում է ձեր ոտքերի տակ՝ առանց ձեր ընթերցման հանգույց ոչ մի հետկանչի։

Նկարագրիչը արգելափակող չէ, եթե այլ բան չասեք, ուստի ընթերցումը վերադառնում է անմիջապես առանց տվյալների՝ թելը կանգնեցնելու փոխարեն։ Արգելափակող սոկետի համար գրված հանգույցն այրում է մեկ միջուկ, իսկ դատարկ ընթերցումը մահացու համարող հանգույցը պոկում է թունելը մեկ պարապ վայրկյանում։ Builder.setBlocking(true) փոխում է այդ վարքը, և գինը ընթերցման ներսում սպասող թելն է։

Երթուղու և հասցեի արգումենտները վավերացվում են որպես LinkAddress արժեքներ, իսկ multicast նպատակակետերը, ինչպիսին է 224.0.0.0/4-ը, անմիջապես մերժվում են։ Կետ-կետ tun ինտերֆեյսը նույնպես հեռարձակման տիրույթ չունի, ուստի դրա միջով multicast հայտնաբերումը պահանջում է այլ ձևավորում, ոչ թե ևս մեկ երթուղի։ Տեղեկատուն 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-ն այն տեղերն են, որտեղ պետք է նայել։ Մեկ ստուգում յուրաքանչյուր շերտի համար խնայում է կես օր գուշակություն։

Առնչվող հոդվածներ