Túnel partit a Android VpnService: què hi pots encaminar de veritat
VpnService només envia els paquets que el builder adreça al seu descriptor. Comprova prefixos, autenticació del proxy, DNS i llistes d'aplicacions abans de culpar el túnel partit.
El túnel partit en una aplicació amb VpnService es trenca en uns quants punts concrets, i la reparació és diferent per a cada un. El client només mostra una destinació que falla, així que la primera feina és esbrinar quina frontera trobes: les rutes que va rebre el builder, el proxy que no pots autenticar, el resolver que només has anunciat o la llista d'aplicacions que la plataforma aplica per usuari. Per això mateix mantinc una comprovació de quatre passos. Va en l'ordre d'aquí sota i s'atura a la primera fallada.
Confirma què ha lliurat el builder a la plataforma
VpnService.Builder és una llista de declaracions. establish() retorna un ParcelFileDescriptor per a una interfície virtual, i la plataforma només envia a aquest descriptor el trànsit que coincideix amb els prefixos d'addAddress i addRoute; addDnsServer diu als clients quin resolver han de consultar. Una destinació fora de tots els prefixos que has afegit no arriba mai a la teva aplicació, digui el que digui la interfície. Aquest sol fet explica la majoria d'informes de túnel partit trencat abans de tocar cap codi.
Registra les llistes d'adreces, rutes i DNS que ha muntat el teu builder i compara-les amb les destinacions que fallen. Un /32 absent per a un host de gestió és molt més habitual que un túnel trencat. Si la llista és correcta, continua; si és incorrecta, la resta de la llista de comprovació és esforç perdut.
excludeRoute només existeix des de l'API 33
L'exclusió per destinació és la petició que més vegades acaba en un fil de suport. builder.excludeRoute(IpPrefix) va arribar a l'API 33; abans, l'única aproximació suportada és afegir rutes per a tot excepte el prefix exclòs, cosa que vol dir calcular tu mateix els prefixos complementaris. Les dues crides no són imatges mirall: una resta del conjunt de rutes de la interfície, l'altra construeix el conjunt des de zero, i un dispositiu antic llançarà o ignorarà en silenci una crida que no reconeix. Fes una branca segons Build.VERSION.SDK_INT i prova els dos camins en maquinari real.
Quan calculis complements, vigila l'alineació dels prefixos. Una exclusió /24 dins d'una ruta /16 necessita els prefixos de cobertura que descriuen de debò la resta, no un /8 mal format. Registra la llista final i comprova que l'host exclòs encara es resol per un camí fora del túnel.
Les credencials del proxy no es poden expressar
setHttpProxy(ProxyInfo) apunta la interfície a un proxy HTTP o SOCKS, i una URL PAC pot triar entre tots dos per destinació. Ni ProxyInfo ni el format PAC porten nom d'usuari i contrasenya, així que un proxy autenticat queda fora de l'API de la plataforma. Les aplicacions que en necessiten un solen incrustar una pila d'espai d'usuari, tun2socks o un motor semblant, i responen elles mateixes el repte 407.
Aquesta decisió té conseqüències. Una pila d'espai d'usuari només veu el trànsit que hi encamines, així que ha de viure dins la mateixa frontera del descriptor i hereta el mateix requisit de protect() per al seu propi socket d'enllaç ascendent. Si t'hi saltes protect(), el túnel captura el seu propi transport, cosa que es veu com un handshake que s'encalla després del primer paquet. Prova-ho contra un proxy que exigeixi credencials, perquè un proxy obert passa pel camí de la plataforma i no demostra res.
Les llistes per aplicació i el DNS també són declaracions
Builder té addAllowedApplication i addDisallowedApplication, i són mútuament excloents: cridar-los tots dos llança una excepció. En qualsevol cas la llista s'aplica per usuari en configurar la interfície, així que una aplicació del perfil de treball es regeix pel túnel del perfil de treball i no pel teu. Cap API et dona control sobre la decisió d'una altra aplicació: no pots forçar un paquet dins del teu túnel des de fora ni pots tancar o reconfigurar una VPN que ja està activa.
El DNS segueix el mateix patró. addDnsServer anuncia una adreça de resolver; no permet que la teva aplicació respongui consultes. Si vols resoldre noms tu mateix, executa un resolver i encamina la seva adreça dins del túnel. Les aplicacions que fan servir DoT o DoH cap a un nom d'host fix poden saltar-se l'adreça que has anunciat, així que tracta la resolució de noms com a millor esforç llevat que interceptis també el camí de la consulta.
Manifest, always-on i la prova de quatre passos
L'elegibilitat es declara al manifest: BIND_VPN_SERVICE, el filtre d'intents android.net.VpnService i un servei en primer pla si el túnel ha de sobreviure en segon pla. L'always-on VPN a més requereix un onRevoke net, perquè el sistema el crida quan l'usuari canvia la configuració o una altra aplicació pren el control.
Fes les comprovacions en ordre i atura't a la primera fallada. Primer, bolca les llistes d'adreces, rutes i DNS passades al builder. Segon, confirma que establish() ha retornat un descriptor i que el socket de transport està protegit. Tercer, demana una destinació dins d'un prefix inclòs i una dins d'un prefix exclòs. Quart, comprova quin resolver ha contestat de veritat consultant un nom d'host que només el teu resolver pot resoldre.
Un handshake complet no demostra cap dels passos posteriors. Cada comprovació aïlla una capa, així que un èxit al segon pas no diu res sobre el quart.
La referència de VpnService.Builder llista tota la superfície del builder. Tot el que necessitis més enllà és una altra API, una altra aplicació o la teva pròpia pila d'espai d'usuari, i això és una decisió de disseny, no un error del túnel partit.