Atribución de VPN en Android: getConnectionOwnerUid fronte a /proc/net
Android 10 oculta /proc/net ás aplicacións. Unha VPN segue recibindo paquetes, pero a atribución pasa a getConnectionOwnerUid, coas súas propias regras de tupla e permisos.
En Android 10 e posteriores, unha aplicación que abre /proc/net/tcp recibe unha denegación de acceso en lugar dunha táboa de sockets. Á interface VPN non lle importa: os paquetes seguen chegando, porque o túnel é unha decisión de encamiñamento, non un permiso de ficheiro. O que cambiou é a resposta a unha pregunta máis estreita: «que aplicación posúe esta conexión?» O substituto é ConnectivityManager.getConnectionOwnerUid, a consulta por conexión admitida, e escoller entre o vello costume e a nova API depende de cal das tres tarefas ten realmente: recibir paquetes, atribuír un fluxo ou sumar consumo.
Dúas formas de responder á pregunta do propietario
Antes da API 29, un proceso privilexiado ou root podía ler as táboas de sockets do kernel, emparellar a cuádrupla e coñecer o UID propietario. Iso daba un inventario de todo o dispositivo nun só ficheiro. Desde a API 29 a vista do sistema de ficheiros está restrinxida, e a ruta admitida é unha consulta por conexión co protocolo, o enderezo local e o enderezo remoto.
| Question | Legacy socket tables | getConnectionOwnerUid |
|---|---|---|
| Who owns this flow? | Scan and match the tuple | Ask once per tuple |
| Device-wide inventory? | Yes, when readable | No, one flow at a time |
| Historical usage? | No | No; that is NetworkStatsManager |
| Works without root? | No on modern releases | Yes, for authorized callers |
A comparación non é «vello contra novo». Son tres tarefas distintas que antes satisfacía un único ficheiro lexible e agora satisfán tres interfaces distintas con tres contratos distintos.
O enderezo local é o extremo da aplicación
A sinatura do método toma un protocolo, un enderezo de socket local e un enderezo de socket remoto. «Local» significa o lado da aplicación na conexión, non o extremo que no momento da inspección resulta ser a orixe do paquete. Un paquete que viaxa do servidor remoto de volta á aplicación ten a orixe e o destino invertidos respecto desa definición.
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
int uid = cm.getConnectionOwnerUid(
OsConstants.IPPROTO_TCP, appLocalAddress, appRemoteAddress);Este erro de dirección é a razón máis común de que unha consulta correcta devolva unha resposta equivocada ou ningunha. Rexistre ambos os extremos antes da chamada e confirme o par contra a conexión que a aplicación abriu realmente. A familia de enderezos tamén importa: un enderezo mapeado por IPv4 e un enderezo IPv6 nativo son tuplas distintas, e tamén o son os fluxos TCP e UDP nos mesmos portos.
A autorización e os resultados descoñecidos forman parte do contrato
A consulta está restrinxida a chamadores autorizados, incluída a aplicación VPN activa para o usuario correspondente. Unha aplicación de monitorización allea non pode supor que pode enumerar as conexións doutro perfil, e unha chamada non autorizada pode lanzar unha excepción de seguridade en lugar de devolver un erro amable. Xestione ambos os resultados de forma explícita.
Un propietario ausente non é un nome de paquete. O fluxo puido pecharse entre o paquete e a consulta, a tupla pode estar incompleta, ou pode que simplemente non se lle permita sabelo ao chamador. Almacenar a primeira resposta correcta e reutilizala despois atribúe unha conexión nova a unha aplicación vella, o que é peor que informar descoñecido.
Cando a atribución falla, o tráfico segue necesitando unha decisión: bloquealo onde a política o esixa, permitir só un ámbito que entenda, ou amosar o oco. Ese respaldo pertence ao deseño, non a un comentario.
A contabilidade é outra pregunta
NetworkStatsManager informa do consumo segundo as súas propias regras de acceso. Pode dicirlle canto transferiu un UID durante un período; non pode dicirlle o propietario dun paquete vivo arbitrario. As capacidades de rede describen propiedades dunha rede, non un directorio de sockets. Mesturar as tres produce paneis que parecen coherentes e non o son.
Para o caso simple de decidir que aplicacións usan o túnel, as listas admitidas de aplicacións permitidas e denegadas de Android xa expresan a política sen extraer nada. Reserve a atribución por fluxo para os casos que realmente a necesitan, e orzamente unha implementación mantida que trate dirección, permisos e estados descoñecidos en conxunto.
Escolla a ferramenta segundo a tarefa e logo probe os bordos
Se o requisito é a selección de túnel, use a lista da plataforma. Se é atribución en vivo, use a consulta de propietario e trate cada tupla sen resposta como descoñecida. Se son totais históricos, use a API de estatísticas. Logo probe os tres bordos que rompen as implementacións inxenuas: un paquete en sentido inverso, un fluxo que se pecha antes de consultalo e un chamador que non é a VPN activa.
Nada disto require desactivar SELinux, facer root no teléfono nin facer as táboas do kernel lexibles globalmente. A restrición é o punto: a plataforma moveu a atribución detrás dun contrato para que a resposta estea dispoñible para o compoñente que lexitimamente a necesita, e para ninguén máis.