NetworkExtension для Swift: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

NetworkExtension для Swift: Полное руководство по управлению VPN и сетевыми расширениями

1. Что такое NetworkExtension и зачем она нужна?

NetworkExtension — это мощный фреймворк от Apple, предоставляющий разработчикам доступ к низкоуровневым сетевым функциям на устройствах под управлением iOS, macOS, tvOS и watchOS. В отличие от обычных приложений, которые работают в изолированной "песочнице" (sandbox), NetworkExtension позволяет создавать системные расширения, которые могут перехватывать, фильтровать, перенаправлять и шифровать сетевой трафик на уровне операционной системы. Это не библиотека в привычном понимании (как Alamofire или Moya), а скорее набор API для создания привилегированных сетевых процессов.

Основное применение NetworkExtension — это создание VPN-клиентов (включая протоколы IKEv2, IPSec, WireGuard и кастомные), контент-фильтров (родительский контроль, блокировка рекламы на уровне DNS), прокси-серверов и инструментов для мониторинга трафика. Без этого фреймворка невозможно реализовать полноценный VPN на устройствах Apple без джейлбрейка. Он также используется корпоративными приложениями для безопасного доступа к внутренним ресурсам.

Для образовательного сайта PythonLib важно понимать, что NetworkExtension — это нативный Swift/Objective-C фреймворк, который не устанавливается через менеджеры пакетов (pip, npm). Он является частью Xcode SDK и требует настройки специальных прав (entitlements) и подписывания кода с соответствующими возможностями. В этой статье мы рассмотрим ключевые компоненты: NETunnelProviderManager, NEPacketTunnelProvider, NEVPNManager, NEFilterProvider и другие.

2. Установка и настройка

Поскольку NetworkExtension является частью системного SDK, его "установка" сводится к правильной конфигурации проекта Xcode. Никаких команд pip install или npm install не требуется. Вот пошаговая инструкция:

  1. Создайте новый проект в Xcode (iOS или macOS).
  2. Добавьте новый Target: File → New → Target. Выберите "Network Extension" из списка. Вам будет предложено выбрать тип расширения: Packet Tunnel Provider (для кастомных VPN), Filter Data Provider (для фильтрации), DNS Proxy и т.д.
  3. Настройка Entitlements: В главном проекте (не в расширении) откройте файл .entitlements и добавьте ключ com.apple.developer.networking.vpn.api со значением allow. Для некоторых функций (например, фильтрация) может потребоваться com.apple.developer.networking.networkextension.
  4. App ID и Provisioning Profile: В Apple Developer Console создайте App ID с включенной возможностью "Network Extensions". Скачайте и установите новый Provisioning Profile, который включает эту возможность.
  5. Подписание кода: Убедитесь, что и главное приложение, и расширение подписаны одним и тем же сертификатом и профилем.

После этого вы можете импортировать модуль в коде расширения:

import NetworkExtension

Для главного приложения (которое управляет расширением) также нужно импортировать этот модуль.

3. Быстрый старт: Минимальный рабочий пример (VPN)

Создадим простейший VPN-клиент, который подключается к серверу. Этот код выполняется в Packet Tunnel Provider (расширение).

import NetworkExtension

class PacketTunnelProvider: NEPacketTunnelProvider {

    override func startTunnel(options: [String : NSObject]? = nil) async throws {
        // Настройка параметров туннеля
        let tunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "192.168.1.1")
        tunnelNetworkSettings.mtu = 1400
        
        // Настройка DNS
        let dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "1.1.1.1"])
        tunnelNetworkSettings.dnsSettings = dnsSettings
        
        // Настройка маршрутизации (весь трафик через VPN)
        let ipv4Settings = NEIPv4Settings(addresses: ["10.0.0.2"], subnetMasks: ["255.255.255.0"])
        ipv4Settings.includedRoutes = [NEIPv4Route.default()]
        tunnelNetworkSettings.ipv4Settings = ipv4Settings
        
        // Применяем настройки
        try await setTunnelNetworkSettings(tunnelNetworkSettings)
        
        // Начинаем чтение пакетов из виртуального интерфейса
        // (в реальном приложении здесь будет цикл чтения/записи)
        print("Туннель запущен!")
    }
    
    override func stopTunnel(with reason: NEProviderStopReason) async {
        print("Туннель остановлен. Причина: \(reason)")
    }
}

Для запуска этого расширения из главного приложения используется NEVPNManager:

import NetworkExtension

class ViewController: UIViewController {
    let vpnManager = NEVPNManager.shared()
    
    func connectVPN() {
        vpnManager.loadFromPreferences { error in
            guard error == nil else { return }
            
            let protocolConfig = NEVPNProtocolIKEv2()
            protocolConfig.serverAddress = "vpn.example.com"
            protocolConfig.authenticationMethod = .none
            
            self.vpnManager.protocolConfiguration = protocolConfig
            self.vpnManager.isEnabled = true
            
            self.vpnManager.saveToPreferences { error in
                guard error == nil else { return }
                try? self.vpnManager.connection.startVPNTunnel()
            }
        }
    }
}

4. Основные классы и методы

4.1. NEVPNManager

Сигнатура: class NEVPNManager : NSObject

Что делает: Центральный класс для управления VPN-конфигурациями на устройстве. Позволяет загружать, сохранять и активировать VPN-профили.

let manager = NEVPNManager.shared()
manager.loadFromPreferences { error in
    if let error = error {
        print("Ошибка загрузки: \(error)")
        return
    }
    // Конфигурация загружена
}

4.2. NEVPNConnection

Сигнатура: class NEVPNConnection : NSObject

Что делает: Представляет активное VPN-соединение. Позволяет отслеживать статус, запускать и останавливать туннель.

let connection = manager.connection
connection.startVPNTunnel() // Запуск
connection.stopVPNTunnel()  // Остановка

// Наблюдение за статусом
NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: connection, queue: .main) { notification in
    switch connection.status {
    case .connected:
        print("Подключено")
    case .disconnected:
        print("Отключено")
    default:
        break
    }
}

4.3. NEPacketTunnelProvider

Сигнатура: class NEPacketTunnelProvider : NEProvider

Что делает: Базовый класс для создания кастомных VPN-протоколов. Вы получаете виртуальный сетевой интерфейс и можете читать/писать IP-пакеты.

class MyProvider: NEPacketTunnelProvider {
    override func startTunnel(options: [String : NSObject]? = nil) async throws {
        // Настройка и запуск
    }
    
    override func stopTunnel(with reason: NEProviderStopReason) async {
        // Остановка
    }
}

4.4. NEPacketTunnelNetworkSettings

Сигнатура: class NEPacketTunnelNetworkSettings : NSObject

Что делает: Конфигурация сетевых параметров туннеля: MTU, DNS, IPv4/IPv6 адреса, маршруты.

let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "10.0.0.1")
settings.mtu = 1500
settings.ipv4Settings = NEIPv4Settings(addresses: ["10.0.0.2"], subnetMasks: ["255.255.255.0"])

4.5. NEIPv4Route

Сигнатура: class NEIPv4Route : NSObject

Что делает: Определяет маршрут для IPv4-трафика. Можно указать, какие подсети должны идти через VPN.

let defaultRoute = NEIPv4Route.default() // Весь трафик
let specificRoute = NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0")

4.6. NEDNSSettings

Сигнатура: class NEDNSSettings : NSObject

Что делает: Настройка DNS-серверов для VPN-соединения.

let dns = NEDNSSettings(servers: ["8.8.8.8", "1.1.1.1"])
dns.domainName = "internal.company.com" // Поиск в этом домене

4.7. NEFilterDataProvider

Сигнатура: class NEFilterDataProvider : NEFilterProvider

Что делает: Позволяет фильтровать сетевой трафик на уровне данных (HTTP/HTTPS). Используется для блокировки контента.

class MyFilter: NEFilterDataProvider {
    override func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict {
        // Анализируем flow и принимаем решение
        return .allow() // или .drop(), .needRules()
    }
}

Сигнатура: class NETunnelProviderManager : NEVPNManager

Что делает: Специализированный менеджер для управления кастомными туннельными протоколами (Packet Tunnel).

let manager = NETunnelProviderManager()
manager.protocolConfiguration = NETunnelProviderProtocol()
// ... настройка
manager.saveToPreferences { error in
    // ...
}

4.9. NEAppProxyProvider

Сигнатура: class NEAppProxyProvider : NEProvider

Что делает: Позволяет создавать прокси-серверы на уровне приложений. Перехватывает трафик от конкретных приложений.

class MyProxy: NEAppProxyProvider {
    override func startProxy(options: [String : NSObject]? = nil) async throws {
        // Запуск прокси
    }
}

5. Реальный практический пример (150+ строк)

Создадим полноценный VPN-клиент с кастомным протоколом, который логирует все проходящие пакеты. Это будет упрощенная версия WireGuard-подобного решения.

Файл: PacketTunnelProvider.swift (расширение)

import NetworkExtension
import os.log

class PacketTunnelProvider: NEPacketTunnelProvider {
    
    private var tunnelInterface: NEPacketTunnelFlow?
    private var isRunning = false
    
    override func startTunnel(options: [String : NSObject]? = nil) async throws {
        os_log("Запуск туннеля...")
        
        // 1. Настройка сетевых параметров
        let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "10.0.0.1")
        settings.mtu = 1280
        
        // DNS через Cloudflare
        let dnsSettings = NEDNSSettings(servers: ["1.1.1.1", "1.0.0.1"])
        settings.dnsSettings = dnsSettings
        
        // IPv4: адрес клиента и маршрут по умолчанию
        let ipv4 = NEIPv4Settings(addresses: ["10.0.0.2"], subnetMasks: ["255.255.255.0"])
        ipv4.includedRoutes = [NEIPv4Route.default()]
        settings.ipv4Settings = ipv4
        
        // Применяем настройки
        try await setTunnelNetworkSettings(settings)
        
        // 2. Запускаем обработку пакетов
        isRunning = true
        await processPackets()
        
        os_log("Туннель успешно запущен")
    }
    
    override func stopTunnel(with reason: NEProviderStopReason) async {
        os_log("Остановка туннеля. Причина: %{public}@", String(describing: reason))
        isRunning = false
    }
    
    private func processPackets() async {
        // Получаем поток пакетов
        guard let flow = self.packetFlow else { return }
        
        while isRunning {
            // Читаем пакеты (максимум 10 за раз)
            let packets = await flow.readPackets()
            if packets.isEmpty {
                // Нет данных — ждем
                try? await Task.sleep(nanoseconds: 100_000_000) // 100ms
                continue
            }
            
            for (data, protocolFamily) in packets {
                // Логируем информацию о пакете
                let protocolName = protocolFamily == AF_INET ? "IPv4" : "IPv6"
                os_log("Получен пакет: %{public}@, размер: %d байт", protocolName, data.count)
                
                // В реальном приложении здесь будет шифрование и отправка на сервер
                // Для примера просто отправляем обратно (loopback)
                // Но в реальности так делать нельзя — это создаст петлю!
                // Вместо этого мы должны отправить пакет через реальный интерфейс
                // с помощью sendPacketFlow
            }
            
            // Отправляем обратно (для демонстрации)
            // В реальном коде здесь будет вызов sendPackets
        }
    }
    
    // Метод для отправки пакетов обратно в стек
    private func sendPacketBack(data: Data, protocolFamily: Int32) {
        // В реальном приложении этот метод вызывается после обработки
        // packetFlow?.writePackets([data], withProtocols: [NSNumber(value: protocolFamily)])
        os_log("Пакет отправлен обратно")
    }
}

Файл: VPNManager.swift (главное приложение)

<                    

Рекомендаций