Posted by Utumno on Wed 29 Mar 2006 at 07:21
apt-get install iproute iptables
noipdefaultWith such configuration, connection can be made with the command
usepeerdns
defaultroute
hide-password
lcp-echo-interval 20
lcp-echo-failure 3
connect /bin/true
noauth
persist
mtu 1492
noaccomp
default-asyncmap
plugin rp-pppoe.so eth0
user "your username here"
pon dsl-providerSo, in order to create a second connection,
noipdefaulti.e. the differences are:
usepeerdns
#defaultroute
hide-password
lcp-echo-interval 20
lcp-echo-failure 3
connect /bin/true
noauth
persist
mtu 1492
noaccomp
default-asyncmap
plugin rp-pppoe.so eth1
user "your username here"
pon dsl-provider24) make this setup permanent across reboots with adding
auto dsl-provider2to /etc/network/interfaces. At this point it is beneficial to use ifrename or udev to make interface names consistant across reboots.
iface dsl-provider2 inet ppp
provider dsl-provider2
pre-up /sbin/ifconfig eth1 up # line maintained by pppoeconf
auto eth1
iface eth1 inet manual
angband:/etc/ppp/peers# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
MY.GA.TE.WAY 0.0.0.0 255.255.255.255 UH 0 0 0 ppp0
MY.GA.TE.WAY 0.0.0.0 255.255.255.255 UH 0 0 0 ppp1
10.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 eth2
0.0.0.0 0.0.0.0 0.0.0.0 U 0 0 0 ppp0
angband:/etc# echo 200 PPP0 >> /etc/iproute2/rt_tables2) use 'ip' to create rules for table PPP0: just add a route to the gateway, and then a default route through ppp0:
angband:/etc# echo 201 PPP1 >> /etc/iproute2/rt_tables
angband:/etc# ip route add MY.GA.TE.WAY dev ppp0 table PPP03) the same for PPP1
angband:/etc# ip route add default via MY.GA.TE.WAY dev ppp0 table PPP0
angband:/etc# ip route add MY.GA.TE.WAY dev ppp1 table PPP1You now can list the contents of your new routing tables with
angband:/etc# ip route add default via MY.GA.TE.WAY dev ppp1 table PPP1
angband:/etc/iproute2# ip route list table PPP0At this point, 'ppp1' is still useless because the new routing tables are not used at all yet.
MY.GA.TE.WAY dev ppp0 scope link
default via MY.GA.TE.WAY dev ppp0
angband:/etc/iproute2# ip route list table PPP1
MY.GA.TE.WAY dev ppp1 scope link
default via MY.GA.TE.WAY dev ppp1
iptables -t mangle -A OUTPUT -m owner --uid-owner 108 -j MARK --set-mark 1'108' is the user id of mldonkey. The above rule will stamp all packets produced by user with such uid with a so-called 'FWMARK' equal to 1 ( all that before any routing decision is made )
A sidenote: here we took advantage of the fact that MLdonkey, as it is packaged in Debian, runs as a dedicated user 'mldonkey'. But what if you need to route some other system service that does not have its own user and runs as root, say, SSHd? Use the '--cmd-owner' parameter:iptables -t mangle -A OUTPUT -m owner --cmd-owner sshd -j MARK --set-mark 1( another sidenote: AFAIK, the '--cmd-owner' flag does not work in recent ( >= 2.6.15 ) kernels )
ip rule add fwmark 1 pri 100 table PPP1That in turn tells the kernel to use table 'PPP1' when routing all packets marked with an FWMARK equal to 1.
iptables -t nat -A POSTROUTING -o ppp1 -j SNAT --to-source=I.P.OF.PPP1where I.P.OF.PPP1 is, of course, ppp1's IP.
echo 0 > /proc/sys/net/ipv4/conf/ppp1/rp_filterrp_filter is a functionality which automatically rejects incoming packets if the routing table entry for their source address doesn't match the network interface they're arriving on. Normally, this has security advantages because it prevents the so-called IP-spoofing, but in our situation ( several IP addresses on different interfaces ) it can pose problems.
ip rule add from I.P.OF.PPP0 pri 200 table PPP0The 'pri' ( short for 'priority' ) parameter controls the precedence in which the rules are applied. Routing algorithm goes from priority 0 upwards and applies the first matching rule. Notice that those two rules have to have a highier priority than the 'fwmark' one. ( also notice that, somewhat illogically, 'highier priority' here really means 'lower importance' ).
ip rule add from I.P.OF.PPP1 pri 300 table PPP1
angband:/etc/iproute2# ip rule list
0: from all lookup local
100: from all fwmark 0x1 lookup PPP1
200: from I.P.OF.PPP0 lookup PPP0
300: from I.P.OF.PPP1 lookup PPP1
32766: from all lookup main
32767: from all lookup default
/etc/init.d/mldonket-server startyou should see it working on ppp1.
tcpdump -i ppp1
#!/bin/shLet's call this script '/etc/init.d/my_initscript' and add it to rc.d:
case "$1" in
start)
echo "Setting up firewall rules..."
IPTABLES=/sbin/iptables
INTERNAL_IFACE=eth2
EXTERNAL_IFACE0=ppp0
EXTERNAL_IFACE1=ppp1
INTERNAL_IP=10.0.0.1
INTERNAL_NETWORK=10.0.0.0/24
# Start with a tough policy.
$IPTABLES -P INPUT DROP
$IPTABLES -P FORWARD DROP
$IPTABLES -P OUTPUT ACCEPT
# clean up
$IPTABLES -F
$IPTABLES -X
$IPTABLES -Z
# Filtering section
# INPUT chain
# We want to allow ONLY:
# 1. local (loopback) traffic
# 2. traffic from the Internet that is part of an existing connection (no new connections)
$IPTABLES -A INPUT -i lo -j ACCEPT
$IPTABLES -m state -A INPUT -i $EXTERNAL_IFACE0 --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -m state -A INPUT -i $EXTERNAL_IFACE1 --state ESTABLISHED,RELATED -j ACCEPT
# on ppp0, allow SSH and HTTP
$IPTABLES -A INPUT -p tcp -m tcp --dport 22 -i $EXTERNAL_IFACE0 -j ACCEPT
$IPTABLES -A INPUT -p tcp -m tcp --dport 80 -i $EXTERNAL_IFACE0 -j ACCEPT
# in ppp1, allow mldonkey
$IPTABLES -A INPUT -p tcp -m tcp --dport 4662 -i $EXTERNAL_IFACE1 -j ACCEPT
$IPTABLES -A INPUT -p udp -m udp --dport 4662 -i $EXTERNAL_IFACE1 -j ACCEPT
$IPTABLES -A INPUT -p tcp -m tcp --dport 4001 -i $EXTERNAL_IFACE1 -j ACCEPT
# everywhere, allow ping and traceroute
$IPTABLES -A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
$IPTABLES -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
$IPTABLES -A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
$IPTABLES -A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
$IPTABLES -A INPUT -p icmp -m icmp --icmp-type 30 -j ACCEPT
$IPTABLES -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
$IPTABLES -A INPUT -p icmp -m state --state RELATED,ESTABLISHED -j ACCEPT
# limit the number of incoming connections on port 22 ( SSH ) to 3 attempts a minute
$IPTABLES -I INPUT -p tcp --dport 22 -i $EXTERNAL_IFACE0 -m state --state NEW -m recent --set
$IPTABLES -I INPUT -p tcp --dport 22 -i $EXTERNAL_IFACE0 -m state --state NEW -m recent --update --seconds 60 --hitcount 3 -j DROP
# allow all ports on the internal interface
$IPTABLES -A INPUT -p udp -m udp -i $INTERNAL_IFACE --dport 1:65000 -j ACCEPT
$IPTABLES -A INPUT -p tcp -m tcp -i $INTERNAL_IFACE --dport 1:65000 -j ACCEPT
# mark all packets from mldonkey ( uid=108 ) so that later on we can route them thru ppp1
$IPTABLES -t mangle -A OUTPUT -m owner --uid-owner 108 -j MARK --set-mark 1
# switch off rp_filter ( otherwise packets coming back thru ppp1 get dropped by it )
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
# hack: go around the case that we can get assigned the same IP
# as we had before the reboot
DATE=`date +"%F %r"`
echo "$DATE REBOOT 1.1.1.1" >> /var/log/check_ppp0
echo "$DATE REBOOT 1.1.1.1" >> /var/log/check_ppp1
# call the cronjob script to complete the work for us
# ( for example, set up the NAT rule - I can't do it here
# because I dont know ppp1's IP yet )
echo "Saving new ip..."
/usr/local/bin/check_ip 2> /var/log/check_errors
# add a routing policy to route all packets marked with a '1' thru the PPP1 table
ip rule add fwmark 1 pri 100 table PPP1
;;
stop)
;;
esac
update-rc.d my_initscript defaultsAnd here's the /usr/local/bin/check_ip script:
#!/bin/shAdd this script to root's cronjob with 'crontab -e' :
DIGITS="[0-9]\{1,3\}"
IP="$DIGITS\.$DIGITS\.$DIGITS\.$DIGITS"
DATE=`date +"%F %r"`
SLEEP=10
MAXTRIES=5
###############################################################################
#### check if PPP0 is up, if not, bring it up and remember its new IP
DEVICE0="ppp0"
LOGFILE0="/var/log/check_${DEVICE0}"
CURRENT_IP0=`/sbin/ifconfig $DEVICE0 | sed -n "s/.*addr:\($IP\) .*/\1/p"`
if [ "x$CURRENT_IP0" = "x" ]
then
pon dsl-provider > /dev/null
sleep $SLEEP
CURRENT_IP0=`/sbin/ifconfig $DEVICE0 | sed -n "s/.*addr:\($IP\) .*/\1/p"`
COUNTER=0
while [ "x$CURRENT_IP0" ="x" -a $COUNTER -le $MAXTRIES ]
do
echo "$DATE Waiting for device $DEVICE0 for the $COUNTER time..." >> $LOGFILE0
let "COUNTER += 1"
sleep $SLEEP
CURRENT_IP0=`/sbin/ifconfig $DEVICE0 | sed -n "s/.*addr:\($IP\) .*/\1/p"`
done
if [ $COUNTER -gt $MAXTRIES ]
then
echo "$DATE Failed to bring up device $DEVICE0, giving up..." >> $LOGFILE0
exit 1
fi
fi
if [[ -e $LOGFILE0 ]]
then
LAST_IP0=`cat $LOGFILE0 | grep $IP | tail -1 | sed -n "s/.*\ \(.*\)/\1/p"`
fi
###############################################################################
#### same for PPP1
DEVICE1="ppp1"
LOGFILE1="/var/log/check_${DEVICE1}"
CURRENT_IP1=`/sbin/ifconfig $DEVICE1 | sed -n "s/.*addr:\($IP\) .*/\1/p"`
if [ "x$CURRENT_IP1" = "x" ]
then
pon dsl-provider2 > /dev/null
sleep $SLEEP
CURRENT_IP1=`/sbin/ifconfig $DEVICE1 | sed -n "s/.*addr:\($IP\) .*/\1/p"`
COUNTER=0
while [ "x$CURRENT_IP1" ="x" -a $COUNTER -le $MAXTRIES ]
do
echo "$DATE Waiting for device $DEVICE1 for the $COUNTER time..." >> $LOGFILE1
let "COUNTER += 1"
CURRENT_IP1=`/sbin/ifconfig $DEVICE1 | sed -n "s/.*addr:\($IP\) .*/\1/p"`
sleep $SLEEP
done
if [ $COUNTER -gt $MAXTRIES ]
then
echo "$DATE Failed to bring up device $DEVICE1, giving up..." >> $LOGFILE1
exit 1
fi
fi
if [[ -e $LOGFILE1 ]]
then
LAST_IP1=`cat $LOGFILE1 | grep $IP | tail -1 | sed -n "s/.*\ \(.*\)/\1/p"`
fi
###############################################################################
#### Save new IP of ppp1; re-create ip rules that depend on ppp1's IP
if [ "x$LAST_IP1" != "x$CURRENT_IP1" ]
then
echo "$DATE $CURRENT_IP1" >> $LOGFILE1
ip rule del from $LAST_IP1 pri 300 table PPP1
ip rule add from $CURRENT_IP1 pri 300 table PPP1
GATEWAY1=`/sbin/ifconfig $DEVICE1 | sed -n "s/.*P-t-P:\(.*\)\ .*/\1/p"`
ip route add $GATEWAY1 dev $DEVICE1 table PPP1
ip route add default via $GATEWAY1 dev $DEVICE1 table PPP1
iptables -t nat -D POSTROUTING 1
iptables -t nat -A POSTROUTING -o $DEVICE1 -j SNAT --to-source=$CURRENT_IP1
fi
###############################################################################
#### same for ppp0 + save it's new IP in an external server.
if [ "x$LAST_IP0" != "x$CURRENT_IP0" ]
then
# save my new ip to my server with a static ip.
# CGI scripts there redirect the traffic back to my home server.
# I know I can do that with DynDNS, but somehow like to do
# everything by myself :) Here we have to have authorized_keys
# set up for this to work.
echo $CURRENT_IP0 | ssh MY_SERVER_WITH_STATIC_IP 'cat > ~/html/server/current_ip'
RET=$?
echo `date +"%F %r"` "new ip saved, ssh returned $RET" >> $LOGFILE0
echo "$DATE $CURRENT_IP0" >> $LOGFILE0
ip rule del from $LAST_IP0 pri 200 table PPP0
ip rule add from $CURRENT_IP0 pri 200 table PPP0
GATEWAY0=`/sbin/ifconfig $DEVICE0 | sed -n "s/.*P-t-P:\(.*\)\ .*/\1/p"`
ip route add $GATEWAY0 dev $DEVICE0 table PPP0
ip route add default via $GATEWAY0 dev $DEVICE0 table PPP0
fi
# m h dom mon dow commandVoilla, we are done!
# every 5 minutes check if we are still connected to DSL;
# if not, reconnect and save the new IP to MY_SERVER_WITH_STATIC_IP.
*/5 * * * * /usr/local/bin/check_ip 2> /var/log/check_errors