В Android 10 и по-нови приложение, което отвори /proc/net/tcp, получава отказ за достъп вместо таблица на сокетите. VPN интерфейсът не се интересува: пакетите продължават да пристигат, защото тунелът е решение за маршрутизиране, а не файлово разрешение. Промени се отговорът на по-тесен въпрос: „кое приложение притежава тази връзка?“ Замяната е ConnectivityManager.getConnectionOwnerUid, поддържаната заявка за отделна връзка, а изборът между стария навик и новия API зависи от това коя от три задачи всъщност имате: приемане на пакети, атрибуция на поток или отчитане на потреблението.

Два начина да отговорим на въпроса за притежателя

Преди API 29 привилегирован или root процес можеше да чете таблиците на сокетите в ядрото, да съпостави четворката и да научи UID на притежателя. Това даваше инвентар за цялото устройство в един файл. От API 29 изгледът на файловата система е ограничен и поддържаният път е заявка за отделна връзка с протокола, локалния адрес и отдалечения адрес.

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

Сравнението не е „старо срещу ново“. Това са три различни задачи, които преди се удовлетворяваха от един четим файл, а сега — от три различни интерфейса с три различни договора.

Локалният адрес е крайната точка на приложението

Сигнатурата на метода приема протокол, локален адрес на сокет и отдалечен адрес на сокет. „Локален“ означава страната на приложението във връзката, а не онзи край, който в момента на проверката случайно е източник на пакета. Пакет, пътуващ от отдалечения сървър обратно към приложението, има разменени източник и дестинация спрямо това определение.

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

Тази грешка в посоката е най-честата причина работеща заявка да върне грешен отговор или никакъв. Записвайте двата края преди извикването и сверявайте двойката с връзката, която приложението действително е отворило. Семейството на адреса също е важно: адрес, представен чрез IPv4, и роден IPv6 адрес са различни кортежи, както и TCP и UDP потоци на едни и същи портове.

Оторизацията и неизвестните резултати са част от договора

Заявката е ограничена до оторизирани извикващи, включително активното VPN приложение за съответния потребител. Несвързано приложение за мониторинг не може да предполага, че ще изброи връзките на друг профил, а неоторизирано извикване може да хвърли изключение за сигурност вместо любезна грешка. Обработвайте и двата изхода явно.

Липсващият притежател не е име на пакет. Потокът може да се е затворил между пакета и заявката, кортежът може да е непълен или на извикващия просто да не е позволено да знае. Кеширането на първия успешен отговор и по-късното му използване приписва нова връзка на старо приложение, което е по-лошо от съобщаване на неизвестно.

Когато атрибуцията се провали, трафикът все още изисква решение: блокирайте го там, където политиката го изисква, позволете само обхват, който разбирате, или покажете празнината. Този резервен вариант принадлежи на дизайна, не на коментар.

Отчитането е друг въпрос

NetworkStatsManager отчита потребление по свои собствени правила за достъп. Може да ви каже колко е прехвърлил даден UID за период; не може да ви каже притежателя на произволен жив пакет. Мрежовите възможности описват свойства на мрежа, не указател на сокети. Смесването на трите дава табла, които изглеждат последователни и не са.

За простия случай да решите кои приложения използват тунела, поддържаните списъци на разрешени и забранени приложения в Android вече изразяват политиката без никакво изстъргване. Запазете атрибуцията за отделен поток за случаите, които наистина се нуждаят от нея, и предвидете бюджет за поддържана реализация, която обработва посоката, разрешенията и неизвестните състояния заедно.

Изберете инструмента по задачата, после тествайте ръбовете

Ако изискването е избор на тунел, използвайте списъка на платформата. Ако е жива атрибуция, използвайте заявката за притежател и третирайте всяка двойка без отговор като неизвестна. Ако са исторически суми, използвайте stats API. След това тествайте трите ръба, които чупят наивните реализации: пакет в обратна посока, поток, който се затваря преди заявката, и извикващ, който не е активният VPN.

Нищо от това не изисква изключване на SELinux, root на телефона или глобално четим таблици на ядрото. Ограничението е смисълът: платформата премести атрибуцията зад договор, за да е отговорът достъпен за компонента, който законно се нуждае от него, и за никого другиго.

Подобни статии