Policy routing IPv6 quando due uplink condividono fe80::1
Due uplink possono presentare lo stesso next hop fe80::1 su link diversi. Scegli una tabella per policy, gestisci le durate e verifica con una lookup di rotta.
Hai due uplink, e ip -6 route show stampa lo stesso next hop su entrambi: default via fe80::1 dev uplink0 e default via fe80::1 dev uplink1. Non è un conflitto di indirizzi duplicati, e copiare il next hop nudo fe80::1 in una tabella di policy non dice al kernel quale link intendevi. La domanda reale è più stretta di quanto suggerisca il sintomo: la seconda tabella ha bisogno di un link o di entrambi, e cosa tiene vive quelle rotte dopo un riavvio. Tre condizioni, controllate in ordine, risolvono la maggior parte.
Lo stesso fe80::1 sono due vicini diversi
Gli indirizzi link-local sono limitati a un singolo link, quindi fe80::1 non è un indirizzo stampato due volte; sono due vicini che per caso condividono un suffisso. L'indice di zona è come un host dice quale intende, quindi fe80::1%uplink0 e fe80::1%uplink1 sono destinazioni diverse per il kernel, e quel suffisso non viaggia mai verso un router remoto. RFC 4007 descrive il modello a zone che sta dietro, ed è per questo che una tabella di policy deve portare un dispositivo con ogni next hop link-local.
Un generatore di configurazioni VPN che copia un ID di zona numerico da un host a un template tratta il contesto locale come se facesse parte dell'indirizzo, e il file che scrive è sbagliato su ogni macchina i cui nomi o indici di interfaccia differiscono. Né una tabella di policy unisce due link in uno: una VPN instradata passa pacchetti tra reti, ma non trasporta l'identità link-local di un vicino Ethernet remoto attraverso il tunnel, quindi il traffico link-local resta sul proprio link.
Rimuovi il dispositivo e la rotta diventa non scrivibile. ip -6 route add default via fe80::1 table 200 non riesce a risolvere il gateway, e iproute2 lo rifiuta con Error: Nexthop has invalid gateway. Aggiungi dev uplink0 e il comando riesce, perché è il dispositivo che fissa il next hop a uno di quei due vicini. Ciò che il dispositivo non può dirti è se uplink0 è il link giusto per questa tabella, e una rotta copiata da un altro host porta con sé i nomi di interfaccia di quell'host, ed è da lì che vengono gli errori reali.
Condizione uno: un link o due nella tabella
Parti da a cosa serve il secondo uplink. Se entrambi i link portano lo stesso traffico e uno è di riserva, la tabella alternativa dovrebbe contenere entrambi i gateway con metriche distinte: stesse destinazioni, un percorso preferito, una seconda scelta ovvia. Se gli uplink esistono perché traffico diverso deve uscire attraverso provider diversi, o perché questo è un split tunnel in cui parte del traffico aggira la VPN mentre il resto resta dentro, i due link non appartengono affatto a una sola tabella. Appartengono a due tabelle, scelte da una regola, e la regola sceglie la tabella, non il tunnel: il traffico che aggira viene marcato prima di essere instradato, e il mark è ciò che lo manda alla seconda tabella.
In quel secondo caso la regola decide quando si consulta una tabella, e la rotta decide dove va il pacchetto una volta che lo si fa:
ip -6 rule add fwmark 0x2 table 201
ip -6 route add default via fe80::1 dev uplink1 metric 100 table 201
ip -6 route add 2001:db8:100::/48 dev uplink1 metric 100 table 201
ip -6 route show table 201Nessuno dei due oggetti sostituisce l'altro, e una tabella che contiene solo un default può comunque non riuscire a risolvere il gateway. Se un add viene rifiutato, o la rotta è presente ma inutilizzabile, metti il prefisso connesso di quell'interfaccia nella stessa tabella, o marca l'hop onlink quando quel prefisso è instradato altrove. Nel blocco qui sopra, 2001:db8:100::/48 sta per quel prefisso connesso su uplink1, non per una destinazione arbitraria. La regola fwmark consuma solo un mark che qualcos'altro deve impostare, da una regola nftables o iptables sul percorso di forwarding o da SO_MARK sul socket, quindi un blocco copiato senza una fonte di mark non corrisponde a nulla.
Condizione due: le metriche non rilevano un provider morto
Una metrica è una preferenza tra rotte che esistono, non un controllo di salute. Quando il carrier cade, il kernel rimuove le rotte su quel dispositivo e il secondo default prende il sopravvento. Quando il cavo resta su e il provider dietro muore, nella tabella non cambia nulla: la rotta preferita è installata, il vicino è raggiungibile al livello 2, e i pacchetti finiscono in un buco nero. Se quella distinzione conta per la tua configurazione, il failover deve venire da qualcosa che sonda il percorso e rimuove la rotta, che sia un daemon di routing o un piccolo script che cancella e reinstalla la voce.
Verifica la scelta dopo che la tabella è costruita, e verificala come la vedrà il kernel. ip -6 route get 2001:db8:100::1 stampa la rotta che verrebbe usata, incluso il dispositivo e l'indirizzo sorgente selezionato, e rispetta le regole di policy. Un indirizzo sorgente che appartiene all'uplink sbagliato è il segno abituale che una regola ha fatto match prima di quanto ti aspettassi.
Condizione tre: decidi chi possiede la persistenza
Una rotta aggiunta con ip -6 route add sopravvive fino al riavvio o finché qualcosa la cancella. Un default appreso dagli annunci del router ha una durata e può essere ritirato dal router, mentre un default aggiunto a mano non scade mai da solo. Mescolare i due in una tabella è come una vecchia rotta permanente finisce per prevalere su una appena annunciata, molto tempo dopo che l'uplink che descriveva ha smesso di esistere.
Scegli un solo proprietario per tabella. Se un network manager o un daemon di routing gestisce già l'interfaccia, descrivi la rotta di policy lì e lascia stare il kernel. Se lo scripti tu, rendi lo script idempotente e scrivi il teardown nello stesso momento, perché ti servirà durante il primo cambiamento sbagliato:
ip -6 route flush table 201
ip -6 rule del fwmark 0x2 table 201Su un router che fa forwarding per altri host, controlla net.ipv6.conf.all.forwarding prima di dare la colpa alla tabella. Con il forwarding disattivato, i pacchetti indirizzati a un'altra rete vengono scartati, per quanto corretta sia la tabella 201.
Registra il progetto così la prossima persona può verificarlo
Un inventario che memorizza solo fe80::1 butta via i campi che contano. Registra insieme destinazione, next hop, dispositivo, tabella e metrica, più da dove viene ogni rotta, se annunciata, generata dal manager o statica. Quando qualcuno più tardi chiede se il secondo uplink sta davvero portando traffico, quel record risponde senza congetture.
Prima di dichiarare la tabella finita, conferma il vicino con ip -6 neigh show dev uplink0, rileggi ip -6 rule show per incidenti di ordinamento, e prova con traffico che corrisponde alla regola invece di un ping che prende la rotta di default. Poi abbassa di proposito un uplink e osserva su quale rotta il kernel ripiega; la risposta dovrebbe corrispondere a quella che hai scritto.