คุณใส่คำสั่ง route ใน /etc/ppp/ip-up เชื่อมต่ออุโมงค์สำนักงานใหม่ แต่ไฟล์ไม่เคยทำงาน ไม่มีอะไรผิดกับสคริปต์: การเชื่อมต่อนั้นไม่ได้ผ่าน PPP เลย ไคลเอนต์ Cisco IPSec และ IKEv2 ที่มีมาให้ไม่เคยเริ่ม pppd ดังนั้นโฟลเดอร์ hook จึงไม่ถูกอ่าน ขณะที่ PPTP และ L2TP บน IPSec ยังคงผ่านมันไป ระบุว่าคุณอยู่ในกรณีใด แล้วกระตุ้นกรณีที่ไม่ใช่ PPP จากงานเฝ้าดู launchd

ตรวจสอบอินเทอร์เฟซก่อนแก้ไข hook ใด ๆ

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

scutil --nc list
ifconfig -l | tr ' ' '\n' | grep -E '^(utun|ppp)[0-9]+$'

บริการที่สร้าง ppp0 จะรัน hook ของ PPP และจะเรียกใช้ไฟล์ของคุณ บริการ Cisco IPSec หรือ IKEv2 จะสร้างอุปกรณ์ utun แทน ซึ่งเป็นอินเทอร์เฟซอุโมงค์ที่ไม่มีชั้น PPP ดังนั้นจึงไม่มีอะไรใน /etc/ppp เข้าถึงได้ เมื่อพบทั้งสองแบบ ให้ตรวจสอบว่าคุณเชื่อมต่อกับบริการใด ผู้คนเสียเวลาช่วงบ่ายไปกับ hook ผิดเพราะอ่านชื่อไคลเอนต์

บนเส้นทาง 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 หรือโปรแกรมแก้ไขแบบกราฟิกมักกลายเป็นของบัญชีคุณ หรือไม่มีบิตเรียกใช้ แล้วล้มเหลวเงียบ ๆ เหมือน hook ที่หายไป วาง 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 daemon ไม่ใช่เอเจนต์ต่อผู้ใช้ เพราะการซ่อมเส้นทางเป็นของเครื่อง 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 การเสียบสาย ตัวป้องกันด้านล่างเปลี่ยนตัวกระตุ้นที่ส่งเสียงรบกวนให้เป็น hook ที่ใช้งานได้ และบรรทัดบันทึกอธิบายเส้นทางที่เกิดในเวลาประหลาด

ความเป็นเจ้าของครอบคลุม 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 ออกด้วยสถานะไม่เป็นศูนย์เมื่อมีเส้นทางอยู่แล้ว สคริปต์ที่ถือทุกสถานะที่ไม่เป็นศูนย์เป็นความล้มเหลวจึงรายงาน hook ที่เสียทุกครั้งที่ทำสิ่งที่ถูกต้อง การทดสอบ 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 ก่อนและหลังทุกความพยายาม สคริปต์ที่รันขณะอุโมงค์ปิดไม่ได้พิสูจน์อะไรเกี่ยวกับเส้นทางเชื่อมต่อ

สาขาการถอดสมควรได้รับการดูแลเหมือนสาขาการเชื่อมต่อ: ลบเส้นทางด้วยมือ ปล่อยให้งานทำงาน และยืนยันว่ามันกลับมา hook ที่เพิ่มเส้นทางและไม่เคยลบมันทิ้งเครื่องที่ทำงานผิดปกติในอีกหนึ่งสัปดาห์ โดยไม่มีอะไรเชื่อมโยงสองเหตุการณ์ เมื่อทั้งสองพฤติกรรมคงอยู่ sudo launchctl bootout system/net.example.tunnel-watch จะปลดงานอย่างสะอาด เดมอนเฝ้าดูที่ปล่อยให้โหลดค้างไว้คือวิธีที่การซ่อมครั้งเดียวกลายเป็นเส้นทางที่อธิบายไม่ได้ในอีกหลายเดือน

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