First, let's define the variables:
$ cat /etc/balance/vars
- #!/bin/bash
- # LAN interface
- IF0="eth1"
- # WAN interface 1
- IF1="eth0"
- # WAN interface 2
- IF2="ppp0"
- IP1="194.9.xx.xx"
- IP2="`ip addr show $IF2 | grep inet | awk '{print $2}'`"
- # gateway 1
- P1="194.9.xx.xx"
- # gateway 2
- P2="195.5.xx.xx"
- # LAN netmask
- P0_NET="192.168.0.0/24"
- # WAN1 netmask
- P1_NET="194.9.xx.xx/xx"
- # WAN2 netmask
- P2_NET="195.5.xx.xx/xx"
- TBL1="provider1"
- TBL2="provider2"
- # Relative weight of channels bandwidth
- W1="2"
- W2="1"
Add two additional routing tables to the /etc/iproute2/rt_tables file:
echo "1 provider1" >> /etc/iproute2/rt_tables
echo "2 provider2" >> /etc/iproute2/rt_tables
Now, let's write a script that will set up all the necessary routes and firewall rules:
cat /etc/balance/routing.sh
- #!/bin/bash
- . /etc/balance/vars
- echo "1" > /proc/sys/net/ipv4/ip_forward
- ip route add $P1_NET dev $IF1 src $IP1 table $TBL1 > /dev/null 2>&1
- ip route add default via $P1 table $TBL1 > /dev/null 2>&1
- ip route add $P2_NET dev $IF2 src $IP2 table $TBL2 > /dev/null 2>&1
- ip route add default via $P2 table $TBL2 > /dev/null 2>&1
- ip route add $P1_NET dev $IF1 src $IP1 > /dev/null 2>&1
- ip route add $P2_NET dev $IF2 src $IP2
- ip route add default via $P1 > /dev/null 2>&1
- ip rule add from $IP1 table $TBL1 > /dev/null 2>&1
- ip rule add from $IP2 table $TBL2 > /dev/null 2>&1
- ip route add $P0_NET dev $IF0 table $TBL1 > /dev/null 2>&1
- ip route add $P2_NET dev $IF2 table $TBL1 > /dev/null 2>&1
- ip route add 127.0.0.0/8 dev lo table $TBL1 > /dev/null 2>&1
- ip route add $P0_NET dev $IF0 table $TBL2 > /dev/null 2>&1
- ip route add $P1_NET dev $IF1 table $TBL2 > /dev/null 2>&1
- ip route add 127.0.0.0/8 dev lo table $TBL2 > /dev/null 2>&1
- iptables -t nat -F POSTROUTING
- iptables -t nat -A POSTROUTING -s $P0_NET -o $IF1 -j MASQUERADE
- iptables -t nat -A POSTROUTING -s $P0_NET -o $IF2 -j MASQUERADE
This set of commands ensures that response packets are routed through the interface on which the request was received, as well as performing masquerading on both interfaces.
Now let's write a script that will determine whether a particular channel is operational and, accordingly, change the default gateway records.
$ cat /etc/balance/check.sh
- #!/bin/bash
- . /etc/balance/vars
- OLDIF1=0
- OLDIF2=0
- . /etc/balance/routing.sh
- while true; do
- ping -c 3 -s 100 $P1 -I $IF1 > /dev/null
- if [ $? -ne 0 ]; then
- echo "Failed IF1!"
- NEWIF1=0
- else
- NEWIF1=1
- fi
- ping -c 3 -s 100 $P2 -I $IF2 > /dev/null
- if [ $? -ne 0 ]; then
- echo "Failed IF2!"
- NEWIF2=0
- else
- NEWIF2=1
- fi
- if (( ($NEWIF1!=$OLDIF1) || ($NEWIF2!=$OLDIF2) )); then
- echo "Changing routes"
- if (( ($NEWIF1==1) && ($NEWIF2==1) )); then
- echo "Both channels"
- ip route delete default
- ip route add default scope global nexthop via $P1 dev $IF1 weight $W1 \
- nexthop via $P2 dev $IF2 weight $W2
- elif (( ($NEWIF1==1) && ($NEWIF2==0) )); then
- echo "First channel"
- ip route delete default
- ip route add default via $P1 dev $IF1
- elif (( ($NEWIF1==0) && ($NEWIF2==1) )); then
- echo "Second channel"
- ip route delete default
- ip route add default via $P2 dev $IF2
- fi
- else
- echo "Not changed"
- fi
- OLDIF1=$NEWIF1
- OLDIF2=$NEWIF2
- sleep 3
- done
We check the channel status by pinging the gateway, and if there is no response to 3 consecutive pings, we consider the channel to be down and accordingly exclude it from the routing table.
Thus, if both channels are working:
$ ip route
195.5.xx.xx dev ppp0 proto kernel scope link src 95.133.xx.xx
194.9.xx.xx/xx dev eth0 proto kernel scope link src 194.9.xx.xx
192.168.0.0/24 dev eth1 proto kernel scope link src 192.168.0.75
default
nexthop via 194.9.xx.xx dev eth0 weight 2
nexthop via 195.5.xx.xx dev ppp0 weight 1
In total, we have two default gateways, the first with a weight of 2 and the second with a weight of 1. That is, twice as much traffic will go through the first channel compared to the second.
To customize these scripts for your needs, you need to configure the values in the vars file; the other scripts practically do not require configuration.
