BGP LLGR : sessions BGP fiables et réactives

Vincent Bernat

Sur un réseau routé via BGP et disposant de multiples chemins redondants, deux objectifs concernant la fiabilité sont souvent recherchés :

  1. Un dysfonctionnement sur un chemin doit rapidement faire tomber la session BGP correspondante. On s’attend généralement à se remettre d’une telle défaillance en moins d’une seconde en déviant le trafic vers les autres chemins disponibles.

  2. Aussi longtemps qu’un chemin est opérationnel, les sessions BGP qui y sont liées doivent rester fonctionnelles, même sous la pression.

Détection rapide des pannes : BFD#

Pour détecter rapidement une panne, BGP peut être associé à BFD, un protocole de détection d’anomalies sur les chemins bidirectionnels1, défini dans la RFC 5880 et la RFC 5882. BFD peut réagir sur des délais très courts, de l’ordre de 100 ms.

Toutefois, lorsque BFD est géré dans un processus au-dessus d’un noyau générique2, notamment lorsque BGP tourne sur l’hôte, il est possible de perdre quelques paquets BFD lorsque le système est sous pression : le démon gérant les sessions BFD peut manquer de CPU pour répondre dans le temps imparti. Dans ce scénario, il n’est pas rare que toutes les sessions BGP terminant sur l’hôte tombent au même moment, créant un incident comme illustré dans le dernier cas du diagramme ci-dessous.

Exemples de dysfonctionnements sur un réseau
BGP
Exemples de dysfonctionnements sur un réseau utilisant BGP. Un problème sur un lien est détecté par BFD et le chemin défectueux est retiré de la route ECMP. Toutefois, en cas d'usage intensif du CPU sur le routeur du bas, les paquets BFD ne sont pas traités assez rapidement et tous les chemins disponibles sont disqualifiés.

À ce niveau, nous avons donc deux possibilités apparemment irréconciliables :

  • descendre le temps de détection de BFD pour rendre le réseau plus réactif aux problèmes ;
  • augmenter le temps de détection de BFD pour s’assurer que les sessions BGP restent opérationnelles.

Correction des faux positifs : BGP LLGR#

Long-lived BGP Graceful Restart (BGP LLGR) est une nouvelle capacité pour BGP permettant de garder les routes périmées (stale) pendant une certaine période après la fin de la session BGP mais en les traitant avec une préférence inférieure. Une nouvelle communauté « bien connue » permet de partager cette information avec les autres routeurs. BGP LLGR est défini dans la RFC 9494 et plusieurs implémentations existent déjà :

  • Juniper Junos (depuis la version 15.1, voir la documentation),
  • Cisco IOS XR (testé sur une 7.3.2),
  • BIRD (depuis les versions 1.6.5 and 2.0.3),
  • GoBGP (depuis la version 1.33),
  • FRR (depuis la version 8.2).

L’illustration ci-dessous montre comment BGP LLGR permet de surmonter certains dysfonctionnements. En ❷, une anomalie sur un lien est détectée par BFD et le chemin correspondant est retiré de la route car il reste deux autres chemins avec une préférence supérieure. Quelques minutes plus tard, le délai de grâce expire et ce chemin ne sera plus utilisé du tout. Peu de temps après, le routeur du bas est victime d’une charge processeur élevée et ne peut plus gérer les paquets BFD dans un délai suffisant. Les sessions BGP tombent et les deux chemins restants sont marqués comme périmés. Toutefois, comme il n’existe plus aucun autre chemin, ils restent utilisés jusqu’à l’expiration du délai de grâce. Cela permet de laisser le temps aux sessions BGP de remonter.

BGP avec LLGR
Exemples de dysfonctionnements sur un réseau utilisant BGP LLGR.

Du point de vue du routeur du haut, le premier chemin a été considéré comme périmé car la session BGP avec R1 a été rompue. Toutefois, lors du second incident, les deux chemins restants ont été marqués comme périmés via la communauté « bien connue » LLGR_STALE (65535:6) par R2 et R3.

Un autre intérêt de BGP LLGR est de pouvoir redémarrer le démon BGP sans impact, tant que les chemins restent stables peu de temps avant et pendant le redémarrage. C’est particulièrement intéressant si BGP tourne sur un hyperviseur3.

BIRD#

Voyons comment configurer BIRD 1.6. Comme BGP LLGR se base sur une capacité similaire appelée BGP graceful restart (BGP GR) mais pour laquelle les routes sont gardées avec une préférence inchangée. Le minuteur pour LLGR démarre après expiration du minuteur pour GR. Aussi, il est important de configurer ce dernier à 0.

template bgp BGP_LLGR {
  bfd graceful;
  graceful restart yes;
  graceful restart time 0;
  long lived graceful restart yes;
  long lived stale time 120;
}

Quand un souci survient sur un chemin, la session BGP se termine et le minuteur pour LLGR commence :

$ birdc show protocol R1_1 all
name     proto    table    state  since       info
R1_1     BGP      master   start  11:20:17    Connect
  Preference:     100
  Input filter:   ACCEPT
  Output filter:  ACCEPT
  Routes:         1 imported, 0 exported, 0 preferred
  Route change stats:     received   rejected   filtered    ignored   accepted
    Import updates:              2          0          0          0          4
    Import withdraws:            0          0        ---          0          0
    Export updates:             12         10          0        ---          2
    Export withdraws:            1        ---        ---        ---          0
  BGP state:          Connect
    Neighbor address: 2001:db8:104::1
    Neighbor AS:      65000
    Neighbor graceful restart active
    LL stale timer:   112/-

Le chemin lié à cette session est marqué comme périmé (ce qui correspond au s dans 100s) et la communauté LLGR_STALE est ajoutée :

$ birdc show route 2001:db8:10::1/128 all
2001:db8:10::1/128 via 2001:db8:204::1 on eth0.204 [R1_2 10:35:01] * (100) [i]
        Type: BGP unicast univ
        BGP.origin: IGP
        BGP.as_path:
        BGP.next_hop: 2001:db8:204::1 fe80::5254:3300:cc00:5
        BGP.local_pref: 100
                   via 2001:db8:104::1 on eth0.104 [R1_1 11:22:51] (100s) [i]
        Type: BGP unicast univ
        BGP.origin: IGP
        BGP.as_path:
        BGP.next_hop: 2001:db8:104::1 fe80::5254:3300:6800:5
        BGP.local_pref: 100
        BGP.community: (65535,6)

La route transmise au noyau n’est plus composée que du chemin restant :

$ ip route show 2001:db8:10::1
2001:db8:10::1 via 2001:db8:204::1 dev eth0.204 proto bird metric 1024 pref medium

Pour mettre à jour BIRD sans impact, il faut qu’il soit démarré avec le drapeau -R et la directive graceful restart yes pour les protocoles kernel. Avant la mise à jour, il faut arrêter BIRD avec SIGKILL plutôt que SIGTERM pour ne pas lui laisser l’occasion de fermer proprement les sessions BGP.

Juniper Junos#

Avec Junos, si BFD est déjà activé, il suffit d’activer BGP LLGR pour chaque famille :

# Enable BGP LLGR
edit protocols bgp group peers family inet6 unicast
set graceful-restart long-lived restarter stale-time 2m

Une fois qu’une erreur survient sur un chemin, la session BGP associée se termine et le minuteur BGP LLGR commence :

> show bgp neighbor 2001:db8:104::4
Peer: 2001:db8:104::4+179 AS 65000 Local: 2001:db8:104::1+57667 AS 65000
  Group: peers                 Routing-Instance: master
  Forwarding routing-instance: master
  Type: Internal    State: Connect        Flags: <>
  Last State: Active        Last Event: ConnectRetry
  Last Error: None
  Export: [ LOOPBACK NOTHING ]
  Options: <Preference HoldTime Ttl AddressFamily Multipath Refresh>
  Options: <BfdEnabled LLGR>
  Address families configured: inet6-unicast
  Holdtime: 6 Preference: 170
  NLRI inet6-unicast:
  Number of flaps: 2
  Last flap event: Restart
  Time until long-lived stale routes deleted: inet6-unicast 00:01:05
  Table inet6.0 Bit: 20000
    RIB State: BGP restart is complete
    Send state: not advertising
    Active prefixes:              0
    Received prefixes:            1
    Accepted prefixes:            1
    Suppressed due to damping:    0
    LLGR-stale prefixes:          1

Le chemin défectueux est marqué comme périmé et devient inactif car il existe de meilleurs chemins disponibles :

> show route 2001:db8:10::4 extensive
[…]
BGP    Preference: 170/-101
       Source: 2001:db8:104::4
       Next hop type: Router, Next hop index: 778
       Next hop: 2001:db8:104::4 via em1.104, selected
       Protocol next hop: 2001:db8:104::4
       Indirect next hop: 0xb1d27c0 1048578 INH Session ID: 0x15c
       State: <Int Ext>
       Inactive reason: LLGR stale
       Local AS: 65000 Peer AS: 65000
       Age: 4  Metric2: 0
       Communities: llgr-stale
       Accepted LongLivedStale
       Localpref: 100
       Router ID: 1.0.0.4
[…]

Jetez un œil au dépôt Git pour les configurations complètes ainsi que les sorties des différentes commandes pour vérifier que tout fonctionne. Il existe aussi une variante qui utilise des réflecteurs de routes. Maintenant que FRR implémente BFD, j’espère qu’il adoptera également LLGR.

Mise à jour (01.2022)

Donatas Abraitis a récemment ajouté le support de LLGR dans FRR ! Il sera disponible dans la version 8.2.

Mise à jour (01.2024)

La RFC 9494 a été publiée en novembre 2023.


  1. Avec des connexions point à point, BGP peut détecter immédiatement un problème sans s’aider de BFD. Toutefois, sur une paire de fibres, le souci peut être unidirectionnel et rester invisible d’un des deux pairs pendant plusieurs secondes. ↩︎

  2. Sur un Juniper MX, BFD est généralement géré directement par le micro-noyau temps réel attaché aux cartes poussant les paquets. Le datagramme BFD contient un bit indiquant si la gestion se fait au plus bas niveau ou non. tcpdump permet de vérifier la valeur de ce bit. Voici un exemple où 10.71.7.1 est un hôte Linux avec BIRD et 10.71.0.3 est un Juniper MX :

    $ sudo tcpdump -pni vlan181 port 3784
    IP 10.71.7.1 > 10.71.0.3: BFDv1, Control, State Up, Flags: [none]
    IP 10.71.0.3 > 10.71.7.1: BFDv1, Control, State Up, Flags: [Control Plane Independent]
    
    ↩︎
  3. Une telle fonctionnalité est l’apanage de BGP graceful restart. Toutefois, sans LLGR, les chemins non fonctionnels sont gardés avec la même préférence et ne sont pas retirés des routes ECMP↩︎