Aynı VpnService döngüsünde iki belirti ortaya çıkar ve ikisi de aynı şeyi suçlar: asla dönmeyen bir okuyucu ve ENOBUFS ile başarısız olan bir yazma. Bunlar tek bir hatanın varyantları değildir. Takılan okuma, eş ile akış denetimi konusunda anlaşmazlıktır; başarısız yazma, arayüzün kabul etmeyeceği bir pakettir. Bunları tek bir sorun olarak ele alırsanız, hiç ilgisi olmayan tamponları ayarlamakla bir gün geçirirsiniz. Aşağıdaki ayrım, döngünün hangi yarısını önce ölçeceğime nasıl karar verdiğimdir.

Tek döngü, iki bağımsız hata noktası

Hizmetiniz TUN dosya tanımlayıcısından bir IP paketi okur, inceler ve aynı tanımlayıcıya bir yanıt yazar. Bu yolda kırılan her şey bu iki çağrıdan birinde ortaya çıkar, bu yüzden ikisi de bozuk tünel olarak bildirilir. Çağrıların sözleşmeleri farklıdır. Okuma, çekirdeğin size bir paket vermesini bekler; yazma ise çekirdeğe reddetme hakkı olan bir paket verir.

Okumalar, düşünmesi kolay yarıdır. Hiçbir şey döndürmeyen bir okuma ya hiç gelmeyen trafiği bekler ya da eş göndermeyi bıraktığı için bekler.

Takılan okuma bir akış denetimi anlaşmazlığıdır

Bir TUN aygıtını okumak, paket hazır olana kadar bloke eder. Bu tasarlanmış davranıştır ve bu yüzden read içinde bekleyen bir iş parçacığı boş bir bağlantıda normal görünür. Kendi TCP yığınınız yerel uygulamadan veri almayı sürdürürken uzak eşe daha fazla göndermesi için yeterli pencereyi hiç bildirmediğinde bu bir kilitlenmeye dönüşür. Eş, bildirdiğiniz pencereyi doldurur, bir ACK ve pencere güncellemesi bekler ve durur. Sonra okuyucunuzun geri verecek bir şeyi kalmaz ve uygulama açık ama sessiz bir bağlantı görür.

Yaygın nedenler, bildirilen pencereyi izlemeden sokete yazan bir yığın ve tamponunu boşalttıktan sonra pencere güncellemesi göndermeyi unutan bir yığındır. Dışarıdan aynı görünürler: baytlar çıkar, hiçbir şey dönmez, hiçbir hata yükselmez. Okuma iş parçacığını ayrıştırma, DNS çözümleme veya disk G/Ç üzerinde bloke etmek, ilgisiz bir nedenle aynı biçimde belirti üretir; bu yüzden akış denetimini suçlamadan önce iş parçacığının gerçekten read içinde olduğunu doğrulayın.

Eşin gönderim kuyruğunu ve en son gördüğü pencereyi denetleyin. Eş, pencereniz sıfır olduğu için veri tutuyorsa düzeltme ACK yolunuzdadır, TUN okumasında değil.

ENOBUFS bir boyut sorunudur, tampon kıtlığı değil

TUN arayüzünde ENOBUFS döndüren bir yazma genellikle paket uzunluğunu reddeder. Arayüzün bir MTU'su vardır, ondan büyük bir paket kuyruğa alınamaz ve aynı paketi yeniden denemek her seferinde aynı şekilde başarısız olur. Bu belirlilik ipucudur. Gerçek bir tampon kıtlığı basınç azaldıkça geçer; boyut reddi ise ne kadar beklediğinizi umursamaz.

TCP'nin tam bunun için bir mekanizması vardır ve bu, insanların atladığı adımdır: MSS kısıtlaması. Kullanıcı alanında segmentler üretirken yük boyutunu siz seçersiniz ve bunu tünelin MTU'su yerine yerel arayüzün MTU'sundan almak, tünelin taşıyamayacağı segmentler üretir. İkisinden küçüğüne kısıtlayın. Kısıtlama yalnızca TCP'yi kapsar, bu yüzden çok büyük bir UDP datagramı kodunuz tarafından parçalanmalı veya atılmalıdır.

Hata ayıklarken yazdığınız her paketin uzunluğunu günlüğe kaydedin. Başarısız olan uzunluk yanıttır ve denemeler arasında değişmez.

Durumları ayıran üç ölçüm

İki hatanın hiçbiri için hata ayıklayıcıya ihtiyacınız yok. Üç gözlem sizi doğru bölüme yönlendirmeye yeter ve yeniden derlemeden daha kısa sürer.

What you seeWhere to look
Reader parked, peer send queue empty, last advertised window zeroWindow updates in your ACK path
Write fails on one length and succeeds on a smaller oneMSS clamp and tunnel MTU
Both directions look fine, app still sees no dataProtected socket and routing loop

Üçüncü satır uyuyorsa döngünün kendisi iyidir ve sorun tünelin göremediği yerdedir: kendi VPN trafiğinizi tünele geri taşıyan korumasız bir taşıma soketi; bu genellikle başarılı olup sonra hiçbir şey teslim etmeyen bir bağlantı olarak görünür.

Her iki yolun bağlı olduğu tesisatı onarın

Arayüz doğru kurulmadan hiçbir belirtiyi kovalamaya değmez. Önemli builder çağrıları adres, rota, MTU ve izindir; bu sırayla:

Builder()
  .addAddress(10.8.0.2, 32)
  .addRoute(0.0.0.0, 0)
  .setMtu(1400)
  .establish()

MTU'yu örneğin kullandığı değerde bırakmak yerine tünelin gerçek değerine ayarlayın. Sonradan yazdığınız her şey o sayıyla sınırlıdır ve ayarladığınız MTU ile kısıtladığınız MSS arasındaki uyumsuzluk, bir düzeltmeden sonra yazma hatasını geri getirmenin en yaygın yoludur. Ardından VPN sunucusunun kendisine trafik taşıyan soketi koruyun. protect olmadan bu paketler kendi tünelinize geri yönlendirilir ve bağlantı hiç tamamlanmaz; bu takılan okuma gibi görünür ama bir yönlendirme döngüsüdür.

İzin atlanamaz. prepare null döndürmeden başlatılan bir hizmet arayüz alamaz ve establish fırlatmak yerine null döndürür, bu yüzden döngüyü başlatmadan önce tanımlayıcıyı denetleyin. VpnService başvurusu, örnek kopyalamak yerine builder sözleşmesini doğrulayacağınız yerdir.

Her seferinde bir yönü doğrulayın

TCP'ye dokunmadan önce tünelden bir UDP yankısı gönderin ve paket uzunluklarını izleyin. Ardından bir el sıkışma açın ve segmentlerin hesapladığınız MSS'nin altında kaldığını doğrulayın. Bağlantıları değil paketleri sayın, böylece takılma asılı bir soket yerine sayaçta bir boşluk olarak görünür. Yazma patlamasından sonra okumalar durursa şüpheli penceredir; yazmalar hemen başarısız olursa uzunluktur.

Sonra aygıtı yeniden başlatın ve bir kez yineleyin, çünkü yalnızca ilk ağ değişikliğine kadar çalışan bir VpnService akış denetimi değil, yönlendirme sorunudur.

İlgili makaleler