Si attiva il tunnel, si esegue il ping di fe80::1 con il tunnel come interfaccia sorgente e non torna nulla. Non è una regola del firewall dimenticata. Un indirizzo link-local ha significato solo sull’unico link in cui è stato formato, e una VPN instradata non è quel link. Il fe80::1 del router remoto vive sul segmento LAN remoto; voi siete a un hop di distanza su un segmento diverso, e IPv6 non ha alcun meccanismo grazie al quale un router possa inoltrare pacchetti link-local.

Il prefisso fe80 descrive un ambito, non un intervallo

Il link-local unicast è fe80::/10, e la proprietà che lo definisce non è il numero ma l’ambito. L’indirizzo è valido solo su un link, e ogni interfaccia ne forma uno dal proprio identificatore, per questo due macchine non correlate su due reti non correlate possono entrambe rispondere a fe80::1 senza il minimo conflitto. Il prefisso in pratica dice “su un certo link”; il link stesso viene dal contesto.

Su un host quel contesto è la zona, o scope ID, che si aggiunge dopo il segno di percentuale: fe80::1%eth0 su Linux, fe80::1%3 o fe80::1%12 su Windows e negli strumenti che espongono gli indici delle interfacce. Il kernel risolve l’indirizzo insieme all’indice dell’interfaccia e poi invia. Togliete la zona e la destinazione è ambigua, quindi lo stack non ha modo di scegliere un segmento.

Perché un tunnel instradato non può consegnarlo

Una VPN instradata è un’interfaccia punto-punto che porta i propri indirizzi, di solito un /64 di spazio IPv6 unique-local o privato dentro il tunnel. La LAN remota è un link separato, raggiunto dall’endpoint del tunnel che instrada e inoltra per voi. Sono due segmenti distinti, quindi un indirizzo di uno non è una destinazione valida sull’altro.

La regola di inoltro è esplicita nello standard, non una stranezza del client: i router non inoltrano pacchetti con sorgente o destinazione link-local. È per questo che il pacchetto muore anche quando la tabella di routing sembra corretta. Aggiungete una rotta per fe80::/64 che punta al tunnel e uno stack conforme o la rifiuta o l’accetta e scarta ogni pacchetto corrispondente: l’ambito della destinazione dice “solo questo link”, e l’interfaccia uscente non è quel link.

La scoperta dei vicini rafforza lo stesso confine. Gli host risolvono i next hop link-local con una sollecitazione di vicinato inviata al multicast con ambito di link e limite di hop 255, così i router possono rifiutare qualsiasi cosa venga inoltrata. Farlo funzionare attraverso un tunnel significherebbe fare da proxy per la scoperta di un segmento remoto, cioè un bridge Layer 2 con un cappello diverso.

C’è un secondo guasto più silenzioso, e coglie chi prova comunque a forzare la rotta: la zona. Qualunque zona digitiate localmente nomina l’interfaccia del tunnel, mentre l’indirizzo che volete è legato all’interfaccia della LAN remota. Le due non coincidono mai, quindi il pacchetto non può essere indirizzato sull’interfaccia giusta all’estremità remota.

Come appare il guasto nella pratica

L’output degli strumenti per un errore di ambito si legge come un errore di routing o di firewall, ed è esattamente per questo che le persone passano un pomeriggio nello strato sbagliato.

What you runWhat you seeWhat it means
ping fe80::1 with no zoneInvalid argument on Linux, or a transmit failure on WindowsThe stack cannot pick an interface for an ambiguous destination
ping -6 -I tun0 fe80::1Destination unreachable, or a silent timeoutThe address is not on the tunnel link, so there is nothing on-link to answer
ssh user@fe80::1 after the tunnel is upHangs, then no route to hostThe SYN never left the machine in a deliverable form; the server logs nothing
ip -6 neigh show dev tun0Empty tableNo neighbor entry can exist for a link-local host that is not on this link
fe80::/64 inside AllowedIPs or route-ipv6Config accepted, traffic still droppedThe tunnel carried the packet; the receiving stack refused to forward it

La terza riga fa perdere più tempo, perché un client ssh senza output sembra un problema del servizio in ascolto. Se avete un modo per entrare, controllate il lato remoto e verificate se sshd è legato a tutti gli indirizzi o solo a uno.

Usate un indirizzo pensato per essere instradato

Per tutto ciò che dovete raggiungere attraverso il tunnel, usate l’indirizzo che è stato dato al servizio proprio per questo: un indirizzo unicast globale, oppure un indirizzo unique-local da fc00::/7, l’intervallo riservato esattamente a questo caso interno. Verificate che il listener lo associ davvero, e controllate il percorso di ritorno e ogni regola del firewall dell’host.

Guardate l’host remoto invece di indovinare. ip -6 addr show elenca gli indirizzi che esistono, e ss -6 -ltn elenca ciò su cui il daemon è in ascolto. Un servizio legato solo a fe80:: è raggiungibile solo dal proprio segmento, che di solito è un valore predefinito che nessuno ha scelto apposta e può essere cambiato con l’accordo dell’amministratore.

Quando il servizio non può davvero essere riassociato, spostate il bridge verso l’alto, al livello applicativo invece che a quello di rete. Un relay sull’host remoto che ascolta su un indirizzo instradabile e si connette a quello link-local risolve il problema senza estendere alcun segmento:

socat TCP6-LISTEN:8080,bind=[fd00:1:2::10] TCP6:[fe80::1%eth0]:80

Qui la zona sta dal lato giusto, sull’host che ha davvero eth0 su quel link. Il client parla con fd00:1:2::10, che è instradato normalmente, e il relay compie il salto link-local in locale. Lo stesso trucco funziona per l’interfaccia web di un appliance o per un vecchio BMC che annuncia solo un indirizzo link-local.

Quando il bridging è davvero la risposta, e cosa controllare

Alcune installazioni hanno davvero bisogno di un segmento condiviso: un dispositivo che parla solo con scoperta link-local, un appliance che dipende dal multicast, un laboratorio che riproduce una LAN piatta. È un problema di Layer 2 e richiede una risposta di Layer 2, come un dispositivo tap collegato in bridge all’interfaccia remota o un prodotto client con una modalità bridge esplicita. Costa traffico broadcast e multicast che attraversa il tunnel, più la scoperta dei vicini che deve funzionare da un capo all’altro.

Non avvicinatevi a questo aggiungendo fe80::/64 a un profilo instradato. Produce lo stato peggiore: la configurazione sembra finita, la tabella di routing mostra una voce, e ogni pacchetto viene comunque scartato da qualche parte tra due stack che si comportano entrambi correttamente.

Verificate con l’interfaccia invece che con il ping. ip -6 addr show tun0 vi dice se il link del tunnel ha formato un proprio indirizzo link-local, e ip -6 route get fe80::1%tun0 vi dice cosa farebbe davvero il kernel con una destinazione con ambito. Quando il kernel dice che la destinazione non è raggiungibile su quell’interfaccia, nessuna impostazione del tunnel lo cambierà, ed è ora di smettere di modificare il profilo.

Una breve sequenza che risolve la questione:

  • Leggete l’indirizzo sull’host remoto e notate se è fe80:: o un prefisso instradabile.
  • Se è fe80::, riassociate il servizio, fate relay su quell’host o mettete in bridge il segmento; se è instradabile, correggete la rotta, il firewall o l’indirizzo di bind.
  • Ricontrollate la zona ogni volta che ripetete il test, perché un comando copiato di solito conserva l’interfaccia sbagliata.

Fonti: RFC 4291, architettura di indirizzamento IPv6.

Articoli correlati