“VPN이 켜져 있는가?”는 사실 한 문장에 담긴 세 가지 질문입니다. 코드에 필요한 것이 그중 어느 것인지가 첫 번째 결정이며, 대부분의 구현이 바로 이것을 건너뜁니다. 이 기기에 터널이 있는지, 이 목적지로 가는 트래픽이 터널을 통해 나가는지, 아니면 원격 끝이 기대하는 신원으로 응답하는지. 속성을 고르고, 그다음 그것을 측정하는 신호를 고르고, 마지막으로 실제로 무엇을 측정했는지 로그에 적으십시오.

세 가지 속성, 세 가지 신호

각각 한 줄로 압축하면 세 질문에는 세 가지 다른 답이 있습니다. 아래 표는 요약이 아니라 결정 그 자체입니다.

PropertySignal that answers itWhat it still leaves open
ExistenceThe platform's own connection state: RAS on Windows, NetworkManager on D-Bus on Linux, scutil on macOSTunnels of a type the platform does not manage, which includes most third-party clients
PathA route lookup for the one destination, or a scoped proxy or interface key that names the tunnelWhether the far end is the peer you trust
IdentityAn authenticated response from the far end, or a source-address policy applied on the serverWhich local interface carried the packets

마지막 행을 천천히 읽으십시오. 응답하는 사설 호스트는 무언가가 응답했다는 것을 증명할 뿐, 그 답이 터널을 통해 왔다는 것을 증명하지 않으며 반대편에 누가 있는지에 대해서는 아무 말도 하지 않습니다. 필요한 속성이 경로라면 경로를 측정하십시오.

존재: 플랫폼 자체의 상태

Windows는 네이티브 프로필에 대해 Remote Access Service API를 통해 이에 답합니다. RasEnumConnections 은 연결된 것을 나열하고, RasGetConnectStatus 는 한 항목의 상태를 보고하며, RasGetLinkStatistics 는 속도, 바이트 카운터, 연결 수명을 추가합니다. 한계가 핵심입니다: OpenVPN, WireGuard, 공급업체 클라이언트는 RAS 연결을 등록하지 않으므로 그런 종류의 정상 터널은 빈 목록을 보여 주고 순진한 코드는 이를 꺼짐으로 보고합니다.

Linux 데스크톱은 D-Bus의 NetworkManager를 통해 같은 속성을 노출하며, VPN 연결 객체의 PropertiesChanged 가 점검을 이벤트로 바꾸고 State 속성이 링크가 연결 상태에 도달한 시점을 보고합니다. 셸에서는 nmcli connection show --active 가 같은 정보를 읽으며, 디버깅 중에 원하는 것이 바로 그것입니다. macOS는 그 목록을 System Configuration 프레임워크에 두고 scutil 가 그 명령줄 보기입니다. 셋 다 플랫폼이 관리하는 연결을 설명하며, 셋 다 플랫폼이 관리하지 않는 터널에 대해서는 침묵합니다.

경로: 패킷이 실제로 가는 곳

이름 일치는 모두가 먼저 손을 뻗는 지름길입니다: 인터페이스를 나열하고 문자열을 tun, tap, ppp 또는 ipsec 과 대조하고 일치를 증거로 삼습니다. 그것은 어림법이며, 인터페이스 집합은 무언가가 지나가든 아니든 같아 보일 수 있습니다. 오래된 iOS 빌드는 이렇게 답했고 이후 빌드가 그만둔 것은 보이는 인터페이스가 더 이상 두 상태를 구분하지 못했기 때문입니다.

그 자리를 대신한 것은 시스템 프록시 사전입니다. VPN이 연결되어 있는 동안 프록시 설정 호출은 터널 관련 인터페이스로 한정된 키를 돌려주고, 아무것도 연결되어 있지 않으면 사전은 빈 채로 돌아옵니다. 이는 들리는 것보다 약한 주장으로, 터널이 있다는 시스템의 믿음을 기록할 뿐, 어느 목적지가 그것을 쓰는지는 기록하지 않습니다.

Android에는 이 함정의 자체 버전이 있습니다: vpn.connectivity 브로드캐스트는 일부 빌드에서는 매니페스트에 선언된 수신자에게 도달하고 다른 빌드에서는 런타임에 등록된 수신자에게 도달합니다. 최선의 노력으로 취급하고 그 뒤에 직접 점검을 두십시오.

목적지가 중요한 경우에는 어댑터 목록이 아니라 라우팅 테이블에 물으십시오. 기본 게이트웨이 어림법은 다중 네트워크 머신과 자체 게이트웨이를 설치하는 터널에서 깨집니다.

신원: 원격 끝을 점검하십시오

몇 초마다 원격 끝으로 ICMP 에코를 보내는 루프는 이런 종류의 점검 중 가장 저렴하며, 유용한 방향으로 실패합니다. 사설 네트워크 내부에만 존재하는 주소를 겨냥하되, 공인 주소는 동작하는 상향 링크 어디로든 응답하며 아무것도 증명하지 못합니다. ICMP가 필터링되는 곳에서는 서비스 포트로의 짧은 TCP 연결이 같은 일을 합니다.

서버 측 점검에 쓸 수 있는 신호는 연결의 출발지 주소 하나뿐입니다. 범위 목록을 유지하는 것은 운영 작업이며, 같은 블록을 우연히 쓰는 다른 네트워크와 터널을 구분하지 못합니다.

거친 정책은 그것으로 충분합니다. 신원은 그렇지 않으므로, 패킷이 어디서 오는 것처럼 보이는지 믿는 대신 클라이언트를 인증하십시오.

판정이 아니라 분류를 기록하십시오

실패 분류를 로그에서 분리하십시오: 인터페이스가 전혀 없음, 인터페이스는 있지만 목적지에 도달할 수 없음, 목적지에는 도달하지만 인증이 거부됨. 이들은 서로 다른 세 주인이며 각각 다른 조사를 엽니다.

이를 하나의 “VPN 다운”으로 합치면 고칠 수 없는 팀에 보고가 갑니다. 유용한 출력은 분류이고, 판정은 그것을 틀리게 만드는 것입니다.

관련 글