Routage inter-subnet intra-VPC via L3VNI (IRB symétrique) #41

Open
opened 2026-08-20 20:06:10 +00:00 by nicolas.boufideline · 2 comments

Contexte et problème

Aujourd'hui le routage entre deux subnets d'un même VPC impose de porter les deux subnets
(bridge + L2VNI + SVI) sur les deux hyperviseurs concernés. C'est de l'IRB asymétrique :
le routage se fait entièrement sur le VTEP d'entrée, qui doit donc connaître localement le
subnet de destination.

Conséquences :

  • chaque host doit porter l'union des subnets de tous les VPC qu'il héberge ;
  • le subnet redevient un objet global au datacenter, ce qui annule l'intérêt du modèle ;
  • ne passe pas à l'échelle en nombre de subnets × nombre de hosts.

Solution retenue

IRB symétrique : introduction d'un L3VNI par VPC, VNI de transit distinct des L2VNI
de subnet, associé à une VRF l3mdev dans le netns underlay.

Le netns du VPC est conservé pour les VM (isolation structurelle là où sont les charges non
fiables). Il ne porte plus que les gateways de subnet et une route supernet de sortie. La
table de routage inter-subnet remonte dans la VRF, où FRR la voit et peut faire de l'EVPN
nativement.

Chemin d'un paquet A (subnet D, host1) → B (subnet E, host2) :

  1. A → gateway de D, dans le netns C de host1 ;
  2. route supernet → veth-l3-C-out, entrée dans vrf-C ;
  3. lookup dans vrf-C → RT-5 apprise, next-hop = VTEP host2, RMAC = RMAC-host2 ;
  4. encapsulation L3VNI, transport underlay ;
  5. host2 décapsule, br-l3-C remonte en L3 dans vrf-C, route vers son netns ;
  6. netns C de host2 → br-E → ARP de B → livraison.

Host1 n'a jamais besoin de br-E ni de vxlan-E, et réciproquement. Par host, le nombre de
VNI passe de « tous les subnets de tous les VPC hébergés » à « subnets réellement locaux

  • 1 par VPC hébergé ».

Articulation avec le plan public existant

Le plan public reste inchangé et cohabite sans interférence :

Objet Portée Modèle
Subnets privés d'un VPC hosts portant des VM du subnet L2VNI + gateway locale dans le netns
Transit inter-subnet d'un VPC hosts portant des VM du VPC L3VNI + VRF, IRB symétrique
Plages publiques tous les hyperviseurs L2VNI étendu, gateway sur border routers

Dans le netns d'un VPC, les deux plans coexistent par longest-prefix : le supernet du VPC part
dans le L3VNI, la route par défaut part vers la MAC anycast des border routers via le VNI
public. Aucun conflit. Les sujets propres au plan public sont traités dans les issues #04,
#05, #06 et #08.

Topologie

                        vrf-C (table 1100)
                        ├── br-l3-C            [RMAC host, pas d'IP]   ← SVI du L3VNI
                        │     └── vxlan-l3-C   [VNI 1100, nolearning]
                        └── veth-l3-C-out      [169.254.0.1/30]        ← lien routé
                                 ╎
        ─────────────────────────╎─────────────────────────── netns C
                              veth-l3-C-in      [169.254.0.2/30]
                              br-D              [gateway 10.1.0.1/24]
                                └── veth-D-in ─╌╌ (L2VNI, inchangé)
                              tap-A

Règle d'invariant : vrf-C contient exactement deux interfaces, br-l3-C (sans IP)
et veth-l3-C-out (avec le /30). Elles ne sont pas pontées entre elles — c'est la FIB de
la VRF qui les relie, et ce lookup est le routage inter-subnet.

Erreurs à ne pas commettre :

  • veth-l3-C-out master br-l3-C → remet le netns dans le domaine de broadcast du L3VNI :
    plus de routage sur l'host, collision généralisée du /30 entre tous les hosts, pollution
    du L3VNI par des RT-2 parasites ;
  • vxlan-l3-C directement dans la VRF sans bridge → pas de terminaison L3, non reconnu
    comme L3VNI par FRR. Le bridge à un port est un passage obligé du modèle Linux.

Plan d'allocation (exemple chiffré)

Élément Valeur
VPC C, supernet 10.1.0.0/16
L3VNI C / ID table de routage 1100
RD <IP-VTEP-host>:1100 — dérivé du VTEP, pas de l'ASN
RT 65000:1100, commun au VPC
Transit netns↔host 169.254.0.0/30, identique pour tous les VPC
ASN hyperviseurs / RR 65001 (commun à tous les hosts) / 65000
host1 : VTEP / RMAC 10.0.0.1 / 02:00:00:00:00:01
host2 : VTEP / RMAC 10.0.0.2 / 02:00:00:00:00:02
VIP des RR 10.0.255.1, 10.0.255.2
Subnet D 10.1.0.0/24, L2VNI 10100 host1, VM A 10.1.0.5
Subnet E 10.1.1.0/24, L2VNI 10101 host2, VM B 10.1.1.5

Contraintes d'allocation :

  • Le RD doit être dérivé de l'IP de VTEP. Tous les hosts partageant l'ASN 65001, un RD
    de la forme <ASN>:<VNI> serait identique partout : les annonces concurrentes du même
    préfixe redeviendraient une seule NLRI, et on perdrait le multipath ainsi que le filet de
    secours du /24 pendant les migrations. Contrainte, pas préférence.
  • RD, RT, L3VNI et ID de table déclarés explicitement, jamais dérivés automatiquement.
    La dérivation auto du RT dépend de l'ASN local, que local-as modifie sur la session RR.
  • Supernet contigu par VPC : la route supernet unique dans le netns suppose que chaque
    VPC est taillé dans un bloc agrégeable. Voir issue #10.
  • RMAC unique par host, MAC de gateway anycast identique sur tous les hosts portant le
    même subnet.

Configuration kernel

Host1 — création du netns C

ip netns add C
ip link add veth-l3-C-out type veth peer name veth-l3-C-in
ip link set veth-l3-C-in netns C

# côté tenant
ip -n C addr add 169.254.0.2/30 dev veth-l3-C-in
ip -n C link set veth-l3-C-in mtu 8950 up
ip -n C route add 10.1.0.0/16 via 169.254.0.1 dev veth-l3-C-in onlink
ip -n C neigh replace 169.254.0.1 lladdr <MAC-veth-l3-C-out> \
    dev veth-l3-C-in nud permanent
ip netns exec C sysctl -qw net.ipv4.ip_forward=1

# côté underlay : VRF + L3VNI
ip link add vrf-C type vrf table 1100
ip link set vrf-C up
ip link add br-l3-C type bridge
ip link set br-l3-C address 02:00:00:00:00:01
ip link set br-l3-C master vrf-C
ip link add vxlan-l3-C type vxlan id 1100 dstport 4789 local 10.0.0.1 nolearning
ip link set vxlan-l3-C master br-l3-C
ip link set vxlan-l3-C mtu 9000 up
ip link set br-l3-C mtu 9000 up

ip link set veth-l3-C-out master vrf-C
ip addr add 169.254.0.1/30 dev veth-l3-C-out
ip link set veth-l3-C-out mtu 8950 up

Host1 — subnet D (L2VNI, inchangé)

ip link add vxlan-D type vxlan id 10100 dstport 4789 local 10.0.0.1 nolearning
ip link add br-D-out type bridge
ip link set vxlan-D master br-D-out
ip link add veth-D-out type veth peer name veth-D-in
ip link set veth-D-out master br-D-out
ip link set veth-D-in netns C

ip -n C link add br-D type bridge
ip -n C link set veth-D-in master br-D
ip -n C link set br-D address 02:00:00:aa:00:0d      # MAC anycast, identique partout
ip -n C addr add 10.1.0.1/24 dev br-D

Aucun L2VNI n'est rattaché à la VRF : la gateway est dans le netns.

Configuration FRR

Host1 — /etc/frr/frr.conf

frr version 8.x
frr defaults datacenter
hostname host1
log syslog informational
service integrated-vtysh-config
!
vrf vrf-C
 vni 1100
exit-vrf
!
router bgp 65001
 bgp router-id 10.0.0.1
 no bgp default ipv4-unicast
 bgp log-neighbor-changes
 !
 ! --- underlay : routeur de cluster, eBGP natif ---
 neighbor UPLINK peer-group
 neighbor UPLINK remote-as <ASN-cluster>
 neighbor UPLINK bfd
 neighbor <ip-routeur-cluster> peer-group UPLINK
 !
 ! --- overlay : RR, session ramenée en iBGP via local-as ---
 neighbor RR peer-group
 neighbor RR remote-as 65000
 neighbor RR local-as 65000 no-prepend replace-as
 neighbor RR update-source 10.0.0.1
 neighbor 10.0.255.1 peer-group RR
 neighbor 10.0.255.2 peer-group RR
 !
 address-family ipv4 unicast
  neighbor UPLINK activate
  neighbor UPLINK route-map UNDERLAY-OUT out
  network 10.0.0.1/32
 exit-address-family
 !
 address-family l2vpn evpn
  neighbor RR activate
  neighbor RR soft-reconfiguration inbound
  neighbor RR maximum-prefix 200000 90 restart 15
  advertise-all-vni
 exit-address-family
exit
!
! --- contexte d'origination du VPC C : AUCUN neighbor ---
router bgp 65001 vrf vrf-C
 bgp router-id 10.0.0.1
 no bgp default ipv4-unicast
 !
 address-family ipv4 unicast
  redistribute kernel route-map VPC-C-EXPORT
  maximum-paths ibgp 4
 exit-address-family
 !
 address-family l2vpn evpn
  rd 10.0.0.1:1100
  route-target both 65000:1100
  advertise ipv4 unicast
 exit-address-family
exit
!
ip prefix-list PL-VPC-C seq 10 permit 10.1.0.0/16 le 32
ip prefix-list PL-UNDERLAY seq 10 permit 10.0.0.1/32
!
route-map VPC-C-EXPORT permit 10
 match ip address prefix-list PL-VPC-C
route-map VPC-C-EXPORT deny 20
!
route-map UNDERLAY-OUT permit 10
 match ip address prefix-list PL-UNDERLAY
route-map UNDERLAY-OUT deny 20
!
line vty

Host2 — seules lignes divergentes

hostname host2
!
router bgp 65001
 bgp router-id 10.0.0.2
 neighbor RR update-source 10.0.0.2
 address-family ipv4 unicast
  network 10.0.0.2/32
!
router bgp 65001 vrf vrf-C
 bgp router-id 10.0.0.2
 address-family l2vpn evpn
  rd 10.0.0.2:1100
!
ip prefix-list PL-UNDERLAY seq 10 permit 10.0.0.2/32

Tout le reste est strictement identique, y compris le RT, le L3VNI, le prefix-list VPC et la
route-map d'export. Trois valeurs à substituer par host : IP de VTEP (router-id,
update-source, RD, prefix-list underlay), RMAC (côté kernel), hostname.

Route reflector — /etc/frr/frr.conf

frr version 8.x
frr defaults datacenter
hostname rr1
log syslog informational
service integrated-vtysh-config
!
router bgp 65000
 bgp router-id 10.0.255.1
 no bgp default ipv4-unicast
 bgp log-neighbor-changes
 bgp cluster-id 10.0.255.1
 !
 ! --- hyperviseurs : peering dynamique, aucun ajout de config par host ---
 neighbor HOSTS peer-group
 neighbor HOSTS remote-as 65000
 neighbor HOSTS update-source 10.0.255.1
 bgp listen range 10.0.0.0/16 peer-group HOSTS
 bgp listen limit 1024
 !
 ! --- border routers ---
 neighbor BORDER peer-group
 neighbor BORDER remote-as 65000
 neighbor BORDER update-source 10.0.255.1
 neighbor <ip-border-1> peer-group BORDER
 neighbor <ip-border-2> peer-group BORDER
 !
 address-family l2vpn evpn
  neighbor HOSTS activate
  neighbor HOSTS route-reflector-client
  neighbor BORDER activate
  neighbor BORDER route-reflector-client
 exit-address-family
exit
!
line vty

Le RR réfléchit les routes, il ne les importe pas : aucune VRF, aucun L3VNI, aucun RT,
aucun VTEP. Ajouter un VPC ne touche jamais à sa configuration, et le listen range fait
qu'ajouter un hyperviseur non plus.

Interdits sur le RR, chacun provoquant une panne silencieuse :

  • next-hop-self : le next-hop d'une route EVPN est le VTEP d'origine. Réécrit, il
    pointe vers le RR, qui n'est pas un VTEP → trou noir total.
  • Route-map touchant aux communautés étendues : RMAC, RT et L3VNI y sont portés.
  • advertise-all-vni : le RR n'est pas un VTEP.

update-source doit être la VIP Pacemaker, sinon la session casse au basculement.

À vérifier sur la version en usage : bgp retain route-target all. Un RR reçoit des
routes dont il n'importe aucun RT ; si la rétention est désactivée il les jette et la
réflexion ne fonctionne plus. Le défaut FRR est la rétention, mais ce point doit être
confirmé — la panne serait totale et immédiate sur tout VPC nouvellement créé. Voir #01.

Pourquoi local-as sur la session RR

Hyperviseurs et RR ont des ASN différents (65001 / 65000), la session serait donc eBGP.
Quatre problèmes en découlent, dont un bloquant :

  • boucle AS-PATH : tous les hosts partageant 65001, une route de host1 revient à host2
    avec 65001 dans l'AS-PATH et est rejetée. Aucune route EVPN n'est échangée entre hosts ;
  • route-reflector-client est un mécanisme iBGP, sans effet en eBGP ;
  • réécriture du next-hop par défaut en eBGP → risque de trou noir ;
  • session multihop à gérer vers une VIP.

local-as 65000 no-prepend replace-as ramène cette seule session en iBGP : le type de
session se détermine par voisin, en comparant remote-as à l'ASN local effectif. Les autres
voisins du host restent en eBGP natif avec 65001.

Portée de diffusion des routes tenant

router bgp 65001 vrf vrf-C ne contient aucun neighbor : c'est un contexte
d'origination, pas une instance BGP supplémentaire. Ce qui décide de la diffusion, c'est
l'AF sous laquelle un voisin est activé.

Session AF activée Transporte
host → RR l2vpn evpn seule RT-2, RT-3, RT-5 de tous les VPC
host → routeur de cluster ipv4 unicast seule underlay uniquement

Deux conditions à ne jamais violer :

  • ne jamais activer le routeur de cluster sous l2vpn evpn — il recevrait toutes les
    RT-5 tenant et pourrait les propager vers le backbone ;
  • ne jamais mettre import vrf vrf-C dans l'instance par défaut — c'est la commande de
    fuite de routes entre VRF.

Ni l'une ni l'autre ne produit d'erreur ni de dégradation visible, seulement une fuite : ce
sont des cas à détecter par contrôle automatisé de configuration.

Opérations

Ajout / suppression de VM

ip route add 10.1.0.5/32 via 169.254.0.2 dev veth-l3-C-out vrf vrf-C
ip route del 10.1.0.5/32 vrf vrf-C

redistribute kernel, pas redistribute static : une route posée par ip route add
depuis l'orchestrateur est de type kernel pour zebra. static désigne les routes configurées
dans FRR. Avec static, aucune route VM n'est annoncée, sans message d'erreur.

Choix du /32 par VM retenu pour que la migration soit un événement de plan de contrôle
échangé, et non une expiration de cache.

Filet de secours : conserver en permanence l'annonce du /24 par chaque host portant le
subnet. Pendant la fenêtre de bascule d'un /32, le trafic tombe sur un host du subnet et
repart en L2VNI vers la VM. Un hop de trop pendant quelques secondes, aucune perte.

Migration de VM

Ordre imposé : retrait du /32 à la source, puis pose à destination.

Dans l'ordre inverse, deux RT-5 identiques coexistent, la sélection peut figer l'ancien
chemin, et le trafic tombe dans le vide de façon stable — bien pire qu'une micro-coupure.

Ajout / suppression de VPC

Uniquement sur les hosts hébergeant une VM du VPC : VRF + br-l3-C + vxlan-l3-C +
rattachement du veth, puis le bloc vrf et router bgp ... vrf ci-dessus. Rien sur les
autres hosts, rien sur les RR, rien sur les border routers.

Créer un subnet ne touche jamais à la config VRF ; créer un VPC ne touche jamais à la config
des subnets. Les deux plans sont orthogonaux.

Effet sur traceroute (accepté)

Deux hops supplémentaires, un par extrémité :

1  10.1.0.1        gateway de D, netns C de host1
2  169.254.0.1     veth-l3-C-out, VRF de host1        ← nouveau
3  169.254.0.2     veth-l3-C-in, netns C de host2     ← nouveau
4  10.1.1.5        B

Le passage en L3VNI est transparent (l'encapsulation VXLAN ne décrémente pas le TTL interne).
Décision : hops visibles conservés, pas de filtrage ICMP time-exceeded. Sous-décisions
restantes en #10.

Vérifications

vtysh -c "show bgp l2vpn evpn summary"                 # sessions internal, pas external
vtysh -c "show bgp neighbor 10.0.255.1"                # vérifier « internal link »
vtysh -c "show bgp l2vpn evpn vni 1100"                # L3VNI ↔ vrf-C, RMAC
vtysh -c "show bgp l2vpn evpn route type prefix"       # RT-5, RD distincts par host
ip route show vrf vrf-C                                # next-hop VTEP + RMAC
ip netns exec C ping -M do -s 8922 10.1.1.5            # MTU de bout en bout

Si show bgp neighbor affiche la session en external, le local-as n'a pas pris et rien
ne fonctionnera entre hosts.

Assertion d'invariant à automatiser : chaque VRF contient exactement deux membres,
br-l3-C sans IP et veth-l3-C-out avec le /30. La topologie est peu intuitive et se prête
au copier-coller approximatif ; ne pas compter sur la relecture humaine.

Risques

L'étanchéité inter-tenant passe d'une propriété du noyau à une propriété de la configuration :
le forwarding local reste isolé par la FIB de la VRF, mais la joignabilité inter-host dépend
désormais du RD/RT et du mapping L3VNI↔VRF
. Trois scénarios de fuite silencieuse : RT mal
alloué, L3VNI dupliqué ou recyclé trop vite, import vrf résiduel. Traité en #02.

L'orchestrateur devient source de vérité du routage tenant : une route /32 non retirée après
destruction d'une VM est un trou noir qui survit à la VM. Traité en #03.

Atténuation structurelle à formaliser dans le dossier d'architecture : le netns est conservé,
donc les charges non fiables restent isolées par le noyau. Seul le segment de transit entre
hyperviseurs est exposé à la configuration.

Autres risques : volumétrie du plan de contrôle (#09), MTU 8950/9000 à revalider (#01),
migration depuis l'existant (#11), usurpation MAC/IP au port virtuel (#04).

Issues liées

# Sujet Priorité
01 Campagne de qualification en lab Bloquant
02 Allocation déterministe des identifiants et test négatif Bloquant
03 Réconciliation orchestrateur ↔ routes tenant Haute
04 Isolation intra-VNI public entre tenants Haute, sécurité
05 MAC anycast des border routers et mobilité EVPN Haute
06 Hygiène des RT sur border routers et filtrage backbone Haute
07 Sortie Internet des VM sans IP publique Moyenne
08 Robustesse du VNI public multi-DC Moyenne
09 Volumétrie et dimensionnement du plan de contrôle Moyenne
10 IPAM : contiguïté des supernets et adressage de transit Moyenne
11 Migration de l'existant asymétrique → symétrique Haute

Variante non retenue

Session BGP unnumbered entre le netns et l'host sur le veth, netns en redistribute connected : supprime toute écriture de route statique, y compris par VM. Coût : un démon BGP
par couple (VPC, host), sur lien point-à-point. À réévaluer si le volume d'écritures devient
un problème opérationnel.


🛡️ Garde-fou Voltalis

  • Niveau : Enterprise Product
  • Risques repérés : étanchéité inter-tenant portée par l'allocation RD/RT/L3VNI et non
    plus par le noyau ; routes /32 rémanentes (trou noir silencieux) ; séquencement de
    migration ; réutilisation d'ID sans quarantaine ; volumétrie du plan de contrôle
  • Comité IA : requis, choix d'architecture structurant sur une infrastructure de
    production multi-tenant
  • Avant déploiement : revue humaine obligatoire, revue de sécurité formelle sur le modèle
    d'isolation avec test négatif inter-VPC comme critère d'acceptation, veille, revue
    technique, tests, traçabilité de l'usage de l'IA dans la conception
  • Escalade : Comité IA — Samy Denno (référent IA) · RSSI — securite@voltalis.com

Configurations conçues avec assistance IA : relecture humaine et validation en lab
obligatoires avant toute application sur un système réel.

## Contexte et problème Aujourd'hui le routage entre deux subnets d'un même VPC impose de porter les deux subnets (bridge + L2VNI + SVI) sur les deux hyperviseurs concernés. C'est de l'**IRB asymétrique** : le routage se fait entièrement sur le VTEP d'entrée, qui doit donc connaître localement le subnet de destination. Conséquences : - chaque host doit porter l'union des subnets de tous les VPC qu'il héberge ; - le subnet redevient un objet global au datacenter, ce qui annule l'intérêt du modèle ; - ne passe pas à l'échelle en nombre de subnets × nombre de hosts. ## Solution retenue **IRB symétrique** : introduction d'un **L3VNI par VPC**, VNI de transit distinct des L2VNI de subnet, associé à une **VRF l3mdev** dans le netns underlay. Le netns du VPC est conservé pour les VM (isolation structurelle là où sont les charges non fiables). Il ne porte plus que les gateways de subnet et une route supernet de sortie. La table de routage inter-subnet remonte dans la VRF, où FRR la voit et peut faire de l'EVPN nativement. Chemin d'un paquet A (subnet D, host1) → B (subnet E, host2) : 1. A → gateway de D, dans le netns C de host1 ; 2. route supernet → `veth-l3-C-out`, entrée dans `vrf-C` ; 3. lookup dans `vrf-C` → RT-5 apprise, next-hop = VTEP host2, RMAC = RMAC-host2 ; 4. encapsulation L3VNI, transport underlay ; 5. host2 décapsule, `br-l3-C` remonte en L3 dans `vrf-C`, route vers son netns ; 6. netns C de host2 → `br-E` → ARP de B → livraison. Host1 n'a jamais besoin de `br-E` ni de `vxlan-E`, et réciproquement. Par host, le nombre de VNI passe de « tous les subnets de tous les VPC hébergés » à « subnets réellement locaux + 1 par VPC hébergé ». ## Articulation avec le plan public existant Le plan public reste inchangé et cohabite sans interférence : | Objet | Portée | Modèle | |---|---|---| | Subnets privés d'un VPC | hosts portant des VM du subnet | L2VNI + gateway locale dans le netns | | Transit inter-subnet d'un VPC | hosts portant des VM du VPC | L3VNI + VRF, IRB symétrique | | Plages publiques | tous les hyperviseurs | L2VNI étendu, gateway sur border routers | Dans le netns d'un VPC, les deux plans coexistent par longest-prefix : le supernet du VPC part dans le L3VNI, la route par défaut part vers la MAC anycast des border routers via le VNI public. Aucun conflit. Les sujets propres au plan public sont traités dans les issues #04, #05, #06 et #08. ## Topologie ``` vrf-C (table 1100) ├── br-l3-C [RMAC host, pas d'IP] ← SVI du L3VNI │ └── vxlan-l3-C [VNI 1100, nolearning] └── veth-l3-C-out [169.254.0.1/30] ← lien routé ╎ ─────────────────────────╎─────────────────────────── netns C veth-l3-C-in [169.254.0.2/30] br-D [gateway 10.1.0.1/24] └── veth-D-in ─╌╌ (L2VNI, inchangé) tap-A ``` **Règle d'invariant** : `vrf-C` contient exactement **deux** interfaces, `br-l3-C` (sans IP) et `veth-l3-C-out` (avec le /30). Elles ne sont **pas** pontées entre elles — c'est la FIB de la VRF qui les relie, et ce lookup *est* le routage inter-subnet. Erreurs à ne pas commettre : - `veth-l3-C-out master br-l3-C` → remet le netns dans le domaine de broadcast du L3VNI : plus de routage sur l'host, collision généralisée du /30 entre tous les hosts, pollution du L3VNI par des RT-2 parasites ; - `vxlan-l3-C` directement dans la VRF sans bridge → pas de terminaison L3, non reconnu comme L3VNI par FRR. Le bridge à un port est un passage obligé du modèle Linux. ## Plan d'allocation (exemple chiffré) | Élément | Valeur | |---|---| | VPC C, supernet | `10.1.0.0/16` | | L3VNI C / ID table de routage | `1100` | | RD | `<IP-VTEP-host>:1100` — **dérivé du VTEP, pas de l'ASN** | | RT | `65000:1100`, commun au VPC | | Transit netns↔host | `169.254.0.0/30`, identique pour tous les VPC | | ASN hyperviseurs / RR | `65001` (commun à tous les hosts) / `65000` | | host1 : VTEP / RMAC | `10.0.0.1` / `02:00:00:00:00:01` | | host2 : VTEP / RMAC | `10.0.0.2` / `02:00:00:00:00:02` | | VIP des RR | `10.0.255.1`, `10.0.255.2` | | Subnet D `10.1.0.0/24`, L2VNI `10100` | host1, VM A `10.1.0.5` | | Subnet E `10.1.1.0/24`, L2VNI `10101` | host2, VM B `10.1.1.5` | Contraintes d'allocation : - **Le RD doit être dérivé de l'IP de VTEP.** Tous les hosts partageant l'ASN `65001`, un RD de la forme `<ASN>:<VNI>` serait identique partout : les annonces concurrentes du même préfixe redeviendraient une seule NLRI, et on perdrait le multipath ainsi que le filet de secours du /24 pendant les migrations. **Contrainte, pas préférence.** - **RD, RT, L3VNI et ID de table déclarés explicitement**, jamais dérivés automatiquement. La dérivation auto du RT dépend de l'ASN local, que `local-as` modifie sur la session RR. - **Supernet contigu par VPC** : la route supernet unique dans le netns suppose que chaque VPC est taillé dans un bloc agrégeable. Voir issue #10. - **RMAC unique par host**, MAC de gateway anycast identique sur tous les hosts portant le même subnet. ## Configuration kernel ### Host1 — création du netns C ```bash ip netns add C ip link add veth-l3-C-out type veth peer name veth-l3-C-in ip link set veth-l3-C-in netns C # côté tenant ip -n C addr add 169.254.0.2/30 dev veth-l3-C-in ip -n C link set veth-l3-C-in mtu 8950 up ip -n C route add 10.1.0.0/16 via 169.254.0.1 dev veth-l3-C-in onlink ip -n C neigh replace 169.254.0.1 lladdr <MAC-veth-l3-C-out> \ dev veth-l3-C-in nud permanent ip netns exec C sysctl -qw net.ipv4.ip_forward=1 # côté underlay : VRF + L3VNI ip link add vrf-C type vrf table 1100 ip link set vrf-C up ip link add br-l3-C type bridge ip link set br-l3-C address 02:00:00:00:00:01 ip link set br-l3-C master vrf-C ip link add vxlan-l3-C type vxlan id 1100 dstport 4789 local 10.0.0.1 nolearning ip link set vxlan-l3-C master br-l3-C ip link set vxlan-l3-C mtu 9000 up ip link set br-l3-C mtu 9000 up ip link set veth-l3-C-out master vrf-C ip addr add 169.254.0.1/30 dev veth-l3-C-out ip link set veth-l3-C-out mtu 8950 up ``` ### Host1 — subnet D (L2VNI, inchangé) ```bash ip link add vxlan-D type vxlan id 10100 dstport 4789 local 10.0.0.1 nolearning ip link add br-D-out type bridge ip link set vxlan-D master br-D-out ip link add veth-D-out type veth peer name veth-D-in ip link set veth-D-out master br-D-out ip link set veth-D-in netns C ip -n C link add br-D type bridge ip -n C link set veth-D-in master br-D ip -n C link set br-D address 02:00:00:aa:00:0d # MAC anycast, identique partout ip -n C addr add 10.1.0.1/24 dev br-D ``` Aucun L2VNI n'est rattaché à la VRF : la gateway est dans le netns. ## Configuration FRR ### Host1 — `/etc/frr/frr.conf` ``` frr version 8.x frr defaults datacenter hostname host1 log syslog informational service integrated-vtysh-config ! vrf vrf-C vni 1100 exit-vrf ! router bgp 65001 bgp router-id 10.0.0.1 no bgp default ipv4-unicast bgp log-neighbor-changes ! ! --- underlay : routeur de cluster, eBGP natif --- neighbor UPLINK peer-group neighbor UPLINK remote-as <ASN-cluster> neighbor UPLINK bfd neighbor <ip-routeur-cluster> peer-group UPLINK ! ! --- overlay : RR, session ramenée en iBGP via local-as --- neighbor RR peer-group neighbor RR remote-as 65000 neighbor RR local-as 65000 no-prepend replace-as neighbor RR update-source 10.0.0.1 neighbor 10.0.255.1 peer-group RR neighbor 10.0.255.2 peer-group RR ! address-family ipv4 unicast neighbor UPLINK activate neighbor UPLINK route-map UNDERLAY-OUT out network 10.0.0.1/32 exit-address-family ! address-family l2vpn evpn neighbor RR activate neighbor RR soft-reconfiguration inbound neighbor RR maximum-prefix 200000 90 restart 15 advertise-all-vni exit-address-family exit ! ! --- contexte d'origination du VPC C : AUCUN neighbor --- router bgp 65001 vrf vrf-C bgp router-id 10.0.0.1 no bgp default ipv4-unicast ! address-family ipv4 unicast redistribute kernel route-map VPC-C-EXPORT maximum-paths ibgp 4 exit-address-family ! address-family l2vpn evpn rd 10.0.0.1:1100 route-target both 65000:1100 advertise ipv4 unicast exit-address-family exit ! ip prefix-list PL-VPC-C seq 10 permit 10.1.0.0/16 le 32 ip prefix-list PL-UNDERLAY seq 10 permit 10.0.0.1/32 ! route-map VPC-C-EXPORT permit 10 match ip address prefix-list PL-VPC-C route-map VPC-C-EXPORT deny 20 ! route-map UNDERLAY-OUT permit 10 match ip address prefix-list PL-UNDERLAY route-map UNDERLAY-OUT deny 20 ! line vty ``` ### Host2 — seules lignes divergentes ``` hostname host2 ! router bgp 65001 bgp router-id 10.0.0.2 neighbor RR update-source 10.0.0.2 address-family ipv4 unicast network 10.0.0.2/32 ! router bgp 65001 vrf vrf-C bgp router-id 10.0.0.2 address-family l2vpn evpn rd 10.0.0.2:1100 ! ip prefix-list PL-UNDERLAY seq 10 permit 10.0.0.2/32 ``` Tout le reste est strictement identique, y compris le RT, le L3VNI, le prefix-list VPC et la route-map d'export. **Trois valeurs à substituer par host** : IP de VTEP (router-id, update-source, RD, prefix-list underlay), RMAC (côté kernel), hostname. ### Route reflector — `/etc/frr/frr.conf` ``` frr version 8.x frr defaults datacenter hostname rr1 log syslog informational service integrated-vtysh-config ! router bgp 65000 bgp router-id 10.0.255.1 no bgp default ipv4-unicast bgp log-neighbor-changes bgp cluster-id 10.0.255.1 ! ! --- hyperviseurs : peering dynamique, aucun ajout de config par host --- neighbor HOSTS peer-group neighbor HOSTS remote-as 65000 neighbor HOSTS update-source 10.0.255.1 bgp listen range 10.0.0.0/16 peer-group HOSTS bgp listen limit 1024 ! ! --- border routers --- neighbor BORDER peer-group neighbor BORDER remote-as 65000 neighbor BORDER update-source 10.0.255.1 neighbor <ip-border-1> peer-group BORDER neighbor <ip-border-2> peer-group BORDER ! address-family l2vpn evpn neighbor HOSTS activate neighbor HOSTS route-reflector-client neighbor BORDER activate neighbor BORDER route-reflector-client exit-address-family exit ! line vty ``` Le RR **réfléchit** les routes, il ne les importe pas : aucune VRF, aucun L3VNI, aucun RT, aucun VTEP. **Ajouter un VPC ne touche jamais à sa configuration**, et le `listen range` fait qu'ajouter un hyperviseur non plus. Interdits sur le RR, chacun provoquant une panne silencieuse : - **`next-hop-self`** : le next-hop d'une route EVPN est le VTEP d'origine. Réécrit, il pointe vers le RR, qui n'est pas un VTEP → trou noir total. - **Route-map touchant aux communautés étendues** : RMAC, RT et L3VNI y sont portés. - **`advertise-all-vni`** : le RR n'est pas un VTEP. `update-source` doit être la VIP Pacemaker, sinon la session casse au basculement. **À vérifier sur la version en usage : `bgp retain route-target all`.** Un RR reçoit des routes dont il n'importe aucun RT ; si la rétention est désactivée il les jette et la réflexion ne fonctionne plus. Le défaut FRR est la rétention, mais ce point doit être confirmé — la panne serait totale et immédiate sur tout VPC nouvellement créé. Voir #01. ## Pourquoi `local-as` sur la session RR Hyperviseurs et RR ont des ASN différents (`65001` / `65000`), la session serait donc eBGP. Quatre problèmes en découlent, dont un bloquant : - **boucle AS-PATH** : tous les hosts partageant `65001`, une route de host1 revient à host2 avec `65001` dans l'AS-PATH et est rejetée. Aucune route EVPN n'est échangée entre hosts ; - `route-reflector-client` est un mécanisme iBGP, sans effet en eBGP ; - réécriture du next-hop par défaut en eBGP → risque de trou noir ; - session multihop à gérer vers une VIP. `local-as 65000 no-prepend replace-as` ramène **cette seule session** en iBGP : le type de session se détermine par voisin, en comparant `remote-as` à l'ASN local effectif. Les autres voisins du host restent en eBGP natif avec `65001`. ## Portée de diffusion des routes tenant `router bgp 65001 vrf vrf-C` ne contient **aucun `neighbor`** : c'est un contexte d'origination, pas une instance BGP supplémentaire. Ce qui décide de la diffusion, c'est l'AF sous laquelle un voisin est activé. | Session | AF activée | Transporte | |---|---|---| | host → RR | `l2vpn evpn` seule | RT-2, RT-3, RT-5 de tous les VPC | | host → routeur de cluster | `ipv4 unicast` seule | underlay uniquement | Deux conditions à ne jamais violer : - **ne jamais activer le routeur de cluster sous `l2vpn evpn`** — il recevrait toutes les RT-5 tenant et pourrait les propager vers le backbone ; - **ne jamais mettre `import vrf vrf-C` dans l'instance par défaut** — c'est la commande de fuite de routes entre VRF. Ni l'une ni l'autre ne produit d'erreur ni de dégradation visible, seulement une fuite : ce sont des cas à détecter par contrôle automatisé de configuration. ## Opérations ### Ajout / suppression de VM ```bash ip route add 10.1.0.5/32 via 169.254.0.2 dev veth-l3-C-out vrf vrf-C ip route del 10.1.0.5/32 vrf vrf-C ``` **`redistribute kernel`, pas `redistribute static`** : une route posée par `ip route add` depuis l'orchestrateur est de type kernel pour zebra. `static` désigne les routes configurées dans FRR. Avec `static`, aucune route VM n'est annoncée, sans message d'erreur. Choix du /32 par VM retenu pour que la migration soit un événement de plan de contrôle échangé, et non une expiration de cache. **Filet de secours** : conserver en permanence l'annonce du /24 par chaque host portant le subnet. Pendant la fenêtre de bascule d'un /32, le trafic tombe sur un host du subnet et repart en L2VNI vers la VM. Un hop de trop pendant quelques secondes, aucune perte. ### Migration de VM **Ordre imposé : retrait du /32 à la source, puis pose à destination.** Dans l'ordre inverse, deux RT-5 identiques coexistent, la sélection peut figer l'ancien chemin, et le trafic tombe dans le vide de façon **stable** — bien pire qu'une micro-coupure. ### Ajout / suppression de VPC Uniquement sur les hosts hébergeant une VM du VPC : VRF + `br-l3-C` + `vxlan-l3-C` + rattachement du veth, puis le bloc `vrf` et `router bgp ... vrf` ci-dessus. Rien sur les autres hosts, rien sur les RR, rien sur les border routers. Créer un subnet ne touche jamais à la config VRF ; créer un VPC ne touche jamais à la config des subnets. Les deux plans sont orthogonaux. ## Effet sur traceroute (accepté) Deux hops supplémentaires, un par extrémité : ``` 1 10.1.0.1 gateway de D, netns C de host1 2 169.254.0.1 veth-l3-C-out, VRF de host1 ← nouveau 3 169.254.0.2 veth-l3-C-in, netns C de host2 ← nouveau 4 10.1.1.5 B ``` Le passage en L3VNI est transparent (l'encapsulation VXLAN ne décrémente pas le TTL interne). Décision : **hops visibles conservés**, pas de filtrage ICMP time-exceeded. Sous-décisions restantes en #10. ## Vérifications ```bash vtysh -c "show bgp l2vpn evpn summary" # sessions internal, pas external vtysh -c "show bgp neighbor 10.0.255.1" # vérifier « internal link » vtysh -c "show bgp l2vpn evpn vni 1100" # L3VNI ↔ vrf-C, RMAC vtysh -c "show bgp l2vpn evpn route type prefix" # RT-5, RD distincts par host ip route show vrf vrf-C # next-hop VTEP + RMAC ip netns exec C ping -M do -s 8922 10.1.1.5 # MTU de bout en bout ``` Si `show bgp neighbor` affiche la session en **external**, le `local-as` n'a pas pris et rien ne fonctionnera entre hosts. **Assertion d'invariant** à automatiser : chaque VRF contient exactement deux membres, `br-l3-C` sans IP et `veth-l3-C-out` avec le /30. La topologie est peu intuitive et se prête au copier-coller approximatif ; ne pas compter sur la relecture humaine. ## Risques L'étanchéité inter-tenant passe d'une propriété du noyau à une propriété de la configuration : le forwarding local reste isolé par la FIB de la VRF, mais la **joignabilité inter-host dépend désormais du RD/RT et du mapping L3VNI↔VRF**. Trois scénarios de fuite silencieuse : RT mal alloué, L3VNI dupliqué ou recyclé trop vite, `import vrf` résiduel. Traité en #02. L'orchestrateur devient source de vérité du routage tenant : une route /32 non retirée après destruction d'une VM est un trou noir qui survit à la VM. Traité en #03. Atténuation structurelle à formaliser dans le dossier d'architecture : le netns est conservé, donc les charges non fiables restent isolées par le noyau. Seul le segment de transit entre hyperviseurs est exposé à la configuration. Autres risques : volumétrie du plan de contrôle (#09), MTU 8950/9000 à revalider (#01), migration depuis l'existant (#11), usurpation MAC/IP au port virtuel (#04). ## Issues liées | # | Sujet | Priorité | |---|---|---| | 01 | Campagne de qualification en lab | **Bloquant** | | 02 | Allocation déterministe des identifiants et test négatif | **Bloquant** | | 03 | Réconciliation orchestrateur ↔ routes tenant | Haute | | 04 | Isolation intra-VNI public entre tenants | **Haute, sécurité** | | 05 | MAC anycast des border routers et mobilité EVPN | Haute | | 06 | Hygiène des RT sur border routers et filtrage backbone | Haute | | 07 | Sortie Internet des VM sans IP publique | Moyenne | | 08 | Robustesse du VNI public multi-DC | Moyenne | | 09 | Volumétrie et dimensionnement du plan de contrôle | Moyenne | | 10 | IPAM : contiguïté des supernets et adressage de transit | Moyenne | | 11 | Migration de l'existant asymétrique → symétrique | Haute | ## Variante non retenue **Session BGP unnumbered entre le netns et l'host** sur le veth, netns en `redistribute connected` : supprime toute écriture de route statique, y compris par VM. Coût : un démon BGP par couple (VPC, host), sur lien point-à-point. À réévaluer si le volume d'écritures devient un problème opérationnel. --- 🛡️ **Garde-fou Voltalis** - **Niveau** : Enterprise Product - **Risques repérés** : étanchéité inter-tenant portée par l'allocation RD/RT/L3VNI et non plus par le noyau ; routes /32 rémanentes (trou noir silencieux) ; séquencement de migration ; réutilisation d'ID sans quarantaine ; volumétrie du plan de contrôle - **Comité IA** : requis, choix d'architecture structurant sur une infrastructure de production multi-tenant - **Avant déploiement** : revue humaine obligatoire, revue de sécurité formelle sur le modèle d'isolation avec test négatif inter-VPC comme critère d'acceptation, veille, revue technique, tests, traçabilité de l'usage de l'IA dans la conception - **Escalade** : Comité IA — Samy Denno (référent IA) · RSSI — securite@voltalis.com Configurations conçues avec assistance IA : relecture humaine et validation en lab obligatoires avant toute application sur un système réel.
Author
Owner

point a regarder :

Campagne de qualification en lab — L3VNI / VRF / iBGP

Priorité : bloquant. Prérequis à tout engagement de design sur #00.

Contexte

La conception #00 repose sur plusieurs comportements de FRR et du noyau que la documentation
ne garantit pas de façon univoque selon les versions. Ils doivent être observés, pas supposés.

Points à qualifier

1. local-as ... no-prepend replace-as sur l'AF l2vpn evpn

Vérifier que la session vers les RR est bien vue comme internal et que les routes EVPN
sont échangées entre hosts partageant l'ASN 65001 sans rejet pour boucle AS-PATH.
Contrôle : show bgp neighbor <RR> doit indiquer un lien internal ; une RT-5 émise par host1
doit apparaître dans show bgp vrf vrf-C ipv4 unicast sur host2.

2. bgp retain route-target all sur les RR

Un RR reçoit des routes dont il n'importe aucun RT. Si la rétention est désactivée il les
jette et la réflexion cesse de fonctionner. Le défaut FRR est la rétention, à confirmer.
Criticité : la panne serait totale et immédiate sur tout VPC nouvellement créé.

3. redistribute kernel dans une VRF

Vérifier qu'une route posée par ip route add ... vrf vrf-C est bien captée et convertie en
RT-5. redistribute static ne fonctionne pas (il ne couvre que les routes configurées dans
FRR) et échoue silencieusement.

4. Sélection de meilleur chemin entre deux RT-5 /32 concurrentes

Simuler une migration : annonce simultanée du même /32 par deux hosts avec des RD distincts.
Observer quel chemin est retenu et combien de temps dure l'incohérence.
Ce test tranche définitivement la stratégie de séquencement de migration.

5. Ajout / retrait à chaud d'une instance VRF sous charge

Vérifier l'absence de reset des sessions existantes, et le comportement de frr-reload.py
sur ces opérations — c'est l'outillage qui peut être brutal, pas la fonctionnalité.

6. SVI de L3VNI alimenté par routes kernel

Vérifier le comportement de FRR quand les préfixes tenant arrivent par route redistribuée
plutôt que par interface connectée dans la VRF.

7. MTU de bout en bout

ip netns exec C ping -M do -s 8922 <ip-distante> doit passer avec veth à 8950 et underlay
à 9000. Valider aussi le comportement de la PMTU discovery.

8. Comportement de la MAC anycast des border routers au flap

Voir #05 : provoquer un basculement de border router et observer si la MAC de gateway est
traitée comme une mobilité, voire gelée par la détection de duplication.

Critère d'acceptation

Les 8 points documentés avec version exacte de noyau et de FRR, résultat observé, et pour
chaque écart un contournement validé. Aucun déploiement avant clôture de cette issue.

point a regarder : # Campagne de qualification en lab — L3VNI / VRF / iBGP **Priorité : bloquant.** Prérequis à tout engagement de design sur #00. ## Contexte La conception #00 repose sur plusieurs comportements de FRR et du noyau que la documentation ne garantit pas de façon univoque selon les versions. Ils doivent être observés, pas supposés. ## Points à qualifier ### 1. `local-as ... no-prepend replace-as` sur l'AF `l2vpn evpn` Vérifier que la session vers les RR est bien vue comme **internal** et que les routes EVPN sont échangées entre hosts partageant l'ASN 65001 sans rejet pour boucle AS-PATH. Contrôle : `show bgp neighbor <RR>` doit indiquer un lien internal ; une RT-5 émise par host1 doit apparaître dans `show bgp vrf vrf-C ipv4 unicast` sur host2. ### 2. `bgp retain route-target all` sur les RR Un RR reçoit des routes dont il n'importe aucun RT. Si la rétention est désactivée il les jette et la réflexion cesse de fonctionner. Le défaut FRR est la rétention, à confirmer. **Criticité : la panne serait totale et immédiate sur tout VPC nouvellement créé.** ### 3. `redistribute kernel` dans une VRF Vérifier qu'une route posée par `ip route add ... vrf vrf-C` est bien captée et convertie en RT-5. `redistribute static` ne fonctionne pas (il ne couvre que les routes configurées dans FRR) et échoue silencieusement. ### 4. Sélection de meilleur chemin entre deux RT-5 /32 concurrentes Simuler une migration : annonce simultanée du même /32 par deux hosts avec des RD distincts. Observer quel chemin est retenu et combien de temps dure l'incohérence. **Ce test tranche définitivement la stratégie de séquencement de migration.** ### 5. Ajout / retrait à chaud d'une instance VRF sous charge Vérifier l'absence de reset des sessions existantes, et le comportement de `frr-reload.py` sur ces opérations — c'est l'outillage qui peut être brutal, pas la fonctionnalité. ### 6. SVI de L3VNI alimenté par routes kernel Vérifier le comportement de FRR quand les préfixes tenant arrivent par route redistribuée plutôt que par interface connectée dans la VRF. ### 7. MTU de bout en bout `ip netns exec C ping -M do -s 8922 <ip-distante>` doit passer avec veth à 8950 et underlay à 9000. Valider aussi le comportement de la PMTU discovery. ### 8. Comportement de la MAC anycast des border routers au flap Voir #05 : provoquer un basculement de border router et observer si la MAC de gateway est traitée comme une mobilité, voire gelée par la détection de duplication. ## Critère d'acceptation Les 8 points documentés avec version exacte de noyau et de FRR, résultat observé, et pour chaque écart un contournement validé. Aucun déploiement avant clôture de cette issue.
Author
Owner

Prochain ticket

Prochain ticket
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
syonad/two#41
No description provided.