Salta al contenuto

Blog ·

Pianificare i pool di indirizzi IPv4 per un VPS

Gli indirizzi sono l’unica risorsa che non si compra di martedì. Come dare forma ai pool, perché deve arbitrare il database e che succede quando uno finisce.

Di Provibr

Tutto il resto in un prodotto VPS è elastico. Serve più CPU, si compra un nodo. Serve più disco, si compra disco. Servono più indirizzi e ci si ritrova in lista d’attesa, a pagare un intermediario o a compilare un modulo di giustificazione per un registro regionale.

IPv4 è l’unica risorsa che si comporta come un bene fisico, e di solito riceve la minore attenzione progettuale, perché all’inizio si ha un /24 e 254 sembrano parecchi. Smettono di sembrare parecchi esattamente nel momento in cui ridisegnare tutto costa caro.

Si parta da ciò che un blocco dà davvero

Il numero utilizzabile non è mai la dimensione del blocco. Due indirizzi vanno alla rete e al broadcast, e un altro va al gateway. Alle dimensioni piccole non è un errore di arrotondamento, è la maggior parte del blocco:

BloccoIndirizziRete + broadcastGatewayVendibiliSpreco
/24256212531%
/273221299%
/2816211319%
/29821538%
/30421175%

Un /29 sembra fatto di otto indirizzi fino al momento in cui se ne vendono cinque. Se acquista spazio di indirizzamento in blocchi piccoli, metta lo spreco a bilancio come una voce a sé: a /29 sta buttando via tre ottavi di ciò che ha pagato.

Due limiti meritano di essere codificati in qualunque cosa costruisca. Accettiamo al massimo un /8, perché per blocchi più grandi una barra di utilizzo smette di significare qualcosa, e al minimo un /30, perché /31 e /32 non hanno alcun indirizzo utilizzabile secondo le normali regole degli host e accettarli produce soltanto un pool che non potrà mai allocare nulla. Rifiutarli in modo esplicito è meglio che accettarli e restare vuoti.

Un’altra sottigliezza: il gateway può stare dentro il suo intervallo di allocazione e viene semplicemente saltato. È voluto, perché è la disposizione più facile da capire per un cliente, ma significa che la capacità di un pool è un calcolo e mai un numero memorizzato. Qualsiasi cosa tenga un contatore degli indirizzi liberi sarà in disaccordo con la realtà il giorno in cui qualcuno sposta un gateway.

Dia forma ai pool attorno a ciò che vende, non a ciò che ha comprato

L’unità che il cliente vede è un piano. L’unità che ha comprato da un intermediario è un blocco. Le due cose non hanno quasi mai la stessa forma, e legarle insieme va storto la prima volta che compra un secondo blocco.

La disposizione che ha retto: un piano punta a un pool, e un pool è un singolo prefisso oppure un gruppo di prefissi con un nome. Aggiungere capacità a un prodotto diventa allora aggiungere un prefisso a un gruppo, cioè una riga. L’allocatore percorre il gruppo in ordine e prende il primo indirizzo che riesce a ottenere, quindi un gruppo è anche un ordine di consumo: metta davanti il blocco che vuole consumare per primo.

Sotto-blocchi, e l’errore che alloca due volte in silenzio

I clienti non usano un /24 come una rete piatta. Lo tagliano: un pezzo dietro un router, un pezzo su una VLAN diversa, un pezzo con un gateway proprio. Un piano di indirizzamento ha quindi bisogno di blocchi figli.

È qui che vive il guasto interessante, e vale la pena descriverlo con precisione perché fallisce in silenzio.

Il meccanismo che impedisce a due ordini simultanei di ottenere lo stesso indirizzo è un indice unico parziale su (prefisso, indirizzo), limitato alle righe non ancora rilasciate. Quell’indice è per prefisso. Se due righe di prefisso possono distribuire ciascuna lo stesso indirizzo, per l’indice quelli sono due indirizzi diversi, e la piattaforma consegnerà lo stesso IP a due macchine senza che nel software vada storto assolutamente nulla.

L’indice non può porvi rimedio. Deve farlo la disposizione. Perciò lo spazio di indirizzamento è una partizione rigorosa, e le regole che la rendono tale sono:

  1. Un figlio sta interamente dentro il proprio genitore ed è strettamente più piccolo.
  2. I fratelli non possono sovrapporsi.
  3. Il genitore non distribuisce nulla che cada dentro un blocco figlio. L’intero blocco viene ritagliato dalla finestra del genitore, non soltanto dall’intervallo di allocazione del figlio, perché ciò renderebbe la risposta dipendente da un intervallo che qualche amministratore sposterà la settimana prossima.
  4. L’annidamento è profondo un livello solo.

Con queste quattro regole, la domanda «a quale prefisso appartiene 192.0.2.70» ha sempre esattamente una risposta, e l’indice esistente basta.

Ciò che un figlio eredita è un insieme di decisioni a parte, e le risposte non sono uniformi. Gateway: mai (un figlio senza un gateway proprio non ha ragione di esistere). DNS: sì, se il suo campo è vuoto, perché i resolver appartengono alla rete e non al blocco. Intervallo di allocazione: solo dove l’intervallo del genitore tocca davvero il figlio, poiché l’intervallo del genitore è espresso negli indirizzi del genitore e di norma non si sovrappone affatto al figlio; ereditarlo alla cieca produce una finestra vuota.

Lasci arbitrare il database

La tentazione, quando arrivano due ordini insieme, è di bloccare qualcosa o di tenere un contatore. Entrambe le cose sono peggiori della risposta noiosa.

Entrambi gli ordini calcolano lo stesso «indirizzo libero più basso» ed entrambi provano a inserire. Un inserimento vince, l’altro incontra la violazione di unicità, e chi perde passa al candidato successivo. Il database è l’unica parte che vede tutti gli scrittori, compreso quello in un altro processo, in un altro worker o in un’altra scheda del browser.

Il corollario conta quando scrive il codice: non controlli prima. Leggere «questo indirizzo è libero» e poi inserire apre proprio la finestra che il vincolo esiste per chiudere. L’inserimento fallito è la risposta alla domanda «era libero», ed è l’unica affidabile.

Un dettaglio di implementazione che coglie molti di sorpresa: parecchi ORM avvolgono gli errori del driver in un proprio tipo di errore, quindi lo SQLSTATE che le serve non sta sull’eccezione che intercetta ma sulla sua causa. Controlli l’intera catena, e usi il nome del vincolo per decidere quale messaggio mostrare. Una sola tabella ha spesso quattro regole di unicità diverse, e farle dire tutte la stessa cosa manda il cliente sul campo sbagliato.

Che cosa succede quando un pool si esaurisce

Dietro «nessun indirizzo disponibile» si nascondono tre guasti diversi, e confonderli insieme produce un ticket di assistenza ogni volta:

SituazioneChe cosa significaChe cosa deve leggere il cliente
Il piano non punta ad alcun poolConfigurazione, non capacitàIl piano non ha un pool di indirizzi
Un prefisso ha dati inutilizzabiliUna riga guasta, le altre a postoQuesti prefissi non è stato possibile leggerli
Ogni candidato è occupatoEsaurimento veroQuesto pool è pieno, ed ecco quale

Quello centrale vale il codice in più. Un singolo prefisso con dati illeggibili non deve bloccare gli altri prefissi del suo gruppo: lo annoti, provi con gli altri e lo segnali soltanto se non ha funzionato proprio nulla. Altrimenti una riga guasta blocca gli ordini di un’intera linea di prodotto.

E il messaggio di esaurimento deve nominare il prefisso e il suo CIDR. «Nessun IP disponibile» manda qualcuno a leggere i log. «Pool pieno: customer-block-2 (198.51.100.0/28)» lo manda a comprare indirizzi.

Restituisca gli indirizzi, e faccia attenzione a quando

Il rilascio è la metà che perde capacità in silenzio.

Due regole che hanno contato più del previsto. Primo, rilasciare non è eliminare la riga: imposta una marca temporale di rilascio. L’indice unico parziale guarda solo le righe vive, quindi l’indirizzo è subito riutilizzabile, mentre «chi aveva 192.0.2.10 l’anno scorso» resta una domanda con risposta. Questa è la ragione principale per fare gestione degli indirizzi, ed è la domanda che pone una segnalazione di abuso.

Secondo, il rilascio avviene quando la macchina è confermata sparita, non quando l’eliminazione è stata richiesta. Solo chi sta accanto all’hypervisor sa che la VM è davvero scomparsa; rilasciare al momento del comando in uscita consegna l’indirizzo al cliente successivo mentre la vecchia macchina è ancora in funzione e risponde ancora su di esso. Il caso speculare è un ordine che fallisce a metà: lì l’allocazione viene scartata anziché rilasciata, perché non è mai stata configurata su nulla.

C’è qui un’asimmetria voluta che vale la pena segnalare, perché un livello più sopra facciamo la scelta opposta. Gli indirizzi si riutilizzano, il libero più basso per primo. Gli ID delle macchine non si riutilizzano mai. Un messaggio di stato tardivo su una VM eliminata che finisce su una riga nuova con lo stesso ID corrompe la storia di due macchine; un indirizzo non porta con sé una storia in quel modo, e non riutilizzarne mai nessuno esaurirebbe per sempre un /24 dopo 254 macchine. Stesso prodotto, regole opposte, ragioni diverse.

Le prenotazioni stanno nella stessa tabella delle allocazioni

Le servirà tenere da parte degli indirizzi: un load balancer, il gateway di un blocco non ancora messo in produzione, i cinque indirizzi che qualcuno ha promesso a un cliente per il trimestre prossimo.

Li metta nella stessa tabella delle allocazioni reali. Ogni lettore che chiede «che cosa è occupato», cioè l’allocatore, la barra di utilizzo, l’elenco dei suggerimenti in un campo indirizzo, li conta allora in automatico, e non c’è una seconda amministrazione che possa disallinearsi. Una tabella separata per le prenotazioni è una seconda fonte di verità che entro un mese sarà in disaccordo con la prima.

Con un’eccezione che è facile sbagliare: un indirizzo prenotato non deve mai finire nella allowlist del firewall di una macchina. Quell’elenco è costruito a partire dalle allocazioni di un singolo server, e una prenotazione non appartiene ad alcun server. Se sbaglia qui, la VM di un cliente comincia ad accettare traffico indirizzato al load balancer di qualcun altro.

E IPv6

Riconosciamo un input IPv6 e lo rifiutiamo con garbo anziché accettarlo e calcolare qualcosa di plausibile: un percorso IPv6 che funziona a metà è più pericoloso di nessun percorso, perché i numeri che produce sembrano corretti. La decisione economica che si ripaga da sola è memorizzare indirizzi, gateway e server DNS come testo anziché come intero a 32 bit, così aggiungere IPv6 in seguito è codice e non la migrazione di ogni riga che possiede.

La versione breve

  • Metta a bilancio lo spreco. Un /29 sono cinque indirizzi vendibili, non otto.
  • Faccia puntare i piani ai pool e i pool ai prefissi. Comprare altro spazio dovrebbe essere una riga.
  • Se consente i sotto-blocchi, faccia dello spazio di indirizzamento una partizione rigorosa, altrimenti la regola di unicità che la protegge è silenziosamente per prefisso e non protegge nulla.
  • Lasci decidere la corsa al vincolo di unicità. Non controlli mai prima.
  • Rilasci alla conferma, non alla richiesta, e conservi la storia.
  • Nomini il pool nel messaggio di esaurimento. È la differenza fra un ticket e un ordine di acquisto.

Questo testo nasce dalla costruzione della gestione degli indirizzi dentro Provibr, dove gli indirizzi vengono emessi quando si inoltra un ordine e restituiti quando l’agente conferma che la macchina è davvero sparita.

Inizi con un host e un agente

Crei un account, installi l’agente con un solo comando e colleghi il suo primo host Proxmox. Il periodo di prova dura 14 giorni e non chiede alcun dato di pagamento.