Kiểm tra mà hầu hết ứng dụng Android dùng để phát hiện VPN là một tên giao diện, và nó chưa bao giờ là kiểm tra VPN. Nó báo rằng tồn tại một giao diện với chuỗi mà lập trình viên mong đợi, điều này trên một thiết bị có nghĩa là OpenVPN và trên thiết bị khác có nghĩa là một thứ hoàn toàn không liên quan. ping là cùng một loại bằng chứng: nó chứng minh một đích đã trả lời, không phải một đường hầm đã chở gói tin. Thứ mà nền tảng thực sự công bố là một tập hợp mạng và các loại truyền tải mà mỗi mạng khai báo, và đó là một câu hỏi khác với một câu trả lời khác.

Niềm tin sai: nếu có tun0 thì VPN đang bật

Ứng dụng VPN trên Android là các triển khai VpnService trao cho hệ thống một bộ mô tả tệp tun. Tên giao diện đến từ người tạo ra nó: OpenVPN thường tạo tun0, PPTP và L2TP tạo ppp0, WireGuard đặt tên giao diện theo đường hầm, và một đường hầm IPsec có thể không có tên thông thường nào cả. Vì vậy đọc /proc/net/dev hay liệt kê đối tượng NetworkInterface là tìm một quy ước, không phải một sự thật.

Cả hai chiều sai đều còn sống. Một thiết bị có thể có tun0 cho một đường hầm cục bộ không liên quan gì đến VPN của người dùng, và một đường hầm WireGuard hoàn toàn khỏe mạnh có thể vô hình với kiểm tra chỉ biết chuỗi tun0. Dương tính giả và âm tính giả trong cùng một hàm khiến nó không dùng được làm chẩn đoán, bởi vì một phiếu hỗ trợ nói «không phát hiện VPN» không đáng tin ở bất kỳ chiều nào.

Quyền truy cập cũng bị thu hẹp. Các bản Android mới hạn chế những gì một ứng dụng thông thường có thể đọc từ /proc, nên một kiểm tra dựa trên tên từng chạy trên Android 8 có thể trả về danh sách rỗng trên thiết bị sau này mà không cần thay đổi mã. Khi kiểm tra thất bại âm thầm theo hướng mở, ứng dụng kết luận không có VPN trên phần cứng mà đường hầm vẫn tốt.

Niềm tin sai: ping thành công nghĩa là đường hầm đang chở lưu lượng

ping tới một máy chủ nội bộ thành công mỗi khi có tuyến đường tới máy chủ đó. Với đường hầm chia tách, máy chủ có thể truy cập được qua Wi-Fi trong khi đường hầm đang bật và làm việc hoàn toàn khác. Với VPN theo từng ứng dụng, ứng dụng thực hiện ping có thể bị loại khỏi đường hầm, nên gói tin ICMP đi ra ngoài nó và quay lại, điều này chứng minh ngược lại với giả định của kiểm tra.

ICMP và TCP cũng được lọc riêng. Một đường hầm cho phép yêu cầu echo nhưng bỏ cổng TCP mà một dịch vụ cần sẽ vượt qua bài kiểm tra ping và thất bại trong ứng dụng, và đó là quy tắc tường lửa ở phía bên kia, không phải thuộc tính của thiết bị. Cách đọc trung thực duy nhất của một ping thành công là đích này đã trả lời giao thức này tại thời điểm này.

Nền tảng thực sự báo cáo gì

Từ API 21, ConnectivityManager.getAllNetworks() trả về mọi mạng mà hệ thống biết, và getNetworkCapabilities(network) báo cáo mạng đó khai báo gì. Một mạng VPN mang TRANSPORT_VPN. Lặp qua tất cả mạng trả lời một câu hỏi: có VPN nào đang bật trên thiết bị này ngay bây giờ không?

Câu hỏi thứ hai, hẹp hơn cần một lệnh gọi khác. getActiveNetwork() trả về mạng mà lưu lượng của ứng dụng sẽ dùng ngay bây giờ, và kiểm tra cùng khả năng đó trên nó trả lời: lưu lượng của tôi có đi qua VPN không? Hai bên có thể không đồng ý, và sự bất đồng đó mang thông tin. Một VPN đang bật nhưng không phải hiện tại chỉ ra định tuyến theo ứng dụng hoặc cấu hình đường hầm chia tách, không phải một đường hầm thiếu.

Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
    && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);

Bảo vệ cả hai lệnh gọi. Khả năng có thể là null với một mạng đã biến mất giữa lúc liệt kê và truy vấn, và mạng đang hoạt động có thể là null trong lúc chuyển giao. NET_CAPABILITY_NOT_VPN hữu ích theo chiều ngược lại, để xác nhận một mạng chắc chắn không phải đường hầm thay vì chỉ thiếu cờ truyền tải VPN.

NetworkInfo là cùng niềm tin sai với một API

ConnectivityManager.TYPE_VPN với NetworkInfo là thế hệ thứ hai của kiểm tra này, và nó lỗi thời vì một lý do. Trên các bản hiện đại, đối tượng trả về cho VPN có thể là null trong khi đường hầm đã kết nối, nên mã coi null là «không có VPN» âm thầm báo cáo âm tính giả trên những thiết bị mà câu trả lời quan trọng nhất.

Cả bản tin thường đi kèm cũng không tốt hơn. CONNECTIVITY_CHANGE có lịch sử được ghi lại về việc bỏ sót chuyển đổi VPN, bao gồm các bản dựng mà nó chỉ kích hoạt khi VPN kết nối hoặc ngắt kết nối thay vì mỗi lần mạng thay đổi. Một trình lắng nghe xây trên nó tạo ra một biến trạng thái trôi xa khỏi góc nhìn của nền tảng cho đến lần kết nối lại đầy đủ tiếp theo.

Phản ứng với callback thay vì thăm dò

Đăng ký một NetworkRequest yêu cầu TRANSPORT_VPN và loại bỏ NET_CAPABILITY_NOT_VPN, rồi gọi registerNetworkCallback với yêu cầu đó. Khi đó onAvailable và onLost cho bạn biết khi nào khai báo thay đổi, và đó là sự kiện bạn thực sự quan tâm. Hủy đăng ký callback khi thành phần dừng, nếu không ứng dụng giữ tham chiếu tới một màn hình đã chết và callback tiếp tục kích hoạt.

NetworkRequest req = new NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    .build();
cm.registerNetworkCallback(req, callback);

Điều này cần ACCESS_NETWORK_STATE, một quyền bình thường: không root, không hộp thoại đồng ý của người dùng, không quyền VPN. Thăm dò bằng bộ hẹn giờ là phương án thay thế mà người ta dựa vào, và nó tốn pin trong khi vẫn bỏ sót một chuyển đổi ngắn hơn khoảng thời gian, và đó chính là chuyển đổi mà người dùng báo cáo.

Điều mà câu trả lời vẫn chưa thể cho bạn biết

Không API nào trong số này báo cáo đường hầm có khỏe mạnh không, nó kết thúc ở máy chủ nào, hay lưu lượng của một ứng dụng cụ thể có ở bên trong nó không. Một mạng VPN có thể tồn tại mà không có tuyến đường dùng được, và một đường hầm theo ứng dụng có thể loại trừ ứng dụng đang đặt câu hỏi.

Vì vậy hãy ghi lại những gì kiểm tra đã thấy, không chỉ giá trị boolean. Ghi lại định danh mạng, các truyền tải được khai báo và liệu mạng đó có đang hoạt động không. Một báo cáo nói «không phát hiện VPN» là vô dụng, trong khi một báo cáo nói rằng một mạng khai báo TRANSPORT_VPN nhưng getActiveNetwork() trả về Wi-Fi chỉ thẳng vào định tuyến hoặc danh sách loại trừ, và đó là bản sửa mà ai đó có thể hành động.

Bài viết liên quan