Cách loại trừ một mạng con khỏi đường hầm VPN Android
VpnService không thể loại trừ một tuyến, vì vậy bạn thêm phần bù: tám tiền tố được tạo cho /8, cùng các kiểm tra IPv6, DNS và tra cứu bắt phần còn lại.
VpnService của Android không có tuyến loại trừ, và không lượng đọc API nào tạo ra được một tuyến. Builder.addRoute() luôn chỉ thêm đích đến vào đường hầm, nên giữ một phân đoạn LAN cục bộ nghĩa là phân đoạn đó không bao giờ vào danh sách tuyến. Thay vào đó, bạn thêm phần bù của phân đoạn, diễn đạt bằng các tiền tố thông thường. Công việc là số học, và nó thất bại ở vài chỗ có thể đoán trước.
Đi qua các kiểm tra dưới đây theo thứ tự. Mỗi kiểm tra giả định kiểm tra trước đã đứng vững, nên hãy dừng ở lỗi đầu tiên thay vì vá triệu chứng ở dưới.
Bắt đầu từ điều mà đường hầm tuyên bố
Chuyển 0.0.0.0/0 cho addRoute() nói với Android rằng mọi đích IPv4 đều thuộc đường hầm. Đó là lý do nhiều ứng dụng khách trông như không thể tách đường hầm: chúng tuyên bố toàn bộ không gian địa chỉ rồi cố trừ ra khỏi đó. Không có gì trong bộ dựng trừ đi. Một tuyến cụ thể hơn cho mạng bạn muốn giữ cục bộ lại làm ngược điều người ta mong đợi, vì tiền tố khớp dài nhất thắng và mạng đó rơi vào bên trong đường hầm.
Danh sách tuyến là toàn bộ chính sách. Không có quyền phủ quyết theo từng đích phía sau nó, và tài liệu tham chiếu Builder không liệt kê phương thức nào cung cấp điều đó.
Tạo phần bù từ số học tiền tố
Phần bù của một tiền tố bị loại trừ là tập hợp các anh em của nó trên đường tới gốc của cây địa chỉ. Với mỗi độ dài từ 1 đến độ dài bị loại trừ, lấy khối có độ dài đó chứa tiền tố của bạn, đảo bit ngay dưới tiền tố và xuất ra kết quả. Nghe có vẻ trừu tượng cho đến khi bạn chạy nó.
Loại trừ 10.0.0.0/8 cho ra tám tuyến: 128.0.0.0/1, 64.0.0.0/2, 32.0.0.0/3, 16.0.0.0/4, 0.0.0.0/5, 12.0.0.0/6, 8.0.0.0/7, 11.0.0.0/8. Không tuyến nào chứa 10.0.0.0/8, và cùng nhau chúng bao phủ mọi thứ còn lại.
Tám dòng tạo ra danh sách đó cho bất kỳ tiền tố nào:
import ipaddress
def complement(addr, length):
base = int(ipaddress.IPv4Address(addr))
out = []
for k in range(1, length + 1):
block = base & (0xFFFFFFFF << (32 - k))
sibling = block ^ (1 << (32 - k))
out.append(f"{ipaddress.IPv4Address(sibling)}/{k}")
return out
print(complement("10.0.0.0", 8))
Số tuyến bằng độ dài tiền tố, và đó là con số cần theo dõi. Một lần loại trừ /21 tốn 21 tuyến và vẫn dễ đọc. Mười lần loại trừ có thể tốn vài trăm, điều đó thường có nghĩa là thiết kế mạng cần thay đổi, không phải bộ tạo.
Kiểm tra họ địa chỉ và phạm vi đường hầm
Các tuyến IPv4 không làm gì cho IPv6. Nếu đường hầm tuyên bố một địa chỉ IPv6, tiền tố LAN IPv6 cần phần bù riêng, được tạo bằng cùng số học trên 128 bit. Một nguyên nhân phổ biến khiến việc loại trừ chạy đúng trên giấy trong khi thiết bị cục bộ vẫn không tới được là thế này: máy chủ được tới qua IPv6, và chỉ IPv4 đã bị cắt bỏ. Kiểm tra cả hai họ trước khi kết luận rằng phép toán sai.
Phạm vi là nửa còn lại của kiểm tra. Android theo dõi định tuyến theo mạng và theo ứng dụng, nên cùng một tiền tố có thể đi qua đường hầm cho ứng dụng này và trực tiếp cho ứng dụng khác tùy theo cách VPN được khai báo.
Đặt quy tắc DNS và theo ứng dụng ở đúng chỗ
Loại trừ tuyến không thay đổi bộ phân giải. Một tên máy chủ cục bộ vẫn có thể được máy chủ DNS của đường hầm trả lời trong khi địa chỉ của nó được định tuyến cục bộ, và sự không khớp hiện ra như một tên phân giải thành thứ mà LAN không tới được. Đặt chính sách bộ phân giải một cách có chủ đích, với miền tìm kiếm hoặc quy tắc DNS chia tách. Đừng cho rằng các tuyến đã quyết định điều đó thay bạn.
addDisallowedApplication() giữ một ứng dụng ra ngoài đường hầm; nó không giữ một đích ở ngoài cho những ứng dụng ở trong. protect() cho phép một socket bạn mở đi vòng qua đường hầm, và đó là cách một đường hầm tránh bắt chính lưu lượng của nó. Cả hai đều không thay thế phần bù.
Xác minh bằng tra cứu, không bằng danh sách của bạn
Khi đường hầm đã lên, hãy hỏi thiết bị xem một địa chỉ thực sự đi đâu thay vì đọc lại danh sách bạn đã dựng. Trên bản dựng có iproute2, adb shell ip route get 10.1.2.3 nêu tên giao diện mà nhân đã chọn. Chạy cùng lệnh cho một địa chỉ công cộng và xác nhận nó nêu tên đường hầm.
Hai kiểm tra nữa khép vòng lặp. Phân giải một tên cục bộ trước, rồi chạy adb shell ip route get trên địa chỉ đã phân giải và yêu cầu một giao diện không phải đường hầm, vì một adb shell ping -c 1 host.lan thành công chứng minh khả năng tới được chứ không phải đường đi. Lặp lại bài kiểm tra họ địa chỉ trên IPv6 nếu đường hầm tuyên bố một cái. Kiểm tra ở mức API bạn phát hành, vì cách các mạng VPN được đưa ra cho bảng định tuyến đã thay đổi giữa các bản phát hành. Một danh sách tuyến trông đúng trên giấy và một tra cứu chọn sai giao diện là hai lỗi khác nhau, và chỉ cái thứ hai tới được người dùng.
Nếu danh sách loại trừ tiếp tục lớn lên, hãy ngừng tạo tuyến và đánh số lại mạng từ xa hoặc thu hẹp đường hầm thay vì thế. Bộ tạo là một cách vòng tránh API còn thiếu, không phải công cụ thiết kế.