Two lines, IMQ, and NAT

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



All,

I can really use some advice here. I help to maintain a server providing internet service to troops in Iraq. Bandwidth is slim, so it is a challenge. A second line is being installed and I need to modify the scripts to make it work. 

The server performs NAT and the qos script runs on IMQ devices both up and down. eth1 (LAN facing) is currently SNAT'ed to eth0 (internet facing). eth2 (internet facing) will be the second line coming in. It is simple enough attaching two lines to an IMQ, but here are my concerns:

The qos script adjusts bandwidth every minute based upon quota and time remaining, so both internet facing NICs need to get an equal share. Does IMQ do load balancing on its own or do I need more? With the two internet facing NICs having different IPs, is it even possible to have a high bandwidth requirement pass through both IPs simultaneously?

How do I deal with SNAT? Can I SNAT to both eth0 and eth2 from the LAN side? Also, coming back in, eth0 is DNAT'ed to eth1. This is the same basic question--do I DNAT from eth0 and eth2 to eth1?

Attached is the script for reference. The firewall script is seperate and handles the NAT'ing, but it's pretty simple. Thanks so much in advance.  

Jason
#/bin/bash

################################################################################
#   This script requires two network cards, one internet facing and one 
#   LAN facing on a NAT machine. Read the script for required modules 
#   (there are a lot). Note that IMQ, if built as a module, must 
#   be manually loaded either by adding to this script or loading 
#   elsewhere (it will not autoload). 
#
#   First set up variables used throughout the script. This particular 
#   ISP uses quotas that reset every 8 hours. Upon consuming X number 
#   of bytes, speed restrictions occur. As the server MUST be the bottle 
#   neck in the link in order to maintain qos, the adjust_bandwidth() 
#   function takes care of this. Ensure the num_users variable below
#   matches the number of IP groups in user_ip below.
################################################################################ 
export dev_lan=eth1 dev_net=eth0 imq_up=imq0 imq_dwn=imq1 imq_up_all=imq2
export rate_up=10880 rate_dwn=80000 max_up=57600 max_dwn=140800
export quota_dwn=2306867200 quota_up=311427072
export logfile=/var/log/bandwidth.log num_users=34 lan_sub=192.168.1

################################################################################
#   Users are grouped by numbers of machines so that everyone, not every 
#   machine, gets equal bandwidth. Make sure IPs are assigned in a manner 
#   consistent with the below IP sets. A space represents one user. For
#   example, the user with 5 machines gets the same bandwidth as the user
#   with one. Don't forget to include both the wireless and wired cards
#   for each user that has both. 
################################################################################

user_ip="8 9 10:11 12 13 14:15 16 17 18 19 20 21 22 23 \
24 25 26 27:56 28 29:57 30 31:55 32:33 34 35:36 37:38:39 40:41 42:43 44:45 \
46:47 48:49 50:53 51:52 54"

################################################################################
#   Clear tables and reset everything. This won't affect regular firewall 
#   rules, as we are using seperate chains for qos. 
################################################################################

iptables -t mangle -D PREROUTING -i $dev_net -j bytes_in &> /dev/null
iptables -t mangle -D POSTROUTING -o $dev_net -j bytes_out &> /dev/null
iptables -t mangle -D PREROUTING -i $dev_lan -j qos_dwn &> /dev/null
iptables -t mangle -D POSTROUTING -o $dev_lan -j qos_up &> /dev/null

for dev in $imq_up $imq_dwn; do
   tc qdisc del dev $dev root &> /dev/null
   ip link set $dev down &> /dev/null
done

iptables -t mangle -D PREROUTING -i $dev_lan -j IMQ --todev 0 &> /dev/null
iptables -t mangle -D POSTROUTING -o $dev_lan -j IMQ --todev 1 &> /dev/null

for chain in bytes_in bytes_out qos_up qos_dwn; do
   iptables -t mangle -F $chain &> /dev/null
   iptables -t mangle -X $chain &> /dev/null
   iptables -t mangle -N $chain &> /dev/null
done

################################################################################
#   The follwing adjusts bandwidth according to a number of parameters. 
#   First, it has been determined that the max consistent speed attainable with 
#   this ISP is 1100/450 megabits down/up. Second, this ISP sets a quota 
#   cap of 2200/297 MB down/up per 8 hour period. We also hit a speed 
#   restriction of 512/60 kilobits/second after 700/180 MB down/up are consumed.# 
#
#   First, we have to determine what time it is UTC (Greenwich). The ISP 
#   resets the quotas at noon, 2000, and 0400 daily. We use the Linux 
#   epoch to accomplish this. The date command below gives the number 
#   of seconds since midnight, Jan 1st 1970. Dividing this by one day 
#   and taking the remainder gives us the number of seconds since midnight 
#   today. From here we can divide by seconds, minutes, and hours to 
#   determine what time it is and how much time remains in a quota period. 
#   Once we have time remaining, we divide by the amount of qouta remaining 
#   to determine desired speed. This is checked against our speed caps and 
#   max speed attainable to ensure that we are not above what is workable. 
#   Once we have our speed calculated, this speed is passed to the 
#   user_class() and parent_class functions to change the bandwidth 
#   allotted to the queues. 
################################################################################

adjust_bandwidth() {

sectime=$(($(date +%s)%86400))

bytes_in=$(iptables -t mangle -L bytes_in -n -x -v | \
   grep $dev_net | awk '{ print $2 }')

bytes_out=$(iptables -t mangle -L bytes_out -n -x -v | \
   grep $dev_net | awk '{ print $2 }')

if [ $sectime -ge 0 -a $sectime -lt 14400 ]; then
   secsremain=$((14400-$sectime))
elif [ $sectime -ge 14400 -a $sectime -lt 43200 ]; then
   secsremain=$((43200-$sectime))
elif [ $sectime -ge 43200 -a $sectime -lt 72000 ]; then
   secsremain=$((72000-$sectime))
elif [ $sectime -ge 72000 -a $sectime -lt 86400 ]; then
   secsremain=$(($((86400-$sectime))+14400))
fi

if [ $bytes_in -eq 0 ]; then
   speed_dwn=$(($quota_dwn/$secsremain))
elif [ $bytes_in -ge 734003200 ]; then
   speed_dwn=65536
else
   speed_dwn=$((($quota_dwn-$bytes_in)/$secsremain))
fi

if [ $bytes_out -eq 0 ]; then
   speed_up=$(($quota_up/$secsremain))
elif [ $bytes_out -ge 188743680 ]; then
   speed_up=7680
else
   speed_up=$((($quota_up-$bytes_out)/$secsremain))
fi

if [ $speed_dwn -ge $max_dwn ]; then
   newrate_dwn=$max_dwn
else
   newrate_dwn=$speed_dwn
fi

if [ $speed_up -ge $max_up ]; then
   newrate_up=$max_up
else
   newrate_up=$speed_up
fi

lograte_dwn=$(($newrate_dwn*8/1024)) lograte_up=$(($newrate_up*8/1024))
echo "$(date) Speed changed to $lograte_dwn/$lograte_up \
kbit down/up." >> $logfile

parent_class change $imq_up $newrate_up
parent_class change $imq_dwn $newrate_dwn

for user in $user_ip; do
   mark=`echo $user | sed -e "s/[^0-9].*//g"`
   mark=$(($mark*10));
   user_class change $imq_up $newrate_up $mark
   user_class change $imq_dwn $newrate_dwn $mark
done

}

#################################################################################   
#   This is the main class structure. We set up the root qdisc with a 
#   lan speed class under it for non-internet transfers. A single pfifo
#   qdisc and tc filter are part of this scheme to get the LAN traffic into
#   the full speed queue. 
#
#   After this we create the interactive class for applications like voip,
#   video conferencing, and delay sensative applications. A pfifo qdisc 
#   is attached to this as opposed to sfq to keep delay to an absolute
#   minimum (we should only have one user in this band at a time, anyway). 
#   Then we create a low bandwidth reserve for ssh, so that we can work on the
#   server during high traffic. Then we create the main internet class, 
#   which gets the lions share of the bandwidth. Last, we create the lowest 
#   priority download class, which gets a total of 20% total bandwidth. Be
#   careful setting this too high, as it is difficult to slow down a 
#   screaming download once it reaches a high level of saturation. 
###############################################################################

parent_class() {

command=$1 dev=$2 rate=$3

if [ $command == add ]; then
   tc qdisc $command dev $dev root handle 1: htb default 5

   tc class add dev $dev parent 1: classid 1:1 htb rate \
   900000bps quantum 1510 prio 0

   tc qdisc add dev $dev parent 1:1 handle 2: pfifo

   tc filter $command dev $dev parent 1: protocol ip prio 1 u32 match \
   ip protocol 0x6 0xff match ip tos 0x10 0xff flowid 1:1

fi

tc class $command dev $dev parent 1: classid 1:2 htb rate $(($rate))bps \
ceil $(($rate))bps quantum 1510

if [ $dev == $imq_up ]; then
   tc class $command dev $dev parent 1:2 classid 1:3 htb rate \
   $(($rate*5/100))bps ceil 12800bps quantum 1510 prio 1
else
   tc class $command dev $dev parent 1:2 classid 1:3 htb rate \
   $(($rate*5/100))bps quantum 1510 prio 1
fi

tc class $command dev $dev parent 1:2 classid 1:4 htb rate \
$(($rate*20/100))bps ceil $(($rate*25/100))bps quantum 1510 prio 3

tc class $command dev $dev parent 1:2 classid 1:5 htb rate \
$(($rate*55/100))bps ceil $(($rate))bps burst 6k cburst 6k quantum 1510 prio 2

tc class $command dev $dev parent 1:2 classid 1:6 htb rate \
$(($rate*20/100))bps ceil $(($rate*25/100))bps quantum 1510 prio 4

if [ $command == add ]; then
   tc qdisc add dev $dev parent 1:3 handle 3: pfifo
   tc qdisc add dev $dev parent 1:4 handle 4: sfq perturb 10
   tc qdisc add dev $dev parent 1:5 handle 5: sfq perturb 10

for class in 1 3 4 5; do
   tc filter add dev $dev parent 1: protocol ip prio 2 handle $class \
   fw flowid 1:$class
done

fi

}

#################################################################################   
#   user_class() creates a class under the parent download class. We don't 
#   allocate everything by individual user, just downloads, as it is  
#   better to stay out of squid's way. squid has it's own scheme and 
#   it works well. 
################################################################################

user_class() {

command=$1 dev=$2 urate=$(($3*20/100)) mark=$4

tc class $command dev $dev parent 1:6 classid 1:$mark htb rate \
$(($urate/$num_users))bps ceil $(($urate))bps quantum 1510 prio 5

if [ $command == add ]; then
   tc qdisc add dev $dev parent 1:$mark handle $mark: sfq perturb 10

   tc filter add dev $dev parent 1: protocol ip prio 3 handle $mark \
   fw classid 1:$mark
fi

}

################################################################################
#   user_rules picks up downloads and marks them with the mark derived from
#   the user's IP address. These are then placed in the lowest priority 
#   class, where 20% of the pipe's total bandwidth is shared by all 
#   downloads. Be careful with the numbers. 256K works well. Much lower 
#   and you'll be placing large web pages in the slowest class, much higher 
#   and downloads run longer than they need to at higher speeds. 
#
#   upload quotas are also initiated here. We give users their share*2, 
#   since not all users will use their entire quota every 8 hours. Adjust 
#   as appropriate. This is reset every 8 hours by the daemon function
#   below. 
################################################################################
 
user_rules() {

chain=$1 flow=$2 ip=$3 mark=$4
cmd="iptables -t mangle -I $chain $flow $ip"

$cmd -m connbytes --connbytes 256000: --connbytes-dir original \
--connbytes-mode bytes -j MARK --set-mark $mark

if [ $chain == qos_up ]; then
   $cmd -j DROP
   $cmd -m quota2 --name $ip --quota $(($quota_up*2/$num_users)) -j ACCEPT
fi

}

################################################################################
#   Here is where all of the marking takes place. Things should be self
#   explanatory--we are just prioritizing and assgning marks. The reason 
#   port 6000 is prioritized is for the Vsee application. The
#   lower numerically the mark, the higher the priority. Class 1:5 is the
#   default, so this traffic needs no mark. Download traffic is pulled 
#   from 1:5 and placed in 1:6 in user_rules() after 256K has been consumed. 
################################################################################

for chain in qos_up qos_dwn; do
cmd="iptables -t mangle -A $chain"

$cmd -s 192.168.1.0 -d 192.168.1.0 -j MARK --set-mark 1
$cmd -s 192.168.1.0 -d 192.168.1.0 -j RETURN

$cmd -p icmp -j MARK --set-mark 3
$cmd -p icmp -j RETURN

$cmd -m layer7 --l7proto sip -j MARK --set-mark 3
$cmd -m layer7 --l7proto sip -j RETURN

$cmd -m layer7 --l7proto skypeout -j MARK --set-mark 3
$cmd -m layer7 --l7proto skypeout -j RETURN

$cmd -m layer7 --l7proto skypetoskype -j MARK --set-mark 3
$cmd -m layer7 --l7proto skypetoskype -j RETURN

$cmd -p tcp --sport 6000 -j MARK --set-mark 3
$cmd -p tcp --sport 6000 -j RETURN

$cmd -p udp --sport 6000 -j MARK --set-mark 3
$cmd -p udp --sport 6000 -j RETURN

$cmd -p tcp --dport 6000 -j MARK --set-mark 3
$cmd -p tcp --dport 6000 -j RETURN

$cmd -p udp --dport 6000 -j MARK --set-mark 3
$cmd -p udp --dport 6000 -j RETURN

$cmd -p tcp --dport ssh -j MARK --set-mark 4
$cmd -p tcp --dport ssh -j RETURN

$cmd -p tcp -m tos ! --tos Minimize-Delay --sport ssh -j MARK --set-mark 4
$cmd -p tcp -m tos ! --tos Minimize-Delay --sport ssh -j RETURN

$cmd -p tcp -m tos ! --tos Minimize-Delay --dport ssh -j MARK --set-mark 4
$cmd -p tcp -m tos ! --tos Minimize-Delay --dport ssh -j RETURN

done

################################################################################
#   Here we call the functions to create the classes, queues, rules, and 
#   filters when the script is first run. The only thing changed after 
#   the script's initial execution is the bandwidth and quotas. 
################################################################################

parent_class add $imq_up $rate_up
parent_class add $imq_dwn $rate_dwn

for user in $user_ip; do
   mark=`echo $user | sed -e "s/[^0-9].*//g"`
   mark=$(($mark*10));
   user_class add $imq_up $rate_up $mark $num_users
   user_class add $imq_dwn $rate_dwn $mark $num_users
   ip_list=`echo $user | sed -e "s/:/ /g" -e "s/[^0-9 ].*//"`
   for ip in $ip_list; do
   ip=$lan_sub.$ip chain_up=qos_up chain_dwn=qos_dwn flow_up="-s" flow_dwn="-d"
      user_rules $chain_up $flow_up $ip $mark
      user_rules $chain_dwn $flow_dwn $ip $mark
   done
done

################################################################################
#   Here we hook the imqs and qos chains into their respective devices and 
#   iptables chains. 
################################################################################

iptables -t mangle -I PREROUTING -i $dev_lan -j IMQ --todev 0
ip link set $imq_up up &> /dev/null
iptables -t mangle -I POSTROUTING -o $dev_lan -j IMQ --todev 1
ip link set $imq_dwn up &> /dev/null
iptables -t mangle -I POSTROUTING -o $dev_lan -j qos_dwn
iptables -t mangle -I PREROUTING -i $dev_lan -j qos_up
iptables -t mangle -A bytes_in -i $dev_net
iptables -t mangle -A PREROUTING -j bytes_in
iptables -t mangle -A bytes_out -o $dev_net
iptables -t mangle -A POSTROUTING -j bytes_out

sleep 1

################################################################################
#   This is the part of the script that runs in memory continuously. The 
#   timer variable divides by 60 every second. When we have no remainder,
#   we are at the top of the minute. At this point, we check to see if 
#   we are at the top of the hour for the 8 hour quota reset. If so, user
#   upload quotas are reset. Then, every minute, the adjust_bandwidth()
#   function is run. 
################################################################################

while :; do

timer=$(($(date +%s)%60))

if [ $timer -eq 0 ]; then
   sectime=$(($(date +%s)%86400))
   logfile=/var/log/bandwidth.log
   if [ $sectime -eq 24400 -o $sectime -eq 43200 -o $sectime -eq 72000 ]; then
      echo "$(date) Bytes in reset at $(($bytes_in/1048576))MB." >> $logfile
      echo "$(date) Bytes out reset at $(($bytes_out/1048576))MB." >> $logfile
      echo "$(date) Quota counters reset." >> $logfile
      iptables -Z
      for s in $(seq 8 57); do
         quota_up=311427072 num_users=34
         echo $(($quota_up*2/$num_users)) > /proc/net/xt_quota/192.168.1.$s
      done
   fi
   adjust_bandwidth
fi

sleep 1

done


[Index of Archives]     [Linux Netfilter Development]     [Linux Kernel Networking Development]     [Netem]     [Berkeley Packet Filter]     [Linux Kernel Development]     [Advanced Routing & Traffice Control]     [Bugtraq]

  Powered by Linux