Atribució de VPN a Android: getConnectionOwnerUid contra /proc/net
Android 10 amaga /proc/net a les aplicacions. Una VPN encara rep paquets, però l'atribució passa a getConnectionOwnerUid, amb les seves pròpies regles de tupla i permisos.
A Android 10 i posteriors, una aplicació que obre /proc/net/tcp rep una denegació d'accés en lloc d'una taula de sockets. La interfície VPN no hi fa cas: els paquets continuen arribant, perquè el túnel és una decisió d'encaminament, no un permís de fitxer. El que ha canviat és la resposta a una pregunta més estreta: «quina aplicació és propietària d'aquesta connexió?» La substitució és ConnectivityManager.getConnectionOwnerUid, la consulta per connexió admesa, i triar entre el vell costum i la nova API depèn de quina de les tres feines tens realment: rebre paquets, atribuir un flux o sumar consum.
Dues maneres de respondre la pregunta del propietari
Abans de l'API 29, un procés privilegiat o root podia llegir les taules de sockets del nucli, aparellar la quàdruple i conèixer l'UID propietari. Això donava un inventari de tot el dispositiu en un sol fitxer. Des de l'API 29 la visió del sistema de fitxers està restringida, i la ruta admesa és una consulta per connexió amb el protocol, l'adreça local i l'adreça remota.
| 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 |
La comparació no és «vell contra nou». Són tres feines diferents que abans satisfeia un sol fitxer llegible i ara satisfan tres interfícies diferents amb tres contractes diferents.
L'adreça local és l'extrem de l'aplicació
La signatura del mètode pren un protocol, una adreça de socket local i una adreça de socket remota. «Local» vol dir el costat de l'aplicació en la connexió, no l'extrem que en el moment de la inspecció resulta ser la font del paquet. Un paquet que viatja del servidor remot cap a l'aplicació té l'origen i la destinació intercanviats respecte d'aquesta definició.
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
int uid = cm.getConnectionOwnerUid(
OsConstants.IPPROTO_TCP, appLocalAddress, appRemoteAddress);Aquest error de direcció és la causa més habitual que una consulta correcta retorni una resposta equivocada o cap. Registra els dos extrems abans de la crida i confirma la parella amb la connexió que l'aplicació ha obert realment. La família d'adreces també importa: una adreça mapejada per IPv4 i una adreça IPv6 nativa són tuples diferents, i també ho són els fluxos TCP i UDP als mateixos ports.
L'autorització i els resultats desconeguts formen part del contracte
La consulta està restringida a cridadors autoritzats, inclosa l'aplicació VPN activa per a l'usuari corresponent. Una aplicació de monitoratge no relacionada no pot suposar que pot enumerar les connexions d'un altre perfil, i una crida no autoritzada pot llançar una excepció de seguretat en lloc de retornar un error amable. Gestiona tots dos resultats explícitament.
Un propietari absent no és un nom de paquet. El flux pot haver-se tancat entre el paquet i la consulta, la tupla pot estar incompleta, o senzillament pot ser que el cridador no tingui permís per saber-ho. Emmagatzemar la primera resposta correcta i reutilitzar-la després atribueix una connexió nova a una aplicació vella, cosa pitjor que informar de desconegut.
Quan l'atribució falla, el trànsit encara necessita una decisió: bloqueja'l on la política ho exigeixi, permet només un abast que entenguis, o mostra la llacuna. Aquest pla B pertany al disseny, no a un comentari.
La comptabilitat és una altra pregunta
NetworkStatsManager informa del consum sota les seves pròpies regles d'accés. Pot dir-te quant ha transferit un UID durant un període; no pot dir-te el propietari d'un paquet viu arbitrari. Les capacitats de xarxa descriuen propietats d'una xarxa, no un directori de sockets. Barrejar les tres coses produeix panells que semblen coherents i no ho són.
Per al cas simple de decidir quines aplicacions usen el túnel, les llistes admeses d'aplicacions permeses i denegades d'Android ja expressen la política sense extreure res. Reserva l'atribució per flux per als casos que realment la necessiten, i pressuposta una implementació mantinguda que tracti direcció, permisos i estats desconeguts alhora.
Tria l'eina segons la feina i després prova les vores
Si el requisit és la selecció de túnel, usa la llista de la plataforma. Si és atribució en directe, usa la consulta de propietari i tracta cada tupla sense resposta com a desconeguda. Si són totals històrics, usa l'API d'estadístiques. Després prova les tres vores que trenquen les implementacions ingènues: un paquet en sentit invers, un flux que es tanca abans de consultar-lo i un cridador que no és la VPN activa.
Res d'això no requereix desactivar SELinux, fer root al telèfon ni fer les taules del nucli llegibles globalment. La restricció és el punt: la plataforma ha mogut l'atribució darrere d'un contracte perquè la resposta estigui disponible per al component que legítimament la necessita, i per a ningú més.