#!/bin/sh
# @desc: 修复同网段路由冲突: 只接管发往特定IP的流量
# @usage: wcl same_subnet <网卡> <本机目标IP> <表ID> [网关IP] [down]
#   例: wcl same_subnet eth0.2 192.168.8.227 221
#       1. 启用(直连模式): <网卡> <本机IP> <表ID>
#       2. 启用(网关模式): <网卡> <本机IP> <表ID> <网关IP>
#       3. 还原/卸载:      <网卡> <本机IP> <表ID> down
# ============================================================
# 脚本名称: fix_conflict_route.sh (V6 - 自定义表ID版)
# 功能描述: 
#   解决双网卡同网段冲突，仅接管发往特定IP的流量。
#   支持自定义路由表ID/防火墙标记ID。
#
# 使用方法: 
#   格式: ./script.sh <网卡> <本机目标IP> <表ID> [网关IP] [down]
#
#   1. 启用 (直连模式):
#      ./script.sh eth0.2 192.168.8.227 221
#
#   2. 启用 (网关模式):
#      ./script.sh eth0.2 192.168.8.227 221 192.168.8.1
#
#   3. 还原/卸载:
#      ./script.sh eth0.2 192.168.8.227 221 down
# ============================================================

# 1. 参数获取
IFACE="$1"
TARGET_IP="$2"
CUSTOM_ID="$3"
ARG4="$4"
ARG5="$5"

# 2. 基础校验
if [ -z "$IFACE" ] || [ -z "$TARGET_IP" ] || [ -z "$CUSTOM_ID" ]; then
    echo "错误: 参数不足。"
    echo "用法: $0 <网卡> <目标IP> <表ID> [网关] [down]"
    echo "示例: $0 eth0.2 192.168.8.227 221"
    exit 1
fi

if [ ! -d "/sys/class/net/$IFACE" ]; then
    echo "错误: 网卡 $IFACE 不存在。"
    exit 1
fi

# 3. 解析模式 (Enable / Disable) 和 网关
MODE="enable"
GATEWAY_IP=""

# 检查第4个参数 (可能是网关，也可能是 down)
case "$ARG4" in
    down|clean|off|remove)
        MODE="disable"
        ;;
    *)
        GATEWAY_IP="$ARG4"
        ;;
esac

# 检查第5个参数 (如果第4个是网关，第5个可能是 down)
case "$ARG5" in
    down|clean|off|remove)
        MODE="disable"
        ;;
esac

# ============================================================
# 函数: 清理逻辑
# ============================================================
clean_up() {
    echo ">> [还原模式] 正在移除策略 (IP:$TARGET_IP, ID:$CUSTOM_ID)..."

    # 1. 恢复 rp_filter (仅针对该接口)
    sysctl -w net.ipv4.conf.$IFACE.rp_filter=1 >/dev/null 2>&1

    # 2. 删除 iptables 规则 (精确匹配 IP 和 ID)
    echo "   [-] 移除防火墙标记规则..."
    while iptables -t mangle -D PREROUTING -i $IFACE -d $TARGET_IP -j CONNMARK --set-mark $CUSTOM_ID 2>/dev/null; do :; done
    # 尝试移除通用的 restore-mark (如果有残留)
    iptables -t mangle -D OUTPUT -j CONNMARK --restore-mark 2>/dev/null

    # 3. 删除策略路由规则
    echo "   [-] 移除 ip rule 策略..."
    # 匹配十六进制 (ip rule show 通常显示 16 进制 fwmark)
    while ip rule show | grep -q "fwmark $(printf '0x%x' $CUSTOM_ID)"; do
        ip rule del fwmark $CUSTOM_ID table $CUSTOM_ID 2>/dev/null
    done
    # 匹配十进制 (双重保险)
    while ip rule show | grep -q "fwmark $CUSTOM_ID"; do
        ip rule del fwmark $CUSTOM_ID table $CUSTOM_ID 2>/dev/null
    done

    # 4. 清空路由表
    ip route flush table $CUSTOM_ID
    ip route flush cache
    echo ">> 清理完成。"
}

# ============================================================
# 函数: 启用逻辑
# ============================================================
setup() {
    echo ">> [启用模式] 配置 $IFACE (IP:$TARGET_IP, Table:$CUSTOM_ID)..."

    # 1. 开启宽松模式
    sysctl -w net.ipv4.conf.all.rp_filter=2 >/dev/null 2>&1
    sysctl -w net.ipv4.conf.$IFACE.rp_filter=2 >/dev/null 2>&1

    # 2. 配置 iptables 标记
    echo "   [+] 添加防火墙标记..."
    # 清理旧规则
    iptables -t mangle -D PREROUTING -i $IFACE -d $TARGET_IP -j CONNMARK --set-mark $CUSTOM_ID 2>/dev/null
    iptables -t mangle -D OUTPUT -j CONNMARK --restore-mark 2>/dev/null
    
    # 添加新规则
    iptables -t mangle -A PREROUTING -i $IFACE -d $TARGET_IP -j CONNMARK --set-mark $CUSTOM_ID
    iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark

    # 3. 配置策略路由
    echo "   [+] 添加 ip rule 规则..."
    while ip rule show | grep -q "fwmark $(printf '0x%x' $CUSTOM_ID)"; do
        ip rule del fwmark $CUSTOM_ID table $CUSTOM_ID 2>/dev/null
    done
    ip rule add fwmark $CUSTOM_ID table $CUSTOM_ID priority 1000

    # 4. 填充路由表
    echo "   [+] 配置路由表 $CUSTOM_ID..."
    ip route flush table $CUSTOM_ID
    
    if [ -n "$GATEWAY_IP" ]; then
        ip route add default via $GATEWAY_IP dev $IFACE table $CUSTOM_ID
        echo "       模式: 网关模式 (via $GATEWAY_IP)"
    else
        ip route add default dev $IFACE table $CUSTOM_ID
        echo "       模式: 直连模式 (On-link)"
    fi

    ip route flush cache
    echo ">> 配置成功! 使用表 ID: $CUSTOM_ID"
}

# 执行入口
if [ "$MODE" = "disable" ]; then
    clean_up
else
    setup
fi