最後更
新日:2005/12/28
FreeBSD + PF + Shell Scritp + Load Sharing + Redundant
Description :
上星期做了在 兩條 ADSL 上自動切換路由 的測試後,有個朋友就跟我說光兩條線路做 Redundant 是不是有點浪費?我想想也對,於是我想到了FreeBSD 中的 PF 有個 round-robin 的功能,它可做 Triffic Outgoing 的 Load Balancing 流量平均的分配,因此再把這功能的應用加上,這下子不但不會斷線還可以於平常就同時使用兩條 ADSL,真的太棒了 ^^
這次的 shell script 有些許的改變,我還是使用 ping 的方式來判斷是否斷線,但是所 ping 的標地物 IP 改變了,不再是 ping 固網的 DNS,而是改 ping 固網內部的 ADSL 之 ATM 設備的虛擬 IP 為判斷標準,這樣的用意是讓我們在切換路由到另一家固網時才能精準判斷是否 100% loss。
以下我只把使用過的設定檔 pf.conf、pf2.conf 及 loadsharing.sh 這個 shell script 檔貼出來,至於 Firewall + NAT 的安裝過程就不贅述,請參考之前的文章:兩條 ADSL 上自動切換路由 。
Environment :
硬體:i386 PC
Intel P3 500
記憶體網卡:512M RAM
作業系統:FreeBSD 6.0
Release
網路卡三片:xl0 vr0 兩片對外 de0 一片對內
Drawing :

Hinet ADSL ATM 內部的虛擬 IP:10.88.88.56 ext_gw1:210.xx.xx.254
xl0:210.xx.xx.8 de0:10.77.77.254
TFN ADSL ATM 內部的虛擬 IP:10.36.128.10 ext_gw2:61.xx.xx.254
vr0:61.xx.xx.6
Setp
1.
#vi /usr/local/sbin/loadsharing.sh # 開始編寫 5 秒鐘於背景跑一次的 shell script
#!/bin/sh
H_ATM_IP="10.88.88.50"
H_GW1_IP="210.xx.xx.254"
H_EXT_IP="210.xx.xx.8"
T_ATM_IP="10.36.128.10"
T_GW2_IP="61.xx.xx.254"
T_EXT_IP="61.xx.xx.6"
active="P"
while [ 1 ]; do
STATUS1=`ping -c 1 $H_ATM_IP |grep loss |awk '{ print $7; }'`
STATUS2=`ping -c 1 $T_ATM_IP |grep loss |awk '{ print $7; }'`
if [ "$STATUS1" = "100%" ]; then
if [ "$STATUS2" = "100%" ]; then # ALL line down
if [ "$active" = "P" ]; then
echo -e "\n\
ALL ADSL DOWN!\n\
Date: $(date)\n\
Host: $(hostname)\n\
" | /usr/bin/mail -s "$(date) ALL ADSL DOWN!" root
active="S"
else
echo -e "ALL ADSL DOWN!"
fi
else # Hinet down, TFN up
if [ "$active" = "P" ]; then
echo -e "\n\
ADSL $H_ATM_IP DOWN!\n\
Date: $(date)\n\
Host: $(hostname)\n\
" | /usr/bin/mail -s "$(date) ADSL Hinet DOWN!" root
route delete -net 0.0.0.0
route add -net 0.0.0.0 -gateway $T_GW2_IP
pfctl -f /etc/pf2.conf
active="S"
else
echo -e "Hinet ADSL DOWN!"
fi
fi
else
if [ "$STATUS2" = "100%" ]; then # Hinet up, TFN down
if [ "$active" = "P" ]; then
echo -e "\n\
ADSL $T_ATM_IP DOWN!\n\
Date: $(date)\n\
Host: $(hostname)\n\
" | /usr/bin/mail -s "$(date) ADSL TFN DOWN!" root
route delete -net 0.0.0.0
route add -net 0.0.0.0 -gateway $H_GW1_IP
pfctl -f /etc/pf2.conf
active="S"
else
echo -e "TFN ADSL DOWN!"
fi
else # All line up
if [ "$active" = "S" ]; then
echo -e "\n\
ALL ADSL UP!\n\
Date: $(date)\n\
Host: $(hostname)\n\
" | /usr/bin/mail -s "$(date) ALL ADSL UP!" root
pfctl -f /etc/pf.conf
active="P"
else
echo -e "ALL ADSL UP!"
fi
fi
fi
sleep 5
done
#chmod 755 /usr/local/sbin/loadsharing.sh # 改變權限更改為可執行
Setp 2.
#vi /etc/pf.conf # PF 設定檔,讓兩片對外的網卡流量負載平衡
lan_net="10.77.77.0/24"
ext_if1="xl0"
ext_if2="vr0"
int_if="de0"
ext_gw1="210.xx.xx.254"
ext_gw2="61.xx.xx.254"
tcp_services= "{ 22, 113 }"
#
# nat outgoing connections on each internet interface
nat on $ext_if1 from $lan_net to any -> ($ext_if1)
nat on $ext_if2 from $lan_net to any -> ($ext_if2)
# default deny
block in from any to any
block out from any to any
# pass all outgoing packets on internal interface
pass out on $int_if from any to $lan_net
# pass in quick any packets destined for the gateway itself
pass in quick on $int_if from $lan_net to $int_if
# load balance outgoing tcp traffic from internal network.
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
proto tcp from $lan_net to any flags S/SA modulate state
# load balance outgoing udp and icmp traffic from internal network
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
proto { udp, icmp } from $lan_net to any keep state
# general "pass out" rules for external interfaces
pass out on $ext_if1 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if1 proto { udp, icmp } from any to any keep state
pass out on $ext_if2 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if2 proto { udp, icmp } from any to any keep state
# route packets from any IPs on $ext_if1 to $ext_gw1 and the same for
# $ext_if2 and $ext_gw2
pass out on $ext_if1 route-to ($ext_if2 $ext_gw2) from $ext_if2 to any
pass out on $ext_if2 route-to ($ext_if1 $ext_gw1) from $ext_if1 to any
#
pass in on $ext_if1 inet proto tcp from any to ($ext_if1) \
port $tcp_services flags S/SA keep state
pass in on $ext_if2 inet proto tcp from any to ($ext_if2) \
port $tcp_services flags S/SA keep state
#vi /etc/pf2.conf # PF 的一般設定,這邊是做為有斷線時所切換採用的 rule
ext_if1="xl0"
ext_if2="vr0"
int_if="de0"
#
nat on $ext_if1 from $int_if:network to any -> ($ext_if1)
nat on $ext_if2 from $int_if:network to any -> ($ext_if2)
rdr on $int_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021
#
pass in all
pass out all
Setp 3.
#sh -x /usr/local/sbin/loadsharing.sh # 可測試 debug 程式
#sh /usr/local/sbin/loadsharing.sh & # 讓程式在背景跑
#vi /usr/local/etc/rc.d/boot_loadsharing.sh # 簡易的開機自動啟動讓程式跑在背景
sh /usr/local/sbin/loadsharing.sh &