บน Android 10 และใหม่กว่า แอปที่เปิด /proc/net/tcp จะได้รับการปฏิเสธการเข้าถึงแทนตาราง socket อินเทอร์เฟซ VPN ไม่สนใจ เพราะแพ็กเก็ตยังมาถึงอยู่ เนื่องจากการอุโมงค์เป็นการตัดสินใจเรื่องเส้นทาง ไม่ใช่สิทธิ์ของไฟล์ สิ่งที่เปลี่ยนไปคือคำตอบของคำถามที่แคบลง: “แอปใดเป็นเจ้าของการเชื่อมต่อนี้?” ตัวแทนคือ ConnectivityManager.getConnectionOwnerUid, คำค้นต่อการเชื่อมต่อที่รองรับ และการเลือกระหว่างนิสัยเดิมกับ API ใหม่ขึ้นอยู่กับว่างานใดในสามอย่างที่คุณมีจริง: การรับแพ็กเก็ต การระบุที่มาของโฟลว์ หรือการรวมปริมาณการใช้งาน

สองวิธีในการตอบคำถามเรื่องเจ้าของ

ก่อน API 29 กระบวนการที่ได้รับสิทธิ์พิเศษหรือ root สามารถอ่านตาราง socket ของเคอร์เนล จับคู่สี่ส่วน และรู้ 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

การเปรียบเทียบไม่ใช่ “เก่ากับใหม่” แต่เป็นสามงานที่แตกต่างกัน ซึ่งเคยได้รับการตอบสนองจากไฟล์ที่อ่านได้ไฟล์เดียว และตอนนี้ได้รับการตอบสนองจากอินเทอร์เฟซสามแบบที่มีสัญญาสามแบบ

ที่อยู่ภายในคือปลายทางของแอป

ลายเซ็นของเมธอดรับโปรโตคอล ที่อยู่ socket ภายใน และที่อยู่ socket ระยะไกล “ภายใน” หมายถึงฝั่งแอปในการเชื่อมต่อ ไม่ใช่ปลายทางที่บังเอิญเป็นแหล่งของแพ็กเก็ตในขณะที่คุณตรวจสอบ แพ็กเก็ตที่เดินทางจากเซิร์ฟเวอร์ระยะไกลกลับมาที่แอปมีแหล่งและปลายทางสลับกันเมื่อเทียบกับคำจำกัดความนั้น

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

ข้อผิดพลาดเรื่องทิศทางนี้เป็นสาเหตุที่พบบ่อยที่สุดที่คำค้นที่ทำงานได้ให้คำตอบผิด หรือไม่ให้เลย บันทึกทั้งสองปลายก่อนเรียก และยืนยันคู่กับการเชื่อมต่อที่แอปเปิดจริง ครอบครัวที่อยู่ก็สำคัญ ที่อยู่ที่แมปผ่าน IPv4 และที่อยู่ IPv6 แบบเนทีฟเป็น tuple ที่ต่างกัน รวมถึงโฟลว์ TCP และ UDP บนพอร์ตเดียวกัน

การอนุญาตและผลลัพธ์ที่ไม่ทราบเป็นส่วนหนึ่งของสัญญา

คำค้นจำกัดเฉพาะผู้เรียกที่ได้รับอนุญาต รวมถึงแอป VPN ที่ใช้งานอยู่สำหรับผู้ใช้ที่เกี่ยวข้อง แอปตรวจสอบที่ไม่เกี่ยวข้องไม่สามารถถือว่าสามารถแจกแจงการเชื่อมต่อของโปรไฟล์อื่นได้ และการเรียกที่ไม่ได้รับอนุญาตอาจโยนข้อยกเว้นด้านความปลอดภัยแทนที่จะคืนข้อผิดพลาดที่เป็นมิตร จัดการทั้งสองผลลัพธ์อย่างชัดเจน

เจ้าของที่หายไปไม่ใช่ชื่อแพ็กเกจ โฟลว์อาจปิดไประหว่างแพ็กเก็ตกับคำค้น tuple อาจไม่สมบูรณ์ หรือผู้เรียกอาจไม่ได้รับอนุญาตให้รู้ การแคชคำตอบที่สำเร็จครั้งแรกแล้วนำกลับมาใช้ใหม่ภายหลังจะระบุการเชื่อมต่อใหม่ให้แอปเก่า ซึ่งแย่กว่าการรายงานว่าไม่ทราบ

เมื่อการระบุที่มาล้มเหลว ทราฟฟิกยังต้องมีการตัดสินใจ: บล็อกในที่ที่นโยบายกำหนด อนุญาตเฉพาะขอบเขตที่คุณเข้าใจ หรือเปิดเผยช่องว่าง ส่วนสำรองนั้นเป็นของการออกแบบ ไม่ใช่ความคิดเห็น

การบัญชีเป็นอีกคำถามหนึ่ง

NetworkStatsManager รายงานการใช้งานภายใต้กฎการเข้าถึงของตัวเอง มันบอกได้ว่า UID หนึ่งถ่ายโอนเท่าใดในช่วงเวลาหนึ่ง แต่บอกเจ้าของแพ็กเก็ตที่มีชีวิตตามอำเภอใจไม่ได้ ความสามารถของเครือข่ายอธิบายคุณสมบัติของเครือข่าย ไม่ใช่ไดเรกทอรีของ socket การผสมทั้งสามจะสร้างแดชบอร์ดที่ดูสอดคล้องแต่ไม่ใช่

สำหรับกรณีง่าย ๆ ในการตัดสินว่าแอปใดใช้การอุโมงค์ รายการแอปที่อนุญาตและไม่อนุญาตที่รองรับของ Android ได้แสดงนโยบายอยู่แล้วโดยไม่ต้องดึงข้อมูลใด ๆ เก็บการระบุที่มาต่อโฟลว์ไว้สำหรับกรณีที่ต้องการจริง ๆ และกันงบประมาณสำหรับการนำไปใช้ที่ดูแลรักษาซึ่งจัดการทิศทาง สิทธิ์ และสถานะที่ไม่ทราบร่วมกัน

เลือกเครื่องมือตามงาน แล้วทดสอบขอบ

หากข้อกำหนดคือการเลือกอุโมงค์ ให้ใช้รายการของแพลตฟอร์ม หากเป็นการระบุที่มาแบบสด ให้ใช้คำค้นหาเจ้าของและถือทุก tuple ที่ตอบไม่ได้ว่าไม่ทราบ หากเป็นยอดรวมย้อนหลัง ให้ใช้ stats API จากนั้นทดสอบสามขอบที่ทำให้การนำไปใช้แบบง่าย ๆ พัง: แพ็กเก็ตที่สวนทาง โฟลว์ที่ปิดก่อนคุณถาม และผู้เรียกที่ไม่ใช่ VPN ที่ใช้งานอยู่

ไม่มีสิ่งใดในนี้ที่ต้องปิด SELinux ทำ root โทรศัพท์ หรือทำให้ตารางเคอร์เนลอ่านได้ทั่วโลก ข้อจำกัดคือประเด็น: แพลตฟอร์มย้ายการระบุที่มาไปไว้หลังสัญญา เพื่อให้คำตอบมีให้กับองค์ประกอบที่ต้องการอย่างชอบธรรมเท่านั้น และไม่มีให้ใครอื่น

บทความที่เกี่ยวข้อง