Android VPN tünelinden bir alt ağ nasıl hariç tutulur
VpnService bir rotayı hariç tutamaz, bu yüzden tümleyeni eklersiniz: /8 için üretilmiş sekiz önek, artı geri kalanı yakalayan IPv6, DNS ve arama kontrolleri.
Android'in VpnService'inde hariç tutma rotası yoktur ve ne kadar API okunursa okunsun bir tane üretmez. Builder.addRoute() tünele her zaman yalnızca hedefler ekler, bu yüzden bir LAN segmentini yerel tutmak o segmentin rota listesine hiç girmediği anlamına gelir. Bunun yerine eklediğiniz şey segmentin tümleyenidir, sıradan öneklerle ifade edilir. İş aritmetiktir ve birkaç öngörülebilir yerde başarısız olur.
Aşağıdaki kontrolleri sırayla yapın. Her biri öncekinin tuttuğunu varsayar, bu yüzden semptomları aşağıda yamamak yerine ilk başarısızlıkta durun.
Tünelin iddia ettiği şeyden başlayın
0.0.0.0/0 öğesini addRoute() öğesine vermek Android'e her IPv4 hedefinin tünele ait olduğunu söyler. Bu yüzden pek çok istemci bölünmüş tünelleme yapamaz görünür: tüm adres alanını talep eder, sonra ondan çıkarmaya çalışırlar. Oluşturucuda hiçbir şey çıkarmaz. Yerel tutmak istediğiniz ağ için daha özel bir rota beklenenin tersini yapar, çünkü en uzun eşleşen önek kazanır ve o ağ tünelin içinde kalır.
Rota listesi politikanın tamamıdır. Ardında hedef başına bir veto yoktur ve Builder başvurusu bunu sağlayacak hiçbir yöntem listelemez.
Tümleyeni önek aritmetiğinden üretin
Hariç tutulan bir öneğin tümleyeni, adres ağacının köküne giden yolda kardeşlerinin kümesidir. 1'den hariç tutulan uzunluğa kadar her uzunluk için, öneğinizi içeren o uzunluktaki bloğu alın, öneğin hemen altındaki biti çevirin ve sonucu yazdırın. Çalıştırana kadar soyut gelir.
10.0.0.0/8 öğesini hariç tutmak sekiz rota verir: 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. Hiçbiri 10.0.0.0/8 içermez ve birlikte geri kalan her şeyi kapsarlar.
Sekiz satır herhangi bir önek için o listeyi üretir:
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))
Rota sayısı önek uzunluğuna eşittir ve izlenmesi gereken sayı budur. Tek bir /21 hariç tutma 21 rotaya mal olur ve okunabilir kalır. On hariç tutma birkaç yüz tutabilir; bu genellikle oluşturucunun değil ağ tasarımının değişmesi gerektiği anlamına gelir.
Adres ailesini ve tünelin kapsamını kontrol edin
IPv4 rotaları IPv6 için hiçbir şey yapmaz. Tünel bir IPv6 adresi talep ediyorsa, bir IPv6 LAN öneği kendi tümleyenine ihtiyaç duyar; 128 bit üzerinde aynı aritmetikle üretilir. Hariç tutmanın kağıt üzerinde çalışıp yerel cihazın erişilemez kalmasının yaygın bir nedeni şudur: ana bilgisayara IPv6 üzerinden ulaşıldı ve yalnızca IPv4 kesilmişti. Matematiğin yanlış olduğu sonucuna varmadan önce iki aileyi de kontrol edin.
Kapsam kontrolün diğer yarısıdır. Android yönlendirmeyi ağ başına ve uygulama başına izler, bu yüzden aynı önek bir uygulama için tünelden geçerken diğeri için doğrudan olabilir; bu VPN'in nasıl bildirildiğine bağlıdır.
DNS ve uygulama başına kuralları ait oldukları yere koyun
Rota hariç tutmaları çözümleyiciyi değiştirmez. Yerel bir ana bilgisayar adı hâlâ tünelin DNS sunucusu tarafından yanıtlanabilirken adresi yerel olarak yönlendirilir ve uyuşmazlık, LAN'ın ulaşamadığı bir şeye çözülen ad olarak görünür. Çözümleyici politikasını bilinçli olarak ayarlayın; bir arama etki alanı veya bölünmüş DNS kuralıyla. Rotaların bunu sizin yerinize kararlaştırdığını varsaymayın.
addDisallowedApplication() bir uygulamayı tünelin dışında tutar; içeride kalan uygulamalar için bir hedefi dışarıda tutmaz. protect() açtığınız soketin tüneli atlamasına izin verir ve böylece tünel kendi taşımasını yakalamaktan kaçınır. İkisi de tümleyenin yerini tutmaz.
Kendi listenizle değil, bir aramayla doğrulayın
Tünel ayaktayken, oluşturduğunuz listeyi yeniden okumak yerine cihaza bir adresin gerçekte nereye gittiğini sorun. iproute2 içeren bir derlemede adb shell ip route get 10.1.2.3 çekirdeğin seçtiği arayüzü adlandırır. Aynı komutu genel bir adres için çalıştırın ve tüneli adlandırdığını doğrulayın.
İki kontrol daha döngüyü kapatır. Önce yerel bir adı çözün, sonra çözülen adres üzerinde adb shell ip route get çalıştırın ve tünel olmayan bir arayüz isteyin, çünkü başarılı bir adb shell ping -c 1 host.lan erişilebilirliği kanıtlar, yolu değil. Tünel bir tane talep ediyorsa adres ailesi testini IPv6 üzerinde yineleyin. Gönderdiğiniz API düzeyinde test edin, çünkü VPN ağlarının yönlendirme tablosuna nasıl sunulduğu sürümler arasında değişti. Kağıt üzerinde doğru görünen bir rota listesi ile yanlış arayüzü seçen bir arama farklı hatalardır ve kullanıcılara yalnızca ikincisi ulaşır.
Hariç tutma listesi büyümeye devam ederse, rota üretmeyi bırakın ve uzak ağı yeniden numaralandırın ya da bunun yerine tüneli daraltın. Oluşturucu eksik API için bir geçici çözümdür, tasarım aracı değildir.