#!/bin/bash
set -o pipefail
umask 077

R=$'\033[0;31m' G=$'\033[0;32m' Y=$'\033[1;33m' B=$'\033[0;34m'
P=$'\033[0;35m' C=$'\033[0;36m' N=$'\033[0m'
setup_terminal() {
    [[ -t 0 ]] && command -v stty &>/dev/null && stty erase '^?' &>/dev/null
    [[ -t 0 ]] && command -v bind &>/dev/null && { bind '"\C-h": backward-delete-char' &>/dev/null; bind '"\e[3~": delete-char' &>/dev/null; }
}
read_input() {
    local p="$1" v="$2" sp="$1"
    if [[ -t 0 ]]; then
        sp=$(printf '%s' "$p" | sed $'s/\033\\[[0-9;]*m/\001&\002/g')
        IFS= read -e -r -p "$sp" "$v"
    else IFS= read -r -p "$p" "$v"
    fi
}
rootness() { [[ $EUID -eq 0 ]] || { echo -e "${R}必须使用root账号运行!${N}" 1>&2; exit 1; }; }
ppp_device_ok() {
    for m in ppp_generic pppox l2tp_ppp; do modprobe $m &>/dev/null; done
    [[ -c /dev/ppp ]] || { mkdir -p /dev; mknod /dev/ppp c 108 0 &>/dev/null; }
    [[ -c /dev/ppp ]] || return 1
    dd if=/dev/ppp of=/dev/null bs=1 count=0 status=none 2>/dev/null
}
kernel_ppp_disabled() {
    local c="/boot/config-$(uname -r)"
    [ -f "$c" ] && grep -q '^# CONFIG_PPP is not set' "$c" 2>/dev/null
}
switch_to_ppp_kernel_deb() {
    echo -e "${Y}检测到当前内核无 PPP 支持，正在安装 linux-image-amd64 并切换默认内核...${N}"
    export DEBIAN_FRONTEND=noninteractive
    apt-get update
    apt-get -y install linux-image-amd64 || { echo -e "${R}安装 linux-image-amd64 失败，无法自动更换内核!${N}" 1>&2; return 1; }
    apt-get -y remove --purge 'linux-image-*-cloud-amd64' linux-image-cloud-amd64 &>/dev/null
    apt-get -y install linux-image-amd64 &>/dev/null
    local k=$(ls -1 /boot/vmlinuz-*-amd64 2>/dev/null | grep -v cloud | sort -V | tail -n1)
    if [ -n "$k" ]; then
        local v=$(basename "$k" | sed 's/^vmlinuz-//')
        ln -sfn "boot/vmlinuz-${v}" /vmlinuz; ln -sfn "boot/initrd.img-${v}" /initrd.img
        if [ -f /etc/default/grub ]; then sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT=0/' /etc/default/grub &>/dev/null; grep -q '^GRUB_DEFAULT=' /etc/default/grub || echo 'GRUB_DEFAULT=0' >> /etc/default/grub; fi
        update-grub &>/dev/null
    fi
    mkdir -p /etc/modules-load.d
    printf 'ppp_generic\npppox\nl2tp_ppp\n' > /etc/modules-load.d/l2tp-ppp.conf
    echo -e "${G}PPP 内核已就绪，即将重启以生效（重启后请重新执行本脚本继续安装）...${N}"
    sync; sleep 2; reboot; exit 0
}
check_tun() {
    ppp_device_ok && return 0
    if { kernel_ppp_disabled || ! ppp_device_ok; } && command -v apt-get &>/dev/null && { kernel_ppp_disabled || [[ "$(uname -r)" == *cloud* ]]; }; then
        switch_to_ppp_kernel_deb
    fi
    kernel_ppp_disabled && echo -e "${R}当前内核未启用 PPP 支持（CONFIG_PPP 未开启），L2TP 无法工作!${N}" 1>&2 || echo -e "${R}PPP 设备不可用，当前内核无法实际打开 /dev/ppp，L2TP 无法工作!${N}" 1>&2
    echo -e "${Y}请更换为支持 PPP 的内核，或更换支持 PPP/L2TP 的 VPS。${N}" 1>&2; exit 1
}
is_public_ipv4() {
    local ip="$1" a b c d
    [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1
    IFS=. read -r a b c d <<< "$ip"
    for i in a b c d; do ((10#${!i} <= 255)) || return 1; done
    ((a == 0 || a == 10 || a == 127 || a >= 224)) && return 1
    ((a == 100 && b >= 64 && b <= 127)) && return 1
    ((a == 169 && b == 254)) && return 1
    ((a == 172 && b >= 16 && b <= 31)) && return 1
    ((a == 192 && b == 168)) && return 1
    ((a == 198 && (b == 18 || b == 19))) && return 1
    return 0
}
get_public_ip() {
    local u r c; IP=""
    for u in "https://myip.ipip.net" "https://api.ipify.org" "https://ipv4.icanhazip.com" "https://ifconfig.me/ip"; do
        r=$(curl -4fsS --connect-timeout 5 --max-time 10 "$u" 2>/dev/null) || r=$(wget -4qO- --timeout=10 "$u" 2>/dev/null) || r=""
        c=$(printf '%s' "$r" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n1)
        is_public_ipv4 "$c" && { IP="$c"; return 0; }
    done
    c=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '/src/ {for(i=1;i<=NF;i++) if($i=="src") {print $(i+1); exit}}')
    is_public_ipv4 "$c" && IP="$c"
}
get_os_info() {
    OS_ID=""; . /etc/os-release 2>/dev/null && { OS=$NAME; OS_ID="${ID:-}"; VERSION_ID="${VERSION_ID:-unknown}"; } || {
        [ -f /etc/redhat-release ] && { OS="CentOS"; VERSION_ID=$(grep -oE '[0-9]+\.' /etc/redhat-release | cut -d. -f1 | head -1); }
        [ -f /etc/alpine-release ] && { OS="Alpine"; VERSION_ID=$(cat /etc/alpine-release); }
        [ -z "${OS:-}" ] && {
            command -v apt-get &>/dev/null && { OS="Debian"; VERSION_ID=$(lsb_release -r 2>/dev/null | awk '{print $2}' || echo "12"); }
            command -v apk &>/dev/null && { OS="Alpine"; VERSION_ID=$(cat /etc/alpine-release 2>/dev/null || echo "3.19"); }
            command -v zypper &>/dev/null && { OS="SUSE"; VERSION_ID=$(rpm -q --queryformat '%{VERSION}' sles-release 2>/dev/null || echo "15"); }
            command -v pacman &>/dev/null && { OS="Arch"; VERSION_ID="rolling"; }
            (command -v dnf &>/dev/null || command -v yum &>/dev/null) && { OS="CentOS"; for rel in centos-release rocky-release almalinux-release redhat-release; do VERSION_ID=$(rpm -q --queryformat '%{VERSION_ID}' "$rel" 2>/dev/null | cut -d. -f1) && break; done; [ -z "$VERSION_ID" ] && VERSION_ID=$(rpm -E "%{rhel}" 2>/dev/null || echo "7"); }
            [ -z "${OS:-}" ] && { echo -e "${R}不支持的操作系统!${N}"; exit 1; }
        }
    }
    [[ "$OS" == *"AlmaLinux"*||"$OS" == *"Rocky"*||"$OS" == *"Alibaba Cloud Linux"*||"$OS" == *"Anolis"*||"$OS" == *"TencentOS"*||"$OS" == *"Tencent Linux"*||"$OS" == *"CTyunOS"*||"$OS" == *"EulerOS"*||"$OS" == *"openEuler"*||"$OS" == *"BCE Linux"*||"$OS" == *"OpenCloudOS"* ]] && OS="CentOS"
    [[ "$OS" == *"Kylin"* ]] && { command -v apt-get &>/dev/null && OS="Debian" || OS="CentOS"; }
    case "$OS_ID" in opencloudos|tencentos|tlinux|anolis|alinux|ctyunos|euleros|openeuler) OS="CentOS"; esac
    [[ "$OS_ID" == "ubuntu" ]] && OS="Ubuntu"
    [[ "$OS_ID" == "debian" ]] && OS="Debian"
    [[ "$OS_ID" == "alpine" ]] && OS="Alpine"
    [[ "$OS_ID" == "arch" ]] && OS="Arch"
    [[ "$OS_ID" == "fedora" ]] && OS="Fedora"
    [[ " ${ID_LIKE:-} " == *" suse "* ]] && OS="SUSE"
    [[ " ${ID_LIKE:-} " == *" debian "* ]] && OS="Debian"
    [[ " ${ID_LIKE:-} " == *" rhel "* || " ${ID_LIKE:-} " == *" fedora "* ]] && OS="CentOS"
    get_public_ip
    [ -z "$IP" ] && echo -e "${Y}暂时无法获取公网IP，安装依赖后将再次检测${N}"
    echo -e "${B}检测到系统: $OS $VERSION_ID${N}"
}
detect_china() {
    local r=$(curl -4fsS --connect-timeout 5 --max-time 10 "https://myip.ipip.net" 2>/dev/null)
    echo "$r" | grep -q "中国 " && ! echo "$r" | grep -qE "香港|澳门|台湾"
}
install_xl2tpd_fallback() {
    command -v xl2tpd &>/dev/null && return 0
    local mv=$(echo "$VERSION_ID" | cut -d. -f1) elver um=$(uname -m)
    [[ "$mv" == "8" || "${PLATFORM_ID:-}" == *el8* || "${PLATFORM_ID:-}" == *oc8* ]] && elver=8
    [[ "$mv" == "7" || "${PLATFORM_ID:-}" == *el7* || "${PLATFORM_ID:-}" == *oc7* ]] && elver=7
    [ -z "$elver" ] && elver=9
    case "$um" in x86_64|amd64) a="x86_64" ;; aarch64|arm64) a="aarch64" ;; *) echo -e "${R}当前架构 $um 无预置 xl2tpd RPM${N}"; return 1 ;; esac
    for vs in "1.3.17-1" "1.3.18-1" "1.3.16-1"; do
        for url in "https://mirrors.aliyun.com/epel/${elver}/Everything/${a}/Packages/x/xl2tpd-${vs}.el${elver}.${a}.rpm" "https://dl.fedoraproject.org/pub/epel/${elver}/Everything/${a}/Packages/x/xl2tpd-${vs}.el${elver}.${a}.rpm"; do
            curl -fsSL --connect-timeout 8 --max-time 60 "$url" -o /tmp/xl2tpd.rpm &>/dev/null || continue
            dnf -y install /tmp/xl2tpd.rpm &>/dev/null || yum -y install /tmp/xl2tpd.rpm &>/dev/null || rpm -Uvh --nodeps /tmp/xl2tpd.rpm &>/dev/null || rpm -ivh --nodeps /tmp/xl2tpd.rpm &>/dev/null || { rm -f /tmp/xl2tpd.rpm; continue; }
            rm -f /tmp/xl2tpd.rpm; echo -e "${G}xl2tpd 安装成功${N}"; return 0
        done
    done
    echo -e "${R}无法安装 xl2tpd，请检查网络或手动安装${N}"; return 1
}
ensure_xl2tpd_service() {
    [ -f /usr/lib/systemd/system/xl2tpd.service -o -f /etc/systemd/system/xl2tpd.service ] && return 0
    command -v xl2tpd &>/dev/null || return 1
    local b=$(command -v xl2tpd)
    cat > /etc/systemd/system/xl2tpd.service << EOF
[Unit]
Description=L2TP Daemon
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0
[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/xl2tpd
RuntimeDirectory=xl2tpd
ExecStart=${b} -D
Restart=on-failure
RestartSec=2
[Install]
WantedBy=multi-user.target
EOF
    systemctl daemon-reload
}
check_l2tp_installed() {
    [ -e /etc/ipsec.conf ] && [ -f /etc/xl2tpd/xl2tpd.conf ] && [ -f /etc/ppp/options.xl2tpd ]
}
get_next_ip() {
    local i
    for ((i=2; i<=254; i++)); do
        awk -v ip="192.168.18.$i" '$1!~/^#/ && $4==ip{f=1}END{exit !f}' /etc/ppp/chap-secrets 2>/dev/null || { echo "192.168.18.$i"; return; }
    done
    echo "没有可用的IP地址!"; exit 1
}
os_major() {
    local m=$(echo "${VERSION_ID:-0}" | cut -d. -f1)
    [[ "$m" =~ ^[0-9]+$ ]] || m=0; echo "$m"
}
should_use_swanctl() {
    systemctl list-unit-files strongswan-starter.service 2>/dev/null | grep -q 'strongswan-starter.service' && return 1
    command -v swanctl &>/dev/null || return 1
    systemctl list-unit-files strongswan.service 2>/dev/null | grep -q 'strongswan.service'
}
need_modern_ipsec_stack() { return 1; }
install_rpm_ipsec() {
    local m="$1"
    [ "$(detect_ipsec_stack)" = "strongswan" ] && return 0
    [ "$(detect_ipsec_stack)" = "libreswan" ] && "$m" -y remove libreswan &>/dev/null
    "$m" -y install strongswan &>/dev/null && [ "$(detect_ipsec_stack)" = "strongswan" ] && return 0
    "$m" -y install libreswan &>/dev/null; [ "$(detect_ipsec_stack)" = "libreswan" ]
}
install_dependencies() {
    if [[ "$OS" == "Rhel"* || "$OS" == "CentOS"* ]]; then
        local mgr; command -v dnf &>/dev/null && mgr=dnf || mgr=yum
        local mv=$(echo "$VERSION_ID" | cut -d. -f1)
        [[ "$mv" == "8" || "$mv" == "8"* ]] && {
            sed -i 's|mirror.centos.org|vault.centos.org|g' /etc/yum.repos.d/*.repo &>/dev/null
            sed -i 's|^#baseurl|baseurl|g;s|^mirrorlist|#mirrorlist|g' /etc/yum.repos.d/*.repo &>/dev/null
            sed -i 's|^#baseurl|baseurl|g;s|^mirrorlist|#mirrorlist|g' /etc/yum.repos.d/epel*.repo &>/dev/null
        }
        [ "$mgr" = "dnf" ] && $mgr -y makecache || { $mgr -y makecache fast 2>/dev/null || $mgr -y makecache; }
        rpm -q epel-release epel-aliyuncs-release &>/dev/null || $mgr -y install epel-release &>/dev/null || true
        for pkg in curl iproute ppp ethtool; do $mgr -y install $pkg &>/dev/null || true; done
        [ "$mgr" = "dnf" ] && $mgr -y install iptables-services &>/dev/null || $mgr -y install iptables-services &>/dev/null || $mgr -y install iptables iptables-nft iptables-legacy &>/dev/null || true
        $mgr -y install kernel-modules-extra &>/dev/null || modprobe l2tp_ppp &>/dev/null || true
        install_rpm_ipsec $mgr; $mgr -y install xl2tpd &>/dev/null || install_xl2tpd_fallback; ensure_xl2tpd_service
        [ "$mgr" = "dnf" ] && [[ "$(echo $VERSION_ID | cut -d. -f1)" =~ ^[0-9]+$ ]] && [[ "$(echo $VERSION_ID | cut -d. -f1)" -ge 10 ]] && $mgr -y install nftables
    elif [[ "$OS" == "Ubuntu"* || "$OS" == "Debian"* ]]; then
        apt-get update
        echo iptables-persistent iptables-persistent/autosave_v4 boolean true | debconf-set-selections &>/dev/null; echo iptables-persistent iptables-persistent/autosave_v6 boolean true | debconf-set-selections &>/dev/null
        if [[ "$OS" == "Debian"* && "$(os_major)" -ge 13 ]]; then
            DEBIAN_FRONTEND=noninteractive apt-get -y install curl iproute2 ppp xl2tpd iptables iptables-persistent ethtool
            DEBIAN_FRONTEND=noninteractive apt-get -y install strongswan-starter libstrongswan-standard-plugins
        else
            DEBIAN_FRONTEND=noninteractive apt-get -y purge strongswan-swanctl charon-systemd &>/dev/null
            DEBIAN_FRONTEND=noninteractive apt-get -y install curl iproute2 ppp xl2tpd iptables iptables-persistent ethtool
            DEBIAN_FRONTEND=noninteractive apt-get -y install strongswan strongswan-starter strongswan-charon libstrongswan-standard-plugins
        fi
        DEBIAN_FRONTEND=noninteractive apt-get -y install libcharon-extauth-plugins &>/dev/null
        apt-cache show libstrongswan-extra-plugins &>/dev/null && DEBIAN_FRONTEND=noninteractive apt-get -y install libstrongswan-extra-plugins &>/dev/null || apt-cache show libcharon-extra-plugins &>/dev/null && DEBIAN_FRONTEND=noninteractive apt-get -y install libcharon-extra-plugins &>/dev/null || true
        ensure_xl2tpd_service
    elif [[ "$OS" == "Fedora"* ]]; then
        dnf -y makecache && dnf -y install curl iproute ppp strongswan xl2tpd iptables iptables-services ethtool
    elif [[ "$OS" == "SUSE"* ]]; then
        zypper --non-interactive refresh && zypper --non-interactive install curl iproute2 ppp strongswan xl2tpd iptables ethtool
    elif [[ "$OS" == "Arch"* ]]; then
        pacman -Sy --noconfirm --needed curl iproute2 ppp strongswan xl2tpd iptables ethtool
    elif [[ "$OS" == "Alpine"* ]]; then
        setup-devd udev &>/dev/null; apk update && apk add curl iproute2 ppp strongswan xl2tpd iptables iptables-legacy ethtool
    else
        echo -e "${R}不支持的操作系统!${N}"; exit 1
    fi
    command -v xl2tpd &>/dev/null || { echo -e "${R}xl2tpd 安装失败!${N}"; exit 1; }
    [ "$(detect_ipsec_stack)" = "unknown" ] && { echo -e "${R}IPSec(strongswan/libreswan) 安装失败!${N}"; exit 1; }
}
fix_service_config() {
    echo -e "${Y}修复服务配置...${N}"
    ensure_xl2tpd_service; modprobe l2tp_ppp &>/dev/null || echo -e "${Y}l2tp_ppp 模块无法加载，使用替代方案${N}"
    sed -i '/lcp-echo-adaptive/d' /etc/ppp/options.xl2tpd &>/dev/null
    sed -i '/close_action=/d' /etc/ipsec.conf /etc/strongswan/ipsec.conf &>/dev/null
    if systemctl list-unit-files strongswan-starter.service 2>/dev/null | grep -q 'strongswan-starter.service'; then
        systemctl disable --now strongswan &>/dev/null; systemctl disable --now strongswan-swanctl &>/dev/null
        rm -f /etc/swanctl/conf.d/l2tp.conf &>/dev/null
    elif should_use_swanctl; then
        rm -f /etc/strongswan/ipsec.conf /etc/ipsec.conf &>/dev/null
        swanctl --load-all &>/dev/null
    fi
    mkdir -p /etc/systemd/system/xl2tpd.service.d
    cat > /etc/systemd/system/xl2tpd.service.d/override.conf << EOF
[Unit]
StartLimitIntervalSec=0
[Service]
Restart=on-failure
RestartSec=2
EOF
    systemctl daemon-reload
    echo -e "${G}服务配置修复完成${N}"
}
detect_ipsec_stack() {
    local v=$(ipsec --version 2>/dev/null)
    [[ "$v" == *Libreswan* || "$v" == *libreswan* ]] && { echo "libreswan"; return; }
    [[ "$v" == *strongSwan* || "$v" == *strongswan* ]] && { echo "strongswan"; return; }
    command -v swanctl &>/dev/null || command -v strongswan &>/dev/null && { echo "strongswan"; return; }
    [ -f /usr/libexec/ipsec/libreswan ] && { echo "libreswan"; return; }
    command -v ipsec &>/dev/null && { echo "strongswan"; return; }
    echo "unknown"
}
select_ipsec_proposals() {
    local m=$(os_major)
    IKE_PROPOSALS="3des-sha1-modp1024,aes128-sha1-modp1024,aes128-sha1-modp2048,3des-sha1-modp1536"
    ESP_PROPOSALS="3des-sha1,aes128-sha1"
    if { [ "$OS" = "Fedora" ] && [ "$m" -ge 39 ]; } || { [ "$OS" = "CentOS" ] && [ "$m" -ge 9 ]; } || { [ "$OS" = "SUSE" ] && [ "$m" -ge 16 ]; } || { [ "$OS" = "Debian" ] && [ "$m" -ge 13 ]; }; then
        IKE_PROPOSALS="aes128-sha1-modp2048,3des-sha1-modp2048,aes128-sha256-modp2048"
        ESP_PROPOSALS="aes128-sha1,3des-sha1,aes128-sha256"
    fi
}

config_ipsec() {
    select_ipsec_proposals
    mkdir -p /etc/ipsec.d/{cacerts,aacerts,ocspcerts,acerts,crls,private,certs,reqs}
    local s=$(detect_ipsec_stack) d="/etc"
    should_use_swanctl || true
    [ "$s" = "strongswan" ] && [ -d /etc/strongswan ] && { [ -x /usr/sbin/strongswan ] || [ -f /etc/strongswan/strongswan.conf ]; } && d="/etc/strongswan"
    mkdir -p "$d"
    if [ "$s" = "strongswan" ] && should_use_swanctl; then
        echo -e "${Y}检测到 swanctl，使用 swanctl 格式配置（跳过 ipsec.conf）${N}"
    elif [ "$s" = "libreswan" ]; then
        cat > "${d}/ipsec.conf" << EOF
version 2.0
config setup
    uniqueids=no
    ikev1-policy=accept
conn %default
    keyexchange=ikev1
    ike=${IKE_PROPOSALS}
    esp=${ESP_PROPOSALS}
    ikelifetime=24h
    salifetime=8h
    rekey=no
    dpddelay=3
    dpdtimeout=15
    dpdaction=clear
conn l2tp-psk
    authby=secret
    pfs=no
    type=transport
    left=%defaultroute
    leftid=${IP}
    leftprotoport=17/1701
    right=%any
    rightid=%any
    rightprotoport=17/%any
    encapsulation=yes
    auto=add
EOF
    else
        cat > "${d}/ipsec.conf" << EOF
version 2.0
config setup
    uniqueids=no
conn %default
    keyexchange=ikev1
    ike=${IKE_PROPOSALS}
    esp=${ESP_PROPOSALS}
    ikelifetime=24h
    keylife=8h
    dpddelay=3
    dpdtimeout=15
    dpdaction=clear
    rekey=no
conn l2tp-psk
    authby=secret
    type=transport
    left=%defaultroute
    leftid=${IP}
    leftprotoport=17/1701
    right=%any
    rightprotoport=17/%any
    keyingtries=%forever
    auto=add
EOF
    fi
    PSK="hm123456"
    cat > "${d}/ipsec.secrets" << EOF
%any %any : PSK "${PSK}"
EOF
    chmod 600 "${d}/ipsec.secrets"
    if [ "$d" != "/etc" ] && [ -f "${d}/ipsec.conf" ]; then
        ln -sfn "${d}/ipsec.conf" /etc/ipsec.conf 2>/dev/null || true
    fi
    if [ "$d" != "/etc" ] && [ -f "${d}/ipsec.secrets" ]; then
        ln -sfn "${d}/ipsec.secrets" /etc/ipsec.secrets 2>/dev/null || true
    fi
}
config_swanctl() {
    select_ipsec_proposals
    command -v swanctl &>/dev/null || return 0
    systemctl list-unit-files strongswan-starter.service 2>/dev/null | grep -q 'strongswan-starter.service' && return 0
    mkdir -p /etc/swanctl/conf.d
    local psk="${PSK:-hm123456}"
    cat > /etc/swanctl/conf.d/l2tp.conf << EOF
connections { l2tp-psk {
    version = 1
    local_addrs = %any
    remote_addrs = %any
    local { auth = psk }
    remote { auth = psk }
    children { l2tp {
        mode = transport
        local_ts = dynamic[udp/l2f]
        remote_ts = dynamic[udp]
        dpd_action = clear
        esp_proposals = ${ESP_PROPOSALS}
        rekey_time = 0
        start_action = none
    } }
    proposals = ${IKE_PROPOSALS}
    dpd_delay = 3
    dpd_timeout = 15
    pull = no
    fragmentation = yes
    encap = yes
    keyingtries = 0
} }
secrets { ike-l2tp { id = %any secret = "${psk}" } }
EOF
    chmod 600 /etc/swanctl/conf.d/l2tp.conf
    swanctl --load-all &>/dev/null || true
    echo -e "${G}swanctl 配置已生成${N}"
}
config_xl2tpd() {
    mkdir -p /etc/xl2tpd /etc/ppp
    if detect_china; then DNS1="223.5.5.5"; DNS2="223.6.6.6"; else DNS1="1.1.1.1"; DNS2="8.8.8.8"; fi
    cat > /etc/xl2tpd/xl2tpd.conf << EOF
[global]
listen-addr = 0.0.0.0
port = 1701
access control = no
[lns default]
ip range = 192.168.18.2-192.168.18.254
local ip = 192.168.18.1
require chap = yes
refuse pap = yes
require authentication = yes
name = l2tpd
exclusive = no
assign ip = yes
ppp debug = no
pppoptfile = /etc/ppp/options.xl2tpd
EOF
    cat > /etc/ppp/options.xl2tpd << EOF
ipcp-accept-local
ipcp-accept-remote
require-mschap-v2
ms-dns ${DNS1}
ms-dns ${DNS2}
noccp
auth
hide-password
idle 0
mtu 1400
mru 1400
nodefaultroute
persist
maxfail 0
holdoff 1
connect-delay 1000
ipcp-max-configure 30
ipcp-max-failure 30
lcp-echo-interval 5
lcp-echo-failure 3
lcp-echo-adaptive
noipx
novj
novjccomp
nopcomp
noaccomp
nobsdcomp
nodeflate
asyncmap 0
receive-all
EOF
    touch /etc/ppp/chap-secrets
    chmod 600 /etc/ppp/chap-secrets
}
config_system() {
    [[ "$OS" == CentOS* ]] && [ -s /etc/selinux/config ] && grep -q 'SELINUX=enforcing' /etc/selinux/config && { sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config; setenforce 0; }
    MAIN_IF=$(ip -4 route show default | awk '/default/ {print $5}' | head -n1)
    [ -n "$MAIN_IF" ] || { MAIN_IF="eth0"; echo -e "${Y}无法自动检测公网接口，使用默认 eth0${N}"; }
    cat > /etc/sysctl.d/99-l2tp.conf << EOF
net.ipv4.ip_forward=1
net.ipv4.icmp_echo_ignore_broadcasts=1
net.ipv4.icmp_ignore_bogus_error_responses=1
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.${MAIN_IF}.rp_filter=0
net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.accept_source_route=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.all.forwarding=1
net.ipv4.conf.default.forwarding=1
net.ipv4.conf.${MAIN_IF}.forwarding=1
net.ipv4.udp_rmem_min = 65536
net.ipv4.udp_wmem_min = 65536
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_udp_timeout = 360
net.netfilter.nf_conntrack_udp_timeout_stream = 600
EOF
    sysctl --system &>/dev/null || echo -e "${Y}部分内核参数不受当前系统支持，已跳过${N}"
    [ "$(sysctl -n net.ipv4.ip_forward)" != "1" ] && { grep -q '^net\.ipv4\.ip_forward\s*=\s*1' /etc/sysctl.conf &>/dev/null || echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf; sysctl -p; }
    read_input "$(echo -e "${Y}是否临时禁用 ${MAIN_IF} 的 GRO/TSO/GSO 以降低延迟? (y/N): ${N}")" DISABLE_OFFLOAD
    [[ "$DISABLE_OFFLOAD" == [yY] ]] && command -v ethtool &>/dev/null && ethtool -K "$MAIN_IF" gro off gso off tso off || [[ "$DISABLE_OFFLOAD" == [yY] ]] && echo -e "${Y}当前系统未安装 ethtool，无法禁用网卡卸载。${N}"
}
remove_firewall_rules() {
    command -v iptables &>/dev/null || return 0
    for t in "" "-t nat" "-t mangle"; do
        eval iptables $t -S 2>/dev/null | grep -q '^-A.*L2TP_MGR_' || continue
        eval iptables $t -S 2>/dev/null | grep '^-A.*L2TP_MGR_' | while read -r rule; do
            eval iptables $t $(echo "$rule" | sed 's/^-A/-D/') 2>/dev/null || true
        done
    done
    for c in INPUT FORWARD; do iptables -D $c -j L2TP_MGR_IN &>/dev/null; iptables -D $c -j L2TP_MGR_FWD &>/dev/null; done
    iptables -F L2TP_MGR_IN &>/dev/null; iptables -X L2TP_MGR_IN &>/dev/null
    iptables -F L2TP_MGR_FWD &>/dev/null; iptables -X L2TP_MGR_FWD &>/dev/null
    iptables -t nat -F L2TP_MGR_NAT &>/dev/null; iptables -t nat -X L2TP_MGR_NAT &>/dev/null
    iptables -t mangle -F L2TP_MGR_MSS &>/dev/null; iptables -t mangle -X L2TP_MGR_MSS &>/dev/null
}
config_firewall() {
    MI=${MI:-$(ip -4 route show default | awk '/default/ {print $5}' | head -n1)}
    [ -n "$MI" ] || MI=$(ip route show default 2>/dev/null | awk '/default/{print $5; exit}')
    [ -n "$MI" ] || { MI="eth0"; echo -e "${Y}无法自动检测主网络接口，回退到 eth0${N}"; }
    echo -e "${Y}使用 iptables 配置防火墙...${N}"
    command -v iptables &>/dev/null || { echo -e "${R}iptables 不可用!${N}"; exit 1; }
    modprobe nf_conntrack &>/dev/null || true
    remove_firewall_rules
    for p in 500 4500 1701; do
        while iptables -C INPUT -p udp --dport $p -j REJECT --reject-with icmp-port-unreachable &>/dev/null; do
            iptables -D INPUT -p udp --dport $p -j REJECT --reject-with icmp-port-unreachable || break
        done
    done
    iptables -N L2TP_MGR_IN
    for p in 500 4500 1701; do iptables -A L2TP_MGR_IN -p udp --dport $p -j ACCEPT; done
    iptables -A L2TP_MGR_IN -p esp -j ACCEPT
    iptables -I INPUT 1 -j L2TP_MGR_IN
    iptables -N L2TP_MGR_FWD
    iptables -A L2TP_MGR_FWD -s 192.168.18.0/24 -j ACCEPT
    iptables -A L2TP_MGR_FWD -d 192.168.18.0/24 -j ACCEPT
    iptables -I FORWARD 1 -j L2TP_MGR_FWD
    iptables -t nat -N L2TP_MGR_NAT
    iptables -t nat -A L2TP_MGR_NAT -s 192.168.18.0/24 -o "$MI" -j MASQUERADE
    iptables -t nat -I POSTROUTING 1 -j L2TP_MGR_NAT
    iptables -t mangle -N L2TP_MGR_MSS
    iptables -t mangle -A L2TP_MGR_MSS -s 192.168.18.0/24 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
    iptables -t mangle -I FORWARD 1 -j L2TP_MGR_MSS
    if [[ "$OS" == CentOS* || "$OS" == Fedora* ]]; then
        service iptables save &>/dev/null || { mkdir -p /etc/sysconfig; iptables-save > /etc/sysconfig/iptables; }
        systemctl list-unit-files iptables.service 2>/dev/null | grep -q iptables.service || {
            [ -f /etc/sysconfig/iptables ] && command -v iptables-restore &>/dev/null && cat > /etc/systemd/system/l2tp-iptables-restore.service << UNIT
[Unit]
Description=Restore L2TP iptables rules
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/sbin/iptables-restore /etc/sysconfig/iptables
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
UNIT
        }
        systemctl daemon-reload &>/dev/null
        systemctl enable l2tp-iptables-restore.service &>/dev/null
    else
        netfilter-persistent save &>/dev/null || { mkdir -p /etc/iptables; iptables-save > /etc/iptables/rules.v4; }
    fi
    echo -e "${G}防火墙规则保存成功!${N}"
}
enable_service_restart() {
    if [[ "$OS" == Alpine* ]]; then
        for s in strongswan xl2tpd; do rc-update add $s default &>/dev/null; rc-service $s restart &>/dev/null || echo -e "${Y}$s 启动失败${N}"; done
        sleep 2
        local i=false l=false
        rc-service strongswan status 2>/dev/null | grep -q started && i=true
        rc-service xl2tpd status 2>/dev/null | grep -q started && l=true
        if $i && $l; then echo -e "${G}IPSec 和 xl2tpd 服务已成功启动${N}"
        else $i || echo -e "${Y}警告: IPSec 服务未运行${N}"; $l || echo -e "${Y}警告: xl2tpd 服务未运行${N}"
        fi
        return
    fi
    for s in strongswan-starter strongswan ipsec; do
        systemctl list-unit-files "${s}.service" 2>/dev/null | grep -q "${s}.service" || continue
        mkdir -p "/etc/systemd/system/${s}.service.d"
        cat > "/etc/systemd/system/${s}.service.d/restart.conf" << EOF
[Unit]
StartLimitIntervalSec=0
[Service]
Restart=on-failure
RestartSec=2
EOF
    done
    systemctl daemon-reload
    systemctl enable xl2tpd &>/dev/null
    if ! should_use_swanctl; then
        systemctl disable --now strongswan &>/dev/null; systemctl disable --now strongswan-swanctl &>/dev/null
    fi
    local ipsec_svc=""
    for s in strongswan-starter strongswan ipsec; do
        systemctl list-unit-files "${s}.service" &>/dev/null || continue
        ipsec_svc="$s"
        systemctl enable "$s" &>/dev/null; systemctl restart "$s" &>/dev/null || echo -e "${Y}$s 启动失败，请重启服务器后检查${N}"
        break
    done
    [ -n "$ipsec_svc" ] || echo -e "${Y}未找到 strongswan/ipsec 服务单元，请手动启动${N}"
    systemctl restart xl2tpd &>/dev/null || echo -e "${Y}xl2tpd 启动失败，请重启服务器后检查${N}"
    sleep 2
    local i=false l=false
    for s in strongswan-starter strongswan ipsec; do systemctl is-active --quiet $s 2>/dev/null && i=true && break; done
    systemctl is-active --quiet xl2tpd 2>/dev/null && l=true
    if $i && $l; then echo -e "${G}IPSec 和 xl2tpd 服务已成功启动${N}"
    else $i || echo -e "${Y}警告: IPSec 服务未运行，可能需要重启后生效${N}"; $l || echo -e "${Y}警告: xl2tpd 服务未运行，可能需要重启后生效${N}"
    fi
}
add_user() {
    local u=$1 m=$2
    [[ "$u" =~ ^[A-Za-z0-9_.-]{1,32}$ ]] || { echo -e "${R}用户名只能包含字母、数字、点、下划线和连字符，长度1-32!${N}"; exit 1; }
    awk -v u="$u" '$1==u{f=1}END{exit !f}' /etc/ppp/chap-secrets && { echo -e "${R}用户名 $u 已存在!${N}"; exit 1; }
    IP_ADDR=$([[ "$m" == [yY] ]] && echo "*" || get_next_ip)
    printf '%s\t%s\t%s\t%s\n' "$u" "l2tpd" "hm123456" "$IP_ADDR" >> /etc/ppp/chap-secrets
    chmod 600 /etc/ppp/chap-secrets
    echo -e "${G}用户添加成功!${N}${C}----------------------------------------${N}"
    printf "%-15s %-20s\n%-15s %-20s\n%-15s %-20s\n" "用户名: $u" "IP: $IP_ADDR" "密码: hm123456" "PSK: hm123456" "服务器IP: $IP" "端口: 1701"
    echo -e "${C}----------------------------------------${N}"
}
delete_user() {
    local u=$1
    [[ "$u" =~ ^[A-Za-z0-9_.-]{1,32}$ ]] || { echo -e "${R}用户名格式无效!${N}"; exit 1; }
    awk -v u="$u" '$1==u{f=1}END{exit !f}' /etc/ppp/chap-secrets || { echo -e "${R}用户名 $u 不存在!${N}"; exit 1; }
    awk -v u="$u" '$1!=u' /etc/ppp/chap-secrets > /etc/ppp/chap-secrets.tmp
    chmod 600 /etc/ppp/chap-secrets.tmp; mv /etc/ppp/chap-secrets.tmp /etc/ppp/chap-secrets
    echo -e "${G}用户删除成功!${N}"
}
show_users() {
    echo -e "${B}当前用户列表:${N}${C}----------------------------------------${N}"
    grep -v '^#' /etc/ppp/chap-secrets | awk '$1!=""&&$4!=""{printf "%-15s %-20s\n","用户名: "$1,"IP: "$4}'
    echo -e "${C}----------------------------------------${N}"
}
uninstall_l2tp() {
    echo -e "${Y}开始彻底卸载 L2TP...${N}"
    for s in xl2tpd ipsec strongswan strongswan-starter; do systemctl stop "$s" &>/dev/null; systemctl disable "$s" &>/dev/null; done
    local mgr=""; for m in dnf yum apt zypper pacman; do command -v "$m" &>/dev/null && { mgr="$m"; break; }; done
    if [ -n "$mgr" ]; then
        echo -e "${Y}使用包管理器: $mgr 移除组件...${N}"
        case "$mgr" in
            dnf|yum) "$mgr" remove -y xl2tpd libreswan strongswan ppp &>/dev/null; "$mgr" autoremove -y &>/dev/null; "$mgr" clean all &>/dev/null ;;
            apt) apt-get purge -y xl2tpd libreswan strongswan ppp strongswan-pki &>/dev/null; apt-get autoremove -y &>/dev/null; apt-get clean &>/dev/null ;;
            zypper) zypper --non-interactive remove xl2tpd libreswan strongswan ppp &>/dev/null ;;
            pacman) pacman -Rns --noconfirm xl2tpd libreswan strongswan ppp &>/dev/null ;;
        esac
    fi
    command -v apk &>/dev/null && { for s in xl2tpd ipsec strongswan; do rc-service "$s" stop &>/dev/null; done; for s in xl2tpd strongswan; do rc-update del "$s" default &>/dev/null; done; apk del xl2tpd libreswan strongswan ppp &>/dev/null; }
    rm -rf /etc/systemd/system/xl2tpd.service.d &>/dev/null
    for s in ipsec strongswan strongswan-starter; do rm -f "/etc/systemd/system/$s.service.d/restart.conf" &>/dev/null; rmdir "/etc/systemd/system/$s.service.d" &>/dev/null; done
    systemctl disable l2tp-iptables-restore.service &>/dev/null; rm -f /etc/systemd/system/l2tp-iptables-restore.service &>/dev/null
    rm -rf /etc/xl2tpd /etc/ppp /etc/ipsec.d /etc/strongswan /var/lib/strongswan /var/lib/libreswan /var/log/xl2tpd /var/run/xl2tpd /var/log/strongswan /var/log/libreswan /etc/firewalld/services/xl2tpd.xml &>/dev/null
    rm -f /etc/ipsec.conf /etc/ipsec.secrets /etc/sysctl.d/99-l2tp.conf /etc/sysctl.d/99-tcp-keepalive.conf /etc/nftables.d/l2tp.nft &>/dev/null
    remove_firewall_rules
    command -v firewall-cmd &>/dev/null && { firewall-cmd --permanent --remove-port=500/udp --remove-port=4500/udp --remove-port=1701/udp &>/dev/null; firewall-cmd --permanent --remove-service=ipsec &>/dev/null; firewall-cmd --reload &>/dev/null; echo -e "${G}firewalld 规则已清理${N}"; }
    command -v iptables-save &>/dev/null && { command -v netfilter-persistent &>/dev/null && netfilter-persistent save &>/dev/null; iptables-save > /etc/iptables/rules.v4; mkdir -p /etc/sysconfig &>/dev/null; iptables-save > /etc/sysconfig/iptables; }
    sysctl --system &>/dev/null; systemctl daemon-reload &>/dev/null
    rm -f /var/log/xl2tpd.log /var/log/strongswan.log /var/log/libreswan.log &>/dev/null
    echo -e "${G}L2TP 已完全彻底卸载!${N}"
}
main() {
    echo -e "${P}L2TP VPN 管理脚本（带自动重拨）${N}"
    setup_terminal; rootness; check_tun; get_os_info
    if ! check_l2tp_installed; then
        echo -e "${Y}未检测到L2TP安装，开始安装...${N}"
        read_input "${C}请输入VPN用户名: ${N}" VPN_USER
        read_input "${C}是否启用多拨? (y/n): ${N}" MULTI_DIAL
        install_dependencies
        [ -z "$IP" ] && get_public_ip
        if [ -z "$IP" ]; then
            echo -e "${Y}无法自动获取出口公网IPv4，可手动填写（须与客户端填写的服务器地址一致）${N}"
            read_input "${C}请输入服务器公网IPv4: ${N}" IP
            is_public_ipv4 "$IP" || { echo -e "${R}无法获取出口公网IPv4地址，请检查服务器是否位于NAT后或网络是否可用!${N}"; exit 1; }
        fi
        config_ipsec; config_swanctl; fix_service_config; config_xl2tpd
        config_system; config_firewall; enable_service_restart
        add_user "$VPN_USER" "$MULTI_DIAL"
        echo -e "${G}安装完成! 已启用自动重拨/服务自恢复功能${N}"
    else
        echo -e "${B}检测到L2TP已安装，请选择操作:${N}"
        echo -e "${C}1. 添加用户${N}${C}2. 删除用户${N}${C}3. 显示所有用户${N}${R}4. 卸载L2TP${N}"
        read_input "${Y}请选择 (1-4): ${N}" choice
        case $choice in
            1) read_input "${C}请输入新用户名: ${N}" nu; read_input "${C}是否启用多拨? (y/n): ${N}" md; add_user "$nu" "$md" ;;
            2) read_input "${C}请输入要删除的用户名: ${N}" du; delete_user "$du" ;;
            3) show_users ;;
            4) read_input "${R}确定要卸载L2TP吗？这将删除所有配置和用户数据！(y/n): ${N}" c; [[ "$c" == [yY] ]] && uninstall_l2tp || echo -e "${Y}已取消卸载操作${N}" ;;
            *) echo -e "${R}无效的选择!${N}"; exit 1 ;;
        esac
    fi
}
main
