基本情况:

1.多互联网出口:内网拥有1条以上互联网出口(1条电信、1条连通)

项目需求:

  • 域名分别通过两个smartdns实例解析。smartdns_dx -> 电信上游 -> 结果写入ip4_dx; smartdns_lt -> 联通上游 -> 结果写入ip4_lt
  • nft根据目标IP是否匹配ip4_dx / ip4_lt来打标记(mark)。
  • ip route + route table 根据标记(mark)选择不同出口。
  • 动态切换时,通过脚本改写wan_policy_route 链内容
  • hook通过fw4 include持久化,避免fw4 reload后丢失。
  • 最优出局:联通Public DNS Server返回的IP从联通出口出局,电信Public DNS Server返回的IP从电信出口出局。
  • 故障切换:当联通线路故障不可用是,从电信接口出局;当电信接口不可用是,从联通接口出局。
  • 默认情况下,根据系统负载自由决定

接口定义:

  • 电信接口:DX -> 0x200
  • 联通接口:LT -> 0x100

环境需求:
DNSMASQ、SmartDNS、NFT

SMARTDNS配置

运行多个smartdns实例,实现不同上游DNS对于不同的nftset
局域网内部的DNS服务器为dnsmasq,dnsmasq可以配置多个上游服务器,并且开启同时向多个上游服务器同时转发请求。
smartdns实例 smartdns_dx
/etc/smartdns/smartdns_dx.conf

bind 127.0.0.1:554 -nftset '#4:inet#fw4#ip4_dx'
server 192.168.71.1

说明:

  • bind 127.0.0.1:554 -nftset '#4:inet#fw4#ip4_dx' ,监听127.0.0.1的554端口,并写入到nftset '#4:inet#fw4#ip4_dx'中。
  • server 192.168.71.1 指定上游DNS服务器。

smartdns实例 smartdns_lt
/etc/smartdns/smartdns_lt.conf

bind 127.0.0.1:553 -nftset '#4:inet#fw4#ip4_lt'
server 192.168.72.1

说明:

  • bind 127.0.0.1:553 -nftset '#4:inet#fw4#ip4_lt' ,监听127.0.0.1的553端口,并写入到nftset '#4:inet#fw4#ip4_lt'中。
  • server 192.168.72.1 指定上游DNS服务器。

分别为smartdns_dx和smartdns_lt创建服务,实现开机自动启动
file name: smartdns_dx

#!/bin/sh /etc/rc.common

START=95
USE_PROCD=1

PROG="/usr/sbin/smartdns"
CONF="/etc/smartdns/smartdns_dx.conf"
PIDFILE="/run/smartdns_dx.pid"

start_service() {
    procd_open_instance
    procd_set_param command "$PROG" -c "$CONF" -p "$PIDFILE" -f
    procd_set_param respawn 3600 5 5
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_close_instance
}

stop_service() {
    [ -f "$PIDFILE" ] && kill "$(cat "$PIDFILE")" 2>/dev/null
}

将该服务启用并启动。

chmod +x /etc/init.d/smartdns_dx
/etc/init.d/smartdns_dx enable
/etc/init.d/smartdns_dx start

另外一个SmartDNS实例,同理配置。

---至此,内网DNS服务器的相关配置已经完成,内网DNS服务会将不同上游DNS返回的IP加入到不同的NFT SET列表中。方便后面引用。

防火墙转发策略配置

为nft配置WAN 转发策略。

set ip4_dx {
    type ipv4_addr
    flags interval
}

set ip4_lt {
    type ipv4_addr
    flags interval
}

chain wan_policy_route {
    ip daddr @ip4_dx meta mark set 0x100
    ip daddr @ip4_lt meta mark set 0x200
}

上述配置完成后,执行下列命令,看看转发链策略是否正常引入(此时不会影响内部报文转发)

fw4 reload
nft list set inet fw4 ip4_dx
nft list set inet fw4 ip4_lt
nft list chain inet fw4 wan_policy_route

对内部流量的转发执行wan_policy_route操作。先用下面的命令测试一下工作是否正常。

nft insert rule inet fw4 mangle_prerouting iifname "br-lan" jump wan_policy_route comment "WAN_POLICY_ROUTE"

执行 nft list chain inet fw4 mangle_prerouting 后,查看nft的策略输出,确定wan_policy_route被引用。

table inet fw4 {
        chain mangle_prerouting {
                type filter hook prerouting priority mangle; policy accept;
                iifname "br-lan" jump wan_policy_route comment "WAN_POLICY_ROUTE"
        }
}

策略路由配置

在文件/etc/iproute2/rt_tables中增加dx和lt的表iD

100 dxwan
200 ltwan

策略路由命令,分别为table dxwan 和 table ltwan 配置默认路由和 rule规则。

ip route add default via 192.168.71.1 dev eth0 table dxwan
ip route add default via 192.168.72.1 dev eth1 table ltwan

策略路由验证

ip rule show
ip route show table dxwan
ip route show table ltwan
ip route get 114.114.114.114 mark 0x100
ip route get 114.114.114.114 mark 0x200

ip rule show

0:      from all lookup local
100:    from all fwmark 0x100 lookup dxwan
200:    from all fwmark 0x200 lookup ltwan
32766:  from all lookup main
32767:  from all lookup default
90002:  from all iif lo lookup main

ip route show table dxwan

default via 192.168.71.1 dev eth0 

ip route show table ltwan

default via 192.168.72.1 dev eth1 

ip route get 114.114.114.114 mark 0x100

114.114.114.114 via 192.168.71.1 dev eth0 table dxwan src 192.168.71.253 mark 0x100 uid 0 
    cache 

ip route get 114.114.114.114 mark 0x200

114.114.114.114 via 192.168.72.1 dev eth1 table ltwan src 192.168.72.253 mark 0x200 uid 0 
    cache 

至此,已经完成了不同DNS服务器返回的IP,从对应的互联网接口出局。实现最优访问。

动态切换

默认情况下,电信返回的IP,从电信接口出局;联通返回的IP,从联通接口出局。但是当电信或者联通链路中断后,不能自动实现切换。

创建策略切换脚本 /usr/bin/wan_policy_apply.sh 和 健康检测脚本 /usr/bin/wan_health_check.sh

/usr/bin/wan_policy_apply.sh

#!/bin/sh

MODE="$1"
CHAIN="wan_policy_route"

nft list chain inet fw4 $CHAIN >/dev/null 2>&1 || exit 1

nft flush chain inet fw4 $CHAIN || exit 1

case "$MODE" in
    normal)
        nft add rule inet fw4 $CHAIN ip daddr @ip4_dx meta mark set 0x100
        nft add rule inet fw4 $CHAIN ip daddr @ip4_lt meta mark set 0x200
        logger -t wan_policy "mode=normal dx->0x100 lt->0x200"
        ;;
    dx_only)
        nft add rule inet fw4 $CHAIN ip daddr @ip4_dx meta mark set 0x100
        nft add rule inet fw4 $CHAIN ip daddr @ip4_lt meta mark set 0x100
        logger -t wan_policy "mode=dx_only all->DX"
        ;;
    lt_only)
        nft add rule inet fw4 $CHAIN ip daddr @ip4_dx meta mark set 0x200
        nft add rule inet fw4 $CHAIN ip daddr @ip4_lt meta mark set 0x200
        logger -t wan_policy "mode=lt_only all->LT"
        ;;
    *)
        exit 1
        ;;
esac

并给这个文件赋予执行权限。并可以手动测试该脚本,观察脚本对nft表链的修改。

/usr/bin/wan_policy_apply.sh normal
/usr/bin/wan_policy_apply.sh dx_only
/usr/bin/wan_policy_apply.sh lt_only
nft list chain inet fw4 wan_policy_route

/usr/bin/wan_health_check.sh

#!/bin/sh

URL="https://www.163.com"
DX_IF="eth0"
LT_IF="eth1"

STATE_FILE="/tmp/wan_policy_state"
FAIL_COUNT_FILE="/tmp/wan_fail_count"
THRESHOLD=2

check_if() {
    local iface="$1"
    curl --interface "$iface" \
         --connect-timeout 2 \
         --max-time 4 \
         -k -s -o /dev/null -I "$URL"
    return $?
}

[ -f "$STATE_FILE" ] && CUR_STATE=$(cat "$STATE_FILE") || CUR_STATE="unknown"
[ -f "$FAIL_COUNT_FILE" ] && FAILS=$(cat "$FAIL_COUNT_FILE") || FAILS=0

check_if "$DX_IF"; DX_OK=$?
check_if "$LT_IF"; LT_OK=$?

if [ $DX_OK -eq 0 ] && [ $LT_OK -eq 0 ]; then
    TARGET="normal"
    FAILS=0
elif [ $DX_OK -eq 0 ] && [ $LT_OK -ne 0 ]; then
    FAILS=$((FAILS+1))
    [ $FAILS -ge $THRESHOLD ] && TARGET="dx_only" || TARGET="$CUR_STATE"
elif [ $DX_OK -ne 0 ] && [ $LT_OK -eq 0 ]; then
    FAILS=$((FAILS+1))
    [ $FAILS -ge $THRESHOLD ] && TARGET="lt_only" || TARGET="$CUR_STATE"
else
    FAILS=$((FAILS+1))
    TARGET="$CUR_STATE"
fi

if [ "$TARGET" != "$CUR_STATE" ]; then
    /usr/bin/wan_policy_apply.sh "$TARGET"
    echo "$TARGET" > "$STATE_FILE"
fi

echo "$FAILS" > "$FAIL_COUNT_FILE"

脚本持续对www.163.com进行检测来测试相应的链路是否正常。
将健康检测脚本加入计划任务,定时执行。
crontab -e

*/1 * * * * /usr/bin/wan_health_check.sh

===
OK
至此一个理论很舒服的内网已经OK了。

标签: 负载分担, smartdns, nft

添加新评论