VRRP et Linux
Ou comment faire croire aux machines de notre sous-réseau qu’on a plusieurs adresses MAC sur une seule interface (et par la même occasion redonner un peu de vie ici …).
Il existe une implémentation libre du protocole : vrrpd, apparemment plus maintenue à ce jour. On lui préfèrera la version patchée par les contributeurs debian qui amène pas mal d’améliorations et de corrections de bug. Je passe la description détaillée du protocole, on notera que vrrpd respecte la rfc 2338, qui a été mise à jour par la rfc 3768 n’apportant en fait rien de spécial, à part en gros la suppression de l’authentification (tout en concervant la comptabilité avec l’ancienne rfc) et quelques clarifications.
Le principal problème sous linux quand on utilise un protocole de redondance utilisant une adresse MAC virtuelle est le fait qu’on ne puisse attribuer qu’une seule adresse mac par NIC, comme on peut le lire dans ce thread concernant justement VRRP.
Pour une unique instance de VRRP, le problème est donc contourné dans vrrpd de la manière suivante:
- L’adresse MAC de l’interface sur laquelle tourne vrrpd est remplacée en mode Master par l’adresse MAC virtuelle de l’instance VRRP (que l’on notera VMAC par la suite),
- La véritable adresse MAC est rajoutée à la liste des filtres multicast de l’interface, cette astuce permettra à celle-ci d’accepter les paquets envoyées à destination de son adresse IP réelle avec l’adresse MAC réelle.
La première opération est effectuée par le code suivant dans la fonction hwaddr_set ligne 508:
/* change the hwaddr */ memcpy( ifr.ifr_hwaddr.sa_data, addr, addrlen ); /* addr est VMAC */ ifr.ifr_hwaddr.sa_family = AF_UNIX; ret = ioctl(fd, SIOCSIFHWADDR, (char *)&ifr);
Et on retrouve la deuxième dans le code de la fonction rcvhwaddr_op ligne 484:
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); memcpy( ifr.ifr_hwaddr.sa_data, addr, addrlen ); /* addr est ici la véritable adresse MAC de l'interface */ ifr.ifr_hwaddr.sa_family = AF_UNSPEC; ret = ioctl(fd, addF ? SIOCADDMULTI : SIOCDELMULTI, (char *)&ifr);
Ces deux fonctions sont appelées lors d’un changement d’état Backup -> Master et inversement.
Alors si ce hack fonctionne pour une instance de VRRP, on est quand même confronté à un problème en mode Master:
- l’interface VRRP est joignable avec le bon couple VIP / VMAC. Quand un équipement du réseau fait une requête ARP pour connaître la VMAC liée à VIP, elle récupère bien la bonne adresse.
- Par contre si l’on essaye d’envoyer des paquets sur l’ancienne IP, soit notre équipement connait déjà l’adresse MAC et il n’y aura pas de requête ARP, soit il ne la connaît pas, effectue une requête ARP pour l’atteindre et reçoit la VMAC au lieu de la véritable adresse MAC de l’interface. Bref cela fonctionnera, mais bon, ce n’est pas très cohérent …
Je vous laisse imaginer le chaos lorsque l’on tente de lancer plusieurs instances de VRRP sur la même interface (avec des group ids différents) et observer ce qu’il se passe après chaque execution d’un nouveau vrrpd en pingant, sniffant, regarder /proc/net/arp sur les machines distantes, et jeter un oeil aux résultats de la commande ip link show <interface>
Le problème apparait alors clairement: Comment faire pour conserver d’une part l’association adresse IP réelle de l’interface / adresse MAC réelle, et pour une ou plusieurs instances de VRRP l’association adresse IP virtuelle 1..n / adresse MAC virtuelle 1..n ?
Si l’on utilise vrrpd sur une interface de type bridge, il est possible de simuler cette association avec ebtables, dont la syntaxe est très proche d’iptables. Les schémas ici donnent une bonne idée du mécanisme et nous montrent où sont positionnés les hooks ebtables dans le cheminement d’un paquet.
Le but est de tromper la couche ethernet en falsifiant les réponses ARP et en créant les associations IP/MAC que l’on désire avec quelques règles.
On pose quelques variables:
- ROUTER_IF: Nom de l’interface sur laquelle tourne l’instance VRRP
- ROUTER_MAC: Adresse MAC de l’interface
- VRRP_IP: Adresse IP Virtuelle VRRP
- VRRP_MAC: Adresse MAC Virtuelle VRRP
Et on attaque avec la première règle qui va empêcher les reply ARP avec la MAC réelle lorsque l’on reçoit un request ARP pour VRRP_IP (0×0806 désigne l’ethertype ARP.):
$EBTABLES -A OUTPUT \ -p 0X0806 \ --logical-out $ROUTER_IF -s $ROUTER_MAC \ --arp-opcode 2 \ --arp-ip-src $VRRP_IP \ -j DROP
L’option –arp-opcode 2 désigne un paquet ARP reply (ebtables -h arp). Dans cette règle on matche les ARP reply générés par la couche ARP du kernel ayant pour source l’ip VRRP_IP.
On veut envoyer à la place la bonne VMAC:
$EBTABLES -t nat -A PREROUTING \ -p 0x0806 --logical-in $ROUTER_IF \ --arp-opcode 1 --arp-htype 1 --arp-ptype 0x0800 \ --arp-ip-dst $VRRP_IP \ -j arpreply --arpreply-mac $VRRP_MAC --arpreply-target ACCEPT
Ici on matche les ARP request pour VRRP_IP, et on génère avec -j arpreply un reply ARP avec la mac VRRP_MAC associée à VRRP_IP.
Maintenant que l’on contrôle les request/reply ARP, on veut aussi que les paquets sortant avec l’adresse IP virtuelle VRRP_IP possèdent l’adresse MAC virtuelle VRRP_MAC:
$EBTABLES -t nat -A POSTROUTING \ -p 0x0800 --logical-out $ROUTER_IF \ --ip-src $VRRP_IP \ -j snat --to-source $VRRP_MAC --snat-target ACCEPT
On veut faire l’inverse sur les paquets entrant, c’est à dire qu’on va changer l’adresse MAC virtuelle VRRP_MAC du paquet en adresse MAC ROUTER_MAC afin que le bridge ne le rejette pas:
$EBTABLES -t broute -A BROUTING \ --logical-in $ROUTER_IF -p 0x0800 -d $VRRP_MAC \ -j redirect
En utilisant la table broute, on change au plus tôt l’adresse MAC.
Le redirect signale au bridge de continuer le traitement du paquet après avoir changer l’adresse MAC.
Et voilà, on a toutes les règles qu’il nous faut, la magie est en place. On va pouvoir lancer plusieurs instances de VRRP indépendantes les unes des autres sur une interface de type bridge, en respectant la RFC et l’intégrité du niveau ethernet.
Pour que vrrpd applique ses règles automatiquement, on désactive sa gestion de l’adresse MAC virtuelle avec l’option “-n” et on place nos règles dans le script /etc/vrrpd/vrrp_switch dans le case “$1″ en différenciant le cas “master” du cas “slave” (fonctionnalité rajouté par les contributeurs Debian, ce script est appelé à chaque changement d’état). Ne pas oublier d’effacer les règles dans le cas “slave”.
Alors certes cette méthode oblige à utiliser une interface de type bridge, mais on peut se dire que si on utilise VRRP sur une box, il y a de grandes chances que l’interface concernée soit déjà un bridge qui agrège plusieurs interfaces eth*. Sinon il existe sûrement une meilleure méthode qui permette les mêmes manipulations au niveau ARP, si quelqu’un a un lien je suis preneur.
Un autre avantage notable c’est que l’on ne tombe plus l’interface VRRP pour changer les adresses MAC (cf les deux fonctions plus haut) et on ne perd plus du coup les règles de routage liées à l’interface (alors oui il y a bien un patch pour ça mais bon…).
Il y aura sûrement d’autres articles sur vrrpd, il y a encore quelques trucs à bidouiller pour l’améliorer et je reviendrai sur l’illustration du bon fonctionnement de cette méthode, le post d’aujourd’hui est assez long comme ça
…
NB : Pour aller plus loin avec ARP en général, je suis tombé récemment sur une page très interessante et un petit utilitaire du même auteur pour faire pleins de choses sympas avec ARP.

Bonjour,
Article vraiment très intéressant, à mon avis il manque juste un petit éclaircissement, dans quel cas précis peut-on avoir besoin de plusieurs MAC sur la même NIC avec Vrrpd ? En faisant tourner plusieurs instances il est parfaitement possible de faire tourner la VMAC sur une instance, et de la désactiver pour les autres (option -n), dans ce cas toutes les ip virtuelles partagent la même MAC
Sinon personnellement j’utilise la solution de l’ip aliasing que je trouve plus élégante et très facilement scriptable
Je me permet de vous citer:
« Par contre si l’on essaye d’envoyer des paquets sur l’ancienne IP, soit notre équipement connaît déjà l’adresse MAC et il n’y aura pas de requête ARP, soit il ne la connaît pas, effectue une requête ARP pour l’atteindre et reçoit la VMAC au lieu de la véritable adresse MAC de l’interface. Bref cela fonctionnera, mais bon, ce n’est pas très cohérent »
Je trouve ça au contraire très cohérent puisque Vrrpd remplace la mac de l’interface NIC par la vmac l’ancienne mac ne doit plus être connue sur le réseau, en cas de panne ça permet une bascule transparente.
Lors de l’interruption de la machine maître le backup récupère la MAC ainsi que les IPs associés et ceci de manière totalement transparent pour les hôtes du réseau.
Mais peut-être n’ai-je pas bien compris le sens de votre article dans ce cas désolé pour le commentaire, en tout cas j’attends le suite avec impatience
Bonjour FredB,
Merci de votre intérêt pour ce post.
En fait le but de l’article est de montrer comment avoir un comportement de Vrrpd respectant la RFC dans l’utilisation de l’adresse MAC virtuelle (enfin, tel que je l’ai comprise).
Je reprends les deux points que vous avez soulevés:
Effectivement la solution que vous proposez fonctionne même si l’on n’utilise qu’une adresse MAC commune à toutes les instances, cela rejoint si je ne me trompe pas la solution de l’IP Aliasing pour assurer la redondance. Dans les deux cas, la bascule sera transparente pour les hôtes du réseau qui ont pour gateway l’Ip VRRP.
Ceci dit, la RFC demande normalement qu’il y ait une association entre une ou plusieurs IPs d’une instance VRRP avec une adresse MAC virtuelle. Cette derniere doit contenir le VRID de l’instance VRRP en respectant ce format : 00-00-5E-00-01-{VRID} (rfc2338.7.3).
On peut lire aussi à divers endroit que l’adresse MAC virtuelle doit être utilisée dans les échanges entre des routeurs d’une même instance VRRP (rfc.2338.3.0, 7.2.). Plus globalement, le pragraphe 2.4. “Efficient Operation over Extended LANs” explique pourquoi VRRP utilise une adresse MAC virtuelle propre à une instance.
À l’inverse, ce point n’est pas explicite dans la RFC mais je pense que le protocole VRRP n’est pas sensé remplacer la MAC de l’interface NIC par la VMAC et occulter l’ancienne, même si Vrrpd se comporte comme cela.
VRRP rajoute une fonctionnalité de redondance mais ne devrait pas modifier la configuration réseau existante des routeurs qu’il regroupe.
On peut le vérifier sur des équipements réseaux propriétaires (je faisais des tests avec un routeur Cisco 800) : La VMAC est uniquement utilisée avec la VIP pour les postes communiquant avec le routeur virtuel de l’instance VRRP. Les routeurs de l’instance VRRP restent toujours accessibles sur leur adresse IP propre, associée avec l’adresse MAC physique de leur carte, qu’ils soient en mode Master ou Backup.
Même si en pratique on peut s’en passer, il me parait plus propre de conserver l’association IP réelle / Mac réelle tout en ayant les associations VIP/VMAC, d’une part pour avoir une VMAC par instance VRRP et respecter la RFC et de l’autre ne pas modifier la topologie du réseau et provoquer trop de changement au niveau Ethernet. L’article tente de montrer comment réaliser cela avec un bridge et ebtables.
J’espère avoir un peu éclairci cet article peut-être un peu fouilli
Bonjour et merci pour vos réponses.
Effectivement Vrrpd est tellement souple sous Linux qu’il existe plusieurs façon de l’utiliser. Pour ma part maintenant(j’utilise Vrrpd sur beaucoup de machines différents) je ne me sert en générale plus de la VIP, je préfère mettre une IP bidon du genre 10.1.1.1 et utiliser dans le script de passage en state Master des ip alias, en n’oubliant pas de les démonter dans le script backup, ça me permet d’avoir tout simplement des interfaces manipulables et visible avec ifconfig, par exemple pouvant être tagger dans un vlan, en conservant l’avantage de la bascule de MAC. Pour moi le principale défaut de Vrrp par rapport à d’autres solutions était de ne superviser qu’une interface à la fois ce qui peut provoquer en cas de problème des flux asymétriques sur une machine ayant plusieurs NIC mais c’est une autre histoire … J’avoue ne pas avoir comparé les solution de failover sous Linux depuis un moment mais Vrrpd m’avait semblé le plus intéressant à l’époque.