Pe Android 10 și versiunile ulterioare, o aplicație care deschide /proc/net/tcp primește un refuz de acces în locul unui tabel de socketuri. Interfața VPN nu este deranjată: pachetele sosesc în continuare, pentru că tunelul este o decizie de rutare, nu o permisiune de fișier. Ce s-a schimbat este răspunsul la o întrebare mai îngustă: „care aplicație deține această conexiune?” Înlocuitorul este ConnectivityManager.getConnectionOwnerUid, interogarea suportată pentru fiecare conexiune, iar alegerea între obiceiul vechi și noul API depinde de care dintre cele trei sarcini o ai de fapt: recepția pachetelor, atribuirea unui flux sau însumarea consumului.

Două moduri de a răspunde la întrebarea despre proprietar

Înainte de API 29, un proces privilegiat sau root putea citi tabelele de socketuri ale nucleului, potrivi cvadruplul și afla UID-ul proprietarului. Asta oferea un inventar la nivel de dispozitiv într-un singur fișier. De la API 29, vizualizarea sistemului de fișiere este restricționată, iar calea suportată este o interogare pentru fiecare conexiune cu protocolul, adresa locală și adresa la distanță.

QuestionLegacy socket tablesgetConnectionOwnerUid
Who owns this flow?Scan and match the tupleAsk once per tuple
Device-wide inventory?Yes, when readableNo, one flow at a time
Historical usage?NoNo; that is NetworkStatsManager
Works without root?No on modern releasesYes, for authorized callers

Comparația nu este „vechi contra nou”. Sunt trei sarcini diferite care înainte erau satisfăcute de un singur fișier lizibil, iar acum sunt satisfăcute de trei interfețe diferite cu trei contracte diferite.

Adresa locală este capătul aplicației

Semnătura metodei primește un protocol, o adresă de socket locală și o adresă de socket la distanță. „Locală” înseamnă partea aplicației din conexiune, nu capătul care în momentul inspecției se întâmplă să fie sursa pachetului. Un pachet care călătorește de la serverul de la distanță înapoi la aplicație are sursa și destinația inversate față de această definiție.

ConnectivityManager cm = getSystemService(ConnectivityManager.class);
int uid = cm.getConnectionOwnerUid(
    OsConstants.IPPROTO_TCP, appLocalAddress, appRemoteAddress);

Această greșeală de direcție este cauza cea mai frecventă pentru care o interogare funcțională returnează răspunsul greșit sau niciunul. Înregistrează ambele capete înainte de apel și confirmă perechea față de conexiunea pe care aplicația a deschis-o efectiv. Și familia de adrese contează: o adresă mapată prin IPv4 și o adresă IPv6 nativă sunt tuple diferite, la fel ca fluxurile TCP și UDP pe aceleași porturi.

Autorizarea și rezultatele necunoscute fac parte din contract

Interogarea este limitată la apelanți autorizați, inclusiv aplicația VPN activă pentru utilizatorul respectiv. O aplicație de monitorizare nelegată nu poate presupune că poate enumera conexiunile altui profil, iar un apel neautorizat poate arunca o excepție de securitate în loc să returneze o eroare prietenoasă. Tratează ambele rezultate explicit.

Un proprietar lipsă nu este un nume de pachet. Fluxul s-ar putea să se fi închis între pachet și interogare, tuplul poate fi incomplet sau pur și simplu nu i se permite apelantului să știe. Dacă memorezi primul răspuns reușit și îl refolosești mai târziu, atribui o conexiune nouă unei aplicații vechi, ceea ce este mai rău decât să raportezi necunoscut.

Când atribuirea eșuează, traficul are totuși nevoie de o decizie: blochează-l acolo unde politica o cere, permite doar un domeniu pe care îl înțelegi sau expune golul. Acea soluție de rezervă aparține designului, nu unui comentariu.

Contabilitatea este o altă întrebare

NetworkStatsManager raportează consumul conform propriilor reguli de acces. Poate spune cât a transferat un UID într-o perioadă; nu poate spune proprietarul unui pachet viu arbitrar. Capabilitățile de rețea descriu proprietățile unei rețele, nu un director de socketuri. Amestecarea celor trei produce tablouri de bord care par consecvente și nu sunt.

Pentru cazul simplu de a decide ce aplicații folosesc tunelul, listele suportate de aplicații permise și interzise din Android exprimă deja politica fără nicio extragere. Păstrează atribuirea pe flux pentru cazurile care chiar au nevoie de ea și bugetează o implementare întreținută care tratează împreună direcția, permisiunile și stările necunoscute.

Alege instrumentul după sarcină, apoi testează marginile

Dacă cerința este selecția tunelului, folosește lista platformei. Dacă este atribuire în timp real, folosește interogarea proprietarului și tratează fiecare tuplu fără răspuns ca necunoscut. Dacă sunt totaluri istorice, folosește stats API. Apoi testează cele trei margini care sparg implementările naive: un pachet în direcție inversă, un flux care se închide înainte de interogare și un apelant care nu este VPN-ul activ.

Nimic din toate acestea nu necesită dezactivarea SELinux, rootarea telefonului sau facerea tabelelor nucleului lizibile global. Restricția este ideea: platforma a mutat atribuirea în spatele unui contract, astfel încât răspunsul să fie disponibil componentei care are nevoie de el în mod legitim și nimănui altcuiva.

Articole similare