====== Due connessioni a internet per un solo host ======
Questa configurazione permette a un host di avere due connessioni internet sempre attive. L'host e` accessibile da tutte e due le connessioni indistintamente, e le usa tutte e due in modo bilanciato per il traffico uscente.
Usando uno script apposito (ho scopiazzato orrendamente, lo ammetto) e` possibile avere un controllo sulla connettivita` delle due connessioni e provvedere a smettere di usare quella che per qualche motivo dovesse andare offline. Questo ovviamente non impatta le connessioni entranti, ma solo quelle uscenti.
Attenzione: questa configurazione non e` stata provata come sistema per dare connettivita` a degli host nattati dietro a una terza interfaccia. Potrebbe funzionare (con due regole di nat) ma anche no.
===== Configurazione statica =====
Questa configurazione definisce le regole necessarie per il routing verso due gateway diversi. E` necessario avere due schede di rete, una per connessione.
* Installare, se non c'e`, il pacchetto iproute2. Nelle nuove installazioni c'e` per default.
* Modificare il file ''/etc/iproute2/rt_tables'' aggiungendo le nostre due tabelle di routing con nomi a nostro piacere:
200 Fastweb
201 Ehiweb
* Modificare il file ''/etc/network/interfaces'' in modo da creare tutte le regole necessarie al routing per ogni interfaccia. (le regole commentate riguardano il NAT, se non servono non abilitatele)
# prima connessione: subnet 93.33.25.248/29, gw 249, il mio host e` il 253
auto ens18
iface ens18 inet static
address 93.33.25.253/29
post-up ip route add 93.33.25.248/29 dev ens18 src 93.33.25.253 table Fastweb
post-up ip route add default via 93.33.25.249 table Fastweb
post-up ip rule add from 93.33.25.253 table Fastweb
# post-up ip rule add from all fwmark 0x1 table Fastweb
post-down ip rule del from 93.33.25.253 table Fastweb
# post-down ip rule del from all fwmark 0x1 table Fastweb
# seconda connessione: subnet 109.23.6.48/29, gw 49, il mio host e` il 53
auto ens19
iface ens19 inet static
address 109.23.6.53/29
post-up ip route add 109.23.6.48/29 dev ens19 src 109.23.6.53 table Ehiweb
post-up ip route add default via 109.23.6.49 table Ehiweb
post-up ip rule add from 109.23.6.53 table Ehiweb
#post-up ip rule add from all fwmark 0x2 table Ehiweb
post-down ip rule del from 109.23.6.53 table Ehiweb
#post-down ip rule del from all fwmark 0x2 table Ehiweb
A questo punto abbiamo due interfacce ma nessun default routing. Per stabilire un routing di default statico, possiamo aggiungerlo come post-up all'ultima delle due interfacce, in questo modo:
# sotto alla interfaccia ens19, come ultima riga:
post-up ip route add default scope global nexthop via 93.33.25.249 dev ens18 weight 1 nexthop via 109.23.6.49 dev ens19 weight 1
**Se vogliamo che il traffico uscente eviti di usare una interfaccia che non funziona, occorre, INVECE del routing statico, usare uno script che effettui un controllo costante e cambi le regole di conseguenza.**
===== Script per il controllo del gateway morto =====
Questo script puo` essere usato per il controllo delle due connettivita`. Provvede a cambiare il default gateway a seconda di quale connessione funziona e quale no. Questo script e` stato copiato, non e` mio. L'autore e` Steve Buzonas e questo e` il link alla versione originale: https://github.com/sbuzonas/multiwan
==== Installazione ====
* Creare lo script ''/usr/local/sbin/multiwan'' contenente quanto segue:
#!/usr/bin/env bash
source /etc/multiwan.conf
STARTUP=1
CHECK_INTERVAL=1
# IP address of each WAN interface
WAN_NET1="$(ip addr show $WAN_IF1 | grep "inet " | tr -s [:space:] | cut -d ' ' -f3)"
WAN_NET2="$(ip addr show $WAN_IF2 | grep "inet " | tr -s [:space:] | cut -d ' ' -f3)"
WAN_IP1="$(echo $WAN_NET1 | cut -d '/' -f1)"
WAN_IP2="$(echo $WAN_NET2 | cut -d '/' -f1)"
# Last link status. Defaults to down to force check of both on first run.
LLS1=1
LLS2=1
# Last ping status.
LPS1=1
LPS2=1
# Current ping status.
CPS1=1
CPS2=1
# Change link status.
CLS1=1
CLS2=1
# Count of consecutive status checks
COUNT1=0
COUNT2=0
function link_status() {
case $1 in
0)
echo "Up" ;;
1)
echo "Down" ;;
*)
echo "Unknown" ;;
esac
}
# check_link $IP $TIMEOUT
function check_link() {
ping -W $2 -I $1 -c 1 $PING_TARGET > /dev/null 2>&1
RETVAL=$?
if [ $RETVAL -ne 0 ] ; then
STATE=1
else
STATE=0
fi
link_status $STATE
return $STATE
}
while : ; do
LINK_STATE="$(check_link $WAN_IP1 $PING_TIMEOUT)"
CPS1=$?
if [ $LPS1 -ne $CPS1 ] ; then
logger -p local6.notice -t MULTIWAN[$$] "Ping state changed for $WAN_TABLE1 from $(link_status $LPS1) to $(link_status $CPS1)"
COUNT1=1
else
if [ $LPS1 -ne $LLS1 ] ; then
COUNT1=`expr $COUNT1 + 1`
fi
fi
if [[ $COUNT1 -ge $SUCCESS_COUNT || ($LLS1 -eq 0 && $COUNT1 -ge $FAILURE_COUNT) ]]; then
CLS1=0
COUNT1=0
if [ $LLS1 -eq 1 ] ; then
LLS1=0
else
LLS1=1
fi
logger -p local6.notice -t MULTIWAN[$$] "Link state for $WAN_TABLE1 is $(link_status $LLS1)"
else
CLS1=1
fi
LPS1=$CPS1
LINK_STATE="$(check_link $WAN_IP2 $PING_TIMEOUT)"
CPS2=$?
if [ $LPS2 -ne $CPS2 ] ; then
logger -p local6.notice -t MULTIWAN[$$] "Ping state changed for $WAN_TABLE2 from $(link_status $LPS2) to $(link_status $CPS2)"
COUNT2=1
else
if [ $LPS2 -ne $LLS2 ] ; then
COUNT2=`expr $COUNT2 + 1`
fi
fi
if [[ $COUNT2 -ge $SUCCESS_COUNT || ($LLS2 -eq 0 && $COUNT2 -ge $FAILURE_COUNT) ]]; then
CLS2=0
COUNT2=0
if [ $LLS2 -eq 1 ]; then
LLS2=0
else
LLS2=1
fi
logger -p local6.notice -t MULTIWAN[$$] "Link state for $WAN_TABLE2 is $(link_status $LLS2)"
else
CLS2=1
fi
LPS2=$CPS2
if [[ $CLS1 -eq 0 || $CLS2 -eq 0 ]] ; then
if [[ $STARTUP -eq 1 ]] ; then
STARTUP=0
CHECK_INTERVAL=15
fi
if [[ $LLS1 -eq 1 && $LLS2 -eq 0 ]] ; then
logger -p local6.notice -t MULTIWAN[$$] "Applying $WAN_TABLE2 only route."
ip route change default scope global via $WAN_GW2 dev $WAN_IF2
elif [[ $LLS1 -eq 0 && $LLS2 -eq 1 ]] ; then
logger -p local6.notice -t MULTIWAN[$$] "Applying $WAN_TABLE1 only route."
ip route change default scope global via $WAN_GW1 dev $WAN_IF1
elif [[ $LLS1 -eq 0 && $LLS2 -eq 0 ]] ; then
logger -p local6.notice -t MULTIWAN[$$] "Applying multiwan load balancing route."
ip route replace default scope global nexthop via $WAN_GW1 dev $WAN_IF1 weight $WAN_WEIGHT1 nexthop via $WAN_GW2 dev $WAN_IF2 weight $WAN_WEIGHT2
fi
fi
sleep $CHECK_INTERVAL
done
* Rendere eseguibile il suddetto script
* Creare lo script di init ''/etc/init.d/multiwan'' (si suppone che non usiate quella ciofeca di systemd!)
#!/bin/sh
### BEGIN INIT INFO
# Provides: multiwan
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Monitor internet connection links and modify route depending upon link state
# Description: This is a startup script for multiwan. Multiwan monitors multiple internet connections
# to provide failover routes depending upon link state.
### END INIT INFO
# Author: Steve Buzonas
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC=multiwan # Introduce a short description here
NAME=multiwan # Introduce the short server's name here
DAEMON=/usr/local/sbin/multiwan # Introduce the server's location here
DAEMON_ARGS="" # Arguments to run the daemon with
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet -b -m --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet -b -m --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:
* Rendere eseguibile il suddetto script
* Creare la configurazione in ''/etc/multiwan.conf'' avendo cura di adattarla ai vostri nomi, interfacce e indirizzi IP
PING_TARGET="8.8.8.8"
PING_TIMEOUT=2
SUCCESS_COUNT=4
FAILURE_COUNT=1
WAN_IF1="ens18"
WAN_IF2="ens19"
WAN_GW1="93.33.25.249"
WAN_GW2="109.23.6.49"
WAN_TABLE1="Fastweb"
WAN_TABLE2="Ehiweb"
WAN_WEIGHT1=1
WAN_WEIGHT2=1
* Fare eseguire lo script multiwan al boot, rendendolo attivo come demone, usando il comando:
insserv multiwan
A questo punto, riavviando la macchina (ce l'avete una console che funzioni anche SENZA LA RETE, vero? Se abbiamo sbagliato qualcosa, addio connessione di rete, ci siamo chiusi fuori per sempre!) dovremmo avere due interfacce di rete attive, e nel syslog vedremo anche le righe relative allo script multiwan che ci dice quali interfacce sta usando e come.
==== Test ====
La macchina dovrebbe essere raggiungibile da fuori a tutti e due i suoi indirizzi pubblici. Staccando una delle due linee l'altra dovrebbe rimanere totalmente funzionante, e la macchina dovrebbe (dopo qualche secondo) usare sempre la connessione funzionante, saltando dall'una all'altra automaticamente.
==== E il NAT? ====
Se il nostro host non deve fare nat verso altri host, siamo a posto così. Ma se volessimo fare NAT verso altri host, allora abbiamo due soluzioni: quella "comoda" è farci un firewall con Openwrt/LEDE e usare il pacchetto MWAN3 che fa tutto lui. Ma se vogliamo farlo a mano, occorre aggiungere un pochino di roba a questa config.
* Per il NAT uscente (dalla LAN alle due WAN) è sufficiente inserire due regole di iptables che facciano SNAT delle connessioni, badando a indicare l'interfaccia giusta e il giusto indirizzo IP sul quale fare nat, una cosa tipo questa:
iptables -t nat -A POSTROUTING -o ens18 -j SNAT -s 192.168.2.0/24 --to-source 93.33.25.253
iptables -t nat -A POSTROUTING -o ens19 -j SNAT -s 192.168.2.0/24 --to-source 109.23.6.53
* Se serve il NAT entrante (per esempio, un host della LAN pubblica una porta sulle due WAN, in modo che da fuori, collegandosi a uno qualsiasi dei due ip pubblici, si raggiunga sempre lo stesso host della LAN) allora la cosa si complica un pochino. Dovremo infatti usare il modulo fwmark di iptables, e gestire i mark nelle regole di routing. Diciamo che vogliamo nattare la porta 25 (smtp) in ingresso, e mandarla a un server che sta in LAN.
* Nel firewall dobbiamo fare il DNAT, e questo e` ovvio. Facendo due regole, una per interfaccia, dico al firewall che qualsiasi connessione entrante da fuori sulla 25 (e su tutte e due le WAN) venga mandata allo stesso host interno
iptables -t nat -A PREROUTING -p tcp -i enps18 --dport 25 -j DNAT --to 192.168.2.2
iptables -t nat -A PREROUTING -p tcp -i enps19 --dport 25 -j DNAT --to 192.168.2.2
* Nel firewall però dobbiamo anche marcare con fwmark le connessioni che stiamo andando a nattare, allo scopo di poterle poi "ricondurre" alla giusta interfaccia WAN, ovvero per fare in modo che i pacchetti di risposta ad una connessione che entra dalla interfaccia WAN numero 1 vengano mandati fuori dalla interfaccia WAN numero 1 e non dall'altra (e viceversa ovviamente). Per farlo, ci sono 3 cose da fare; marcare le connessioni entranti (operazione che avviene nella table "mangle") distinguendo con un mark diverso quelle della interfaccia WAN 1 da quelle della WAN 2, e poi restorare il mark nei pacchetti "related, established" che tornano dalla LAN alla WAN, in modo che poi possiamo "dirigerli" verso la giusta interfaccia WAN usando una "ip rule".
# marco con mark 1 le connessoni "new" che entrano dalla enps18
iptables -t mangle -A PREROUTING -p tcp -i enps18 --dport 25 -m conntrack --ctstate NEW -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -p tcp -i enps18 --dport 25 -m conntrack --ctstate NEW -j CONNMARK --save-mark
# marco con mark 2 le connessoni "new" che entrano dalla enps19
iptables -t mangle -A PREROUTING -p tcp -i enps19 --dport 25 -m conntrack --ctstate NEW -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -p tcp -i enps19 --dport 25 -m conntrack --ctstate NEW -j CONNMARK --save-mark
# dico a iptables di marcare (con mark 1 o 2, a seconda di come erano in origine) i pacchetti che tornano indietro.
iptables -t mangle -A PREROUTING -i -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark
* Fatto questo, devo aggiungere due regole di routing che, a seconda del mark (1 o 2) mandino il traffico uscente verso la wan giusta. Per farlo, possiamo modificare ulteriormente il file ''/etc/network/interfaces'' in modo da aggiungere due nuove regole di routing che siano legate al mark (vedi le regole commentate all'inizio della pagina)
post-up ip rule add from all fwmark 0x1 table Fastweb
post-up ip rule add from all fwmark 0x2 table Ehiweb