A.2. Communication réseau avec commutateur virtuel Open vSwitch

Le script ovs-startup.sh sert à lancer une instance de système virtualisé raccordé à un commutateur virtuel via une interface TAP (Voir Section 9, « Fonction TUN/TAP du noyau Linux »). Ce mode de fonctionnement est présenté à la Section 10, « Communications réseau avec un commutateur virtuel ».

Accès : ovs-startup.sh

#!/bin/bash

# This script is part of https://inetdoc.net project
# 
# It starts a qemu/kvm x86 virtual machine plugged into an Open VSwitch port
# through an already existing tap interface.
# It should be run by a normal user account which belongs to the kvm system
# group and is able to run the ovs-vsctl command via sudo
#
# This version of the virtual machine startup script uses the UEFI boot sequence
# based on the files provided by the ovmf package. 
# The qemu parameters used here come from ovml package readme file
# Source: https://github.com/tianocore/edk2/blob/master/OvmfPkg/README
#
# File: ovs-startup.sh
# Author: Philippe Latu
# Source: https://github.com/platu/inetdoc/blob/master/guides/vm/files/ovs-startup.sh
#
#       This program is free software: you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation, either version 3 of the License, or
#       (at your option) any later version.
#
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#
#       You should have received a copy of the GNU General Public License
#       along with this program.  If not, see <http://www.gnu.org/licenses/>.

RED='\e[1;31m'
GREEN='\e[1;32m'
BLUE='\e[1;34m'
NC='\e[0m' # No Color

vm=$1
shift
memory=$1
shift
tapnum=$1
shift

# Are the 3 parameters there ?
if [[ -z "${vm}" || -z "${memory}" || -z "${tapnum}" ]]
then
        echo -e "${RED}ERROR : missing parameter.${NC}"
        echo -e "${GREEN}Usage : $0 [image file] [RAM size in MB] [tap interface number]${NC}"
        exit 1
fi

# Does the VM image file exist ?
if [[ ! -f "${vm}" ]]
then
        echo -e "${RED}ERROR : the ${vm} image file does not exist.${NC}"
        exit 1
fi

# Is the amount of ram sufficient to run the VM ?
if [[ ${memory} -lt 128 ]]
then
        echo -e "${RED}ERROR : unsifficient RAM size : ${memory}MB${NC}"
        echo -e "${GREEN}RAM size must be above 128MB.${NC}"
        exit 1
fi

# Is the tap interface free ?
if [[ ! -z "$(ps aux | grep =[t]ap${tapnum}, )" ]]
then
        echo -e "${RED}tap${tapnum} is already in use by another process.${NC}"
        exit 1
fi

# Are the OVMF symlink and file copy there ?
if [[ ! -L "./OVMF_CODE.fd" ]]
then
        ln -s /usr/share/OVMF/OVMF_CODE.fd .
fi

if [[ ! -f "${vm}_OVMF_VARS.fd" ]]
then
        cp /usr/share/OVMF/OVMF_VARS.fd ${vm}_OVMF_VARS.fd
fi

# Is it possible to set a new Software TPM socket ?
if [[ -z "$(which swtpm)" ]]
then
        echo -e "${RED}TPM emulator not available${NC}"
        exit 1
fi

# Does the software TPM directory exists ?
tpm_dir=${vm}_TPM

if [[ ! -d "${tpm_dir}" ]]
then
        mkdir ${tpm_dir}
fi

# Is swtpm already there for this virtual machine
tpm_pid=$(pgrep -u $USER -a swtpm | grep ${tpm_dir}/swtpm-sock | cut -f 1 -d ' ')
if [[ ! -z "${tpm_pid}" ]]
then
        kill ${tpm_pid}
fi

swtpm socket \
        --tpmstate dir=${tpm_dir} \
        --ctrl type=unixio,path=${tpm_dir}/swtpm-sock \
        --log file=${tpm_dir}/swtpm.log \
        --tpm2 \
        --terminate &

# Is the switch port available ? Which mode ? Which VLAN ?
second_rightmost_byte=$(printf "%02x" $(expr ${tapnum} / 256))
rightmost_byte=$(printf "%02x" $(expr ${tapnum} % 256))
macaddress="b8:ad:ca:fe:$second_rightmost_byte:$rightmost_byte"
lladdress="fe80::baad:caff:fefe:$(printf "%x" ${tapnum})"
vlan_mode="$(sudo ovs-vsctl list port tap${tapnum} | grep vlan_mode | egrep -o '(access|trunk)')"

if [[ "$vlan_mode" == "access" ]]
then
        svi="vlan$(sudo ovs-vsctl list port tap${tapnum} | grep tag | grep -o -E '[0-9]+')"
else
        svi="dsw-host"
fi

image_format="${vm##*.}"

spice=$((5900 + ${tapnum}))
telnet=$((2300 + ${tapnum}))

# Is TPM socket is ready.
wait=0

while [[ ! -S ${tpm_dir}/swtpm-sock ]] && [[ $wait -lt 10 ]]
do
        echo "Waiting a second for TPM socket to be ready."
        sleep 1s
        ((wait++))
done

if [[ ${wait} -eq 10 ]]
then
        echo -e "${RED}TPM socket setup failed. Giving up.${NC}"
        exit 1
fi

echo -e "~> Virtual machine filename   : ${RED}${vm}${NC}"
echo -e "~> RAM size                   : ${RED}${memory}MB${NC}"
echo -e "~> SPICE VDI port number      : ${GREEN}${spice}${NC}"
echo -e "~> telnet console port number : ${GREEN}${telnet}${NC}"
echo -e "~> MAC address                : ${BLUE}${macaddress}${NC}"
echo -e "~> Switch port interface      : ${BLUE}tap${tapnum}, ${vlan_mode} mode${NC}"
echo -e "~> IPv6 LL address            : ${BLUE}${lladdress}%${svi}${NC}"
tput sgr0

ionice -c3 qemu-system-x86_64 \
        -machine type=q35,accel=kvm:tcg,kernel-irqchip=split \
        -cpu max,l3-cache=on,+vmx \
        -device intel-iommu,intremap=on \
        -daemonize \
        -name ${vm} \
        -m ${memory} \
        -device virtio-net-pci,mq=on,vectors=6,netdev=net${tapnum},disable-legacy=on,disable-modern=off,mac="${macaddress}" \
        -netdev tap,queues=2,ifname=tap${tapnum},id=net${tapnum},script=no,downscript=no,vhost=on \
        -serial telnet:localhost:${telnet},server,nowait \
        -device virtio-balloon \
        -smp 8,threads=2 \
        -rtc base=localtime,clock=host \
        -device i6300esb \
        -watchdog-action poweroff \
        -boot order=c,menu=on \
        -object iothread,id=iothread.drive0 \
        -drive if=none,id=drive0,aio=native,cache.direct=on,discard=unmap,format=${image_format},media=disk,l2-cache-size=8M,file=${vm} \
        -device virtio-blk,num-queues=4,drive=drive0,scsi=off,config-wce=off,iothread=iothread.drive0 \
        -global driver=cfi.pflash01,property=secure,value=on \
        -drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on \
        -drive if=pflash,format=raw,unit=1,file=${vm}_OVMF_VARS.fd \
        -k fr \
        -vga none \
        -device qxl-vga,vgamem_mb=64 \
        -spice port=${spice},addr=localhost,disable-ticketing=on \
        -device virtio-serial-pci \
        -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 \
        -chardev spicevmc,id=spicechannel0,name=vdagent \
        -object rng-random,filename=/dev/urandom,id=rng0 \
        -device virtio-rng-pci,rng=rng0 \
        -chardev socket,id=chrtpm,path=${tpm_dir}/swtpm-sock \
        -tpmdev emulator,id=tpm0,chardev=chrtpm \
        -device tpm-tis,tpmdev=tpm0 \
        -usb \
        -device usb-tablet,bus=usb-bus.0 \
        -device ich9-intel-hda,addr=1f.1 \
        -audiodev spice,id=snd0 \
        -device hda-output,audiodev=snd0 \
        $*