Arrancas una VM. Se inicia. Obtiene una IP. El DNS parece correcto. Y sin embargo: no hay Internet. El ping al gateway falla como si estuviera en huelga. Empiezas a murmurar sobre “los cambios en la red de Ubuntu” y “tal vez la imagen está rota”, y antes de darte cuenta has reiniciado libvirtd tres veces y no has aprendido nada.
Esto casi siempre es un problema de capas: diseño de puente equivocado, manejo de VLAN incorrecto, ruta por defecto mal, o el firewall del host haciendo su trabajo un poco demasiado bien. La solución no es “probar configuraciones al azar hasta que funcione”. La solución es construir el puente/VLAN correctamente y luego verificarlo como un SRE: un salto, una tabla, una decisión a la vez.
Un modelo mental sensato: qué debe ser cierto para que una VM llegue a Internet
Cuando una VM “no tiene Internet”, es tentador tratarlo como un único problema. No lo es. Es una cadena de condiciones. Rompe cualquier eslabón y tu VM se convierte en un localhost muy caro.
La cadena no negociable
- El enlace del invitado está arriba: la NIC virtio/e1000 presente, carrier activo, MAC correcta, driver correcto.
- El invitado tiene la configuración IP correcta: IP/máscara, ruta por defecto, DNS.
- El tráfico del invitado sale del invitado: ARP/ND funciona, los paquetes salen de la NIC del invitado.
- El host reenvía L2 correctamente: la interfaz tap/vnet de la VM está esclavizada al puente correcto; el puente está arriba y haciendo forwarding.
- El etiquetado VLAN coincide con la realidad: tramas sin tag vs tagueadas son exactamente lo que espera el switch ascendente.
- El host tiene un enlace al red real: el puente está conectado a la NIC física (o a la subinterfaz VLAN correcta), con carrier.
- El puerto del switch ascendente es correcto: access vs trunk, VLANs permitidas, comportamiento de VLAN nativa conocido (no asumido).
- El filtrado no está bloqueando en silencio: nftables/iptables del host, filtros de libvirt, firewall del invitado por cloud-init, o rp_filter.
- El camino más allá del gateway funciona: NAT, enrutamiento, o ACLs ascendentales permiten el tráfico.
La mayoría de las interrupciones están en el medio: el puente del host está bien pero el etiquetado VLAN es incorrecto, o la VM está enchufada al puente equivocado, o se usa NAT de libvirt cuando crees que estás en modo puenteado.
Un principio operativo: deja de adivinar dónde murió el paquete. Pon ojos en cada salto hasta encontrar el primer punto donde la realidad diverge de tu modelo mental. Ahí está la solución.
Hechos y contexto interesantes (porque el pasado sigue en tu red)
- El bridging en Linux existe desde principios de los 2000, y creció junto a la virtualización; KVM no inventó la necesidad, solo la industrializó.
- El etiquetado VLAN es anterior a la mayoría de “productos” de red en la nube. IEEE 802.1Q apareció a finales de los 90 y sigue ganando por simplicidad: un cable, muchas redes.
- La red por defecto de Libvirt (virbr0) es NAT. Es genial para portátiles y terrible para “mi VM debe estar en la misma LAN que todo lo demás”.
- Netplan no es un demonio de red. Es un traductor de configuración que típicamente apunta a systemd-networkd en servidores (o NetworkManager en escritorios).
- Los bridges de Linux pueden filtrar y etiquetar VLANs (bridge VLAN filtering). Puedes hacer comportamiento tipo switch en el kernel, con membresía VLAN por puerto.
- Spanning Tree Protocol (STP) no es solo para switches físicos. Un puente Linux puede participar, y habilitar STP en el lugar equivocado puede añadir segundos de “¿por qué nada pasa?” tras el enlace arriba.
- El filtrado por ruta inversa (rp_filter) ha causado más incidentes de “ping funciona en un sentido” de lo que a cualquiera le gusta admitir, especialmente en hosts multihomed.
- La pila de red de systemd maduró mucho en la última década. Lo que antes requería pegamentos en /etc/network/interfaces ahora es declarativo y predecible—si declaras lo correcto.
- El comportamiento “nativo” de VLAN difiere por proveedor y por configuración. La palabra “nativa” es donde las suposiciones van a morir.
También: las VLAN son como organigramas. Todo el mundo piensa que son simples hasta que tiene que cambiar una.
Cita (idea parafraseada): Werner Vogels suele impulsar la idea de que “lo construyes, lo ejecutas”—el ciclo de retroalimentación operacional es parte de la ingeniería, no un añadido.
Guion de diagnóstico rápido
Este es el flujo “necesito señal en cinco minutos”. Asume que la VM debe estar en modo puente a una red real, opcionalmente vía VLAN.
Primero: prueba que la VM está realmente en el puente que crees
- Comprueba que la interfaz vnet/tap de la VM existe en el host.
- Confirma que está esclavizada al puente correcto (no virbr0, no algún puente huérfano).
- Confirma que el puente tiene el uplink físico adjunto (o la subinterfaz VLAN correcta adjunta).
Segundo: encuentra el primer salto fallido desde dentro de la VM
- Haz ping al gateway por defecto.
- Si eso falla, revisa la tabla ARP/neighbor y captura tráfico en la interfaz de la VM y en el uplink.
- Si el gateway funciona pero Internet no, comprueba DNS vs enrutamiento vs ACLs ascendentes.
Tercero: valida las expectativas de VLAN en host y switch
- Si la VM está sin tag, el puente/uplink debe ser access/native en la VLAN correcta.
- Si la VM está tagueada, o bien el invitado etiqueta tramas (802.1Q dentro del invitado) o el puente del host aplica tags (bridge VLAN filtering). Elige un enfoque y apégate a él.
- Confirma la lista de VLANs permitidas en el puerto del switch; “trunk” sin “allowed” es cómo desaparecen VLANs.
Cuarto: descarta firewall y opciones del kernel en el host
- Revisa el ruleset de nftables por drops sobre tráfico de puente.
- Confirma los ajustes bridge netfilter; podrías estar filtrando tránsito L2 sin darte cuenta.
- Revisa rp_filter en el host si es multihomed o existe policy routing.
Si sigues esa secuencia, normalmente encuentras el culpable antes de que se enfríe tu café.
Tareas prácticas: comandos, salida esperada y qué decides después
Estas son tareas operativas reales. Ejecútalas en orden hasta que la salida te diga dónde se detiene el paquete. Cada tarea incluye: comando(s), qué significa la salida y la decisión que tomas.
Task 1: Identificar el uplink del host y el estado del enlace
cr0x@server:~$ ip -br link
lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
enp3s0 UP 3c:ec:ef:12:34:56 <BROADCAST,MULTICAST,UP,LOWER_UP>
br0 UP 3c:ec:ef:12:34:56 <BROADCAST,MULTICAST,UP,LOWER_UP>
vnet3 UP fe:54:00:aa:bb:cc <BROADCAST,MULTICAST,UP,LOWER_UP>
Significado: enp3s0 tiene carrier (LOWER_UP). br0 existe y está UP. Si tu NIC física está DOWN/NO-CARRIER, detente aquí; no tienes uplink.
Decisión: Si el uplink está abajo, arregla cableado/switch/driver primero. Si está arriba, continúa con la pertenencia al puente.
Task 2: Confirmar pertenencia al puente (quién está enchufado a br0)
cr0x@server:~$ bridge link
3: enp3s0 state UP : <BROADCAST,MULTICAST,UP,LOWER_UP> master br0
5: vnet3 state UP : <BROADCAST,MULTICAST,UP,LOWER_UP> master br0
Significado: Tanto la NIC física como la interfaz de VM vnet3 están en br0. Si vnet3 está en virbr0 o sin master, la VM no está en la red prevista.
Decisión: Si la VM no está esclavizada al puente correcto, arregla el attachment de la NIC en libvirt antes de tocar VLANs o firewalls.
Task 3: Verificar que la IP del host vive en el puente (no en la NIC física)
cr0x@server:~$ ip -br addr show br0 enp3s0
br0 UP 192.0.2.10/24 fe80::3eec:efff:fe12:3456/64
enp3s0 UP fe80::3eec:efff:fe12:3456/64
Significado: La dirección IPv4 del host está en br0. Eso es lo que quieres para un diseño puenteado simple. Si IPv4 está en enp3s0 mientras br0 también está en juego, estás en territorio de “podría funcionar hasta que no”.
Decisión: Si la IP del host está en la interfaz equivocada, arregla netplan para que el puente tenga la IP.
Task 4: Comprobar que la VM ve IP, ruta y DNS
cr0x@server:~$ virsh domifaddr vm1
Name MAC address Protocol Address
-------------------------------------------------------------------------------
vnet3 fe:54:00:aa:bb:cc ipv4 192.0.2.50/24
Significado: La VM tiene una dirección IPv4. Eso no prueba que pueda alcanzar el gateway, pero demuestra que DHCP (o configuración estática) ocurrió.
Decisión: Si no hay dirección, depura DHCP/VLAN en el invitado antes de culpar al firewall del host.
Task 5: Desde dentro de la VM, prueba primer salto (gateway) y segundo salto (IP pública)
cr0x@server:~$ ping -c 3 192.0.2.1
PING 192.0.2.1 (192.0.2.1) 56(84) bytes of data.
64 bytes from 192.0.2.1: icmp_seq=1 ttl=64 time=0.547 ms
64 bytes from 192.0.2.1: icmp_seq=2 ttl=64 time=0.510 ms
64 bytes from 192.0.2.1: icmp_seq=3 ttl=64 time=0.522 ms
--- 192.0.2.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2040ms
cr0x@server:~$ ping -c 3 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=55 time=12.1 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=55 time=12.0 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=55 time=12.2 ms
Significado: Si el ping al gateway falla, tratas con L2/VLAN/adjunto del puente o un firewall local. Si el gateway funciona pero la IP pública falla, es enrutamiento/NAT/ACL ascendente.
Decisión: Elige la rama adecuada. No investigues DNS cuando no puedes alcanzar el gateway.
Task 6: Inspeccionar tabla ARP/neighbor para la MAC del gateway
cr0x@server:~$ ip neigh show
192.0.2.1 dev eth0 lladdr 00:11:22:33:44:55 REACHABLE
Significado: REACHABLE con una MAC significa que ARP completó. Si está INCOMPLETE/FAILED, la VM está gritando al vacío (o un desajuste de VLAN se está comiendo las tramas).
Decisión: Si ARP está incompleto, captura tráfico y verifica el etiquetado VLAN.
Task 7: Capturar tráfico en la interfaz vnet del host (¿la VM emite ARP?)
cr0x@server:~$ sudo tcpdump -ni vnet3 -c 10 arp or icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on vnet3, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:00:01.000000 ARP, Request who-has 192.0.2.1 tell 192.0.2.50, length 28
12:00:02.000000 ARP, Request who-has 192.0.2.1 tell 192.0.2.50, length 28
Significado: La VM está enviando ARP. Si nunca ves ARP aquí, el problema está dentro del invitado (interfaz equivocada, enlace abajo, ruta incorrecta) o la VM no está realmente conectada.
Decisión: Si hay ARP en vnet pero no regresa respuesta, la caída está entre el puente y el uplink (VLAN, puerto del switch o filtrado del host).
Task 8: Capturar en el uplink del host (¿sale ARP de la caja?)
cr0x@server:~$ sudo tcpdump -ni enp3s0 -c 10 arp
listening on enp3s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:00:01.000100 ARP, Request who-has 192.0.2.1 tell 192.0.2.50, length 28
Significado: Ver ARP en enp3s0 significa que el puente lo reenvió fuera. Si lo ves en vnet3 pero no en enp3s0, el puente está bloqueando o el filtrado VLAN está mal configurado.
Decisión: Si no sale de la máquina, inspecciona bridge VLAN filtering y el estado de STP.
Task 9: Inspeccionar la base de reenvío del puente (FDB) y aprendizaje de MAC
cr0x@server:~$ bridge fdb show br br0 | head
fe:54:00:aa:bb:cc dev vnet3 master br0
00:11:22:33:44:55 dev enp3s0 master br0
Significado: El puente aprendió la MAC de la VM en vnet3 y la MAC del gateway en enp3s0. Si la MAC del gateway nunca aparece, las respuestas ascendentes no están llegando (switchport/VLAN) o están siendo filtradas.
Decisión: No hay MACs aprendidas = busca desajuste de VLAN o STP bloqueando.
Task 10: Comprobar configuración VLAN en el host (dispositivos VLAN + bridge VLAN filtering)
cr0x@server:~$ ip -d link show br0 | sed -n '1,12p'
6: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 3c:ec:ef:12:34:56 brd ff:ff:ff:ff:ff:ff
bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 1 vlan_protocol 802.1Q
cr0x@server:~$ bridge vlan show
port vlan-id
enp3s0 10 PVID Egress Untagged
vnet3 10 PVID Egress Untagged
br0 10 PVID Egress Untagged
Significado: vlan_filtering=1 significa que el puente actúa como un switch consciente de VLAN. PVID 10 untagged en ambos puertos significa que el tráfico sin tag se trata como VLAN 10 y sale sin tag.
Decisión: Si tu puerto de switch espera VLAN 10 tagueada pero tú envías sin tag, arregla ya sea el puerto del switch o la configuración VLAN del puente. Elige una verdad.
Task 11: Comprobar ajustes netfilter/bridge que pueden filtrarse tráfico bridged en silencio
cr0x@server:~$ sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.bridge.bridge-nf-call-arptables
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-arptables = 0
Significado: Con bridge-nf-call-iptables=1, tu firewall del host puede filtrar tramas bridged reenviadas. Eso puede estar bien, pero es una causa común de “por qué solo las VMs fallan”.
Decisión: Si dependes de nftables para seguridad del host, mantenlo habilitado pero asegúrate de que tus reglas permitan el tráfico de las VMs. Si no, desactívalo deliberadamente y documenta por qué.
Task 12: Inspeccionar nftables por drops que afecten el forwarding
cr0x@server:~$ sudo nft list ruleset | sed -n '1,120p'
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
iif "lo" accept
ct state established,related accept
tcp dport 22 accept
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "br0" oifname "br0" accept
}
chain output {
type filter hook output priority filter; policy accept;
}
}
Significado: Política drop por defecto en forward está bien si existen aceptes explícitos. Aquí, el tráfico intra-bridge se acepta, pero el tráfico de br0 a enp3s0 no (porque oifname no es br0). Eso matará la salida de las VMs.
Decisión: Añade una regla para permitir el reenvío de br0 → uplink (o una regla más precisa por VLAN/subred). No hagas “policy accept” para evitar pensar.
Task 13: Confirmar el tipo de red en libvirt (puenteado vs NAT)
cr0x@server:~$ virsh domiflist vm1
Interface Type Source Model MAC
-------------------------------------------------------
vnet3 bridge br0 virtio fe:54:00:aa:bb:cc
Significado: Type=bridge Source=br0 significa que la VM está realmente puenteada. Si ves Type=network Source=default, estás en NAT de libvirt (virbr0).
Decisión: Si está en NAT pero esperabas puente, cambia la fuente de la NIC de la VM.
Task 14: Verificar rutas en el host (cuando el host también es tu router/NAT)
cr0x@server:~$ ip route
default via 192.0.2.1 dev br0 proto dhcp src 192.0.2.10 metric 100
192.0.2.0/24 dev br0 proto kernel scope link src 192.0.2.10
Significado: Caso simple: ruta por defecto del host via gateway LAN. Si tu diseño usa el host como router para VLANs de VM, necesitarás enrutamiento adicional y forwarding IP.
Decisión: Si el host debe enrutar entre VLANs, habilita ip_forward y confirma rutas de retorno correctas recordando a la red ascendente dónde están las subredes de las VMs.
Task 15: Comprobar IP forwarding y rp_filter en el host (casos de enrutamiento)
cr0x@server:~$ sysctl net.ipv4.ip_forward net.ipv4.conf.all.rp_filter net.ipv4.conf.br0.rp_filter
net.ipv4.ip_forward = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.br0.rp_filter = 1
Significado: ip_forward=0 significa que el host no va a enrutar paquetes. rp_filter=1 puede descartar tráfico asimétrico (común con policy routing o múltiples uplinks).
Decisión: Si el host es un router, habilita ip_forward y ajusta rp_filter apropiadamente (a menudo 2 para modo loose en escenarios multihomed).
Task 16: Validar que DHCP realmente viene de la VLAN esperada
cr0x@server:~$ sudo tcpdump -ni enp3s0 -c 20 udp port 67 or udp port 68
listening on enp3s0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:05:01.000000 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request
12:05:01.050000 IP 192.0.2.2.67 > 192.0.2.50.68: BOOTP/DHCP, Reply
Significado: Puedes ver que el servidor DHCP respondió. Si DHCP funciona pero ARP del gateway falla, podrías estar obteniendo DHCP de un relay o de una VLAN distinta a la que crees.
Decisión: Si la IP del servidor DHCP te sorprende, detente y reconcilia la configuración VLAN y del puerto del switch.
Broma #1: DHCP es como el café de oficina: cuando falta, todos de repente se vuelven ingenieros de red.
Patrones de diseño correctos (puente + VLAN) para hosts Ubuntu 24.04
Hay tres patrones que funcionan de forma fiable. El patrón malo es “un poco de cada uno” porque copiaste un fragmento de tres blogs distintos a las 2 a.m.
Patrón A: Puente sin tag (una sola VLAN / puerto access)
Usar cuando: tu host de virtualización y las VMs viven en una sola red, y el puerto del switch es un access port (o trunk con una VLAN nativa bien definida que realmente controlas).
Diseño: NIC física → puente br0; br0 tiene la IP del host; las VMs se conectan a br0. Sin filtrado VLAN. Sin subinterfaces VLAN.
Por qué es bueno: menos piezas móviles. Menos posibilidades de mal etiquetado. Menos discusiones sobre “VLANs permitidas” con el equipo de red.
Modo de fallo: alguien cambia el puerto del switch a trunk o te mueven a otra VLAN y no te avisan; te quedas sin tag y acabas en el lugar equivocado.
Patrón B: Subinterfaz VLAN en uplink + puente por VLAN
Usar cuando: quieres VMs en múltiples VLANs, pero prefieres mantener la lógica VLAN simple y explícita.
Diseño: enp3s0 es trunk en el switch. Crea interfaces VLAN en el host (enp3s0.10, enp3s0.20). Crea puentes br10 y br20, cada uno conectado a la correspondiente interfaz VLAN. Conecta VMs a br10 o br20.
Por qué es bueno: muy fácil de razonar. Puedes ver claramente qué está tagueado dónde. También es sencillo aplicar firewall por puente si es necesario.
Modo de fallo: la gente olvida permitir la VLAN en el trunk del switch. O conectan la VM a br0 (puente equivocado) y luego juran que las VLANs están “rotas”.
Patrón C: Puente consciente de VLAN con bridge VLAN filtering
Usar cuando: quieres un puente y que se comporte como un pequeño switch: uplink en trunk, puertos de acceso para VMs, quizá trunks a VMs especiales, y membresía VLAN controlada en el host.
Diseño: enp3s0 se adjunta directamente a br0; br0 tiene vlan_filtering=1. Asignas membresía VLAN por puerto (enp3s0 y vnetX). Las VMs pueden ser sin tag (access) o con tag (trunk) según la configuración.
Por qué es bueno: potente, escalable, topología limpia. Un puente, muchas VLANs, menos interfaces.
Modo de fallo: potente significa fácil de equivocarse. Un PVID faltante o una regla de egress incorrecta hace que el tráfico desaparezca sin drama ni disculpas.
Mi sesgo operacional: si eres pequeño-mediano y quieres menos sorpresas, el Patrón B es el punto dulce. Si estás construyendo una plataforma y puedes permitirte gestión disciplinada de la configuración y pruebas, el Patrón C es excelente.
Broma #2: Las VLANs no “se rompen al azar”. Se rompen de maneras perfectamente deterministas—solo que no siempre están documentadas.
Ejemplos de netplan que funcionan (y por qué)
Ubuntu 24.04 típicamente usa netplan para generar configuración para systemd-networkd en servidores. El error más común es mezclar supuestos de NetworkManager con la realidad de networkd, o dejar interfaces medio configuradas.
Ejemplo 1: Puente simple sin tag br0
Puerto del switch: access VLAN X (sin tag).
Host: IP en br0 vía DHCP o estática.
cr0x@server:~$ sudo cat /etc/netplan/01-br0.yaml
network:
version: 2
renderer: networkd
ethernets:
enp3s0:
dhcp4: no
dhcp6: no
bridges:
br0:
interfaces: [enp3s0]
dhcp4: yes
parameters:
stp: false
forward-delay: 0
Por qué: enp3s0 no tiene IP; br0 sí. STP desactivado evita más de 30 segundos de “¿por qué no llego a nada después del reboot?” en topologías simples.
Ejemplo 2: Puentes por VLAN (Patrón B)
Puerto del switch: trunk; VLANs 10 y 20 permitidas/tagueadas.
Host: gestión en VLAN 10; VMs usan VLAN 20.
cr0x@server:~$ sudo cat /etc/netplan/01-vlan-bridges.yaml
network:
version: 2
renderer: networkd
ethernets:
enp3s0:
dhcp4: no
dhcp6: no
vlans:
enp3s0.10:
id: 10
link: enp3s0
enp3s0.20:
id: 20
link: enp3s0
bridges:
br10:
interfaces: [enp3s0.10]
dhcp4: yes
parameters:
stp: false
forward-delay: 0
br20:
interfaces: [enp3s0.20]
dhcp4: no
dhcp6: no
parameters:
stp: false
forward-delay: 0
Por qué: br10 y br20 son explícitos. Tu VM va a br20 y no puedes aterrizar por accidente en la red de gestión a menos que lo elijas.
Ejemplo 3: Puente consciente de VLAN (Patrón C) con gestión del host solo en una VLAN
Esta es la jugada avanzada. La IP del host está en una interfaz VLAN (VLAN de gestión), mientras el puente transporta varias VLANs para VMs.
cr0x@server:~$ sudo cat /etc/netplan/01-vlan-aware-bridge.yaml
network:
version: 2
renderer: networkd
ethernets:
enp3s0:
dhcp4: no
dhcp6: no
bridges:
br0:
interfaces: [enp3s0]
dhcp4: no
parameters:
stp: false
forward-delay: 0
vlans:
br0.10:
id: 10
link: br0
dhcp4: yes
Por qué: br0 es puramente L2; br0.10 es la presencia L3 de gestión del host. Las VMs pueden conectarse a br0 y colocarse en VLANs vía bridge VLAN filtering (configurado fuera de netplan, típicamente vía networkd o comandos explícitos del bridge al arrancar).
Advertencia operativa: netplan no expresa todo lo que puedas querer para bridge VLAN filtering por puerto. Si eliges el Patrón C, trátalo como una pequeña plataforma de switching: configúralo consistentemente y prueba después de cada cambio.
Aplicar netplan de forma segura
cr0x@server:~$ sudo netplan try
Do you want to keep these settings?
Press ENTER before the timeout to accept the new configuration
Changes will revert in 120 seconds
Significado: netplan try te da una ventana de rollback. Úsalo en sistemas remotos a menos que disfrutes de consolas fuera de banda.
Decisión: Si la conectividad cae, espera el rollback y arregla tu YAML con calma.
Libvirt/KVM attachment: evitar la trampa “está en virbr0”
Los valores por defecto de Libvirt están optimizados para “un desarrollador en portátil ejecuta una VM con Internet vía NAT”. En producción, normalmente quieres redes puenteadas para que tu VM sea un ciudadano de primera clase en la LAN/VLAN.
Comprueba qué redes existen
cr0x@server:~$ virsh net-list --all
Name State Autostart Persistent
--------------------------------------------
default active yes yes
Significado: Existe la red NAT por defecto de libvirt. No es maligna; solo a menudo no es lo que quieres.
Decisión: Decide explícitamente: NAT (default) vs puente (br0/br10/br20). No dejes que libvirt decida por accidente.
Adjuntar una NIC de VM a un puente (ejemplo)
cr0x@server:~$ virsh attach-interface --domain vm1 --type bridge --source br20 --model virtio --config
Interface attached successfully
Significado: La configuración persistente cambió. Puede que aún necesites desacoplar la NIC vieja o reiniciar dependiendo de cómo el invitado maneje el hotplug.
Decisión: Tras los cambios, verifica con domiflist y luego valida desde dentro de la VM.
Confirmar que la NIC de la VM aterrizó donde pretendías
cr0x@server:~$ virsh domiflist vm1
Interface Type Source Model MAC
-------------------------------------------------------
vnet3 bridge br20 virtio fe:54:00:aa:bb:cc
Significado: Ahora estás puenteado a br20. Si Internet aún falla, no es porque te quedaste accidentalmente en virbr0.
Decisión: Continúa con las comprobaciones de VLAN y firewall.
Errores comunes: síntoma → causa raíz → solución
1) La VM obtiene IP pero no puede hacer ping al gateway
Síntoma: DHCP funciona, pero el primer salto falla.
Causa raíz: DHCP viene de otro lado (relay, VLAN equivocada), o el gateway está en una VLAN distinta a la VLAN efectiva de la VM.
Solución: Captura ARP en vnet y uplink. Confirma etiquetado VLAN y modo del puerto del switch. Alinea access/native VLAN con el puente sin tag, o etiqueta la VLAN correctamente.
2) La VM puede hacer ping al gateway pero no a IPs públicas
Síntoma: L2 está bien; L3 más allá del gateway falla.
Causa raíz: Enrutamiento/NAT/ACL ascendente, o ruta por defecto incorrecta en el invitado.
Solución: Revisa la tabla de enrutamiento del invitado. Revisa ACLs ascendentes. Si el host hace routing/NAT, habilita ip_forward y añade reglas NAT intencionalmente.
3) La VM llega a IPs públicas pero DNS falla
Síntoma: ping 1.1.1.1 funciona, ping a un nombre falla.
Causa raíz: Servidores DNS inalcanzables, resolv.conf/systemd-resolved mal configurado, o UDP/TCP 53 bloqueado.
Solución: Consulta DNS directamente; comprueba firewall por UDP/TCP 53. Valida el estado de systemd-resolved en el invitado.
4) Solo algunas VLANs funcionan; otras están muertas
Síntoma: VLAN 10 bien, VLAN 20 muerta en todas las VMs.
Causa raíz: La lista de VLAN permitidas del trunk del switch no incluye la VLAN 20, o la tabla VLAN del puente del host no tiene membresía para VLAN 20.
Solución: Añade la VLAN a la lista permitida del switch y a la configuración VLAN del host. Verifica con bridge vlan show y tcpdump con filtro VLAN.
5) Internet funciona hasta el reinicio; luego las VMs quedan aisladas
Síntoma: Tweaks manuales al puente/VLAN funcionaron, pero no persistieron.
Causa raíz: Los comandos runtime del puente no estaban codificados en netplan/systemd-networkd; el reinicio borra el estado.
Solución: Haz la configuración declarativa (netplan + networkd drop-ins) y versiona. Prueba con reinicio como parte del cambio.
6) El host puede alcanzar la red; las VMs no
Síntoma: El host hace ping; la VM falla; el puente parece correcto.
Causa raíz: Política forward de nftables que descarta el reenvío bridged, o interacción bridge netfilter con reglas de firewall.
Solución: Inspecciona la cadena forward de nft y los sysctls bridge-nf. Añade reglas accept explícitas para subredes/puentes de VM.
7) El tráfico de VM es intermitente; ARP hace flapping
Síntoma: A veces el gateway es alcanzable; a veces no; las MAC parecen moverse.
Causa raíz: IPs duplicadas, filtros anti-MAC spoofing en el switch, o varios bridges conectados creando un bucle (y STP no protegiéndote).
Solución: Busca respuestas ARP duplicadas, audita features de seguridad del switch, y asegúrate de tener exactamente un camino L2 hacia la VLAN a menos que intentes redundancia intencional.
8) Todo funciona, pero el rendimiento es terrible
Síntoma: Latencia alta, throughput bajo, pérdidas bajo carga.
Causa raíz: Mismatch de MTU (especialmente con tagging VLAN), peculiaridades de offload, o CPU del host consumiéndose por firewall/conntrack en tráfico bridged.
Solución: Verifica MTU extremo a extremo. Evalúa ajustes de offload. No pongas un firewall stateful sorpresa en el camino de forwarding sin dimensionarlo.
Listas de verificación / plan paso a paso
Paso a paso: Construir una red de VM puenteada en una sola VLAN (aburrida y correcta)
- Elige el Patrón A (sin tag) si solo necesitas una VLAN.
- Configura netplan para que la NIC física no tenga IP y esté esclavizada a br0.
- Aplica netplan con
netplan try. - Verifica:
ip -br addrmuestra la IP en br0. - Adjunta la NIC de la VM a br0 (tipo bridge en libvirt).
- Verifica:
bridge linkmuestra vnetX master br0. - Prueba en la VM: ping al gateway, luego a IP pública, luego DNS.
- Si falla: tcpdump en vnetX y en uplink para encontrar el primer frame que falta.
Paso a paso: Añadir VLANs sin hacer miserable al tú del futuro
- Elige el Patrón B a menos que tengas una razón fuerte para bridge VLAN-aware.
- Pide que el puerto del switch esté configurado como trunk con lista de VLANs permitidas explícita.
- Crea subinterfaces VLAN en el host (enp3s0.10, enp3s0.20).
- Crea puentes por VLAN (br10, br20). Pon la gestión del host en una VLAN solo.
- Adjunta VMs al puente correcto según su VLAN. No “usar br0 para todo”.
- Documenta el mapeo VLAN → puente en el repo que guarda los configs netplan.
- Prueba con reinicio. Siempre.
Lista de validación (ejecutar tras cada cambio)
- Host: pertenencia al puente correcta (
bridge link). - Host: IP en el puente (o en la interfaz VLAN del puente, intencionalmente).
- Host: membresía VLAN alineada con expectativas del puerto del switch.
- Host: cadena forward del firewall permite los flujos requeridos.
- Invitado: ruta + DNS correctos.
- Paquete: ARP sale de la VM, sale del host y las respuestas vuelven.
Tres micro-historias corporativas desde las trincheras de redes
Micro-historia #1: El incidente causado por una suposición equivocada
En una empresa, un clúster de virtualización se “estandarizó” con uplink en trunk: VLAN 10 para gestión, VLAN 20 para cargas. El ingeniero que construyó el primer host asumió que el puerto del switch tenía VLAN nativa 20 porque “eso es lo que solemos hacer”. Construyó el Patrón A: br0 sin tag, VMs conectadas, sin etiquetado en ningún lado.
Funcionó en su rack. Falló en el rack contiguo. Mismo modelo de servidor, mismo netplan, mismo hypervisor. La mitad de las VMs tenía Internet; la otra mitad ni siquiera hacía ARP al gateway. El equipo de redes juró que nada había cambiado. Entonces alguien hizo la única pregunta que importaba: “¿Son idénticos realmente los puertos del switch?”
No lo eran. Un rack tenía native VLAN 20. Otro tenía native VLAN 10. Un tercero no tenía configurada la native VLAN igual porque se usó otra plantilla meses antes. El tráfico sin tag aterrizaba donde el switch decidía, que es una forma educada de decir “no tienes control”.
La solución no fue “más reintentos” ni “imágenes nuevas”. Fue dejar de depender del comportamiento de VLAN nativa. Pasaron al Patrón B: subinterfaces VLAN explícitas y puentes por VLAN. Requirió una ventana de mantenimiento, pero después los racks dejaron de ser copos de nieve. El postmortem incluyó una línea clave: las suposiciones son configuración, solo que sin documentar.
Micro-historia #2: La optimización que salió mal
Otra organización quiso reducir el número de interfaces en sus hosts. Tenían un puente por VLAN, y alguien decidió que eran “demasiados dispositivos”. Así que pasaron a un único puente VLAN-aware con filtering, configurado vía scripts runtime al arrancar. Parecía limpio: un puente, uplink en trunk, VMs asignadas dinámicamente a VLANs.
Entonces llegó la primera interrupción real: tras una actualización del kernel y reinicio, un subconjunto de hosts arrancó con entradas VLAN faltantes en algunos puertos vnet. Nadie lo notó de inmediato porque los hosts estaban bien en la VLAN de gestión. Pero las VMs de ciertos tenants quedaron aisladas. Los síntomas: timeouts de DHCP, ARP incomplete, sin reachability al gateway.
La causa no fue que Linux “olvidara VLANs”. Fue su propio scripting y el orden de arranque. systemd-networkd levantó el puente, libvirt arrancó VMs temprano, y el script de membresía VLAN corrió después—sin rellenar reglas de puerto de forma fiable. Algunas interfaces tenían PVID, otras no. Una condición de carrera se convirtió en política de red.
Revirtieron al Patrón B en la mayoría de clústeres y conservaron el Patrón C solo donde pudieron codificar reglas VLAN de forma determinista, versionada y segura respecto al orden de arranque. La optimización falló no porque bridge VLAN-aware sea malo, sino porque el método de despliegue no fue tan aburrido como debía ser.
Micro-historia #3: La práctica aburrida pero correcta que salvó el día
Un equipo de servicios financieros ejecutaba KVM en Ubuntu con networking puenteado y VLANs. Nada espectacular. Pero tenían un hábito: después de cada cambio de red ejecutaban un script estándar que capturaba tres cosas—pertenencia al puente, tabla VLAN y política forward de nftables—y almacenaban la salida con el ticket de cambio.
Una mañana, varias VMs perdieron conectividad saliente. El host parecía “up”. El puente estaba “up”. El puerto del switch estaba “up”. Aquí es donde los equipos empiezan a reiniciar cosas hasta que algo cambia. Ellos no. Compararon salidas last-known-good con las actuales.
La diferencia fue pequeña y decisiva: la política de la cadena forward cambió a drop, y la regla accept para br0 → uplink no estaba presente. Resultó que una actualización de un rol de hardening se había aplicado ampliamente; era correcta para servidores standalone e incorrecta para hosts de virtualización que hacen forwarding de tráfico bridged.
La solución tardó minutos: añadir la regla accept faltante (con el scope correcto), volver a desplegar el rol de hardening con conciencia del tipo de host, y restaurar el servicio. La práctica aburrida no fue genial. Fue evidencia. La evidencia vence al pánico siempre.
Preguntas frecuentes
1) ¿Debo usar NetworkManager o systemd-networkd en servidores Ubuntu 24.04?
Usa systemd-networkd (vía netplan) en servidores a menos que tengas una razón específica para estandarizar en NetworkManager. Mezclarlos es cómo aparecen “configs fantasma”.
2) Mi VM está adjunta a br0, pero sigue usando direcciones 10.0.2.0/24. ¿Por qué?
Eso suele ser NAT de libvirt (red por defecto). Revisa virsh domiflist. Si la fuente es default y el tipo es network, no estás en modo puente.
3) ¿Necesito STP en un puente Linux?
Normalmente no para un uplink único, puente único y sin bucles. STP puede añadir retrasos de forwarding y confusión. Actívalo solo cuando sepas que hay potencial de bucles L2 y quieras protección.
4) ¿Cuál es la forma más limpia de poner VMs en diferentes VLANs?
Puentes por VLAN (Patrón B) son lo más limpio operativamente. El puente VLAN-aware (Patrón C) es potente pero exige configuración más disciplinada.
5) ¿Puede el invitado hacer el etiquetado VLAN por sí mismo?
Sí. Puedes presentar un trunk a una VM y dejar que cree subinterfaces VLAN dentro del invitado. Es apropiado para aparatos router/firewall o nodos Kubernetes que gestionan sus propias VLANs. Para VMs ordinarias, mantén la lógica VLAN en el host o en la red ascendente.
6) ¿Por qué DHCP funciona pero ARP al gateway falla?
Porque DHCP puede tener éxito vía relays o aceptar tramas mal tagueadas de una manera que no garantiza tu adyacencia L2 al gateway. Prueba la corrección de VLAN con tcpdump y estado ARP, no con “obtuvo una IP”.
7) ¿Es seguro filtrar tráfico de VM con nftables en el host?
Sí, si lo haces intencionalmente y entiendes si las tramas bridged atraviesan iptables/nftables vía bridge-nf. La versión insegura es “política drop” sin aceptes de forward.
8) ¿Cómo sé si el problema es el puerto del switch o el host?
Captura en ambos lados del puente del host: interfaz vnet y uplink físico. Si los paquetes salen de vnet pero no del uplink, es lado host. Si salen del uplink pero las respuestas nunca regresan, es upstream (switch/VLAN/ACL).
9) ¿Debe mi host tener IP en cada VLAN que usan mis VMs?
No. En un diseño L2 puenteado puro, el host no necesita presencia L3 en las VLANs de VM. Añade IPs del host solo cuando haya una necesidad operativa clara (monitorización, enrutamiento, servicios) y puedas securizarlo.
10) ¿Y el MTU y la sobrecarga VLAN?
El etiquetado 802.1Q añade overhead; MTUs desajustadas pueden causar fallos parciales raros (especialmente si PMTUD está bloqueado). Si usas jumbo frames, verifica MTU extremo a extremo en NIC del host, puente, NIC de VM y puerto del switch.
Conclusión: siguientes pasos que no te perjudicarán después
Si tu VM de Ubuntu 24.04 “no tiene Internet”, resiste la tentación de reconfigurarlo todo a la vez. Tu trabajo es encontrar el primer salto roto y luego arreglar el diseño para que se mantenga arreglado.
- Elige un patrón: A (sin tag), B (puentes por VLAN) o C (puente VLAN-aware). No los mezcles a la ligera.
- Haz la configuración del host declarativa: netplan + networkd, aplicados con
netplan try. - Prueba pertenencia al puente: interfaz vnet en el puente correcto; uplink físico adjunto.
- Prueba la realidad VLAN: la tabla VLAN del puente coincide con el modo del puerto del switch y las VLANs permitidas.
- Prueba que el forwarding no esté bloqueado: cadena forward de nftables y ajustes bridge-nf alineados con tu intención.
- Operacionaliza la validación: conserva un pequeño conjunto de comandos que ejecutas tras cada cambio y guarda la salida.
Si lo haces así, “La VM no tiene Internet” deja de ser un misterio y se convierte en lo que siempre debió haber sido: un ejercicio directo de aislamiento de fallos con una solución permanente al final.