Du lägger ett route-kommando i /etc/ppp/ip-up, återansluter kontorstunneln, och filen körs aldrig. Det är inget fel på skriptet: den anslutningen går inte genom PPP alls. De inbyggda Cisco IPSec- och IKEv2-klienterna startar aldrig pppd, så hook-mappen läses aldrig, medan PPTP och L2TP över IPSec fortfarande går genom den. Ta reda på vilket fall du har och utlös sedan icke-PPP-fallet från ett launchd-bevakningsjobb.

Kontrollera gränssnittet innan du redigerar någon hook

Två kommandon avgör vilken mekanism som kan fungera. Lista de konfigurerade tjänsterna och lista sedan de gränssnitt som finns medan tunneln är uppe. Gränssnittsnamnet är beviset; etiketten i Systeminställningar är det inte.

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

En tjänst som skapar ppp0 kör PPP-hookarna och kommer att köra din fil. En Cisco IPSec- eller IKEv2-tjänst skapar i stället en utun-enhet, ett tunnelgränssnitt utan PPP-lager, så ingenting inuti /etc/ppp kan nå den. När båda typerna dyker upp, kontrollera vilken tjänst du anslöt till. Folk slösar bort en eftermiddag på fel hook genom att läsa klientnamnet.

På PPP-vägen måste filen ägas av root och vara körbar, eftersom pppd kontrollerar båda innan den kör något. install sätter ägare och läge i ett steg.

sudo install -o root -g wheel -m 0755 /dev/null /etc/ppp/ip-up
sudo ls -l /etc/ppp/ip-up

En fil som skapas via Finder eller en grafisk editor hamnar ofta ägd av ditt konto, eller utan körbiten, och misslyckas då lika tyst som den saknade hooken. Lägg ip-down bredvid den nu. root och wheel i ägarkolumnen plus rwxr-xr-x i lägeskolumnen är vad du vill ha.

Låt launchd utlösa skriptet när en tunnel dyker upp

För klienter som aldrig rör PPP måste utlösaren komma någon annanstans ifrån, och systemkonfigurationsarkivet är den ärliga källan: macOS skriver om det vid varje tunnelövergång. Ett launchd-jobb som bevakar den mappen anropas när en tunnel dyker upp eller försvinner.

<?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>

Installera det som en launch-daemon, inte som en agent per användare, eftersom ruttlagning hör till maskinen. bootstrap ersätter den äldre load -w-formen i nuvarande utgåvor. RunAtLoad gör den första körningen observerbar, och ThrottleInterval hindrar en våg av Wi-Fi-anslutningar från att starta skriptet om och om igen.

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

Fällan är att den bevakade mappen ändras även för händelser utan koppling till din VPN: en Wi-Fi-anslutning, en DHCP-förnyelse, en kabel som sätts i. Vakten nedan gör en bullrig utlösare till en användbar hook, och en loggrad förklarar en rutt vid en udda timme.

Ägandet omfattar plist likaväl som skriptet. En daemon som kör en fil i en mapp där ditt eget konto kan skriva ger det kontot root.

Skydda skriptet så att en andra körning inte ändrar något

Skriptet avgör först om tunneln finns och avslutar tyst när den inte gör det. Allt därefter måste vara säkert att upprepa, eftersom utlösaren avfyras mycket oftare än en enda anslutning. Lös gränssnittet vid körning i stället för att hårdkoda ett index som ändras vid nästa anslutning.

#!/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 avslutar med icke-nollstatus när rutten redan finns, så ett skript som behandlar varje icke-nollstatus som fel rapporterar en trasig hook varje gång det gör rätt. netstat-testet ovan gör det fallet till en uttrycklig no-op.

Gränssnittets närvaro är svagare bevis än det ser ut, eftersom andra VPN-klienter och vissa containermiljöer också skapar utun-enheter. Fråga tjänsten själv med scutil --nc status "Office VPN" och kräv det anslutna tillståndet innan du rör routingen. Spara svaret i en loggrad, för en vecka senare är den posten den enda förklaring du har.

Hämta tillbaka gatewayen utan att bryta DNS

När tunneln tar över standardrutten går vanlig surfning ut genom kontoret och den lokala internetvägen slutar fungera. Fånga först den fysiska gränssnittets router och peka sedan standardrutten tillbaka på den. DHCP slår att tolka en tabell som redan innehåller tunnelns egen standardrutt.

PHYS=en0
GW=$(ipconfig getoption "$PHYS" router)
[ -n "$GW" ] && sudo route -n change default "$GW"

Resolverare är ett eget lager med ett eget verktyg, och att laga rutten lagar dem inte. Titta med scutil --dns vad som gäller och rätta sedan det som skapar det: tjänsteordningen och DNS-posterna på den fysiska tjänsten. Att tömma cachen diagnostiserar en uppslagning som hänger efter ett nätverksbyte; det är inget att schemalägga.

En tunnel som överlever ett Wi-Fi-byte medan DNS stannar är ett tredje fel: sudo killall -HUP mDNSResponder får resolveraren att läsa om sin konfiguration. Använd det en gång, för hand.

Bevisa att jobbet avfyras av sig själv

Tvinga fram en körning med kickstart -k innan du litar på bevakningsvägen och läs sedan den exitstatus som jobbet registrerade. En ren exit bevisar att skriptet fungerar; den bevisar inte att utlösaren fungerar. Koppla sedan från och till igen med jobbet laddat, och håll utkik efter en körning du inte bad om.

sudo launchctl kickstart -k system/net.example.tunnel-watch
sudo launchctl print system/net.example.tunnel-watch | grep -E 'last exit code|state ='

Kontrollera scutil --nc status före och efter varje försök. Ett skript som kördes medan tunneln var nere bevisar ingenting om anslutningsvägen.

Nedrivningsgrenen förtjänar samma behandling som anslutningsgrenen: ta bort rutten för hand, låt jobbet köra och bekräfta att den kommer tillbaka. En hook som lägger till en rutt och aldrig tar bort den lämnar en maskin som beter sig illa en vecka senare, utan att något kopplar samman de två händelserna. När båda beteendena håller drar sudo launchctl bootout system/net.example.tunnel-watch tillbaka jobbet rent. En bevakningsdaemon som lämnas laddad är hur en engångsreparation blir en oförklarad rutt månader senare.

Relaterade artiklar