sabato 13 gennaio 2018

BGP News: Graceful Shutdown di una sessione

Quante volte è capitato di dover effettuare un reload di un router in produzione, magari a seguito dell'aggiornamento del Sistema Operativo o di un solo processo. L'operazione, se eseguita senza alcun accorgimento, porta a perdita di traffico a causa di vari motivi tra cui la convergenza dei protocolli di routing. 

Il problema è ben noto ed è stato affrontato in passato sia per OSPF che IS-IS. Di questo, poiché old news, parlerò in post separati. In questo post affronterò l'argomento per il protocollo BGP.  Benché ancora la procedura, nota come BGP Graceful Shutdown, sia a livello di draft IETF, l'idea alla base è molto semplice ed è già applicata dai vendor di apparati, alcuni dei quali nelle versioni più recenti dei loro Sistemi Operativi hanno implementato dei comandi ad hoc (vedi Cisco IOS/IOS XE/IOS XR). La RFC 6198 - Requirements for the Graceful Shutdown of BGP Sessions, Aprile 2011, definisce le motivazioni dietro l'esigenza di applicare la procedura di BGP Graceful Shutdown.

IL PROBLEMA
Consideriamo il semplice scenario della figura seguente e supponiamo che il BGP peer dell'AS 1 debba effettuare delle operazioni di manutenzione sul router R1 e che quindi sia costretto a disconnetterlo dalla rete.




Una soluzione plausibile potrebbe essere quella di mettere, prima del distacco del router, in shutdown la sessione BGP e quindi eseguire le operazioni di manutenzione. Solo per completezza, per mettere una sessione nello stato shutdown amministrativo i comandi da eseguire sono i seguenti:

Cisco IOS e IOS XE
router(config)# router bgp numero-AS 
router(config-router)# neighbor IP-peer shutdown

! Per ristabilire la sessione
 router(config-router)# no neighbor IP-peer shutdown

Cisco IOS XR
RP/0/RP0/CPU0:router(config)# router bgp numero-AS
RP/0/RP0/CPU0:router(config-bgp)# neighbor IP-peer

RP/0/RP0/CPU0:router(config-bgp-nbr)# shutdown

! Per ristabilire la sessione
RP/0/RP0/CPU0:router(config-bgp-nbr)# no shutdown

In entrambi i casi, lo stato della sessione viene riportato ad "Idle" e segnalato che la sessione è stata posta in shutdown in modo amministrativo, vedi la figura seguente per un esempio relativo all'IOS XR.




JUNOS
Il JUNOS non ha un comando equivalente. Per mettere in shutdown una sessione è necessario disattivare la configurazione:

[edit protocols bgp]
tt@router# deactivate protocols bgp group nome-gruppo neighbor IP-peer
tt@router# commit 

Così facendo la sessione viene chiusa ma a differenza dei router Cisco, il neighbor non viene più mostrato nel comando "show bgp summary".

Ora, cosa accade quando si mette in shutdown una sessione BGP ? Per primo, i due router agli estremi della sessione eliminano dal DataBase degli annunci BGP, tutti gli annunci ricevuti dal BGP peer. Se un annuncio non è best path, la storia finisce qui. In caso contrario, il processo BGP determina un nuovo best path e lo annuncia ai suoi BGP peer seguendo le regole classiche del BGP. E la propagazione degli annunci prosegue sugli altri router sempre seguendo le regole classiche del BGP.

Giusto per fare un esempio, consideriamo lo scenario della figura sopra in cui il router R1 inizia la procedura di shutdown della sessione BGP con il router RA e consideriamo il prefisso 1.1/16. Per RA, come si vede dalla figura, l'annuncio ricevuto da R1 è il best path, poiché all'annuncio è stato assegnato un valore di Local Preference 200 (contro il valore di default 100 assegnato da RB all'annuncio allo stesso prefisso, proveniente da R3). RA, che ha nel proprio DataBase BGP un solo annuncio, lo cancella dal DataBase e quindi attraverso un messaggio BGP UPDATE inviato a RB e RC, ritira l'annuncio del prefisso 1.1/16. Faccio notare che RA da questo momento non è più in grado di instradare traffico verso il prefisso 1.1/16 poiché non ha ancora la possibilità di determinare strade alternative. Lo stesso accade a RC.

Il black-hole del traffico instradato attraverso RA e RC dura fortunatamente poco. Infatti, il router RB alla ricezione del ritiro del prefisso 1.1/16 da parte di RA, poiché ha un secondo annuncio BGP nel suo DataBase, riesegue il processo di selezione ed elegge come nuovo best path l'annuncio ricevuto dal router R3 dell'AS 3. Quindi annuncia a RA e RC il nuovo best path, e quindi sia RA che RC potranno installare nelle propria FIB il nuovo percorso (che fa transito su RB) per raggiungere il prefisso 1.1/16. Tutto è bene quel che finisce bene, solo che:
  1. Per un certo tempo, seppure breve, RA e RC non saranno in grado di instradare il traffico verso il prefisso 1.1/16, con conseguente perdita della stesso. Ho fatto una prova di laboratorio nella quale ho ripetuto lo scenario sopra, con un backbone BGP/MPLS, utilizzando l'AS 4 come transito tra un ulteriore AS (non visualizzato nella figura) e l'AS 2. Ho eseguito un ping ripetuto dal nuovo AS verso un host del prefisso 1.1/16 e il risultato è stato la perdita di due pacchetti, dopodiché il traffico è ritornato a fluire regolarmente.
  2. Se invece che un prefisso, R1 ne avesse annunciati migliaia se non centinaia di migliaia, come accade nella realtà, la perdita complessiva di traffico ne avrebbe risentito notevolmente poiché il ragionamento che ho fatto sopra per il prefisso 1.1/16 andrebbe ripetuto per ciascun prefisso (a meno che non vengano adottate nuove funzionalità tipo BGP PICBGP best External o BGP Add Path, che ho trattato ampiamente in un documento che potete scaricare su questo blog collegandovi a questo link).
In ogni caso, anche se la perdita di traffico fosse piccola, perché perdere comunque quel traffico ? Esiste un metodo più "morbido" per eseguire lo shutdown di una sessione BGP ? Qui entra in gioco il BGP Graceful Shutdown, un'idea estremamente semplice, oserei dire quasi banale, che è in via di standardizzazione in IETF, vedi questo draft.

FUNZIONAMENTO
L'idea alla base del BGP Graceful Shutdown non è assolutamente nuova. Sotto altre forme è già stata implementata sia da OSPF (v2/v3) che da IS-IS. In fondo si potrebbe riassumere come segue:

Ciao, io sono il tuo BGP peer X.X.X.X. Se mi stai utilizzando come best path verso un qualsiasi prefisso IP, per cortesia inizia a considerarmi come worst path poiché devo eseguire delle operazioni sul router e sono costretto a chiudere la sessione BGP che ho con te. Cercati nel frattempo vie alternative in modo tale da non perdere traffico.

Come segnalare a un BGP peer la volontà di chiudere una sessione BGP ? Bene, il draft IETF propone l'utilizzo di uno speciale valore di Community, 65535:0 (= 0xFFFF0000), riservato dallo IANA proprio per questo scopo. Questa speciale Community è stata "battezzata" come GRACEFUL_SHUTDOWN. Un router in grado di supportare il BGP Graceful Shutdown, dovrebbe applicare una politica inbound che sulla base del valore di Community GRACEFUL_SHUTDOWN assegni agli annunci un valore di Local Preference (LP) più basso di tutti gli altri annunci dello stesso prefisso (valore consigliato ovviamente, LP = 0), in modo da consentire un passaggio del traffico uscente, dal router in questione verso un altro best path.

Ma vediamo bene il funzionamento con l'esempio della figura sopra. Supponiamo che l'amministratore dell'AS 1 voglia eseguire delle operazioni di manutenzione straordinarie sul proprio router R1, e che quindi sia costretto a disconnetterlo temporaneamente dalla rete. Il router RA, che è il suo BGP peer, è in grado di supportare il BGP Graceful Shutdown. A tale scopo ha pre-configurato una politica inbound del tipo:

if (annuncio contiene la Community GRACEFUL_SHUTDOWN) then set LP = 0;

Ad esempio, supponendo che RA sia un router Cisco con l'IOS XR, la politica da applicare è la seguente (riportata nel draft come esempio):

community-set GRACEFUL_SHUTDOWN
     65535:0
end-set
!
route-policy XYZ-IN
     if community matches-any GRACEFUL_SHUTDOWN then
       set local-preference 0
     endif
end-policy
!
router bgp 4
 address-family ipv4 unicast
  neighbor R1
     remote-as 1
     address-family ipv4 unicast
        route-policy XYZ-IN in

Vediamo la sequenza delle operazioni, prima della disconnessione del router R1:
  1. Il router R1 crea una politica outbound, tramite la quale reinvia al BGP peer RA tutti i propri best path, aggiungendo la Community GRACEFUL_SHUTDOWN.
  2. RA applica la politica inbound pre-configurata e quindi pone LP=0 per tutti gli annunci ricevuti da R1.
  3. RA invia di nuovo i suoi best path con LP=0 all'interno del proprio AS dando la possibilità a tutti router di scegliere un nuovo best path (che nel nostro esempio sarà RB). Dopo un certo tempo, il traffico uscente dall'AS 4 non transiterà più da RA ma da RB.
  4. Il router R1, per evitare di essere best path  per il traffico uscente dal proprio AS, applica una politica inbound identica a quella pre-configurata da RA e quindi forza RA a reinviargli tutti i best path (per questo è sufficiente un "clear ... soft in"; nell'IOS XR è sufficiente il semplice "commit").
  5. L'amministratore dell'AS 1 attende un po' di tempo che tutto si stabilizzi nella nuova situazione e quindi mette in shutdown la sessione BGP con RA. Questo tempo può essere più o meno lungo in funzione di vari fattori. Non esiste un segnale specifico che tutto si sia stabilizzato, ma un semplice monitoraggio del traffico sul link di collegamento R1-RA è sufficiente.
Si noti che la perdita di traffico è in questo caso praticamente nulla perché quando si esegue lo shutdown della sessione BGP, già il traffico ha trovato una via alternativa. Lo stesso, durante la ricerca della via alternativa il traffico non viene perso poiché RA mantiene comunque nella propria FIB il percorso originale, fino a quando non lo sostituirà con il nuovo best path. Morale della favola, ho rifatto il test citato sopra, di un ping verso un host del prefisso 1.1/16, da un AS che utilizza AS 4 come transito, e il risultato è stato "success rate 100%" (contro i due ping persi seguendo la procedura classica).

NOTA: per far si che il passo 1 si compia, le configurazioni da eseguire sono abbastanza banali. Ad esempio, supponendo R1 sia un router Cisco con l'IOS (o anche IOS XE), le configurazioni da eseguire sono le seguenti:

route-map GSHUT permit 10
  set community 65535:0
!
router bgp 1
  neighbor RA send-community
  neighbor RA route-map GSHUT out
!
clear ip bgp RA soft out

La procedura come si può notare è estremamente semplice e non richiede strumenti sofisticati, se non l'applicazione di semplici politiche inbound/outbound. Non tutti i provider al momento "onorano" la Community GRACEFUL_SHUTDOWN. Se volete una lista aggiornata collegatevi a questo link, curato dal fantasmagorico Job Snijders di NTT Communications, dove tra l'altro potrete trovare altre utili informazioni.

CONCLUSIONI
Escludere un router dalla rete per manutenzione straordinaria o periodica è una operazione che andrebbe fatta non brutalmente, del tipo "stacco la spina, tanto ci pensano i protocolli di routing a rimettere tutto a posto", ma eseguendo prima delle semplici operazioni che consentano in un tempo più o meno breve, di escludere il router dal forwarding path. Questa esclusione va pianificata ed eseguita con cura, riducendo al minimo "sindacale" la perdita di traffico. Nel caso del BGP, l'idea base è molto semplice e utilizza politiche elementari inbound/outbound. Nel caso di OSPF si ricorre a un trucco altrettanto banale, che è quello di porre temporaneamente le metriche delle interfacce al loro valore massimo. IS-IS risolve invece lo stesso problema con un semplice bit (il bit overload). Tratterò questi due casi in un post successivo.

Arrivederci al prossimo post ...



4 commenti:

  1. La semplicità con cui ogni volta spieghi concetti più o meno complessi, mi spiazza sempre. Well Done ammiraglio.

    RispondiElimina
  2. Caro Vincenzo, ringrazio commosso per l'apprezzamento. Come sai, cerco di fare sempre del mio meglio ...

    RispondiElimina
  3. Number One! (Y)

    RispondiElimina