KVM IPv6 forwarding?
Weiterleitung von IPv6 Datenverkehr an einen KVM/libvirt Gast
Datum: 2020-06, Tags: KVM, libvirt, IPv6, iptables, forwarding, routingKVM oder Kernel Virtual Machine ist eine Server-Virtualisierung. Ich benutze KVM Gastsysteme, um Dienst oder Services von meinen eigentlichen Servern zu entkoppeln. Das macht es mir leichter, einzelne Komponenten zu aktualisieren oder auszutauschen.
Das Beispiel hier soll die Auslagerung eines Webservers in eine virtuelle Maschine sein. Ich habe ein Setup mit anonymisierten IP-Adressen wie folgt:
- Ein öffentlicher Linux Server (Host, Debian) mit der IP-Adresse
111.0.0.123
- Unser Provider hat uns eine öffentliche IPv6-Adresse zugeteilt:
2001:fff:1057:c0de::/64
- Ein vorhandenes KVM Setup mit einem Gast (Guest, Debian).
- Der Guest ist über eine Netzwerk-Brücke (bridge) mit DHCP angebunden und hat die IP-Adresse
192.168.1.11
- Der Host hat auf der Netzwerkbrücke die IP-Adresse
192.168.1.1
- Der http- und https Datenverkehr über die Ports
80
und443
wird vom Host an den Guest weitergeleitet.
Die Weiterleitung der Ports 80 und 443 erfolgt auf dem Host über eine iptables Regelsatz:
host:~iptables-save | grep 192.168 -A FORWARD -d 192.168.1.11/32 -p tcp -m tcp -m multiport --dports 80,443 -j ACCEPT -A PREROUTING -d 111.0.0.123/32 -p tcp -m tcp -m multiport --dports 80,443 -j DNAT --to-destination 192.168.1.11 -A POSTROUTING -s 192.168.1.0/24 -o enp0s31f6 -j MASQUERADE
Auf dem Guest läuft ein nginx Dienst, der die Requests für http und https entgegennimmt:
server { server_name _; listen 80 default_server; listen [::]:80 default_server; listen 443 ssl default_server; listen [::]:443 ssl default_server; ssl_certificate ... ssl_certificate_key ... root ... ... }
Die Konfiguration ist nicht nut für IPv4 vorbereitet, sondern müsste auch für IPv6 gültig sein. Das wollen wir auch nutzen, denn IPv6 ist darauf ausgelegt, IPv4 komplett abzulösen und die freien IP-Adressbereiche sind schon lange knapp geworden.
IPv4
IP-Adressen Version 4 bestehen aus einer 32-Bit Zahl. Üblicherweise wird diese Zahl als vier einzelne "Oktette" als Dezimalzahlen dargestellt, wobei ein Oktett immer 8 Bit bzw. ein Byte bzw. eine Zahl zwischen 0 und 255 darstellt.
Beispiel:
IPv4-Adresse dezimal: 111.0.0.123
IPv4-Adresse binär: 01101111 00000000 00000000 01111011
Damit lassen sich 2^32 Adressen erstellen, das sind ca. 4,3 Milliarden Adressen. Davon sind allerdings gut 10% nicht nutzbar.
Die Subnetzmaske bestimmt das der Adresse zugehörige Netzwerksegment. In dem Netzwerksegment haben alle binären Adressen den gleichen Anfang, entsprechend der angegebenen Länge von links.
Beispiel:
IP-Adresse: 111.0.0.123/24
Die ersten 24 Bit der Adresse sind gleich.
Erste Adresse des Segmentes: 01101111 00000000 00000000 00000000
oder 111.0.0.0
Letzte Adresse des Segmentes: 01101111 00000000 00000000 11111111
oder 1111.0.0.255
Die Subnetzmaske wird bei IPv4 auch als Oktett-Kombination geschrieben. Binär dargestellt ergibt die Anzahl der Einsen von links dann wieder die Länge der Subnetzmaske in der Kurz-Notation:
Subnetzmaske: 255.255.255.0
Binär: 11111111 11111111 11111111 00000000
Kurz-Notation: /24
IPv6
In der Version 6 besteht eine Adresse aus 128 Bit. Davon bilden die ersten 64 Bit den "Präfix", die hinteren 64 Bit den "Interface Identifier". Üblicherweise wird eine IPv6 Adresse als acht Hexadezimalblöcke notiert, die jeweils von einem Doppelpunkt getrennt sind. Führende Nullen können pro Block weggelassen werden. Blöcke, die lediglich '0000' enthalten, können komplett weggelassen werden, was mit einem doppelten Doppelpunkt abgekürzt wird. Diese Abkürzung darf jedoch nur einmal verwendet werden.
Die Subnetzmasken funktionieren analog zu IPv4.
Beispiel:
IPv6-Adresse abgekürzt: 2001:fff:1057:c0de::2
IPv6-Adresse ausgeschrieben: 2001:0fff:1057:c0de:0000:0000:0000:0002
Damit lassen sich 2^64 Adressen darstellen, das sind etwa 340 Sextillionen (3,4·10^38) Adressen. Das ist die Vierer-Potenz der möglichen Adressen von IPv4. Die Subnetzmaske oder Präfix funktioniert analog zu IPv4. Vorgeschrieben ist, dass die Zuweisung eines IPv6 Adressraums mindestens einen 64-er Präfix hat, das bedeutet, dass man die letzten vier Hexadezimalblöcke für die eigene Verwendung hat, das sind imemrhin mehr als 18 Trillionen (1,8·10^19) Adressen!
Eine URL mit IPv6 Adresse wird übrigens mit eckigen Klammern um die IP-Adresse notiert:
http://[2001:fff:1057:c0de::2]:80/
. Der Adressbereich fc00::/7
ist analog zu IPv4
ein privater Adressbereich, der "Unique Local Unicast" bezeichnet wird. fe80::/64
sind "Link Local"
Adressen, die automatisch für das Routing in einem Netzwerksegment angelegt werden.
Routing
Die IPv6 Adresse, die wir von unserem Provider zugeteilt bekommen haben, möchten wir nun an den Guest mit dem webserver weiterreichen. Als Vorlage könnten wir das Port-Forwarding benutzen, dass wir bereits für IPv4 konfiguriert haben. Doch Achtung: so ist das ein Denkfehler: Wir haben nicht eine IPv6 Adresse von unserem Provider erhalten, sondern einen IPv6 Adressbereich. Wir können und sollten also kein Port-Forwarding verwenden, sondern Routing. Das stellt uns aber noch vor andere Aufgaben:
- Wir müssen aufpassen, dass wir nicht komplett sämtlichen Traffic per IPv6 auf den Guest routen, denn auf der
VM haben wir typischerweise keine Firewall konfiguriert. Trotzdem laufen auf den Guests möglicherweise angreifbare
Dienste, die wir schon am Host schützen müssen. Dazu benutzen wir die IPv6 Version von iptables:
ip6tables
. - Das bridge Interface und das Netzwerk zwischen Host und Guest wird von der Virtualisierung aufgebaut und
verwaltet. Wir müssen mit der Konfiguration hier ansetzen und möglichst den Betrieb der Server
nicht beeinträchtigen. Dazu benutzen wir das tool
virsh
aus den libvirt-Paketen, je nach Distribution ist das Paket unterschiedlich benannt.
Um das Folgende nachzustellen, benutze bitte echte IP-Adressen, nicht die von mir hier angegebenen, die sind wie gesagt ausgedacht.
Unser Provider hat uns den IP-Adressbereich 2001:fff:1057:c0de::/64
zugeteilt und routet den auf das
primäre Netzwerk-Interface enp0s31f6
unseres Servers.
Um damit arbeiten zu können, versehen wir unser Interface mit einer Adresse aus dem Bereich. Die ::0
ist nicht nutzbar, die ::1
lassen wir frei und benutzen die ::2
. Die
Netzwerkeinstellungen unseres Hosts enthalten dann:
... auto enp0s31f6 iface enp0s31f6 inet static address 111.0.0.123 netmask 255.255.255.192 gateway 111.0.0.65 up route add -host 111.0.0.65 dev enp0s31f6 up route add -net 111.0.0.64 netmask 255.255.255.192 gw 111.0.0.65 dev enp0s31f6 iface enp0s31f6 inet6 static address 2001:fff:1057:c0de::2 netmask 64 gateway fe80::1
Damit ist das Netzwerk auf unserem Host nun wie folgt eingestellt:
host:~ip a 1: lo:mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: enp0s31f6: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 90:1b:0e:fc:ec:cf brd ff:ff:ff:ff:ff:ff inet 111.0.0.123/26 brd 111.0.0.127 scope global enp0s31f6 valid_lft forever preferred_lft forever inet6 2001:fff:1057:c0de::2/64 scope global valid_lft forever preferred_lft forever inet6 fe80::921b:eff:fefc:eccf/64 scope link valid_lft forever preferred_lft forever 23: virbr0: mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 52:54:00:30:b9:06 brd ff:ff:ff:ff:ff:ff inet 192.168.1.1/24 brd 192.168.1.255 scope global virbr0 valid_lft forever preferred_lft forever 25: vnet0: mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN group default qlen 1000 link/ether fe:54:00:84:64:f1 brd ff:ff:ff:ff:ff:ff inet6 fe80::fc54:ff:fe84:64f1/64 scope link valid_lft forever preferred_lft forever host:~ip r default via 111.0.0.65 dev enp0s31f6 onlink 111.0.0.64/26 via 111.0.0.65 dev enp0s31f6 111.0.0.64/26 dev enp0s31f6 proto kernel scope link src 111.0.0.123 111.0.0.65 dev enp0s31f6 scope link 192.168.1.0/24 dev virbr0 proto kernel scope link src 192.168.1.1 host:~ip -6 r 2001:fff:1057:c0de::/64 dev enp0s31f6 proto kernel metric 256 pref medium fe80::/64 dev enp0s31f6 proto kernel metric 256 pref medium fe80::/64 dev vnet0 proto kernel metric 256 pref medium default via fe80::1 dev enp0s31f6 metric 1024 pref medium
Auf unserem primären Netzwerkinterface ist die IP-Adresse mit dem InterfaceIdentifier ::2
eingerichtet.
Das komplette Netz wird auch über eben das Interface geroutet.
Wenn die Einstellungen so noch nicht vorhanden sind, kann die IPv6 Adresse auch "per Hand" hinzugefügt werden. Der Vorteil dabei ist, dass A) den laufenden Betrieb des Serevrs nicht stört, es sei denn, man macht einen groben Fehler, B) man den Server aber einfach neu starten kann, wenn man sich versehentlich aussperrt. Nach erfolgreichem Test muss die Konfiguration dann natürlich auch fixiert werden, und durch einen Neustart des Servers bei nächster Gelegenheit überprüft werden.
host:~ip -6 address add 2001:fff:1057:c0de::2/64 dev enp0s31f6
Der Host sollte nun per IPv6 erreichbar sein. Aber Achtung: Gerade bei den DSL-Verbindungen, die man zu Hause hat, ist möglicherweise
IPv6 noch nicht freigeschaltet, so dass es scheint, als wäre der Host noch nicht erreichbar.
Die Verfügbarkeit von IPv6 für die aktuelle Internetanbindung kann z.B. über
https://www.wieistmeineip.de/ geprüft werden, dort steht
im oberen Bereich die aktuelle IPv4- und IPv6-Adresse.
Es gibt jedoch im Netz
jede Menge Online-Tools, die einen IPv6 Ping ausführen und die Erreichbarkeit des Hosts testen können.
somewhere:~ping6 2001:fff:1057:c0de::2 PING 2001:fff:1057:c0de::2(2001:fff:1057:c0de::2) 56 data bytes 64 bytes from 2001:fff:1057:c0de::2: icmp_seq=1 ttl=54 time=29.7 ms 64 bytes from 2001:fff:1057:c0de::2: icmp_seq=2 ttl=54 time=27.9 ms 64 bytes from 2001:fff:1057:c0de::2: icmp_seq=3 ttl=54 time=23.3 ms ^C --- 2001:fff:1057:c0de::2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 23.338/27.016/29.766/2.705 ms
Um nun die IP-Adresse an dem Guest nutzbar zu machen, benötigen wir:
- Ein Subnetz des IPv6 Netzes, dass über das Interface
virbr0
geroutet wird. - Generell muss das Routing von IPv6 Traffic auf dem Host eingeschaltet werden.
- Der Guest braucht eine IPv6 Adresse aus dem Subnetz und passende Routen.
IPv6 Subnetz
Für das IPv6 Subnetz benötigen wir einen Teil des Netzes, aus dem wir dann eine IP-Adresse dem Host zuweisen und
eine andere dem Guest. Ich möchte hier den Bereich 2001:fff:1057:c0de:0:1::/96
nutzen, siehe auch
internex IPv6 Subnet Calculator.
Unser Setup soll dann am Ende wie folgt aussehen:
Der Host muss erst einmal grundlegend als IPv6 Router eingestellt werden. Das geschieht über die Kernel-Parameter
in dem Verzeichnis /proc/sys/net/ipv6/conf
bzw. über das utility sysctl
. Überprüfe die
Konfiguration und korrigiere sie ggf., z.B. sysctl -w net.ipv6.conf.all.forwarding=1
host:~sysctl net.ipv6.conf.all.forwarding net.ipv6.conf.all.disable_ipv6 net.ipv6.conf.all.forwarding = 1 net.ipv6.conf.all.disable_ipv6 = 0
Schauen wir uns nun die Konfiguration des virtuellen Netzwerkes an, das heißt wahrscheinlich default
:
host:~virsh net-list --all Name State Autostart Persistent ---------------------------------------------------------- default active yes yes host:~virsh net-dumpxml default <network> <name>default</name> <uuid>f4cbfd80-51e4-4086-b3da-481565f9cff8</uuid> <forward mode='open'/> <bridge name='virbr0' stp='on' delay='0'/> <mac address='52:54:00:30:b9:06'/> <ip address='192.168.1.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.1.100' end='192.168.1.254'/> <host mac='52:54:00:84:64:f1' name='guets-1' ip='192.168.1.11'/> </dhcp> </ip> </network>
Wir weisen also unseren Guests statische IP-Adressen über DHCP zu. Für uns ist das überigens der Prozess
dnsmasq
, der vom libvirtd
gestartet wird.
Wenn wir diese Konfiguration nun ändern, müssen wir das "default" Netz neu starten (destroy und start), wobei aber
leider unser Guest vom Netz getrennt wird und sich wahrscheinlich aufhängt, wenn er nicht vorher geregelt heruntergefahren
wird. Es gibt die Möglichkeit, gewisse Konfigurationen im laufenden Betrieb
nachzuziehen, das sind aber nicht alle und leider nicht die, die wir nun brauchen, siehe
virsh net-update Dokumentation.
Der Guest ist so eingestellt, dass er IP-Adressen für Version 4 und 6 per DHCP bzw. DHCP6 bezieht.
source /etc/network/interfaces.d/* auto lo iface lo inet loopback iface lo inet6 loopback allow-hotplug ens3 iface ens3 inet dhcp iface ens3 inet6 dhcp
Wir editieren das Netz und fügen einen IPv6 Block, sowie einen DHCP6 Bereich hinzu. Wichtig: Editiere nicht die Dateien direkt, sondern
benutze virsh net-edit default
. Die virsh
Konsole wird dir beim Speichern die Konfiguration
prüfen und ggf. Syntax-Fehler bemängeln.
host:~virsh net-edit default <network> <name>default</name> <uuid>f4cbfd80-51e4-4086-b3da-481565f9cff8</uuid> <forward mode='open'/> <bridge name='virbr0' stp='on' delay='0'/> <mac address='52:54:00:30:b9:06'/> <ip address='192.168.1.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.1.100' end='192.168.1.254'/> <host mac='52:54:00:84:64:f1' name='guest-1' ip='192.168.1.11'/> </dhcp> </ip> <ip family='ipv6' address='2001:fff:1057:c0de:0:1:0:2' prefix='96'> <dhcp> <range start='2001:fff:1057:c0de:0:1:0:77' end=2001:fff:1057:c0de:0:1:0:ff'/> </dhcp> </ip> </network>
Nun müssen wir leider das Netz einmal neu starten, denn diese Konfiguration kann nicht dynamisch nachgeladen werden. Doch bevor wir das tun, müssen wir noch einmal unsere Firewall-Konfiguration prüfen, denn wir müssen DHCP6 ermöglichen und generell das Neighbor Discovery ermöglichen. Das "Neighbor Discovery" ist das Pendant zu ARP für IPv4, benutzt jedoch nicht das APR-Protokoll, sondern ICMP6.
An dieser Stelle wird es leider etwas haarig, denn um Fehler in der Konfiguration zu finden, müssen die Logfiles
analysiert werden, sowie ggf, der TCP traffic selbst. Benutze dafür journalctl -f
um das Logging
zu analysieren, tcpdump -i any host [ipv6-address]
um den Traffic zwischen dem Host und dem
Guets zu analysieren.
Die folgende ip6tables
Konfiguration ist nur ein Auszug aus der komplette Konfiguration.
Wichtig daran ist, dass der DHCP6 Traffic auf UDP Port 457 zugelassen wird, sowie einige ICMP-Paket-Typen,
die für die Ermittlung der Nachbarschaft nötig sind. Beachte auch, dass der Status der ICMP Paktete für die
Typen "neighbor-solicitation" und "neighbor-advertisement" INVALID
ist, und diese daher akzeptiert werden müssen, bevor die Regel für INVALID Pakete diese verwirft. Ich habe das
diesem Archlinux Wiki Artikel
entnommen, weiter aber noch keinen Hinweis auf die Ursache dieses Verhaltens gefunden.
„ICMPv6 Neighbor Discovery packets remain untracked, and will always be classified "INVALID" though they are not corrupted or the like. Keep this in mind, and accept them before this rule! iptables -A INPUT -p 41 -j ACCEPT”
... # -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT -A INPUT -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT -A INPUT -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT # Allow some other types in the INPUT chain, but rate limit. -A INPUT -p icmpv6 --icmpv6-type echo-request -m limit --limit 900/min -j ACCEPT -A INPUT -p icmpv6 --icmpv6-type echo-reply -m limit --limit 900/min -j ACCEPT # Allow others ICMPv6 types but only if the hop limit field is 255. -A INPUT -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation"" -m hl --hl-eq 255 -j ACCEPT -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT -A INPUT -p icmpv6 --icmpv6-type redirect -m hl --hl-eq 255 -j ACCEPT # # DHCP6 -A INPUT -i virbr+ -m udp -p udp --dport 547 -j ACCEPT # -A INPUT -m state --state INVALID -j LOG --log-level 6 --log-prefix "IPT-INVAID: " -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT # -A INPUT -m state --state UNTRACKED -j LOG --log-level 6 --log-prefix "IPT-UNTRACKED: " # -A INPUT -j DROP # ... # -A FORWARD -o virbr+ -p tcp -m tcp -m multiport --dports 80,443 -j ACCEPT # ...
Starte das Bridge-Netz neu.
host:~virsh shutdown guest-1 Domain guest-1 is being shutdown host:~virsh net-destroy default Network default destroyed host:~virsh net-start default Network default started host:~virsh start guest-1 Domain guest-1 started
Anschließend können wir uns wieder auf unserem Guest einloggen und finden folgende Konfiguration vor:
guest-1:~ip a 1: lo:mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens3: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:84:64:f1 brd ff:ff:ff:ff:ff:ff inet 192.168.1.11/24 brd 192.168.1.255 scope global dynamic ens3 valid_lft 3487sec preferred_lft 3487sec inet6 2001:fff:1057:c0de:0:1:0:80/128 scope global valid_lft forever preferred_lft forever inet6 fe80::5054:ff:fe84:64f1/64 scope link valid_lft forever preferred_lft forever guest-1:~ip -6 r ::1 dev lo proto kernel metric 256 pref medium 2001:fff:1057:c0de:0:1:0:80 dev ens3 proto kernel metric 256 pref medium fe80::/64 dev ens3 proto kernel metric 256 pref medium guest-1:~ping6 2001:fff:1057:c0de:0:1:0:2 connect: Network is unreachable
Der Guest hat also eine dynaimsche Adresse zugewiesen bekommen, das wolen wir gleich noch einmal auf der Seite DHCP-Server fest ziehen. Und der Guest hat noch keine Route für das IPv6 Netzwerk, bzw. kein Gateway bekommen.
Das können wir nun auf dem Host für das default
Netz einstellen, diesmal können wir auch dynamisch
das Netz aktualisieren, siehe net-update Dokumentation.
Wir brauchen dafür jedoch noch die "Client ID" oder "DUID" des Guests und müssen beachten,
dass wir den rchtigen DHCP Konten verwalten,
den wir haben zwei, einen für IPv4 und einen für IPv6. Auch müssen wir mit den Anführungszeichen aufpassen und im
"inneren XML" nur einfache Hochkommata verwenden.
host:~virsh dominfo guest-1 Id: 1 Name: guest-1 UUID: 7dc6f99a-2e2a-4b2b-a35d-eadde15f948d OS Type: hvm State: running CPU(s): 1 CPU time: 98.7s Max memory: 1048576 KiB Used memory: 1048576 KiB Persistent: yes Autostart: enable Managed save: no Security model: none Security DOI: 0 host:~virsh net-dhcp-leases default Expiry Time MAC address Protocol IP address Hostname Client ID or DUID ------------------------------------------------------------------------------------------------------------------- 2020-06-08 12:37:29 52:54:00:84:64:f1 ipv6 2001:fff:1057:c0de:0:1:0:80/96 - 00:01:00:01:26:3c:34:f5:52:54:00:b0:57:5f 2020-06-08 12:49:42 52:54:00:84:64:f1 ipv4 192.168.1.11/24 guest-1 ff:00:84:64:f1:00:01:00:01:26:3c:34:f5:52:54:00:b0:57:5f host:~virsh net-update default add ip-dhcp-host \ "<host id='00:01:00:01:26:3c:34:f5:52:54:00:b0:57:5f' \ name='guest-1' \ ip='2001:fff:1057:c0de:0:1:0:11' />" \ --live --config --parent-index 1 Updated network default persistent config and live state host:~virsh net-dumpxml default <network> <name>default</name> <uuid>f4cbfd80-51e4-4086-b3da-481565f9cff8</uuid> <forward mode='open'/> <bridge name='virbr0' stp='on' delay='0'/> <mac address='52:54:00:30:b9:06'/> <ip address='192.168.1.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.1.100' end='192.168.1.254'/> <host mac='52:54:00:84:64:f1' name='guest-1' ip='192.168.1.11'/> </dhcp> </ip> <ip family='ipv6' address='2001:fff:1057:c0de:0:1:0:2' prefix='96'> <dhcp> <range start='2001:fff:1057:c0de:0:1:0:77' end='2001:fff:1057:c0de:0:1:0:ff'/> <host id='00:01:00:01:26:3c:34:f5:52:54:00:b0:57:5f' name='guest-1' ip='2001:fff:1057:c0de:0:1:0:11'/> </dhcp> </ip> </network>
Nun braucht der Guest noch einen neuen DHCP lease. Da habe ich vergeblich versucht auf Guest-Seite über dhclient
den lease neu anzufordern, daraufhin habe ich dem Guest virtuell das Netzwerkkabel gezogen:
host:~virsh domiflist guest-1 Interface Type Source Model MAC ------------------------------------------------------- vnet1 network default virtio 52:54:00:84:64:f1 host:~virsh domif-setlink guest-1 vnet1 down Device updated successfully host:~virsh domif-setlink guest-1 vnet1 up Device updated successfully
guest:~ip a 1: lo:mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens3: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:84:64:f1 brd ff:ff:ff:ff:ff:ff inet 192.168.1.11/24 brd 192.168.1.255 scope global dynamic ens3 valid_lft 3539sec preferred_lft 3539sec inet6 2001:fff:1057:c0de:0:1:0:11/128 scope global valid_lft forever preferred_lft forever inet6 2001:fff:1057:c0de:0:1:0:80/128 scope global valid_lft forever preferred_lft forever inet6 fe80::5054:ff:fe84:64f1/64 scope link valid_lft forever preferred_lft forever guest:~ip -6 r ::1 dev lo proto kernel metric 256 pref medium 2001:fff:1057:c0de:0:1:0:11 dev ens3 proto kernel metric 256 pref medium 2001:fff:1057:c0de:0:1:0:80 dev ens3 proto kernel metric 256 pref medium fe80::/64 dev ens3 proto kernel metric 256 pref medium
Nun hat der Guest die gewünschte IPv6-Adresse erhalten. Er hat auch die vorherige, aber diese und die entsprechende Route laufen in kurzer Zeit ab und werden dann automatisch entfernt werden.
Wir stellen fest, dass auch noch etwas Routig fehlt. Das hat mich etwas gewundert und ich habe viele Ansätze ausprobiert.
Doch das war am Ende leider alles vergeblich, das Routing in der VM in Kombination mit DHCP6 ließ sich nicht einzustellen.
Das mag an meiner libvirt
Version liegen, die ist nicht mehr ganz auf dem neuesten Stand.
In der Netzwerk-Definition von libvirt
gibt es einen Abschnitt
über Statische Routen, doch
das trifft für unseren Fall nicht zu, denn das bezieht sich auf die Host-Seite für Subnetze, die in den VMs nicht direkt
vom Host aus erreichbar sind.
Ich bin an dieser Stelle dazu übergegangen, die Adressvergabe per DHCP/DHCP6 beizubehalten,
jedoch die benötigten Routen auf dem Guest selbst in den Netzwerk-Definitionen
zu hinterlegen. Die Datei /etc/network/interfaces
auf dem Guest wird wie folgt angepasst:
source /etc/network/interfaces.d/* auto lo iface lo inet loopback iface lo inet6 loopback allow-hotplug ens3 iface ens3 inet dhcp iface ens3 inet6 dhcp up ip -6 route add 2001:fff:1057:c0de:0:1::/96 dev ens3 up ip -6 route add default via 2001:fff:1057:c0de:0:1:0:2
Wenn wir die Netzwerk-Konfiguration des Guests nun neu laden (z.B. reboot), erhalten wir folgende Konfiguration:
guest:~ip -6 r ::1 dev lo proto kernel metric 256 pref medium 2001:fff:1057:c0de:0:1:0:11 dev ens3 proto kernel metric 256 pref medium 2001:fff:1057:c0de:0:1::/96 dev ens3 metric 1024 pref medium fe80::/64 dev ens3 proto kernel metric 256 pref medium guest:~ping6 2001:fff:1057:c0de::2 64 bytes from 2001:fff:1057:c0de::2: icmp_seq=1 ttl=64 time=0.305 ms 64 bytes from 2001:fff:1057:c0de::2: icmp_seq=2 ttl=64 time=0.321 ms 64 bytes from 2001:fff:1057:c0de::2: icmp_seq=3 ttl=64 time=0.326 ms ^C --- 2001:fff:1057:c0de::2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 40ms rtt min/avg/max/mdev = 0.305/0.317/0.326/0.017 ms guest:~ping6 google.de PING google.de(fra16s14-in-x03.1e100.net (2a00:1450:4001:81a::2003)) 56 data bytes 64 bytes from fra16s14-in-x03.1e100.net (2a00:1450:4001:81a::2003): icmp_seq=1 ttl=56 time=5.18 ms 64 bytes from fra16s14-in-x03.1e100.net (2a00:1450:4001:81a::2003): icmp_seq=2 ttl=56 time=5.32 ms 64 bytes from fra16s14-in-x03.1e100.net (2a00:1450:4001:81a::2003): icmp_seq=3 ttl=56 time=5.36 ms ^C --- google.de ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 6ms rtt min/avg/max/mdev = 5.177/5.287/5.363/0.115 ms
Der Guest ist ebenfalls vom Host und von extern erreichbar (wenn auf dem externen Rechner IPv6 verfügbar ist) und auch der Webserver ist unter der IPv6 URL erreichbar https://[2001:fff:1057:c0de:0:1:0:11]/ :
host:~ping6 2001:fff:1057:c0de:0:1:0:11 PING 2001:fff:1057:c0de:0:1:0:11(2001:fff:1057:c0de:0:1:0:11) 56 data bytes 64 bytes from 2001:fff:1057:c0de:0:1:0:11: icmp_seq=1 ttl=64 time=0.255 ms 64 bytes from 2001:fff:1057:c0de:0:1:0:11: icmp_seq=2 ttl=64 time=0.164 ms 64 bytes from 2001:fff:1057:c0de:0:1:0:11: icmp_seq=3 ttl=64 time=0.430 ms ^C --- 2001:fff:1057:c0de:0:1:0:11 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2037ms rtt min/avg/max/mdev = 0.164/0.283/0.430/0.110 ms
somewhere:~ping6 2001:fff:1057:c0de:0:1:0:11 PING 2001:fff:1057:c0de:0:1:0:11(2001:fff:1057:c0de:0:1:0:11) 56 data bytes 64 bytes from 2001:fff:1057:c0de:0:1:0:11: icmp_seq=1 ttl=53 time=31.2 ms 64 bytes from 2001:fff:1057:c0de:0:1:0:11: icmp_seq=2 ttl=53 time=24.0 ms 64 bytes from 2001:fff:1057:c0de:0:1:0:11: icmp_seq=3 ttl=53 time=28.3 ms ^C --- 2001:fff:1057:c0de:0:1:0:11 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2002ms rtt min/avg/max/mdev = 24.021/27.874/31.208/2.956 ms
Zusammenfassung
IPv6 ist doch deutlich anderes als IPv4. Der ganze Bereich "Neighbor Detection" und "Router Advertising",
kein ARP, kein Proxy-ARP, keine Broadcasts, dafür
deutlich stärkere Nutzung von ICMP Nachrichten. Gerade ICMP6 in Kombination mit der ip6tables Firewall
war für mich ein großes Hindernis. Dass ein ICMP6 Paket den Status INVALID haben kann, erscheint mir wie ein
mitgewachsener Fehler.
Es würde mich nicht wundern, wenn viele Admins einfach ALLE ICMP6 Pakete erlauben.
Das ist auch so ein Punkt: kaum ist IPv6 für meinen Webhost aktiv, wird er schon reichlich "ausgespäht".
Dass das Routing auf Seiten der VMs nicht so funktioniert wie gedacht ist zwar schade, aber es fragt sich
auch, ob man in dieser Konstellation überhaupt mit DHCP/DHCP6 arbeiten sollte, oder nicht besser eh' alles
statisch konfiguriert.
Schickt Kommentare oder Anmerkungen bitte über Twitter an @limitland oder @jlue.