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.
Per aiutarvi a capire come funziona il sistema, descrivo qui la configurazione usata in questo esempio.
Passiamo quindi alla effettiva configurazione del Mikrotik:
/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
/routing table add disabled=no fib name=ISP1_table add disabled=no fib name=ISP2_table
/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
/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)
/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
/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.
/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
/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.
/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
/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.
Ora che la configurazione è fatta possiamo provarla.
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