Twoja aplikacja mówi rozłączone, pasek menu mówi połączone, a ruch wyraźnie płynie. Ta luka nie jest warunkiem wyścigu, który naprawisz ponowną próbą: aplikacja i tunel to osobne procesy, a obiekt, który czyta twój kod, jest buforowanym widokiem konfiguracji przechowywanej przez system. Powierzchnia Personal VPN od Apple to API sterowania, a nie ścieżka danych, i większość jej dziwnego zachowania wynika z tego jednego faktu. Oto co naprawdę dzieje się przy każdym wywołaniu.

Obiekt menedżera to buforowany widok, a nie tunel

NETunnelProviderManager jest zapełniany z magazynu preferencji należącego do systemu, więc jego właściwości nic nie znaczą, dopóki loadFromPreferences się nie zakończy. Odczytaj connection.status wcześniej, a dostaniesz wartość domyślną, i dlatego pierwsze odświeżenie statusu po restarcie zgłasza rozłączone, gdy tunel działa. Najpierw wczytaj, potem odczytaj status i konfigurację protokołu z tego samego handlera zakończenia. Późniejsze zmiany obserwuj przez NEVPNStatusDidChange zamiast odpytywać, i zauważ, że powiadomienie wywołuje się także dla połączeń uruchomionych przez On Demand, gdzie nie ma żadnego wywołania aplikacji, które mogłoby coś zwrócić. To samo dotyczy ustawień protokołu: są czytane z wczytanej konfiguracji, a nie z tego, co przekazała aplikacja.

Sam tunel żyje w procesie dostawcy Network Extension, więc aplikacja może stracić własny stan bez utraty połączenia. Wszystko, co pokazuje interfejs, musi być odtworzone z menedżera, a nie przypominane z poprzedniej sesji.

Kolejność uruchamiania: zapisz, wczytaj, potem uruchom

Połączenie oparte na dostawcy wymaga trzech kroków w kolejności. Zapisz konfigurację dostawcy i wywołaj saveToPreferences, wczytaj menedżera, aby zapisana konfiguracja była tą, którą trzymasz, a potem uruchom przez NETunnelProviderSession.startVPNTunnel(). Jeśli pominiesz zapis, uruchomienie nie ma czego uruchomić; jeśli wczytasz przed zapisem, menedżer nadal odzwierciedla starą konfigurację.

Wartość logiczna zwracana przez startVPNTunnel mówi tylko, że żądanie zostało przyjęte. Błędy na poziomie dostawcy przychodzą później jako zmiana statusu na rozłączone, którą obserwujesz przez powiadomienie NEVPNStatusDidChange, a handler powiadomienia musi ponownie odczytać connection.status po loadFromPreferences, ponieważ buforowana wartość może wciąż być stara. Błędy na poziomie żądania wracają natychmiast z rzucającej wyjątki formy wywołania albo przez parametr wyjściowy błędu startVPNTunnelAndReturnError:. Kod, który traktuje wartość zwracaną jako wynik, zgłasza połączenie, które nigdy nie zostało nawiązane. Inna cicha awaria to identyfikator pakietu dostawcy, który nie pasuje do zainstalowanego rozszerzenia: żądanie jest odrzucane, i nic w komunikacie nie wskazuje identyfikatora. Jeśli identyfikator jest błędny, żądanie zwykle zawodzi od razu, zanim pojawi się jakikolwiek wiersz logu dostawcy.

Ponowne wczytanie preferencji po uruchomieniu jest tym, co czyni ekrany statusu poprawnymi. Menedżer nie wypycha ci nowej konfiguracji, gdy system ją zmienia.

Materiał zaufania przychodzi przez profil, nigdy z kodu aplikacji

Aplikacja nie ma wspieranej drogi dodania certyfikatu głównego do magazynu zaufania iOS, więc konfiguracja Personal VPN może odwoływać się tylko do materiału, któremu urządzenie już ufa: łańcucha ustanowionego przez zainstalowany profil, zarządzanie urządzeniem lub CA, której system ufa z innych powodów. Aplikacja, która przy pierwszym uruchomieniu tworzy własne CA i oczekuje, że system je uzna, zawiedzie walidację wewnątrz dostawcy, gdzie interfejs aplikacji nie widzi przyczyny.

Traktuj tę granicę jako ograniczenie projektowe, którym jest, i dostarcz kotwicę zaufania wraz z profilem rejestracji. Trzymaj uwierzytelnianie VPN osobno od decyzji o zaufaniu, ponieważ zawodzą w różnych miejscach i debugują je różni ludzie.

Liczniki pochodzą z interfejsu, a nie z API VPN

Menedżer udostępnia status i konfigurację, i nic, co liczyłoby bajty na tunel. Jeśli potrzebujesz liczb użycia, wywołaj getifaddrs() i odczytaj if_data dla odpowiedniego interfejsu: en0 dla Wi-Fi, interfejsy pdp_ip dla sieci komórkowej i urządzenie utun dla samego tunelu. Liczniki utun opisują ruch wewnętrzny i obejmują narzut protokołu.

Te liczby to szacunki, a nie rozliczanie na aplikację, i zerują się, gdy interfejs jest tworzony od nowa. Oznacz je tak w interfejsie, bo inaczej ktoś użyje ich do rozliczenia klienta. Jeśli potrzebujesz liczb na aplikację, potrzebujesz zupełnie innego mechanizmu, nie lepszego odczytu tych pól.

Sprzątanie należy do stopVPNTunnel, a nie do zakończenia

applicationWillTerminate daje ci kilka sekund, zanim system może zabić proces, i to nie jest wiarygodne miejsce do zrywania połączenia. Wyślij zatrzymanie do stopVPNTunnel() albo obsłuż je w wywołaniu zwrotnym zatrzymania dostawcy, i zachowaj identyfikator menedżera lub konfiguracji, z którym zacząłeś, aby zatrzymanie trafiło w to połączenie, a nie w każdą konfigurację na urządzeniu.

Zapisz ten identyfikator obok profilu, do którego należy, ponieważ obiekt menedżera nie rejestruje, którą konfigurację wybrał użytkownik. I pamiętaj, że połączenie podniesione przez On Demand nie ma za sobą żadnego wywołania aplikacji, więc ekran statusu nadal musi wczytać preferencje, zanim cokolwiek narysuje.

Powiązane artykuły