Праграма кажа, што злучэнне скінута, а тунель працуе
Праграма і тунель — асобныя працэсы, а аб'ект менеджара — гэта кэшаванае прадстаўленне. Спачатку загрузіце налады, потым чытайце статус і не лічыце значэнне, якое вярнуў выклік запуску, вынікам.
Ваша праграма кажа, што злучэнне скінута, радок меню кажа, што яно ёсць, а трафік відавочна ідзе. Гэтая розніца не з'яўляецца гонкай, якую можна выправіць паўторам: праграма і тунель — асобныя працэсы, а аб'ект, які чытае ваш код, — гэта кэшаванае прадстаўленне канфігурацыі, якую захоўвае сістэма. Паверхня Personal VPN ад Apple — гэта праграмны інтэрфейс кіравання, а не шлях даных, і большасць яе дзіўных паводзін вынікае з гэтага аднаго факту. Вось што насамрэч адбываецца пад кожным выклікам.
Аб'ект менеджара — гэта кэшаванае прадстаўленне, а не тунель
NETunnelProviderManager запаўняецца са сховішча налад, якім валодае сістэма, таму яго ўласцівасці нічога не значаць, пакуль не завяршыцца loadFromPreferences. Чытайце connection.status раней — і атрымаеце значэнне па змаўчанні; менавіта таму першае абнаўленне статусу пасля перазапуску паведамляе пра скінутае злучэнне, пакуль тунель працуе. Спачатку загрузіце, потым чытайце статус і канфігурацыю пратакола з таго ж апрацоўшчыка завяршэння. Назірайце за наступнымі зменамі праз NEVPNStatusDidChange замест апытання і заўважце, што апавяшчэнне таксама спрацоўвае для злучэнняў, якія пачаў On Demand, дзе няма выкліку праграмы, які мог бы нешта вярнуць. Тое ж датычыцца налад пратакола: яны чытаюцца з загружанай канфігурацыі, а не з таго, што перадала праграма.
Сам тунель жыве ў працэсе пастаўшчыка Network Extension, таму праграма можа страціць свой стан, не страціўшы злучэнне. Усё, што паказвае інтэрфейс, павінна будавацца наноў з менеджара, а не ўзгадвацца з папярэдняй сесіі.
Парадак запуску: захаваць, загрузіць, потым запусціць
Злучэнне на базе пастаўшчыка патрабуе трох крокаў па парадку. Запішыце канфігурацыю пастаўшчыка і выклічце saveToPreferences, загрузіце менеджар, каб захаваная канфігурацыя была той, якую вы трымаеце, потым запусціце праз NETunnelProviderSession.startVPNTunnel(). Прапусціце захаванне — і запуску няма чаго запускаць; загрузіце да захавання — і менеджар усё яшчэ адлюстроўвае старую канфігурацыю.
startVPNTunnel вяртае Boolean, які гаворыць толькі пра тое, што запыт прыняты. Памылкі ўзроўню пастаўшчыка прыходзяць пазней як змена статусу на «скінута», якую вы назіраеце праз апавяшчэнне NEVPNStatusDidChange, і апрацоўшчык апавяшчэння павінен перачытаць connection.status пасля loadFromPreferences, бо кэшаванае значэнне можа быць яшчэ старым. Памылкі ўзроўню запыту вяртаюцца адразу з формы выкліку, якая кідае выключэнне, або праз выхадны параметр памылкі ў startVPNTunnelAndReturnError:. Код, які лічыць вяртаемае значэнне вынікам, паведамляе пра злучэнне, якое так і не ўсталявалася. Іншая ціхая памылка — ідэнтыфікатар пакета пастаўшчыка, які не адпавядае ўсталяванаму пашырэнню: запыт адхіляецца, і нішто ў паведамленні не ўказвае на ідэнтыфікатар. Калі ідэнтыфікатар няправільны, запыт звычайна правальваецца адразу, перш чым з'явіцца хоць адзін радок журнала пастаўшчыка.
Перазагрузка налад пасля запуску — гэта тое, што робіць экраны статусу правільнымі. Менеджар не прадстаўляе вам новую канфігурацыю, калі сістэма яе змяняе.
Матэрыял даверу прыходзіць праз профіль, ніколі з кода праграмы
Праграма не мае падтрыманага спосабу дадаць каранёвы сертыфікат у сховішча даверу iOS, таму канфігурацыя Personal VPN можа спасылацца толькі на матэрыял, якому прылада ўжо давярае: ланцуг, створаны ўсталяваным профілем, кіраваннем прыладай або цэнтрам сертыфікацыі, якому сістэма давярае з іншых прычын. Праграма, якая стварае ўласны цэнтр сертыфікацыі пры першым запуску і чакае, што сістэма яго прызнае, праваліць праверку ўнутры пастаўшчыка, дзе інтэрфейс праграмы не бачыць прычыны.
Разглядайце гэтую мяжу як сапраўднае абмежаванне дызайну і пастаўляйце якар даверу разам з профілем рэгістрацыі. Трымайце аўтэнтыфікацыю VPN асобна ад рашэння пра давер, бо яны правальваюцца ў розных месцах і іх наладжваюць розныя людзі.
Лічыльнікі прыходзяць з інтэрфейса, а не з праграмнага інтэрфейсу VPN
Менеджар прадстаўляе статус і канфігурацыю, і нічога, што лічыць байты на тунель. Калі вам патрэбныя лічбы выкарыстання, выклічце getifaddrs() і прачытайце if_data для адпаведнага інтэрфейса: en0 для Wi-Fi, інтэрфейсы pdp_ip для сотавай сувязі і прыладу utun для самога тунеля. Лічыльнікі utun апісваюць унутраны трафік і ўключаюць накладныя выдаткі пратакола.
Гэтыя лічбы — ацэнкі, а не паасобны ўлік па праграмах, і яны скідваюцца, калі інтэрфейс ствараецца нанова. Пазначайце іх так у інтэрфейсе, інакш хтосьці выкарыстае іх для выстаўлення рахунку кліенту. Калі вам патрэбныя лічбы па кожнай праграме, патрэбны зусім іншы механізм, а не лепшае чытанне гэтых палёў.
Прыбіранне належыць stopVPNTunnel, а не завяршэнню
applicationWillTerminate дае вам некалькі секунд, перш чым сістэма можа забіць працэс, і гэта ненадзейнае месца, каб разбураць злучэнне. Адпраўце запыт спынення ў stopVPNTunnel() або апрацуйце яго ў зваротным выкліку спынення пастаўшчыка, і захавайце менеджар або ідэнтыфікатар канфігурацыі, з якім вы пачалі, каб спыненне нацэльвалася на тое злучэнне, а не на кожную канфігурацыю на прыладзе.
Захоўвайце гэты ідэнтыфікатар побач з профілем, да якога ён належыць, бо аб'ект менеджара не запісвае, якую канфігурацыю выбраў карыстальнік. І памятайце, што за злучэннем, якое падняў On Demand, наогул няма выкліку праграмы, таму экран статусу ўсё роўна павінен загрузіць налады, перш чым нешта маляваць.