VPN ב-Mac מחובר אבל ip-up לא הופעל: הפעילו מעקב launchd
מנהרות Cisco IPSec ו-IKEv2 לא מפעילות אף פעם את pppd, ולכן /etc/ppp/ip-up נשאר שקט. עקבו אחר תיקיית התצורה ותנו לסקריפט להוכיח את המנהרה.
אתם שמים פקודת route בתוך /etc/ppp/ip-up, מתחברים מחדש למנהרה של המשרד, והקובץ לעולם לא רץ. אין שום בעיה בסקריפט: החיבור הזה לא עובר דרך PPP בכלל. לקוחות Cisco IPSec ו-IKEv2 המובנים לא מפעילים אף פעם את pppd, כך שתיקיית ההוקים לא נקראת לעולם, בעוד PPTP ו-L2TP מעל IPSec עדיין עוברים דרכה. גלו איזה מקרה יש לכם, ואז הפעילו את המקרה שאינו PPP ממשימת מעקב launchd.
בדקו את הממשק לפני שאתם עורכים הוק כלשהו
שתי פקודות מכריעות איזה מנגנון יכול לעבוד. רשמו את השירותים המוגדרים, ואחר כך רשמו את הממשקים שקיימים כשהמנהרה פעילה. שם הממשק הוא הראיה; התווית בהגדרות המערכת אינה.
scutil --nc list
ifconfig -l | tr ' ' '\n' | grep -E '^(utun|ppp)[0-9]+$'
שירות שמפיק ppp0 מריץ את הוקי ה-PPP ויריץ את הקובץ שלכם. שירות Cisco IPSec או IKEv2 מפיק במקום זאת התקן utun, ממשק מנהרה ללא שכבת PPP, כך ששום דבר בתוך /etc/ppp לא יכול להגיע אליו. כששני הסוגים מופיעים, בדקו לאיזה שירות התחברתם. אנשים מבזבזים אחר צהריים על ההוק הלא נכון בגלל קריאת שם הלקוח.
במסלול PPP הקובץ חייב להיות בבעלות root ולהפעיל הרצה, כי pppd בודק את שניהם לפני שהוא מריץ משהו. install קובע בעלים והרשאה בצעד אחד.
sudo install -o root -g wheel -m 0755 /dev/null /etc/ppp/ip-up
sudo ls -l /etc/ppp/ip-up
קובץ שנוצר דרך Finder או עורך גרפי לרוב נשאר בבעלות החשבון שלכם, או בלי סיבית ההרצה, ואז נכשל בשקט בדיוק כמו ההוק החסר. שימו עכשיו את ip-down לצידו. root ו-wheel בעמודת הבעלים בתוספת rwxr-xr-x בעמודת ההרשאה הם מה שאתם רוצים.
תנו ל-launchd להפעיל את הסקריפט כשמנהרה מופיעה
בלקוחות שלעולם לא נוגעים ב-PPP הטריגר חייב לבוא ממקום אחר, ומאגר תצורת המערכת הוא המקור הישר: macOS כותב אותו מחדש בכל מעבר מנהרה. משימת launchd שצופה בתיקייה הזו נקראת כשמנהרה מופיעה או נעלמת.
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>WatchPaths</key>
<array><string>/Library/Preferences/SystemConfiguration</string></array>
<key>Label</key><string>net.example.tunnel-watch</string>
<key>ProgramArguments</key>
<array><string>/bin/sh</string><string>/usr/local/sbin/tunnel-hook</string></array>
<key>RunAtLoad</key><true/>
<key>ThrottleInterval</key><integer>20</integer>
</dict>
</plist>
התקינו אותו כדמון launch, לא כסוכן לכל משתמש, כי תיקון מסלולים שייך למכונה. bootstrap מחליף את הצורה הישנה load -w בגרסאות הנוכחיות. RunAtLoad הופך את ההרצה הראשונה לניתנת לתצפית, ו-ThrottleInterval מונע ממבול של שינויי התחברות ל-Wi-Fi להפעיל את הסקריפט שוב ושוב.
sudo install -o root -g wheel -m 0644 tunnel-watch.plist \
/Library/LaunchDaemons/net.example.tunnel-watch.plist
sudo launchctl bootstrap system \
/Library/LaunchDaemons/net.example.tunnel-watch.plist
המלכודת היא שהתיקייה הנצפית משתנה גם באירועים שאין להם שום קשר ל-VPN שלכם: התחברות ל-Wi-Fi, חידוש DHCP, כבל שנכנס. השומר שלמטה הופך טריגר רועש להוק שמיש, ושורת לוג מסבירה מסלול בשעה מוזרה.
הבעלות מכסה את ה-plist כמו את הסקריפט. דמון שמריץ קובץ בתיקייה שהחשבון שלכם יכול לכתוב בה מעניק לאותו חשבון root.
הגנו על הסקריפט כדי שהרצה שנייה לא תשנה דבר
הסקריפט מחליט קודם אם המנהרה קיימת, ויוצא בשקט כשהיא לא. כל מה שאחרי זה חייב להיות בטוח לחזרה, כי הטריגר נורה הרבה יותר פעמים מחיבור אחד. פתרו את הממשק בזמן ריצה במקום לקודד קשיח אינדקס שמשתנה בחיבור הבא.
#!/bin/sh
set -u
TUN=$(ifconfig -l | tr ' ' '\n' | grep -m1 '^utun[0-9]' || true)
[ -n "$TUN" ] || exit 0
netstat -rn -f inet | grep -q '^10\.42' && exit 0
route -n add -net 10.42.0.0/16 -interface "$TUN"
route add יוצא בסטטוס שאינו אפס כשהמסלול כבר קיים, ולכן סקריפט שמתייחס לכל סטטוס שאינו אפס כאל כשל מדווח על הוק שבור בכל פעם שהוא עושה את הדבר הנכון. הבדיקה עם netstat למעלה הופכת את המקרה הזה ל-no-op מפורש.
נוכחות הממשק היא ראיה חלשה ממה שהיא נראית, כי לקוחות VPN אחרים וכמה סביבות מכולה יוצרים גם הם התקני utun. שאלו את השירות עצמו עם scutil --nc status "Office VPN" ודרשו את המצב המחובר לפני שאתם נוגעים בניתוב. שמרו את התשובה בשורת לוג, כי כעבור שבוע הרשומה הזו תהיה ההסבר היחיד שיהיה לכם.
החזירו את השער בלי לשבור DNS
כשהמנהרה משתלטת על המסלול המוגדר כברירת מחדל, גלישה רגילה יוצאת דרך המשרד ונתיב האינטרנט המקומי מפסיק לעבוד. תפסו קודם את הראוטר של הממשק הפיזי, ואז הפנו את ברירת המחדל חזרה אליו. DHCP עדיף על ניתוח טבלה שכבר מכילה את ברירת המחדל של המנהרה עצמה.
PHYS=en0
GW=$(ipconfig getoption "$PHYS" router)
[ -n "$GW" ] && sudo route -n change default "$GW"
הפותרות הן שכבה נפרדת עם כלי נפרד, ותיקון המסלול לא מתקן אותן. הביטו עם scutil --dns במה שבתוקף, ואז תקנו את מה שיוצר אותו: סדר השירותים ורשומות ה-DNS בשירות הפיזי. ריקון המטמון מאבחן חיפוש שנתקע אחרי החלפת רשת; זה לא משהו לתזמן.
מנהרה ששורדת החלפת Wi-Fi בזמן ש-DNS נתקע היא תקלה שלישית: sudo killall -HUP mDNSResponder גורם לפותר לקרוא שוב את התצורה שלו. השתמשו בו פעם אחת, ביד.
הוכיחו שהמשימה נורית מעצמה
כפו הרצה אחת עם kickstart -k לפני שאתם סומכים על נתיב המעקב, ואז קראו את סטטוס היציאה שהמשימה רשמה. יציאה נקייה מוכיחה שהסקריפט עובד; היא לא מוכיחה שהטריגר עובד. אחר כך התנתקו והתחברו מחדש כשהמשימה טעונה, וצפו בהרצה שלא ביקשתם.
sudo launchctl kickstart -k system/net.example.tunnel-watch
sudo launchctl print system/net.example.tunnel-watch | grep -E 'last exit code|state ='
בדקו את scutil --nc status לפני ואחרי כל ניסיון. סקריפט שרץ כשהמנהרה למטה לא מוכיח דבר על נתיב החיבור.
ענף הפירוק ראוי לאותה התייחסות כמו ענף החיבור: הסירו את המסלול ביד, תנו למשימה לרוץ, ואשרו שהוא חוזר. הוק שמוסיף מסלול ולא מסיר אותו לעולם משאיר מכונה שמתנהגת רע כעבור שבוע, בלי ששום דבר מחבר בין שני האירועים. כששתי ההתנהגויות מחזיקות, sudo launchctl bootout system/net.example.tunnel-watch מוציא את המשימה לגמלאות בצורה נקייה. דמון מעקב שנשאר טעון הוא הדרך שבה תיקון חד-פעמי הופך למסלול לא מוסבר כעבור חודשים.