# Прямая маршрутизация macOS на RU

### Ручное переключение
```Python
sudo tee /usr/local/sbin/direct-routes >/dev/null <<'ZSH'
#!/bin/zsh

set -euo pipefail

WIFI_IF=$(
  /usr/sbin/networksetup -listallhardwareports |
  awk '
    /Hardware Port: (Wi-Fi|AirPort)/ {
      getline
      print $2
      exit
    }
  '
)

if [[ -z "$WIFI_IF" ]]; then
  echo "Ошибка: Wi-Fi-интерфейс не найден"
  exit 1
fi

GW=$(/usr/sbin/ipconfig getoption "$WIFI_IF" router 2>/dev/null || true)

if [[ -z "$GW" ]]; then
  GW=$(
    /usr/sbin/netstat -rn -f inet |
    awk -v iface="$WIFI_IF" '
      $1 == "default" &&
      $2 ~ /^[0-9]+\./ &&
      $NF == iface {
        print $2
        exit
      }
    '
  )
fi

if [[ -z "$GW" ]]; then
  echo "Ошибка: не удалось определить шлюз для $WIFI_IF"
  exit 1
fi

echo "Wi-Fi-интерфейс: $WIFI_IF"
echo "Текущий шлюз: $GW"

normalize_network() {
  /usr/bin/python3 - "$1" <<'PY'
import ipaddress
import sys

try:
    print(ipaddress.ip_network(sys.argv[1], strict=False))
except ValueError as error:
    print(f"Ошибка CIDR: {error}", file=sys.stderr)
    sys.exit(1)
PY
}

for INPUT_NET in "$@"; do
  NET=$(normalize_network "$INPUT_NET") || {
    echo "Пропускаю некорректную сеть: $INPUT_NET"
    continue
  }

  if [[ "$NET" != "$INPUT_NET" ]]; then
    echo "Нормализация: $INPUT_NET → $NET"
  fi

  # Удаляем старый scoped-маршрут, созданный предыдущей версией.
  /sbin/route -n delete \
    -net \
    -ifscope "$WIFI_IF" \
    "$NET" "$GW" \
    >/dev/null 2>&1 || true

  # Удаляем существующий обычный маршрут.
  /sbin/route -n delete \
    -net \
    "$NET" "$GW" \
    >/dev/null 2>&1 || true

  echo "Добавляю: $NET → $GW"

  # Важно: здесь намеренно нет -ifscope.
  /sbin/route -n add \
    -net \
    -static \
    "$NET" "$GW"
done

echo
echo "Маршруты через $GW:"
/usr/sbin/netstat -rn -f inet |
awk -v gw="$GW" '$2 == gw {print}'
ZSH

sudo chmod 755 /usr/local/sbin/direct-routes
```

```Python
sudo /usr/local/sbin/direct-routes \
  95.173.0.0/16 \
  31.44.0.0/16 \
  46.243.0.0/16 \
  87.250.0.0/16 \
  87.240.0.0/16 \
  77.88.0.0/16 \
  213.59.0.0/16 \
  109.207.0.0/16 \
  153.43.0.0/16 \
  194.67.0.0/16 \
  161.104.49.0/17
```

### Автомат
```Python
route -n get buildin.ai |
egrep 'destination|gateway|interface'
```

```Python
sudo mkdir -p /usr/local/etc

sudo tee /usr/local/etc/direct-routes.txt >/dev/null <<'EOF'
95.173.0.0/16
31.44.0.0/16
46.243.0.0/16
87.250.0.0/16
87.240.0.0/16
77.88.0.0/16
213.59.0.0/16
109.207.0.0/16
153.43.0.0/16
194.67.0.0/16
163.171.0.0/16
140.150.0.0/16
87.249.0.0/16
77.105.0.0/16
185.178.0.0/16
213.85.0.0/16
85.119.0.0/16
5.188.0.0/16
46.243.185.206/32
EOF
```

```Python
sudo nano /usr/local/etc/direct-routes.txt
```

```Python
sudo tee /usr/local/sbin/direct-routes-auto >/dev/null <<'ZSH'
#!/bin/zsh

set -u

ROUTES_FILE="/usr/local/etc/direct-routes.txt"
APPLY_SCRIPT="/usr/local/sbin/direct-routes"
STATE_FILE="/var/run/direct-routes-auto.state"

[[ -r "$ROUTES_FILE" ]] || exit 0
[[ -x "$APPLY_SCRIPT" ]] || exit 0

# Определяем физический Wi-Fi-интерфейс, обычно en0.
WIFI_IF=$(
  /usr/sbin/networksetup -listallhardwareports |
  /usr/bin/awk '
    /Hardware Port: (Wi-Fi|AirPort)/ {
      getline
      print $2
      exit
    }
  '
)

[[ -n "$WIFI_IF" ]] || exit 0

# Получаем шлюз текущей Wi-Fi-сети.
GW=$(
  /usr/sbin/ipconfig getoption "$WIFI_IF" router 2>/dev/null ||
  true
)

# Резервный способ определения шлюза.
if [[ -z "$GW" ]]; then
  GW=$(
    /usr/sbin/netstat -rn -f inet |
    /usr/bin/awk -v iface="$WIFI_IF" '
      $1 == "default" &&
      $2 ~ /^[0-9]+\./ &&
      $NF == iface {
        print $2
        exit
      }
    '
  )
fi

# Если Wi-Fi сейчас не подключён, ждём следующего запуска.
[[ -n "$GW" ]] || exit 0

ROUTES=()

while IFS= read -r NET; do
  ROUTES+=("$NET")
done < <(
  /usr/bin/awk '
    {
      sub(/#.*/, "")
      gsub(/^[[:space:]]+|[[:space:]]+$/, "")
      if ($0 != "") print
    }
  ' "$ROUTES_FILE"
)

(( ${#ROUTES[@]} > 0 )) || exit 0

# Сигнатура текущего интерфейса, шлюза и списка маршрутов.
FILE_STATE=$(
  /usr/bin/cksum "$ROUTES_FILE" |
  /usr/bin/awk '{print $1 ":" $2}'
)

NEW_STATE="${WIFI_IF}|${GW}|${FILE_STATE}"
OLD_STATE=""

[[ -r "$STATE_FILE" ]] && OLD_STATE="$(<"$STATE_FILE")"

# Проверяем фактический маршрут к первой сети.
PROBE_IP="${ROUTES[1]%/*}"

ROUTE_DATA=$(
  /sbin/route -n get "$PROBE_IP" 2>/dev/null ||
  true
)

CURRENT_GW=$(
  print -r -- "$ROUTE_DATA" |
  /usr/bin/awk '/gateway:/ {print $2; exit}'
)

CURRENT_IF=$(
  print -r -- "$ROUTE_DATA" |
  /usr/bin/awk '/interface:/ {print $2; exit}'
)

# Ничего не делаем, если сеть не изменилась и маршрут на месте.
if [[ "$NEW_STATE" == "$OLD_STATE" &&
      "$CURRENT_GW" == "$GW" &&
      "$CURRENT_IF" == "$WIFI_IF" ]]; then
  exit 0
fi

echo "$(date '+%Y-%m-%d %H:%M:%S') Переприменяю маршруты: $WIFI_IF → $GW"

# После смены Wi-Fi даём DHCP и таблице маршрутов стабилизироваться.
sleep 2

if "$APPLY_SCRIPT" "${ROUTES[@]}"; then
  print -r -- "$NEW_STATE" > "$STATE_FILE"
  echo "$(date '+%Y-%m-%d %H:%M:%S') Маршруты успешно применены"
else
  echo "$(date '+%Y-%m-%d %H:%M:%S') Ошибка применения маршрутов" >&2
  exit 1
fi
ZSH

sudo chown root:wheel /usr/local/sbin/direct-routes-auto
sudo chmod 755 /usr/local/sbin/direct-routes-auto
```

```Python
sudo tee /Library/LaunchDaemons/com.local.direct-routes-auto.plist >/dev/null <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.local.direct-routes-auto</string>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/sbin/direct-routes-auto</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>StartInterval</key>
    <integer>10</integer>

    <key>ProcessType</key>
    <string>Background</string>

    <key>StandardOutPath</key>
    <string>/var/log/direct-routes-auto.log</string>

    <key>StandardErrorPath</key>
    <string>/var/log/direct-routes-auto-error.log</string>
</dict>
</plist>
PLIST

sudo chown root:wheel \
  /Library/LaunchDaemons/com.local.direct-routes-auto.plist

sudo chmod 644 \
  /Library/LaunchDaemons/com.local.direct-routes-auto.plist
```

```Python
sudo launchctl bootout system \
  /Library/LaunchDaemons/com.local.direct-routes-auto.plist \
  2>/dev/null || true

sudo launchctl bootstrap system \
  /Library/LaunchDaemons/com.local.direct-routes-auto.plist

sudo launchctl kickstart -k \
  system/com.local.direct-routes-auto
```

```Python
sudo launchctl bootout system \
  /Library/LaunchDaemons/com.local.direct-routes-auto.plist

sudo rm \
  /Library/LaunchDaemons/com.local.direct-routes-auto.plist

sudo rm /var/run/direct-routes-auto.state 2>/dev/null || true
```

```Python
route -n get 95.173.136.162 |
egrep 'destination|gateway|interface'
```

```Python
sudo /usr/local/sbin/direct-routes-auto
```

обновление: скрипт с постоянной проверкой:

[file](https://notes.vtorum.net/api/public/web/%D0%BF%D1%80%D1%8F%D0%BC%D0%B0%D1%8F-%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F-macos-%D0%BD%D0%B0-ru-0504e25a/assets/3ae29e68-7a48-4166-a397-21c422eedf2c/raw)

```Python
chmod +x install-direct-routes-watchdog.sh
./install-direct-routes-watchdog.sh
```

журнал:

```Python
tail -f /var/log/direct-routes-auto.log
```

ошибки:

```Python
tail -f /var/log/direct-routes-auto-error.log
```
