#!/bin/bash
# -----------------------------------------------------------------------------
# 脚本名称: fifteenhuawei_manage.sh
# 功能说明: Debian 下 L2TP + SOCKS5 共存部署脚本
# 功能说明: 安装完成后可通过 wawacloud 菜单修改 L2TP/SOCKS5 参数
# 功能说明: 每次修改完成后自动重新输出最新 IP/端口/账号/密码 信息
# 使用说明: 请确保当前目录存在 ip.txt
# -----------------------------------------------------------------------------

# L2TP 默认预共享密钥
VPN_IPSEC_PSK_DEFAULT="wawacloud"

# L2TP 用户名前缀
VPN_USER_PREFIX_DEFAULT="user"

# L2TP 默认通用密码
VPN_PASSWORD_DEFAULT="wawacloud"

# SOCKS5 默认用户名
SOCKS_USER_DEFAULT="root"

# SOCKS5 默认密码
SOCKS_PASSWORD_DEFAULT="wawacloud"

# SOCKS5 默认端口
SOCKS_PORT_DEFAULT="23456"

# 配置文件名
CONFIG_FILE="ip.txt"

# 默认父网卡
PARENT_INTERFACE="eth0"

# 最大 L2TP 用户数
MAX_L2TP_USERS=10

# 临时清洗后的配置文件
SANITIZED_CONFIG_FILE="/tmp/vpn_ip_clean.$$"

# 自定义配置目录
SETTINGS_DIR="/etc/wawacloud"

# 参数配置文件
SETTINGS_FILE="${SETTINGS_DIR}/settings.conf"

# 网卡信息数据库
NETINFOS_FILE="${SETTINGS_DIR}/net_infos.db"

# 运行时环境文件
RUNTIME_ENV_FILE="${SETTINGS_DIR}/runtime.env"

# L2TP 用户到出口映射文件
USER_ROUTE_MAP="/etc/vpn_user_route.map"

# 开机初始化脚本
BOOT_SCRIPT="/usr/local/bin/vpn_boot_init.sh"

# L2TP 启动脚本
START_L2TP="/usr/local/bin/start_native_l2tp.sh"

# SOCKS5 启动脚本
START_SOCKS="/usr/local/bin/start_multi_dante.sh"

# 后台管理命令
MANAGE_CMD="/usr/local/bin/wawacloud"

# xl2tpd 名称
L2TP_NAME="l2tpd-main"

# 清理临时文件函数
cleanup_tmp_files() {
    # 如果临时文件存在则删除
    [ -f "$SANITIZED_CONFIG_FILE" ] && rm -f "$SANITIZED_CONFIG_FILE"
}

# 按 IP 排序函数
sort_infos_by_ip() {
    # 如果参数数量为 0 则返回
    [ "$#" -eq 0 ] && return 0

    # 对输入内容按第二列 IP 做排序
    printf '%s\n' "$@" | awk -F'|' '
        NF >= 2 {
            split($2, a, ".");
            printf "%03d.%03d.%03d.%03d|%s\n", a[1], a[2], a[3], a[4], $0;
        }
    ' | sort | cut -d'|' -f2-
}

# 加载配置参数函数
load_settings() {
    # 创建配置目录
    mkdir -p "$SETTINGS_DIR"

    # 如果配置文件不存在则写入默认配置
    if [ ! -f "$SETTINGS_FILE" ]; then
        cat > "$SETTINGS_FILE" <<EOCFG
VPN_IPSEC_PSK="${VPN_IPSEC_PSK_DEFAULT}"
VPN_USER_PREFIX="${VPN_USER_PREFIX_DEFAULT}"
VPN_PASSWORD="${VPN_PASSWORD_DEFAULT}"
SOCKS_USER="${SOCKS_USER_DEFAULT}"
SOCKS_PASSWORD="${SOCKS_PASSWORD_DEFAULT}"
SOCKS_PORT="${SOCKS_PORT_DEFAULT}"
EOCFG
    fi

    # 加载配置文件
    source "$SETTINGS_FILE"

    # 如果变量为空则使用默认值
    VPN_IPSEC_PSK="${VPN_IPSEC_PSK:-$VPN_IPSEC_PSK_DEFAULT}"
    VPN_USER_PREFIX="${VPN_USER_PREFIX:-$VPN_USER_PREFIX_DEFAULT}"
    VPN_PASSWORD="${VPN_PASSWORD:-$VPN_PASSWORD_DEFAULT}"
    SOCKS_USER="${SOCKS_USER:-$SOCKS_USER_DEFAULT}"
    SOCKS_PASSWORD="${SOCKS_PASSWORD:-$SOCKS_PASSWORD_DEFAULT}"
    SOCKS_PORT="${SOCKS_PORT:-$SOCKS_PORT_DEFAULT}"
}

# 保存运行时环境函数
save_runtime_env() {
    # 写入运行环境文件
    cat > "$RUNTIME_ENV_FILE" <<EORUN
ENTRY_IFACE="${ENTRY_IFACE}"
ENTRY_IP="${ENTRY_IP}"
ENTRY_PUBLIC_IP="${ENTRY_PUBLIC_IP}"
L2TP_NAME="${L2TP_NAME}"
SETTINGS_FILE="${SETTINGS_FILE}"
NETINFOS_FILE="${NETINFOS_FILE}"
USER_ROUTE_MAP="${USER_ROUTE_MAP}"
START_L2TP="${START_L2TP}"
START_SOCKS="${START_SOCKS}"
BOOT_SCRIPT="${BOOT_SCRIPT}"
MAX_L2TP_USERS="${MAX_L2TP_USERS}"
EORUN
}

# 保存网卡信息函数
save_net_infos() {
    # 先清空数据库文件
    : > "$NETINFOS_FILE"

    # 循环写入网卡信息
    for info in "${NET_INFOS[@]}"; do
        echo "$info" >> "$NETINFOS_FILE"
    done
}

# 根据网卡名获取公网 IP
get_public_ip_by_iface() {
    # 目标网卡名
    local target_iface="$1"

    # 索引计数器
    local idx=0

    # 临时变量
    local info iface ip table type

    # 遍历网卡信息
    for info in "${NET_INFOS[@]}"; do
        # 拆分数据
        IFS='|' read -r iface ip table type <<< "$info"

        # 如果网卡匹配则返回对应公网 IP
        if [ "$iface" = "$target_iface" ]; then
            echo "${PUBLIC_IPS[$idx]}"
            return 0
        fi

        # 索引加一
        ((idx++))
    done

    # 未找到则返回失败
    return 1
}

# 注册退出时清理临时文件
trap cleanup_tmp_files EXIT

# 写入后台管理脚本
write_manage_script() {
cat > "$MANAGE_CMD" <<'EOMANAGE'
#!/bin/bash

# 自定义配置目录
SETTINGS_DIR="/etc/wawacloud"

# 参数配置文件
SETTINGS_FILE="${SETTINGS_DIR}/settings.conf"

# 网卡信息数据库
NETINFOS_FILE="${SETTINGS_DIR}/net_infos.db"

# 运行时环境文件
RUNTIME_ENV_FILE="${SETTINGS_DIR}/runtime.env"

# 用户路由映射文件
USER_ROUTE_MAP="/etc/vpn_user_route.map"

# L2TP 启动脚本
START_L2TP="/usr/local/bin/start_native_l2tp.sh"

# SOCKS5 启动脚本
START_SOCKS="/usr/local/bin/start_multi_dante.sh"

# 加载配置参数
load_settings() {
    # 判断配置文件是否存在
    [ ! -f "$SETTINGS_FILE" ] && echo "错误: 找不到 $SETTINGS_FILE" && exit 1

    # 加载配置文件
    source "$SETTINGS_FILE"
}

# 加载运行环境
load_runtime() {
    # 判断运行时文件是否存在
    [ ! -f "$RUNTIME_ENV_FILE" ] && echo "错误: 找不到 $RUNTIME_ENV_FILE" && exit 1

    # 加载运行时文件
    source "$RUNTIME_ENV_FILE"
}

# 修改配置文件中的键值
write_settings_value() {
    # 读取键名
    local key="$1"

    # 读取值
    local value="$2"

    # 如果键已存在则替换
    if grep -q "^${key}=" "$SETTINGS_FILE" 2>/dev/null; then
        sed -i "s#^${key}=.*#${key}=\"${value}\"#" "$SETTINGS_FILE"
    else
        # 如果不存在则追加
        echo "${key}=\"${value}\"" >> "$SETTINGS_FILE"
    fi
}

# 输出完整最新信息
print_full_outputs() {
    # 加载配置
    load_settings

    # 加载运行时环境
    load_runtime

    # 输出分隔
    echo
    echo "========== 最新配置信息 =========="
    echo

    # 输出 SOCKS5 信息
    echo "Socks5格式"

    # 如果网卡信息文件存在
    if [ -f "$NETINFOS_FILE" ]; then
        # 循环读取网卡信息
        while IFS='|' read -r IFACE IP TABLE TYPE; do
            # 跳过空 IP
            [ -z "$IP" ] && continue

            # 通过对应 IP 探测公网 IP
            PUB_IP=$(curl -s -L --interface "$IP" --connect-timeout 5 http://whatismyip.akamai.com | grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | head -n 1)

            # 如果探测不到则回退为内网 IP
            [ -z "$PUB_IP" ] && PUB_IP="$IP"

            # 输出 SOCKS5 信息
            echo "$PUB_IP/$SOCKS_PORT/$SOCKS_USER/$SOCKS_PASSWORD"
        done < "$NETINFOS_FILE"
    fi

    # 输出空行
    echo

    # 输出 L2TP 信息
    echo "L2TP格式"

    # 如果映射文件存在
    if [ -f "$USER_ROUTE_MAP" ]; then
        # 循环读取 L2TP 映射
        while IFS='|' read -r CURRENT_USER CLIENT_VPN_IP IFACE TABLE EXIT_PUB_IP; do
            # 跳过空用户
            [ -z "$CURRENT_USER" ] && continue

            # 如果出口公网 IP 为空则显示未知
            [ -z "$EXIT_PUB_IP" ] && EXIT_PUB_IP="未知"

            # 输出 L2TP 信息
            echo "$ENTRY_PUBLIC_IP/$CURRENT_USER/$VPN_PASSWORD/$VPN_IPSEC_PSK/对应出口IP:$EXIT_PUB_IP"
        done < "$USER_ROUTE_MAP"
    fi

    # 输出结束分隔
    echo
    echo "=================================="
    echo
}

# 重载 L2TP 配置
reload_l2tp_configs() {
    # 加载设置
    load_settings

    # 加载运行环境
    load_runtime

    # 检查映射文件是否存在
    [ ! -f "$USER_ROUTE_MAP" ] && echo "错误: 找不到 $USER_ROUTE_MAP" && return 1

    # 重写 ipsec.conf
    cat > /etc/ipsec.conf <<EOIPSEC
version 2.0
config setup

conn %default
    forceencaps=yes
    authby=secret
    pfs=no
    auto=add
    rekey=no
    type=transport
    ike=aes128-sha1-modp1024,3des-sha1-modp1024
    phase2alg=aes128-sha1,3des-sha1

conn L2TP-MAIN
    left=$ENTRY_IP
    leftid=$ENTRY_PUBLIC_IP
    leftprotoport=17/1701
    right=%any
    rightprotoport=17/%any
EOIPSEC

    # 重写 PSK 文件
    echo "$ENTRY_IP %any : PSK \"$VPN_IPSEC_PSK\"" > /etc/ipsec.secrets

    # 清空 chap-secrets
    : > /etc/ppp/chap-secrets

    # 重新写入用户密码
    while IFS='|' read -r CURRENT_USER CLIENT_VPN_IP IFACE TABLE EXIT_PUB_IP; do
        [ -z "$CURRENT_USER" ] && continue
        echo "\"$CURRENT_USER\" \"$L2TP_NAME\" \"$VPN_PASSWORD\" $CLIENT_VPN_IP" >> /etc/ppp/chap-secrets
    done < "$USER_ROUTE_MAP"

    # 重启 ipsec
    systemctl restart ipsec 2>/dev/null || service ipsec restart 2>/dev/null || true

    # 调用 L2TP 启动脚本
    if [ -x "$START_L2TP" ]; then
        bash "$START_L2TP"
    fi

    # 输出完成提示
    echo "L2TP 参数已重载完成。"
}

# 重载 SOCKS5 配置
reload_socks_configs() {
    # 加载设置
    load_settings

    # 加载运行环境
    load_runtime

    # 检查网卡信息文件
    [ ! -f "$NETINFOS_FILE" ] && echo "错误: 找不到 $NETINFOS_FILE" && return 1

    # SOCKS5 已改用 Xray：账号/密码只写进 Xray 配置，绝不创建或修改任何系统用户，
    # 彻底消除旧版 chpasswd 误改 root 系统密码的 bug。
    # 启动脚本 START_SOCKS 会读取 settings.conf + net_infos.db，按每个出口 IP 重新
    # 生成 Xray SOCKS5 配置并重启，这里直接调用它即可完成重载。

    # 调用 SOCKS5 启动脚本
    if [ -x "$START_SOCKS" ]; then
        bash "$START_SOCKS"
    fi

    # 输出完成提示
    echo "SOCKS5 参数已重载完成。"
}

# 显示当前核心参数
show_current() {
    # 加载配置
    load_settings

    # 加载运行时环境
    load_runtime

    # 显示当前状态
    echo "----------------------------------------"
    echo "当前 L2TP 入口公网IP: $ENTRY_PUBLIC_IP"
    echo "当前 L2TP 密钥: $VPN_IPSEC_PSK"
    echo "当前 L2TP 账号前缀: $VPN_USER_PREFIX"
    echo "当前 L2TP 通用密码: $VPN_PASSWORD"
    echo "当前 SOCKS5 端口: $SOCKS_PORT"
    echo "当前 SOCKS5 用户名: $SOCKS_USER"
    echo "当前 SOCKS5 密码: $SOCKS_PASSWORD"
    echo "----------------------------------------"
}

# 死循环显示菜单
while true; do
    # 清屏
    clear

    # 显示当前状态
    show_current

    # 显示菜单
    echo "1. 修改 L2TP 密钥"
    echo "2. 修改 L2TP 通用密码"
    echo "3. 修改 SOCKS5 端口"
    echo "4. 修改 SOCKS5 用户名"
    echo "5. 修改 SOCKS5 密码"
    echo "6. 一键重载 L2TP"
    echo "7. 一键重载 SOCKS5"
    echo "8. 查看当前输出信息"
    echo "0. 退出"
    echo "----------------------------------------"

    # 读取用户输入
    read -rp "请输入编号: " choice

    # 根据输入执行对应操作
    case "$choice" in
        1)
            # 输入新密钥
            read -rp "请输入新的 L2TP 密钥: " val

            # 检查不能为空
            [ -z "$val" ] && echo "不能为空" && read -n 1 -s -r -p "按任意键继续..." && continue

            # 写入配置
            write_settings_value "VPN_IPSEC_PSK" "$val"

            # 重载 L2TP
            reload_l2tp_configs

            # 自动输出最新信息
            print_full_outputs

            # 暂停
            read -n 1 -s -r -p "按任意键继续..."
            ;;
        2)
            # 输入新密码
            read -rp "请输入新的 L2TP 通用密码: " val

            # 检查不能为空
            [ -z "$val" ] && echo "不能为空" && read -n 1 -s -r -p "按任意键继续..." && continue

            # 写入配置
            write_settings_value "VPN_PASSWORD" "$val"

            # 重载 L2TP
            reload_l2tp_configs

            # 自动输出最新信息
            print_full_outputs

            # 暂停
            read -n 1 -s -r -p "按任意键继续..."
            ;;
        3)
            # 输入新端口
            read -rp "请输入新的 SOCKS5 端口: " val

            # 判断端口是否合法
            if ! [[ "$val" =~ ^[0-9]+$ ]] || [ "$val" -lt 1 ] || [ "$val" -gt 65535 ]; then
                echo "端口不合法"
                read -n 1 -s -r -p "按任意键继续..."
                continue
            fi

            # 写入配置
            write_settings_value "SOCKS_PORT" "$val"

            # 重载 SOCKS5
            reload_socks_configs

            # 自动输出最新信息
            print_full_outputs

            # 暂停
            read -n 1 -s -r -p "按任意键继续..."
            ;;
        4)
            # 输入新用户名
            read -rp "请输入新的 SOCKS5 用户名: " val

            # 检查不能为空
            [ -z "$val" ] && echo "不能为空" && read -n 1 -s -r -p "按任意键继续..." && continue

            # 写入配置
            write_settings_value "SOCKS_USER" "$val"

            # 重载 SOCKS5
            reload_socks_configs

            # 自动输出最新信息
            print_full_outputs

            # 暂停
            read -n 1 -s -r -p "按任意键继续..."
            ;;
        5)
            # 输入新密码
            read -rp "请输入新的 SOCKS5 密码: " val

            # 检查不能为空
            [ -z "$val" ] && echo "不能为空" && read -n 1 -s -r -p "按任意键继续..." && continue

            # 写入配置
            write_settings_value "SOCKS_PASSWORD" "$val"

            # 重载 SOCKS5
            reload_socks_configs

            # 自动输出最新信息
            print_full_outputs

            # 暂停
            read -n 1 -s -r -p "按任意键继续..."
            ;;
        6)
            # 重载 L2TP
            reload_l2tp_configs

            # 自动输出最新信息
            print_full_outputs

            # 暂停
            read -n 1 -s -r -p "按任意键继续..."
            ;;
        7)
            # 重载 SOCKS5
            reload_socks_configs

            # 自动输出最新信息
            print_full_outputs

            # 暂停
            read -n 1 -s -r -p "按任意键继续..."
            ;;
        8)
            # 输出当前完整信息
            print_full_outputs

            # 暂停
            read -n 1 -s -r -p "按任意键继续..."
            ;;
        0)
            # 退出程序
            exit 0
            ;;
        *)
            # 输入错误提示
            echo "输入错误"
            read -n 1 -s -r -p "按任意键继续..."
            ;;
    esac
done
EOMANAGE

# 添加执行权限
chmod +x "$MANAGE_CMD"
}

# 判断是否以 root 身份运行
if [[ $EUID -ne 0 ]]; then
    echo "错误: 请用 root 运行。"
    exit 1
fi

# 判断 ip.txt 是否存在
if [ ! -f "$CONFIG_FILE" ]; then
    echo "错误: 找不到 $CONFIG_FILE"
    exit 1
fi

# 加载配置
load_settings

# 清洗 ip.txt 的 BOM 和回车
sed '1s/^\xEF\xBB\xBF//;s/\r$//' "$CONFIG_FILE" > "$SANITIZED_CONFIG_FILE"

# 开始处理 DNS
echo ">> [0/9] 检测并修复 DNS..."

# 如果 DNS 不通则重写 resolv.conf
if ! ping -c 1 -W 1 repo.huaweicloud.com >/dev/null 2>&1; then
    cp /etc/resolv.conf /etc/resolv.conf.bak 2>/dev/null
    echo "nameserver 100.125.1.250" > /etc/resolv.conf
    echo "nameserver 8.8.8.8" >> /etc/resolv.conf
    echo "nameserver 114.114.114.114" >> /etc/resolv.conf
fi

# 开始安装依赖
echo ">> [1/9] 清理环境并安装原生软件..."

# 卸载可能冲突的软件
apt-get remove -y docker docker-engine docker.io containerd runc 2>/dev/null

# 更新软件源
apt-get update -y

# 安装依赖
apt-get install -y libreswan xl2tpd ppp curl wget iproute2 iptables net-tools

# 如果 rc.local 不存在则创建
if [ ! -f /etc/rc.local ]; then
    echo '#!/bin/bash' > /etc/rc.local
    echo 'exit 0' >> /etc/rc.local
    chmod +x /etc/rc.local
    systemctl unmask rc-local 2>/dev/null || true
    systemctl enable rc-local 2>/dev/null || true
    systemctl start rc-local 2>/dev/null || true
fi

# 加载内核模块
modprobe af_key
modprobe l2tp_ppp
modprobe ppp_generic
modprobe ppp_mppe

# 写入 sysctl 配置
cat > /etc/sysctl.d/99-vpn.conf <<EOSYSCTL
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
EOSYSCTL

# 应用 sysctl 配置
sysctl -p /etc/sysctl.d/99-vpn.conf >/dev/null

# 写入开机初始化脚本
cat > "$BOOT_SCRIPT" <<EOBOOT
#!/bin/bash
modprobe 8021q
modprobe af_key
modprobe l2tp_ppp
modprobe ppp_generic
modprobe ppp_mppe
sysctl -p /etc/sysctl.d/99-vpn.conf
iptables -F
iptables -t nat -F
iptables -I INPUT -p 50 -j ACCEPT
iptables -I INPUT -p udp --dport 500 -j ACCEPT
iptables -I INPUT -p udp --dport 4500 -j ACCEPT
iptables -I INPUT -p udp --dport 1701 -j ACCEPT
iptables -I INPUT -p tcp --dport ${SOCKS_PORT} -j ACCEPT
iptables -I INPUT -p udp --dport ${SOCKS_PORT} -j ACCEPT
iptables -A FORWARD -j ACCEPT
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
EOBOOT

# 添加权限
chmod +x "$BOOT_SCRIPT"

# 立即执行一次初始化
bash "$BOOT_SCRIPT"

# 开始识别网卡
echo ">> [2/9] 配置网络接口并探测公网 IP..."

# 定义网卡信息数组
declare -a NET_INFOS

# 定义公网 IP 数组
declare -a PUBLIC_IPS

# 路由表起始编号
TABLE_ID_START=100

# 获取物理网卡列表
PHYSICAL_IFS=$(ip -o link show | awk -F': ' '{print $2}' | grep -vE '^lo|^docker|^veth|\.' | cut -d@ -f1)

# 遍历物理网卡
for IFACE in $PHYSICAL_IFS; do
    # 获取当前网卡 IPv4 地址
    IP=$(ip -4 addr show "$IFACE" | grep -oP '(?<=inet\s)\d+(\.\d+){3}')

    # 如果存在 IP 则加入数组
    if [ -n "$IP" ]; then
        echo "   - 检测到物理网卡: $IFACE ($IP)"
        NET_INFOS+=("$IFACE|$IP|$TABLE_ID_START|PHYSICAL")
        echo "sysctl -w net.ipv4.conf.${IFACE}.rp_filter=0" >> "$BOOT_SCRIPT"
        echo "sysctl -w net.ipv4.conf.${IFACE}.arp_ignore=1" >> "$BOOT_SCRIPT"
        echo "sysctl -w net.ipv4.conf.${IFACE}.arp_announce=2" >> "$BOOT_SCRIPT"
        ((TABLE_ID_START++))
    fi
done

# 如果清洗后的配置文件存在则处理 VLAN
if [ -f "$SANITIZED_CONFIG_FILE" ]; then
    # 逐行读取 ip.txt
    while read -r line || [[ -n "$line" ]]; do
        # 跳过注释行
        [[ "$line" =~ ^#.*$ ]] && continue

        # 跳过空行
        [[ -z "$line" ]] && continue

        # 取第一列
        FIRST_COL=$(echo "$line" | awk '{print $1}')

        # 判断是否未写父网卡
        if [[ "$FIRST_COL" =~ ^[0-9]+$ ]]; then
            PARENT_IF="$PARENT_INTERFACE"
            read -r VLAN_ID MAC_ADDR IP_INPUT <<< "$(echo "$line" | tr ',' ' ')"
        else
            read -r PARENT_IF VLAN_ID MAC_ADDR IP_INPUT <<< "$(echo "$line" | tr ',' ' ')"
        fi

        # 如果 VLAN_ID 为空则跳过
        [ -z "$VLAN_ID" ] && continue

        # 如果 IP 为空则跳过
        [ -z "$IP_INPUT" ] && continue

        # 如果 IP 没有 CIDR 则默认 /24
        if [[ "$IP_INPUT" != */* ]]; then
            IP_CIDR="${IP_INPUT}/24"
        else
            IP_CIDR="$IP_INPUT"
        fi

        # 提取纯 IP
        IP_ONLY=$(echo "$IP_CIDR" | cut -d/ -f1)

        # 生成子接口名
        IFACE_NAME="${PARENT_IF}.${VLAN_ID}"

        # 如果子接口不存在则创建
        if ! ip link show "$IFACE_NAME" >/dev/null 2>&1; then
            # 判断父网卡是否存在
            if ! ip link show "$PARENT_IF" >/dev/null 2>&1; then
                continue
            fi

            # 创建 VLAN 子接口
            ip link add link "$PARENT_IF" name "$IFACE_NAME" type vlan id "$VLAN_ID"

            # 如果 MAC 有值则写入
            if [[ "$MAC_ADDR" != "default" && "$MAC_ADDR" != "-" && -n "$MAC_ADDR" ]]; then
                ip link set dev "$IFACE_NAME" down
                ip link set dev "$IFACE_NAME" address "$MAC_ADDR"
            fi

            # 启动接口
            ip link set dev "$IFACE_NAME" up

            # 802.1Q 子接口 MTU 降到 1496：1500 的 IP 包打标签后以太帧达 1522>1518(baby-giant)，
            # 会被云底层 vSwitch 丢弃，导致 VLAN 出口大包丢失、L2TP 速度异常。降 4 字节后帧≤1518，
            # MSS 钳制也会据此把 VLAN 出口的 MSS 协商到 1456，下载方向大包不再超限。
            ip link set dev "$IFACE_NAME" mtu 1496

            # 清理旧地址
            ip addr flush dev "$IFACE_NAME"

            # 绑定 IP
            ip addr add "$IP_CIDR" dev "$IFACE_NAME"
        fi

        # 写入开机启动动作
        echo "# VLAN $VLAN_ID on $PARENT_IF" >> "$BOOT_SCRIPT"
        echo "ip link add link $PARENT_IF name $IFACE_NAME type vlan id $VLAN_ID 2>/dev/null" >> "$BOOT_SCRIPT"

        # 如果 MAC 有值则开机设置
        if [[ "$MAC_ADDR" != "default" && "$MAC_ADDR" != "-" && -n "$MAC_ADDR" ]]; then
            echo "ip link set dev $IFACE_NAME down" >> "$BOOT_SCRIPT"
            echo "ip link set dev $IFACE_NAME address $MAC_ADDR" >> "$BOOT_SCRIPT"
        fi

        # 开机时启用接口并绑定 IP
        echo "ip link set dev $IFACE_NAME up" >> "$BOOT_SCRIPT"
        echo "ip link set dev $IFACE_NAME mtu 1496" >> "$BOOT_SCRIPT"
        echo "ip addr add $IP_CIDR dev $IFACE_NAME 2>/dev/null" >> "$BOOT_SCRIPT"
        echo "sysctl -w net.ipv4.conf.${IFACE_NAME}.rp_filter=0" >> "$BOOT_SCRIPT"

        # 加入数组
        NET_INFOS+=("$IFACE_NAME|$IP_ONLY|$TABLE_ID_START|VLAN")

        # 路由表编号递增
        ((TABLE_ID_START++))
    done < "$SANITIZED_CONFIG_FILE"
fi

# 为 boot_script 添加结束语句
echo "exit 0" >> "$BOOT_SCRIPT"

# 开始探测真实公网 IP
echo "   - 正在探测所有出口的真实公网 IP..."

# 初始化计数器
COUNT=0

# 遍历网卡信息探测公网 IP
for INFO in "${NET_INFOS[@]}"; do
    IFS='|' read -r IFACE IP TABLE TYPE <<< "$INFO"
    DETECTED_IP=$(curl -s -L --interface "$IP" --connect-timeout 5 http://whatismyip.akamai.com | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n 1)
    [ -z "$DETECTED_IP" ] && DETECTED_IP=$(curl -s -L --interface "$IP" --connect-timeout 8 http://ifconfig.me | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n 1)
    [ -z "$DETECTED_IP" ] && DETECTED_IP="$IP"
    PUBLIC_IPS[$COUNT]="$DETECTED_IP"
    ((COUNT++))
done

# 对网卡信息按 IP 排序
mapfile -t L2TP_ROUTE_INFOS < <(sort_infos_by_ip "${NET_INFOS[@]}")

# 如果没有可用线路则退出
[ ${#L2TP_ROUTE_INFOS[@]} -eq 0 ] && echo "错误: 没有可用线路。" && exit 1

# 默认入口网卡使用父网卡
ENTRY_IFACE="$PARENT_INTERFACE"

# 如果父网卡不存在则找第一个物理网卡
if ! ip link show "$ENTRY_IFACE" >/dev/null 2>&1; then
    ENTRY_IFACE=""
    for INFO in "${NET_INFOS[@]}"; do
        IFS='|' read -r IFACE IP TABLE TYPE <<< "$INFO"
        if [ "$TYPE" = "PHYSICAL" ]; then
            ENTRY_IFACE="$IFACE"
            break
        fi
    done
fi

# 如果还没找到则取第一条线路
if [ -z "$ENTRY_IFACE" ]; then
    IFS='|' read -r ENTRY_IFACE ENTRY_IP_DUMMY ENTRY_TABLE_DUMMY ENTRY_TYPE_DUMMY <<< "${L2TP_ROUTE_INFOS[0]}"
fi

# 获取入口网卡内网 IP
ENTRY_IP=$(ip -4 -o addr show "$ENTRY_IFACE" 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -n 1)

# 如果获取不到则从数组中找
if [ -z "$ENTRY_IP" ]; then
    for INFO in "${NET_INFOS[@]}"; do
        IFS='|' read -r IFACE IP TABLE TYPE <<< "$INFO"
        if [ "$IFACE" = "$ENTRY_IFACE" ]; then
            ENTRY_IP="$IP"
            break
        fi
    done
fi

# 如果还为空则强制取第一条
[ -z "$ENTRY_IP" ] && IFS='|' read -r ENTRY_IFACE ENTRY_IP ENTRY_TABLE_DUMMY ENTRY_TYPE_DUMMY <<< "${L2TP_ROUTE_INFOS[0]}"

# 获取入口公网 IP
ENTRY_PUBLIC_IP=$(get_public_ip_by_iface "$ENTRY_IFACE")

# 如果数组没找到则在线探测
[ -z "$ENTRY_PUBLIC_IP" ] && ENTRY_PUBLIC_IP=$(curl -s -L --interface "$ENTRY_IP" --connect-timeout 5 http://whatismyip.akamai.com | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n 1)

# 第二备用探测
[ -z "$ENTRY_PUBLIC_IP" ] && ENTRY_PUBLIC_IP=$(curl -s -L --interface "$ENTRY_IP" --connect-timeout 8 http://ifconfig.me | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n 1)

# 最终回退
[ -z "$ENTRY_PUBLIC_IP" ] && ENTRY_PUBLIC_IP="$ENTRY_IP"

# 保存网卡信息
save_net_infos

# 保存运行时环境
save_runtime_env

# 配置 IPsec
echo ">> [3/9] 配置 IPsec (Libreswan)..."

# 备份旧配置
mv /etc/ipsec.conf /etc/ipsec.conf.bak 2>/dev/null

# 写入新的 ipsec.conf
cat > /etc/ipsec.conf <<EOIPSECMAIN
version 2.0
config setup

conn %default
    forceencaps=yes
    authby=secret
    pfs=no
    auto=add
    rekey=no
    type=transport
    ike=aes128-sha1-modp1024,3des-sha1-modp1024
    phase2alg=aes128-sha1,3des-sha1

conn L2TP-MAIN
    left=${ENTRY_IP}
    leftid=${ENTRY_PUBLIC_IP}
    leftprotoport=17/1701
    right=%any
    rightprotoport=17/%any
EOIPSECMAIN

# 写入 PSK
echo "${ENTRY_IP} %any : PSK \"${VPN_IPSEC_PSK}\"" > /etc/ipsec.secrets

# 重启 ipsec
systemctl restart ipsec 2>/dev/null || service ipsec restart 2>/dev/null || true

# 配置 L2TP
echo ">> [4/9] 配置 L2TP (xl2tpd)..."

# 停止旧 xl2tpd
systemctl stop xl2tpd 2>/dev/null || true
systemctl disable xl2tpd 2>/dev/null || true
pkill xl2tpd 2>/dev/null || true
rm -rf /var/run/xl2tpd/*.pid

# 写入 PPP 配置
cat > /etc/ppp/options.xl2tpd <<EOPPP
require-mschap-v2
ms-dns 119.29.29.29
ms-dns 223.6.6.6
asyncmap 0
auth
hide-password
lcp-echo-interval 30
lcp-echo-failure 4
mtu 1380
mru 1380
noccp
novj
novjccomp
nobsdcomp
nodeflate
EOPPP

# 清空 chap-secrets
: > /etc/ppp/chap-secrets

# 清空映射文件
: > "$USER_ROUTE_MAP"

# 写入 xl2tpd 配置
cat > /etc/xl2tpd/xl2tpd-main.conf <<EOXL2TP
[global]
listen-addr = ${ENTRY_IP}
port = 1701
auth file = /etc/ppp/chap-secrets
access control = no

[lns default]
ip range = 192.168.60.10-192.168.60.250
local ip = 192.168.60.1
require chap = yes
refuse pap = yes
require authentication = yes
name = ${L2TP_NAME}
ppp debug = no
pppoptfile = /etc/ppp/options.xl2tpd
length bit = yes
EOXL2TP

# 写入 L2TP 启动脚本
cat > "$START_L2TP" <<EOSTARTL2TP
#!/bin/bash
source "${SETTINGS_FILE}"
mkdir -p /var/run/xl2tpd
pkill xl2tpd 2>/dev/null
ip rule del to 192.168.0.0/16 table main 2>/dev/null
ip rule add to 192.168.0.0/16 table main priority 50
EOSTARTL2TP

# 计算可创建的用户数
TOTAL_L2TP_USERS=${#L2TP_ROUTE_INFOS[@]}

# 如果超过最大值则截断
[ "$TOTAL_L2TP_USERS" -gt "$MAX_L2TP_USERS" ] && TOTAL_L2TP_USERS="$MAX_L2TP_USERS"

# 如果没有用户则退出
[ "$TOTAL_L2TP_USERS" -le 0 ] && echo "错误: L2TP 线路数量为 0。" && exit 1

# 初始化用户索引
USER_INDEX=1

# 循环生成用户和策略路由
while [ "$USER_INDEX" -le "$TOTAL_L2TP_USERS" ]; do
    # 取对应线路
    INFO="${L2TP_ROUTE_INFOS[$((USER_INDEX - 1))]}"

    # 拆分线路信息
    IFS='|' read -r IFACE IP TABLE TYPE <<< "$INFO"

    # 生成当前用户名
    CURRENT_USER="${VPN_USER_PREFIX}${USER_INDEX}"

    # 生成客户端虚拟 IP
    CLIENT_VPN_IP="192.168.60.$((9 + USER_INDEX))"

    # 获取出口公网 IP
    EXIT_PUBLIC_IP=$(get_public_ip_by_iface "$IFACE")

    # 如果获取不到则回退为内网 IP
    [ -z "$EXIT_PUBLIC_IP" ] && EXIT_PUBLIC_IP="$IP"

    # 写入 chap-secrets
    echo "\"${CURRENT_USER}\" \"${L2TP_NAME}\" \"${VPN_PASSWORD}\" ${CLIENT_VPN_IP}" >> /etc/ppp/chap-secrets

    # 写入映射文件
    echo "${CURRENT_USER}|${CLIENT_VPN_IP}|${IFACE}|${TABLE}|${EXIT_PUBLIC_IP}" >> "$USER_ROUTE_MAP"

    # 默认网关按 /24 推算
    GATEWAY=$(echo "$IP" | awk -F. '{print $1"."$2"."$3".1"}')

    # 如果系统里有更准确的默认路由则使用它
    GW_FROM_ROUTE=$(ip route show default dev "$IFACE" | awk '/default/ {print $3; exit}')
    [ -n "$GW_FROM_ROUTE" ] && GATEWAY="$GW_FROM_ROUTE"

    # 写入专属路由和 NAT 规则
    echo "ip route flush table ${TABLE} 2>/dev/null" >> "$START_L2TP"
    echo "ip route add default via ${GATEWAY} dev ${IFACE} table ${TABLE} 2>/dev/null" >> "$START_L2TP"
    echo "ip route add ${IP}/32 dev ${IFACE} table ${TABLE} 2>/dev/null" >> "$START_L2TP"
    echo "ip rule del from ${CLIENT_VPN_IP}/32 table ${TABLE} priority 110 2>/dev/null" >> "$START_L2TP"
    echo "ip rule add from ${CLIENT_VPN_IP}/32 table ${TABLE} priority 110 2>/dev/null" >> "$START_L2TP"
    echo "iptables -t nat -D POSTROUTING -s ${CLIENT_VPN_IP}/32 -o ${IFACE} -j MASQUERADE 2>/dev/null" >> "$START_L2TP"
    echo "iptables -t nat -A POSTROUTING -s ${CLIENT_VPN_IP}/32 -o ${IFACE} -j MASQUERADE" >> "$START_L2TP"

    # 用户索引加一
    ((USER_INDEX++))
done

# 启动 xl2tpd
echo 'xl2tpd -c /etc/xl2tpd/xl2tpd-main.conf -p /var/run/xl2tpd/xl2tpd-main.pid -C /var/run/xl2tpd/l2tp-control-main &' >> "$START_L2TP"

# 赋予执行权限
chmod +x "$START_L2TP"

# 立即执行 L2TP 启动
bash "$START_L2TP"

# 配置 SOCKS5
echo ">> [5/9] 配置 SOCKS5..."

# 安装 xray 二进制（优先自有源，失败回退；SOCKS5 改用 Xray，账号只写进配置不碰系统用户）
XRAY_BIN_PATH="/usr/local/bin/xray"
if [ ! -x "$XRAY_BIN_PATH" ]; then
    echo "下载 xray..."
    wget -q -O "$XRAY_BIN_PATH" "http://wawaapi.com/static/xray" \
      || curl -fsSL -o "$XRAY_BIN_PATH" "http://wawaapi.com/static/xray" \
      || { echo "❌ xray 下载失败，请检查网络后重试"; exit 1; }
    chmod +x "$XRAY_BIN_PATH"
fi

# 清理可能残留的旧 danted（历史版本用过 dante，迁移到 Xray 后不再需要）
systemctl stop danted 2>/dev/null || true
systemctl disable danted 2>/dev/null || true
killall danted 2>/dev/null || true

# 落盘“出口IP -> 公网IP”映射，供 START_SOCKS 给 SOCKS5 UDP associate 回包用（缺它远程客户端 UDP 不可用）。
# 安装阶段 PUBLIC_IPS[] 已是各出口探测到的公网IP，直接复用，无需重复探测。
UDP_PUB_MAP="${SETTINGS_DIR}/udp_pub_ip.map"
: > "$UDP_PUB_MAP"
_C=0
for INFO in "${NET_INFOS[@]}"; do
    IFS='|' read -r _IF _IP _T _TY <<< "$INFO"
    _PUB="${PUBLIC_IPS[$_C]}"
    [ -z "$_PUB" ] && _PUB="$_IP"
    echo "${_IP} ${_PUB}" >> "$UDP_PUB_MAP"
    _C=$((_C+1))
done

# 写入 SOCKS5 启动脚本：自包含，读取 settings.conf(账号/密码/端口) + net_infos.db(每个出口IP) + udp_pub_ip.map(UDP回包公网IP)，
# 为每个出口 IP 启动一个 Xray SOCKS5 实例（listen=该IP，sendThrough=该IP，配合已有 PBR 实现独立出口）。
# 账号写进 Xray 配置的 accounts，纯代理凭据，绝不创建/修改系统用户 —— 彻底杜绝改 root 密码的 bug。
cat > "$START_SOCKS" <<'EOSTARTSOCKS'
#!/bin/bash
SETTINGS_FILE="/etc/wawacloud/settings.conf"
NETINFOS_FILE="/etc/wawacloud/net_infos.db"
XRAY_BIN="/usr/local/bin/xray"
XRAY_ETC="/usr/local/etc/xray"
UDP_PUB_MAP="/etc/wawacloud/udp_pub_ip.map"

[ -f "$SETTINGS_FILE" ] && source "$SETTINGS_FILE"
SOCKS_USER="${SOCKS_USER:-root}"
SOCKS_PASSWORD="${SOCKS_PASSWORD:-wawacloud}"
SOCKS_PORT="${SOCKS_PORT:-23456}"

mkdir -p "$XRAY_ETC"

# 停掉旧的 Xray 实例与历史遗留 danted
pkill -f "xray run -c ${XRAY_ETC}/socks5_" 2>/dev/null || true
killall danted 2>/dev/null || true
sleep 1

# 逐个出口 IP 生成 Xray 配置并启动
while IFS='|' read -r IFACE IP TABLE TYPE; do
    [ -z "$IP" ] && continue
    # 查该出口IP对应的公网IP作为 SOCKS5 UDP associate 回包地址；查不到则回退用出口IP本身
    PUB_IP="$IP"
    if [ -f "$UDP_PUB_MAP" ]; then
        _m=$(awk -v k="$IP" '$1==k{print $2; exit}' "$UDP_PUB_MAP")
        [ -n "$_m" ] && PUB_IP="$_m"
    fi
    CONF="${XRAY_ETC}/socks5_${IFACE}.json"
    cat > "$CONF" <<JSON
{
  "log": {"loglevel": "warning"},
  "inbounds": [{
    "tag": "in-${IFACE}",
    "listen": "${IP}",
    "port": ${SOCKS_PORT},
    "protocol": "socks",
    "settings": {
      "auth": "password",
      "udp": true,
      "ip": "${PUB_IP}",
      "accounts": [{"user": "${SOCKS_USER}", "pass": "${SOCKS_PASSWORD}"}]
    }
  }],
  "outbounds": [{"protocol": "freedom", "settings": {}, "sendThrough": "${IP}"}]
}
JSON
    "$XRAY_BIN" run -c "$CONF" >/dev/null 2>&1 &
done < "$NETINFOS_FILE"
EOSTARTSOCKS

# 赋予启动脚本执行权限
chmod +x "$START_SOCKS"

# 立即启动 SOCKS5
bash "$START_SOCKS"

# 清理 rc.local 旧记录
if grep -q 'exit 0' /etc/rc.local; then
    sed -i '/exit 0/d' /etc/rc.local
fi

# 删除旧的启动行
sed -i '/vpn_boot_init.sh/d' /etc/rc.local
sed -i '/start_multi_dante.sh/d' /etc/rc.local
sed -i '/start_native_l2tp.sh/d' /etc/rc.local

# 写入新的 rc.local 启动项
echo "$BOOT_SCRIPT" >> /etc/rc.local
echo "$START_SOCKS" >> /etc/rc.local
echo "$START_L2TP" >> /etc/rc.local
echo 'exit 0' >> /etc/rc.local

# 写入后台管理命令
write_manage_script

# 输出初始 SOCKS5 信息
echo
echo '----------------------------------------'
echo 'Socks5格式-切勿用于违法行为，仅可作为技术交流'
echo '一切行为造成的后果由使用者承担'
echo 'IP/端口/用户名/密码'

# 重置计数器
COUNT=0

# 输出每个公网线路的 SOCKS5 信息
for INFO in "${NET_INFOS[@]}"; do
    PUB_IP=${PUBLIC_IPS[$COUNT]}
    [ -n "$PUB_IP" ] && echo "${PUB_IP}/${SOCKS_PORT}/${SOCKS_USER}/${SOCKS_PASSWORD}"
    ((COUNT++))
done

# 输出初始 L2TP 信息
echo '----------------------------------------'
echo 'L2格式-切勿用于违法行为，仅可作为技术交流'
echo '一切行为造成的后果由使用者承担'
echo 'IP/账号/密码/密钥/对应出口IP'

# 输出用户映射信息
if [ -f "$USER_ROUTE_MAP" ]; then
    while IFS='|' read -r CURRENT_USER CLIENT_VPN_IP IFACE TABLE EXIT_PUB_IP; do
        [ -z "$CURRENT_USER" ] && continue
        [ -z "$EXIT_PUB_IP" ] && EXIT_PUB_IP='未知'
        echo "${ENTRY_PUBLIC_IP}/${CURRENT_USER}/${VPN_PASSWORD}/${VPN_IPSEC_PSK}/对应出口IP:${EXIT_PUB_IP}"
    done < "$USER_ROUTE_MAP"
fi

# 输出后台菜单说明
echo '----------------------------------------'
echo '后台修改命令: wawacloud'
echo