Skip to main content

Module 3 · Networking

Network Interfaces

  • Lesson 7/15
  • 35 min
  • Difficulty: Intermediate

This lesson is not available in English yet: you are reading it in Italian.

Objective

Diagnosticare una macchina che non comunica in rete con tre domande in ordine, ciascuna decisa da un comando, e costruire una rete di prova isolata senza privilegi di amministratore

Prerequisites

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.