#!/bin/bash
# ============================================================================
# 自动网络性能检测与内核优化脚本
# 适配：Debian/Ubuntu, CentOS/RHEL/Alma/Rocky, Alpine, Arch, Fedora
# 作者：kejilion
# ============================================================================

# ======================== 颜色定义 ========================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m'

# ======================== 工具函数 ========================
info()  { echo -e "${CYAN}[检测]${NC} $*"; }
ok()    { echo -e "${GREEN}[✓]${NC} $*"; }
warn()  { echo -e "${YELLOW}[!]${NC} $*"; }
fail()  { echo -e "${RED}[✗]${NC} $*"; }
title() { echo -e "\n${WHITE}━━━ $* ━━━${NC}"; }

# 获取内核主版本号用于兼容性判断
kernel_version() {
    local ver
    ver=$(uname -r | grep -oP '^\d+\.\d+')
    echo "$ver"
}

# 比较版本号: version_ge 5.4 4.9 => true
version_ge() {
    printf '%s\n%s' "$2" "$1" | sort -V -C
}

# 安全读取 sysctl 值
sysctl_get() {
    sysctl -n "$1" 2>/dev/null || echo "N/A"
}

# 获取发行版
detect_distro() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        case "$ID" in
            debian|ubuntu|linuxmint|pop|kali) echo "debian" ;;
            centos|rhel|almalinux|rocky|ol|amzn|fedora) echo "rhel" ;;
            alpine) echo "alpine" ;;
            arch|manjaro|endeavouros) echo "arch" ;;
            opensuse*|sles) echo "suse" ;;
            *) echo "unknown" ;;
        esac
    elif [ -f /etc/debian_version ]; then
        echo "debian"
    elif [ -f /etc/redhat-release ]; then
        echo "rhel"
    else
        echo "unknown"
    fi
}

# ======================== 网络检测 ========================

# 检测网卡速率（Mbps）
detect_link_speed() {
    local speed=0
    local iface

    # 获取默认路由网卡
    iface=$(ip route show default 2>/dev/null | awk '/default/{print $5}' | head -1)
    [ -z "$iface" ] && iface=$(ls /sys/class/net/ | grep -v lo | head -1)

    if [ -n "$iface" ]; then
        # 尝试 ethtool
        if command -v ethtool &>/dev/null; then
            speed=$(ethtool "$iface" 2>/dev/null | awk '/Speed:/{gsub(/[^0-9]/,"",$2); print $2}')
        fi

        # 尝试 sysfs
        if [ -z "$speed" ] || [ "$speed" = "0" ]; then
            speed=$(cat "/sys/class/net/$iface/speed" 2>/dev/null)
        fi
    fi

    # VPS 虚拟网卡可能读不到速率，默认 1000
    [ -z "$speed" ] || [ "$speed" -le 0 ] 2>/dev/null && speed=1000

    echo "$speed"
}

# 检测网络延迟（ms，ping 多个公共 DNS）
detect_latency() {
    local total=0 count=0 avg
    local targets=("8.8.8.8" "1.1.1.1" "9.9.9.9" "208.67.222.222")

    for t in "${targets[@]}"; do
        local rtt
        rtt=$(ping -c 3 -W 2 "$t" 2>/dev/null | awk -F'/' '/avg/{print $5}')
        if [ -n "$rtt" ]; then
            total=$(awk "BEGIN{print $total + $rtt}")
            count=$((count + 1))
        fi
    done

    if [ "$count" -gt 0 ]; then
        avg=$(awk "BEGIN{printf \"%.1f\", $total / $count}")
    else
        avg="50"
    fi

    echo "$avg"
}

# 检测丢包率（%）
detect_packet_loss() {
    local loss
    loss=$(ping -c 10 -W 2 8.8.8.8 2>/dev/null | awk -F',' '/loss/{gsub(/%/,"",$3); gsub(/ packet/,"",$3); print $3+0}')
    [ -z "$loss" ] && loss=0
    echo "$loss"
}

# 获取当前 TCP 拥塞算法
detect_congestion() {
    sysctl_get net.ipv4.tcp_congestion_control
}

# 检测总物理内存（MB）
detect_memory_mb() {
    awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo
}

# ======================== 带宽分级 ========================
# 返回: low / medium / high / ultra
classify_bandwidth() {
    local speed=$1
    if [ "$speed" -lt 100 ]; then
        echo "low"
    elif [ "$speed" -lt 1000 ]; then
        echo "medium"
    elif [ "$speed" -lt 10000 ]; then
        echo "high"
    else
        echo "ultra"
    fi
}

# ======================== 核心优化函数 ========================

auto_optimize_network() {
    local CONF="/etc/sysctl.d/99-network-optimize.conf"
    local BACKUP="/etc/sysctl.d/99-network-optimize.conf.bak.$(date +%s)"
    local KVER
    KVER=$(kernel_version)
    local DISTRO
    DISTRO=$(detect_distro)

    echo -e "${WHITE}"
    echo "╔══════════════════════════════════════════════════════════════╗"
    echo "║          自动网络性能检测与内核优化                        ║"
    echo "╚══════════════════════════════════════════════════════════════╝"
    echo -e "${NC}"

    # ── 权限检查 ──
    if [ "$(id -u)" -ne 0 ]; then
        fail "请以 root 权限运行"
        return 1
    fi

    # ── 环境信息 ──
    title "环境信息"
    info "发行版: $(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || echo "$DISTRO")"
    info "内核版本: $(uname -r)"
    info "架构: $(uname -m)"
    info "内存: $(detect_memory_mb) MB"

    # ── 网络检测 ──
    title "网络性能检测"

    local SPEED LATENCY LOSS CONGESTION MEM_MB BW_CLASS

    info "检测网卡速率..."
    SPEED=$(detect_link_speed)
    ok "链路速率: ${SPEED} Mbps"

    info "检测网络延迟..."
    LATENCY=$(detect_latency)
    ok "平均延迟: ${LATENCY} ms"

    info "检测丢包率..."
    LOSS=$(detect_packet_loss)
    ok "丢包率: ${LOSS}%"

    CONGESTION=$(detect_congestion)
    ok "当前拥塞算法: ${CONGESTION}"

    MEM_MB=$(detect_memory_mb)
    BW_CLASS=$(classify_bandwidth "$SPEED")

    title "检测结论"
    case "$BW_CLASS" in
        low)   info "带宽等级: ${YELLOW}低带宽 (<100M)${NC} → 保守优化" ;;
        medium) info "带宽等级: ${CYAN}中带宽 (100M-1G)${NC} → 标准优化" ;;
        high)  info "带宽等级: ${GREEN}高带宽 (1G-10G)${NC} → 激进优化" ;;
        ultra) info "带宽等级: ${WHITE}超高带宽 (>10G)${NC} → 极致优化" ;;
    esac

    local HIGH_LATENCY=false
    if [ "$(awk "BEGIN{print ($LATENCY > 100)}")" = "1" ]; then
        HIGH_LATENCY=true
        warn "高延迟网络 (${LATENCY}ms > 100ms)，启用额外优化"
    fi

    # ── 备份现有配置 ──
    title "备份与准备"
    if [ -f "$CONF" ]; then
        cp "$CONF" "$BACKUP"
        ok "已备份: $BACKUP"
    else
        ok "首次优化，无需备份"
    fi

    # ── 根据带宽等级设定参数 ──
    local TCP_RMEM_MAX TCP_WMEM_MAX TCP_RMEM TCP_WMEM
    local NETDEV_BUDGET NETDEV_BUDGET_USECS
    local SOMAXCONN NETDEV_BACKLOG
    local CONNTRACK_MAX

    case "$BW_CLASS" in
        low)
            TCP_RMEM="4096 65536 2097152"
            TCP_WMEM="4096 65536 2097152"
            TCP_RMEM_MAX=2097152
            TCP_WMEM_MAX=2097152
            SOMAXCONN=1024
            NETDEV_BACKLOG=1000
            NETDEV_BUDGET=300
            NETDEV_BUDGET_USECS=2000
            CONNTRACK_MAX=65536
            ;;
        medium)
            TCP_RMEM="4096 131072 16777216"
            TCP_WMEM="4096 131072 16777216"
            TCP_RMEM_MAX=16777216
            TCP_WMEM_MAX=16777216
            SOMAXCONN=4096
            NETDEV_BACKLOG=5000
            NETDEV_BUDGET=600
            NETDEV_BUDGET_USECS=4000
            CONNTRACK_MAX=262144
            ;;
        high)
            TCP_RMEM="4096 262144 67108864"
            TCP_WMEM="4096 262144 67108864"
            TCP_RMEM_MAX=67108864
            TCP_WMEM_MAX=67108864
            SOMAXCONN=8192
            NETDEV_BACKLOG=10000
            NETDEV_BUDGET=1200
            NETDEV_BUDGET_USECS=6000
            CONNTRACK_MAX=524288
            ;;
        ultra)
            TCP_RMEM="4096 524288 134217728"
            TCP_WMEM="4096 524288 134217728"
            TCP_RMEM_MAX=134217728
            TCP_WMEM_MAX=134217728
            SOMAXCONN=16384
            NETDEV_BACKLOG=20000
            NETDEV_BUDGET=2400
            NETDEV_BUDGET_USECS=8000
            CONNTRACK_MAX=1048576
            ;;
    esac

    # 高延迟追加：加大初始窗口
    local HIGH_LAT_EXTRA=""
    if $HIGH_LATENCY; then
        # 高延迟网络使用更大的初始拥塞窗口提升吞吐
        HIGH_LAT_EXTRA="
# ── 高延迟网络额外优化 ──
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_notsent_lowat = 16384"
    fi

    # 内存不足时缩小缓冲区
    if [ "$MEM_MB" -lt 512 ]; then
        warn "内存不足 512MB，缩小 TCP 缓冲区"
        TCP_RMEM="4096 32768 1048576"
        TCP_WMEM="4096 32768 1048576"
        TCP_RMEM_MAX=1048576
        TCP_WMEM_MAX=1048576
        CONNTRACK_MAX=32768
    fi

    # ── 根据内存大小设定 VM 参数 ──
    local SWAPPINESS MIN_FREE_KB
    if [ "$MEM_MB" -ge 16384 ]; then
        # 16G+ 大内存：激进
        SWAPPINESS=5
        MIN_FREE_KB=131072
    elif [ "$MEM_MB" -ge 4096 ]; then
        # 4G-16G：标准
        SWAPPINESS=10
        MIN_FREE_KB=65536
    elif [ "$MEM_MB" -ge 1024 ]; then
        # 1G-4G：保守
        SWAPPINESS=20
        MIN_FREE_KB=32768
    else
        # <1G：极度保守
        SWAPPINESS=30
        MIN_FREE_KB=16384
    fi

    # ── BBR 检测与加载 ──
    title "TCP 拥塞算法优化"
    local USE_BBR=false
    local TARGET_CC="cubic"

    if version_ge "$KVER" "4.9"; then
        # 内核 >= 4.9 支持 BBR
        if ! lsmod 2>/dev/null | grep -q tcp_bbr; then
            modprobe tcp_bbr 2>/dev/null
            if lsmod 2>/dev/null | grep -q tcp_bbr; then
                ok "BBR 模块已加载"
            else
                warn "BBR 模块加载失败，使用 cubic"
            fi
        fi

        if sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -q bbr; then
            USE_BBR=true
            TARGET_CC="bbr"
            ok "拥塞算法: cubic → ${GREEN}BBR${NC}"
        fi
    else
        warn "内核 $KVER < 4.9，不支持 BBR，保持 cubic"
    fi

    # BBR 配合 fq 队列调度
    local QDISC="fq"
    if ! $USE_BBR; then
        QDISC="fq_codel"
    fi

    # ── 生成优化配置 ──
    title "写入优化配置"

    cat > "$CONF" << SYSCTL
# ============================================================================
# 自动网络优化配置
# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
# 带宽等级: $BW_CLASS (${SPEED}Mbps)
# 平均延迟: ${LATENCY}ms | 丢包率: ${LOSS}%
# 内核: $(uname -r) | 发行版: $DISTRO | 内存: ${MEM_MB}MB
# ============================================================================

# ── TCP 拥塞控制 ──
net.core.default_qdisc = $QDISC
net.ipv4.tcp_congestion_control = $TARGET_CC

# ── TCP 缓冲区 ──
net.core.rmem_max = $TCP_RMEM_MAX
net.core.wmem_max = $TCP_WMEM_MAX
net.core.rmem_default = $(echo "$TCP_RMEM" | awk '{print $2}')
net.core.wmem_default = $(echo "$TCP_WMEM" | awk '{print $2}')
net.ipv4.tcp_rmem = $TCP_RMEM
net.ipv4.tcp_wmem = $TCP_WMEM
net.ipv4.udp_rmem_min = 8192
net.ipv4.udp_wmem_min = 8192

# ── 连接队列 ──
net.core.somaxconn = $SOMAXCONN
net.core.netdev_max_backlog = $NETDEV_BACKLOG
net.core.netdev_budget = $NETDEV_BUDGET
$(sysctl -n net.core.netdev_budget_usecs &>/dev/null && echo "net.core.netdev_budget_usecs = $NETDEV_BUDGET_USECS" || echo "# netdev_budget_usecs 不支持，跳过")

# ── TCP 连接优化 ──
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_max_syn_backlog = $SOMAXCONN
net.ipv4.tcp_max_tw_buckets = 65536
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 3
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_window_scaling = 1

# ── 内存与端口 ──
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_mem = $((MEM_MB * 1024 / 8)) $((MEM_MB * 1024 / 4)) $((MEM_MB * 1024 / 2))
net.ipv4.tcp_max_orphans = 32768

# ── 安全与防护 ──
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# ── IPv6 优化 ──
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0

# ── 虚拟内存优化 ──
vm.swappiness = $SWAPPINESS
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.overcommit_memory = 1
vm.min_free_kbytes = $MIN_FREE_KB
vm.vfs_cache_pressure = 50

# ── CPU/内核调度优化 ──
kernel.sched_autogroup_enabled = 0
$([ -f /proc/sys/kernel/numa_balancing ] && echo "kernel.numa_balancing = 0" || echo "# numa_balancing 不支持，跳过")

# ── 文件描述符 ──
fs.file-max = 1048576
fs.nr_open = 1048576

# ── 连接跟踪 ──
$(if [ -f /proc/sys/net/netfilter/nf_conntrack_max ]; then
    echo "net.netfilter.nf_conntrack_max = $CONNTRACK_MAX"
    echo "net.netfilter.nf_conntrack_tcp_timeout_established = 7200"
    echo "net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30"
    echo "net.netfilter.nf_conntrack_tcp_timeout_close_wait = 15"
    echo "net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 15"
else
    echo "# conntrack 未启用，跳过"
fi)
$HIGH_LAT_EXTRA
SYSCTL

    ok "配置已写入: $CONF"

    # ── 应用配置 ──
    title "应用优化"
    local apply_errors
    apply_errors=$(sysctl -p "$CONF" 2>&1 | grep -i "error\|invalid\|cannot" || true)

    if [ -n "$apply_errors" ]; then
        warn "部分参数不支持（已跳过）:"
        echo "$apply_errors" | while read -r line; do
            echo -e "  ${YELLOW}$line${NC}"
        done
    fi
    ok "sysctl 参数已应用"

    # ── 禁用透明大页面（减少延迟抖动） ──
    if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
        echo never > /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null && \
        ok "透明大页面已禁用" || warn "透明大页面禁用失败"
    fi

    # ── 设置文件描述符限制 ──
    if ! grep -q "# network-optimize" /etc/security/limits.conf 2>/dev/null; then
        cat >> /etc/security/limits.conf << 'LIMITS'

# network-optimize - 自动网络优化添加
* soft nofile 1048576
* hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
LIMITS
        ok "文件描述符限制已更新"
    fi

    # ── 持久化 BBR 模块 ──
    if $USE_BBR; then
        if [ ! -f /etc/modules-load.d/bbr.conf ]; then
            echo "tcp_bbr" > /etc/modules-load.d/bbr.conf 2>/dev/null
            ok "BBR 模块持久化"
        fi
    fi

    # ── 优化摘要 ──
    title "优化摘要"
    echo -e "${WHITE}┌──────────────────────────────────────────────────┐${NC}"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "项目" "值"
    echo -e "${WHITE}├──────────────────────────────────────────────────┤${NC}"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "带宽等级" "$BW_CLASS (${SPEED}Mbps)"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "拥塞算法" "$TARGET_CC"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "队列调度" "$QDISC"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "TCP 缓冲区(max)" "$(numfmt --to=iec $TCP_RMEM_MAX 2>/dev/null || echo ${TCP_RMEM_MAX})"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "连接队列" "$SOMAXCONN"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "网络延迟" "${LATENCY}ms"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "高延迟优化" "$(if $HIGH_LATENCY; then echo '已启用'; else echo '未需要'; fi)"
    printf "${WHITE}│${NC} %-20s ${WHITE}│${NC} %-25s ${WHITE}│${NC}\n" "配置文件" "$CONF"
    echo -e "${WHITE}└──────────────────────────────────────────────────┘${NC}"

    echo -e "\n${GREEN}网络优化完成！${NC}"
    echo -e "回滚命令: ${CYAN}restore_network_defaults${NC}"
}

# ======================== 回滚函数 ========================

restore_network_defaults() {
    local CONF="/etc/sysctl.d/99-network-optimize.conf"

    title "回滚网络优化"

    if [ "$(id -u)" -ne 0 ]; then
        fail "请以 root 权限运行"
        return 1
    fi

    # 查找最近的备份
    local latest_bak
    latest_bak=$(ls -t /etc/sysctl.d/99-network-optimize.conf.bak.* 2>/dev/null | head -1)

    if [ -n "$latest_bak" ]; then
        cp "$latest_bak" "$CONF"
        sysctl -p "$CONF" 2>/dev/null
        ok "已从备份恢复: $latest_bak"
    elif [ -f "$CONF" ]; then
        rm -f "$CONF"
        sysctl --system 2>/dev/null
        ok "已删除优化配置，恢复系统默认"
    else
        warn "没有优化配置需要回滚"
        return 0
    fi

    # 清理 limits.conf 添加的部分
    if grep -q "# network-optimize" /etc/security/limits.conf 2>/dev/null; then
        sed -i '/# network-optimize/,+4d' /etc/security/limits.conf
        ok "文件描述符限制已恢复"
    fi

    # 清理 BBR 持久化
    rm -f /etc/modules-load.d/bbr.conf 2>/dev/null

    ok "网络配置已回滚"
}

# ======================== 查看当前状态 ========================

show_network_status() {
    title "当前网络内核参数"

    local params=(
        "net.ipv4.tcp_congestion_control"
        "net.core.default_qdisc"
        "net.core.rmem_max"
        "net.core.wmem_max"
        "net.ipv4.tcp_rmem"
        "net.ipv4.tcp_wmem"
        "net.core.somaxconn"
        "net.core.netdev_max_backlog"
        "net.ipv4.tcp_fastopen"
        "net.ipv4.tcp_tw_reuse"
        "net.ipv4.tcp_fin_timeout"
        "net.ipv4.ip_local_port_range"
        "net.ipv4.tcp_mtu_probing"
        "fs.file-max"
    )

    for p in "${params[@]}"; do
        local val
        val=$(sysctl_get "$p")
        printf "  %-45s = %s\n" "$p" "$val"
    done

    if [ -f /etc/sysctl.d/99-network-optimize.conf ]; then
        echo ""
        ok "优化配置已安装: /etc/sysctl.d/99-network-optimize.conf"
    else
        echo ""
        warn "未检测到优化配置"
    fi
}

# ======================== 入口 ========================
# 用法:
#   source network-optimize.sh
#   auto_optimize_network    # 自动检测并优化
#   show_network_status      # 查看当前状态
#   restore_network_defaults # 回滚到默认
#
# 或直接运行:
#   bash network-optimize.sh
# ========================

if [[ "${BASH_SOURCE[0]}" == "${0}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then
    # 支持参数: bash xxx.sh restore / bash xxx.sh status
    # 支持环境变量: ACTION=restore curl ... | bash
    _action="${1:-${ACTION:-optimize}}"
    case "$_action" in
        restore|rollback|回滚)
            restore_network_defaults
            ;;
        status|状态)
            show_network_status
            ;;
        *)
            auto_optimize_network
            ;;
    esac
fi
