Last active
March 17, 2026 13:49
-
-
Save lixin9311/1c692470e189eb615f3e2524d2e66ca5 to your computer and use it in GitHub Desktop.
Enable V6 Plus Static IP on UDM.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # Configure JPIX "v6 Plus" Fixed IP service (IPIP). | |
| # Intended for unattended use (e.g., crontab). | |
| # | |
| # Use --dry-run to preview the changes | |
| # | |
| # Prerequisites: System must already have MAP-E JPIX configured. | |
| # | |
| # Note: Weekly scheduled backup could revert the network settings. | |
| # Disable the backup to allow the change to persist. | |
| # Or use the provided systemd service to persist the settings. | |
| set -eu | |
| # ── Parameters ──────────────────────────────────────────────────────── | |
| USERNAME="kotei0123456" | |
| PASSWORD="p1a2s3s4" | |
| IPV4_CIDR="8.8.8.8/32" # static IPv4 in CIDR notation | |
| IPV6_IID="::1:2:3:4" # IPv6 Interface Identifier (lower 64 bits) | |
| IPV6_REMOTE="2404:9200:225:100::65" # tunnel remote endpoint (IPv6 or hostname) | |
| STATE_FILE="/data/udapi-config/ubios-udapi-server/ubios-udapi-server.state" | |
| WORKING_FILE="${STATE_FILE}.modified" | |
| # ────────────────────────────────────────────────────────────────────── | |
| DRY_RUN=false | |
| case "${1:-}" in --dry-run) DRY_RUN=true ;; esac | |
| # Split CIDR into address and prefix length. | |
| IPV4_ADDR="${IPV4_CIDR%/*}" | |
| IPV4_PREFIX="${IPV4_CIDR#*/}" | |
| # Create working copy. | |
| cp -p "$STATE_FILE" "$WORKING_FILE" | |
| # Build the serverName payload: IID|IPv6_Remote|IPv4_Address|IPv4_Prefix | |
| SERVER_NAME="${IPV6_IID}|${IPV6_REMOTE}|${IPV4_ADDR}|${IPV4_PREFIX}" | |
| # Apply modifications with jq: | |
| # 1. Change capability from map_e_jpix → ipip_jpix | |
| # 2. Add authentication block with serverName encoding | |
| # 3. Remove 192.0.0.x addresses from all interfaces | |
| jq --indent 1 \ | |
| --arg user "$USERNAME" \ | |
| --arg pass "$PASSWORD" \ | |
| --arg sname "$SERVER_NAME" \ | |
| ' | |
| .interfaces |= [ | |
| .[] | | |
| # Update hb46pp capability and add authentication | |
| if .ipv6.hb46pp.capability == "map_e_jpix" then | |
| .ipv6.hb46pp.capability = "ipip_jpix" | |
| | .ipv6.hb46pp.authentication = { | |
| username: $user, | |
| password: $pass, | |
| serverName: $sname | |
| } | |
| else . end | |
| | | |
| # Remove 192.0.0.x addresses | |
| if .addresses then | |
| .addresses |= [ .[] | select(.cidr == null or (.cidr | startswith("192.0.0.") | not)) ] | |
| else . end | |
| ] | |
| ' "$WORKING_FILE" > "${WORKING_FILE}.tmp" && mv "${WORKING_FILE}.tmp" "$WORKING_FILE" | |
| # Dry run: show diff and exit without applying. | |
| if [ "$DRY_RUN" = true ]; then | |
| diff -u "$STATE_FILE" "$WORKING_FILE" || true | |
| rm -f "$WORKING_FILE" | |
| exit 0 | |
| fi | |
| # Apply changes. | |
| ubios-udapi-client put /system/ubios/udm/configuration "@${WORKING_FILE}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # install.sh | |
| # Run this on your UniFi router to deploy the network watcher service. | |
| # It will copy network scripts to /data/scripts. | |
| # Network watcher service will watch for network configuration changes. | |
| # On changes, it will run on-network-change.sh, which will run the configure.sh to restore static IP settings. | |
| # Usage: | |
| # 1. download all scripts into the same folder | |
| # 2. grant executable permission: chmod +x *.sh | |
| # 3. fill in your IPv4 connections details inside the Parameters section of configure.sh | |
| # 4. run configure.sh once to apply the settings for the first time | |
| # 5. run install.sh to install the watcher service | |
| # 6. Change some settings (assign a static ip to a client device etc.) | |
| # 7. Check if the static IP setting is persisted, check the logs: journalctl -u network-watcher | |
| set -euo pipefail | |
| SCRIPT_DIR="/data/scripts" | |
| SERVICE_FILE="/etc/systemd/system/network-watcher.service" | |
| echo "=== Network Watcher Installer ===" | |
| # 1. Install inotify-tools if missing | |
| if ! command -v inotifywait &> /dev/null; then | |
| echo "[1/4] Installing inotify-tools..." | |
| apt-get update -qq && apt-get install -y -qq inotify-tools | |
| else | |
| echo "[1/4] inotify-tools already installed" | |
| fi | |
| # 2. Deploy scripts | |
| echo "[2/4] Deploying scripts to ${SCRIPT_DIR}/" | |
| mkdir -p "$SCRIPT_DIR" | |
| cp network-watcher.sh "$SCRIPT_DIR/" | |
| cp on-network-change.sh "$SCRIPT_DIR/" | |
| cp configure.sh "$SCRIPT_DIR/" | |
| chmod +x "$SCRIPT_DIR"/*.sh | |
| # 3. Install systemd service | |
| echo "[3/4] Installing systemd service..." | |
| cp network-watcher.service "$SERVICE_FILE" | |
| systemctl daemon-reload | |
| systemctl enable network-watcher.service | |
| # 4. Start the service | |
| echo "[4/4] Starting network-watcher..." | |
| systemctl restart network-watcher.service | |
| echo "" | |
| echo "=== Done ===" | |
| echo "" | |
| echo "Useful commands:" | |
| echo " systemctl status network-watcher # Check service status" | |
| echo " journalctl -fu network-watcher # Follow logs live" | |
| echo " journalctl -u network-watcher --since '1 hour ago' # Recent logs" | |
| echo "" | |
| echo "Edit your custom logic in: ${SCRIPT_DIR}/on-network-change.sh" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Unit] | |
| Description=Watch ubios-udapi-server state for network changes | |
| After=network.target unifi-core.service udapi-server.service ubntconf.service | |
| Wants=network.target | |
| [Service] | |
| Type=simple | |
| ExecStartPre=/bin/sh -c 'command -v inotifywait >/dev/null 2>&1 || { echo "inotifywait not found. Install with: apt-get install -y inotify-tools"; exit 1; }' | |
| ExecStart=/data/scripts/network-watcher.sh | |
| Restart=on-failure | |
| RestartSec=10 | |
| StartLimitIntervalSec=120 | |
| StartLimitBurst=3 | |
| StandardOutput=journal | |
| StandardError=journal | |
| SyslogIdentifier=network-watcher | |
| [Install] | |
| WantedBy=multi-user.target |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # /data/scripts/network-watcher.sh | |
| # Watches the ubios-udapi-server state file for changes and triggers | |
| # the on-network-change.sh script when modifications are detected. | |
| set -euo pipefail | |
| STATE_FILE_ORIG="/data/udapi-config/ubios-udapi-server/ubios-udapi-server.state" | |
| STATE_FILE="$(readlink -f "$STATE_FILE_ORIG")" | |
| CHANGE_SCRIPT="/data/scripts/on-network-change.sh" | |
| log() { | |
| logger -t "network-watcher" "$1" | |
| echo "$(date '+%Y-%m-%d %H:%M:%S') [network-watcher] $1" | |
| } | |
| log "Starting network watcher (PID: $$)" | |
| log "Original path: ${STATE_FILE_ORIG}" | |
| log "Resolved path: ${STATE_FILE}" | |
| # Wait for the state file to exist (may not be ready immediately after boot) | |
| if [ ! -f "$STATE_FILE" ]; then | |
| log "State file not found, waiting..." | |
| while [ ! -f "$STATE_FILE" ]; do | |
| sleep 5 | |
| done | |
| log "State file found, proceeding" | |
| fi | |
| # Verify inotifywait is available | |
| if ! command -v inotifywait &> /dev/null; then | |
| log "ERROR: inotifywait not found. Install with: apt-get install -y inotify-tools" | |
| exit 1 | |
| fi | |
| # Verify change script exists and is executable | |
| if [ ! -x "$CHANGE_SCRIPT" ]; then | |
| log "ERROR: ${CHANGE_SCRIPT} not found or not executable" | |
| exit 1 | |
| fi | |
| STATE_DIR="$(dirname "$STATE_FILE")" | |
| STATE_BASENAME="$(basename "$STATE_FILE")" | |
| log "Entering watch loop (watching directory: ${STATE_DIR})" | |
| inotifywait -m -e moved_to,create,modify,close_write --format '%T %e %f' --timefmt '%Y-%m-%dT%H:%M:%S' "$STATE_DIR" | | |
| while read timestamp event filename; do | |
| if [ "$filename" = "$STATE_BASENAME" ]; then | |
| log "State file changed: event=${event} file=${filename} at ${timestamp}" | |
| "$CHANGE_SCRIPT" & | |
| fi | |
| done | |
| # If inotifywait exits unexpectedly | |
| log "ERROR: inotifywait exited unexpectedly" | |
| exit 1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # /data/scripts/on-network-change.sh | |
| # Called by network-watcher.sh when the ubios state file changes. | |
| # Includes debounce logic to avoid rapid-fire execution. | |
| set -euo pipefail | |
| LOCKFILE="/tmp/network-change.lock" | |
| DEBOUNCE_SECONDS=5 | |
| log() { | |
| logger -t "network-change" "$1" | |
| echo "$(date '+%Y-%m-%d %H:%M:%S') [network-change] $1" | |
| } | |
| # Debounce: skip if another instance is already running | |
| if [ -f "$LOCKFILE" ]; then | |
| log "Debounce: skipping (another instance running)" | |
| exit 0 | |
| fi | |
| cleanup() { | |
| rm -f "$LOCKFILE" | |
| } | |
| trap cleanup EXIT | |
| touch "$LOCKFILE" | |
| log "Network change detected, waiting ${DEBOUNCE_SECONDS}s debounce..." | |
| sleep "$DEBOUNCE_SECONDS" | |
| log "Executing network change actions" | |
| # ============================================================ | |
| # ADD YOUR CUSTOM LOGIC BELOW | |
| # ============================================================ | |
| STATE_FILE="/data/udapi-config/ubios-udapi-server/ubios-udapi-server.state" | |
| if grep -q "map_e_jpix" "$STATE_FILE"; then | |
| log "Network config reverted to map_e_jpix in state file, running configure.sh" | |
| /data/scripts/configure.sh && \ | |
| log "configure.sh completed successfully" || \ | |
| log "configure.sh failed with exit code $?" | |
| else | |
| log "Network config did not revert to map_e_jpix in state file, skipping" | |
| fi | |
| # ============================================================ | |
| # END CUSTOM LOGIC | |
| # ============================================================ | |
| log "Network change actions completed" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # uninstall.sh | |
| # Removes the network watcher service and scripts from the router. | |
| # Usage: bash uninstall.sh | |
| set -euo pipefail | |
| SERVICE_NAME="network-watcher" | |
| SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" | |
| SCRIPT_DIR="/data/scripts" | |
| echo "=== Network Watcher Uninstaller ===" | |
| # 1. Stop and disable the service | |
| if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then | |
| echo "[1/3] Stopping ${SERVICE_NAME}..." | |
| systemctl stop "$SERVICE_NAME" | |
| else | |
| echo "[1/3] Service not running, skipping stop" | |
| fi | |
| if systemctl is-enabled --quiet "$SERVICE_NAME" 2>/dev/null; then | |
| echo " Disabling ${SERVICE_NAME}..." | |
| systemctl disable "$SERVICE_NAME" | |
| fi | |
| # 2. Remove service file | |
| if [ -f "$SERVICE_FILE" ]; then | |
| echo "[2/3] Removing ${SERVICE_FILE}" | |
| rm -f "$SERVICE_FILE" | |
| systemctl daemon-reload | |
| systemctl reset-failed "$SERVICE_NAME" 2>/dev/null || true | |
| else | |
| echo "[2/3] Service file not found, skipping" | |
| fi | |
| # 3. Remove scripts (but leave configure.sh alone) | |
| echo "[3/3] Removing scripts..." | |
| rm -f "${SCRIPT_DIR}/network-watcher.sh" | |
| rm -f "${SCRIPT_DIR}/on-network-change.sh" | |
| rm -f "${SCRIPT_DIR}/configure.sh" | |
| rm -f "/tmp/network-change.lock" | |
| # Clean up empty dir, but only if nothing else is in there | |
| if [ -d "$SCRIPT_DIR" ] && [ -z "$(ls -A "$SCRIPT_DIR")" ]; then | |
| rmdir "$SCRIPT_DIR" | |
| echo " Removed empty ${SCRIPT_DIR}/" | |
| else | |
| echo " Kept ${SCRIPT_DIR}/ (other files present)" | |
| fi | |
| echo "" | |
| echo "=== Uninstall complete ===" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment