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!)
### 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 <steve@fancyguy.com> # 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 <interfaccia LAN> -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