שום דבר ב-VpnService לא לוכד תעבורה מעצמו. הוא מבקש מהמערכת ממשק tun, מוסר לממשק הזה קבוצה של כתובות ומסלולים, ומרגע זה הגרעין מעתיק כל מנה שמתאימה למסלולים האלה לתיאור קובץ שהאפליקציה יכולה לקרוא. הקוד של האפליקציה לעולם לא רואה את המנות שהמסלולים לא בחרו, ולכן היקף המנהרה הוא שאלה של טבלת ניתוב לפני שהוא שאלה של תכנות. לכן אותו מימוש שירות יכול לבלוע כל זרימה בטלפון אחד ורק חלק באחר, ולכן הוספת מסלול לפעמים מתקנת מנהרה שנראתה שבורה. מה שאתה מקבל הוא מה שהצהרת, פחות כל מה שהבונה סירב בשקט.

המסלולים קובעים אילו מנות מגיעות

Builder.addRoute(prefix, prefixLength) מתקין קידומת בטבלת הניתוב של רשת ה-VPN. המערכת מתייחסת לרשת הזו כברירת מחדל עבור האפליקציות בהיקף, ולכן הרשומות שלה נבדקות לפני מסלול ברירת המחדל של הרשת הפיזית וקידומת תואמת מספיקה כדי להסיט זרימה. לכן מסלול עבור 0.0.0.0/0 לוקח את IPv4 בשלמותו, כולל המדפסת, יעד ה-cast ועמוד הניהול של הנתב.

לקוחות שרוצים את הכיסוי הזה בלי למחוק את רשומת ברירת המחדל הפיזית משתמשים במקום בזוג 0.0.0.0/1 ו-128.0.0.0/1. שני מסלולים ספציפיים יותר מנצחים מסלול ברירת מחדל, יחד הם מכסים את כל מרחב הכתובות, ורשומת ברירת המחדל של הטלפון עצמו נשארת בטבלה עבור השקעים שעדיין קשורים לרשת הפיזית.

כתובות ורשומות DNS מגיעות מאותו בונה ועונות על שאלה אחרת. addAddress נותן לממשק כתובת עם אורך קידומת, ו-addDnsServer מתקין פותר שהאפליקציות בהיקף ישאלו דרכו דרך הממשק. שתי המשפחות יכולות לשבת על ממשק אחד, ולכן קבוצת מסלולים של IPv4 בלבד לצד כתובת IPv6 היא תצורה שנקראת נכון ובכל זאת מדליפה. אם אתה מנתב משפחה שאין לממשק כתובת בה, לגרעין אין כתובת מקור עבור המנות האלה והן לעולם לא מופיעות בתיאור.

מה establish() מחזיר

establish() מחזיר ParcelFileDescriptor, או null כשהאפליקציה אינה מורשית. null הוא מצב הסכמה ולא שגיאת רשת: VpnService.prepare() כבר היה צריך להחזיר null, או שהמשתמש היה צריך לאשר את דיאלוג המערכת לפני הקריאה. אפשר לבטל הסכמה בזמן שהשירות רץ, והממשק נסגר מתחתיך בלי שום קריאה חוזרת ללולאת הקריאה שלך.

התיאור אינו חוסם אלא אם תאמר אחרת, ולכן קריאה חוזרת מיד בלי נתונים במקום להחנות את התהליכון. לולאה שנכתבה לשקע חוסם שורפת ליבה, ולולאה שמתייחסת לקריאה ריקה כקטלנית קורעת את המנהרה בתוך שנייה סרק. Builder.setBlocking(true) משנה את ההתנהגות הזו, והמחיר הוא תהליכון שממתין בתוך הקריאה.

ארגומנטים של מסלול וכתובת מאומתים כערכי LinkAddress, ויעדי multicast כמו 224.0.0.0/4 נדחים על הסף. לממשק tun מנקודה לנקודה גם אין תחום שידור, ולכן גילוי multicast דרכו דורש תכנון אחר ולא עוד מסלול. מדריך VpnService.Builder הוא הרשימה שכדאי לבדוק לפני שמסיקים שקריאה התעלמה.

ההיקף לפי אפליקציה מצמצם את הקבוצה, לא את המנהרה

מ-API 21 הבונה מקבל addAllowedApplication ו-addDisallowedApplication, ושתי הרשימות סותרות זו את זו בתוך בונה אחד. רשימת היתר מעמידה את המנהרה עבור המנות האלה בעוד כל השאר שומר על הרשת הפיזית; רשימת איסור לוכדת הכול מלבד המנות הנקובות, שזה מה שלקוח מציע כפיצול מנהרה. לפני API 21 אין מנגנון פלטפורמה בכלל, וההתנהגות לפי אפליקציה באה מלקוחות צד שלישי שסיננו לפי UID בתוך המעביר שלהם. ההבחנה חשובה בניפוי, כי מנהרה לפי אפליקציה עם רשימת היתר של מנה אחת כמעט אינה מייצרת תעבורה וזו התנהגות נכונה, לא מסלול שבור.

יהיה ההיקף אשר יהיה, השקע שהאפליקציה שלך פותחת לשרת ה-VPN לא צריך לעבור דרך המנהרה שהיא בונה. VpnService.protect(socket) קושר את השקע הזה לרשת שמתחת כדי שהמנות שלו ידלגו על התקן tun. אם תדלג על הקריאה, לחיצת היד נבלעת בממשק שאמור היה להעלות אותה.

VpnService.setUnderlyingNetworks סוגר את המעגל בכך שהוא אומר למערכת איזו רשת פיזית נושאת את המנהרה. הערך הזה קובע איך ה-VPN מדווח על קישוריות לאפליקציות אחרות, וערך null או מיושן הוא סיבה שגרתית למנהרה מחוברת ששאר הטלפון מתייחס אליה כאל לא מחוברת. עדכן אותו כשהרשת שמתחת משתנה, לא רק באתחול.

סכומי בדיקה ו-MTU: איפה מסלול נכון עדיין נכשל

לאחר שהמנות מגיעות, שני פרטים ברמת המנה קובעים אם משהו עובד מקצה לקצה. הראשון הוא כתובת המקור: עם כמה כתובות ממשק הגרעין עשוי לפלוט מנות שנושאות את הראשונה, ולכן מעביר שמשכתב כותרות חייב לתקן את הכתובת ואת סכום הבדיקה שמכסה אותה.

השני הוא היקף סכום הבדיקה. כשהאפליקציה בונה בעצמה מנות TCP או UDP, סכום הבדיקה מכסה כותרת מדומה שמחזיקה את כתובת המקור, כתובת היעד, מספר הפרוטוקול ואורך התעבורה, והשמטת כל אחד מהם נותנת מנות שמגיעות ונזרקות. הכשל הזה נראה כמו פסק זמן אחרי תשובה שלעולם לא מגיעה, מה שמחזיר את רוב האנשים למסלולים. setMtu קובע את ה-MTU של הממשק, וכל קריאה מחזירה בדיוק מנת IP שלמה אחת, ולכן חוצץ שנכתב לזרם בתים יפצל את הפענוח שלך ולא את המנה.

מעבר אימות שמפריד בין השכבות

כדי למצוא איפה ההיקף סטה, בדוק את השכבות לפי הסדר. שאל את ההתקן אילו מסלולים רשת ה-VPN באמת מחזיקה, ואז בדוק אם זרימה לכתובת מוכרת מופיעה כקריאה בתיאור, ואז בדוק אם המנה שהמעביר שלך פולט מעוצבת היטב. בעיית מסלול לא מציגה דבר בתיאור, ואילו בעיית סכום בדיקה מציגה מנה שמגיעה ונעלמת.

אם דבר לא מגיע לתיאור, החשוד הוא קבוצת המסלולים ולא הקורא: אמת את הקידומות, אמת שיש כתובת באותה משפחה, ואמת שהאפליקציה נמצאת בהיקף שהגדרת. אם מנות מגיעות לתיאור אבל דבר לא חוזר, בדוק את protect() ואת ערך הרשת שמתחת לפני שאתה נוגע שוב במסלול. אם הצד הרחוק מדווח על מנות פגומות, סכום הבדיקה וה-MTU הם המקומות להסתכל בהם. בדיקה אחת לכל שכבה חוסכת אחר צהריים של ניחושים.

מאמרים קשורים