1. Contexte et objectif

Le réseau domestique était un LAN plat unique (192.168.10.0/24) où tous les équipements partageaient le même domaine de diffusion :

  • appliances (routeur/firewall OPNsense, AP, répéteur WiFi, switch)
  • hyperviseurs Proxmox
  • services homelab
  • clusters Kubernetes
  • NAS Synology
  • IoT
  • appareils personnels (téléphones, tablettes, consoles de jeux)

Objectif : introduire une segmentation VLAN par usage pour isoler les zones de confiance et préparer une approche zero trust.

1.0 Matériel utilisé

ÉlémentDétail
Routeur/firewallOPNsense 26.7.1_1 (FreeBSD, PC Engines APU4 — AMD GX-412TC)
Machine de testALIX 2d3 (PC Engines) + OpenBSD 7.7
AP WiFiPC Engines APU2 — OpenWrt 24.10.1 (x86/64, AMD GX-412TC)
RépéteurASUS RT-AX59U — OpenWrt 25.12.5 (mediatek/filogic, ARMv8)
SwitchMikroTik CSS610-8G-2S+ (SwOS)
HyperviseursProxmox VE (pve0-4)
NASSynology DiskStation
DNS/DHCPUnbound + KEA (OPNsense)

1.1 Topologie physique initiale

┌───────────┐
│  Livebox  │
│  .1.1 WAN │
└─────┬─────┘
      │ igb0
┌─────┴───────────────────────────────────────────────────────────────┐
│  OPNsense (.1) — FreeBSD                                            │
│                                                                     │
│  igb0 ── WAN                                                        │
│                                                                     │
│  bridge0 = LAN (.10.1)  [igb1 + igb2 + igb3]                        │
│     │               │               │                               │
│  igb1 (libre)    igb2 (AP)      igb3 (pve1)                         │
│     │               │               │                               │
└─────┴───────────────┼───────────────┼───────────────────────────────┘
                      │               │
                      │               └───── pve1 (.11)
                      │                     (Proxmox)
                      │
┌─────────────────────┴───────────────────────────────────────────────┐
│  AP OpenWrt (.2)                                                    │
│  br-lan (bridge plat, pas de VLAN)                                  │
│  eth0 ── libre                                                      │
│  eth1 ← igb2                                                        │
│  SSIDs: Vince-AC (5 GHz), Vince-N (2.4 GHz)                         │
└──────┬──────────────────────────────────────────────────────────────┘
       │ [WiFi, Relay Bridge (relayd)]
       │ phy0-ap0 ~~~~~~~~~~  wwan (STA)
┌──────┴──────────────────────────────────────────────────────────────┐
│  Répéteur (.254) — relayd                                           │
│  br-lan [lan1+lan2+lan3+wwan]                                       │
│  wwan (STA) ← [WiFi relayd]                                         │
│  (relaye le LAN sans VLAN, trame complète)                          │
└──────┬──────────────────────────────────────────────────────────────┘
       │ lan1 (filaire)
┌──────┴──────────────────────────────────────────────────────────────┐
│  Switch CSS610 (.5) — SwOS                                          │
│  (LAN transparent, PVID 1 — pas de VLAN configuré)                  │
└──┬──────┬──────┬──────┬──────┬──────┬───────────────────────────────┘
   │      │      │      │      │      │
pve0(.10) pve2(.12) pve3(.13) pve4(.14) diskstation(.3)
(Proxmox) (Proxmox) (Proxmox) (Proxmox) (NAS synology)

1.2 Mapping des ports

OPNsense (.1) :

PortConnexionRôle
igb0WAN (Livebox)Internet
igb1Libre (initialement) → port de test VLAN (ALIX)Sorti du bridge pour les tests
igb2AP OpenWrtPort de production VLAN
igb3pve1 (direct)Chemin filaire alternatif

AP OpenWrt (.2) :

ÉlémentConnexionRôle
eth0LibrePort physique disponible
eth1igb2 (OPNsense)Uplink filaire
br-laneth0 + eth1Bridge principal
phy0-ap0[WiFi 5 GHz]Radio 5 GHz
phy1-ap0[WiFi 2.4 GHz]Radio 2.4 GHz
Vince-AC5 GHzSSID principal
Vince-N2.4 GHzSSID secondaire

Répéteur (.254) :

ÉlémentConnexionRôle
lan1Switch MikroTik (port 1)Port switch
lan2ALIX vr1Port test
lan3LibrePort disponible
br-lanlan1 + lan2 + lan3 + wwanBridge principal, 192.168.10.254
wwan (STA)[WiFi] → APLien uplink WiFi

Switch MikroTik CSS610 (.5) :

PortConnexionRôle
1Répéteur (lan1)Uplink
2pve2Hyperviseur
3pve0Hyperviseur
4pve3Hyperviseur
5pve4Hyperviseur
6NAS (Synology)Stockage
7ALIX (vr0)Test VLAN
8ALIX (vr1)Test VLAN

1.3 Machine de test (ALIX 2d3 — PC Engines, OpenBSD 7.7)

🎯 Rôle de la machine de test : cette ALIX sert à valider pas à pas le déploiement des VLANs sur chaque équipement. Elle est déplacée et rebranchée sur les différents points du réseau (OPNsense, répéteur, switch…) afin de tester la connectivité avec ou sans VLAN à chaque étape du rollout progressif.

InterfaceMACUsage
vr000:0d:b9:1b:aa:4cSans VLAN (192.168.10.23) — tests LAN natif
vr100:0d:b9:1b:aa:4dAvec VLAN — support des interfaces vlan
vr200:0d:b9:1b:aa:4eLibre

Config réseau (OpenBSD 7.7, fichiers /etc/hostname.*) :

FichierContenuUsage
/etc/hostname.vr0inet autoconfSans VLAN — DHCP sur le LAN natif
/etc/hostname.vr1inet autoconfSupport physique des VLANs
/etc/hostname.vlanXXvlan XX vlandev vr1 + dhcpVLAN XX (les tags précis seront définis en section 4.2)

Méthode de test : selon l’équipement à valider, on branche vr0 (sans VLAN, accès LAN natif) ou vr1 (avec VLAN, via les interfaces vlanXX) et on vérifie l’obtention d’une IP DHCP + la connectivité vers le gateway du VLAN concerné.

1.4. Modèle OSI — la pierre angulaire

Le modèle OSI (Open Systems Interconnection) découpe la communication réseau en 7 couches. C’est la pierre angulaire pour comprendre ce projet : il permet de situer précisément les trames Ethernet, les trames WiFi et le VXLAN, et de saisir pourquoi le VXLAN est la solution retenue.

CoucheNomExemplesRôle
7ApplicationHTTP, DNS, SSHServices applicatifs
6PrésentationTLS, SSLChiffrement / format
5SessionTLSGestion des sessions
4TransportTCP, UDPFiabilisation, ports
3RéseauIP, ICMPRoutage (L3)
2LiaisonEthernet, WiFi (802.11), 802.1QTrames (L2)
1PhysiqueCâble, radioSupport physique

Où se situent les éléments clés de ce projet :

ÉlémentCouche
Trame Ethernet (+ tag 802.1Q)L2
Trame WiFi (802.11)L2 (structure différente de l’Ethernet)
IP (routage)L3
UDP (transport VXLAN)L4
VXLAN (encapsule une trame L2 dans UDP/L3)Overlay L2 sur L3

Le point clé : le VXLAN encapsule une trame Ethernet L2 complète (avec son tag 802.1Q) dans un paquet UDP (L4) transporté par IP (L3). C’est un overlay L2 sur L3 : il fait voyager la trame L2 à travers un réseau L3, ce qui permet de transporter les VLANs sur le WiFi (qui ne gère pas le 802.1Q en natif — voir section 6.1).

2. Mode opératoire — rollout progressif

🎯 Approche : valider chaque maillon de la chaîne réseau un par un, sans jamais toucher plusieurs équipements en même temps, afin de pouvoir revenir en arrière à tout moment.

  1. Backup de l’équipement concerné avant toute modification
  2. Configurer un seul maillon (ex: OPNsense → VLANs)
  3. Valider avec la machine de test ALIX (déplacée sur ce maillon) : obtention IP DHCP + ping vers la gateway, avec et sans VLAN
  4. Rollback si échec (restauration de la config)
  5. Passer au maillon suivant (AP → répéteur → switch) une fois le précédent validé

Cette démarche s’applique à la configuration réseau sur chaque équipement (OPNsense, AP, répéteur, switch) et permet de déployer sans risque de coupure globale.


3. Audit réseau (lecture seule)

3.1. Contexte et outils utilisés

Avant toute modification, un audit complet a été réalisé :

🛡️ Contexte de l’audit : cet audit a été réalisé par un agent IA (agent Hermes de Nous Research) sur mandat de Vincent. Les connexions aux APIs OPNsense et Proxmox ont été faites via des tokens API en lecture seule (aucune modification possible), et l’exploration a été complétée par des requêtes ARP/ping et de la navigation web en lecture seule. Aucun équipement n’a été modifié pendant cette phase.

OutilDonnées collectées
API OPNsense (read-only)Version (26.7.1_1), règles firewall, gateways, WireGuard, Unbound, compteurs
API ProxmoxNœuds (pve0-4), VMs/LXC, interfaces réseau
ARP / ping / OUI lookupCartographie des hôtes, identification constructeurs
Navigation webIdentification AP (LuCI), répéteur (LuCI), Synology (DSM)

3.2. Commandes d’audit utilisées

# API OPNsense (token read-only)
hermes@host:~$ curl -sk -u "$KEY:$SECRET" "https://192.168.10.1/api/core/firmware/info"
hermes@host:~$ curl -sk -u "$KEY:$SECRET" "https://192.168.10.1/api/firewall/filter/searchRule"
hermes@host:~$ curl -sk -u "$KEY:$SECRET" "https://192.168.10.1/api/unbound/settings/get"

# API Proxmox (token hermes)
hermes@host:~$ curl -sk -H "Authorization: PVEAPIToken=hermes@pve!hermes=$SECRET" \
  "https://192.168.10.10:8006/api2/json/nodes"
hermes@host:~$ curl -sk -H "Authorization: PVEAPIToken=hermes@pve!hermes=$SECRET" \
  "https://192.168.10.10:8006/api2/json/cluster/resources?type=vm"

# ARP local
hermes@host:~$ ip neigh show

# Identification OUI (maclookup.app)
# 00:0d:b9 = PC Engines (APU, ALIX)
# 58:9c:fc = FreeBSD Foundation (OPNsense)
# 00:0d:b9 = MikroTik (historique)

Exemples de résultats obtenus :

# Exemple 1 — Version du firmware OPNsense
$ curl -sk -u "$KEY:$SECRET" "https://192.168.10.1/api/core/firmware/info" | jq '.product_version'
"26.7.1_1"

# Exemple 2 — Nombre de règles firewall actives
$ curl -sk -u "$KEY:$SECRET" "https://192.168.10.1/api/firewall/filter/searchRule" | jq '.rows | length'
57

# Exemple 2b — Détail d'une règle firewall présente dès le départ (confiance implicite du LAN)
$ curl -sk -u "$KEY:$SECRET" "https://192.168.10.1/api/firewall/filter/searchRule" | jq '.rows[] | select(.description=="Default allow LAN to any rule") | {action, source_net, destination_net, protocol, description, log, enabled}'
{
  "action": "pass",
  "source_net": "lan",
  "destination_net": "any",
  "protocol": "any",
  "description": "Default allow LAN to any rule",
  "log": 0,
  "enabled": 1
}

# Exemple 3 — Cartographie des hôtes (ARP + identification OUI)
$ ip neigh show
192.168.10.2   dev br-lan lladdr 00:0d:b9:...  REACHABLE   # AP — PC Engines
192.168.10.3   dev br-lan lladdr 00:11:32:...  REACHABLE   # NAS — Synology
192.168.10.10  dev br-lan lladdr XX:XX:XX:...  REACHABLE   # pve0 — Proxmox
192.168.10.254 dev br-lan lladdr XX:XX:XX:...  REACHABLE   # Répéteur — ASUS

3.3. Synthèse de l’audit

Découvertes importantes

  1. Topologie en chaîne (pas une étoile) : OPNsense → AP → répéteur WiFi → switch → pve0/2/3/4. Le segment AP→répéteur passe par le WiFi, ce qui a des implications fortes pour le VLAN (voir section 6).
  2. pve1 est connecté en direct sur l’interface igb3 d’OPNsense (filaire, pas via le switch ni le WiFi) — un chemin de test propre pour les VLANs sans dépendre du WiFi.
  3. Un seul sous-réseau 192.168.10.0/24 (LAN plat) héberge tous les équipements sans aucune segmentation : OPNsense, 5 hyperviseurs Proxmox, clusters K8s, NAS, IoT, appareils personnels — tous dans le même domaine de diffusion.
  4. Aucune règle firewall entre les équipements du LAN : les flux internes sont totalement libres (confiance implicite), seul le trafic WAN est filtré. Un appareil IoT compromis peut joindre directement un hyperviseur ou le NAS.
  5. Services exposés sur Internet sans isolation : Caddy (.22) est exposé en WAN (443) dans le même segment que les machines internes — un compromis de Caddy donnerait accès à tout le LAN.
DonnéeRésultat
Version OPNsense26.7.1_1
Règles firewall57 actives
Équipements identifiésOPNsense (.1), AP (.2), NAS (.3), switch (.5), pve0-4 (.10-.14), haos (.20), Caddy (.22), répéteur (.254), etc.
Constructeurs (OUI)PC Engines (APU/ALIX), FreeBSD Foundation (OPNsense), Synology, MikroTik, ASUS

🗺️ Contexte : avant segmentation, tout est à plat dans un même réseau 192.168.10.0/24 — les équipements fixes, les VMs/CTs, les clusters K8s, le NAS, l’IoT et les appareils personnels cohabitent sans aucune séparation. Le range DHCP initial alloué par OPNsense/KEA était 192.168.10.100 → 192.168.10.199 (les IPs < .100 sont des adresses statiques/machines).

IPÉquipementRôle
.1OPNsenseRouteur/firewall (FreeBSD)
.2AP OpenWrtPoint d’accès WiFi (PC Engines)
.3Synology DiskStationNAS
.5Switch MikroTik CSS610Switch (SwOS)
.10pve0Hyperviseur Proxmox
.11pve1Hyperviseur Proxmox (direct sur igb3)
.12pve2Hyperviseur Proxmox
.13pve3Hyperviseur Proxmox
.14pve4Hyperviseur Proxmox
.20haosHome Assistant
.22CaddyReverse proxy (exposé WAN 443)
.142devboxVM Ubuntu
.184/.131laptop / iPhoneAppareils personnels
.187hermesagentAgent d’infra
.189/.191/.192prometheus/exporter/grafanaMonitoring
.203ai-clusterNœud K8s Talos (RTX 3060)
.230-.235LB CiliumLoad balancers K8s
.254Répéteur OpenWrtRépéteur WiFi

4. Réflexion sur la segmentation

4.1. Question de départ

« Comment utiliser 192.168.20.0/24 pour mes clusters K8s ? »

Rapidement élargie en : segmentation complète par usage, pas seulement un VLAN K8s.

Le point de départ était lié à la complexité d’adressage K8s : avec 2-3 clusters Kubernetes (home, ai-cluster), et Cilium en CNI avec ses Load Balancers (LBs), l’adressage devient rapidement compliqué à gérer sans convention ni segmentation :

  • IPs des nodes : control plane (CP) + workers de chaque cluster
  • IPs des services : plages de services Kubernetes
  • IPs des LBs Cilium : plage dédiée pour les load balancers (ex: *.ai → LB .230-.235)

Sans convention d’adressage ni segmentation, ces plages se mélangent et deviennent impossibles à distinguer/mémoriser — d’où l’idée initiale de réserver un sous-réseau dédié aux clusters, avant d’élargir à une segmentation complète par usage.

4.2. Plan VLAN retenu

🏷️ Remarque : les VLANs DMZ, IOT et GUEST sont des catégories assez standard dans les segmentations réseau par VLAN — on les retrouve dans de nombreuses configurations (entreprises, homelabs, documentation Cisco/Unifi/MikroTik…). Leur rôle est bien établi :

  • DMZ : isoler les services exposés à Internet (réduit la surface d’attaque)
  • IOT : contenir les appareils peu fiables (objets connectés) dans un segment contrôlé
  • GUEST : offrir un accès Internet aux invités, sans accès au réseau interne

Le plan ci-dessous s’inscrit donc dans des bonnes pratiques reconnues, appliquées à l’échelle d’un homelab.

TagNomPlageUsage
10USER192.168.10.0/24Appareils réels + gestion (OPNsense, pve, switch, AP, NAS)
20DMZ192.168.20.0/24Services exposés Internet (Caddy)
30IOT192.168.30.0/24Appareils IoT (haos, équipements non fiables)
40GUEST192.168.40.0/24Invités / appareils non fiables
50HOMELAB192.168.50.0/24Services internes + clusters K8s

4.3. Décisions clés

DécisionRaison
K8s dans HOMELAB (pas de VLAN dédié)Un seul sous-réseau 192.168.50.0/24 dans HOMELAB est largement suffisant. C’est un homelab : on n’y aura jamais un cluster K8s avec des centaines de nœuds. Le /24 offre 250 adresses utilisables, plus qu’assez pour les nodes + services + LBs Cilium. Réduire la complexité, K8s derrière le même firewall, NetworkPolicies Cilium gèrent déjà l’interne.
NAS dans USER avec accès HOMELAB→USERAppareil de confiance, les services doivent y accéder
Zero trust en phase 2La mise en place des VLANs est déjà assez complexe — il faut y aller par étapes. Le durcissement des règles firewall (zero trust) sera fait en phase 2, une fois tous les flux réseau validés et stables.

4.4. Matrice de flux cible (zero trust)

Matrice présentée au format règle firewall (Source → Destination → Ports → Action), proche de la configuration OPNsense :

#SourceDestinationProtocole / PortsAction
1USER (10)Internet*Pass
2USER (10)DMZ (20)TCP 443🟡 Pass (accès services)
3HOMELAB (50)Internet*Pass
4HOMELAB (50)USER (10)TCP 8006, 22, 443🟡 Pass (gestion)
5DMZ (20)Internet*Pass
6DMZ (20)HOMELAB (50)TCP (services backends)🟡 Pass (Caddy → backends)
7IOT (30)InternetUDP 53, TCP 443🟡 Pass (mises à jour)
8IOT (30)USER/HOMELAB/DMZ*🚫 Block
9GUEST (40)Internet*Pass
10GUEST (40)LAN (tout sauf Internet)*🚫 Block

⚠️ Note : les ports/protocoles sont des valeurs cibles indicatives (basées sur les services connus : Caddy 443, Proxmox 8006…). Le détail exact des flux sera affiné en phase 2 (durcissement zero trust).


5. Mise en place des VLANs sur OPNsense

5.0. Préparation

  • Backup OPNsense : System → Configuration → Backups → Download configuration (fichier XML)
  • Identifier les ports physiques et leurs usages
  • Préparer la machine de test (ALIX/OpenBSD)

5.1. Le point technique fondamental

Sur OPNsense/FreeBSD, un bridge logiciel (if_bridge) n’est pas VLAN-aware comme un switch matériel. On ne peut pas faire passer un trunk 802.1Q dans un bridge brut. La méthode correcte :

Créer des interfaces VLAN enfants sur chaque port physique, puis un bridge dédié par VLAN.

5.2. Architecture cible (bridge-par-VLAN)

                         OPNsense
                          │
       ┌──────────────────┼───────────────────────┐
       │                  │                       │
     igb1 (test)        igb2 (AP)              igb3 (pve1)
       │                  │                       │
  ┌────┴─────┐       ┌────┴─────┐            (bridge0 natif)
  │ brut     │       │ brut     │
  │ vlan0.1.X│       │ vlan0.2.X│
  └────┬─────┘       └────┬─────┘
       │                  │
       ▼                  ▼
┌─────────────────────────────────────┐
│  bridge0  = igb2(brut) + igb3(brut) │ ← VLAN 10 USER (natif, non taggé)
│  bridge1  = vlan0.1.20 + vlan0.2.20 │ ← VLAN 20 DMZ  → SVI 192.168.20.1
│  bridge2  = vlan0.1.30 + vlan0.2.30 │ ← VLAN 30 IOT  → SVI 192.168.30.1
│  bridge3  = vlan0.1.40 + vlan0.2.40 │ ← VLAN 40 GUEST→ SVI 192.168.40.1
│  bridge4  = vlan0.1.50 + vlan0.2.50 │ ← VLAN 50 HOMELAB → SVI .50.1
└─────────────────────────────────────┘

5.3. Convention de nommage FreeBSD

ÉlémentFormatExemple
Interface VLANvlan0.<parent>.<tag>vlan0.1.20 = tag 20 sur igb1
Bridgebridge<N> (auto)bridge1 (numéro = ordre de création, pas le VLAN)
Assignation OPNsenseopt<N>opt8 = DMZ

Piège rencontré : impossible de nommer librement (vlan20 refusé, bridge20 refusé). Le nom système est imposé par FreeBSD, la Description sert de nom logique.

5.4. Création des VLANs et des bridges

Objectif : disposer d’un port isolé pour tester les VLANs sans risque.

Avant :

bridge0 = igb1 + igb2 + igb3

Action : retirer igb1 de bridge0 (Interfaces → Bridges → bridge0 → Edit).

Après :

bridge0 = igb2 + igb3   (VLAN 10 natif, inchangé)
igb1 = port libre → port de test VLAN

Vérification : l’AP (igb2) et pve1 (igb3) continuent de fonctionner — aucun impact réel (igb1 était vide).

Créer les VLANs sur le port de test + brancher l’ALIX

Interfaces → Other Types → VLAN → Add (×4) :

#TagParentDescriptionDevice généré
120igb1DMZvlan0.1.20
230igb1IOTvlan0.1.30
340igb1GUESTvlan0.1.40
450igb1HOMELABvlan0.1.50

Save + Apply après chaque création.

VLANs OPNsense

Interfaces → Assignments → Add (×4) : activer sans IP (membres de bridge).

AssignationDevice
opt3vlan0.1.20
opt5vlan0.1.30
opt6vlan0.1.40
opt7vlan0.1.50

Schéma à cette étape (test filaire sur igb1) :

                          OPNsense
                           │
     ┌─────────────────────┼──────────────────────┐
     │                     │                      │
   igb1 (test)           igb2 (AP)              igb3 (pve1)
     │                     │                      │
   vlan0.1.20-50          (brut)                 (brut)
     │                     │                      │
     │               AP OpenWrt .2               pve1 .11
     │                     │
     │               [WiFi] Répéteur .254
     │                     │
     │               Switch MikroTik
     │                     │
     │               pve0/2/3/4
     │
  ┌──┴──────────┐
  │ ALIX (OpenBSD) │
  │ vr1 → igb1     │  ← interface de test VLAN
  │ vr0 → AP eth0  │  ← LAN témoin 192.168.10.23
  └───────────────┘

Créer les bridges VLAN

Interfaces → Other Types → Bridge → Add (×4) :

BridgeMembresIP (SVI)
bridge1vlan0.1.20192.168.20.1/24
bridge2vlan0.1.30192.168.30.1/24
bridge3vlan0.1.40192.168.40.1/24
bridge4vlan0.1.50192.168.50.1/24

Configurer chaque bridge (Interfaces → [bridge]) :

ParamètreValeur
Enable☑️
IPv4 Configuration TypeStatic IPv4
IPv4 address(voir tableau)
Block private networks❌ décocher
Block bogon networks❌ décocher
DescriptionDMZ / IOT / GUEST / HOMELAB

Vérification (Diagnostics → Ping depuis OPNsense) : les 4 IPs gateway répondent.

5.5. Étendre les VLANs à igb2 (AP)

Objectif : faire transiter les VLANs sur le port réel vers l’AP.

Interfaces → Other Types → VLAN → Add (×4) :

TagParentDescriptionDevice
20igb2DMZ (AP)vlan0.2.20
30igb2IOT (AP)vlan0.2.30
40igb2GUEST (AP)vlan0.2.40
50igb2HOMELAB (AP)vlan0.2.50

Assigner (sans IP) puis ajouter aux bridges existants :

BridgeMembres après ajout
bridge1 (DMZ)vlan0.1.20 + vlan0.2.20
bridge2 (IOT)vlan0.1.30 + vlan0.2.30
bridge3 (GUEST)vlan0.1.40 + vlan0.2.40
bridge4 (HOMELAB)vlan0.1.50 + vlan0.2.50

Schéma à cette étape :

                          OPNsense
                           │
     ┌─────────────────────┼──────────────────────┐
     │                     │                      │
   igb1 (test)           igb2 (AP)              igb3 (pve1)
     │                     │                      │
   vlan0.1.20-50      vlan0.2.20-50             (brut)
     │                     │                      │
     │  bridge1-4          │  bridge1-4           │
     └────────┬────────────┘                      │
              │                                   │
         AP OpenWrt .2                     pve1 .11
         (vlan_filtering)                    (bridge0)
              │
         [WiFi] Répéteur .254
              │
         Switch MikroTik
              │
         pve0/2/3/4

Vérification de la structure (SSH OPNsense) :

# Vérifier les membres et IP de chaque bridge (sur OPNsense)
for b in bridge0 bridge1 bridge2 bridge3 bridge4; do
  echo "=== $b ==="
  ifconfig $b 2>/dev/null | grep -E "member|inet "
done

Résultat attendu :

bridge0 = igb2, igb3                    (10.1)
bridge1 = vlan0.1.20 + vlan0.2.20       (20.1)
bridge2 = vlan0.1.30 + vlan0.2.30       (30.1)
bridge3 = vlan0.1.40 + vlan0.2.40       (40.1)
bridge4 = vlan0.1.50 + vlan0.2.50       (50.1)

Bridges OPNsense

5.6. Configurer KEA DHCP (subnets par VLAN)

Services → Kea DHCP → Subnets → Add Subnet (×4) :

SubnetPoolGatewayDNS
192.168.20.0/24.100 - .199192.168.20.1192.168.10.1
192.168.30.0/24.100 - .199192.168.30.1192.168.10.1
192.168.40.0/24.100 - .199192.168.40.1192.168.10.1
192.168.50.0/24.100 - .199192.168.50.1192.168.10.1

Chaque subnet : Add → Pool (range) → Options (Router = gateway, DNS = 192.168.10.1).

Subnets DHCP Kea OPNsense par VLAN

5.7. Règles firewall par VLAN

Firewall → Rules → [chaque VLAN] → Add :

InterfaceSourceDestinationProtoDescription
opt8 (DMZ)opt8192.168.20.1ICMPTest ping gateway
opt9 (IOT)opt9192.168.30.1ICMPTest ping gateway
opt10 (GUEST)opt10192.168.40.1ICMPTest ping gateway
opt11 (HOMELAB)opt11192.168.50.1ICMPTest ping gateway

⚠️ NE PAS utiliser (self) comme destination — voir le piège des règles ICMP dans les Annexes (Pièges rencontrés). La destination doit être l’IP de la gateway du VLAN uniquement.

5.8. Mapping final (après implémentation)

AssignationDeviceRôleIP
wanigb0WANDHCP
lanbridge0LAN 10/24192.168.10.1
opt1igb2Physique brute (AP)
opt2igb3Physique brute (pve1)
opt3vlan0.1.20VLAN 20 igb1
opt5vlan0.1.30VLAN 30 igb1
opt6vlan0.1.40VLAN 40 igb1
opt7vlan0.1.50VLAN 50 igb1
opt12vlan0.2.20VLAN 20 igb2
opt13vlan0.2.30VLAN 30 igb2
opt14vlan0.2.40VLAN 40 igb2
opt15vlan0.2.50VLAN 50 igb2
opt8bridge1DMZ192.168.20.1
opt9bridge2IOT192.168.30.1
opt10bridge3GUEST192.168.40.1
opt11bridge4HOMELAB192.168.50.1
opt4HomeWireGuardVPN

5.9. Validation sur OPNsense (ALIX) — connectivité

ℹ️ La configuration des interfaces VLAN sur l’ALIX (vr0 sans VLAN, vr1 avec vlan20-50) est détaillée en section 1.3 (machine de test). On ne la répète pas ici.

⚠️ Piège OpenBSD : le parent doit être monté d’abord :

root@alix:~# ifconfig vr1 up    # SANS ça, les paquets ne sortent pas (100% perte, tcpdump vide)

Script de test d’isolation (ALIX)

#!/bin/sh
# test-vlans.sh — Test d'isolation inter-VLAN (OpenBSD)
# Usage: sh test-vlans.sh
# Les IPs sources sont récupérées dynamiquement depuis ifconfig (DHCP)

echo "=== TEST ISOLATION VLAN (gateway = doit répondre, autres = bloqués) ==="
echo ""

# Récupérer l'IP source d'une interface
get_ip() {
    ifconfig "$1" 2>/dev/null | awk '/inet /{print $2; exit}'
}

# Tester un flux : ping depuis l'IP source vers une destination
# $1 = source IP, $2 = destination, $3 = label
test_ping() {
    if ping -S "$1" -c2 -w2 "$2" >/dev/null 2>&1; then
        echo "  vers $2 ($3) : ⚠️ RÉPOND (attendu: BLOQUÉ)"
    else
        echo "  vers $2 ($3) : ✅ bloqué"
    fi
}

# Tester la gateway d'un VLAN : doit répondre
test_gateway() {
    if ping -S "$1" -c2 -w2 "$2" >/dev/null 2>&1; then
        echo "  gateway $2     : ✅ RÉPOND"
    else
        echo "  gateway $2     : ❌ PERDU (attendu: répond)"
    fi
}

# --- VLAN 20 (DMZ) ---
IP20=$(get_ip vlan20)
[ -n "$IP20" ] && echo "--- VLAN 20 (DMZ) source $IP20 ---" || echo "--- VLAN 20 (DMZ) ❌ pas d'IP ---"
if [ -n "$IP20" ]; then
    test_gateway "$IP20" 192.168.20.1 "DMZ gateway"
    test_ping "$IP20" 192.168.30.1 "IOT"
    test_ping "$IP20" 192.168.10.1 "LAN"
fi
echo ""

# --- VLAN 30 (IOT) ---
IP30=$(get_ip vlan30)
[ -n "$IP30" ] && echo "--- VLAN 30 (IOT) source $IP30 ---" || echo "--- VLAN 30 (IOT) ❌ pas d'IP ---"
if [ -n "$IP30" ]; then
    test_gateway "$IP30" 192.168.30.1 "IOT gateway"
    test_ping "$IP30" 192.168.10.1 "LAN"
    test_ping "$IP30" 192.168.20.1 "DMZ"
fi
echo ""

# --- VLAN 40 (GUEST) ---
IP40=$(get_ip vlan40)
[ -n "$IP40" ] && echo "--- VLAN 40 (GUEST) source $IP40 ---" || echo "--- VLAN 40 (GUEST) ❌ pas d'IP ---"
if [ -n "$IP40" ]; then
    test_gateway "$IP40" 192.168.40.1 "GUEST gateway"
    test_ping "$IP40" 192.168.10.1 "LAN"
fi
echo ""

# --- VLAN 50 (HOMELAB) ---
IP50=$(get_ip vlan50)
[ -n "$IP50" ] && echo "--- VLAN 50 (HOMELAB) source $IP50 ---" || echo "--- VLAN 50 (HOMELAB) ❌ pas d'IP ---"
if [ -n "$IP50" ]; then
    test_gateway "$IP50" 192.168.50.1 "HOMELAB gateway"
    test_ping "$IP50" 192.168.10.1 "LAN"
fi
echo ""

echo "=== RÉSULTAT ==="
echo "  ✅ = conforme au comportement attendu"
echo "  ⚠️ = anomalie (un flux qui devrait être bloqué passe)"
echo "  ❌ = la gateway du VLAN ne répond pas (problème VLAN)"

Résultat attendu : toutes les gateways répondent, tous les accès LAN/bloqués sont refusés. Les IPs sources sont lues dynamiquement via ifconfig vlanXX (robuste aux changements DHCP).

Sortie réelle du script exécuté :

$ sh ./test-vlans.sh
=== TEST ISOLATION VLAN (gateway = doit répondre, autres = bloqués) ===

--- VLAN 20 (DMZ) source 192.168.20.101 ---
  gateway 192.168.20.1     : ✅ RÉPOND
  vers 192.168.30.1 (IOT) : ✅ bloqué
  vers 192.168.10.1 (LAN) : ✅ bloqué

--- VLAN 30 (IOT) source 192.168.30.102 ---
  gateway 192.168.30.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué
  vers 192.168.20.1 (DMZ) : ✅ bloqué

--- VLAN 40 (GUEST) source 192.168.40.100 ---
  gateway 192.168.40.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué

--- VLAN 50 (HOMELAB) source 192.168.50.100 ---
  gateway 192.168.50.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué

=== RÉSULTAT ===
= conforme au comportement attendu
  ⚠️ = anomalie (un flux qui devrait être bloqué passe)
= la gateway du VLAN ne répond pas (problème VLAN)

Test DHCP (KEA)

Les interfaces VLAN de l’ALIX (vlan20-50 sur vr1) sont déjà configurées en DHCP — voir la config de la machine de test en section 1.3.

Vérification côté OPNsense : Services → Kea DHCP → Leases → chaque VLAN doit avoir une lease assigned pour l’ALIX.

Exemple de leases (une IP ALIX par interface VLAN) :

SubnetIPMACHostnameÉtat
DMZ (20)192.168.20.10100:0d:b9:1b:aa:4dalixassigned
IOT (30)192.168.30.10200:0d:b9:1b:aa:4dalixassigned
GUEST (40)192.168.40.10000:0d:b9:1b:aa:4dalixassigned
HOMELAB (50)192.168.50.10000:0d:b9:1b:aa:4dalixassigned

(L’ALIX utilise la même MAC 00:0d:b9:1b:aa:4d (vr1) sur les 4 VLANs, avec une IP distincte par subnet.)

Leases DHCP Kea OPNsense par VLAN

Vérification côté ALIX : ifconfig vlanXX | grep inet doit montrer une IP dans la plage du subnet correspondant.


5.10. Résultats finaux et persistance

TestRésultat
Connectivité 4 VLANs✅ 0% perte
Isolation inter-VLAN✅ 100% bloqué
Isolation LAN✅ bloqué depuis tous les VLANs
DHCP KEA✅ 4/4 leases
Réseau existant✅ intact

Test de persistance (reboot OPNsense) : après un redémarrage d’OPNsense, les bridges sont recréés avec leurs membres (vlan0.X.20-50 + vxlanX), les interfaces VLAN sont présentes et le réseau reste intact ✅.

6. Le WiFi : AP et répéteur

6.1. Pourquoi le 802.1Q n’est pas standard sur le WiFi

Le tag 802.1Q est une étiquette de trame Ethernet filaire (IEEE 802.3 + champ 802.1Q). Sur un lien WiFi (802.11), la trame a une structure différente :

ÉlémentFilaire EthernetWiFi (802.11)
TrameTrame Ethernet + champ 802.1QTrame 802.11 (structure différente)
Priorisation QoS802.1p (dans le tag VLAN)WMM / 802.11e (QoS Control, pas de tag)
Segmentation VLANTag 802.1Q standardSSIDs multiples = segmentation native

La trame 802.11 n’a pas de champ 802.1Q. Le standard WiFi segmente via SSIDs multiples (chaque SSID mappé à un réseau/VLAN), pas via des tags dans la trame.

Méthodes réelles pour “VLAN sur WiFi” :

MéthodeStandard ?Comment
SSIDs multiples✅ IEEE 802.11Chaque SSID → un VLAN (méthode native)
WDS (4 adresses)Préserve la trame Ethernet complète — tag dépend du driver
802.1X / WPA3-EnterpriseLe RADIUS assigne un VLAN par utilisateur (dynamic VLAN)
802.11s meshMaillage, pas de tags
Tag 802.1Q dans la trame 802.11PAS standardiséNon défini par l’IEEE

Conséquence pour ce projet : le 802.1Q “brut” sur WiFi n’est pas standard — d’où le recours aux SSIDs dédiés (méthode native) et à l’encapsulation VXLAN (section 7) pour transporter les VLANs.

6.2. Le problème : le WiFi ne transporte pas les VLANs

Le segment AP OpenWrt → Répéteur passe par le WiFi. Un tag 802.1Q est une étiquette de trame Ethernet filaire : sur un lien 802.11, elle peut être perdue selon le mode du répéteur.

Mode répéteurTags VLAN traversent ?
Routage/NAT (client standard)❌ Non — décapsulation IP, tag détruit
Relay Bridge (relayd)❌ Non — relayd fait du routage de trames, tag détruit
Pont WDS (4-adresses)✅ Oui

6.3. Vérification sur le terrain

  • Répéteur (.254) : interface repeater_bridge en protocole Relay Bridge ✅ → les VLANs passeront
  • AP (.2) : brctl showbr-lan contient eth1 + phy0-ap0 + phy1-ap0 = pont transparent
# Vérification AP (SSH root)
root@openwrt:~# brctl show
bridge name  bridge id          STP enabled  interfaces
br-lan       7fff.000db95ae901   no           eth1
                                              phy0-ap0
                                              phy1-ap0

Les VLANs traversent le WiFi via relayd mais : réécriture MAC (relayd), débit partagé entre VLANs sur la radio, multicast parfois capricieux. Acceptable pour un homelab, à connaître.

6.4. Découverte : relayd ne transporte pas les VLANs

L’audit initial supposait que le répéteur (.254) était en “Relay Bridge” (relayd). La réalité vérifiée sur le terrain :

config wifi-iface 'wifinet2'
 option device 'radio1'
 option mode 'sta'          ← CLIENT WiFi, PAS relayd !
 option network 'wwan'      ← interface séparée
br-lan = lan1, lan2, lan3, phy0-ap0    ← wwan ABSENT du bridge

Conséquence : le répéteur fait du routage/NAT entre le WiFi et le LAN filaire — les tags VLAN 20-50 sont détruits au décapsulage WiFi. Seul le VLAN 10 natif (non taggé) traverse.

Impact : le switch MikroTik (derrière le répéteur) ne peut pas transporter les VLANs via le WiFi. Symptôme observé : les trames 802.1Q partent de l’ALIX mais n’arrivent jamais sur igb2 d’OPNsense (tcpdump : 10 852 paquets reçus, 0 tag 802.1Q).

Options pour résoudre :

  1. Câble filaire switch → AP/OPNsense (fiable, recommandé)
  2. Convertir le répéteur en WDS (4-adresses) — voir section 6.5
  3. Accepter : le switch reste sur le VLAN 10, VLANs seulement sur les chemins filaires directs

6.5. Conversion répéteur relayd → WDS

Objectif : tenter de faire traverser les tags 802.1Q sur le lien WiFi répéteur ↔ AP. Le mode STA (2-adresses) détruit les tags ; le WDS (4-adresses) transporte la trame Ethernet complète, ce qui est le seul espoir de préserver le tag. Mais le 802.1Q n’étant pas standard sur le WiFi (section 6.1), le résultat n’était pas garanti — c’est ce qu’on a dû vérifier sur le terrain.

Prérequis (côté AP)

Le SSID cible doit accepter les clients WDS (wds='1') :

# AP (/etc/config/wireless) — SSID Vince-AC (5 GHz)
root@ap:~# uci show wireless.default_radio0.wds
# → wireless.default_radio0.wds='1'   ← obligatoire

Config cible (côté répéteur)

# /etc/config/wireless — le client WDS (au lieu de relayd)
config wifi-iface 'wifinet3'
    option device 'radio1'      # 5 GHz
    option mode 'sta'
    option ssid 'Vince-AC'
    option encryption 'psk2'
    option key '...'
    option wds '1'              # ← WDS activé
    option network 'lan'        # ← DANS le bridge (PAS wwan séparé)

Point clé : le client WDS doit être ponté dans br-lan (option network 'lan'), pas dans une interface wwan séparée avec IP — sinon c’est du routage et les tags sont détruits.

# /etc/config/network — br-lan inchangé (les ports LAN + le lien WDS)
config device
    option name 'br-lan'
    option type 'bridge'
    list ports 'lan1'
    list ports 'lan2'
    list ports 'lan3'
    # le lien WDS (phy1-sta0) est ajouté automatiquement via network 'lan'

Vérification de la conversion

# 1. Le lien WDS est établi et ponté ?
$ iwinfo
phy1-sta0 ESSID: "Vince-AC"
         Access Point: 0a:bf:b8:1d:b7:9f
         Mode: Client  Channel: 36 (5.180 GHz)  HT Mode: VHT80
         Center Channel 1: 42 2: unknown
         Tx-Power: 20 dBm  Link Quality: 39/70
         Signal: -71 dBm  Noise: -92 dBm
         Bit Rate: 526.5 MBit/s
         Encryption: none
         Type: nl80211  HW Mode(s): 802.11ax/ac/n
         Hardware: embedded [MediaTek MT7986]
         TX power offset: none
         Channel offset: none
         Supports VAPs: yes  PHY name: phy1

# 2. Le lien WDS est dans le bridge ?
$ brctl show
bridge name bridge id  STP enabled interfaces
br-lan  7fff.08bfb80db79e yes  lan2
       phy1-sta0
       lan3
       lan1

🚨 DÉCOUVERTE : WDS ne suffit PAS sur ath10k

Les tags 802.1Q ne traversent toujours pas le lien WiFi même en WDS. Diagnostic croisé tcpdump :

Point d’écouteTrafic VLAN 20 taggéConclusion
ALIX (vlan20)✅ émis (length 46)L’émission est correcte
Répéteur (phy1-sta0)✅ émis vers le WiFiLe répéteur émet les tags
AP (phy0-ap0 / phy1-ap0)0 paquet 802.1QLes tags sont strippés par le driver ath10k à la réception

Preuve que le WDS transporte bien les trames : les ARP du réseau 10 (pve derrière le switch) arrivent bien sur phy0-ap0 — le lien WDS fonctionne pour le trafic non taggé. Seuls les tags VLAN sont perdus.

Cause : limitation du driver ath10k (QCA9880) — le tag 802.1Q est une couche Ethernet filaire, le driver le retire à la réception de la trame 802.11, même en 4-adresses. Aucune config OpenWrt ne préserve les tags sur ce matériel.

Confirmation externe : le driver ath10k a une limitation documentée — il droppe ou échoue à transmettre les trames 802.1Q taggées selon le firmware Qualcomm officiel et la version du driver, notamment en mode bridge/WDS. Les workarounds recommandés consistent à stripper les tags au niveau hôte (sous-interfaces Ethernet → WLAN untagged). Notre solution VXLAN est un contournement encore plus robuste : elle encapsule la trame VLAN dans UDP 4789, ce qui évite totalement que ath10k traite les tags 802.1Q bruts.

Vérification driver (dmesg AP) : phy0-ap0 (5 GHz, où le répéteur est connecté via WDS) est piloté par ath10k_pci (qca988x hw2.0). C’est bien ce driver qui strippe les tags sur le chemin répéteur → AP. La radio 2.4 GHz (phy1-ap0) utilise ath9k — non concernée par le WDS.

Conclusion

SolutionTransporte les VLANs ?Statut
STA (2-adresses)❌ NonAbandonné
relayd (Relay Bridge)❌ Non (jamais été actif)Abandonné
WDS (4-adresses)❌ Non sur ath10kDécouvert
VXLAN par-dessus WDS✅ Oui (encapsulation UDP)En cours de validation (section 7)

Leçon : sur le WiFi, la segmentation VLAN se fait par SSIDs dédiés (Vince-IOT → VLAN 30, Vince-Guest → VLAN 40 — validés), pas par tags sur un lien. Pour transporter des VLANs à travers un lien WiFi, il faut encapsuler (VXLAN).


6.6. Configurer l’AP OpenWrt (vlan_filtering)

Objectif : rendre l’AP VLAN-aware pour transporter les tags 20-50.

Config /etc/config/network (AP) :

config device
    option name 'br-lan'
    option type 'bridge'
    option vlan_filtering '1'
    list ports 'eth0'
    list ports 'eth1'

config bridge-vlan
    option device 'br-lan'
    option vlan '10'
    list ports 'eth0:u*'
    list ports 'eth1:u*'

config bridge-vlan
    option device 'br-lan'
    option vlan '20'
    list ports 'eth0:t'
    list ports 'eth1:t'

config bridge-vlan
    option device 'br-lan'
    option vlan '30'
    list ports 'eth0:t'
    list ports 'eth1:t'

config bridge-vlan
    option device 'br-lan'
    option vlan '40'
    list ports 'eth0:t'
    list ports 'eth1:t'

config bridge-vlan
    option device 'br-lan'
    option vlan '50'
    list ports 'eth0:t'
    list ports 'eth1:t'

config interface 'lan'
    option device 'br-lan.10'      # ← IP sur la sous-interface VLAN 10 !
    option proto 'static'
    list ipaddr '192.168.10.2/24'
    option gateway '192.168.10.1'

Légende : u = untagged, t = tagged, * = PVID.

Schéma AP après config :

                 AP OpenWrt .2
                 ┌──────────────┐
    ALIX vr0 ────│ eth0         │
                 │  ┌────────┐  │
                 │  │ br-lan │  │
                 │  │ vlan   │  │
                 │  │ filter │  │
                 │  └───┬────┘  │
                 │      │       │
    OPNsense ────│ eth1 │       │
    (igb2)       └──────┼───────┘
                        │
            VLANs 10(natif)/20/30/40/50

⚠️ Piège critique : avec vlan_filtering=1, l’IP de management doit être sur br-lan.10 (sous-interface VLAN 10), PAS sur br-lan brut — sinon perte de connectivité et rollback automatique LuCI.

Appliquer :

root@ap:~# /etc/init.d/network reload

Vérifier :

# vlan_filtering est-il actif sur le bridge ?
$ cat /sys/class/net/br-lan/bridge/vlan_filtering
1

# L'IP de gestion est-elle bien sur br-lan.10 (et non br-lan brut) ?
$ ip addr show br-lan.10 | grep inet
    inet 192.168.10.2/24 brd 192.168.10.255 scope global br-lan.10
    inet6 fe80::20d:b9ff:fe5a:e900/64 scope link

# La gateway OPNsense répond-elle ?
$ ping -c2 192.168.10.1
PING 192.168.10.1 (192.168.10.1): 56 data bytes
64 bytes from 192.168.10.1: seq=0 ttl=64 time=0.821 ms
64 bytes from 192.168.10.1: seq=1 ttl=64 time=0.807 ms

--- 192.168.10.1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.807/0.814/0.821 ms

Schéma final à cette étape (ALIX sur l’AP pour le test de traversée) :

                          OPNsense
                           │
     ┌─────────────────────┼──────────────────────┐
     │                     │                      │
   igb1 (test)           igb2 (AP)              igb3 (pve1)
     │                     │                      │
   vlan0.1.20-50      vlan0.2.20-50             (brut)
     │                     │                      │
     │  bridge1-4          │  bridge1-4           │
     └────────┬────────────┘                      │
              │                                   │
         AP OpenWrt .2                     pve1 .11
         (vlan_filtering)
              │
         ┌────┴────────────┐
         │ ALIX (OpenBSD)  │
         │ vr1 → AP eth0   │  ← test VLAN via l'AP
         │ vr0 → igb1      │  ← (test direct, optionnel)
         └─────────────────┘

6.7. Validation AP OpenWrt

TestRésultat
vlan_filtering actif (bridge vlan show)
VLANs 20-50 traversent l’AP (ALIX → eth0 → eth1 → igb2)✅ 0% perte
Isolation conservée à travers l’AP
IP statique AP (192.168.10.2) + br-lan.10

6.8. WiFi Guest et IOT

Config AP : 2 VIFs ajoutées sur les radios (via interfaces réseau dédiées, pas l’option vlan de l’UI) :

🏷️ Bonne pratique : la segmentation VLAN par SSID est la méthode native et standard pour séparer les réseaux sur le WiFi (voir section 6.1). Elle est d’autant plus pertinente ici que les VLANs IOT (30) et GUEST (40) n’accueilleront que des appareils mobiles/WiFi — des objets connectés (IOT) et des invités (GUEST) qui se connecteront exclusivement sans fil. Le SSID dédié mappe directement le client vers son VLAN, sans dépendre du transport 802.1Q sur l’air.

SSIDRadioInterface réseauVLAN
Vince-IOTradio0 (5GHz)lan_iot → br-lan.3030
Vince-Guestradio1 (2.4GHz)lan_guest → br-lan.4040

Wireless AP OpenWrt (SSIDs par VLAN)

Config AP (interfaces) :

config interface 'lan_iot'
    option device 'br-lan.30'
    option proto 'static'
    option ipaddr '192.168.30.2'
    option gateway '192.168.30.1'

config interface 'lan_guest'
    option device 'br-lan.40'
    option proto 'static'
    option ipaddr '192.168.40.2'
    option netmask '255.255.255.0'
    option gateway '192.168.40.1'

Interfaces AP OpenWrt (lan, lan_iot, lan_guest)

Devices AP OpenWrt (br-lan, bridges VLAN)

Modèle de règles firewall OPNsense par VLAN (copier/coller, adapter la source) :

ℹ️ Il s’agit du firewall OPNsense (la passerelle qui filtre entre les VLANs) — pas du firewall OpenWrt (celui de l’AP/répéteur, qui ne concerne que le trafic local de ces équipements). Les règles ci-dessous se configurent dans OPNsense → Firewall → Rules.

#ActionProtocolSourcePortDestinationPortGatewayDescription
1PASSTCP/UDPVLAN net*This Firewall53*VLAN → DNS (Unbound)
2PASSICMPVLAN net*This Firewall**VLAN → ping gateway
3🚫 BLOCK*VLAN net*autres VLANs + lan**VLAN → pas de réseaux privés (Log ☑️)
4PASS*VLAN net*any**VLAN → Internet

⚠️ Ordre des règles : OPNsense (basé sur pf) évalue les règles de haut en bas, et la première règle qui correspond gagne (le trafic est traité par la 1re règle match, les suivantes sont ignorées). L’ordre est donc déterminant :

  • Les règles PASS spécifiques (DNS, ICMP) doivent être en premier
  • Le BLOCK des réseaux privés (règle 3) doit venir avant le PASS Internet (règle 4), sinon le trafic vers les autres VLANs passerait via la règle Internet
  • Respecter cet ordre garantit que seuls les flux voulus sortent du VLAN, et que l’isolation inter-VLAN est réellement effective

Règles firewall OPNsense (exemple filtré sur HOMELAB)

Exemple concret — règles firewall OPNsense pour IOT :

#ActionProtocolSourcePortDestinationPortGatewayDescription
1PASSTCP/UDPIOT network*This Firewall53*IOT → DNS (Unbound)
2PASSICMPIOT network*This Firewall**IOT → ICMP
3🚫 BLOCK*IOT network*DMZ network, GUEST network, HOMELAB network, LAN network**IOT → pas de réseaux privés
4PASS*IOT network****IOT → Internet

Liste des autres VLANs par interface (avec les alias dynamiques OPNsense) :

  • Guest (opt10) : BLOCK → DMZ networks, HOMELAB networks, IOT networks, LAN net (alias dynamiques auto-créés)
  • IOT (opt9) : BLOCK → DMZ networks, HOMELAB networks, Guest networks, LAN net

⚠️ Différence clé : le BLOCK IOT doit inclure Guest networks (IOT ne joint pas Guest) ; le BLOCK Guest inclut IOT networks. Le WAN et le réseau de l’interface elle-même ne sont JAMAIS dans le BLOCK — d’où l’utilisation des alias dynamiques d’OPNsense (ex: IOT networks, DMZ networks…) plutôt que l’alias RFC1918 PrivateNetworks qui couvrirait le WAN (192.168.1.14 — Livebox) et casserait Internet.

Résultats de validation :

                    ┌────────────────────────────────────────────────┐
                    │                 OPNsense                       │
                    │                                                │
                    │  bridge2 (IOT .30.1)   bridge3 (Guest .40.1)   │
                    │      ▲                        ▲                │
                    │      │ VLAN 30                │ VLAN 40        │
                    │   igb2 (vlan0.2.30)       igb2 (vlan0.2.40)   │
                    └──────┼────────────────────────┼────────────────┘
                           │                        │
                    ┌──────┴────────────────────────┴──────┐
                    │             AP OpenWrt .2            │
                    │        (vlan_filtering actif)        │
                    │                                      │
                    │  br-lan.30 (lan_iot)   br-lan.40     │
                    │      ▲   phy0-ap1          ▲  phy1-ap1│
                    │      │   Vince-IOT         │  Vince-G │
                    │      │   (5GHz)            │  uest    │
                    └──────┼────────────────────┼──────────┘
                           │                    │   (2.4GHz)
                           │                    │
                    ┌──────┴───┐          ┌─────┴──────┐
                    │ iPhone    │          │ iPad       │
                    │ 192.168.30│          │ 192.168.40.│
                    │ .102      │          │ 102        │
                    │ [WiFi]    │          │ [WiFi]     │
                    └───────────┘          └────────────┘

Résultats des tests :

TestRésultat
iPhone → Vince-IOT → IP 192.168.30.102
iPad → Vince-Guest → IP 192.168.40.102
Internet depuis IOT et Guest✅ (après règle DNS + BLOCK corrigé)
IOT → LAN (.9:7000, .180:7000 AirPlay)🚫 bloqué + loggé
Guest → LAN (.10:8006 pve0)🚫 bloqué + loggé
Isolation inter-VLAN
                    ┌──────────────────────────────────────┐
                    │      Flux de test pendant la phase   │
                    │                                       │
                    │  iPhone (IOT) → .9:7000  ──🚫 BLOCK──▶│
                    │  iPhone (IOT) → Internet ──✅ PASS───▶│
                    │  iPad (Guest) → .10:8006 ──🚫 BLOCK──▶│
                    │  iPad (Guest) → Internet ──✅ PASS───▶│
                    └──────────────────────────────────────┘

Anecdote identifiée : le port 7000 + 5000 sur .9/.180 = appareils AirPlay (Apple TV/HomePod) — l’iPhone IOT tentait de diffuser vers le LAN, bloqué par la règle IOT ✅

✅ Point d’étape — où en est-on à la fin de la section 6

À ce stade du rollout, la segmentation par SSID est fonctionnelle :

  • OPNsense : les 4 bridges VLAN (DMZ 20, IOT 30, GUEST 40, HOMELAB 50) sont créés et routent (section 5)
  • AP OpenWrt : vlan_filtering actif, les SSIDs Vince-IOT (→ VLAN 30) et Vince-Guest (→ VLAN 40) sont opérationnels (sections 6.6-6.8)
  • VLANs fonctionnels via WiFi : IOT et GUEST traversent l’AP et rejoignent leurs subnets respectifs, avec isolation inter-VLAN + accès Internet validés (section 6.8)

Il reste un chaînon à couvrir : le répéteur WiFi (et derrière lui le switch MikroTik) ne peuvent pas encore transporter les VLANs — le WiFi ne transporte pas le 802.1Q (section 6.1) et ni relayd ni WDS ne suffisent sur ce matériel (sections 6.4-6.5). C’est l’objet de la section 7 (VXLAN), qui va permettre de faire traverser les VLANs 20/30/40/50 au répéteur et au switch.


7. VXLAN — transporter les VLANs sur le WiFi

L’AP (Qualcomm QCA9880/ath10k) strippe les tags 802.1Q sur les trames reçues du WiFi (section 6.5), même en WDS 4-adresses — les VLANs ne pouvaient donc pas traverser le lien WiFi directement.

Solution : VXLAN (encapsulation UDP). La trame Ethernet complète (avec son tag VLAN) est encapsulée dans un paquet UDP/IP entre OPNsense et le répéteur : OPNsense crée un tunnel VXLAN (vxlan1-4), l’AP joue le rôle de bridge L2 transparent (elle forwarde l’UDP 4789 sans toucher au contenu), et le répéteur décapsule pour exposer les VLANs. Le résultat : les VLANs 20/30/40/50 traversent le WiFi de bout en bout.

7.1. Qu’est-ce que VXLAN ?

VXLAN (Virtual eXtensible LAN) est un protocole d’overlay L2 sur réseau L3 (RFC 7348) :

  • Il encapsule des trames Ethernet complètes (avec leur tag VLAN) dans des paquets UDP/IP
  • VNI (VNET Identifier, 24 bits) = identifiant du réseau virtuel
  • Port UDP 4789 (par défaut)
  • Les flags [I] (0x08) indiquent une trame VNI (observés en capture)
| Ethernet ext (14) | IP (20) | UDP (8) | VXLAN (8) | Trame Ethernet interne (avec tag VLAN) |

Overhead : 14 + 20 + 8 + 8 = 50 octets → MTU interne max = 1500 − 50 = 1450.

7.2. Pourquoi l’utiliser ici

Le WiFi (ath10k) strippe les tags 802.1Q (section 6.2), et relayd fait du routage de trames (section 6.4). VXLAN encapsule la trame VLAN complète dans UDP/IP, qui lui traverse normalement le WiFi :

  • L’AP est un bridge transparent — elle forwarde l’UDP sans toucher au contenu
  • Le répéteur décapsule et expose les VLANs

7.3. Architecture cible

┌──────────────────────────────────────────────────────────────────────┐
│  OPNsense (.1) — FreeBSD                                             │
│                                                                      │
│  igb0 ── WAN (Livebox)                                               │
│                                                                      │
│  bridge1 = DMZ   (.20.1)  ◄── vxlan1 (VNI 20, descr vxlan20)         │
│  bridge2 = IOT   (.30.1)  ◄── vxlan2 (VNI 30, descr vxlan30)         │
│  bridge3 = GUEST (.40.1)  ◄── vxlan3 (VNI 40, descr vxlan40)         │
│  bridge4 = HOMELAB (.50.1) ◄─ vxlan4 (VNI 50, descr vxlan50)         │
│                                                                      │
│  vxlan1-4 : local 192.168.10.1:4789  →  remote 192.168.10.254:4789   │
│     │                │                │                │             │
└─────┼────────────────┼────────────────┼────────────────┼─────────────┘
      │                │  UDP 4789 (VXLAN encapsulé en IP — L3)
      │                ▼
┌─────┴────────────────────────────────────────────────────────────────┐
│  AP OpenWrt (.2) — bridge L2 TRANSPARENT                             │
│  (ne fait AUCUN VXLAN — forwarde l'UDP 4789 sans y toucher)          │
│  igb2 ← OPNsense      eth1                                           │
│  br-lan [eth0 + eth1]  vlan_filtering=1                              │
│  phy1-ap0 ──► [WiFi WDS]                                             │
└──────┬───────────────────────────────────────────────────────────────┘
       │  UDP 4789 (VXLAN encapsulé — traverse le WiFi en IP)
       │  [WiFi] ~~~~ phy1-sta0
┌──────┴───────────────────────────────────────────────────────────────┐
│  Répéteur (.254) — OpenWrt                                           │
│  phy1-sta0 ← [WiFi WDS]  (uplink AP)                                 │
│  br-lan [lan1 + lan2 + lan3 + phy1-sta0]                             │
│  vxlan20/30/40/50  (Décapsulation UDP → trames VLAN exposées)        │
│  br-vx20 [lan1.20+lan2.20+vxlan20]   (idem 30/40/50)                 │
└──┬──────────────────────┬────────────────────────────────────────────┘
   │ lan1.X (trunk)       │ lan2.X (test ALIX)      
   ▼                      ▼                         
┌──────────────────┐  ┌─────────────┐               
│ Switch CSS610    │  │ ALIX (.X)   │               
│ VLANs 20-50      │  │ VLAN 20-50  │               
└──────┬───────────┘  └─────────────┘               
       ▼
   pve0/2/3/4 + NAS

Le VXLAN encapsule la trame Ethernet complète (avec son tag 802.1Q) dans UDP 4789. Le WiFi transporte l’IP (ça marche), VXLAN transporte la trame VLAN dedans — les tags survivent.

Point clé : l’AP est transparente

L’AP ne fait aucun VXLAN — elle est un bridge L2 transparent qui forwarde l’UDP 4789 entre eth1 (OPNsense) et le WiFi (répéteur). Ses bridge-vlan (20/30/40/50 taggés sur eth0+eth1) ne concernent que le trafic direct AP→OPNsense, pas le VXLAN.

7.4. Mise en place sur OPNsense

Prérequis : charger le module if_vxlan

Le module if_vxlan (pilote VXLAN FreeBSD) n’est pas chargé par défaut sur OPNsense. Il doit être chargé et persisté au boot avant de créer les devices VXLAN.

Chargement immédiat (CLI) :

root@opnsense:~# kldload if_vxlan

Persistance au boot (via l’UI OPNsense) — System → Settings → Tunables → Add :

Tunable      : if_vxlan_load
Value        : 1
Description  : Charger le module VXLAN (if_vxlan) au démarrage

Ceci génère <tunable>if_vxlan_load</tunable> dans /conf/config.xml — le module se charge à chaque boot.

Vérification :

root@opnsense:~# kldstat | grep vxlan
25    1 0xffffffff83881000     7420 if_vxlan.ko

Config OPNsense (via l’UI)

Interfaces → Other Types → VXLAN → Add (×4, mode unicast) :

#Device (auto)DescriptionVNISourceRemote
1vxlan1vxlan2020192.168.10.1192.168.10.254
2vxlan2vxlan3030192.168.10.1192.168.10.254
3vxlan3vxlan4040192.168.10.1192.168.10.254
4vxlan4vxlan5050192.168.10.1192.168.10.254

Multicast group + Device : VIDES (mode unicast). Le device auto (vxlan<N>) n’est pas renommable ; la Description (vxlan<vlan_id>) est le nom logique.

VXLAN OPNsense

Puis Interfaces → Assignments : créer une interface par device VXLAN (SANS IP, membre de bridge). Puis Interfaces → Bridges : ajouter chaque vxlanX à son bridge.

Bridges finaux OPNsense :

BridgeMembresIP (SVI)VLAN
bridge0igb2, igb3, igb1192.168.10.110 (natif)
bridge1vlan0.1.20 + vlan0.2.20 + vlan0.3.20 + vxlan1192.168.20.120 (DMZ)
bridge2vlan0.1.30 + vlan0.2.30 + vlan0.3.30 + vxlan2192.168.30.130 (IOT)
bridge3vlan0.1.40 + vlan0.2.40 + vlan0.3.40 + vxlan3192.168.40.140 (GUEST)
bridge4vlan0.1.50 + vlan0.2.50 + vlan0.3.50 + vxlan4192.168.50.150 (HOMELAB)

Chaque bridge transporte :

  • Le VLAN (via les sous-interfaces ou le natif) : vlan0.1.X (igb1), vlan0.2.X (igb2/AP), vlan0.3.X (igb3/pve1), ou natif pour bridge0
  • Le VXLAN : vxlan1-4 (le tunnel WiFi) — sur bridge1-4 uniquement
  • Le SVI : l’IP gateway du bridge lui-même

⚠️ Nuance “trafic brut” : les bridges transportent le trafic taggé (VLAN) et le VXLAN, pas du “trafic brut non taggé” — chaque bridge est dédié à UN VLAN précis. Le trafic brut/non taggé reste sur bridge0 (VLAN 10 natif).

7.5. Mise en place sur le répéteur

Prérequis : packages VXLAN (OpenWrt)

Sur OpenWrt, le VXLAN nécessite le module noyau vxlan fourni par le package kmod-vxlan, ainsi que les packages de configuration. Sur OpenWrt 25.12 (répéteur), le gestionnaire de paquets est apk (et non opkg).

Packages à installer (via apk) :

root@repeater:~# apk add kmod-vxlan vxlan luci-proto-vxlan

Vérification du module chargé :

root@repeater:~# lsmod | grep vxlan
ip6_udp_tunnel         12288  1 vxlan
udp_tunnel             16384  1 vxlan
vxlan                  94208  0

Vérification des packages installés :

root@repeater:~# apk list --installed | grep -i vxlan
kmod-vxlan-6.12.94-r1 ... [installed]
luci-proto-vxlan-26.218.70234 ... [installed]
vxlan-7 ... [installed]

Sans kmod-vxlan, le noyau ne peut pas créer les interfaces vxlanX (échec netifd).

Config répéteur (/etc/config/network)

# VXLANs (créés via l'UI LuCI, protocol VXLAN)
config interface 'vxlan20'
    option proto 'vxlan'
    option peeraddr '192.168.10.1'
    option vid '20'
    option multipath 'off'
    option mtu '1446'
# ... idem pour vxlan30/40/50 (vid 30/40/50)

# Bridges (créés via l'UI LuCI, type bridge)
config device
    option type 'bridge'
    option name 'br-vx30'
    list ports 'lan1.30'
    list ports 'vxlan30'
# ... br-vx20/40/50 identiques

# Sous-interfaces 8021q sur lan1 (port switch)
config device
    option type '8021q'
    option ifname 'lan1'
    option vid '20'
    option name 'lan1.20'
# ... lan1.30/40/50 identiques

Interfaces répéteur OpenWrt (lan + vxlan)

Devices répéteur OpenWrt (vxlan, bridges, 8021q)

Config répéteur (/etc/rc.local) — persistance des sous-interfaces

⚠️ netifd ne crée PAS les sous-interfaces 8021q (bug DSA sur MT7986) → persistance via rc.local :

# --- VLANs 20/30/40/50 : sous-interfaces + bridges VXLAN ---
for pair in "lan1 20" "lan1 30" "lan1 40" "lan1 50" "lan2 20" "lan2 30" "lan2 40" "lan2 50"; do
    set -- $pair
    parent=$1; vid=$2
    iface=${parent}.${vid}
    if ! ip link show $iface >/dev/null 2>&1; then
        ip link add link $parent name $iface type vlan id $vid
        ip link set $iface up
    fi
done
for v in 20 30 40 50; do
    if ! ip link show br-vx$v >/dev/null 2>&1; then
        ip link add br-vx$v type bridge
        ip link set br-vx$v up
    fi
    ip link set lan1.$v master br-vx$v 2>/dev/null
    ip link set lan2.$v master br-vx$v 2>/dev/null
    ip link set vxlan$v master br-vx$v 2>/dev/null
done
exit 0

ℹ️ Note sur lan2.X : ces sous-interfaces (port de test ALIX) apparaissent en NO-CARRIER / LOWERLAYERDOWN tant que rien n’est branché sur lan2 — c’est normal, l’interface est UP mais sans lien. Elles ne deviennent actives qu’à la connexion d’un équipement.

Firewall OpenWrt (répéteur)

Chaque interface vxlanX doit être dans la zone lan (sinon rejet → Destination Port Unreachable) :

root@repeater:~# uci add_list firewall.@zone[0].network='vxlan20'
root@repeater:~# uci add_list firewall.@zone[0].network='vxlan30'
root@repeater:~# uci add_list firewall.@zone[0].network='vxlan40'
root@repeater:~# uci add_list firewall.@zone[0].network='vxlan50'
root@repeater:~# uci commit firewall
root@repeater:~# /etc/init.d/firewall reload

7.6. Impact du MTU sur le VXLAN

Le VXLAN a un coût inhérent : 50 octets d’overhead par trame. Chaque trame Ethernet encapsulée dans VXLAN ajoute :

Ethernet ext (14) + IP (20) + UDP (8) + VXLAN (8) = 50 octets

Conséquence : le MTU effectif du tunnel est 1500 − 50 = 1450. Le lien entre OPNsense et le répéteur (le tunnel VXLAN) est donc configuré à MTU 1446. Ce coût doit être pris en compte dans le setup VXLAN, pas seulement corrigé a posteriori : tout équipement derrière le tunnel (VMs VLAN 20-50) doit s’y adapter.

Symptôme : Connection reset sur SSH

Symptôme : depuis le LAN, ping vers une VM VLAN (20-50) fonctionne, mais ssh échoue avec Connection reset by ... port 22.

Cause : trou noir MTU (MTU blackhole). Le tunnel a un MTU de 1446 (1500 − 50 d’overhead). Les gros paquets TCP (1500, typiques du handshake SSH/KEX) ne passent pas dans le tunnel et sont perdus. Le ping passe car il utilise de petits paquets (56 octets).

Diagnostic : tester la fragmentation depuis la VM :

root@alix:~# ping -c2 -s 1400 192.168.50.1   # ✅ passe
root@alix:~# ping -c2 -s 1472 192.168.50.1   # ❌ 100% perte → MTU blackhole confirmé

Solution : réduire le MTU à 1400.

  • Proxmox : Hardware → Network Device → mtu=1400
  • FreeBSD (/etc/rc.conf) : ifconfig_vtnet0="DHCP mtu 1400" (ou statique + mtu)
  • NetBSD (/etc/ifconfig.vioif0) : up + mtu 1400 (fichier d’interface, lu au boot)
  • Haiku (~/config/settings/boot/UserBootscript) : ifconfig /dev/net/virtio/0 mtu 1400
  • Linux (LXC/VM) : PMTUD gère souvent, mais mtu=1400 recommandé pour la fiabilité

⚠️ Pourquoi pas le PMTUD : l’option net.inet.tcp.pmtud_blackhole_detection=1 (FreeBSD) ne suffit pas — le handshake SSH KEX envoie des paquets pleine taille dès le début, cassant la connexion avant toute ré-adaptation du MSS. Le MTU 1400 est la solution à la racine : en réduisant le MTU de la VM, le système émet directement des segments de taille adaptée (≤ 1400), donc les gros paquets de 1500 ne sont jamais générés et n’ont pas à être fragmentés.

➡️ Le détail des changements de config MTU par VM (netbsd, freebsd, haiku) est traité en section 9.2 (VLAN sur les hyperviseurs).

7.7. Résultat final validé

Résultat final validé

ALIX (4 VLANs) → répéteur lan2 → br-vxX → vxlanX → [WiFi] → OPNsense → gateways
VLANGatewayTest ALIXStatut
20 (DMZ)192.168.20.1✅ ping + DHCP
30 (IOT)192.168.30.1✅ ping + DHCP
40 (GUEST)192.168.40.1✅ ping + DHCP
50 (HOMELAB)192.168.50.1✅ ping + DHCP

Persistance validée après reboot (interfaces VXLAN créées par netifd, sous-interfaces + bridges par rc.local).

Test d’isolation complet depuis l’ALIX (à travers le VXLAN) :

$ sh ./test-vlans.sh
=== TEST ISOLATION VLAN (gateway = doit répondre, autres = bloqués) ===

--- VLAN 20 (DMZ) source 192.168.20.101 ---
  gateway 192.168.20.1     : ✅ RÉPOND
  vers 192.168.30.1 (IOT) : ✅ bloqué
  vers 192.168.10.1 (LAN) : ✅ bloqué

--- VLAN 30 (IOT) source 192.168.30.102 ---
  gateway 192.168.30.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué
  vers 192.168.20.1 (DMZ) : ✅ bloqué

--- VLAN 40 (GUEST) source 192.168.40.100 ---
  gateway 192.168.40.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué

--- VLAN 50 (HOMELAB) source 192.168.50.100 ---
  gateway 192.168.50.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué

=== RÉSULTAT ===
= conforme au comportement attendu
  ⚠️ = anomalie (un flux qui devrait être bloqué passe)
= la gateway du VLAN ne répond pas (problème VLAN)

(Ce test valide l’isolation inter-VLAN de bout en bout à travers le VXLAN — le même script que la validation OPNsense en section 5.9, mais en passant par le répéteur + le WiFi.)

7.8. Convention de nommage VXLAN OPNsense

  • Device : vxlan<N> (auto, Device ID — non renommable)
  • Description : vxlan<vlan_id> (ex: vxlan20)
  • VNI = VLAN ID (20/30/40/50)
  • Source/Remote : IP LAN OPNsense / IP répéteur

✅ Point d’étape — où en est-on à la fin de la section 7

Le VXLAN est opérationnel de bout en bout :

  • OPNsense : les devices vxlan1-4 (VNI 20/30/40/50) sont créés et rattachés aux bridges 1-4 (section 7.4)
  • Répéteur : le tunnel VXLAN (netifd, MTU 1446) + les bridges br-vx20-50 décapsulent et exposent les VLANs (section 7.5)
  • AP : bridge L2 transparent — elle forwarde l’UDP 4789 sans y toucher
  • Résultat : les VLANs 20/30/40/50 traversent le WiFi de bout en bout, avec isolation inter-VLAN validée par le test test-vlans.sh (section 7.7)
  • MTU : le coût VXLAN (50 octets) est pris en compte (section 7.6) ; l’ajustement des VMs est détaillé en section 9.2

Il reste à couvrir : le switch MikroTik (section 8), qui doit étendre ces VLANs aux services Proxmox et au NAS derrière le répéteur.


8. Switch MikroTik CSS610

8.1. Configurer le switch (VLANs opérationnels)

Objectif

Le switch CSS610 (derrière le répéteur) doit donner aux services Proxmox (pve0/2/3/4) et au NAS l’accès aux VLANs 20-50. Le chemin complet : switch → répéteur → VXLAN → WiFi → OPNsense.

Configuration SwOS

Table VLANs (VLAN membership) :

VLANPorts membresÉquipements
20 (DMZ)1, 2, 3, 4, 5, 8répéteur, pve2, pve0, pve3, pve4, ALIX vr1
30 (IOT)1, 2, 3, 4, 5, 8idem
40 (GUEST)1, 2, 3, 4, 5, 8idem
50 (HOMELAB)1, 2, 3, 4, 5, 8idem

(Les 4 VLANs ont les mêmes membres — la table ne gère que l’appartenance (VLAN membership), il n’y a pas de notion tagged/untagged ici.)

VLANs membres MikroTik SwOS

Ports (menu VLAN) : tout en mode permissif :

  • VLAN Mode : optional
  • VLAN Receive : any (accepte taggé + non taggé)
  • Default VID : 1 (transparent, ne pas modifier)

ℹ️ Nuance SwOS : en SwOS, la distinction tagged/untagged se fait via VLAN Receive et Default VLAN ID, pas dans la table VLAN. Ici en mode any, le switch laisse passer le trafic taggé (VLANs 20-50 selon la table) ET non taggé (VLAN 10 via PVID 1) sans coupure.

Mode permissif MikroTik SwOS (VLAN Mode + VLAN Receive)

⚠️ Pourquoi laisser en optional : le mode permissif fonctionne pour le rollout progressif. Passer en strict (durcissement) plus tard, une fois tous les services migrés.

Chemin validé (test ALIX sur port 8)

ALIX (VLAN 20-50) → Switch port 8 → port 1 → Répéteur lan1 → br-vxX → VXLAN → [WiFi] → OPNsense → gateways

Validation tcpdump (3 points d’écoute simultanés) :

Point d’écouteTrafic observéConclusion
ALIX vlan20ICMP echo request + reply✅ émission + retour
Répéteur lan1.20ICMP echo request + reply✅ switch → répéteur
OPNsense vxlan1ICMP echo request + reply✅ VXLAN → OPNsense

Ping des 4 VLANs depuis l’ALIX (port 8) :

VLANGatewayPingStatut
20 (DMZ)192.168.20.10% perte
30 (IOT)192.168.30.10% perte
40 (GUEST)192.168.40.10% perte
50 (HOMELAB)192.168.50.10% perte

Convention de câblage (rappel, section 8.1)

PortÉquipementRôle
1Répéteur (lan1)Uplink → VXLAN (chemin critique)
2pve2Hyperviseur
3pve0Hyperviseur
4pve3Hyperviseur
5pve4Hyperviseur
6NASAccess PVID 10
7ALIX (vr0)Test VLAN
8ALIX (vr1)Test VLAN

✅ Point d’étape — où en est-on à la fin de la section 8

Toutes les appliances réseau sont maintenant configurées pour les VLANs et le VXLAN :

ÉquipementRôleVLANs
OPNsensebridges + VXLAN (section 7)10, 20, 30, 40, 50
AP OpenWrtSSIDs + vlan_filtering (section 6)10, 30, 40
Répéteur OpenWrtVXLAN + bridges br-vxX (section 7)10, 20, 30, 40, 50
Switch CSS610VLAN membership (section 8)10, 20, 30, 40, 50

Le réseau (cœur, WiFi, switch) transporte désormais tous les VLANs : les 4 VLANs (DMZ, IOT, GUEST, HOMELAB) traversent le switch, le répéteur, le WiFi et OPNsense de bout en bout.

Il reste à couvrir : les derniers équipements — les hyperviseurs Proxmox (PVE) et les VMs (section 9), qui doivent consommer ces VLANs (trunk sur pve1, MTU ajusté sur les VMs, etc.).


9. VLAN sur les hyperviseurs

9.1. VLAN sur les hyperviseurs (config identique sur tous les pve)

Contexte : la configuration VLAN est identique sur les 5 nœuds Proxmox (pve0, pve1, pve2, pve3, pve4) : vmbr0 VLAN-aware + une sous-interface vmbr0.50 pour le management. Seuls les IPs changent. pve1 est connecté directement à OPNsense (igb3, chemin filaire sans VXLAN) ; les autres passent par le switch + le WiFi.

NœudIP LAN (vmbr0)IP management (vmbr0.50)Chemin
pve0192.168.10.10192.168.50.10switch → WiFi
pve1192.168.10.11192.168.50.11direct igb3
pve2192.168.10.12192.168.50.12switch → WiFi
pve3192.168.10.13192.168.50.13switch → WiFi
pve4192.168.10.14192.168.50.14switch → WiFi

Architecture

pve1 (enp1s0) → igb3 → vlan0.3.20/30/40/50 → bridge1-4 → gateways    [chemin filaire direct]
pve0/2/3/4 → switch → répéteur → VXLAN → OPNsense → gateways          [chemin WiFi]

OPNsense : igb3 est membre de bridge0 (VLAN 10 natif) ET porte les VLANs enfants (vlan0.3.20/30/40/50, membres de bridge1-4). Sur les autres pve, le trafic passe par le switch et le VXLAN (section 7).

Config pve — vmbr0 VLAN-aware

/etc/network/interfaces (identique sur tous les pve, seule l’adresse IP change) :

auto lo
iface lo inet loopback
iface <interface physique> inet manual

auto vmbr0
iface vmbr0 inet static
    address <IP_LAN>/24           # ex: 192.168.10.11 (pve1)
    gateway 192.168.10.1
    bridge-ports <interface physique>
    bridge-stp off
    bridge-fd 0
    bridge-vlan-aware yes
    bridge-vids 2-4094

auto vmbr0.50
iface vmbr0.50 inet static
    address <IP_MGMT>/24          # ex: 192.168.50.11 (pve1)
source /etc/network/interfaces.d/*

(Exemple réel pve1 : interface enp1s0, IP LAN 192.168.10.11, IP mgmt 192.168.50.11 — voir le tableau des IPs ci-dessus pour les autres nœuds.)

Le bridge-vlan-aware yes permet aux VMs/CTs d’utiliser le tag VLAN via bridge=vmbr0,tag=XX.

Bridge vmbr0 VLAN-aware Proxmox

Utilisation dans une VM/CT

Le VLAN est choisi par le tag sur l’interface de la VM :

  • net0: bridge=vmbr0, tag=20, ip=dhcp → VLAN 20 (DMZ)
  • net0: bridge=vmbr0, tag=50, ip=dhcp → VLAN 50 (HOMELAB)

Validation LXC 112 (Debian) : 4 interfaces avec tags 20/30/40/50, toutes ont obtenu une IP DHCP via OPNsense Kea.

Sortie ip a du LXC Debian (les 4 interfaces VLAN) :

root@debian:~# ip a
2: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether bc:24:11:e2:01:5a brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.20.100/24 brd 192.168.20.255 scope global dynamic eth0
3: eth1@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether bc:24:11:59:c3:e6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.30.103/24 brd 192.168.30.255 scope global dynamic eth1
4: eth2@if30: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether bc:24:11:91:d8:f1 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.40.101/24 brd 192.168.40.255 scope global dynamic eth2
5: eth3@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether bc:24:11:94:a0:46 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.50.101/24 brd 192.168.50.255 scope global dynamic eth3
InterfaceTag VLANIP DHCPSubnet
eth020 (DMZ)192.168.20.100.20.0/24
eth130 (IOT)192.168.30.103.30.0/24
eth240 (GUEST)192.168.40.101.40.0/24
eth350 (HOMELAB)192.168.50.101.50.0/24

(Chaque interface virtio a obtenu une IP DHCP distincte de son subnet via OPNsense Kea — les 4 VLANs sont opérationnels sur le LXC.)

LXC debian Proxmox — 4 interfaces VLAN (20/30/40/50)

Script de test d’isolation (LXC Debian, équivalent de test-vlans.sh sur ALIX) :

#!/bin/sh
# test-vlans-lxc.sh — Test d'isolation inter-VLAN (Debian/LXC)
# Usage: sh test-vlans-lxc.sh
# Les IPs sources sont récupérées dynamiquement (DHCP)

echo "=== TEST ISOLATION VLAN (gateway = doit répondre, autres = bloqués) ==="
echo ""

# Récupérer l'IP d'une interface
get_ip() {
    ip -4 -o addr show "$1" 2>/dev/null | awk '{print $4}' | cut -d/ -f1
}

# Tester un flux depuis une interface source vers une destination
test_ping() {
    if ping -I "$1" -c2 -w2 "$2" >/dev/null 2>&1; then
        echo "  vers $2 ($3) : ⚠️ RÉPOND (attendu: BLOQUÉ)"
    else
        echo "  vers $2 ($3) : ✅ bloqué"
    fi
}

# Tester la gateway d'un VLAN : doit répondre
test_gateway() {
    if ping -I "$1" -c2 -w2 "$2" >/dev/null 2>&1; then
        echo "  gateway $2     : ✅ RÉPOND"
    else
        echo "  gateway $2     : ❌ PERDU (attendu: répond)"
    fi
}

# --- VLAN 20 (DMZ) — eth0 ---
IP20=$(get_ip eth0)
[ -n "$IP20" ] && echo "--- VLAN 20 (DMZ) interface eth0 source $IP20 ---" || echo "--- VLAN 20 (DMZ) eth0 ❌ pas d'IP ---"
if [ -n "$IP20" ]; then
    test_gateway eth0 192.168.20.1
    test_ping eth0 192.168.30.1 "IOT"
    test_ping eth0 192.168.10.1 "LAN"
fi
echo ""

# --- VLAN 30 (IOT) — eth1 ---
IP30=$(get_ip eth1)
[ -n "$IP30" ] && echo "--- VLAN 30 (IOT) interface eth1 source $IP30 ---" || echo "--- VLAN 30 (IOT) eth1 ❌ pas d'IP ---"
if [ -n "$IP30" ]; then
    test_gateway eth1 192.168.30.1
    test_ping eth1 192.168.10.1 "LAN"
    test_ping eth1 192.168.20.1 "DMZ"
fi
echo ""

# --- VLAN 40 (GUEST) — eth2 ---
IP40=$(get_ip eth2)
[ -n "$IP40" ] && echo "--- VLAN 40 (GUEST) interface eth2 source $IP40 ---" || echo "--- VLAN 40 (GUEST) eth2 ❌ pas d'IP ---"
if [ -n "$IP40" ]; then
    test_gateway eth2 192.168.40.1
    test_ping eth2 192.168.10.1 "LAN"
fi
echo ""

# --- VLAN 50 (HOMELAB) — eth3 ---
IP50=$(get_ip eth3)
[ -n "$IP50" ] && echo "--- VLAN 50 (HOMELAB) interface eth3 source $IP50 ---" || echo "--- VLAN 50 (HOMELAB) eth3 ❌ pas d'IP ---"
if [ -n "$IP50" ]; then
    test_gateway eth3 192.168.50.1
    test_ping eth3 192.168.10.1 "LAN"
fi
echo ""

echo "=== RÉSULTAT ==="
echo "  ✅ = conforme au comportement attendu"
echo "  ⚠️ = anomalie (un flux qui devrait être bloqué passe)"
echo "  ❌ = la gateway du VLAN ne répond pas (problème VLAN)"

(Équivalent Linux de test-vlans.sh : les interfaces eth0-3 correspondent aux VLANs 20-50, la source est spécifiée par ping -I <interface> au lieu de -S <IP>.)

Sortie réelle du script exécuté sur le LXC :

$ sh ./test-vlans-lxc.sh
=== TEST ISOLATION VLAN (gateway = doit répondre, autres = bloqués) ===

--- VLAN 20 (DMZ) interface eth0 source 192.168.20.100 ---
  gateway 192.168.20.1     : ✅ RÉPOND
  vers 192.168.30.1 (IOT) : ⚠️ RÉPOND (attendu: BLOQUÉ)
  vers 192.168.10.1 (LAN) : ⚠️ RÉPOND (attendu: BLOQUÉ)

--- VLAN 30 (IOT) interface eth1 source 192.168.30.103 ---
  gateway 192.168.30.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué
  vers 192.168.20.1 (DMZ) : ✅ bloqué

--- VLAN 40 (GUEST) interface eth2 source 192.168.40.101 ---
  gateway 192.168.40.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué

--- VLAN 50 (HOMELAB) interface eth3 source 192.168.50.101 ---
  gateway 192.168.50.1     : ✅ RÉPOND
  vers 192.168.10.1 (LAN) : ✅ bloqué

=== RÉSULTAT ===
= conforme au comportement attendu
  ⚠️ = anomalie (un flux qui devrait être bloqué passe)
= la gateway du VLAN ne répond pas (problème VLAN)

⚠️ Remarque : ce test révèle que certaines règles firewall sont encore à revoir et ajuster. Le VLAN 20 (DMZ) répond vers IOT et LAN alors qu’il devrait être bloqué — la cause suspectée est la règle ICMP (self) (qui autorise le ping vers toutes les IPs d’OPNsense, voir piège #11) et/ou l’ordre des règles DMZ. À traiter lors du durcissement (phase zero trust). Les VLANs IOT, GUEST et HOMELAB sont correctement isolés.

Accès admin VLAN 50 (management PVE)

  • L’IP de gestion vmbr0.50 = 192.168.50.X (ex: .11 pour pve1, voir tableau des IPs)
  • Le Mac (VLAN 10) joint le pve via la règle pass lan → any (défaut)
  • L’accès SSH (22) + Proxmox GUI (8006) validé sur tous les nœuds

Sous-interface vmbr0.50 Proxmox


9.2. Réglage MTU sur les VMs VLAN (chemin VXLAN)

Suite au réglage MTU VXLAN (section 7.6), les VMs des VLAN 20-50 doivent avoir leur MTU réduit à 1400 (elles passent par le tunnel VXLAN). Ce réglage se fait après la configuration du switch et des hyperviseurs.

Neptune (VM 1000, NetBSD, pve4) — VLAN 50 (.50.103) :

Fichier créé/modifié /etc/ifconfig.vioif0 :

up
mtu 1400

(Fichier d’interface NetBSD, lu au boot — méthode native NetBSD pour la persistance)

Commande immédiate : ifconfig vioif0 up mtu 1400

Saturne (VM 1001, FreeBSD, pve4) — VLAN 50 (.50.104) :

Fichier /etc/rc.conf — ligne modifiée :

ifconfig_vtnet0="DHCP mtu 1400"

(DHCP conservé, mtu 1400 ajouté — FreeBSD n’applique pas le MTU de l’hyperviseur)

Commande immédiate : ifconfig vtnet0 mtu 1400

Haiku (VM 111, Haiku OS, pve4) — VLAN 50 (.50.105) :

Fichier ~/config/settings/boot/UserBootscript — ligne ajoutée :

ifconfig /dev/net/virtio/0 mtu 1400

(UserBootscript Haiku exécuté à chaque démarrage)

Commande immédiate : ifconfig /dev/net/virtio/0 mtu 1400

Toutes (Proxmox, côté hyperviseur) : net0 de chaque VM reçoit mtu=1400 (Hardware → Network Device).

⚠️ Important : le mtu=1400 dans Proxmox ne suffit PAS seul pour FreeBSD (qui garde 1500) — il faut le mettre dans l’OS. Pour NetBSD/Haiku, le fichier d’interface/bootscript est la persistance.

⚠️ Portée : ce piège concerne tout accès à une VM VLAN 20-50 depuis le LAN (via le chemin VXLAN). Le trafic inter-VM sur le même pve (même bridge, sans VXLAN) n’est pas affecté.

Nuances et validation

⚠️ Nuance NetBSD/Haiku : neptune (NetBSD) et haiku (Haiku) peuvent se connecter en SSH à MTU 1500 depuis une VM du même pve (chemin local sans VXLAN), et même parfois depuis le LAN grâce à une meilleure gestion PMTUD — mais c’est lent (retransmissions) et fragile. Configurer à 1400 rend la connexion rapide et fiable.

⚠️ Piège du test depuis le même pve : tester le SSH depuis neptune vers une autre VM du même pve4 ne valide PAS le chemin VXLAN (bridge local, pas de tunnel). Le test valide est depuis le Mac/LAN (qui traverse le VXLAN).

✅ Validé (2026-08-12) : neptune (NetBSD, /etc/ifconfig.vioif0), saturne (FreeBSD, rc.conf) et haiku (Haiku, UserBootscript) — tous à MTU 1400, SSH fonctionnel depuis le Mac.


10. Conclusion

La migration VLAN de tout le réseau est opérationnelle : segmentation par usage (USER, DMZ, IOT, GUEST, HOMELAB), transport des VLANs sur le WiFi via VXLAN, isolation inter-VLAN assurée par le firewall OPNsense.

La configuration VLAN est en place sur toutes les briques :

ÉquipementÉtat VLAN
Appliances réseau (OPNsense, AP, répéteur, switch)✅ Configurées pour les VLANs + VXLAN (sections 5-8)
Hyperviseurs pve (pve0-4)vmbr0 VLAN-aware + vmbr0.50 management (section 9.1)
VMs/LXC de test✅ En VLAN 50 (HOMELAB) pour valider : haiku, neptune, saturne (+ debian/alpine multi-VLAN)

Quelques VMs et LXC ont été configurés en VLAN 50 pour valider et tester le chemin complet (recensement : haiku, neptune, saturne sur VLAN 50 ; debian et alpine sur les 4 VLANs 20-50).

Les équipements IOT ont aussi été reconfigurés pour utiliser le VLAN 30 (IOT) : caméra, robot aspirateur (Ecovacs), chaudière connectée.

Prochaines étapes

La migration des services actifs (portainer, caddy, prometheus, grafana, haos, devbox, etc.) depuis le VLAN natif vers leurs VLANs cibles, ainsi que le durcissement zero trust (règles firewall granulaires, passage en strict sur le switch), pourront faire l’objet d’un prochain article — cette configuration de test sur quelques VMs/LXC suffit à documenter la méthode.

Architecture finale

┌───────────┐
│  Livebox  │
│  .1.1 WAN │
└─────┬─────┘
      │ igb0
┌─────┴───────────────────────────────────────────────────────────────┐
│  OPNsense (.1) — FreeBSD                                            │
│                                                                     │
│  igb0 ── WAN                                                        │
│                                                                     │
│  bridge0 = LAN (.10.1)  [igb2 + igb3 + igb1]                        │
│     │               │               │                               │
│  igb1 (trunk)      igb2 (AP)      igb3 (pve1)                       │
│  │   vlan0.1.20-50   │   vlan0.2.20-50   │   vlan0.3.20-50          │
│  │                   │                   │                          │
│  ├─ bridge1 = DMZ (.20.1)   [vlan0.1/2/3.20 + vxlan1]               │
│  ├─ bridge2 = IOT  (.30.1)  [vlan0.1/2/3.30 + vxlan2]               │
│  ├─ bridge3 = GUEST(.40.1)  [vlan0.1/2/3.40 + vxlan3]               │
│  ├─ bridge4 = HOMELAB(.50.1)[vlan0.1/2/3.50 + vxlan4]               │
│  │                                                                  │
│  vxlan1-4 (VNI 20/30/40/50, UDP 4789)                               │
└─────┼───────────────────────────────────────────────────────────────┘
      │ igb2 (filaire) — porte VLANs taggés + VXLAN (UDP 4789)
┌─────┴───────────────────────────────────────────────────────────────┐
│  AP OpenWrt (.2)                                                    │
│  br-lan vlan_filtering=1                                            │
│  eth1 ← igb2                                                        │
│  bridge-vlan: 10 natif 20-50 taggé                                  │
│  SSIDs: Vince-AC → VLAN 10, Vince-IOT → VLAN 30, Vince-Guest → 40   │
│  (l'AP forwarde le VXLAN : eth1 ↔ phy1-ap0, bridge transparent)     │
└──────┬──────────────────────────────────────────────────────────────┘
       │ [WiFi WDS] — encapsule les VXLANs (UDP 4789) + VLAN 10
       │ phy1-ap0 ~~~~~~~~~~  phy1-sta0
┌──────┴──────────────────────────────────────────────────────────────┐
│  Répéteur (.254) — MT7986 filogic                                   │
│  br-lan [lan1+lan2+lan3+phy1-sta0]                                  │
│  vxlan20/30/40/50 (VNI 20-50) — décapsule depuis le WiFi WDS        │
│  br-vx20 = lan1.20+lan2.20+vxlan20                                  │
│  br-vx30 = lan1.30+lan2.30+vxlan30                                  │
│  br-vx40 = lan1.40+lan2.40+vxlan40                                  │
│  br-vx50 = lan1.50+lan2.50+vxlan50  (lanX.Y créées par rc.local)    │
└──────┬──────────────────────────────────────────────────────────────┘
       │ lan1 (filaire)
┌──────┴──────────────────────────────────────────────────────────────┐
│  Switch CSS610 (.5) — SwOS                                          │
│  VLANs 20/30/40/50 = ports 1+2+3+4+5+6+8                            │
│  (tagged, mode permissif PVID 1)                                    │
└──┬───────────┬───────────┬───────────┬────────────────┬────────┬────┘
   │           │           │           │                │        │
┌──┴───────┐┌──┴───────┐┌──┴───────┐┌──┴───────┐┌───────┴────┐┌──┴───────┐
│ alix(.23)││ pve0(.10)││ pve2(.12)││ pve3(.13)││ pve4(.14)  ││ nas(.3)  │
│ OpenBSD  ││ Proxmox  ││ Proxmox  ││ Proxmox  ││ Proxmox    ││ Synology │
└──────────┘└──────────┘└──────────┘└──────────┘└────────────┘└──────────┘

Chemin du VXLAN :

OPNsense vxlan1-4 → igb2 → AP eth1 → [WiFi WDS] → répéteur phy1-sta0 → br-vxX

Bridges OPNsense (après implémentation VLAN + VXLAN) :

BridgeIP (SVI)VLANMembres
bridge0192.168.10.110 (natif)igb2, igb3, igb1
bridge1192.168.20.120 (DMZ)vlan0.1.20, vlan0.2.20, vlan0.3.20, vxlan1
bridge2192.168.30.130 (IOT)vlan0.1.30, vlan0.2.30, vlan0.3.30, vxlan2
bridge3192.168.40.140 (GUEST)vlan0.1.40, vlan0.2.40, vlan0.3.40, vxlan3
bridge4192.168.50.150 (HOMELAB)vlan0.1.50, vlan0.2.50, vlan0.3.50, vxlan4

Mapping AP OpenWrt (.2) :

ÉlémentValeurRôle
eth0LibrePort physique disponible
eth1igb2 (OPNsense)Uplink filaire
br-laneth0 + eth1Bridge principal, vlan_filtering=1
br-lan.10192.168.10.2VLAN 10 natif (LAN)
br-lan.30192.168.30.2VLAN 30 (IOT) — IP gestion
br-lan.40192.168.40.2VLAN 40 (GUEST) — IP gestion
Vince-AC5 GHz, wds=1SSID principal → VLAN 10
Vince-IOT5 GHzSSID IOT → VLAN 30
Vince-Guest2.4 GHzSSID GUEST → VLAN 40

bridge-vlan (trunk eth0 + eth1) : VLAN 10 u* (natif) + VLAN 20/30/40/50 t (taggés).

Mapping Répéteur (.254) :

ÉlémentValeurRôle
lan1Switch MikroTik (port 1)Port switch
lan2ALIX vr1Port test VLAN
lan3LibrePort disponible
br-lanlan1 + lan2 + lan3 + phy1-sta0Bridge principal, 192.168.10.254
phy1-sta0[WiFi WDS] → APLien uplink WiFi (Vince-AC)
vxlan20VNI 20, remote 192.168.10.1Tunnel VXLAN → OPNsense (DMZ)
vxlan30VNI 30Tunnel VXLAN → OPNsense (IOT)
vxlan40VNI 40Tunnel VXLAN → OPNsense (GUEST)
vxlan50VNI 50Tunnel VXLAN → OPNsense (HOMELAB)

Bridges VXLAN du répéteur :

BridgeMembresVLAN
br-vx20lan1.20 + lan2.20 + vxlan2020
br-vx30lan1.30 + lan2.30 + vxlan3030
br-vx40lan1.40 + lan2.40 + vxlan4040
br-vx50lan1.50 + lan2.50 + vxlan5050

Les sous-interfaces lanX.Y (8021q) sont créées par /etc/rc.local (netifd ne les crée pas sur DSA MT7986).



Annexes

Pièges rencontrés (leçons apprises)

Tableau unifié des pièges, classés par catégorie (équipement ou sujet) :

#CatégoriePiègeSymptômeSolution / Contournement
1OPNsenseModule if_vxlan non chargéVXLAN ne fonctionne paskldload if_vxlan (persister via tunables)
2OPNsenseBridge FreeBSD non VLAN-awareARP ne répond pasCréer des sous-interfaces VLAN + bridge par VLAN
3OPNsenseParent VLAN doit être une interface physiqueImpossible de créer sur un bridgeChoisir igbX, pas bridge0
4OPNsenseNommage imposé (vlan0.1.20, bridgeX)vlan20 refuséUtiliser la Description comme nom logique
5OPNsenseICMP (self) = This Firewall = toutes les IPsLe VLAN 50 pouvait pinger le LAN (.10.1)Destination = IP de la gateway uniquement
6OPNsenseSyntaxe FreeBSD VXLAN (vxlanport 4799)Invalid argumentifconfig vxlan create vxlanid <vni> vxlanlocal <ip> vxlanremote <ip> (port 4789)
7OPNsenseDefault deny bloque le ping vers la gatewayPing gateway sans réponseAjouter une règle ICMP → gateway
8OPNsenseSlots opt réutilisés (opt3 réutilisé)Confusion d’interfacesNe pas se fier aux numéros opt, vérifier la Description
9OPNsenseRetirer un membre de bridge à chaud corrompt l’apprentissage MACARP sans réponse, DHCP passe mais pas l’unicastRetrait via l’UI (persistant) + reboot
10OPNsenseBridge multi-membres (vlan0.1.20 + vlan0.2.20)Apprentissage MAC ambiguLégitime (2 chemins) mais valider chaque membre séparément
11OpenWrtBusyBox ip link add type vxlan NE supporte PAS VXLANInterface Ethernet générique (type=1)Utiliser netifd via UCI/UI (proto vxlan)
12OpenWrtnetifd échoue à créer les sous-interfaces 8021q (DSA MT7986)Sous-interfaces absentesrc.local (créations manuelles ip link)
13OpenWrtVXLAN : ifup seul ne crée pas l’interfaceip addr show vxlan20 → device not foundnetwork restart obligatoire
14OpenWrtVXLAN : peeraddr requis (pas seulement remote)Interface sans remote noyauDéfinir remote ET peeraddr
15OpenWrtVXLAN : vtep n’existe pasConfig invalideNe pas définir vtep (proto netifd natif)
16OpenWrtFirewall : vxlanX hors zone lanDestination Port UnreachableAjouter vxlanX à la zone lan
17OpenWrtip link show <iface1> <iface2> multi-argsPas de sortieVérifier interface par interface
18OpenWrtUI LuCI vs UCI manuelSyntaxe UCI directe peut échouerPrivilégier l’UI LuCI (syntaxe exacte)
19OpenWrtMAC du bridge change au reloadIP instableRéserver par MAC stable ou IP statique
20OpenWrtAP : IP sur br-lan brut avec vlan_filteringPerte de connectivité, rollback LuCIIP sur br-lan.10 (sous-interface VLAN)
21VXLANMTU 1446 obligatoire (overhead 50 octets)Fragmentation, SSH Connection resetmtu 1446 tunnel + mtu 1400 sur VMs
22VXLANUn port physique dans UN seul bridgeTrafic mal routé (br-lan + br-vx20)Retirer le port de br-lan avant br-vx20
23PVEbridge-vlan-aware yes + bridge-vids 2-4094 manquantsTags VLAN ignorésAjouter ces options au trunk
24PVELa sous-interface vmbr0.50 donne une IP admin cross-VLANPratique pour l’admin VLAN 50
25PVEFirewall OPNsense : accès LAN → HOMELABPassage par règle pass lan → anyRègle granulaire à préparer (durcissement)
26SwOSModifier le PVID des ports existantsCoupure réseauRester en PVID 1 transparent
27SwOSCâble mal branché = faux négatifTest switch échoue à tortVérifier physiquement le port avant de douter
28SwOSMode optional/any permissifTrafic entre ports non configurésFonctionnel pour rollout, durcissement strict plus tard
29FirewallRFC1918 en bloc quand le WAN est en 192.168.xInternet coupé (WAN NATé bloqué)Alias dynamiques (IOT networks…) ou interfaces comme dest
30FirewallRègle DNS manquante pour les VLANsInternet bloqué (DNS self:53 non couvert)PASS VLAN → self:53 AVANT la règle Internet
31FirewallOrdre des règles = sécuritéIsolation ou DNS cassé1re règle qui matche gagne ; BLOCK avant PASS Internet
32FirewallTrafic L2 local jamais filtréGuest→Guest (même VLAN) passeNe pas inclure le réseau de l’interface dans le BLOCK
33FirewallLog désactivé sur les règles BLOCKBlocages invisiblesLog = ☑️ sur chaque règle BLOCK
34ALIXParent OpenBSD non UPPing 100% perte, tcpdump videifconfig vr1 up (le parent doit être monté)
35ALIXping -I vlan20 invalideCommande refuséeUtiliser ping -S <IP_source>
36ALIXdhclient absentPas d’IP DHCPFichier /etc/hostname.vlanXX + netstart
37ALIXIPs “gateway” dans netstat -rn = IP localesConfusionNormal, routes connectées
38WiFiath10k strippe les tags VLAN même en WDS (4-adresses)0 paquet 802.1QEncapsuler : VXLAN ou SSIDs dédiés
39OPNsenseUpgrade OPNsense a cassé les VXLANVLANs 20-50 morts derrière le répéteur (pas de DHCP, ARP incomplete), VLAN 10 OKRéactiver : Interfaces → Devices → VXLAN → recréer le device + Apply

Diagnostic et troubleshooting

Comment diagnostiquer un problème et avec quels outils :

OutilUsageExemple de diagnostic
tcpdumpÉcouter des deux côtés (émetteur + récepteur)Localiser l’arrêt d’un flux VLAN/VXLAN
arp -n <gateway>Tester le chemin L2Séparer “le VLAN traverse” de “le firewall bloque”
ping -s <taille>Tester la fragmentation/MTUDétecter un trou noir MTU (blackhole)
ifconfig / ip linkVérifier interfaces et bridgesMembres de bridge, sous-interfaces créées
lsmod / kldstatVérifier les modules noyauvxlan (OpenWrt), if_vxlan (FreeBSD)
Logs firewallVérifier les blocagesLe default deny n’a pas de logging → ne pas conclure “pas de trafic” d’un log vide

Tableau de suivi des étapes

PhaseActionRisqueStatut
2bWiFi Guest (VLAN 40) + IOT (VLAN 30) sur l’AP — SSIDs dédiés, isolation + Internet validés✅ Fait
2cRépéteur converti en WDS — lien 4-adresses ponté dans br-lan (section 6.5)✅ Fait
3Switch MikroTik CSS610 : VLANs opérationnels — table VLANs 20-50, 4 VLANs traversent (section 8.1)✅ Fait
3bTest chemin complet : ALIX sur le switch — valide Switch → Répéteur → VXLAN → OPNsense✅ Fait
3cVXLAN OPNsense ↔ répéteur — encapsulation UDP 4789, 4 VLANs traversent le WiFi (section 7)✅ Fait
3dVLAN sur les pve — vmbr0 VLAN-aware, tags validés (section 9.1)✅ Fait
4Configurer les ports services (pve, NAS) en trunk/access sur le switchMoyenÀ faire
5Migrer les services vers HOMELAB (50)MoyenÀ faire
6Migrer Caddy vers DMZ (20)MoyenÀ faire
7Migrer haos vers IOT (30)MoyenÀ faire
8Durcissement zero trust (règles firewall granulaires)FaibleÀ faire
9Durcissement SwOS (passage en strict)FaibleÀ faire

Convention de câblage du switch MikroTik CSS610

PortÉquipementMode SwOS prévuRôle
1RépéteurTrunk 10-50Uplink → OPNsense (chemin critique)
2pve2Trunk 10-50Hyperviseur
3pve0Trunk 10-50Hyperviseur
4pve3Trunk 10-50Hyperviseur
5pve4Trunk 10-50Hyperviseur
6NASAccess PVID 10Synology (reste sur USER)
7ALIX (vr0)Access PVID 20Test VLAN 20 sans tagging
8ALIX (vr1)Trunk 10-50Test multi-VLAN taggé
9-10SFP+DésactivésInutilisés

Logique : uplink + pve en trunk (porteront les VLANs à la Phase 4), NAS en access 10 (confiance), ALIX sur 2 ports (access 20 + trunk) pour tester les 2 modes en parallèle.


Références officielles

Documents officiels utiles pour approfondir chaque sujet :

SujetLien
WiFi — Extender/Repeater WDSOpenWrt Wiki
WiFi — Extender/Repeater relaydOpenWrt Wiki
VLAN OpenWrt (DSA/8021q)OpenWrt Wiki
VLAN SwOS (MikroTik)CSS610 series Manual
IEEE 802.1Q (VLANs)Wikipedia
VXLAN (RFC 7348)IETF RFC 7348
VXLAN OPNsenseOPNsense Docs
VLAN et LAGG OPNsenseOPNsense Docs
VXLAN OpenWrtOpenWrt Wiki
VLAN ProxmoxProxmox Wiki
Kea DHCP OPNsenseOPNsense Docs
Unbound DNS OPNsenseOPNsense Docs
OpenBSD vlan(4)OpenBSD man page