#!/bin/bash
# =============================================================
# Debian 12 L2TP/IPsec 多出口终版（双网卡 + ip.txt 8条 VLAN 出口 => 10出口）【修复版】
# 规则：
# - user1 -> eth0 出口
# - user2 -> eth1 出口
# - user3~user10 -> /root/ip.txt 前8行（VLAN_ID MAC IP），创建 eth0.VLAN_ID 做独立出口
# 组件：strongSwan + xl2tpd + ppp + iptables
# 自恢复：wawa-l2tp-restore.service
# wawa：修改 PSK/密码 界面
# 开头：自动写 DNS（223/119）
# =============================================================

set -e
trap 'echo "[ERROR] 行号 ${LINENO} 出错，脚本中止"; exit 1' ERR

# ----------------- DNS FIX -----------------
echo "nameserver 223.5.5.5" > /etc/resolv.conf
echo "nameserver 119.29.29.29" >> /etc/resolv.conf

# ----------------- 固定参数 -----------------
ETH0="eth0"
ETH1="eth1"
PARENT_IF="${ETH0}"
GATEWAY_IP="192.168.0.1"
IP_FILE="/root/ip.txt"

VPN_LOCAL_IP="172.30.0.1"
VPN_POOL_START="172.30.0.10"
VPN_POOL_END="172.30.0.80"

USER_COUNT=10
PPP_BASE_OCTET=10

STATE_FILE="/etc/wawa-l2tp10.env"
RESTORE_SCRIPT="/usr/local/bin/wawa-l2tp-restore.sh"
RESTORE_SERVICE="/etc/systemd/system/wawa-l2tp-restore.service"
WAWA_CMD="/usr/local/bin/wawa"

# ----------------- 检查 -----------------
if [ "$(id -u)" -ne 0 ]; then
  echo "错误：请用 root 运行。"
  exit 1
fi

ip link show "${ETH0}" >/dev/null 2>&1 || { echo "错误：未找到 ${ETH0}"; exit 1; }
ip link show "${ETH1}" >/dev/null 2>&1 || { echo "错误：未找到 ${ETH1}"; exit 1; }
[ -f "${IP_FILE}" ] || { echo "错误：未找到 ${IP_FILE}"; exit 1; }

ETH0_IP="$(ip -4 addr show dev "${ETH0}" | awk '/inet /{print $2}' | head -n1 | cut -d/ -f1 || true)"
ETH1_IP="$(ip -4 addr show dev "${ETH1}" | awk '/inet /{print $2}' | head -n1 | cut -d/ -f1 || true)"
[ -n "${ETH0_IP}" ] || { echo "错误：${ETH0} 没有 IPv4"; exit 1; }
[ -n "${ETH1_IP}" ] || { echo "错误：${ETH1} 没有 IPv4"; exit 1; }

# ----------------- 依赖 -----------------
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y strongswan strongswan-pki xl2tpd ppp iproute2 iptables iptables-persistent vlan curl

# sysctl
cat >/etc/sysctl.d/99-wawa-l2tp10.conf <<EOF
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.${ETH0}.rp_filter = 0
net.ipv4.conf.${ETH1}.rp_filter = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
EOF
sysctl --system >/dev/null 2>&1 || true

# VLAN module
modprobe 8021q || true
cat >/etc/modules-load.d/8021q.conf <<EOF
8021q
EOF

# ----------------- 读取 ip.txt 前8条（关键修复：用 mapfile，避免管道子shell） -----------------
# 只取前三列 VLAN_ID MAC IP，跳过空行/# 注释
mapfile -t VLAN_LINES < <(head -n 8 "${IP_FILE}" | sed 's/\r$//' | awk 'NF>=3 && $1 !~ /^#/ {print $1" "$2" "$3}')

if [ "${#VLAN_LINES[@]}" -lt 8 ]; then
  echo "错误：${IP_FILE} 前8行有效 VLAN 配置不足 8 条（当前 ${#VLAN_LINES[@]} 条）"
  echo "请保证前8行都是：VLAN_ID MAC IP"
  exit 1
fi

# ----------------- 输入 PSK / 密码 -----------------
read -r -p "请输入 IPSec PSK（默认 wawa）: " PSK
PSK="${PSK:-wawa}"

while true; do
  read -r -p "请输入 VPN 登录密码（user1~user10 共用）: " VPN_PASS
  [ -n "${VPN_PASS}" ] && break
  echo "密码不能为空！"
done

# ----------------- 写 state（关键修复：确定写入 EXIT_IFACE_3~10 / EXIT_SNAT_3~10） -----------------
{
  echo "L2TP_PSK=\"${PSK}\""
  echo "L2TP_PASS=\"${VPN_PASS}\""
  echo "USER_COUNT=\"${USER_COUNT}\""
  echo "ETH0=\"${ETH0}\""
  echo "ETH1=\"${ETH1}\""
  echo "PARENT_IF=\"${PARENT_IF}\""
  echo "GATEWAY_IP=\"${GATEWAY_IP}\""

  echo "EXIT_IFACE_1=\"${ETH0}\""
  echo "EXIT_SNAT_1=\"${ETH0_IP}\""

  echo "EXIT_IFACE_2=\"${ETH1}\""
  echo "EXIT_SNAT_2=\"${ETH1_IP}\""

  idx=3
  for line in "${VLAN_LINES[@]}"; do
    VLAN_ID="$(echo "${line}" | awk '{print $1}')"
    VLAN_MAC="$(echo "${line}" | awk '{print $2}')"
    VLAN_IP="$(echo "${line}" | awk '{print $3}')"
    echo "VLAN_ID_${idx}=\"${VLAN_ID}\""
    echo "VLAN_MAC_${idx}=\"${VLAN_MAC}\""
    echo "EXIT_IFACE_${idx}=\"${PARENT_IF}.${VLAN_ID}\""
    echo "EXIT_SNAT_${idx}=\"${VLAN_IP}\""
    idx=$((idx+1))
  done
} > "${STATE_FILE}"

# ----------------- strongSwan -----------------
cat >/etc/ipsec.conf <<EOF
config setup
  uniqueids=no

conn l2tp-psk
  keyexchange=ikev1
  authby=psk
  ike=aes128-sha1-modp1024,3des-sha1-modp1024!
  esp=aes128-sha1,3des-sha1!
  ikelifetime=8h
  keylife=1h
  rekey=no
  type=transport
  left=%defaultroute
  leftprotoport=17/1701
  right=%any
  rightprotoport=17/%any
  auto=add
EOF

cat >/etc/ipsec.secrets <<EOF
%any %any : PSK "${PSK}"
EOF

# ----------------- xl2tpd + ppp -----------------
mkdir -p /etc/xl2tpd
cat >/etc/xl2tpd/xl2tpd.conf <<EOF
[global]
ipsec saref = yes

[lns default]
ip range = ${VPN_POOL_START}-${VPN_POOL_END}
local ip = ${VPN_LOCAL_IP}
require chap = yes
refuse pap = yes
require authentication = yes
pppoptfile = /etc/ppp/options.xl2tpd
length bit = yes
EOF

cat >/etc/ppp/options.xl2tpd <<EOF
require-mschap-v2
ms-dns 119.29.29.29
ms-dns 223.5.5.5
asyncmap 0
auth
crtscts
lock
hide-password
modem
name xl2tpd
proxyarp
lcp-echo-interval 30
lcp-echo-failure 4
mtu 1400
mru 1400
EOF

{
  for u in $(seq 1 "${USER_COUNT}"); do
    ppp_ip="172.30.0.$((PPP_BASE_OCTET + u - 1))"
    echo "\"user${u}\" xl2tpd \"${VPN_PASS}\" ${ppp_ip}"
  done
} > /etc/ppp/chap-secrets
chmod 600 /etc/ppp/chap-secrets

# ----------------- restore 脚本（按 state 精准重建） -----------------
cat > "${RESTORE_SCRIPT}" <<'EOF'
#!/bin/bash
set -e

STATE_FILE="/etc/wawa-l2tp10.env"
# shellcheck disable=SC1090
source "${STATE_FILE}"

USER_COUNT="${USER_COUNT:-10}"
PPP_BASE_OCTET=10

get_gw_for_dev() {
  local dev="$1"
  local gw
  gw="$(ip route show default dev "${dev}" 2>/dev/null | awk '/default/{print $3}' | head -n1 || true)"
  if [ -n "${gw}" ]; then echo "${gw}"; return 0; fi
  gw="$(ip route show default 2>/dev/null | awk '/default/{print $3}' | head -n1 || true)"
  echo "${gw}"
}

ensure_vlans() {
  for u in $(seq 3 "${USER_COUNT}"); do
    eval vlan_id=\$VLAN_ID_${u}
    eval vlan_mac=\$VLAN_MAC_${u}
    eval iface=\$EXIT_IFACE_${u}
    eval ipaddr=\$EXIT_SNAT_${u}

    [ -n "${vlan_id}" ] || continue

    if ! ip link show "${iface}" >/dev/null 2>&1; then
      ip link add link "${PARENT_IF}" name "${iface}" type vlan id "${vlan_id}"
    fi

    ip link set dev "${iface}" address "${vlan_mac}" 2>/dev/null || true
    ip link set "${iface}" up
    ip addr flush dev "${iface}" >/dev/null 2>&1 || true
    ip addr add "${ipaddr}/24" dev "${iface}"
  done
}

ensure_ports_forward() {
  iptables -C INPUT -p udp --dport 500 -j ACCEPT 2>/dev/null || iptables -I INPUT -p udp --dport 500 -j ACCEPT
  iptables -C INPUT -p udp --dport 4500 -j ACCEPT 2>/dev/null || iptables -I INPUT -p udp --dport 4500 -j ACCEPT
  iptables -C INPUT -p udp --dport 1701 -j ACCEPT 2>/dev/null || iptables -I INPUT -p udp --dport 1701 -j ACCEPT

  iptables -P FORWARD ACCEPT
  iptables -C FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
  iptables -C FORWARD -s 172.30.0.0/24 -j ACCEPT 2>/dev/null || iptables -A FORWARD -s 172.30.0.0/24 -j ACCEPT

  iptables -t mangle -C FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu 2>/dev/null || \
    iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
}

ensure_pbr() {
  for u in $(seq 1 "${USER_COUNT}"); do
    ppp_ip="172.30.0.$((PPP_BASE_OCTET + u - 1))"
    table_id=$((4000 + u))
    eval iface=\$EXIT_IFACE_${u}

    ip rule del from "${ppp_ip}/32" table "${table_id}" priority "${table_id}" 2>/dev/null || true
    ip route flush table "${table_id}" 2>/dev/null || true

    ip rule add from "${ppp_ip}/32" table "${table_id}" priority "${table_id}"
    gw="$(get_gw_for_dev "${iface}")"
    [ -n "${gw}" ] || gw="${GATEWAY_IP}"
    ip route add default via "${gw}" dev "${iface}" table "${table_id}"
  done
  ip route flush cache || true
}

ensure_nat() {
  iptables -t nat -N WAWA_L2TP 2>/dev/null || true
  iptables -t nat -F WAWA_L2TP
  iptables -t nat -C POSTROUTING -j WAWA_L2TP 2>/dev/null || iptables -t nat -A POSTROUTING -j WAWA_L2TP

  for u in $(seq 1 "${USER_COUNT}"); do
    ppp_ip="172.30.0.$((PPP_BASE_OCTET + u - 1))"
    eval out_ip=\$EXIT_SNAT_${u}
    iptables -t nat -A WAWA_L2TP -s "${ppp_ip}/32" -j SNAT --to-source "${out_ip}"
  done

  iptables -t nat -A WAWA_L2TP -s 172.30.0.0/24 -j MASQUERADE
  netfilter-persistent save >/dev/null 2>&1 || true
}

main() {
  ensure_vlans
  ensure_ports_forward
  ensure_pbr
  ensure_nat
}
main
EOF
chmod +x "${RESTORE_SCRIPT}"

cat > "${RESTORE_SERVICE}" <<EOF
[Unit]
Description=WawaCloud L2TP 10-exit restore (eth0/eth1 + ip.txt 8 VLAN) [fixed]
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=${RESTORE_SCRIPT}
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable wawa-l2tp-restore.service
systemctl restart wawa-l2tp-restore.service

systemctl enable --now strongswan-starter
systemctl restart strongswan-starter
systemctl enable --now xl2tpd
systemctl restart xl2tpd

# wawa
cat > "${WAWA_CMD}" <<'EOF'
#!/bin/bash
set -e
STATE_FILE="/etc/wawa-l2tp10.env"

if [ "$(id -u)" -ne 0 ]; then
  echo "错误：请用 root 执行。"
  exit 1
fi
[ -f "${STATE_FILE}" ] || { echo "错误：缺少 ${STATE_FILE}"; exit 1; }

# shellcheck disable=SC1090
source "${STATE_FILE}"

OLD_PSK="$(grep -Eo 'PSK ".*"' /etc/ipsec.secrets 2>/dev/null | head -n1 | sed 's/PSK "//;s/"$//' || true)"
OLD_PASS="$(awk 'NR==1{print $4}' /etc/ppp/chap-secrets 2>/dev/null | tr -d '"' || true)"
USER_COUNT="${USER_COUNT:-10}"

echo ""
echo "========== L2TP 一键修改界面（修复版） =========="
echo "当前 PSK      : ${OLD_PSK:-未知}"
echo "当前 VPN 密码 : ${OLD_PASS:-未知}"
echo "提示：直接回车表示保留不变"
echo "------------------------------------------------"
read -r -p "输入新的 PSK(回车保留): " NEW_PSK
read -r -p "输入新的 VPN 密码(回车保留): " NEW_PASS

if [ -n "${NEW_PSK}" ]; then
  cat >/etc/ipsec.secrets <<EOF2
%any %any : PSK "${NEW_PSK}"
EOF2
fi

if [ -n "${NEW_PASS}" ]; then
  {
    for u in $(seq 1 "${USER_COUNT}"); do
      ppp_ip="172.30.0.$((10 + u - 1))"
      echo "\"user${u}\" xl2tpd \"${NEW_PASS}\" ${ppp_ip}"
    done
  } > /etc/ppp/chap-secrets
  chmod 600 /etc/ppp/chap-secrets
fi

systemctl restart wawa-l2tp-restore.service || true
systemctl restart strongswan-starter || true
systemctl restart xl2tpd || true
echo "✅ 修改完成并已生效。"
EOF
chmod +x "${WAWA_CMD}"

# 输出
SERVER_PUBIP="$(curl -s --max-time 6 ifconfig.me || echo "获取失败")"

get_pub_by_src() {
  local src_ip="$1"
  local out ip4
  for url in "http://whatismyip.akamai.com" "https://api.ipify.org" "https://ip.sb" "https://ipv4.icanhazip.com"; do
    out="$(curl -s --max-time 6 --connect-timeout 4 --interface "${src_ip}" "${url}" 2>/dev/null || true)"
    ip4="$(echo "${out}" | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n1 || true)"
    [ -n "${ip4}" ] && { echo "${ip4}"; return 0; }
  done
  echo "获取失败"
}

echo ""
echo "服务器公网IP / 用户名 / 密码 / PSK / 出口接口 / 出口内网IP / 出口公网IP"
echo "--------------------------------------------------------------------------------"
for u in $(seq 1 "${USER_COUNT}"); do
  eval dev=\$EXIT_IFACE_${u}
  eval out_ip=\$EXIT_SNAT_${u}
  wan="$(get_pub_by_src "${out_ip}")"
  echo "${SERVER_PUBIP} / user${u} / ${VPN_PASS} / ${PSK} / ${dev} / ${out_ip} / ${wan}"
done

echo ""
echo "✅ 完成：L2TP 10 出口已部署（规则=eth0/eth1 + ip.txt 前8条 VLAN）"
echo "✅ 一键修改命令：wawa"
