KVM/qemu mit QXL – hohe Auflösungen und virtuelle Monitore im Gastsystem definieren und nutzen – I

Wer KVM/QEMU ohne Spezialkenntnisse nutzen will, setzt hierfür z.B. "virt-manager" ein und vertraut darauf, dass das Gespann libvirt/qemu darunter schon sauber seine Arbeit verrichten wird. Die graphischen Tools von "virt-manager" unterstützen einen zumindest in Grenzen recht gut bei der Einrichtung und Überwachung der Gastsysteme. Das reicht für kleinere, lokale Virtualisierungsvorhaben in der Regel völlig aus; man muss nicht immer gleich zu Profi-Tools für die Verwaltung virtueller Systeme greifen. Für spezielle Gegebenheiten ist allerdings etwas Handarbeit erforderlich - das trifft u.U. auch auf den Einsatz eines oder mehrerer hochauflösender Monitore zu.

Diese Artikelserie geht auf die Frage ein, wie man mittels Tools von libvirt, QEMU, Spice und X-Windows hohe Monitor-Auflösungen auch beim Arbeiten mit virtualisierten Gastsystemen nutzen kann. Dies ist im besonderen dann von Interesse, wenn das Virtualisierungssystem bestimmte physikalische Möglichkeiten (z.B. externer Monitore) am Host oder in einem Remote-System nicht richtig erkennt und die gewünschte Auflösung deshalb über Standardtools der involvierten Desktop-Umgebungen des Gastes und u.U. auch des Hostes nicht angeboten wird.

Als ebenso spannend erweist sich in der Praxis die Frage, wie man denn für Linux-Gäste mehrere virtuelle Monitore auf hochauflösenden physikalischen Displays des Hostes oder eines Remote Systems nutzen kann. "virt-manager" allein bietet hierfür nämlich keine hinreichenden Konfigurationsoptionen an.

Ich setze nachfolgend voraus, dass der Leser sich schon mal mit "virt-manager" befasst hat und auch schon mal ein Linux-Gastsystem auf einem KVM-Host angelegt hat.

Voraussetzungen: Grafischer Zugriff auf das Gstsystem

Ist eine virtuelle Maschine mittels "virt-manager" unter KVM/QEMU erst einmal konfiguriert, so stellt sich die Frage nach dem Zugriff auf den Gast vom Host oder Remote-Systemen [RS] aus. Dabei sollen komplette grafische Oberflächen/Desktops des Gastes auf den physikalischen Monitoren des Hosts/RS dargestellt werden. Das ist natürlich u.a. mit VNC möglich. Speziell für Linux-Hosts und -Gäste wird ein performantes, verzögerungsfreies und auch bequemes Arbeiten vor allem aber durch die Kombination des Spice-Protokolls mit einer grafischen Spice-Konsole (Display Fenster) und mit einer virtuellen Grafikeinheit vom Typ "QXL" im Gastsystem gewährleistet.

Spice ist dabei für die Kommunikation der virtuellen Maschine mit dem Host/RS und darunter liegender HW verantwortlich. Spice ist als Client/Server-Protokoll konzipiert; auf dem Gastsystem verrichten Spice-Server-Komponenten, auf dem Host/RS dagegen Spice-Client-Komponenten ihre Arbeit. Der Grafik-Output des Gastes wird unter Einsatz von Kompressions- und Caching-Verfahren zum Host/RS transferiert und dort in ein Fenster (grafische Spice-Konsole) des Host/RS-Desktops eingeblendet (ähnlich wie bei VNC oder X2GO; allerdings ist Spice wohl nicht für mehrere parallele Verbindungen gedacht). Es gibt zwei Arten von grafischen Konsolen, die mit Spice kooperieren. "virt-manager" selbst bietet den sog. "virt-viewer" an.

"QXL" spielt dagegen den Part eines virtuellen Grafik-Devices im Gastsystem; ich nenne ein solches Device nachfolgend etwas vereinfacht auch eine "virtuelle Grafikkarte". Für deren effiziente Nutzung benötigt das Gastsystem einen speziellen qxl-Treiber. Der wird meist über X-Server-Pakete der jeweiligen Linux-Distribution bereitgestellt. (Ich behandle in dieser Artikelserie nicht den virtio-Treiber für die Grafik-Einheiten der Gastsysteme.)

Hinsichtlich der Auflösung des Gast-Desktops unter einer Spice-Konsole auf dem Host/RS unterstützt QXL dann im Normalfall eine breite Palette an Werten. Eine entsprechende Liste, die sich weitgehend an den physikalischen Fähigkeiten des Hosts/RS orientiert, bietet das Gast-System (!) unter Gnome bzw. KDE typischerweise über die jeweiligen Tools für die Screen/Display-Konfiguration an.

Der User kann dann im laufenden Betrieb die für ihn passende Auflösung wählen und umschalten. "virt-manager" erlaubt ergänzend eine Anpassung des "virt-viewer"-Fensters an die Größe des Gast-Desktops. Das hatte ich in dieser Form auch in einem früheren Artikel
KVM, video QXL und video virtio Treiber – Video-Auflösung des Gnome-Desktops eines Debian 8-Gastystems einstellen
erläutert.

Ist das schon die ganze Wahrheit? Leider nein. Es gibt nämlich eine Reihe von Problemen, die man als normaler User von "virt-manager" nicht oder nicht unmittelbar lösen kann:

  • Problem 1: Manchmal erweist sich das unter QXL angebotene Auflösungsspektrum nämlich als begrenzter als die physikalischen Möglichkeiten der Host-Monitore. Offenbar werden vermeintlich erkannte physikalische Einschränkungen des Hosts in einigen Fällen an das Gastsystem durchgereicht.
  • Problem 2: Copy/Paste zwischen Host und Gast funktionieren nicht!
  • Problem 3: Eine automatische Anpassung der Schirmauflösung des Gastsystems an die Größe des Spice-Viewers funktioniert nicht ohne weiteres!
  • Problem 4: Ein Multi-Monitor-Betrieb lässt sich für hohe Auflösungen auch für Linux-Gäste nicht ohne Kunstgriffe bewerkstelligen. Obwohl manche Artikel und Youtube Movies im Internet etwas anders versprechen!

Ich werde in den nachfolgenden Artikeln darstellen, dass und wie man dann u.U. "xrandr" nutzen kann, um die Grenzen des angebotenen Auflösungsspektrums im KVM-Gastsystem (genauer dessen graphischer Desktop) zu überschreiten und die Screen-Auflösung des Gastes an oder über die physikalischen Auflösungsmöglichkeiten der Host-Monitore zu treiben. Wobei ein "über" in der Praxis natürlich nur einen begrenzten Sinn hat.

Zudem werfen wir kurz einen Blick auf die Möglichkeit, den Speicher der virtuellen Video-Einheit zu erhöhen. Es liegt ja auf der Hand, dass hohe Auflösungen auch den Speicherbedarf von virtuellen Grafik-Devices erhöhen werden. Das gilt vor allem für einen Multi-Monitor-Betrieb; hierfür reichen die von virt-manger vergebenen Standardwerte nicht aus. Mittels "spice-vdagent" sorgen wir dann für etwas mehr Komfort. Wir beleuchten danach weitere Voraussetzungen zur Nutzung eines KVM-Gastes mit mehreren virtuellen Monitoren.

Problemmeldung eines Lesers

Wiso nehme ich mich hier überhaupt dieses Themas an? Kurz vor dem letzten Wochenende hat mich ein Leser angeschrieben, der an einem Laptop einen hochauflösenden externen Schirm mit einer Auflösung von 2560x1440 betreibt. Da über Display-Port sogar mit einer Vertikalfrequenz von 60Hz. Leider war es ihm nicht möglich, diese Auflösung auch in debian-basierten, virtualisierten Gastsystemen zu erreichen, die er unter KVM/qemu installiert hatte. Seine Frage war, was man da tun könne.

Ich konnte das Verhalten an einem eigenen Laptop weitgehend nachstellen. Es handelt sich bei meinem Laptop um einen älteren Worthmann Terra mit einer Optimus-Kombination aus Nvidia-Grafikkarte und einer CPU-gebundenen Intel HD4000, wobei letztere auch eine HDMI-Schnittstelle versorgt. Leider sind die Fähigkeiten der HD4000/HDMI-Kombination limitiert (auch durch den Hersteller). Das Board versorgt den HDMI-Bus nur mit einer Maximalfrequenz von 225 MHz. Das reicht leider nicht, um Auflösungen jenseits von 2048x1152 bei einer Vertikalfrequenz von 60Hz zu versorgen. Möglich sind aber 2048x1152 bei 60Hz und 2560x1440 bei einer Frequenz von 40 oder 44 Hz. Damit konnte ich das Problem des Lesers auf meinem Laptop aber nachstellen. Der Laptop wurde dazu als KVM-Host unter Opensuse Leap 42.2 mit KDE-Desktop betrieben:

Die maximale Auflösung, die mir debian-basierte KVM-Gastsysteme (Kali2017 mit Kernel 4.9, Debian 8 mit Kernel 3.16) auf diesem KVM-Host anboten, betrug unter "spice" und "qxl" 1920x1080 Pixel. Das entsprach gerade der Auflösung des integrierten Laptop-Schirms!

Die physikalisch möglichen Auflösungen des externen Monitors von 2048x1152_60.00Hz bzw. 2560x1440_44.00Hz unter HDMI wurden dagegen völlig ignoriert! Warum auch immer ... Tja, was kann man in einem solchen Fall tun?

Einsatz von xrandr und CVT auf dem Host als Ausgangspunkt ...

Es ist instruktiv, sich zunächst anzusehen, wie ich den Laptop dazu brachte, 2560x1440__44.00Hz auf dem HDMI-Monitor zu unterstützen. KDE's Tool "systemsettings5 > Anzeige und Monitor" bot mir diese Auflösung für den Desktop des Hosts nämlich keineswegs an; ich nehme an, dass die Ursache dafür auch im Intel i915-Treiber zu suchen ist:

Unter Linux steht (berechtigten Usern) das Werkzeug "xrandr" zur Verfügung, um Auflösungen für verschiedene Monitore zu konfigurieren. xrandr nutzt dazu die RandR-Erweiterung des X-Window-Systems. Siehe die unten angegebenen Links für weitere Infos. Verschiedene Desktops (Gnome, KDE, LXDE) nutzen "xrandr" über eigene integrierte grafische Tools. "xrandr" lässt sich als Kommando jedoch auch direkt an einem Shell-Prompt absetzen; ein Blick in die man-Seiten lohnt sich.

Gibt man etwa xrandr ohne Parameter am Prompt eines Terminalfensters ein, so erhält man Informationen zu den aktuell erkannten Monitoren und deren Auflösung. In meinem Fall etwa:

myself@mytux:~> xrandr
Screen 0: minimum 8 x 8, current 3968 x 1152, maximum 32767 x 32767
LVDS1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 382mm x 215mm
   1920x1080     60.02*+
   1400x1050     59.98  
   1600x900      60.00  
   1280x1024     60.02  
   1280x960      60.00  
   1368x768      60.00  
   1280x720      60.00  
   1024x768      60.00  
   1024x576      60.00  
   960x540       60.00  
   800x600       60.32    56.25  
   864x486       60.00  
   640x480       59.94  
   720x405       60.00  
   640x360       60.00  
DP1 disconnected (normal left inverted right x axis y axis)
HDMI1 connected 2048x1152+1920+0 (normal left inverted right x axis y axis) 553mm x 311mm
   2048x1152_60.00  59.90  
   2048x1152     60.00* 
   1920x1200     59.95  
   1920x1080     60.00    60.00    50.00    59.94    30.00    25.00    24.00    29.97    23.98  
   1920x1080i    60.00    50.00    59.94  
   1600x1200     60.00  
   1680x1050     59.88  
   1280x1024     75.02    60.02  
   1200x960      59.99  
   1152x864      75.00  
   1280x720      60.00    50.00    59.94  
   1024x768      75.03    60.00  
   800x600       75.00    60.32  
   720x576       50.00  
   720x576i      50.00  
   720x480       60.00    59.94  
   720x480i      60.00    59.94  
   640x480       75.00    60.00    59.94  
   720x400       70.08  
VGA1 disconnected (normal left inverted right x axis y axis)
VIRTUAL1 disconnected (normal left inverted right x axis y axis)

 
Man erkennt, dass für den HDMI1-Monitor als Maximal-Mode 2048x1440 bei 60Hz gelistet ist. Natürlich kann der tatsächlich angeschlossene Dell U2515H aber mehr.

Einsatz von xrandr:
Wirft man einen Blick in die man-Seiten zu xrandr, entdeckt man, dass xrandr einem Monitor Video-"Modes" zuordnen kann. Dazu benötigt man aber bestimmte Daten einer sog. "Modline". Wie erhält man nun eine gültige Modline für neue, vom System nicht erkannte Auflösungen? Unter Linux hilft hier das Tool "CVT". Es berechnet für gewünschte Auflösungen/Frequenzen die notwendigen Daten. Damit kann man ein wenig rumspielen - Bsp.:

myself@mytux:~> cvt 2560 1440 40
# 2560x1440 39.96 Hz (CVT) hsync: 58.98 kHz; pclk: 201.00 MHz                                                          
Modeline "2560x1440_40.00"  201.00  2560 2720 2984 3408  1440 1443 1448 1476 -hsync +vsync                             

myself@mytux:~> cvt 2560 1440 44
# 2560x1440 43.99 Hz (CVT) hsync: 65.06 kHz; pclk: 222.75 MHz                                                          
Modeline "2560x1440_44.00"  222.75  2560 2720 2992 3424  1440 1443 1448 1479 -hsync +vsync      

myself@mytux:~> cvt 2560 1440 50
# 2560x1440 49.96 Hz (CVT 3.69M9) hsync: 74.15 kHz; pclk: 256.25 MHz                                                   
Modeline "2560x1440_50.00"  256.25  2560 2736 3008 3456  1440 1443 1448 1484 -hsync +vsync                             

 
Die erste Zahl nach dem Auflösungsstring gibt jeweils die Taktrate/Frequenz an, mit der die Pixel angesteuert werden (PixelClock). Die nimmt mit Auflösung und Vertikal-Frequenz natürlich zu. In meinem Fall muss sie - wie gesagt - unter 225MHz liegen.

Folgende Sequenz an Befehlen erfasst nun eine neue Mode und ordnet diese dem HDMI1-Monitor zu (ggf. als User root absetzen).

mytux:~ # xrandr --newmode 2560x1440_44 222.75  2560 2720 2992 3424  1440 1443 1448 1479 -hsync +vsync
mytux:~ # xrandr --addmode HDMI1 2560x1440_44
mytux:~ # xrandr --output HDMI1 --mode "2560x1440_44"

Danach bietet mir KDE's "systemsettings5" diese Auflösung bereits an:

Dort kann man die Auflösung auch aktivieren. Wer für die Aktivierung jedoch die Kommandozeile vorzieht, kann auch den Befehl

mytux:~ # xrandr --output HDMI1 --mode "2560x1440_44"

absetzen. Und schon läuft der externe Schirm mit höherer Auflösung :

myself@mytux:~> xrandr --current
Screen 0: minimum 8 x 8, current 4480 x 1440, maximum 32767 x 32767
LVDS1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 382mm x 215mm
   1920x1080     60.02*+
   ....
   .... 
DP1 disconnected (normal left inverted right x axis y axis)
HDMI1 connected 2560x1440+1920+0 (normal left inverted right x axis y axis) 553mm x 311mm
   2560x1440     43.99 +
   2048x1152_60.00  59.90  
   2048x1152     60.00  
   1920x1200     59.95  
   1920x1080     60.00    60.00    50.00    59.94    30.00    25.00    24.00    29.97    23.98  
   1920x1080i    60.00    50.00    59.94  
   1600x1200     60.00  
   1680x1050     59.88  
   1280x1024     75.02    60.02  
   1200x960      59.99  
   1152x864      75.00  
   1280x720      60.00    50.00    59.94  
   1024x768      75.03    60.00  
   800x600       75.00    60.32  
   720x576       50.00  
   720x576i      50.00  
   720x480       60.00    59.94  
   720x480i      60.00    59.94  
   640x480       75.00    60.00    59.94  
   720x400       70.08  
   2560x1440_44  43.99* 
VGA1 disconnected (normal left inverted right x axis y axis)
VIRTUAL1 disconnected (normal left inverted right x axis y axis)

 

Um eine Persistenz dieser Auflösungsvorgaben über einen Shutdown und Reboot hinaus zu erreichen, trägt man die gefundene Modline dann in eine Datei "/etc/X11/xorg.conf.d/10-monitor.conf" ein. Dies ist u.a. unter https://wiki.archlinux.org/index.php/xrandr beschrieben. Alternativ kann man eine Batchdatei anlegen und diese beim Start des Desktops automatisch ausführen lassen. Eine weitere Möglichkeit ist unter https://wiki.ubuntuusers.de/RandR/ beschrieben. Ich komme darauf in einem Folgeartikel detaillierter zurück; dort aber bei der Anwendung von "xrandr" im KVM-Gastsystem.

Funktioniert xrandr auch innerhalb von KVM/QEMU-Gastsystemen?

Für Neugierige, die schon mal experimentieren wollen: Ja, das geht! Zumindest mit QXL! Ist dabei die Vertikalfrequenz relevant? So, wie ich das verstehe, nicht. Die Video-Information wird ja per Spice in den Speicher der physikalischen Grafikkarte eingeblendet. Dafür sollte nur die Fenster- und Pixel-Info relevant sein. Aber es schadet nicht, sich an vernünftige Limits (60Hz) zu halten.

Es gibt allerdings andere Beschränkungen für ein performantes Verhalten hochauflösender Videomodes. Genauso wie physikalische Karten muss auch die virtuelle Video-Einheit mit hinreichendem Speicher für hohe Auflösungen versorgt werden!

Und Leute, die sich mit VMware auskennen, wissen auch, dass im Gastsystem Zusatzprogramme aktiviert werden müssen - u.a. um Copy/Paste zwischen Host und Gast zu ermöglichen oder eine dynamische Anpassung der Gast-Auflösung an die Fenstergröße zu ereichen. Man vermutet nicht ganz zu Unrecht, das es etwas Korrespondierendes wohl auch unter KVM/qemu geben sollte.

Bevor wir auf dem Client xrandr testen, kümmern wir uns deshalb zunächst um drei wichtige Aspekte der Gast-Ausstattung für Spice/QXL - Vorgaben für den Video-Speicher des QXL-Devices, die Bereitstellung eines Treibers und des sog. "spice-vdagent". Als erstes müssen wir Spice und QXL überhaupt erstmal für unsere Virtuelle Maschine unter QEMU bereitstellen und aktivieren.

Spice und QXL aktivieren

Spice und QXL lassen sich bereits bei der Konfiguration eines Gastsystems über die entsprechende Oberfläche von "virt-manager" einstellen. Die Video-Konfiguration lässt sich dort aber auch im Nachhinein beeinflussen. Man entfernt dazu die alten Video-HW-Komponenten und ersetzt sie durch folgende:

Man sieht am letzten Fenster bereits, dass die Parameter für die QXL-Karte unter "virt-manager" leider nicht beeinflussbar sind!

Parameter für das Video Memory des QXL-Devices

Hohe 2D-Auflösungen bis 4K (4096 × 2160) erfordern als Minimum 32MiB Speicherkapazität für einen Schirm. Für flüssiges Arbeiten und die dafür notwendige Pufferung von Bildschirminhalten etc. aber deutlich mehr (64MB). Ich rede hier lediglich über 2D-Operationen. Die Standardeinstellung der qxl-Karte ist (zumindest unter Opensuse) für Framebuffer-Memory lediglich 16MB. Das reicht für 2560x1440x32 gerade so. Es schadet aber nichts, für noch höhere Auflösungen, mit denen man ggf. arbeiten will, mehr Video-RAM zu Verfügung zu stellen. Wie macht man das?

Leider gibt es keine Möglichkeit, das direkt mit "virt-manager" (Vers. 1.4) zu erledigen. Man ist vielmehr gezwungen, die unter libvirt erzeugten XML-Dateien, die die Konfiguration virtueller Maschinen beschreiben, zu editieren. Unter "libvirt" spricht man auch von der Konfigurationsdatei einer virtuellen "Domäne".

Das Editieren der XML-Domän-Dateien kann man entweder mit "virsh edit" oder aber durch direkten Zugriff mit seinem Lieblingseditor auf die Konfigurationsdateien durchführen. In letzterem Fall muss man selbst dafür sorgen, dass die XML-Struktur in Takt bleibt und alle Tags sauber abgeschlossen werden.

Man findet die Konfigurationsdateien unter "/etc/libvirt/qemu" in der Form "NAME.xml". NAME steht hier für eine - z.B. über virt-manager angelegte - Virtualisierungs-"Domäne" (s. hierzu die Einleitung in https://libvirt.org/formatdomain.html#elements .

Für virtuelle qxl-basierte Video-Einheiten gibt es z.Z. mehrere Konfigurationsparameter: ram, vram, vram64, vgamem. Zusätzlich wichtig ist der Parameter "heads". Der besagt, wieviele virtuelle Monitore angeschlossen werden sollen und kann max. den Wert "4" annehmen. Dieser Wert wird nur in Linux-Gast-Systeme berücksichtigt. (Virtueller Multi-Monitor-Betrieb von Windows-Gastsystemen erfordert mehrere QXL-Einheiten).

Ein typischer Standard-Eintrag für ein QXL-Device hat etwa folgende Form:

    <video>
      <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/>
    </video>

 
Bzgl. der Anzahl, der Bezeichnung und Bedeutung der QXL-Paramter hat sich in den letzten Jahren einiges in schnellem Rhythmus verändert. Ich kann daher nur versuchen, mein aktuelles Verständnis davon wiederzugeben; es beruht auf nachfolgend genannten Artikeln im Internet und eigenen Tests.
Siehe hierzu etwa https://libvirt.org/formatdomain.html#elementsVideo, siehe aber auch http://www.ovirt.org/documentation/draft/video-ram/.

Die Angaben unter den beiden Links sind - soweit ich das sehe - nicht ganz kompatibel. Was sagen eigene Tests?

  • KiB: Die Werte zu diesen Parametern werden in der Konfigurationsdatei in "KiB" angegeben. 512MiB entsprechen daher 524288 KiB und '536870912' Byte, 64MiB = 67108864 Byte. Das ist deshalb interessant, weil diese Werte vom System in anderen Kontexten durchaus in unterschiedlichen Einheiten angegeben werden.
  • Standardwerte: ram='65536', vram='65536', vram64='0', vgamem='16384', 'heads=1'.
  • heads:Steht für die Anzahl der Ausgänge der virtuellen qxl-Grafikkarte, d.h. für die Anzahl der virtuellen Monitore, die man in einem Linux-Gastsystem über dasselbe Video-Device bedienen will. Ja, es ist durchaus möglich, ein Gastsystem mit mehreren virtuellen Monitoren auszustatten; s. hierzu eine der kommenden Artikel dieser Serie. Der Maximalwert für "heads" ist z.Z. offenbar 4. ["heads" mit einem Wert > 1 zu versehen, ist übrigens nur für Linux-Gastsysteme sinnvoll. Für Windows-Gastsysteme müssen mehrere Grafik-Devices für virtuelle Multi-Monitor-Konfigurationen angelegt werden.]
  • vgamem:Minimalwert (entspricht gleichzeit der Framebuffergröße) - dieser Minimalwert muss den Wert der Auflösung x Farbtiefe abdecken. Der Framebuffer-Modus dient auch als Fallback-Modus - etwa, wenn der QXL-Treiber im Gastystem nicht installiert ist. Da 32Bit Farbtiefe 4 Bytes entsprechen, ist die Auflösung Höhe x Breite in Pixel mit einem Faktor 4 zu multiplizieren. Im Falle von Linux-Gastystemen ist als Faktor noch die Anzahl der Schirme (=Heads) zu berücksichtigen.
  • ram: Tests zeigen, dass der Parameter offenbar einen limitierten Memory-Bereich wiedergibt (für einen sog. virtuellen Memory-Bar 1; entspricht einem PCI Memory Bar-Bereich). Für diesen primären Memory-"Bar" erhält man nie effektiv wirksame Video-Ram-Werte > 512MiB - auch wenn man etwa ram='1073741824' festlegt. Das liegt offenbar an einer 32-Bit Adressierung. Dieser Speicher steht als sog. "IO Pages Memory" zur Verfügung. Er sollte (Pufferung!) mehr als das 4-fache des vgamem-Wertes betragen.
  • vram:Tests zeigen, dass dieser Parameter offenbar einen weiteren mit 32Bit addressierten Memory-Bereich wiedergibt (Adress-Bar 2; entspricht einem PCI Memory Bar-Bereich). Auch für diesen sekundären Memory-"Bar" erhält man daher nie effektiv wirksame Video-Ram-Werte > 512MiB. Der Speicherbereich steht als sog. Surface-Memory der virtuellen Graka zur Verfügung. Er sollte mindestens so groß sein, wie das Doppelte des vgamem-Wertes.
    Dieser Memory-bereich kann bei Bedarf neben Texturen wohl auch IO-Page-Daten aufnehmen.
  • vram64:Alternative zu vram! Tests zeigen, dass dieser Parameter offenbar einen mit 64Bit adressierten Memory-Bereich wiedergibt (Adress-Bar 2). Der Parameter überschreibt vram; d.h. wenn angegeben, gilt vram64 und vram wird ignoriert. Der zweite Memory-Bar kann unter vram64 somit sehr viele größere Speicherwerte adressieren als unter vram. vram64-Angaben sind daher vor allem interessant, wenn man mit wirklich großen Auflösungen (4K und größer) und mehreren virtuellen Schirmen arbeiten will.

Man kann sich das QXL-Device - also die virtuelle Grafikkarte - etwa wie folgt vorstellen:

Für Linux-Gastsysteme gelten dabei folgende Formeln :

Formeln zur Berechnung des QXL Video RAMs

vgamem >= Breite x Höhe x 4 x Anzahl Heads
ram >= 4 x vgamem (maximal 524288 KiB)
vram >= 2 x vgamem (maximal 524288 KiB)
Hohe Auflösungen : vram64 > 4 x vgamem (beliebig ≤ 2x10^12)

Was bedeutet das in unserem Fall für die Auflösung 2560 x 1440 :

vgamem >= 2560 x 1440 x 4 x Anzahl Heads
Plant man also mit 4 virtuellen Monitoren zu arbeiten:
vgamem ≥ 14400 x 4; wir runden das auf: 16384 x 4 = 65536 (= 64 MiB)
ram ≥ 262144 (248 MiB)
vram ≥ 131072 (128 MiB)

Rücksichtnahme auf den verfügbaren RAM des Hosts!

Natürlich kann es für Experimente auch mit nur einem virtuellen Monitor sinnvoll sein, bei hohen Auflösungen bzgl. der Video-Speicher-Größe auf die sichere Seite zu gehen. Es gilt aber:

Der insgesamt bereitgestellte Video RAM addiert sich zum festgelegten RAM der virtuellen Maschine dazu; d.h. er wird zusätzlich vom verfügbaren RAM des Hosts für KVM-Gastsysteme abgezweigt.

Das ist dann wichtig, wenn der RAM des Hostes begrenzt ist. In meinem Fall (16G RAM auf dem Laptop, 64G auf Desktops) ist das bei einer oder zwei virtuellen Maschinen nicht so relevant. Aber wenn der Speicher begrenzter ist und/oder mehr virtuelle Maschinen laufen sollen, lohnt es sich schon, den Video RAM auf das Notwendige einzuschränken. Für die Gesamtperformance des Systems kann mehr RAM durchaus viel wichtiger sein als ein zu groß gewählter Video RAM !

Abändern der Parameter in den Domän-Dateien von libvirt

Man kann meines Wissens z.Z. noch keine Einstellungen von Parametern für virtuelle QXL-Devices mittels "virt-manager" vornehmen. Davon lassen wir uns aber nicht abschrecken. Was immer man beim Aufsetzen einer virtuellen Maschine unter KVM/qemu mittels des Tools "virt-manager" vornimmt, landet ja in einer entsprechenden XML-Konfigurationsdatei unter dem Verzeichnis "/etc/libvirt/qemu". In der dortigen XML-Datei muss man die Zeilen mit <video> suchen und abändern. Valide Einträge, die man mit dem Lieblingseditor seiner Wahl vornimmt, sind in unserem Fall (heads=4) etwa :

    <video>
      <model type='qxl' ram='262144' vram='262144' vgamem='65536' heads='4' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>

Getestet habe ich in den nachfolgenden Artikeln mit den eben genannten Werten.

Mit Maximalwerten für ram, vram sähe das Ganze dagegen so aus:

    <video>
      <model type='qxl' ram='524288' vram='524288' vgamem='65536' heads='4' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>

Testweise kann man sich ja aber auch mal ansehen, was bei folgenden Einstellungen passiert:

    <video>
      <model type='qxl' ram='262144' vram64='2097152' vgamem='65536' heads='4' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>
    
    <video>
      <model type='qxl' ram='1048576' vram='1048576' vgamem='65536' heads='4' primary='yes'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>

 

Wichtiger Nachtrag 15.08.2017:

Ich musste gerade feststellen, dass es mit dem theoretischen Maximalwert für ram in Abhängigkeit vom RAM der virtuellen Maschine ("memory") ein Problem gibt. In einer der Debian-Gastsysteme versuchte ich zwischenzeitlich, den normalen RAM von 2048 MB auf 4096 MB zu erhöhen. vram war auf "524288" gesetzt. Daraufhin blieb der Bildschirm des Gastes beim Starten leider schwarz.

Lösung:

Nach etwas Recherche habe ich dann eine Bugmeldung für Virtual-Box zu einem ähnlichen Fehler gefunden; dort half eine Reduktion des Video Rams. Also habe ich das mal unter QEMU ausprobiert und bin bzgl. QXL zurück auf ram='262144'. Dann klappt alles auch mit einem Memory der VM ≥ 4096 MB.

Eine Wert vram='524288' oder vram64='2097152' bereitet dagegen keine Probleme. Dieser Wert ist auch groß genug für die meisten sinnvollen Anwendungen.

Mir ist im Moment nicht klar, warum dieses Problem auftritt. Der geneigte Leser sei hiermit aber vorgewarnt.

Wichtiger Hinweis: Restart des libvirtd.service vornehmen oder "virsh define" ausführen, um die Änderungen an den Domän-Dateien unter virt-manager bekannt zu machen.

Wenn ich solche Eingriffe in einer Domän-XML-Datei vornehme fahre ich die betroffene virtuelle Maschine vorher zur Sicherheit runter. (Den Zustand aller anderen laufenden Gastsysteme kann man zwischenzeitlich dagegen auf die Festplatte speichern und die zugehörigen virtuellen Maschinen selbst pausieren lassen. Siehe hierzu die entsprechende Option "Save" unter virt-manager"). Nach der Durchführung der Änderungen an der XML-Definitionsdatei zu einer einzelnen virtuellen Maschine ( bzw. "Domäne") müssen die neuen Einstellungen dem laufenden libvirtd-Dämon oder zugehörigen administrativen Programmen bekannt gemacht werden. Dafür gibt es zwei Möglichkeiten:/p>

Möglichkeit 1: Restart des libvirtd-Dämons und ein neues Connecten zum Dämon unter "virt-manager". Vorab sollte der Zustand aller laufenden virtuellen Maschinen Ge-"saved" werden. Danach als user "root" das Kommando
systemctl restart libvirtd.service
absetzen! Dann sich unter virt-manager wieder mit dem Dämon verbinden und die geänderte Maschine neu hochfahren sowie die suspendierten anderen Systeme erneut starten.

Möglichkeit 2: Nutzen von "virsh" für eine Re-Definition der geänderten libvirt-"Domäne":

$ virsh --connect qemu:///system
Connecting to uri: qemu:///system
Welcome to virsh, the virtualization interactive terminal.

Type:  'help' for help with commands
       'quit' to quit

virsh # define /etc/libvirt/qemu/NAME_der_geanderten_VM.xml

Siehe zur 2-ten Möglichkeit auch: https://help.ubuntu.com/community/KVM/Managing.

Überprüfung des angeforderten und des tatsächlich bereitgestellten Video-Speichers

Wie prüfe ich, wie die Daten der XML-Datei beim erneuten Start des Gastsystems umgesetzt werden? Dazu kann man sich zunächst mal den Befehl ansehen, der zum Start der virtuellen Maschine faktisch abgesetzt wurde:

ps aux | grep qemu | grep mem

Im Output finden sich dann Abschnitte wie

    -chardev spicevmc,id=charchannel0,name=vdagent 
    -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=com.redhat.spice.0 
    -spice port=5900,addr=127.0.0.1,disable-ticketing,image-compression=off,seamless-migration=on 
    -device qxl-vga,id=video0,ram_size=268435456,vram_size=268435456,vram64_size_mb=0,vgamem_mb=64,max_outputs=4,bus=pci.0,addr=0x2

 

Man erkennt hier unschwer die Umsetzung der Speicheranforderungen. Komischerweise in unterschiedlichen Einheiten (ram_size und vram_size in Bytes und vgamem_mb bzw. vram64_size in MiB! Das soll uns aber nicht weiter stören ....

Im Gastsystem selbst hilft nach dem Start meist ein Absetzen des Kommandos "dmesg | grep drm".

root@kali2017-1:~# dmesg | grep drm
[    1.668901] [drm] Initialized
[    1.828524] [drm] Device Version 0.0
[    1.828529] [drm] Compression level 0 log level 0
[    1.828530] [drm] Currently using mode #0, list at 0x488
[    1.828530] [drm] 114686 io pages at offset 0x4000000
[    1.828531] [drm] 67108864 byte draw area at offset 0x0
[    1.828531] [drm] RAM header offset: 0x1fffe000
[    1.828532] [drm] rom modes offset 0x488 for 142 modes
[    1.832481] [drm] qxl: 64M of VRAM memory size
[    1.832481] [drm] qxl: 511M of IO pages memory ready (VRAM domain)
[    1.832482] [drm] qxl: 2048M of Surface memory size
[    1.836647] [drm] main mem slot 1 [a0000000,1fffe000]
[    1.836648] [drm] surface mem slot 2 [100000000,80000000]
[    1.836649] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[    1.836649] [drm] No driver support for vblank timestamp query.
[    1.837401] [drm] fb mappable at 0xA0000000, size 3145728
[    1.837402] [drm] fb: depth 24, pitch 4096, width 1024, height 768
[    1.838735] fbcon: qxldrmfb (fb0) is primary device
[    1.865428] qxl 0000:00:02.0: fb0: qxldrmfb frame buffer device
[    1.889589] [drm] Initialized qxl 0.1.0 20120117 for 0000:00:02.0 on minor 0
[    1.927986] [drm] Device Version 1.0
[    1.927987] [drm] Compression level 0 log level 0
[    1.927988] [drm] Currently using mode #0, list at 0x488
[    1.927989] [drm] 12286 io pages at offset 0x1000000
[    1.927989] [drm] 16777216 byte draw area at offset 0x0
[    1.927990] [drm] RAM header offset: 0x3ffe000
[    1.927990] [drm] rom modes offset 0x488 for 128 modes
[    1.927996] [drm] qxl: 16M of VRAM memory size
[    1.927997] [drm] qxl: 63M of IO pages memory ready (VRAM domain)
[    1.927997] [drm] qxl: 64M of Surface memory size
[    1.932065] [drm] main mem slot 1 [e0000000,3ffe000]
[    1.932067] [drm] surface mem slot 2 [e4000000,4000000]
[    1.932068] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[    1.932068] [drm] No driver support for vblank timestamp query.
[    1.933351] [drm] fb mappable at 0xE0000000, size 3145728
[    1.933352] [drm] fb: depth 24, pitch 4096, width 1024, height 768
[    1.933890] qxl 0000:00:0a.0: fb1: qxldrmfb frame buffer device
[    1.933894] [drm] Initialized qxl 0.1.0 20120117 for 0000:00:0a.0 on minor 1
root@kali2017-1:~# 

 
Der aufmerksame Leser hat sicher mitbekommen, dass ich im letzten Beispiel eine virtuelle Maschine mit 2 QXL-Devices dargestellt habe. Das erste QXL-Device hat dabei Werte von ram='524288' vram64='2097152' vgamem='65536' zugeteilt bekommen.

Wenn der Ringpuffer des dmesg-Befehl das obige Ergebnis nicht liefert (kann nach einer Weile im laufenden Betrieb des Gastsystems passieren), der kann sich z.B. das Paket "hwinfo" installieren, damit alle HW-Einstellungen auslesen und dann im Output nach "graphics" suchen (oder die Ergebnisse von vornherein auf Zeilen mit "video" und "Graphic" einschränken.)

Genug für heute! Im Nachfolgeartikel

KVM/qemu mit QXL - hohe Auflösungen und mehrere virtuelle Monitore im Gastsystem definieren und nutzen - II

laden wir den qxl-Treiber im Gastsdystem nach, starten zudem auch den spice-vdagent und wenden schließlich "xrandr" im Gastsystem an.

Links

virt-manager
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Virtualization_Deployment_and_Administration_Guide/sect-Creating_guests_with_virt_manager.html
https://wiki.ubuntuusers.de/virt-manager
https://www.thomas-krenn.com/de/wiki/Virtual_Machine_Manager_-_GUI_zur_Verwaltung_virtueller_Maschinen
https://wiki.libvirt.org/page/CreatingNewVM_in_VirtualMachineManager

Video und Video-RAM
https://www.x.org/wiki/Development/Documentation/HowVideoCardsWork/
http://www.ovirt.org/documentation/draft/video-ram/
http://bart.vanhauwaert.org/hints/installing-win10-on-KVM.html
https://wiki.archlinux.org/index.php/QEMU#qxl
https://lists.freedesktop.org/archives/spice-devel/2015-June/020459.html
https://askubuntu.com/questions/46197/how-to-check-video-memory-size

xrandr
https://wiki.archlinux.org/index.php/xrandr
https://wiki.gentoo.org/wiki/Xrandr
https://pkg-xorg.alioth.debian.org/howto/use-xrandr.html
https://bbs.archlinux.org/viewtopic.php?id=199100
https://unix.stackexchange.com/questions/227876/how-to-set-custom-resolution-using-xrandr-when-the-resolution-is-not-available-i
https://wiki.ubuntuusers.de/RandR/
https://axebase.net/blog/2011/07/27/hinzufuegen-einer-aufloesung-ueber-cvt-und-xrandr/

CVT
http://www.uruk.org/projects/cvt/

KVM – Anlegen eines privaten, isolierten Netzwerks mit virt-manager

Im vorhergehenden Artikel dieses Blogs
Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts
hatte ich 2 Arten der Anbindung eines KVM-Gastsystems an die physikalische Umwelt des KVM-Hostes diskutiert. Ich hatte angemerkt, dass es unter KVM/libvirt neben einem direkten Bridging zu einer physikalischen Host-NIC natürlich auch die Möglichkeit gibt, KVM-Gäste an ein sog. "Host-Only-Network" [HON] anzubinden. Intern wird dieses Netzwerk durch eine virtuelle Bridge repräsentiert. Soll man aus dem HON heraus mit der physikalischen Umwelt (LAN) kommunizieren, muss man auf dem KVM-Host Routing zwischen einer bereitgestellten virtuellen Host-NIC der Bridge zu einer physikalischen NIC des Hostes ermöglichen. Letztere leitet die Pakete dann ins LAN.

Unterlässt man das Routing (und/oder filtert man Pakete aus dem HON) auf dem Host, so befinden sich Gastsystem und Host in einem isolierten virtuellen Netz, aus dem nach außen ohne weitere Vorkehrungen nicht kommuniziert werden kann. Dem isolierten Netz können natürlich weitere Gäste beitreten.

Wegen der Nachfrage einer Leserin, zeige ich nachfolgend kurz die Anlage eines virtuellen Host-Only-Netzwerks unter KVM mittels "virt-manager". Ich setze voraus, dass der "libvirtd"-Daemon läuft.

Man ruft als root "virt-manager" auf und geht im Übersichtsfenster auf "Edit >> Connection Details" und dort auf den Reiter "Virtual Networks". Dort findet man unter der Übersichtsliste zu bereits vorhandenen Netzwerken, einen Button mit einem "+" Symbol zum Anlegen eines neuen Netzwerks. Ich zeige nachfolgend die Dialogsequenz:

new_network_1

new_network_2

new_network_3

new_network_4

new_network_5

Die Bridge und eine zugehörige Host-NIC tauchen dann auch in der Liste der vorhandenen Netzwerk-Devices auf. Unter Opensuse zeigt das Komamndo "wicked show all" dann etwa ein virbr-Device (virtual bridge - im Beispiel ein "virbr2"):

virbr2          device-unconfigured
      link:     #73, state device-up, mtu 1500
      type:     bridge
      addr:     ipv4 192.168.120.1/24

virbr2-nic      device-unconfigured
      link:     #74, state down, mtu 1500, master virbr2
      type:     tap, hwaddr 52:54:00:c9:bd:24

 
Das neue Netzwerk findet sich dann auch in Form einer XML-Netzwerk-Konfigurations-Datei unter "/etc/libvirt/qemu/networks/host2.xml" wieder:

<!--
WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:
  virsh net-edit host2
or other application using the libvirt API.
-->

<network>
  <name>host2</name>
  <uuid>f47c2d04-b1d6-48bf-a6dc-a643d28b38d3</uuid>
  <bridge name='virbr2' stp='on' delay='0'/>
  <mac address='52:54:00:c9:bd:24'/>
  <domain name='host2'/>
  <ip address='192.168.120.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.120.128' end='192.168.120.254'/>
    </dhcp>
  </ip>
</network>

 
Will man eine solche Host-Only-Bridge von einem KVM-Gast aus nutzen, so muss man für diesen Gast ein entsprechendes "Netzwerk"-Device (NIC) anlegen, das der Bridge zugeordnet wird. "virt-manager" bietet auch hierfür entsprechende grafische Dialoge an. Ich gehe davon aus, dass eine virtuelle Maschine (z.B. namens "kali2") bereits existiert. Man öffnet deren Konfigurations-Oberfläche durch Doppelklick auf den entsprechenden Eintrag in der Liste aller KVM-Instanzen unter "virt-manager". Im sich öffnenden Fenster klickt man weiter auf den Button mit dem "i"-Symbol:

new_network_6

Danach taucht das Device auch in der XML-Konfigurationsdatei für den Gast auf - in meinem Beispiel etwa für einen Kali-Gast mit der Datei "/etc/libvirt/qemu/kali.xml" - ich zeige nur den relevanten Ausschnitt:

    <interface type='network'>
      <mac address='52:54:00:0d:3c:8b'/>
      <source network='host2'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x0d' function='0x0'/>
    </interface>

 
Danach muss man den Gast neu starten und in ihm natürlich das neu aufgetauchte Netzwerk-Interface manuell konfigurieren - falls man nicht auf DHCP setzt und/oder besondere Einstellungen benötigt. Man merke sich hierzu die MAC-Adresse, um bei mehreren NICs nicht den Überblick zu verlieren! Man achte auch auf das Default-Gateway, wenn Routing über den Host gewünscht ist.

Achtung: Das neue virtuelle Netzwerk des Gastes ist mit "ifconfig", "ip" oder "wicked" Kommandos erst als ein "vnetN"-Device sichtbar, wenn der Gast gestartet und aktiv ist. Das "N" steht dabei für eine fortlaufende Nummer, die vom System (libvirt) vergeben wird. Z.B. taucht unter "wicked show all" dann ggf. ein Device "vnet3" auf:

virbr2          device-unconfigured
      link:     #73, state device-up, mtu 1500
      type:     bridge
      addr:     ipv4 192.168.120.1/24

virbr2-nic      device-unconfigured
      link:     #74, state down, mtu 1500, master virbr2
      type:     tap, hwaddr 52:54:00:c9:bd:24
...
...
vnet3           device-unconfigured
      link:     #17, state up, mtu 1500, master virbr2
      type:     tap, hwaddr fe:54:00:0d:3c:8b

 
Man beachte, dass sowohl die Host-Nic, als auch das Device des Gastes "tap"-Devices sind. Allg. Infos zu tap-devices finden sich hier:
https://www.kernel.org/doc/Documentation/networking/tuntap.txt
https://de.wikipedia.org/wiki/TUN/TAP

Hinweise für Leser, die die Konfiguration lieber manuell und über die Kommandozeile durchführen mögen

Wie man eine virtuelle Bridge auf dem KVM-Host mittels des "brctl"-Kommandos einrichtet, benennt und wie man ihr "tap"-Devices zuordnet, habe ich im Prinzip bereits früher am Beispiel einer direkten Bridge zu einem physikalischen Device beschrieben:
Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts
Siehe aber auch hier:
http://www.linux-kvm.org/page/Networking#Configuring_Guest_Networking - Abschnitt (Private Network)

"tap"-Devices kann man manuell und temporär über das Kommando "tunctl" auf dem Host erzeugen.
Siehe etwa :
http://unix.stackexchange.com/questions/86720/can-i-create-a-virtual-ethernet-interface-named-eth0
http://serverfault.com/questions/347895/creating-tun-tap-devices-on-linux
http://www.naturalborncoder.com/virtualization/2014/10/17/understanding-tun-tap-interfaces/
http://blog.elastocloud.org/2015/07/qemukvm-bridged-network-with-tap.html

Die Zuordnung von "tap"-Devices zu einer virtuellen Linux-Bridge erfolgt über das "brctl addif"-Kommando. Nun fehlt also nur noch eine Methode, um einmal erzeugte "tap"-Devices in die Konfiguration eines Gastes einzubinden.

Ich kenne einige Leute, die starten ihre virtuellen Maschinen lieber eigenhändig und über Scripts statt über virt-manager. Dann kann die Netzwerk-Konfiguration des Gastsystems in Form passender Optionsparameter des Kommandos "qemu-system-x86_64" (mit weiteren Optionen für KVM-Unterstützung; s.u.) oder des Kommandos "qemu-kvm" zum Starten eines KVM-Gastes geschehen. Eine Übersicht über diese Möglichkeit findet man hier:
http://qemu-buch.de/de/index.php?title=QEMU-KVM-Buch/_Netzwerkoptionen/_Virtuelle_Netzwerke_konfigurieren
In abgekürzter Form auch hier :
https://bbs.archlinux.org/viewtopic.php?pid=1148335#p1148335
https://bbs.archlinux.org/viewtopic.php?pid=1424044#p1424044

Eine weitere Alternative ist hier beschrieben (s. den Abschnitt zu "Private Networking"):
http://www.linux-kvm.org/page/Networking#Configuring_Guest_Networking

Übrigens: "qemu-kvm" ist auf aktuellen Linux-Systemen meist nur ein kleines Shell-Script-Kommando, dass "qemu-system-x86_64" mit KVM-Hardware-Unterstützungsoptionen aufruft! Siehe zum Unterschied zw. "qemu-system-x86_64" und "qemu-kvm" etwa
http://www.linux-kvm.com/content/qemu-kvm-or-qemu-system-x8664%EF%BC%9F).
Die Optionen des "qemu-kvm"-Kommandos sind z.B. hier beschrieben:
https://www.suse.com/documentation/sles11/book_kvm/data/cha_qemu_running_gen_opts.html
Eine Zusammenfassung zu tap-Devices und verschiedenen Bridging-Varianten gibt auch
https://www.suse.com/documentation/sles11/book_kvm/data/cha_qemu_running_networking.html

Will man die Tools von "libvirt/virt-manager" zum Starten der virtuellen Maschine benutzen, dann kann die "manuelle" Definition von virtuellen NICs für einen KVM-Gastes aber auch mittels des "virsh edit"-Kommandos zur Manipulation des XML-Files für die Gastkonfiguration durchgeführt werden.
Siehe:
http://serverfault.com/questions/665440/set-up-network-interfaces-in-ubuntu-for-kvm-virtual-machine

In einigen Situationen kann es auch erforderlich sein, den Gast im laufenden Betrieb um ein Netzwerkinterface zu einem neu definierten virtuellen Netz zu erweitern. Informationen hierzu findet man hier:
http://www.linuxwave.info/2014/12/hot-attach-and-hot-detach-network.html
https://kashyapc.fedorapeople.org/virt/add-network-card-in-guest.txt

Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts

Wenn man sich mit KVM beschäftigt, taucht u.U. das Thema auf, dass dem einen oder anderen KVM-Gastsystem ggf. ein virtuelles Netzwerk-Device im "bridged mode" zugeordnet werden soll. Dabei wird ein physikalisches Netzwerkdevice (NIC) des KVM-Hostes vom Gastsystem als Ressource für Verbindungen in die Netzwerkumgebung des Hosts genutzt. Das Gastsystem erhält eine IP-Adresse im physikalischen LAN des Hosts. Ich hatte ein solches Vorgehen in einem früheren Artikel als "direktes Bridging zum LAN" bezeichnet, um es von anderen Formen des Einsatzes virtueller Brigdes abzugrenzen. Wir beschreiben in diesem Artikel Elemente einer solchen Bridge-Konfiguration und gehen dann der Frage nach, wie man die Bridge mittels Linux-Kommandos manuell anlegt.

YaST legt virtuelle direkte LAN-Bridges im Rahmen einer Hypervisor-Installation automatisch an

Unter Opensuse unterstützt einen YaST nicht nur bei der Einrichtung von Netzwerk-Devices sondern auch beim grundlegenden Setup eines KVM- oder XEN-Hostes. Im Zuge eines solchen Setups (YaST2 >> "Install Hypervisor and Tools") legt YaST automatisch für jedes vorhandene und konfigurierte physikalische Device des Hostes - z.B. enp8s0 - eine durch Gäste "direkt" nutzbare Bridge - z.B. br0 - an. Damit auch der Host über die Bridge kommunizieren kann, wird der physikalischen Schnittstelle (hier enp8s0) die IP-Adresse entzogen und dem erzeugten Bridge-Device (z.B. br0) zugeordnet. Siehe hierzu etwa:
http://libvirt.org/formatdomain.html#elementsNICS
bzw.
http://libvirt.org/formatdomain.html#elementsNICSBridge

Wozu ist eine direkte LAN-Bridge eines KVM-Hosts eigentlich gut?

Es gibt verschiedene Möglichkeiten KVM- (oder auch XEN-) Gastsysteme mit einer physikalischen LAN-Umgebung des Hosts zu verbinden. Eine "Direct Bridge To Lan" bietet einen bequemen, wenn auch nicht den sichersten Weg, einzelne Gäste in das LAN zu integrieren. Dabei teilen ggf. mehrere KVM Gastsysteme und der Host ein physikalisches Netzwerk-Device zur direkten Paketübermittlung in das physikalische LAN - und darüber wiederum ggf. ins Internet. Derartige direkte Bridges werden typischerweise mit einer Bezeichnung der Form "brN" (N steht für eine Nummer) versehen.

Wir leisten uns zum besseren Verständnis einen kurzen Exkurs: Die nachfolgende Skizze zeigt schematisch zwei unterschiedliche Arten der Anbindung von KVM-Gästen an physikalische Netze.

kvm_br0_2

Das Bild dient nur einer grundsätzlichen Veranschaulichung und ist nicht in jedem Detail ernstzunehmen. Wie der Ethernet-Protokollstapel für verschiedene virtuelle Devices intern tatsächlich verarbeitet wird, sei mal dahingestellt.

Direct Bridge to LAN:
Im unteren Bereich erkennt man den Einsatz eines physikalischen Ethernet-Devices "enp8s0" im Rahmen einer "Direct Bridge to LAN".

Die Bridge ist zwar ein virtuelles Konstrukt (mit Kernel-Unterstützung); im Sinne der Zeichnung kann man sich aber vorstellen, dass sie wie ein realer Hub/Switch mit diversen Ports versehen ist, an die wiederum physikalische und virtuelle Netzwerkdevices angebunden werden. Ich habe die Ports mit den entsprechenden Bezeichnungen der angebundenen Devices versehen.

Die Bridge übernimmt dabei eine Doppel-Rolle - einerseits entspricht sie einem host-bezogenen Ethernet-Netzwerk-Device mit einer IP-Adresse und garantiert eine entsprechende Paketverarbeitung im Kernel; andererseits subsummiert sie die gesamte Logik zur Ethernet-Paketelimination bzw. -Paket-Verteilung zwischen den virtuellen Ports. Die Bridge behandelt Pakete normalerweise und im Gegensatz zur physikalischen NIC dabei nicht im "promiscuous mode", sondern verwirft Pakete, die angeschlossenen Devices nicht zugeordnet werden können. (Für bestimmte Zwecke kann man eine Linux-Bridge aber auch in einen "promiscuous mode" versetzen.)

Die Bridge ist bei bestimmten Einstellungen (sog. ageing-Parameter; s. die "man"-Seiten für das Kommando "brctl") ferner in der Lage, wie ein Switch zu fungieren und Pakete gezielt und ausschließlich zu den richtigen Target-Ports weiterzuleiten. Das ist der Standard; die Bridge kann aber auch in einen HUB-Modus versetzt werden. Wegen einer weitgehenden Gästeisolation ist das aber selten wirklich wünschenswert.

Das physikalische Interface (enp8s0) wird gezielt mit der Bridge assoziiert (s.u.) und übernimmt in etwa die Funktionen eines physikalischen Uplinks. Die physikalische NIC wird bei der Bridgezuordnung automatisch in den sog. "promiscuous mode" geschaltet, da sie ja Pakete für verschiedene IP- bzw. MAC-Adressen in Empfang nehmen muss.

Die beiden dargestellten KVM-Gastsysteme nutzen die Kapazitäten der physikalischen NIC über die Bridge. Hierzu werden weitere virtuelle Netzwerk-Interfaces (tun/tap-Devices) "vnet0" und "vnet2" verwendet, die in die Konfiguration des Gastsystems aufgenommen und natürlich auch der Bridge zugeordnet werden.

Der Host selbst erhält im Beispiel über die Adresse 192.168.2.1, die br0 zugeordnet wurde, Kontakt zum umliegenden physikalischen LAN. Die Gastsysteme teilen sich die physikalischen Ressource über die Adressen 192.168.2.5 bzw. 192.168.2.6. Sie erscheinen mit diesen Adressen direkt im (physikalischen) LAN und sind (je nach Firewall-Einstellungen) von dort aus auch ansprechbar. Dem physikalischen Interface (enp8s0) selbst wird in dieser Konfiguration keine IP zugeordnet.

Alternative - Host-Only-Bridge und Routing:
Im oberen Bereich der Abbildung ist dagegen eine weitere virtuelle Bridge "virbr0" dargestellt ist, die ein reines "Host-Only Netzwerk" realisiert. Die beiden Gäste wie der Host können über (virtuelle) NICs auch mit einer solchen rein virtuellen Bridge "virbr0" verbunden werden. Das der Bridge ggf. zugeordnete virtuelle Host-Device taucht bei einer Standardvorgehensweise mit KVM oder libvirt-Tools (virt-manager !) normalerweise unter der Bezeichnung "virbr0-nic" auf. Diese Bridge-Variante, die im Grunde wie ein virtueller Switch funktioniert, wird jedoch nicht direkt mit einem physikalischen Device verbunden. Sind die Gastsysteme nur an eine solche "virbr0" angebunden, so erfordert eine Verbindung zur Außenwelt ein (ggf. NAT-) Routing auf dem Host zwischen dem virtuellen Interface "virbr0-nic" des Hostes und einer seiner physikalischen NICs. Eine solche - relativ kontrolliert und gut absicherbare - Situation trifft man häufig auf produktiven KVM-Hosts an.

Hinweis: Die Skizze dient nur der Illustration unterschiedlicher Typen virtueller Bridges - und stellt kein praktisch sinnvolles Szenario dar. Würde man die dargestellte - wenig ressourcenschonende - Situation tatsächlich realisieren, müsste man natürlich aufpassen, keine Netzwerk-Loops durch problematisches Routing zu generieren. Auch die Namensgebung pro Host und Netzwerk sowie die DHCP-Dienste für die beiden Netze müssten strikt getrennt werden. Ein Default Gateway sollte dann nur für eine Netzwerkverbindung definiert sein, hier für das physikalische LAN.

Ich mache ausdrücklich darauf aufmerksam, dass ein direktes Bridging zwar eine einfache Möglichkeit darstellt, Gastsysteme an die physikalische Umwelt anzubinden. Das Ganze ist aus Sicherheitsgründen aber durchaus problematisch - zu denken ist vor allem an ARP- und auch IP-Spoofing durch die Gastsystemnutzer. Der Host muss deshalb mit einer Kombination von iptables- und ebtables-Regeln ausgestattet werden, um die direkte Bridge abzusichern. Auch und gerade, wenn der Host noch Routing-Aufgaben übernimmt. In produktiven Umgebungen ist eher auf Host-Only Konfigurationen mit/ohne NAT zu setzen.

Auf Test- und Übungssystemen - insbesondere lokalen Pentest-Umgebungen, bei denen Zielsysteme für Übungen im gleichen oder unterschiedlichen virtuellen Segmenten untergebracht werden - vereinfacht eine temporäre Nutzung von gebridgten Host-Devices aber durchaus die regelmäßig erforderliche Aktualisierung von bestimmten Gastsystemen - man sollte nur nicht vergessen, diese Devices unter internen Penetrationstests in seinen virtuellen Netzen abzuklemmen. Für interne Pentest-Übungen zwischen Gästen eines Virtualisierungshosts wird man dagegen Host-Only Bridges - ohne (!) aktiviertes Routing auf dem Host - nutzen, die gegenüber der Umwelt isoliert sind. Aber auch wenn Penetrationstests nach außen gerichtet werden, kann die Nutzung virtuelle Maschinen hinter einer gebridgten NIC sinnvoll sein.

Manuelles Anlegen einer direkten br0-Bridge zu einem physikalischen Device

Eine durchaus interessante Frage ist, ob und wie man unter Opensuse/SLES eine solche Bridge auch manuell anlegen kann - z.B. wenn man sich (möglicherweise über YaST) die Bridge-Konfiguration zerschossen hat. (Z.B. dadurch, dass man in einem Anflug von Unkonzentriertheit der Ethernet-Schnittstelle über YaST irgendwann wieder direkt eine IP-Adresse zugeordnet hat).

Voraussetzung - ein aktivierbares Netzwerk-Interface

Ich möchte in diesem Artikel nicht auf die manuelle Anlage eines physikalischen Ethernet-Netzwerk-Devices selbst eingehen. Hierzu müsste man für moderne Linux-Systeme den aktuellen "udev"- und "systemd"-Mechanismus, das automatische Erkennen und Identifizieren von Devices gem. entspr. Datenbanken, die Bereitstellung von Gerätedateien unter "/dev", das (automatische) Laden zugehöriger Treiber und auch die Vergabe von "predictable" Device-Namen besprechen (und verstehen). Hier zieht man sich besser auf YaST oder die suse-spezifischen Konfigurationsdateien unter "/etc/sysconfig/network" zurück, in denen bestimmte Parameter wie die IP-Adresse für ein Device permanent hinterlegt werden.

SuSE-spezifische Informationen zur Konfiguration findet man hier:
https://www.suse.com/de-de/documentation/sled11/book_sle_admin/data/sec_basicnet_manconf.html
und hier
https://www.suse.com/documentation/sles-12/book_sle_admin/data/sec_basicnet_manconf.html
Die letzte Doku passt auch gut zu Opensuse 13.2.

Übrigens: Wer verstehen will, wie udev/systemd zu den "predictable names" für Devices gelangen, der lese folgenden Artikel

Understanding systemd’s predictable network device names

und meine Zusammenfassung unter
Linux-Namensgebung für physikalische Netzwerk-Devices

Ich gehe davon aus, dass es auf unserem KVM-Host z.B. bereits ein aktiviertes Ethernet-Device - in unserem Beispiel "enp8s0" - gibt. Soll es von KVM-Gästen über eine direkte virtuelle Bridge zum LAN genutzt werden, darf ihm beim Systemstart keine IP-Adresse zugeordnet werden. Dies kann man einerseits über "YaST2 >> Netzwerkeinstellungen" für diese NIC einstellen. Alternativ kann man manuell das opensuse-spezifische File "/etc/sysconfig/network/ifcfg-enp8s0" anlegen und mit leeren Konfigurationsanweisungen editieren:

mytux:/etc/sysconfig/network # cat ifcfg-enp8s0 
BOOTPROTO='none'
BROADCAST=''
ETHTOOL_OPTIONS=''
IPADDR=''
MTU=''
NAME=''
NETMASK=''
NETWORK=''
REMOTE_IPADDR=''
STARTMODE='auto'
DHCLIENT_SET_DEFAULT_ROUTE='yes'
PREFIXLEN=''
mytux:/etc/sysconfig/network # 

Einrichtung einer Bridge mittels des Kommandos brctl

Opensuse 13.2 nutzt im Gegensatz zu früheren Opensuse-Versionen "wicked" zum Starten von NICs etc.. Durch den "compat"-Modus von "wicked" werden aber immer noch die klassischen "ifcfg-..."-Dateien, die unter "/etc/sysconfig/network" definiert sind, unterstützt und ausgelesen.

Leider ist die zugehörige Factory-Doku von SuSE zu diesem Themenkomplex noch nicht ausgereift. Es lohnt sich daher, zunächst ein Blick in die SLES12-Doku zu werfen. Siehe hierzu:
https://activedoc.opensuse.org/book/opensuse-reference/chapter-13-basic-networking
https://www.suse.com/de-de/documentation/sles-12/book_sle_admin/data/sec_basicnet_manconf.html
Siehe ergänzend zu den bereits erwähnten Quellen auch :
https://activedoc.opensuse.org/book/opensuse-virtualization-with-kvm/chapter-12-running-virtual-machines-with-qemu-kvm

Man kann eine Standard-Bridge "br0" unter Opensuse natürlich mit Hilfe von YaST2 einrichten.
Es lohnt sich aber, das mal manuell zu machen, um entsprechende Befehle und suse-spezifische Konfigurationsdateien kennenzulernen. Der entscheidende Befehl hierzu ist "brctl", zu dem man sich unbedingt mal die man-pages durchlesen sollte.

Man beachte auch: Das Tool "Network-Manager" unterstützt bislang meines Wissens keine Bridge-Strukturen - im besonderen nicht "direkte" Linux-Bridges zu physikalischen NIC_Devices.

Das nachfolgende Beispiel zeigt die Anlage einer "direkten" Host-Bridge über "brctl addbr" und die Zuordnung zu einem physikalischen Ethernet Device (für direktes Bridging) mittels "brctl addif". [Hierbei wurde vorausgesetzt, dass dem physikalischen Device bislang keine IP-Adresse zugeordnet wurde.] Die Aktivierung des erzeugten Bridge-Devices erfolgt ganz konventionell über "ifconfig br0 up".

Der Bridge selbst wird dann mittels "ifconfig" sehr eine IP-Adresse zugeordnet. Dies ist dann die Adresse unter der der (KVM-) Linux-Host selbst nach außen kommunizieren kann. Natürlich hätte man das alles statt mit "ifconfig" auch mit dem Kommando "ip" und seinen Optionen erledigen können. Ich überlasse diese Übung dem Leser.

mytux:~ # ifconfig enp8s0 down 
mytux:~ # ifconfig enp8s0 up 
mytux:~ # brctl addbr br0
mytux:~ # ifconfig br0 up 
mytux:~ # brctl addif br0 enp8s0
mytux:~ # brctl show
mytux:~ # ifconfig br0 192.168.2.1 netmask 255.255.255.0 broadcast 192.168.2.255
mytux:~ # ifconfig br0 

Um diese erreichte Konfiguration permanent machen, muss man noch ein entsprechendes ifcfg-File unter "/etc/sysconfig/network" mit folgendem beispielhaften Inhalt anlegen:

mytux:/etc/sysconfig/network # cat ifcfg-br0
BOOTPROTO='static'
BRIDGE='yes'
BRIDGE_FORWARDDELAY='0'
BRIDGE_PORTS='enp8s0'
BRIDGE_STP='off'
BROADCAST=''
DHCLIENT_SET_DEFAULT_ROUTE='yes'
ETHTOOL_OPTIONS=''
IPADDR='192.168.2.1/24'
MTU=''
NETWORK=''
PREFIXLEN='24'
REMOTE_IPADDR=''
STARTMODE='auto'
NAME=''
mytux:/etc/sysconfig/network #

 
Nicht vergessen sollte man die Definition eines Default-Gateways für Routing nach außen - im Beispiel etwa über einen Host "192.168.2.10". Das erfordert eine weitere Textdatei namens "ifroute-br0" unter "/etc/sysconfig/network". Der Inhalt besteht nur aus einer Zeile "default 192.168.2.10 - br0":

mytux:/etc/sysconfig/network # cat ifroute-br0 
default 192.168.2.10 - br0 
mytux:/etc/sysconfig/network # 

 
Das ganze Vorgehen lässt sich natürlich für die Anlage weiterer direkter Bridge-Devices zu anderen noch vorhandenen physikalischen Erhernet-Devices wiederholen.

Über Tools zur Einrichtung von KVM-Gastsystemen (wie virt-manager) kann man nun virtuelle NICs der Gastsysteme anlegen und der erzeugten Bridge zuordnen. Ich gehe darauf in einem weiteren kommenden Artikel ein.

Steht die Bridge erst einmal, so kann man ihren Status und aktive, zugeordnete Devices über das Kommando "brctl show br0" ansehen:

mytux # brctl show br0
bridge name     bridge id               STP enabled     interfaces
br0             8000.1c6f653dfd1e       no              enp8s0
                                                        vnet0
                                                        vnet2

 
Der Befehl "wicked show all" zeigt übrigens auch die NIC-Zuordnung zu Bridges und zudem die IP-Adressen an:

mytux: # wicked show all           
lo              up
      link:     #1, state up
      type:     loopback
      config:   compat:/etc/sysconfig/network/ifcfg-lo
      leases:   ipv4 static granted
      leases:   ipv6 static granted
      addr:     ipv4 127.0.0.1/8 [static]
      addr:     ipv6 ::1/128 [static]

enp8s0          enslaved
      link:     #2, state up, mtu 1500, master br0
      type:     ethernet, hwaddr 1d:ff:76:5c:cd:4e
      config:   compat:/etc/sysconfig/network/ifcfg-enp8s0

br0             up
      link:     #4, state up, mtu 1500 
      type:     bridge               
      config:   compat:/etc/sysconfig/network/ifcfg-br0
      leases:   ipv4 static granted    
      addr:     ipv4 192.168.2.1/24 [static]
      route:    ipv4 default via 192.168.2.10

vnet0           device-unconfigured
      link:     #12, state up, mtu 1500, master br0
      type:     tap, hwaddr fe:54:00:cc:dd:ee

vnet1           device-unconfigured
      link:     #13, state up, mtu 1500, master br0
      type:     tap, hwaddr dd:54:00:dd:ee:ff

vnet2           device-unconfigured
      link:     #14, state up, mtu 1500, master br0
      type:     tap, hwaddr fe:54:00:22:33:44

 
Viel Spaß beim Experimentieren mit virtuellen Bridges!