Multi WAN e failover su Mikrotik ROS 7.x
Lo scopo di questa configurazione è avere due (o più) link WAN con una divisione del traffico fra i due. È inoltre possibile creare regole specifiche per traffico specifico (per esempio SMTP esce sempre su una specifica WAN). In caso di guasto a una delle WAN, il traffico verrà ridiretto sulle WAN rimanenti. In questo esempio useremo solo due connessioni, ma non è difficile adattarlo per averne di più.
Questa configurazione permette al traffico entrante da entrambe le connessioni di essere ridiretto (DNAT) verso delle macchine interne, o di essere ricevuto dal nostro Mikrotik. Per fare questo occorre che esista una regola che dice, in sostanza: "se il traffico entra dal provider X, i pacchetti che escono come risposta devono anche essi passare dal provider X"
La configurazione in questo esempio prevede di avere due router per i nostri due provider, tutti e due con una subnet privata dietro al router, e tutti gli ip assegnati al Mikrotik sono statici, e le rotte inserite manualmente.
Funzionalità che vogliamo ottenere
- Il traffico uscente viene diviso fra i due provider in maniera per quanto possibile bilanciata
- Possiamo definire regole per cui certo traffico non rispetta la divisione ma viene fatto passare per il provider che vogliamo noi.
- Le connessioni da fuori a dentro funzionano su tutti e due i provider
- Uso un ping verso un ip esterno (9.9.9.9 o altro ip pubblico che so essere raggiungibile con un ping) per determinare se i provider vanno. Se un provider smette di funzionare, smetto di usarlo e uso il rimanente.
Descrizione dettagliata della configurazione del sistema
Per aiutarvi a capire come funziona il sistema, descrivo qui la configurazione usata in questo esempio.
- ether1 è la interfaccia connessa al provider numero 1 con ip 192.168.1.0/24, dove il router del provider è il .1 e il Mikrotik è il .2, tutto statico, niente dhcp.
- ether2 è la interfaccia connessa al provider numero 2 con ip 192.168.2.0/24, dove il router del provider è il .1 e il Mikrotik è il .2, tutto statico, niente dhcp.
- ether1 e ether2 sono state messe tutte e due nella interface list denominata "WAN"
- usiamo 9.9.9.9 (un DNS pubblico che risponde al ping) come indirizzo da pingare per vedere se il provider numero 1 è online
- usiamo 9.9.9.10 (un DNS pubblico che risponde al ping) come indirizzo da pingare per vedere se il provider numero 2 è online
- Le regole di firewall "minime" sono già presenti (in pratica, la default config) per cui non mi occuperò né della sicurezza né della configurazione del SNAT fra LAN e WAN
Configurazione
Passiamo quindi alla effettiva configurazione del Mikrotik:
Disattivare (se ci sono) le regole di fasttrack nel forward. Tipicamente c'è una regola di fasttrack nel firewall della configurazione di default. Se non la togliamo il tracking delle connessioni non funzionerà. È importante, non dimenticatelo.
- È importante escludere dal meccanismo di load balancing tutto il traffico diretto verso "ciò che non è internet", quindi LAN, DMZ, VPN, o qualsiasi altra rete che non sia internet. Creiamo quindi una address list che chiameremo "local" (nome a nostra scelta) e ci mettiamo dentro le subnet della nostra LAN, DMZ, VPN, e anche le subnet (private o pubbliche) che abbiamo fra il nostro Mikrotik e i router dei nostri due provider (se non siamo in pppoe).
/ip firewall address-list add address=192.168.88.0/24 list=local #LAN add address=192.168.1.0/24 list=local #WAN1 add address=192.168.2.0/24 list=local #WAN2
- Creare una routing table per ogni provider
/routing table add disabled=no fib name=ISP1_table add disabled=no fib name=ISP2_table
- In ogni routing table così creata, devo impostare una rotta di default verso il gateway del relativo provider, usando il trucco delle rotte ricorsive per poter capire se il provider funziona o è guasto. In questa configurazione non specifico diverse distance, tengo tutto default a 1. Queste regole di routing verranno usate dal traffico che verrà marcato dalle regole di mangle che inseriremo nel firewall in seguito. Il traffico non marcato continuerà a usare la table "main". Notare che queste tabelle sono fatte in modo che se per qualche motivo il traffico non trova la sua "strada" per essere routato dentro la tabella allora l'elaborazione passa alla table main. Questo significa che se va giù il provider legato alla specifica tabella, e la rotta in essa inserita diventa inattiva (c'è il check del ping che la rende inattiva) allora il traffico passera` nella table main. Userò 9.9.9.9 come indirizzo di riferimento per il test di connettività del provider 1, e userò 9.9.9.10 per il provider 2.
/ip route add disabled=no distance=1 dst-address=9.9.9.9/32 gateway=192.168.1.1 routing-table=ISP1_table scope=11 target-scope=10 add check-gateway=ping disabled=no distance=1 dst-address=0.0.0.0/0 gateway=9.9.9.9 routing-table=ISP1_table scope=30 target-scope=11 add disabled=no distance=1 dst-address=9.9.9.10/32 gateway=192.168.1.1 routing-table=ISP2_table scope=11 target-scope=10 add check-gateway=ping disabled=no distance=1 dst-address=0.0.0.0/0 gateway=9.9.9.10 routing-table=ISP2_table scope=30 target-scope=11
- Nella routing table "main" inserisco di fatto le stesse rotte che ho inserito nelle due routing table ISP1 e ISP2, questa volta però le inserisco con distanze diverse, in modo da rendere "preferenziale" una delle due. Arbitrariamente renderò preferenziale quella del provider numero 1, ma di fatto non fa molta differenza. Queste rotte hanno un duplice scopo: Il primo è quello di funzionare come "scorta" se uno dei due provider va giù, in quel caso il traffico che era destinato al provider che è andato giù (e che era stato diretto, per esempio, alla table ISP2) finirà gestito qui nella main, dove troverà una rotta funzionante verso il provider 1. Il secondo è quello di gestire il traffico che non viene diretto verso nessuna delle due routing tables (ISP1 o ISP2) come per esempio il traffico generato dal Mikrotik stesso, il quale non viene gestito dalla regola di load balancing, ma uscirà sempre dal provider 1 (se funzionante, altrimenti uscirà dal provider 2).
/ip route # rotta per il provider 1 con distanza 1 add disabled=no distance=1 dst-address=9.9.9.9/32 gateway=192.168.1.1 routing-table=main scope=11 target-scope=10 add check-gateway=ping disabled=no distance=1 dst-address=0.0.0.0/0 gateway=9.9.9.9 routing-table=main scope=30 target-scope=11 # rotta per il provider 2 con distanza 2 (automaticamente disattiva se non muore la rotta 1) add disabled=no distance=1 dst-address=9.9.9.10/32 gateway=192.168.2.1 routing-table=main scope=11 target-scope=10 add check-gateway=ping disabled=no distance=2 dst-address=0.0.0.0/0 gateway=9.9.9.10 routing-table=main scope=30 target-scope=11 # (Distance=2, quindi questa ha priorità minore)
- Nella sezione "mangle" del firewall, dobbiamo definire delle regole che permettano al traffico entrante da ogni provider di essere rimandato indietro attraverso quel provider, questo per poter nattare il traffico DA FUORI A DENTRO. Creo quindi due regole, ogni regola dice che le connessioni in connection state "new" che entrano dalla interfaccia connessa al provider X e che attualmente non hanno nessun connection mark le devo marcare con una connection mark "ISPX_conn" (con X che è il numero del nostro provider, 1 o 2)
/ip firewall mangle add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new in-interface=ether1 new-connection-mark=ISP1_conn passthrough=yes add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new in-interface=ether2 new-connection-mark=ISP2_conn passthrough=yes
- Nella sezione "mangle" del firewall, devo creare delle regole che permettano al traffico uscente dal Mikrotik, ovvero i pacchetti di risposta al traffico entrante che arriva da fuori (quello del punto precedente) di essere indirizzato verso la corretta tabella di routing a seconda di come quel traffico e` stato marcato al momento in cui e` entrato. Questo mi permette di rispondere tramite il provider 1 al traffico che entra attraverso il provider 1, e tramite il provider 2 al traffico che entra attraverso il provider 2. Notare che queste regole non si applicano al traffico da/per la mia LAN ma solo da/per il Mikrotik. Poi penseremo a quello da/per la LAN.
/ip firewall mangle add action=mark-routing chain=output connection-mark=ISP1_conn new-routing-mark=ISP1_table passthrough=yes add action=mark-routing chain=output connection-mark=ISP2_conn new-routing-mark=ISP2_table passthrough=yes
Arrivati a questo punto abbiamo costruito un sistema che è capace di ricevere connessioni da fuori da tutte le interfacce WAN e rispondere sulla interfaccia giusta. Quello che ancora non facciamo è dividere il traffico uscente dalla LAN (nattato verso internet) in modo che usi tutte e due le connessioni. Con la configurazione attuale il traffico uscente dalla LAN viene passato nella routing table main e usa quindi la prima delle WAN in ordine di distanza. Questo traffico infatti non tocca le routing table specifiche di ogni WAN. Questa soluzione ci può andare benissimo così se il nostro scopo è avere tutto il traffico uscente sul provider 1 e solo in caso di guasto passare al provider 2, mantenendo però la capacità di rispondere al traffico entrante da tutti e due i provider. Ma non siamo qui per questo, siamo qui per fare load balancing, quindi procediamo.
Noi però vogliamo che anche il traffico uscente dalla LAN verso internet venga distribuito fra i nostri due provider, dobbiamo quindi definire una regola che distribuisca le connessioni uscenti nel modo in cui vogliamo noi (con eventualmente anche una preferenza per l'una o l'altra connessione). Qui giocando con le regole possiamo fare quello che vogliamo, da un semplice 50/50 di connessioni a situazioni non bilanciate tipo 66/33 oppure possiamo definire regole che dicono che per dire certo traffico esce sempre dalla linea X a meno che non vada giù, e allora ricade sull'altra. Cominciamo con un 50/50 e poi vedremo quali altri trucchi possiamo adottare. Qui usiamo la funzione "per connection classifier", e possiamo scegliere diversi modi per generare una distribuzione "circa al metà" del traffico fra i provider. Per approfondire i vari metodi che possiamo usare, potete fare riferimento a https://help.mikrotik.com/docs/spaces/ROS/pages/152600617/Per+connection+classifier.
/ip firewall mangle add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local dst-address-list=!local in-interface-list=LAN new-connection-mark=ISP1_conn passthrough=yes per-connection-classifier=both-addresses-and-ports:2/0 add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local dst-address-list=!local in-interface-list=LAN new-connection-mark=ISP2_conn passthrough=yes per-connection-classifier=both-addresses-and-ports:2/1
- Se abbiamo anche una DMZ oltre a una LAN, e vogliamo che anche le connessioni uscenti da essa vengano gestite allo stesso modo, dobbiamo replicare le regole del punto precedente facendo però riferimento alla in-interface-list DMZ (interface list che avremo creato e popolato correttamente)
/ip firewall mangle add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local dst-address-list=!local in-interface-list=DMZ new-connection-mark=ISP1_conn passthrough=yes per-connection-classifier=both-addresses-and-ports:2/0 add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local dst-address-list=!local in-interface-list=DMZ new-connection-mark=ISP2_conn passthrough=yes per-connection-classifier=both-addresses-and-ports:2/1
Notare che qui usiamo come riferimento per distribuire le varie connessioni un numero calcolato partendo da indirizzo ip e porta sia della sorgente che del destinatario del traffico, ovvero in altri termini IP e porta del PC locale che sta generando il traffico, più IP e porta del server remoto al quale ci colleghiamo. Questo è sicuramente il metodo migliore per "distribuire circa a caso" le connessioni fra i due provider, però può dare problemi con siti "sicuri" che non accettano che quando sono autenticato io faccia connessioni a caso da due ip pubblici diversi (le mie due WAN). Nel caso posso o creare una regola per cui tutto il traffico verso un determinato ip remoto passa per una sola connessione sempre, oppure forse tentare con una logica più conservativa per il solo traffico https (porta di destinazione 443) facendolo trattare con il connection classifier "both-addresses" e non "both-addresses-and-ports". C'è sicuramente una marea di piccoli problemi che possono sorgere e che vanno studiati e risolti caso per caso.
- Nei punti precedenti ho creato regole di mangle che marcano le connessioni con un connection mark ma non ho mai indicato cosa devo farci, quindi ora devo fare in modo che il traffico che abbiamo marcato vada a usare le tabelle di routing relativo ai nostri due provider:
/ip firewall mangle add action=mark-routing chain=prerouting connection-mark=ISP1_conn in-interface-list=LAN new-routing-mark=ISP1_table passthrough=yes add action=mark-routing chain=prerouting connection-mark=ISP2_conn in-interface-list=LAN new-routing-mark=ISP2_table passthrough=yes
- Anche in questo caso, come sopra, se abbiamo una DMZ dobbiamo replicare queste regole con la interface list "DMZ"
/ip firewall mangle add action=mark-routing chain=prerouting connection-mark=ISP1_conn in-interface-list=DMZ new-routing-mark=ISP1_table passthrough=yes add action=mark-routing chain=prerouting connection-mark=ISP2_conn in-interface-list=DMZ new-routing-mark=ISP2_table passthrough=yes
Notare che con questa configurazione ho reso funzionante anche l'eventuale NAT entrante dai due provider verso la DMZ o la LAN, perché il traffico in ingresso viene marcato con un connection mark quando arriva verso il router. Questo traffico verrà poi nattato dalle regole di DNAT (che qui non stiamo a descrivere, ma sono le classiche regole di NAT che useremmo se avessimo un solo provider) e quindi verrà diretto verso la LAN o la DMZ, e quando uscirà nuovamente verrà indirizzato verso la tabella di routing corretta in quanto il connection mark che gli è stato dato quando è entrato farà si che le regole di mark-routing lo dirigano verso la giusta routing table.
Test
Ora che la configurazione è fatta possiamo provarla.
- Con tutti e due i provider connessi, il traffico generato dalla LAN deve uscire diviso circa al 50% fra i due provider.
- Con tutti e due i provider connessi, il traffico entrante dal provider numero 1 deve funzionare, sia esso diretto verso il Mikrotik o sia esso ridiretto da una regola di DNAT verso la rete interna
- Con tutti e due i provider connessi, il traffico entrante dal provider numero 2 deve funzionare, sia esso diretto verso il Mikrotik o sia esso ridiretto da una regola di DNAT verso la rete interna
- Disconnettendo uno qualsiasi due due provider, dopo una decina di secondi al massimo deve avvenire la commutazione, e il traffico generato dalla LAN deve uscire sempre tutto verso il provider rimanente.
- Riconnettendolo ovviamente dopo qualche secondo il traffico verrà nuovamente bilanciato
Regole speciali di mangle
Se vogliamo forzare un determinato tipo di traffico ad avere comportamenti diversi dal "dividersi a caso al 50%", dovremo inserire regole di mangle specifiche prima di quelle generiche (in quanto l'ordine conta). Per esempio possiamo cercare di evitare che il traffico https venga sparso su due connessioni diverse usando una regola che dice che tutto il traffico dal client X in LAN e diretto verso il server Y su internet attraversi sempre la stessa connessione (una delle due a caso, ma sempre quella). Una regola del genere (non l'ho provata, non sono sicuro che funzioni) potrebbe essere questa:
/ip firewall mangle add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local dst-address-list=!local in-interface-list=LAN protocol=tcp dst-port=443 new-connection-mark=ISP1_conn passthrough=yes per-connection-classifier=both-addresses:2/0 add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local dst-address-list=!local in-interface-list=LAN protocol=tcp dst-port=443 new-connection-mark=ISP2_conn passthrough=yes per-connection-classifier=both-addresses:2/1 # lo sapevate che HTTP/2 (QUIC) usa anche UDP oltre a TCP? Se non lo sapevate, ora lo sapete. Per questo motivo metto regole anche per la porta di destionazione 443 udp oltre che 443 tcp add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local dst-address-list=!local in-interface-list=LAN protocol=udp dst-port=443 new-connection-mark=ISP1_conn passthrough=yes per-connection-classifier=both-addresses:2/0 add action=mark-connection chain=prerouting connection-mark=no-mark connection-state=new dst-address-type=!local dst-address-list=!local in-interface-list=LAN protocol=udp dst-port=443 new-connection-mark=ISP2_conn passthrough=yes per-connection-classifier=both-addresses:2/1