რატომ იჭერს Android VpnService ტრაფიკის ნაწილს და არა ყველაფერს
მარშრუტები, DNS ჩანაწერები და აპისეული ფარგლები წყვეტენ, რომელი პაკეტები აღწევს tun მოწყობილობას, რატომ ქრება ლოკალური მოწყობილობები და რაზე თქვა უარი მშენებელმა.
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 არის ადგილები, სადაც უნდა შეხედოთ. თითო შემოწმება თითო ფენაზე ზოგავს ნახევარ დღეს ვარაუდს.