#!/bin/sh
# RelayWay soft-router IPv6 leak guard.
#
# The transparent data plane is IPv4/fake-IP based today. If the router keeps
# advertising a real ISP IPv6 prefix to LAN clients, Android/Windows/HarmonyOS
# can prefer IPv6 and bypass the proxy entirely. Keep this script UCI-only so it
# works on old opkg/fw3 routers as well as new fw4/nft routers.

STATE_DIR=/etc/relayway/ipv6-leak-guard
LOGTAG=relayway-ipv6-guard

log() {
    logger -t "$LOGTAG" "$*" 2>/dev/null || true
}

restart_odhcpd() {
    [ -x /etc/init.d/odhcpd ] && /etc/init.d/odhcpd restart 2>/dev/null || true
}

get_relayway_opt() {
    uci -q get "relayway.cored.$1" 2>/dev/null || true
}

guard_enabled() {
    local enabled
    enabled="$(get_relayway_opt ipv6_leak_guard)"
    [ -n "$enabled" ] || enabled=1
    case "$enabled" in
        0|false|FALSE|off|OFF|no|NO|disabled|DISABLED) return 1 ;;
    esac
    return 0
}

lan_section() {
    local section
    section="$(get_relayway_opt ipv6_leak_guard_lan)"
    [ -n "$section" ] || section=lan
    printf '%s\n' "$section"
}

save_opt_once() {
    local section="$1"
    local opt="$2"
    local file="$STATE_DIR/$opt"
    local value

    [ -f "$file" ] && return 0
    value="$(uci -q get "dhcp.$section.$opt" 2>/dev/null || true)"
    if [ -n "$value" ]; then
        printf '%s\n' "$value" > "$file"
    else
        printf '%s\n' "__unset__" > "$file"
    fi
}

apply_guard() {
    command -v uci >/dev/null 2>&1 || exit 0
    guard_enabled || exit 0

    local section
    section="$(lan_section)"
    uci -q show "dhcp.$section" >/dev/null 2>&1 || {
        log "dhcp.$section not found; IPv6 leak guard skipped"
        exit 0
    }

    mkdir -p "$STATE_DIR" 2>/dev/null || exit 0
    [ -f "$STATE_DIR/section" ] || printf '%s\n' "$section" > "$STATE_DIR/section"
    save_opt_once "$section" ra
    save_opt_once "$section" dhcpv6
    save_opt_once "$section" ndp

    uci -q set "dhcp.$section.ra=disabled" || exit 0
    uci -q set "dhcp.$section.dhcpv6=disabled" || true
    uci -q set "dhcp.$section.ndp=disabled" || true
    uci -q commit dhcp || true
    restart_odhcpd
    log "disabled LAN IPv6 RA/DHCPv6/NDP on dhcp.$section to prevent proxy bypass"
}

restore_opt() {
    local section="$1"
    local opt="$2"
    local file="$STATE_DIR/$opt"
    local value

    [ -f "$file" ] || return 0
    value="$(cat "$file" 2>/dev/null || true)"
    if [ "$value" = "__unset__" ] || [ -z "$value" ]; then
        uci -q delete "dhcp.$section.$opt" 2>/dev/null || true
    else
        uci -q set "dhcp.$section.$opt=$value" 2>/dev/null || true
    fi
}

restore_guard() {
    command -v uci >/dev/null 2>&1 || exit 0
    [ -d "$STATE_DIR" ] || exit 0

    local section
    section="$(cat "$STATE_DIR/section" 2>/dev/null || true)"
    [ -n "$section" ] || section=lan
    uci -q show "dhcp.$section" >/dev/null 2>&1 || {
        rm -rf "$STATE_DIR"
        exit 0
    }

    restore_opt "$section" ra
    restore_opt "$section" dhcpv6
    restore_opt "$section" ndp
    uci -q commit dhcp || true
    rm -rf "$STATE_DIR"
    restart_odhcpd
    log "restored LAN IPv6 RA/DHCPv6/NDP on dhcp.$section"
}

case "$1" in
    apply) apply_guard ;;
    restore) restore_guard ;;
    *)
        echo "Usage: $0 {apply|restore}" >&2
        exit 2
        ;;
esac
