Android-ის VpnService-ს არ აქვს გამორიცხვის მარშრუტი და API-ის არავითარი რაოდენობის კითხვა ვერ შექმნის მას. Builder.addRoute() გვირაბს ყოველთვის მხოლოდ დანიშნულებებს უმატებს, ასე რომ LAN სეგმენტის ლოკალურად შენახვა ნიშნავს, რომ ეს სეგმენტი მარშრუტების სიაში საერთოდ არ შედის. სამაგიეროდ ამატებთ სეგმენტის დანამატს, გამოხატულს ჩვეულებრივი პრეფიქსებით. სამუშაო არითმეტიკაა და ის რამდენიმე წინასწარმეტყველებად ადგილას ვარდება.

ქვემოთ მოცემული შემოწმებები რიგრიგობით გაიარეთ. თითოეული ვარაუდობს, რომ წინამ შეძლო, ამიტომ გაჩერდით პირველ წარუმატებლობაზე, ქვემოთ სიმპტომების შეკეთების ნაცვლად.

დაიწყეთ იმით, რასაც გვირაბი ამტკიცებს

0.0.0.0/0-ის addRoute()-ში გადაცემა Android-ს ეუბნება, რომ ყოველი IPv4 დანიშნულება გვირაბს ეკუთვნის. ამიტომ ბევრი კლიენტი გაყოფილი გვირაბიზაციის უნარის გარეშე გამოიყურება: ისინი მთელ მისამართთა სივრცეს ითხოვენ და შემდეგ ცდილობენ მისგან გამოკლებას. ამგებში არაფერი აკლებს. უფრო კონკრეტული მარშრუტი იმ ქსელისთვის, რომლის ლოკალურად დატოვებაც გინდოდათ, მოსალოდნელის საწინააღმდეგოს აკეთებს, რადგან ყველაზე გრძელი დამთხვევადი პრეფიქსი იმარჯვებს და ეს ქსელი გვირაბის შიგნით ექცევა.

მარშრუტების სია მთელი პოლიტიკაა. მის უკან დანიშნულების ვეტო არ არსებობს და Builder-ის ცნობარი არ ჩამოთვლის არცერთ მეთოდს, რომელიც მას მოგვცემდა.

შექმენით დანამატი პრეფიქსების არითმეტიკიდან

გამორიცხული პრეფიქსის დანამატი მისი დედმამიშვილების სიმრავლეა მისამართების ხის ფესვამდე მიმავალ გზაზე. 1-დან გამორიცხულ სიგრძემდე ყოველი სიგრძისთვის აიღეთ იმ სიგრძის ბლოკი, რომელიც თქვენს პრეფიქსს შეიცავს, გადაატრიალეთ პრეფიქსის ქვემოთ არსებული ბიტი და გამოიტანეთ შედეგი. აბსტრაქტულად ჟღერს, სანამ არ გაუშვებთ.

10.0.0.0/8-ის გამორიცხვა რვა მარშრუტს იძლევა: 128.0.0.0/1, 64.0.0.0/2, 32.0.0.0/3, 16.0.0.0/4, 0.0.0.0/5, 12.0.0.0/6, 8.0.0.0/7, 11.0.0.0/8. არცერთი მათგანი არ შეიცავს 10.0.0.0/8-ს და ერთად ისინი ყველა დანარჩენს ფარავენ.

რვა სტრიქონი ქმნის ამ სიას ნებისმიერი პრეფიქსისთვის:

import ipaddress
def complement(addr, length):
    base = int(ipaddress.IPv4Address(addr))
    out = []
    for k in range(1, length + 1):
        block = base & (0xFFFFFFFF << (32 - k))
        sibling = block ^ (1 << (32 - k))
        out.append(f"{ipaddress.IPv4Address(sibling)}/{k}")
    return out
print(complement("10.0.0.0", 8))

მარშრუტების რაოდენობა პრეფიქსის სიგრძის ტოლია და სწორედ ეს რიცხვია საყურადღებო. ერთი /21 გამორიცხვა 21 მარშრუტი ღირს და კითხვადი რჩება. ათი გამორიცხვა შეიძლება რამდენიმე ასეული ღირდეს, რაც ჩვეულებრივ ნიშნავს, რომ ქსელის დიზაინი უნდა შეიცვალოს და არა გენერატორი.

შეამოწმეთ მისამართების ოჯახი და გვირაბის ფარგლები

IPv4 მარშრუტები IPv6-ისთვის არაფერს აკეთებენ. თუ გვირაბი IPv6 მისამართს ამტკიცებს, IPv6 LAN პრეფიქსს საკუთარი დანამატი სჭირდება, რომელიც იმავე არითმეტიკით 128 ბიტზეა გენერირებული. გავრცელებული მიზეზი, რის გამოც გამორიცხვა ქაღალდზე მუშაობს, ხოლო ლოკალური მოწყობილობა მიუწვდომელი რჩება, ესაა: ჰოსტს IPv6-ით მივაღწიეთ და მხოლოდ IPv4 იყო ამოჭრილი. შეამოწმეთ ორივე ოჯახი, სანამ დაასკვნით, რომ მათემატიკა არასწორია.

ფარგლები შემოწმების მეორე ნახევარია. Android მარშრუტიზაციას ქსელისა და აპლიკაციის მიხედვით თვალს ადევნებს, ამიტომ ერთი და იგივე პრეფიქსი ერთი აპლიკაციისთვის გვირაბით შეიძლება წავიდეს, ხოლო მეორისთვის პირდაპირ, იმის მიხედვით, თუ როგორ გამოცხადდა VPN.

განათავსეთ DNS და თითოეული აპლიკაციის წესები იქ, სადაც ეკუთვნის

მარშრუტების გამორიცხვა რეზოლვერს არ ცვლის. ლოკალურ ჰოსტის სახელს კვლავ შეიძლება გვირაბის DNS სერვერი პასუხობდეს, სანამ მისი მისამართი ლოკალურად მარშრუტიზდება, და შეუსაბამობა ჩანს როგორც სახელი, რომელიც იშლება იმაში, რასაც LAN ვერ მიაღწევს. დააყენეთ რეზოლვერის პოლიტიკა განზრახ, საძიებო დომენით ან გაყოფილი DNS წესით. ნუ ჩათვლით, რომ მარშრუტებმა ეს თქვენთვის გადაწყვიტეს.

addDisallowedApplication() ერთ აპლიკაციას გვირაბის გარეთ ტოვებს; ის დანიშნულებას გარეთ არ ტოვებს შიგნით დარჩენილი აპლიკაციებისთვის. protect() საშუალებას აძლევს თქვენ მიერ გახსნილ სოკეტს გვირაბი აუაროს, და ასე გვირაბი თავისივე ტრანსპორტის დაჭერას თავს არიდებს. არცერთი მათგანი არ ცვლის დანამატს.

გადაამოწმეთ ძიებით და არა საკუთარი სიით

გვირაბის აწევის შემდეგ ჰკითხეთ მოწყობილობას, სად მიდის მისამართი სინამდვილეში, ნაცვლად იმისა, რომ თქვენ მიერ აწყობილი სია ხელახლა წაიკითხოთ. iproute2-ის მქონე ბილდზე adb shell ip route get 10.1.2.3 ასახელებს ინტერფეისს, რომელიც ბირთვმა აირჩია. გაუშვით იგივე ბრძანება საჯარო მისამართისთვის და დაადასტურეთ, რომ ის გვირაბს ასახელებს.

კიდევ ორი შემოწმება ხურავს ციკლს. ჯერ ლოკალური სახელი ამოხსენით, შემდეგ გაუშვით adb shell ip route get ამოხსნილ მისამართზე და მოითხოვეთ ინტერფეისი, რომელიც გვირაბი არ არის, რადგან წარმატებული adb shell ping -c 1 host.lan მიღწევადობას ამტკიცებს და არა გზას. გაიმეორეთ მისამართების ოჯახის ტესტი IPv6-ზე, თუ გვირაბი მას ითხოვს. ტესტეთ იმ API დონეზე, რომელსაც აწვდით, რადგან ის, თუ როგორ ვლინდება VPN ქსელები მარშრუტიზაციის ცხრილისთვის, გამოშვებებს შორის შეიცვალა. ქაღალდზე სწორად შემჩნეული მარშრუტების სია და არასწორი ინტერფეისის ამომრჩევი ძიება სხვადასხვა ხარვეზია და მხოლოდ მეორე აღწევს მომხმარებლებამდე.

თუ გამორიცხვების სია კვლავ იზრდება, შეწყვიტეთ მარშრუტების გენერაცია და ხელახლა დანომრეთ დისტანციური ქსელი ან სამაგიეროდ შეავიწროვეთ გვირაბი. გენერატორი დაკარგული API-ის გვერდის ავლაა და არა დიზაინის ხელსაწყო.

დაკავშირებული სტატიები