Android VPN 라우팅: main 테이블보다 먼저 오는 정책 규칙
Android는 목적지 조회보다 먼저 uid와 마크 규칙으로 라우팅 테이블을 고르므로, main에 추가한 경로는 아예 조회되지 않을 수 있다.
Android에서 main 테이블에 추가한 경로는 모든 목록에서 올바르게 보이지만 트래픽을 나르지 않는다. 이유는 main 테이블이 여러 선택지 중 하나일 뿐이고 가장 먼저 조회되지 않기 때문이다. Android의 네트워킹 서비스는 목적지 주소에 들어 있지 않은 정보 — 애플리케이션의 uid, 패킷에 붙은 마크, 패킷이 도착한 인터페이스 — 로 라우팅 테이블을 고르는 정책 규칙을 만들고, 목적지 조회는 그다음에 그 규칙이 고른 테이블 안에서 일어난다.
고정된 순서의 세 가지 결정
나가는 패킷은 세 가지 별개의 결정을 한다. 첫째, 정책 규칙이 어떤 라우팅 테이블이 적용되는지 정한다. 둘째, 그 테이블 안의 경로가 다음 홉과 나가는 인터페이스를 정한다. 셋째, netfilter 훅이 패킷이 살아남는지, 수정되는지, 리다이렉트되는지 정한다.
순서가 중요한 이유는 각 단계가 자기 질문에만 답할 수 있기 때문이다. 패킷을 받아들이는 방화벽 규칙은 테이블을 고르지 않고, 어떤 규칙도 고르지 않는 테이블에 추가한 경로는 아무 효과가 없다. Android VPN을 둘러싼 혼란의 상당 부분은 셋 중 하나를 고쳐도 변화가 보이지 않는 데서 오는데, 고장은 다른 단계에 있었기 때문이다.
| Stage | Selector | What it cannot decide |
|---|---|---|
| Policy rule | uid range, mark, incoming interface | Which route exists inside the table |
| Route lookup | Destination prefix, longest match | Whether the packet is allowed out |
| Netfilter hook | Chain position, match conditions | Which table was selected |
마크는 채워 넣은 필드이지 당신 소유의 라벨이 아니다
Android는 여러 사실을 같은 마크 값에 채워 넣는다. 어느 네트워크가 선택되었는지, 애플리케이션이 네트워크를 명시적으로 골랐는지, 소켓이 VPN에서 보호되는지, 권한과 관련된 비트가 그것이다. AOSP의 fwmark 헤더가 현재 트리에 대한 그 배치를 정의하며, 배치는 릴리스마다 바뀌는 플랫폼 내부 사항이다.
그래서 Android에서는 iptables -j MARK --set-mark 로 마크 값 전체를 쓰는 것이 위험하다. 태그를 더하는 것이 아니라 시스템이 이미 한 네트워크 선택을 대체한다. 그러면 패킷이 다른 규칙에 걸리는데, 어느 비트를 덮어썼는지에 따라 VPN이 동작하기 시작한 것처럼 보이거나 무관한 앱이 연결을 잃은 것처럼 보일 수 있다.
마크를 꼭 건드려야 한다면 먼저 현재 값을 읽고, 자신이 이해하는 비트만 바뀌도록 마스크를 사용하라.
ip rule show
ip route show table all | head -40
ip route get 10.20.4.11연결 마크는 패킷 마크와 별개의 객체이며 둘 다 경로가 아니다. 응답 패킷에서 마크를 복원하는 것은 나갈 때 테이블을 고르는 것과 다른 단계다. 그래서 단방향 수정은 열렸다가 멈추는 연결을 만든다.
규칙과 테이블의 소유자는 누구인가
Android의 네트워크 서비스인 netd가 규칙과 네트워크별 테이블을 만든다. AOSP RouteController 는 VPN, 물리 네트워크, uid 범위를 위해 그 규칙이 조립되는 곳이며, 수명 주기는 설정 파일이 아니라 네트워크 이벤트에 묶여 있다.
두 가지 결과가 따른다. 첫째, 한 기기에서 발견한 우선순위와 테이블 번호는 빌드 내부 사항이지 인터페이스가 아니다. 다른 Android 버전에서 똑같아 보이는 번호가 전혀 다른 의미일 수 있다. 둘째, 네트워크가 바뀌면 상태가 다시 만들어지므로 오늘 root 셸에서 삽입한 규칙이 다음 Wi-Fi 전환 뒤에는 사라져 있을 수 있다.
이 사라짐은 흔히 시스템이 경로를 무시한다고 보고된다. 실제로 일어난 일은 테이블을 고르던 규칙이 더 이상 존재하지 않고, 테이블 안의 경로가 이제 패킷 관점에서 도달 불가능해진 것이다. 재연결 뒤에 동작하던 설정이 멈추면 경로 목록이 아니라 규칙 목록을 확인하라.
훅 순서에 대한 질문에는 보편적인 답이 없다
iptables가 VPN 라우팅보다 먼저 실행되는가라는 질문은 패킷 경로를 지목하지 않고는 답할 수 없다. 로컬에서 생성된 패킷은 output 훅을 지나며, 거기서 마크 변경이 재라우팅 검사를 유발한다. 이는 첫 번째 조회를 소급해 고치는 것이 아니라 두 번째 조회다. 테더링 클라이언트에서 오는 패킷은 prerouting 경로를 지나 대신 포워딩되며, 그것을 보는 체인은 다르다.
그래서 같은 규칙이 어떤 앱의 트래픽에는 효과가 있고 테더링 트래픽에는 보이지 않을 수 있다. 영에 머무는 카운터는 규칙이 틀렸는지가 아니라 패킷이 어느 경로를 택했는지 알려준다. 일치 조건을 바꾸기 전에 카운터로 경로를 확정하라.
정책 라우팅 실수가 보기 어려워지는 단계도 여기다. 존재하지만 목적지를 담지 않은 테이블로 라우팅하면 방화벽 드롭처럼 보이는 네트워크 도달 불가가 나오고, 방화벽 드롭은 라우팅 블랙홀처럼 보이는 시간 초과를 낸다.
시스템 내부에서 진단하거나 손대지 말라
승인된 조사라면 무엇이든 건드리기 전에 네 가지 사실을 확정하라. 앱의 uid와 프로필, Android가 그 앱에 고른 네트워크, VPN이 그 앱을 애초에 포함하는지, 문제가 두 주소 체계 모두에서 나타나는지다. 앱별 제외는 라우팅 결함 없이도 VPN이 안 된다는 보고 상당수를 설명한다.
그다음 실제로 유효한 규칙, 마스크, 테이블을 읽고, 관찰 창 안에 네트워크 전환을 넣어 두라. 재연결을 넘어 동작이 바뀐다면 보고 있는 것은 관리되는 수명 주기이지 빠진 경로가 아니다.
일반적인 요구에는 지원되는 제어를 사용하라.앱별 포함과 제외 를 VPN 애플리케이션에서, 특정 경로가 필요한 모든 것에는 플랫폼 자체의 네트워크 선택을 사용하라. 권한 있는 통합이 정말로 라우팅 기계와 협력해야 한다면 지원하는 정확한 Android 버전의 구현을 대상으로 하고, 그것을 소유한 컴포넌트를 통해 수명 주기를 유지하라. 플랫폼 체인을 플러시하거나 우선순위가 높은 규칙을 삽입하면 진단이 두 번째 네트워크 관리자로 바뀌고, 기기는 그에 맞게 동작한다.