{"id":2102,"date":"2018-05-28T16:01:09","date_gmt":"2018-05-28T14:01:09","guid":{"rendered":"http:\/\/blog.sfsoft.it\/?p=2102"},"modified":"2018-06-04T10:29:01","modified_gmt":"2018-06-04T08:29:01","slug":"configurare-ubuntu-18-04-come-gateway-con-failoverload-balancing-su-doppia-wan","status":"publish","type":"post","link":"http:\/\/www.sfsoft.it\/blog\/2018\/05\/28\/configurare-ubuntu-18-04-come-gateway-con-failoverload-balancing-su-doppia-wan\/","title":{"rendered":"Configurare Ubuntu 18.04 come gateway con failover\/load balancing su doppia WAN"},"content":{"rendered":"<p><span style=\"color: #ff0000;\">Aggiorno l&#8217;articolo <a style=\"color: #ff0000;\" href=\"http:\/\/blog.sfsoft.it\/2016\/01\/19\/configurare-ubuntu-come-gateway-con-failoverload-balancing-su-doppia-wan\/\">Configurare Ubuntu come gateway con failover\/load balancing su doppia WAN<\/a> adattandolo a Ubuntu 18.04<\/span><\/p>\n<p>Abbiamo due WAN e vogliamo impostare una sorta di <em>failover<\/em> ( ridondanza delle connessioni, se cade una il gateway passa alla seconda e\/o viceversa ) o di <em>load balancing<\/em> ( per suddividere il carico fra le due connessioni ).<\/p>\n<p>Se non ci interessa niente di troppo particolare o personalizzabile si fa prima a utilizzare le distro preposte a questo, si veda <a href=\"http:\/\/blog.sfsoft.it\/2016\/01\/11\/pfsense-failover-e-load-balancing-su-doppia-wan\/\" target=\"_blank\" rel=\"noopener\">pfSense: failover e load balancing su doppia WAN<\/a>.<\/p>\n<p>A me serve poter avere pi\u00f9 flessibilit\u00e0 sulla macchina <em>gateway<\/em> e poter installare altri programmi quindi ho provato a replicarne le funzionalit\u00e0 su un Ubuntu Server 18.04<\/p>\n<p>Si veda l&#8217;articolo <a href=\"http:\/\/blog.sfsoft.it\/2018\/05\/28\/configurare-ubuntu-18-04-come-gateway\/\" target=\"_blank\" rel=\"noopener\">Configurare Ubuntu 18.04 come gateway<\/a> per le istruzioni base su come configurare un <em>gateway<\/em>.<\/p>\n<p>Da Ubuntu 16.04 sono cambiati i nomi delle interfacce di rete, se vogliamo i nomi &#8220;vecchio stile&#8221; rimando all&#8217;articolo <a href=\"http:\/\/blog.sfsoft.it\/2017\/01\/11\/rinominare-le-interfacce-di-rete-di-ubuntu-16-04-con-i-nomi-vecchi\/\">Rinominare le interfacce di rete di Ubuntu 16.04 con i nomi \u201cvecchi\u201d<\/a>.<\/p>\n<p>La situazione della prova \u00e8 la seguente ( gli IP sono fittizzi, i dati delle 2 WAN ovviamente dipendono dagli ISP ):<\/p>\n<ul>\n<li>LAN: eth0 ( 192.168.1.1 )<\/li>\n<li>WAN1: eth1 ( 192.168.10.2 \/ gateway 192.168.10.1 )<\/li>\n<li>WAN2: eth2 ( 192.168.20.2 \/ gateway 192.168.20.1 )<\/li>\n<\/ul>\n<p>Visto che normalmente non si potrebbe impostare due <em>gateway<\/em> diversi occorre utilizzare il programma <em>iproute<\/em> che ci da qualche scappatoia.<\/p>\n<p>Per prima cosa occorre creare due nuove tabelle per il <em>routing<\/em>:<\/p>\n<pre class=\"lang:default decode:true\">sudo nano \/etc\/iproute2\/rt_tables<\/pre>\n<p>E si aggiungono le nuove tabelle:<\/p>\n<pre class=\"lang:default decode:true\">253     default\r\n\r\n# tabelle aggiunte\r\n101     wan1\r\n102     wan2\r\n\r\n0       unspec\r\n<\/pre>\n<p>Occorre ora istruire <em>iproute<\/em>, ho creato un semplice <em>script<\/em> base:<\/p>\n<pre class=\"lang:default decode:true \">#!\/bin\/bash\r\n\r\n# Cancello la routing table base\r\nip route del default table main\r\n\r\n# Imposto la nuova routing table della wan1\r\nip route flush table wan1\r\nip route add 192.168.10.0\/24 dev eth1 src 192.168.10.2 table wan1\r\nip route add default via 192.168.10.1 dev eth1 table wan1\r\nip route add 192.168.1.0\/24 dev eth0 table wan1\r\nip route add 192.168.20.0\/24 dev eth2 table wan1\r\nip route add 127.0.0.0\/8 dev lo table wan1\r\n\r\n# Imposto la nuova routing table della wan2\r\nip route flush table wan2\r\nip route add 192.168.20.0\/24 dev eth2 src 192.168.20.2 table wan2\r\nip route add default via 192.168.20.1 dev eth2 table wan2\r\nip route add 192.168.1.0\/24 dev eth0 table wan2\r\nip route add 192.168.10.0\/24 dev eth1 table wan2\r\nip route add 127.0.0.0\/8 dev lo table wan2\r\n\r\n# Applico le regole di routing\r\nip rule add from 192.168.10.2 table wan1\r\nip rule add from 192.168.20.2 table wan2\r\nip rule add order 10 from all lookup main\r\nip rule add order 20 iif lo lookup wan1\r\n\r\n# Istruzioni per poter poi suddividere il carico tramite iptables\r\nip rule add fwmark 1 table wan1 prio 1024\r\nip rule add fwmark 2 table wan2 prio 1025\r\n\r\n# IP Stack\r\nfor f in \/proc\/sys\/net\/ipv4\/conf\/*\/rp_filter ; do echo 0 &gt; $f ; done\r\necho 0 &gt; \/proc\/sys\/net\/ipv4\/route\/flush<\/pre>\n<p>Se a questo punto ci servisse solo un bilanciamento del carico o <em>failover<\/em> automatico basterebbe aggiungere il comando:<\/p>\n<pre class=\"lang:default decode:true\">ip route add default scope global nexthop via 192.168.10.1 dev eth1 weight 1 \r\nnexthop via 192.168.20.1 dev eth2 weight 1<\/pre>\n<p>Ma a me serve poter decidere chi usa quale connessione e solo nell&#8217;eventualit\u00e0 che una delle due cada allora deve spostare temporaneamente tutti su quella attiva, quindi come primo passo ho creato un <em>script<\/em> che si occupa di indirizzare le richieste da una parte all&#8217;altra, e se parametrizzata forza tutti ad usare una specifica connessione:<\/p>\n<pre class=\"lang:default decode:true\">#!\/bin\/bash\r\n\r\n# Per forzare tutti i gruppi sulla stessa WAN (es. link assente)\r\n# occorre richiamare:\r\n#   &lt;nome-script&gt; wan1\r\n# oppure:\r\n#   &lt;nome-script&gt; wan2\r\n\r\n# Tabelle\r\nTBL1=1\r\nTBL2=2\r\n\r\n# Controllo parametri\r\nWAN=$1\r\n\r\ncase $WAN in\r\n  \"wan1\") TBL2=$TBL1 ;; # Forza tutti i gruppi si WAN1\r\n  \"wan2\") TBL1=$TBL2 ;; # Forza tutti i gruppi su WAN2\r\nesac\r\n\r\n# Gruppo #1\r\nGRP1=(\r\n192.168.1.10\r\n192.168.1.11\r\n192.168.1.12\r\n)\r\n\r\n# Gruppo #2\r\nGRP2=(\r\n192.168.1.20\r\n192.168.1.21\r\n192.168.1.22\r\n)\r\n\r\n# Regole balancing\r\niptables -t mangle -F\r\niptables -t mangle -X\r\nfor i in \"${GRP1[@]}\"\r\ndo\r\n  iptables -t mangle -A PREROUTING -s $i -j MARK --set-mark $TBL1\r\ndone\r\nfor i in \"${GRP2[@]}\"\r\ndo\r\n  iptables -t mangle -A PREROUTING -s $i -j MARK --set-mark $TBL2\r\ndone<\/pre>\n<p>Come ultima fase occorre controllare lo stato delle due connessioni ed in caso che una o l&#8217;altra risulti scollegata va spostato il carico su quella attiva, per poi ripristinare il tutto quando la connessione ritorna.<\/p>\n<p>Questo script quando eseguito verifica tramite ping in uscita sulla WAN da controllare se ha risposte, in caso contrario segna la WAN come offline e sposta tutto il carico sulla WAN attiva.<\/p>\n<pre class=\"lang:default decode:true \">#!\/bin\/bash\r\n\r\n# parametri\r\nFORCE=false\r\nEMAIL=true\r\nfor i in $@; do\r\n  case $i in\r\n    \"--force\" | \"-f\" ) FORCE=true ;; # Forza l'aggiornamento\r\n    \"--no-email\" | \"-n\" ) EMAIL=false ;; # Inibisce l'email\r\n  esac\r\ndone\r\n\r\n# va installato un mailserver\/relay e un client\r\n# ad esempio sendmail\/postfix\/nullmailer e mutt\r\nEMAIL_TXT=\"\" # Contenuto del messaggio da mandare via email\r\nEMAIL_PRG=\"mutt\"\r\nEMAIL_TO=\"xxxxx@xxx.xx\"\r\n\r\n# directory corrente\r\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" &amp;&amp; pwd )\"\r\n\r\n# script da eseguire per il bilanciamento\r\nSCRIPT=\"&lt;nome-script-per-il-bilanciamento-dei-gruppi&gt;\"\r\n\r\n# file dove salvare l'ultimo stato del controllo\r\nSTATUS=\"$0-last\"\r\n\r\n# indirizzi IP da controllare lo status connessioni\r\nIP1=\"8.8.8.8\"\r\nIP2=\"8.8.4.4\"\r\n\r\n# comandi di eliminazione\/aggiunta lookup\r\nDEL=\"ip rule del order 20\"\r\nADD=\"ip rule add order 20 iif lo lookup\"\r\n\r\n# controllo status wan2\r\nWAN2=\"off\"\r\n# rimuovo tutte le regole in ordine 20, quelle che uso come lookup localhost\r\nfor i in $(ip rule show | grep \"20:\" | awk {'print $1'})\r\ndo\r\n  $DEL &gt; \/dev\/null 2&gt;&amp;1\r\ndone\r\n# aggiungo il lookup sulla wan2\r\n$ADD wan2\r\nping -I eth2 $IP1 -c 1 &gt; \/dev\/null 2&gt;&amp;1\r\n# se fallisce il primo ping provo il secondo\r\nif [ $? -ne 0 ]; then\r\n  ping -I eth2 $IP2 -c 1 &gt; \/dev\/null 2&gt;&amp;1\r\nfi\r\n# se questa volta non ho errori allora la wan2 \u00e8 attiva\r\nif [ $? -eq 0 ]; then\r\n  WAN2=\"on\"\r\nfi\r\n\r\n# controllo status wan1\r\nWAN1=\"off\"\r\n# qui elimino solo una regola 20 perch\u00e8 gli eventuali doppioni li ho tolti prima\r\n$DEL &gt; \/dev\/null 2&gt;&amp;1\r\n# aggiungo il lookup sulla wan1\r\n$ADD wan1\r\nping -I eth1 $IP1 -c 1 &gt; \/dev\/null 2&gt;&amp;1\r\n# se fallisce il primo ping provo il secondo\r\nif [ $? -ne 0 ]; then\r\n  ping -I eth1 $IP2 -c 1 &gt; \/dev\/null 2&gt;&amp;1\r\nfi\r\n# se questa volta non ho errori allora la wan1 \u00e8 attiva\r\nif [ $? -eq 0 ]; then\r\n  WAN1=\"on\"\r\nfi\r\n\r\n\r\n\r\n# se dall'ultimo controllo la situazione \u00e8 cambiata avviso via email\r\nif [ -f $STATUS ]; then\r\n  LAST=$(cat $STATUS)\r\nfi\r\nCURR=\"wan1: $WAN1 | wan2: $WAN2\"\r\n\r\nif [ $FORCE == true ] || [ \"$CURR\" != \"$LAST\" ]; then\r\n\r\n  EMAIL_TXT=$(date)\"nnLast status: $LASTnCurrent status: $CURRn\"\r\n  # aggiorno lo status\r\n  echo $CURR &gt; $STATUS\r\n  $DEL &gt; \/dev\/null 2&gt;&amp;1\r\n  if [ $WAN1 = \"off\" -a $WAN2 = \"on\" ]; then\r\n    EMAIL_TXT=$EMAIL_TXT\"nForced WAN2\"\r\n    $ADD wan2\r\n    # forzo il bilanciamento solo sulla wan2\r\n    $SCRIPT wan2\r\n  elif [ $WAN1 = \"on\" -a $WAN2 = \"off\" ]; then\r\n    EMAIL_TXT=$EMAIL_TXT\"nForced WAN1\"\r\n    $ADD wan1\r\n    # forzo il bilanciamento solo sulla wan1\r\n    $SCRIPT wan1\r\n  else\r\n    EMAIL_TXT=$EMAIL_TXT\"nUse WAN1 and WAN2\"\r\n    $ADD wan1\r\n    # se entrambe le wan sono su eseguo il bilanciamento normale\r\n    $SCRIPT\r\n  fi\r\n\r\n  #spedire email\r\n  if [ $EMAIL == true ]; then\r\n\r\n    EMAIL_TITLE=\"$HOSTNAME: Connection status changed\"\r\n    if [ $FORCE == true ]; then EMAIL_TITLE=\"$EMAIL_TITLE (FORCED)\"; fi\r\n    echo -e $EMAIL_TXT | $EMAIL_PRG -s \"$EMAIL_TITLE\" $EMAIL_TO\r\n\r\n  fi\r\n\r\nfi\r\n<\/pre>\n<p>Lo script \u00e8 possibile farlo girare in un cron ogni minuto, se occorre avere un tempo di controllo pi\u00f9 ristretto occorre appoggiarsi ad un ulteriore script (che girer\u00e0 ogni minuto) e che eseguir\u00e0 lui ogni tot secondi (che non superi il minuto ovviamente) il controllo:<\/p>\n<pre class=\"lang:default decode:true \">#!\/bin\/bash\r\n\r\n# directory corrente\r\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" &amp;&amp; pwd )\"\r\n\r\n# numero di ripetizioni al minuto\r\nREP=6\r\n\r\nif (( $REP &gt; 30 )); then\r\n  echo \"Error in config\"\r\n  exit 1\r\nfi\r\n\r\nWAIT=$(expr 60 \/ $REP)\r\nfor ((  i=1; i&lt;$REP; i++  ))\r\ndo\r\n  $DIR\/&lt;nome-dello-script-per-il-controllo-della-connessione&gt;\r\n  sleep $WAIT\r\ndone<\/pre>\n<p>Nel caso sulla macchina <em>gateway<\/em> ci sia anche installato il proxy server <em>Squid<\/em> occorre anche dire a lui quale IP debba uscire da quale WAN.<\/p>\n<p>In tal caso si creano due ACL con gli IP o i range di suddivisione (si possono far popolare ogni volta in automatico dallo script che si occupa della suddivisione del carico) e nel file <em>squid.conf<\/em> si utilizza la regola <em>tcp_outgoing_address<\/em>:<\/p>\n<pre class=\"lang:default decode:true\">tcp_outgoing_address 192.168.10.2 acl-wan1\r\ntcp_outgoing_address 192.168.20.2 acl-wan2<\/pre>\n<p>Ricordandoci poi di riavviare Squid ad ogni modifica delle liste che il semplice reload delle regole non mi funziona con questo parametro.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Aggiorno l&#8217;articolo Configurare Ubuntu come gateway con failover\/load balancing su doppia WAN adattandolo a Ubuntu 18.04 Abbiamo due WAN e vogliamo impostare una sorta di failover ( ridondanza delle connessioni, se cade una il gateway passa alla seconda e\/o viceversa ) o di load balancing ( per suddividere il carico fra le due connessioni ). [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[15,3,23],"tags":[270,236,16,238,39,8,237,5],"_links":{"self":[{"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/posts\/2102"}],"collection":[{"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/comments?post=2102"}],"version-history":[{"count":2,"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/posts\/2102\/revisions"}],"predecessor-version":[{"id":2115,"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/posts\/2102\/revisions\/2115"}],"wp:attachment":[{"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/media?parent=2102"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/categories?post=2102"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.sfsoft.it\/blog\/wp-json\/wp\/v2\/tags?post=2102"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}