Il problema, prima della teoria: una macchina che non parla
Hai un server appena installato. Non risponde a nulla. Da dentro, nemmeno
ping 8.8.8.8 funziona. Non hai l'interfaccia grafica, non hai documentazione, e hai una
sola sessione aperta. Qual è il primo comando che esegui, e che informazione ne
vuoi ottenere?
Fermati e scrivi la tua risposta, con la ragione. Non c'è una sola risposta valida, ma ci sono risposte che restringono il problema e risposte che lo lasciano dov'è. Questa lezione parte dal problema e non dalla spiegazione, perché la configurazione di rete si impara diagnosticando: un elenco di comandi letto a freddo non si ricorda.
La cosa che rende questo problema affrontabile è che Linux ti permette di costruirne una copia in laboratorio, senza toccare la tua rete e senza permessi di amministratore. Lo farai in questa lezione, e il laboratorio ti servirà anche nelle due successive.
Che cosa devi già sapere, e come lo verifichi
Prima di proseguire, controlla tre cose su te stesso. Sai leggere l'output di un comando e dire in
quale colonna sta l'informazione che cerchi? Sai che un comando lascia un codice di uscita, e che
zero significa successo? Sai dire che cosa fa LC_ALL=C davanti a un comando? Se una
delle tre esita, le lezioni sulla shell contengono la risposta.
La terza ti servirà subito, e per una ragione concreta che vedrai fra poco: alcuni strumenti di rete traducono i messaggi di errore nella lingua del sistema e altri no, quindi lo stesso guasto compare in due lingue nella stessa sessione.
Un laboratorio di rete che non puoi rompere
La difficoltà di imparare la rete è che gli esperimenti si fanno sulla rete che stai usando. Il
comando unshare risolve il problema: crea un namespace, cioè una vista isolata
di una risorsa del sistema. Con -n isola la rete, e con -r ti dà i
privilegi di amministratore dentro quell'isolamento, senza averli fuori.
$ unshare -rn bash
# ip -br link
lo DOWN 00:00:00:00:00:00 <LOOPBACK>
# ip route
Dentro c'è una rete vuota: una sola interfaccia di anello, spenta, e nessuna rotta. Il secondo comando non stampa niente, e quel niente è informazione: non esiste una strada per uscire. È esattamente lo stato del server che non parla, riprodotto in un comando e senza rischi.
Da qui si costruisce. Una interfaccia va accesa prima di avere un indirizzo utile, e un indirizzo da solo non basta: serve anche una rotta che dica dove mandare i pacchetti. I tre concetti, interfaccia, indirizzo e rotta, sono separati in Linux, e si configurano con tre sottocomandi diversi dello stesso programma.
# ip link add veth-a type veth peer name veth-b
# ip addr add 10.42.0.1/24 dev veth-a
# ip addr add 10.42.0.2/24 dev veth-b
# ip link set veth-a up
# ip link set veth-b up
# ip -br addr show veth-a
veth-a@veth-b UP 10.42.0.1/24 fe80::f9:a8ff:fe93:94cf/64
# ip route
10.42.0.0/24 dev veth-a proto kernel scope link src 10.42.0.1
10.42.0.0/24 dev veth-b proto kernel scope link src 10.42.0.2
Una coppia veth è un cavo virtuale con due capi: quello che entra da uno esce
dall'altro. La dicitura proto kernel nelle rotte dice chi le ha create: non tu, il
kernel, come conseguenza dell'indirizzo che hai assegnato. È il primo pezzo di una diagnosi, perché
distingue le rotte che derivano dalla configurazione da quelle aggiunte a mano o arrivate da un
server DHCP.
Il laboratorio fatto bene: due capi in due stanze diverse
Con i due capi del cavo nello stesso namespace, il laboratorio ha un difetto che smonteremo fra poco: entrambi gli indirizzi sono locali, quindi il traffico fra loro non attraversa il cavo. Per avere due macchine vere servono due namespace, e si ottengono così.
# unshare -n sleep 60 &
# P=$!
# ip link set veth-b netns "$P"
# ip addr add 10.42.0.1/24 dev veth-a && ip link set veth-a up
# nsenter -t "$P" -n ip addr add 10.42.0.2/24 dev veth-b
# nsenter -t "$P" -n ip link set veth-b up
# nsenter -t "$P" -n ip -br addr show veth-b
veth-b@if3 UP 10.42.0.2/24 fe80::20a0:31ff:fee8:ce97/64
Il processo sleep avviato con unshare -n tiene in vita un secondo namespace
di rete, e il suo identificativo di processo è il modo di raggiungerlo:
ip link set veth-b netns sposta il capo del cavo dentro quel namespace, e
nsenter esegue comandi lì. Da questo momento hai due stanze collegate da un cavo, e il
ping misura il cavo:
# ping -c2 -W2 10.42.0.2
2 packets transmitted, 2 received, 0% packet loss, time 1026ms
rtt min/avg/max/mdev = 0.039/0.045/0.051/0.006 ms
Chi configura la rete quando non la configuri tu
Sui sistemi reali non scrivi comandi ip a mano, perché le modifiche fatte così
non sopravvivono al riavvio. Le scrive un gestore, e sapere quale gestore è attivo è
la prima domanda da fare su una macchina che non conosci, perché cambia il file da modificare.
Su questa macchina il gestore attivo è NetworkManager, e lo ho verificato il 2026-10-30 con
systemctl is-active NetworkManager systemd-networkd, che risponde active
per il primo e inactive per il secondo. Si configura con nmcli. Sui server
Ubuntu è più frequente trovare netplan, che è un livello di descrizione in formato YAML
sotto /etc/netplan/: scrivi lì, e netplan genera la configurazione per il
gestore vero. Sulle distribuzioni che usano systemd-networkd i file stanno sotto
/etc/systemd/network/.
La regola che vale su tutte: modificare con ip serve a provare, modificare il
file del gestore serve a configurare. Chi inverte le due cose ottiene una macchina che
funziona fino al riavvio, oppure una configurazione corretta nel file che un gestore sovrascrive.
Il confronto: ifconfig non è solo vecchio, nasconde
Troverai ovunque la frase «ifconfig è deprecato, usa ip». Detta così è una
questione di gusto, e chi la sente alza le spalle. Il motivo vero è misurabile, e si vede su una
interfaccia che ha più di un indirizzo IPv4, cosa del tutto normale su un server che ospita più
servizi.
# ip addr add 10.42.0.9/24 dev veth-a
# ip -4 addr show veth-a
3: veth-a@veth-b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
inet 10.42.0.1/24 scope global veth-a
valid_lft forever preferred_lft forever
inet 10.42.0.9/24 scope global secondary veth-a
valid_lft forever preferred_lft forever
# ifconfig veth-a
veth-a: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.42.0.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::8c1b:88ff:fefc:e712 prefixlen 64 scopeid 0x20<link>
ether 8e:1b:88:fc:e7:12 txqueuelen 1000 (Ethernet)
L'interfaccia ha due indirizzi IPv4. ip li mostra entrambi, e marca il secondo come
secondary. ifconfig mostra solo il primo, senza dire che
ce n'è un altro. Chi diagnostica un problema con ifconfig su una macchina configurata
così sta guardando un'informazione incompleta e non ha modo di accorgersene.
Lo stesso vale per la coppia route e ip route, e per netstat e
ss: i primi di ciascuna coppia appartengono al pacchetto net-tools, che su
questa macchina è installato nella versione 2.10-2ubuntu1 e quindi funziona, ma parla
con il kernel attraverso un'interfaccia più vecchia che non espone tutto ciò che il kernel oggi sa.
L'errore di classificazione plausibile è pensare che le due famiglie siano sinonimi con una sintassi
diversa: hanno una sintassi diversa e un campo visivo diverso.
Esempio svolto: la diagnosi in tre domande
Torna al problema iniziale, il server che non parla. Il ragionamento che uso, e che ti consiglio di usare perché restringe il campo a ogni passo, è una sequenza di tre domande in ordine, ciascuna con un comando che la decide.
Prima domanda: l'interfaccia esiste ed è accesa? Il comando è
ip -br link. Se l'interfaccia non compare affatto, il problema è il driver o
l'hardware, e non è un problema di configurazione: cercare nei file di rete sarebbe tempo perso. Se
compare con DOWN, non è accesa. Se compare con NO-CARRIER, è accesa ma
dall'altra parte non c'è nessuno.
Seconda domanda: ha un indirizzo? Il comando è ip -br addr. Un'
interfaccia accesa senza indirizzo IPv4 su una rete che usa DHCP significa che il server DHCP non ha
risposto, e questo sposta l'indagine fuori dalla macchina.
Terza domanda: esiste una strada verso la destinazione? Qui la maggior parte delle
persone esegue ip route e legge la tabella. Il comando migliore è un altro, e vale la
pena impararlo adesso perché risponde invece di darti dati da interpretare:
# ip route get 10.42.0.2
10.42.0.2 dev veth-a src 10.42.0.1 uid 0
cache
# ip route get 10.99.99.99
RTNETLINK answers: Network is unreachable
# echo $?
2
ip route get chiede al kernel quale decisione prenderebbe per quella destinazione
precisa, e il kernel risponde con l'interfaccia, l'indirizzo di partenza, e il prossimo salto se c'è.
Nel secondo caso dice che non c'è strada, e lascia codice di uscita 2, quindi la
risposta è usabile anche dentro uno script.
La decisione che prendo: ip route get prima di ip route. L'alternativa,
leggere la tabella completa e ragionare sulle maschere a mente, l'ho scartata perché su una macchina
reale la tabella è lunga e la regola della corrispondenza più specifica si sbaglia facilmente. Sulla
mia macchina ho contato quindici rotte con ip route | wc -l, in buona parte create da
contenitori: la lettura a vista di quella tabella è un invito all'errore.
L'errore che avrei potuto commettere è dare per buono il messaggio di ping, che su
questo sistema risponde ping: connect: La rete non è raggiungibile, in italiano, mentre
ip risponde in inglese. Due lingue nella stessa sessione, e se stai cercando il
messaggio in rete per capirlo, la traduzione è un ostacolo. Con LC_ALL=C davanti al
comando ottieni il testo originale, ed è quello che va cercato e che va messo in un rapporto.
Il caso negativo: un ping che non prova quello che credi
Questo è l'errore che ho commesso io costruendo il laboratorio di questa lezione, e lo lascio qui
perché è più istruttivo della versione corretta. Nel primo laboratorio, con i due capi del cavo nello
stesso namespace, avevo eseguito ping 10.42.0.2 e avevo concluso che il cavo
funzionava. Poi ho rifatto la prova senza accendere l'interfaccia di anello, e il ping
ha smesso di funzionare. Prova a spiegare perché, prima di leggere il seguito.
# ip -br link show lo
lo DOWN 00:00:00:00:00:00 <LOOPBACK>
# ping -c1 -W1 10.42.0.2
1 packets transmitted, 0 received, 100% packet loss, time 0ms
# ip route get 10.42.0.2
local 10.42.0.2 dev lo src 10.42.0.2 uid 0
cache <local>
# ip link set lo up
# ping -c1 -W1 10.42.0.2
1 packets transmitted, 1 received, 0% packet loss, time 0ms
La riga che spiega tutto è la risposta di ip route get: local 10.42.0.2 dev
lo. L'indirizzo 10.42.0.2 è assegnato a un'interfaccia di questa
stessa macchina, quindi il kernel non lo manda su nessun cavo: lo consegna internamente
attraverso l'interfaccia di anello. Il ping non stava provando il cavo: stava provando
l'anello, e con l'anello spento non c'era strada.
L'errore è plausibile perché il risultato era quello atteso. Un ping che risponde è la
prova che tanti accettano senza chiedersi da dove è passato il pacchetto, e in questo caso dava la
risposta giusta per il motivo sbagliato. Nel laboratorio corretto, con i due capi in due namespace
diversi, lo stesso comando risponde 10.42.0.2 dev veth-a src 10.42.0.1, senza la parola
local: ora il pacchetto attraversa il cavo per davvero.
La lezione generale, che vale fuori dal laboratorio: un indirizzo locale non prova la connettività.
Se vuoi collaudare una strada di rete, la destinazione deve stare su un'altra macchina, e il modo di
accertarsi che il pacchetto passi da dove credi non è il ping ma
ip route get.
Esercizio 1, guidato: accendi un capo alla volta
Costruisci il laboratorio a due namespace e fermati prima di accendere le interfacce. Poi esegui
ip route e ip -br link show veth-a in tre momenti: con entrambi i capi
spenti, con solo il tuo acceso, e con entrambi accesi. Scrivi prima di ogni passo che cosa ti
aspetti. Criterio: sai dire in quale dei tre momenti la rotta compare, in quale compare con una
marca che segnala un problema, e quale delle tre domande della diagnosi lo avrebbe individuato.
Esercizio 2, autonomo: il gestore della tua macchina
Sulla tua macchina, scopri quale gestore di rete è attivo e dove tiene la configurazione. Poi, dentro il laboratorio isolato, aggiungi un secondo indirizzo a un'interfaccia e verifica con due comandi diversi quanti indirizzi vede ciascuno. Criterio di successo: sai nominare il gestore attivo sulla tua macchina, il percorso dei suoi file, e hai riprodotto con i tuoi comandi la differenza di campo visivo fra i due strumenti.
Soluzioni commentate
Esercizio 1. Con entrambi i capi spenti, ip route non stampa niente: il
kernel non installa la rotta per una rete direttamente collegata se l'interfaccia non è attiva.
Questo è il punto che sorprende, perché l'indirizzo c'era già. L'indirizzo da solo non crea una
strada.
Con solo il tuo capo acceso, lo stato misurato è LOWERLAYERDOWN con le marche
NO-CARRIER e M-DOWN, e la rotta compare ma con la parola
linkdown alla fine della riga. È la situazione del cavo staccato dall'altra parte, e
linkdown è la marca che te lo dice senza bisogno di indovinare. Con entrambi accesi lo
stato è UP con LOWER_UP, e la parola linkdown scompare.
La domanda della diagnosi che individua il problema è la prima, non la terza, ed è
il motivo per cui l'ordine delle tre domande non è casuale. Perché le altre spiegazioni non reggono:
«serve una rotta di default» è sbagliato, perché la destinazione è nella stessa rete e la rotta di
default serve per uscirne; «serve un firewall permissivo» è sbagliato, perché nel namespace appena
creato non c'è nessuna regola; «serve il ping con privilegi» è sbagliato, perché dentro
un namespace creato con -r i privilegi li hai.
Esercizio 2. Quello che devi avere in mano sono tre dati concreti: il nome del
gestore, ottenuto con systemctl is-active provato sui candidati, il percorso dei file, e
il conteggio degli indirizzi visti dai due strumenti. Sul mio sistema i tre dati sono
NetworkManager, i file YAML sotto /etc/netplan/ generati per quelle connessioni, e due
indirizzi visti da ip contro uno visto da ifconfig.
Una cosa su cui vale la pena fermarsi: la presenza di file sotto /etc/netplan/ su una
macchina gestita da NetworkManager sorprende, e la spiegazione è che i due livelli convivono.
netplan descrive, il gestore esegue. Se modifichi il file sbagliato dei due, la modifica
viene ignorata o sovrascritta, e il sintomo è una configurazione che non prende. La domanda da farsi
prima di scrivere è quale dei due livelli è la fonte su questa macchina, e si risponde guardando chi
ha generato i file: i nomi che cominciano per 90-NM- dichiarano nel nome stesso di
essere stati generati, non scritti a mano.
Che cosa adesso sai fare
Sai costruire una rete isolata con un comando, senza privilegi di amministratore e senza toccare la tua rete, e sai costruirla con due namespace perché il traffico attraversi davvero il cavo. Sai distinguere i tre livelli della configurazione, interfaccia, indirizzo e rotta, e sai che una rotta per la rete collegata non esiste finché l'interfaccia non è attiva. Sai condurre una diagnosi in tre domande, ciascuna con il comando che la decide.
Sai che ip route get risponde dove ip route dà dati da interpretare, e sai
che un ping riuscito verso un indirizzo locale non prova nessuna connettività. La
verifica concreta: esegui ip route get 1.1.1.1 sulla tua macchina e leggi da quale
interfaccia esce il traffico verso l'esterno, e con quale indirizzo di partenza.