یک فرمان 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 کار را پاک بازنشسته می‌کند. شبح پایشی که بارگذاری‌شده رها شود، همان راهی است که یک ترمیم یک‌باره ماه‌ها بعد به مسیری توضیح‌نشده تبدیل می‌شود.

مقالات مرتبط