Logo OVS

Configuración de un NUC con Ubuntu + Open vSwitch + VLAN’s + KVM + VM’s con netplan y Open vSwitch. El networking está basado en OVS y Netplan tanto en el Host como en las máquinasa virtuales.

El año pasado documenté aquí cómo configurar OpenvSwitch con KVM. En este apunte voy a ir más al grano, mostraré el estado final deseado y cómo configurarlo.


Introducción

Netplan

Netplan es una utilidad para configurar fácilmente la red en un sistema Linux. Basta con crear una descripción YAML de las interfaces de red necesarias y lo que deben hacer. A partir de esta descripción Netplan generará toda la configuración necesaria para que la herramienta de renderizado haga su magia y la active. Netplan lee la configuración desde /etc/netplan/*.yaml y genera archivos de configuración específicos en /run y se los entrega al demonio o backend particular del equipo para que “renderice” dicha configuración. Los soportados son:

  • NetworkManager
  • Systemd-networkd <== usado en Ubuntu 22.04.1


Open vSwitch

Open vSwitch (OVS) es un bridge virtual de código abierto. Orientado a la automatización y a mejorar el switching en entornos de máquinas virtuales. Date un paseo por la página de OVS en la Wikipedia, para una introducción.

  • Permite trabajar en un único Hypervisor o de forma distribuida en varios.
  • Está considerada como la implementación más popular de OpenFlow, un controlador/protocolo para manejar switches hardware (que soporten openflow), donde la red se puede programar por software, independiente de la marca del fabricante. Mi instalación va a ser standlone, sin controlador externo (no uso OpenFlow)
  • Admite interfaces y protocolos de gestión estándar (p. ej. NetFlow, sFlow, IPFIX, RSPAN, CLI, LACP, 802.1ag).
  • Está diseñado para poder distribuir el switching a través de múltiples servidores físicos, similar a lo que se hace con vNetwork distributed vswitch de VMware o el Nexus 1000V de Cisco.
  • Soporta bastantes cosas más, aquí tienes la lista completa.
Nota: Open vSwitch puede trabajar con interfaces TUN, TAP y Internal Ports (propias). En mi configuración NO UTILIZO las interfaces TUN ni TAP, solo voy a usar sus propios Internal Ports.

A modo recordatorio, TUN y TAP son dispositivos virtuales de red que residen en el Kernel, TUN opera a nivel 3 (routing) y TAP a nivel 2 (bridges/switching). Trabajan usando sockets en el espacio de usuario. Tradicionalmente se han usado siempre junto con el Bridge estándar que incorpora el Kernel de Linux y con KVM/QEMU.

Con Open vSwitch utilizo solo sus propios Internal Ports en vez de interfaces TAP. Es más, ten cuidado con los ejemplos que encuentres por ahí documentados con tap y Open vSwitch porque te pueden llevar a confusión. En este apunte (y en mi instalación en producción) con KVM/QEMU y OVS solo voy a usar Internal Ports. El motivo es que Open vSwitch puede trabajar con TAP pero los trata como si fuesen un interfaz normal, es decir, no los abre vía sockets (modo estándar) y eso supone ciertas limitaciones e implicaciones, te recomiendo leer el artículo: Q: I created a tap device tap0, configured an IP address on it, and added it to a bridge…: en los Common Configuration Issues de OVS.

Sus principales componentes son:

  • ovs-vswitchd: Es el demonio (núcleo) de OVS junto con el módulo openvswitch_mod.ko (para el kernel). Ambos se encargan de la conmutación, VLAN’s, bonding, monitorización. El primer paquete lo gestiona el daemon en el user-space, pero del resto de la conmutación se encarga el módulo del kernel.
  • ovsdb-server: Es el segundo al mando, se trata de un servidor de base de datos ligero que guarda la configuración de OVS.
Arquitectura de OVS (standalone)


Instalación

Instalación de Ubuntu

Antes de meterme con el networking voy a dejar aquí un mini resumen de la instalación del servidor host ubuntu sobre NUC de intel:

  • Descargo Ubuntu Server LTS 22.04.1
  • Verifico la descarga, copio la imagen a una USB (usando BalenaEtcher)
  • Sigo el tutorial de instalción
    • Boot desde la USB, NUC (F10) -> UEFI Bootloader -> Try to install Ubuntu Server
    • Español, Spanish, Ubuntu Server, Ethernet, Mirror por defecto, actualizo instalador
    • Uso el disco entero, creo el usuario, instalo openssh, ignoro los snaps
    • Cuando termina la instalación hago reboot.
    • Creo la clave público/privada para poder trabajar vía SSH
  • Actualizo el sistema operativo
    apt update && apt upgrade -y && apt full-upgrade -y
    apt autoremove -y --purge
    systemctl reboot -f
    
  • Verifico si puedo limpiar kernel’s antiguos
    uname -a
    dpkg --list | grep linux-image
    apt purge linux-image-xxxxxx
    
  • Limpio el sistema
    apt clean
    journalctl --vacuum-time=2d
    journalctl --vacuum-size=500M
    


Instalación de Open vSwitch

Tras la instalación de Ubuntu Server LTS 22.04.1,

  • Instalo OVS
    root@luna:~# apt install openvswitch-switch
    
  • Ubuntu habilita y arranca el servicio openvswitch-switch.service que a su vez arranca los dos daemons antes comentados.
    root@luna:~# ps -ef | grep ovs
    root        1179       1  0 13:29 ?        00:00:00 ovsdb-server /etc/openvswitch/conf.db -vconsole:emer -vsyslog:err -vfile:info --remote=punix:/var/run/openvswitch/db.sock --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir --log-file=/var/log/openvswitch/ovsdb-server.log --pidfile=/var/run/openvswitch/ovsdb-server.pid --detach
    root        1238       1  0 13:29 ?        00:00:00 ovs-vswitchd unix:/var/run/openvswitch/db.sock -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir --log-file=/var/log/openvswitch/ovs-vswitchd.log --pidfile=/var/run/openvswitch/ovs-vswitchd.pid --detach
    


Instalación de herramientas de red

  • Instalo el soporte de VLAN’s y de paso algunas herramientas útiles
    root@luna:~# apt install net-tools nmap vlan
    


Configuración

Preparo luna con la siguiente configuración.

  • Interfaz eth0 conectado a un puerto del Switch en modo trunk
  • El propio servidor accede a la red por la vlan100 con una IP estática
  • los guest’s (VM’s) futuros de KVM instanciarán Internal Ports dinámicamente.

Deshabilito IPv6

  • No voy a usar IPv6 para nada, así que lo quito por completo de forma permanente
    root@luna:~# cat /etc/default/grub
    :
    GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1"
    :
    root@luna:~# update-grub
    root@luna:~# reboot -f
    

Configuración netplan

  • Preparo el fichero netplan definitivo, pero de momento no lo activo. Asigno el nombre eth0 al interfaz ethernet, desactivo el interfaz WiFi, defino la IP en la vlan100 (usando la interfaz vnet100 que crearé más adelante).
    root@luna:~# cat /etc/netplan/00-red-luna.yaml
    root@luna:~# cat /etc/netplan/00-red-luna.yaml
    #
    # Config de red del servidor luna
    # By LuisPa 2023
    #
    network:
    version: 2
    renderer: networkd
    
    wifis: {}
    
    ethernets:
      eth0:
        optional: true  # Ignorar la red para acelerar el tiempo de boot
        match:
          macaddress: "94:c6:91:1a:a6:3e"
          name: eno1
          name: eth0
        set-name: eth0
        dhcp4: no
    
      lunabr:
        dhcp4: no
    
      vnet100:
        optional: true
        addresses:
        - 192.168.105.253/24
        routes:
        - to: default
          via: 192.168.105.1
        nameservers:
          addresses: [192.168.105.224]
          search: [tudominio.com]
    
A partir de ahora cuidado si estás conectado vía SSH, los siguientes comandos van a cortar la comunicación, así que mejor vete a la consola y sigue desde ahí

Configuración OVS

  • Creo el bridge lunabr. Añado el puerto eth0 al bridge lunabr como puerto Trunk (no hace falta hacer nada especial, los puertos físicos se añaden en modo Trunk por defecto). Creo el interfaz vnet100 para que luna pueda trabajar en la vlan100. Estos comandos de ovs-vsctl son persistentes (recuerda la base de datos).
    root@luna:~# ovs-vsctl add-br lunabr
    root@luna:~# ovs-vsctl add-port lunabr eth0  # Aquí es donde se corta la comunicación
    root@luna:~# ovs-vsctl add-port lunabr vnet100 tag=100 -- set Interface vnet100 type=internal
    

Activo la configuración

  • Activo la configuración de netplan que había hecho antes, cambio el cable de red de mi servidor a un puerto TRUNK del switch y hago un reboot
    root@luna:~# netplan apply
    :
    CAMBIO EL CABLE de luna a un puerto TRUNK del Switch
    :
    root@luna:~# reboot -f      
    
  • Tras el arranque, conecto con luna vía SSH desde un laptop en la vlan100, verifico el estado de los puertos, el bridge, etc.
    ➜  ~ ssh luis@192.168.105.253
    :
    root@luna:~# ip link
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
      link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master ovs-system state UP mode DEFAULT group default qlen 1000
      link/ether 94:c6:91:1a:a6:3e brd ff:ff:ff:ff:ff:ff
      altname enp0s31f6
    3: wlp58s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
      link/ether a0:c5:89:aa:13:c4 brd ff:ff:ff:ff:ff:ff
    4: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
      link/ether 82:d4:11:3d:34:37 brd ff:ff:ff:ff:ff:ff
    5: lunabr: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
      link/ether 94:c6:91:1a:a6:3e brd ff:ff:ff:ff:ff:ff
    6: vnet100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
      link/ether 7e:14:01:8e:72:5b brd ff:ff:ff:ff:ff:ff
    
    root@luna:~# ovs-vsctl show
    f283cd6c-17e8-4bf3-be1a-b50904e91fc3
      Bridge lunabr
          Port lunabr
              Interface lunabr
                  type: internal
          Port eth0
              Interface eth0
          Port vnet100
              tag: 100
              Interface vnet100
                  type: internal
      ovs_version: "2.17.3"
    


Añadimos KVM/QEMU

  • Instalo KVM/QEMU y virt-manager. La instalación es relativamente sencilla, al principio del apunte sobre Vagrant con Libvirt KVM explico cómo hacerlo.
    root@luna:~# apt install qemu qemu-kvm libvirt-clients \
                     libvirt-daemon-system virtinst
    root@luna:~# apt install virt-manager
    
Nota: No instalo las bridge-utils, utilidades para configurar el Bridge Ethernet en Linux porque no las necesitamos, vamos a usar OVS
  • Añado mi usuario al grupo libvirt y kvm… y reboot para que luego funcione qemu-system-x86_64)
    root@luna:~# adduser luis libvirt
    root@luna:~# adduser luis kvm
    root@luna:~# systemctl reboot -f
    


Configuración dinámica OVS + Libvirt + QEMU/KVM

Aprovecho la integración entre Libvirt y Open vSwitch para no tener que hacer configuraciones estáticas. Dejaré que sea libvirt quien llame a ovs-vsctl para crear (al levantar las VMs) y destruir (al parar las VMs) los puertos del Bridge.

Integración de Open vSwitch con Libvirt

Open vSwitch soporta las redes que gestiona libvirt en modo bridged (no las NAT). Encontrarás más información aquí.

  • Voy a crear una de red persistente en Libvirt desde un fichero XML. Creo el fichero /root/switch-lunabr.xml que define la posibilidad de acceder desde las futuras máquinas virtuales al TRUNK o a las VLAN’s independientemente.
<!-- Ejemplo de fichero XML para crear una red virtual persistente dentro de Libvirt. 
-->

<network>
  <name>switch-lunabr</name>
  <forward mode='bridge'/>
  <bridge name='lunabr'/>
  <virtualport type='openvswitch'/>
  <portgroup name='Sin vlan'>
  </portgroup>
  <portgroup name='Trunk Core'>
    <vlan trunk='yes'>
      <tag id='2'/>
      <tag id='3'/>
      <tag id='6'/>
      <tag id='100'/>
    </vlan>
  </portgroup>
  <portgroup name='Vlan 2'>
    <vlan>
      <tag id='2'/>
    </vlan>
  </portgroup>
  <portgroup name='Vlan 3'>
    <vlan>
      <tag id='3'/>
    </vlan>
  </portgroup>
  <portgroup name='Vlan 6'>
    <vlan>
      <tag id='6'/>
    </vlan>
  </portgroup>
  <portgroup name='Vlan 100' default='yes'>
    <vlan>
      <tag id='100'/>
    </vlan>
  </portgroup>
</network>
  • Desactivo la red default que se instala por defecto
root@luna:~# virsh net-autostart --disable default
Network default unmarked as autostarted

root@luna:~# virsh net-destroy default
Network default destroyed

root@luna:~# virsh net-undefine default
Network default has been undefined
  • Activo la nueva red persistente desde el fichero que acabo de crear
root@luna:~# virsh net-define switch-lunabr.xml
(Se crea el fichero /etc/libvirt/qemu/networks/switch-lunabr.xml)

root@luna:~# virsh net-start switch-lunabr
Network switch-lunabr started

root@luna:~# virsh net-autostart switch-lunabr
Network switch-lunabr marked as autostarted

root@luna:~# virsh net-list
 Name        State    Autostart   Persistent
----------------------------------------------
 switch-lunabr   active   yes         yes
  • Desde mis VM’s veré las nuevas opciones cuando seleccione la Red
  • Lo mejor está por llegar. Una vez que arranque la VM se va a crear dinámicamente un nuevo interfaz llamado vnetN con la configuración que establecí en el XML. En este ejemplo hemos seleccionado el llamado VLAN100, en el XML tiene tag id='100', por lo tanto nos creará un virtual port interno con el tag: 100.


Maquina virtual y puerto TRUNK dinámico

Es posible entregar el puerto TRUNK a una VM y que se cree en OVS de forma dinámica durante el arranque. Lo primero que hago es instalar mi VM y luego añado un interfaz de red. En mi caso arranco virt-manager de la siguiente forma:

luis@luna:~$ type vm
vm is aliased to `LC_ALL=C virt-manager'
luis@luna:~$ vm
  • Creo una VM basada en Ubuntu y añado un interfaz de tipo Bridge
Configuración del puerto en modo Bridge
IMPORTANTE - crea una configuración incompleta. Si entramos en la sección XML vemos que no se están especificando qué VLAN’s queremos presentar a la máquina virtual. Si intento arrancar la VM devolverá el error libvirt unable to add trunk bridge port vnetXX operation not supported
Configuración XML inválida !!
  • Edito la sección XML y añado manulamente algunos parámetros. El ID en <parameters interfaceid=""> me lo he inventado.
$ virsh edit lunafw.tudominio.com
:
<interface type="bridge">
  <mac address="52:54:01:aa:01:01"/>
  <source bridge="lunabr"/>
  <vlan trunk="yes">
    <tag id="2"/>
    <tag id="3"/>
    <tag id="6"/>
    <tag id="100"/>
  </vlan>
  <virtualport type="openvswitch">
    <parameters interfaceid="7d072813-014c-41f9-9f0d-4ebc350b9887"/>
  </virtualport>
  <target dev="vnet5"/>
  <model type="virtio"/>
  <alias name="net0"/>
  <address type="pci" domain="0x0000" bus="0x01" slot="0x00" function="0x0"/>
</interface>
Configuración XML correcta !!
  • A partir de aquí la VM arrancará correctamente.


Ejemplos de configuración en las VM

Para poder trabajar con este TRUNK en la Virtual Machine tendremos que configurarlo de forma adecuada. Cada sistema operativo tiene su forma de hacerlo.

  • Ubuntu LTS 22.04 con netplan
# Netplan by LuisPa
network:
  ethernets:
      eth0:
        optional: true
        match:
            macaddress: '52:54:01:aa:01:01'
            name: enp1s0
            name: eth0
        set-name: eth0
        dhcp4: no
  vlans:
      vlan6:
        optional: true
        id: 6
        link: eth0
        macaddress: "52:54:01:aa:00:06"
        dhcp4: yes
      vlan100:
        optional: true
        id: 100
        link: eth0
        macaddress: "52:54:01:aa:01:00"
        addresses:
        - 192.168.105.1/22
        nameservers:
          addresses:
          - 192.168.105.224
          search:
          - tudominio.com
  version: 2
  • Gento linux con systemd-networkd.service

Hay que crear múltiples ficheros en el directorio /etc/systemd/network

gentoo-lunafw /etc/systemd/network # ls -al
total 76
drwxr-xr-x 2 root root 4096 feb 14 10:33 .
drwxr-xr-x 9 root root 4096 feb 14 09:53 ..
-rw-r--r-- 1 root root  110 feb 14 08:54 eth0.network
-rw-r--r-- 1 root root    0 feb 14 09:52 .keep_sys-apps_systemd-0
-rw-r--r-- 1 root root  115 feb 14 10:26 vlan100.netdev
-rw-r--r-- 1 root root   98 feb 14 10:26 vlan100.network
-rw-r--r-- 1 root root  108 feb 14 10:33 vlan2.netdev
-rw-r--r-- 1 root root   90 feb 14 10:23 vlan2.network
-rw-r--r-- 1 root root  108 feb 14 10:33 vlan3.netdev
-rw-r--r-- 1 root root   90 feb 14 10:28 vlan3.network
-rw-r--r-- 1 root root  108 feb 14 10:32 vlan6.netdev
-rw-r--r-- 1 root root   90 feb 14 10:31 vlan6.network
-rw-r--r-- 1 root root   38 feb 14 08:50 vSwitch100.netdev
-rw-r--r-- 1 root root  454 feb 14 10:25 vSwitch100.network
-rw-r--r-- 1 root root  159 feb 14 10:23 vSwitch2.netdev
-rw-r--r-- 1 root root  184 feb 14 10:29 vSwitch2.network
-rw-r--r-- 1 root root  159 feb 14 10:31 vSwitch3.netdev
-rw-r--r-- 1 root root  184 feb 14 10:29 vSwitch3.network
-rw-r--r-- 1 root root  163 feb 14 10:30 vSwitch6.netdev
-rw-r--r-- 1 root root  184 feb 14 10:30 vSwitch6.network

------------ eth0.network ---------------
#
# Interfaz principal de la VM
#
[Match]
Name=eth0

[Network]
VLAN=vlan2
VLAN=vlan3
VLAN=vlan6
VLAN=vlan100


------------ vlan100.netdev ---------------
#
# Dispositivo de Red Virtual (netdev) para definir la VLAN 100
#
[NetDev]
Name=vlan100
Kind=vlan

[VLAN]
Id=100


------------ vlan100.network ---------------
#
# Conecto la vlan100 al bridge "vSwitch100"
#
[Match]
Name=vlan100

[Network]
Bridge=vSwitch100

------------ vlan2.netdev ---------------
#
# Dispositivo de Red Virtual (netdev) para definir la VLAN 2
#
[NetDev]
Name=vlan2
Kind=vlan

[VLAN]
Id=2

------------ vlan2.network ---------------
#
# Conecto la vlan2 al bridge "vSwitch2"
#
[Match]
Name=vlan2

[Network]
Bridge=vSwitch2

------------ vlan3.netdev ---------------
#
# Dispositivo de Red Virtual (netdev) para definir la VLAN 3
#
[NetDev]
Name=vlan3
Kind=vlan

[VLAN]
Id=3

------------ vlan3.network ---------------
#
# Conecto la vlan2 al bridge "vSwitch3"
#
[Match]
Name=vlan3

[Network]
Bridge=vSwitch3

------------ vlan6.netdev ---------------
#
# Dispositivo de Red Virtual (netdev) para definir la VLAN 6
#
[NetDev]
Name=vlan6
Kind=vlan

[VLAN]
Id=6

------------ vlan6.network ---------------
#
# Conecto la vlan2 al bridge "vSwitch6"
#
[Match]
Name=vlan6

[Network]
Bridge=vSwitch6

------------ vSwitch100.netdev ---------------
[NetDev]
Name=vSwitch100
Kind=bridge


------------ vSwitch100.network ---------------
#
# Para poder conmutar trafico en el Switch100 es necesario crear
# el fichero vSwitch100.network (ademas de vSwitch100.netdev)
# y hacer un Match con el vSwitch100
#
[Match]
Name=vSwitch100

# En el caso del Switch100 necesito tener una dirección IP. Este equipo
# va a tener una IP fija en el bridge virtual "vSwitch100" (al que añadiré
# la VLAN100).
#
[Network]
Address=192.168.105.222/24
DNS=192.168.105.224
Gateway=192.168.105.1
IPForward=yes


------------ vSwitch2.netdev ---------------
#
# Bridge Virtual al que asigno el nombre "vSwitch2" y que utilizaré
# para tener acceso al servicio IPTV de Movistar
#
[NetDev]
Name=vSwitch2
Kind=bridge


------------ vSwitch2.network ---------------
#
# Para poder conmutar trafico en el Switch2 es necesario crear
# el fichero vSwitch2.network (ademas de vSwitch2.netdev)
# y hacer un Match con el vSwitch2
#
[Match]
Name=vSwitch2


------------ vSwitch3.netdev ---------------
#
# Bridge Virtual al que asigno el nombre "vSwitch3" y que utilizaré
# para tener acceso al servicio VoIP de Movistar
#
[NetDev]
Name=vSwitch3
Kind=bridge


------------ vSwitch3.network ---------------
#
# Para poder conmutar trafico en el Switch3 es necesario crear
# el fichero vSwitch3.network (ademas de vSwitch3.netdev)
# y hacer un Match con el vSwitch3
#
[Match]
Name=vSwitch3


------------ vSwitch6.netdev ---------------
#
# Bridge Virtual al que asigno el nombre "vSwitch6" y que utilizaré
# para tener acceso al servicio de datos de Movistar
#
[NetDev]
Name=vSwitch6
Kind=bridge


------------ vSwitch6.network ---------------
#
# Para poder conmutar trafico en el Switch6 es necesario crear
# el fichero vSwitch6.network (ademas de vSwitch6.netdev)
# y hacer un Match con el vSwitch6
#
[Match]
Name=vSwitch6