Source routing

Traffico

In fase di sviluppo Leggere le avvertenze In fase di sviluppo

Due dei problemi presenti in ogni connessione ad Internet sono la scarsità di banda ed il fatto che, a volte, il collegamento si interrompe. In questa pagina presento una soluzione relativamente semplice che permette di utilizzare due connessioni ad Internet (nota 1). Due gli obbiettivi:

Lo scenario

Consideriamo una scuola (il mio caso) o un'azienda con molti PC:

  1. La banda è sempre poca quando si hanno centinaia di utenze collegate. Per questo utilizzeremo due connessioni (nota 1), la  cui banda viene sommata (nota 2), distribuendo il carico sulle due linee
  2. La connettività è essenziale: quindi meglio non affidarsi ad un singolo ISP. Per questo utilizzeremo le connessioni di due gestori (nota 1) il più possibile indipendenti tra di loro (nota 11)
  3. I computer usati dagli utenti devono essere numerosi: è importante avere presente questo vincolo perché quanto qui descritto non permette in genere di "raddoppiare" la banda del singolo utente, ma solo quella complessiva della rete

Quanto mostrato utilizza come router una macchina Linux Debian Buster, ma dovrebbe essere adattabile ad altre distribuzioni ed anche a router non basati su Linux.

Nella seguente figura sono mostrati lo schema della rete e gli indirizzi IP esemplificativi che utilizzerò in seguito:

Indirizzi IP

L'idea

Alcune macchine della rete 172.16.0.0/16 si collegheranno ad internet attraverso la connessione ISP-A, usando il modem/router 192.168.10.1, altre attraverso ISP-B usando il modem/router 192.68.111.254.

La decisione della connessione da utilizzare dipende dall'indirizzo IP della macchina da cui la connessione ha origine, da cui il nome source routing, caso particolare di policy routing; il tutto sarà gestito dal router e dovrà essere assolutamente trasparente rispetto agli utenti.

I passi da fare

  1. Configurare l'host per gestire due collegamenti ad Internet
  2. Configurare il router per permette l'accesso dalla rete locale
  3. Rendere automatico l'avvio dei servizi
  4. Prevedere il funzionamento anche nel caso di caduta di una delle connessioni

Perché non un routing "normale"?

Chiunque abbia una conoscenza anche minima delle reti sa che un router IP decide come inoltrare un pacchetto in base all'indirizzo di destinazione, consultando la sua tabella di routing (nota 10).

Il problema potrebbe quindi essere risolto usando rotte statiche basate sulla destinazione dei pacchetti IP, in parte instradati attraverso ISP-A ed in parte attraverso ISP-B. Infatti lo scopo del livello 3 ISO/OSI (IP nel nostro caso) è garantire una connessione end-to-end tra destinazione e sorgente, indipendentemente dal percorso.

Peccato che, se la rete è nattata, cade il concetto di connessione end-to-end a livello IP; e tutte le reti IPv4 ordinarie sono nattate... (nota 3).

I primi esperimenti di bilanciamento di due linee li ho fatti con il routing normale: facile da implementare ed il tutto sembrava funzionare... Purtroppo alcuni servizi internet usano centinaia di indirizzi IP e tutti devono essere raggiunti con lo stesso IP pubblico, pena errori casuali e difficili da identificare. Se non vi interessano servizi come quelli offerti da Microsoft, Google o Amazon, potete implementare anche un routing normale, altrimenti quella del source routing è l'unica soluzione funzionante (nota 4).

Configurare l'host

Innanzitutto occorre rendere operativo l'host che fungerà da router affinché possa collegarsi ad internet potendo scegliere la connessione da utilizzare.

Di seguito il file di configurazione della rete.

vv@router:~# cat /etc/network/interfaces
[...]
# ISP-B
auto ens192
 iface ens192 inet static
 address 192.168.111.100/24

# LAN
auto ens224
 iface ens224 inet static
 address 172.16.0.1/16

# ISP-A
auto ens256
 iface ens256 inet static
 address 192.168.10.100/24

Si noti l'assenza di qualsivoglia rotta di default e di conseguenza l'impossibilità di connettersi ad Internet (nota 5):

vv@router:~# ip route
172.16.0.0/16 dev ens224 proto kernel scope link src 172.16.0.1
192.168.10.0/24 dev ens256 proto kernel scope link src 192.168.10.100
192.168.111.0/24 dev ens192 proto kernel scope link src 192.168.111.100

vv@router:~# ping 8.8.8.8
connect: La rete non è raggiungibile

Verifichiamo di poter raggiungere i due modem/router:

vv@router:~# ping 192.168.10.1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0,956 ms

vv@router:~# ping 192.168.111.254
PING 192.168.111.254 (192.168.111.254) 56(84) bytes of data.
64 bytes from 192.168.111.254: icmp_seq=1 ttl=64 time=1,83 ms

Verifichiamo la connessione ad internet di entrambi i modem/router definendo una rotta predefinita provvisoria. Cominciamo con ISP-B:

root@router:~# ip route add default via 192.168.111.254

root@router:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=11.9 ms

root@router:~# wget -qO- ipinfo.io/ip
82.56.121.xxx

root@router:~# ip route del default

Passiamo ora a ISP-A:

root@router:~# ip route add default via 192.168.10.1

root@router:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=7.99 ms

root@router:~# wget -qO- ipinfo.io/ip
2.44.3.xxx

root@router:~# ip route del default

Si noti il cambio del TTL e dell'indirizzo pubblico tra il primo ed il secondo blocco di test.

Creare le tabelle di routing

Occorre ora creare due nuove tabelle di routing, ciascuna specifica per una connessione ad Internet. Il nome viene definito nelle due righe evidenziate in /etc/iproute2/rt_tables; i numeri indicati non sono rilevanti:

root@router:~# cat /etc/iproute2/rt_tables
255 local
254 main
253 default
0 unspec
10 ISPA
111 ISPB

Occorre quindi popolare le tabelle di routing e specificare quando devono essere utilizzate. Durante i test potrebbe essere opportuno inserire i comandi in uno script, oppure fare il Copia&Incolla:

root@router:~# nano local-source-routing.sh
#!/bin/sh

ip route flush table ISPB
ip rule flush table ISPB
ip rule add from 192.168.111.100 table ISPB
ip route add 192.168.111.0/24 dev ens192 table ISPB
ip route add default via 192.168.111.254 table ISPB

ip route flush table ISPA
ip rule flush table ISPA
ip rule add from 192.168.10.100 table ISPA
ip route add 192.168.10.0/24 dev ens256 table ISPA
ip route add default via 192.168.10.1 table ISPA

I due blocchi sono simili:

Creiamo le tabelle di routing, eseguendo lo script:

root@router~# ./local-source-routing.sh

Abbiamo ora tre tabelle di routing principali, le due che abbiamo creato e la main, generata automaticamente dal sistema operativo:

root@router:~# ip route show table ISPA
default via 192.168.10.1 dev ens256
192.168.10.0/24 dev ens256 scope link

root@router:~# ip route show table ISPB
default via 192.168.111.254 dev ens192
192.168.111.0/24 dev ens192 scope link

root@router:~# ip route show table main
172.16.0.0/16 dev ens224 proto kernel scope link src 172.16.0.1
192.168.10.0/24 dev ens256 proto kernel scope link src 192.168.10.100
192.168.111.0/24 dev ens192 proto kernel scope link src 192.168.111.100

Per conoscere le regole che permettono di scegliere quale tabella utilizzare:

vv@router~# ip rule
0: from all lookup local
32764: from 192.168.10.100 lookup ISPA
32765: from 192.168.111.100 lookup ISPB
32766: from all lookup main
32767: from all lookup default

Più il numero è basso, maggiore è la priorità: in pratica le regole vanno lette nell'ordine con cui sono presentate dal comando ip rule.

Verifichiamo il funzionamento, specificando quale IP sorgente deve essere utilizzato e, di conseguenza, quale tabella di routing e quale connessione. Il primo test utilizza un semplice ping:

pivv@router:~$ ping 8.8.8.8 -I 192.168.111.100
PING 8.8.8.8 (8.8.8.8) from 192.168.111.100 : 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=9.44 ms

vv@router:~$ ping 8.8.8.8 -I 192.168.10.100
PING 8.8.8.8 (8.8.8.8) from 192.168.10.100 : 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=7.60 ms

L'uso di traceroute (nota 6) permette di ricavare informazioni sul percorso

vv@router:~$ traceroute -s 192.168.10.100 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets
1 192.168.10.1 (192.168.10.1) 1.444 ms * 1.351 ms
2 2.44.0.1 (2.44.0.1) 10.169 ms 10.187 ms 10.149 ms
3 83.224.46.174 (83.224.46.174) 10.597 ms 14.003 ms 14.062 ms
4 83.224.46.173 (83.224.46.173) 14.050 ms 14.003 ms 14.095 ms
5 185.210.48.3 (185.210.48.3) 13.741 ms 83.224.46.237 (83.224.46.237) 13.929 ms 185.210.48.3 (185.210.48.3) 14.096 ms
6 * 74.125.245.225 (74.125.245.225) 13.567 ms *
7 8.8.8.8 (8.8.8.8) 8.397 ms 10.698 ms 10.467 ms

vv@router:~$ traceroute -s 192.168.111.100 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets
1 192.168.111.254 (192.168.111.254) 8.077 ms 8.443 ms 8.108 ms
2 10.4.0.254 (10.4.0.254) 8.419 ms 8.362 ms 8.262 ms
3 * * *
4 * 172.17.17.80 (172.17.17.80) 19.094 ms 21.164 ms
5 172.17.22.72 (172.17.22.72) 17.189 ms 172.17.16.18 (172.17.16.18) 17.202 ms 172.17.22.82 (172.17.22.82) 17.410 ms
6 172.19.177.24 (172.19.177.24) 17.594 ms 12.211 ms 12.578 ms
7 195.22.205.98 (195.22.205.98) 12.754 ms 13.511 ms 195.22.205.116 (195.22.205.116) 16.222 ms
8 72.14.219.236 (72.14.219.236) 19.999 ms 72.14.221.64 (72.14.221.64) 12.125 ms 72.14.216.154 (72.14.216.154) 14.726 ms
9 108.170.245.65 (108.170.245.65) 14.675 ms * 108.170.245.81 (108.170.245.81) 13.762 ms
10 8.8.8.8 (8.8.8.8) 11.455 ms 11.640 ms 72.14.234.75 (72.14.234.75) 10.850 ms

Infine per la conferma degli indirizzi IP pubblici (nota 7):

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.10.100
2.44.3.xxx

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.111.100
82.56.121.xxx

Un'applicazione pratica

Così come è, questa doppia connessione non è particolarmente utile in quanto la maggior parte delle volte si preferisce non dover utilizzare esplicitamente l'indirizzo di binding. Un uso interessante potrebbe però essere quello di effettuare da linea di comando il download contemporaneo di due file, uno per ciascuna connessione, in pratica raddoppiando la velocità. Di seguito un esempio che utilizza contemporaneamente due linee FTTC da 55 Mbit/s, cioè 6.9 MB/s (nota 7):

vv@router:~# wget --bind-address=192.168.10.100 debian.zero.com.ar/debian-cd/10.4.0/amd64/iso-bd/debian-edu-10.4.0-amd64-BD-1.iso
[...]
debian-edu-10.4.0-amd64-BD-1.iso 78%[============================> ] 4,18G 5,96MB/s prev 4m 7s

vv@router:~# wget --bind-address=192.168.111.100 cdimage.debian.org/cdimage/weekly-builds/amd64/iso-dvd/debian-testing-amd64-DVD-1.iso
[...]
debian-testing-amd64-DVD-1.iso 9%[========> ] 348,70M 5,85MB/s prev 12m 3ss

iptraf: download con due ISP

Un'altra applicazione

Se sul router è presente un proxy, è possibile fargli utilizzare entrambe le linee. Qui è descritto come fare.

Non solo l'indirizzo sorgente

Nelle regole di una policy routing possono apparire anche altri selettori oltre al solo indirizzo sorgente. L'elenco può essere ottenuto direttamente dal manuale:

vv@router:~# ip rule help
[...]
SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]
[ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]
[ uidrange NUMBER-NUMBER ]
[ ipproto PROTOCOL ]
[ sport [ NUMBER | NUMBER-NUMBER ]
[ dport [ NUMBER | NUMBER-NUMBER ] ]

Per esempio il comando:

root@router:~# ip rule add from 192.168.10.100 ipproto ICMP table ISPA

Permette di indicare che la tabella ISPA verrà utilizzata solo dai pacchetti ICMP in uscita, in pratica il ping. Useremo questa possibilità in seguito.

Configurare il router

Il nostro scopo non è configurare un host generico per sfruttare due connessioni Internet, ma un router che possa connettere alcune macchine della LAN usando la connessione ISP-A, altre macchine usando ISP-B

Per ottenere ciò è innanzitutto necessario attivare l'inoltro dei pacchetti:

root@router:~# nano /etc/sysctl.conf

# [...]
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
# [...]

Occorre inoltre attivare il Source NAT (PAT secondo la terminologia Cisco) sulle due interfacce connesse ad internet (nota 8):

root@router:~#  iptables -t nat -A POSTROUTING -o ens192 -j SNAT --to 192.168.111.100
root@router:~#  iptables -t nat -A POSTROUTING -o ens256 -j SNAT --to 192.168.10.100

Infine serve aggiungere alle due tabelle di routing ISPA e ISPB le regole e le rotte qui sotto evidenziate:

ip route flush table ISPB
ip rule flush table ISPB
ip rule add from 192.168.111.100 table ISPB
ip rule add from 172.16.1.2 table ISPB
ip rule add from 172.16.1.4 table ISPB

ip route add 192.168.111.0/24 dev ens192 table ISPB
ip route add 172.16.0.0/16 dev ens224 table ISPB
ip route add default via 192.168.111.254 table ISPB

ip route flush table ISPA
ip rule flush table ISPA
ip rule add from 192.168.10.100 table ISPA
ip rule add from 172.16.2.0/24 table ISPA
ip route add 192.168.10.0/24 dev ens256 table ISPA
ip route add 172.16.0.0/16 dev ens224 table ISPA
ip route add default via 192.168.10.1 table ISPA

Si noti in particolare come tra le regole è possibile aggiungere come sorgente sia gli indirizzi di singoli host (primo blocco) che intere reti (secondo blocco).

Di seguito la schermata di due computer Windows 10 che mostrano IP privato ed IP pubblico, uno connesso tramite ISP-A e l'altro tramite ISP-B. Il sistema operativo installato sui computer utenti è irrilevante e non è richiesta alcuna configurazione particolare (nota 9)

Connesso tramite ISP A

Connesso tramite ISP B

Avvio automatico

Alcune delle impostazioni che abbiamo fatto sono persistenti e quindi non necessitano di ulteriori interventi:

NAT

Le impostazioni create con iptables possono essere rese persistenti digitando dopo ogni modifica:

root@router~# dpkg-reconfigure iptables-persistent

Tale comando rende persistenti anche tutte le (eventuali) regole del firewall gestite attraverso iptables.

Tabelle di routing

Per rendere permanenti le tabelle di routing (regole e rotte) è possibile modificare /etc/network/interfaces, usando la parola chiave post-up e gli stessi comandi sopra descritti. In alternativa è possibile utilizzare uno script eseguito ad intervalli regolari, come quello mostrato per gestire il fail-over. Di seguito un esempio completo, coerente con la tabella già descritta:

root@router~# cat /etc/network/interfaces
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback

# LAN
auto ens224
iface ens224 inet static
address 172.16.0.1/16

# ISP-B
auto ens192
iface ens192 inet static
address 192.168.111.100/24
post-up ip rule add from 192.168.111.100 table ISPB
post-up ip rule add from 172.16.1.2 table ISPB
post-up ip rule add from 172.16.1.4 table ISPB
post-up ip route add 192.168.111.0/24 dev ens192 table ISPB
post-up ip route add 172.16.0.0/16 dev ens224 table ISPB
post-up ip route add default via 192.168.111.254 table ISPB

# ISP-A
auto ens256
iface ens256 inet static
address 192.168.10.100/24
post-up ip rule add from 192.168.10.100 table ISPA
post-up ip rule add from 172.16.2.0/24 table ISPA
post-up ip route add 192.168.10.0/24 dev ens256 table ISPA
post-up ip route add 172.16.0.0/16 dev ens224 table ISPA
post-up ip route add default via 192.168.10.1 table ISPA

Non penso servano commenti.

Unica avvertenza: i vari comandi devono utilizzare solo reti ed indirizzi già attivati. In particolare l'interfaccia LAN deve essere attivata per prima in quanto alcuni comandi successivi utilizzano gli indirizzi della rete 172.16.0.0/16.

Fail-over

Ipotizziamo il seguente scenario: una delle due connessioni ad Internet si interrompe.

L'idea è utilizzare solo l'altra connessione per tutto il traffico fino a quando entrambe le linee linea tornano operative (nota 11).

Per fare ciò utilizzeremo uno script che:

Lo script potrebbe essere il seguente, da adattare alla proprio rete e qui relativo allo stesso scenario descritto in questa pagina (nota 13):

root@router:~# cat /etc/script/fail-over.sh

#!/bin/bash
wanA=192.168.10.100
gwA=192.168.10.1
nameA=ISP-A

wanB=192.168.111.100
gwB=192.168.111.254
nameB=ISP-B

test_host=8.8.8.8 # Deve essere un serve affidabile
ping_c=2 # Numero di test da effettuare. Con linea molto carica, un raramente un ping potrebbe fallire

cur_date=`date "+%Y %b %d %H:%M"`

# Il ping ha successo per le due connessioni?
testA=`ping -I $wanA -c $ping_c $test_host | grep "64 bytes" | wc -l`
testB=`ping -I $wanB -c $ping_c $test_host | grep "64 bytes" | wc -l`

# Esistono regole specifiche per pacchetti ICMP, indice di un precedente malfunzionamento
icmpA=`ip rule show table ISPA | grep icmp | wc -l`
icmpB=`ip rule show table ISPB | grep icmp | wc -l`

# Il test attuale ha successo, ma un test precedente ha disattivato una connessione. Riattivo ISPA
if [ $testA -gt 0 ] && [ $icmpA -eq 1 ]; then
 echo "$cur_date $nameA links come back"
 ip rule flush table ISPA
 ip rule add from $wanA table ISPA
fi

# Il test fallisce; passo alla connessione predefinita e disattivo la tabella ISPA (tranne che per gli ICMP)
if [ $testA -eq 0 ]; then
 echo "$cur_date $nameA links is Down"
 ip route del default
 ip route add default via $gwB
 ip rule flush table ISPA
 ip rule add from $wanA ipproto ICMP table ISPA
fi

if [ $testB -gt 0 ] && [ $icmpB -eq 1 ]; then
 echo "$cur_date $nameB links come back"
 ip rule flush table ISPB
 ip rule add from $wanB table ISPB
fi

if [ $testB -eq 0 ]; then
 echo "$cur_date $nameB links is Down"
 ip route del default
 ip route add default via $gwA
 ip rule flush table ISPB
 ip rule add from $wanB ipproto ICMP table ISPB
fi

Questo script deve essere eseguito con una certa frequenza; per esempio è possibile utilizzare cron, avviandolo una volta il minuto:

root@router:~# cat /etc/crontab
[...]
* * * * * root /etc/script/fail-over.sh
#

Di seguito l'output generato leggendo l'IP pubblico mentre, manualmente, è stato staccato il cavo fisico utilizzato dai due provider, ad intervalli di qualche minuto (nota 12); si notino i due indirizzi pubblici diversi oppure uguali, in funzione del numero di linee funzionanti.

Entrambe le linee funzionanti (condizione normale)

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.111.100
82.56.121.xxx

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.10.100
2.44.3.xxx

ISP-B smette di funzionare

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.111.100
2.44.3.xxx

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.10.100
2.44.3.xxx

ISP-B torna a funzionare

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.111.100
82.56.121.xxx

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.10.100
2.44.3.xxx

ISP-A smette di funzionare

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.111.100
82.56.121.xxx

vv@router:~# wget -qO- ipinfo.io/ip --bind-address=192.168.10.100
82.56.121.xxx

Un problema: ogni volta che viene modificata una tabella di routing si interrompono le connessioni che la utilizzano, causando, a seconda dell'applicazione, interruzioni di qualche secondo o, addirittura, il blocco dell'applicazione. Questo metodo funziona quindi solo nel caso in cui entrambe le reti possono garantire il funzionamento continuo di almeno qualche ora consecutiva.

Note

  1. L'idea è applicabile anche a più di due connessioni. Per esempio nella mia scuola la sto utilizzando con 5 FTTC, ottenendo qualche centinaio di Mbps di banda in download e, soprattutto, ben 80 Mbps un upload, indispensabile per la DaD
  2. In realtà non è una somma aritmetica in quanto il bilanciamento è tutt'altro che perfetto
  3. Ma un giorno arriverà IPv6, certo che arriverà...
  4. Esistono soluzioni molto interessanti che si appoggiano a server esterni con collegamenti veloci, garantendo il bilanciamento a livello 2 (VPN) o anche a livello 7 (parents proxy), Prossimamente?
  5. In realtà converrà definire un gateway predefinito, ma meglio fare questo al termine della configurazione, per non interferire con i test intermedi
  6. Purtroppo mtr non è utilizzabile causa un bug documentato se si usa l'opzione --address
  7. Per usare questo comando serve l'uso di un server DNS, non raggiungibile con la configurazione qui descritta. Per risolvere velocemente è possibile per esempio aggiungere un rotta predefinita alla tabella main:
    root@router:~# ip route add default via 192.168.10.1 dev ens256
  8. Questa porcata non è necessaria se si attiva una rotta statica sui due modem/router!
  9. Unica avvertenza: è necessario che le regole e l'assegnazione degli indirizzi IP sia abbastanza equilibrata tra le due connessioni. Per esempio le regole mostrate, in relazione al piano di indirizzamento mostrato in figura, garantisce che ciascuna delle due connessioni sia utilizzata da due PC
  10. I sacri testi dicono che dovrebbe utilizzare anche il TOS, ma in genere non viene fatto dagli ISP, perlomeno non ufficialmente
  11. Mediamente una linea Internet rimane non operativa qualche volta nel corso dell'anno, per qualche ora. Spesso la causa è un malfunzionamento dell'intera rete del provider: per questo è bene averne due diversi e non una doppia connessione con lo stesso provider; altre volte è la linea fisica o il modem; altre volte ancora è la distruzione fisica della centralina (a me è capitato tre volte, due per un incidente stradale che ha distrutto la colonnina stradale, una per un fulmine): per questo è bene avere due tecnologie diverse di connessione, per esempio FTTC più FWA.
  12. Questo script funziona correttamente solo nel caso in cui le interruzioni sono assolutamente saltuarie. In particolari tutte le connessioni già aperte tipicamente subiscono un arresto, risolvibile per esempio con un reload della pagina web. Ovviamente durante un download pesante la cosa è tutt'altro che indolore
  13. Per mantenere compatto l'esempio non ho inserito le rotte relative alla LAN


Data di creazione di questa pagina: luglio 2020
Ultima modifica: 12 gennaio 2o22


Pagina principaleAccessibilitàNote legaliPosta elettronicaXHTML 1.0 StrictCSS 3

Vai in cima