转自:http://www.iuiuiu.com/plog/post/1/29

经过一周的摸索,实现了双adsl链路冗余互备及流量分担,整理如下:
目的:实现双adsl的冗余热备和流量分担

双adsl同时连线;
当两条adsl同时连线时,流量经策略路由分流到两条链路上;
当其中一条adsl掉线时,所有流量自动分配另一条可用连接;
可以扩展到多条ppp连接.

1.添加网卡
插入网卡,启动机器,修改/etc/modules.conf并加入
alias eth2 driver.o

2.配置adsl拨号
通过adsl-setup程序创建ppp0和ppp1的拨号配置文件,并保存配置。
修改/etc/sysconfig/network-script/ifcfg-ppp*文件,将其中的PIDFILE参数设为:
PIDFILE=/var/run/ppp-adsl*.pid
其中*对应0、1等
如果不修改此参数将无法启动第二个ppp接口。

3.启动ppp接口
因为adsl-start 命令缺省只能启动第一的ppp接口。所以要启动两个接口,必须指定配置文件。
adsl-start /etc/sysconfig/network-script/ifcfg-ppp0
adsl-start /etc/sysconfig/network-script/ifcfg-ppp1

4.添加路由表
缺省情况下,系统只有3个路由表,local、main、default,路由规则为所有进入的数据报都参照main、defaul来决策路由,这可以通过ip rule ls来查看。其输出如下:

代码:

[root@linuxrouter root]# ip rule ls
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup 253
[root@linuxrouter root]#
要实现策略路由,添加额外的路由表是必须的。
下面两条命令分别添加名为ppp0和ppp1的路由表。

echo 201 ppp0 >> /etc/iproutes/rt_tables
echo 202 ppp1 >> /etc/iproutes/rt_tables

5.创建路由表项
上面创建的两个路由表都是空表,需要在其中注入路由表项。
本人编写了如下脚本用于注入路由表项:

代码:

#!/bin/bash
# Name: cprt
# This program copy the route from $1 route table to $2 route table,
# exclude the default route entry.

if [ -z "$1" -o -z "$2" ]; then
   echo $"usage: cprt  "
   exit 1
fi

SOURCE=$1
DEST=$2

# Clear the destination route table
echo $"Clearing route table $DEST ......"
echo
/sbin/ip route flush table $DEST

# Inject routes from source to destination
echo $"Injecting route from $SOURCE to $DEST ......"
/sbin/ip route ls table $SOURCE | grep -v default > /tmp/route-tmp
while read line; do
     /sbin/ip route add table $DEST $line
done

把main表中的路由表项复制到ppp0和ppp1中。
将下面命令加入到/etc/rc.d/rc.local中。
cprt main ppp0
cprt main ppp1

此时,两个路由表中都有相同的路由表项了,除了default路由以外。缺省路由的添加要通过另外的途径添加。当ppp激活,或者掉线时,pppd守护进程会调用/etc/ppp/目录下的ip-up、ip-down脚本,这些教本又分别调用ip-up.local、ip-down.local。在此我们利用这两个脚本来对路由表和流量控制策略进行维护,此脚本需要人工创建。

下面时本例中的脚本:
/etc/ppp/ip-up.local

代码:

#!/bin/bash
# Name: ip-up.local
# Created by lyking@CU
# If the if-down is not completed, this script can't be excute.
while [ -e /var/lock/subsys/if-down.$IFNAME ] ; do
       sleep 3
done

# Creat a lock file to prevent the if-down from running on my turn
touch /var/lock/subsys/if-up.$IFNAME

# Determin device here
# We should use IFNAME as the interface name.For some reason, the IFNAME maybe not
# same as the LINKNAME. And the route table should associate with the IFNAME. For
# some conveniency, I name the route table as "ppp0" and "ppp1".
RT_TABLE=$IFNAME

# Add or change static route here,including default route.
# Check if a default is exist.
RS=""
ip route ls table $RT_TABLE | grep default
RS=$?

if [ $RS -eq 0 ] ; then
       ip route change default dev $IFNAME table $RT_TABLE
else
       ip route add default dev $IFNAME table $RT_TABLE
fi
echo >> /var/log/ifchang.log
echo "$0: $IFNAME going up at `date`." >> /var/log/ifchang.log
echo "$0: $IFNAME got address: $IPLOCAL, peer address is $IPREMOTE." >> /var/log/ifchang.log
echo "$0: Table $RT_TABLE default route change to `ip route ls table $RT_TABLE | grep default`." >> /var/log/ifchang.log

# Refresh routing cache to activating the routing immediately.
ip route flush cache

# Add traffic control policy here
tc qdisc add dev $IFNAME root handle 1: prio
## This *instantly* creates classes 1:1, 1:2, 1:3

tc qdisc add dev $IFNAME parent 1:1 handle 10 sfq perturb 20
tc qdisc add dev $IFNAME parent 1:2 handle 20 sfq perturb 20
tc qdisc add dev $IFNAME parent 1:3 handle 30 sfq

# Remove the lock file
rm -f /var/lock/subsys/if-up.$IFNAME

/etc/ppp/ip-down.local

代码:

#!/bin/bash
# Name: ip-down.local
# Created by lyking@CU
while [ -e /var/lock/subsys/if-up.$IFNAME ] ; do
       sleep 3
done

touch /var/lock/subsys/if-down.$IFNAME

cd /etc/sysconfig/network-scripts
. network-functions

# Determin device here
# We should use IFNAME as the interface name.For some reason, the IFNAME maybe not
# same as the LINKNAME. And the route table should associate with the IFNAME. For
# some conveniency, I name the route table as "ppp0" and "ppp1".
RT_TABLE=$IFNAME

# Looking for a valide connection to Internet
DEFAULT_RT=""
PPPS='ppp0 ppp1'
for i in $PPPS ; do
ifconfig | grep $i
RS=$?
       if [ $RS -eq 0 ] ; then
           DEFAULT_RT=$i
           break
       fi
done

# Update default route here as nesessary
if [ $DEFAULT_RT != $IFNAME ] ; then
       if [ $DEFAULT_RT != "" ] ; then
               ip route add default dev $DEFAULT_RT table $RT_TABLE
       else
               for i in $PPPS ; do
                       ip route del default dev $i table $i
               done
               echo >> /var/log/ifchang.log
               echo "$0: All connection is down, remove all default route from all branch tables" >> /var/log/ifchang.log
       fi
       echo >> /var/log/ifchang.log
       echo "$0: $IFNAME going down at `date`." >> /var/log/ifchang.log
       echo "$0: Connection lasted $CONNECT_TIME seconds." >> /var/log/ifchang.log
       echo "$0: $BYTES_SENT bytes sent, $BYTES_RCVD bytes received." >> /var/log/ifchang.log
       echo "$0: $DEFAULT_RT is available." >> /var/log/ifchang.log
       echo "$0: Table $RT_TABLE default route changed to `ip route ls table $RT_TABLE | grep default`. " >> /var/log/ifchang.log
fi

# Refresh routing cache to activating the routing immediately.
ip route flush cache

rm -f /var/lock/subsys/if-down.$IFNAME

注意,创建完脚本后必须将其属性改为可执行,否则不会被执行。

6.路由策略的选择
策略路由可以通过源地址、目标地址、ToS或者fwmark标记来进行选择。在此,为了利用iptables的强大的过滤功能采用fwmark标记来决策路由。
在/etc/rc.d/rc.local中添加如下命令:
/sbin/ip rule add fwmark 1 table ppp0
/sbin/ip rule add fwmark 2 table ppp1

7.防火墙规则的添加
这里利用的iptables的强大过滤功能来对流量进行标记。本例中仅根据ip地址的奇偶性来拆分流量,根据具体需求,你还可以根据第4层端口号、ToS等来拆分流量。防火墙需要添加如下命令:

代码:

# Divid traffic to different mark
iptables -t mangle -A PREROUTING -s 10.0.0.0/255.255.255.1 -j MARK --set-mark 0x1
iptables -t mangle -A PREROUTING -s 10.0.0.1/255.255.255.1 -j MARK --set-mark 0x2

# NAT
/sbin/iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
/sbin/iptables -t nat -A POSTROUTING -o ppp1 -j MASQUERADE
至此,双adsl链路的热互备及负载分担基本完成。

下面是一些运行结果
A.ifconfig显示结果
ppp0 Link encap:Point-to-Point Protocol
inet addr:220.163.38.208 P-t-P:220.163.38.1 Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1492 Metric:1
RX packets:100295 errors:0 dropped:0 overruns:0 frame:0
TX packets:67817 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:3
RX bytes:108844271 (103.8 Mb) TX bytes:6073206 (5.7 Mb)

ppp1 Link encap:Point-to-Point Protocol
inet addr:220.163.36.57 P-t-P:220.163.36.1 Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1492 Metric:1
RX packets:150583 errors:0 dropped:0 overruns:0 frame:0
TX packets:125136 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:3
RX bytes:132921157 (126.7 Mb) TX bytes:8749585 (8.3 Mb)

B./var/log/ifchang.log部分内容
/etc/ppp/ip-down.local: ppp0 going down at Tue Aug 10 08:05:45 CST 2004.
/etc/ppp/ip-down.local: Connection lasted 22 seconds.
/etc/ppp/ip-down.local: 30 bytes sent, 30 bytes received.
/etc/ppp/ip-down.local: ppp1 is available.
/etc/ppp/ip-down.local: Table ppp0 default route changed to default dev ppp1 scope link .

/etc/ppp/ip-up.local: ppp0 going up at Tue Aug 10 08:05:53 CST 2004.
/etc/ppp/ip-up.local: ppp0 got address: 61.159.254.30, peer address is 61.159.254.1.
/etc/ppp/ip-up.local: Table ppp0 default route change to default dev ppp0 scope link .

/etc/ppp/ip-down.local: ppp1 going down at Tue Aug 10 08:12:45 CST 2004.
/etc/ppp/ip-down.local: Connection lasted 853 seconds.
/etc/ppp/ip-down.local: 9313 bytes sent, 116181 bytes received.
/etc/ppp/ip-down.local: ppp0 is available.
/etc/ppp/ip-down.local: Table ppp1 default route changed to default dev ppp0 scope link .

/etc/ppp/ip-up.local: ppp1 going up at Tue Aug 10 08:12:53 CST 2004.
/etc/ppp/ip-up.local: ppp1 got address: 220.163.38.20, peer address is 220.163.38.1.
/etc/ppp/ip-up.local: Table ppp1 default route change to default dev ppp1 scope link .