双端独立 IP 的 IX 路由配置教程,以 halo 家的 IX 产品为例。

流程图 #

flowchart

前置端 #

IX产品一般只对各大云厂有路由,你需要准备阿里云、腾讯云等云厂的VPS为前置机。

使用 nft 规则直接转发特定端口到IX端。编辑/etc/nftables.conf,注意替换尖括号中的内容。

#!/usr/sbin/nft -f

flush ruleset

table inet main {
    chain prerouting {
        type nat hook prerouting priority dstnat; policy accept;
        meta l4proto { tcp, udp } th dport <PORT> dnat ip to <IX-PUBLIC-IP:PORT>
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
        ct state { established, related } accept
        ct status dnat accept
    }

    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        ct status dnat masquerade
    }
}
## 测试配置
nft -c -f /etc/nftables.conf

## 使nft生效
systemctl restart nftables
systemctl enable nftables

## 开启 ipv4 转发
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/90-fwd.conf
sysctl -p /etc/sysctl.d/90-fwd.conf

IX端 #

halo家的IX和HK端都是独立的IP和机器,需要同时购买IX和HK两个VPS,然后在面板上组SDWAN,让IX和HK走内网通信。

要实现各接口流量原进原出、IX机通外网,同时不影响nft转发,策略路由是最好的方案。

我们可以创建个systemd服务,开机后自动设置。由于配置过程中会破坏默认路由,以下操作建议在VNC中进行,或者从HK端SSH连接到IX端进行配置。

创建一个配置脚本并编辑/root/apps/netinit/netinit.sh

#!/bin/bash 

if [ $# != 1 ]
then
   echo "Use $(basename "$0") <set|clear>"
   exit 1;
fi

TOIP="192.168.X.X"    ## HK端SDWAN-IP,注意修改为你自己的
PORT_FWD="25000-25020"  ## 端口转发规则,注意修改为你自己的

TO_PREFIX=$(echo "$TOIP" | cut -d. -f1-3)
SDWAN_DEV=$(ip -o -4 addr show | grep "$TO_PREFIX" | awk '{print $2}')
PUBLIC_DEV=$(ip -4 route show default | awk '{print $5}')

nftrule=$(cat <<EOF
table inet main {
    chain prerouting {
        type filter hook prerouting priority mangle; policy accept;
        ct state != new meta mark set ct mark
        iifname "$PUBLIC_DEV" ct mark 0 meta mark set 0x5 ct mark set meta mark
    }

    chain prerouting_fwd {
        type nat hook prerouting priority dstnat; policy accept;
        iifname "$PUBLIC_DEV" meta l4proto { tcp, udp } th dport $PORT_FWD dnat ip to $TOIP
    }

    chain output {
        type route hook output priority mangle; policy accept;
        meta mark 0 meta mark set ct mark
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
        ct state { established, related } accept
        ct status dnat accept
    }

    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        ct status dnat oifname "$SDWAN_DEV" masquerade
    }
}
EOF
)

function ClearRules()
{
    IPRULE=$(ip rule show | grep 100)
    if [ -n "$IPRULE" ]
    then
        ip route del default via "$TOIP" dev "$SDWAN_DEV" table 100
        ip rule del not fwmark 0x5 lookup 100
    fi

    nft flush ruleset
    echo "clear rules"
}

function SetRules()
{
    ip route add default via "$TOIP" dev "$SDWAN_DEV" table 100
    ip rule add not fwmark 0x5 lookup 100

    echo "$nftrule" | nft -f -
    sysctl -w net.ipv4.ip_forward=1 > /dev/null
    echo "nameserver 8.8.4.4" > /etc/resolv.conf
    echo "set rules"
}

if [ $1 = 'set' ]
then
    ClearRules
    SetRules
elif [ $1 = 'clear' ]
then
    ClearRules
fi

创建服务文件并编辑/root/apps/netinit/netinit.service:

[Unit]
Description=netinit.sh
After=network.target nss-lookup.target network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/bash /root/apps/netinit/netinit.sh set
ExecStop=/usr/bin/bash /root/apps/netinit/netinit.sh clear

[Install]
WantedBy=multi-user.target
## 测试
bash /root/apps/netinit/netinit.sh set

## 检查配置
root@debian:~# ip r
default via 165.X.X.1 dev ens18 proto static
10.0.X.0/20 dev ens19 proto kernel scope link src 10.0.X.X
165.X.X.0/23 dev ens18 proto kernel scope link src 165.X.X.X
192.168.X.0/23 dev ens20 proto kernel scope link src 192.168.X.X

root@debian:~# ip rule
0:      from all lookup local
32765:  not from all fwmark 0x5 lookup 100
32766:  from all lookup main
32767:  from all lookup default

root@debian:~# ip r show table 100
default via <HK-SDWAN-IP> dev ens20

root@debian:~# nft list ruleset
table inet main {
    chain prerouting {
        type filter hook prerouting priority mangle; policy accept;
        ct state != new meta mark set ct mark
        iifname "ens18" ct mark 0x00000000 meta mark set 0x00000005 ct mark set meta mark
    }

    chain prerouting_fwd {
        type nat hook prerouting priority dstnat; policy accept;
        iifname "ens18" meta l4proto { tcp, udp } th dport 1234-5678 dnat ip to 192.168.X.X
    }

    chain output {
        type route hook output priority mangle; policy accept;
        meta mark 0x00000000 meta mark set ct mark
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
        ct state { established, related } accept
        ct status dnat accept
    }

    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        ct status dnat oifname "ens20" masquerade
    }
}
## 使其生效
ln -s /root/apps/netinit/netinit.service /etc/systemd/system/netinit.service
systemctl daemon-reload
systemctl restart netinit
systemctl enable netinit

HK端 #

使用 nft 规则设置IX端为白名单,避免被邻居偷路由。

编辑/etc/nftables.conf,注意替换尖括号中的内容。

#!/usr/sbin/nft -f

flush ruleset

table inet main {
    chain forward {
        type filter hook forward priority filter; policy drop;
        ct state { established, related } accept
        ip saddr <IX-SDWAN-IP> accept
    }

    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        ip saddr <IX-SDWAN-IP> masquerade
    }
}
## 测试配置
nft -c -f /etc/nftables.conf

## 使nft生效
systemctl restart nftables
systemctl enable nftables

## 开启 ipv4 转发
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/90-fwd.conf
sysctl -p /etc/sysctl.d/90-fwd.conf