Bei ICANN kocht - wieder einmal - die Diskussion über Whois hoch. Gerade mit den Anforderungen der DSGVO in der EU ist ICANN in massive Probleme gelaufen. Doch es gibt vermutlich eine Lösung: Ultra-Thin-Whois.

Worum geht es eigentlich?

75462257_239566563678051_7023322528932167680_o

Whois ist der Dienst, der darüber Auskunft gibt, wer welche Ressource des Internets gebucht hat. Üblicherweise sind das IP-Adressen oder Domainnamen.

IP-Adressen braucht man, um überhaupt am Internet teilzunehmen. Der Rechner oder das Handy bekommt während der Anmeldung im Netz eine Adresse zugewiesen, mit der es Datenpakete aus aller Welt empfangen kann. Die IP-Adresse ist also so eine Art Telefonnummer des Rechners im Netz. Wenn man mit einem Server redet, schickt man an dessen Adresse ein Paket mit der Anfrage und seiner eigenen Adresse als Absender. Der Server antwortet an die angegeben Adresse.

Natürlich kann man nicht alle Adressen aller Dienste im Netz auswendig kennen. Domain-Namen sind dafür da, den Diensten unter einem leicht merkbaren Namen in einen Eintrag in einer Art Telefonbuch zu geben. Das Telefonbuch ist heißt DNS und liefert zu einem Namen eine IP-Adresse zurück. Damit kann man den Server des Dienstes dann auch ansprechen.

Damit es keine Dopplungen gibt, die Adressen und Namen eindeutig bleiben, bedarf es eine Koordinierung, eine Art Internet Governance. ICANN ist die Organisation, die die Regeln und Verträge festlegt, nach denen die Namensräume verwaltet werden.

Sämtliche Ressourcen werden initial durch die IANA verwaltet, gemäß der ICANN-Verträge bekommen verschiedene Registries einen ganzen Block oder Teile des Namensraums zur Verwaltung zugewiesen. Wer eine Ressource benötigt, wendet sich über einen Registrar, eine Art Wiederverkäufer, an die Registry um die Ressource auf sich selbst eintragen zu lassen. Nach der Eintragung kann der Registrant dann die Ressource benutzen.

Das Whois-Dilemma

In der Praxis kommt es vor, dass man wissen will, wer gerade für ein bestimmten Dienst verantwortlich ist. Sei es, weil etwas nicht funktioniert (E-Mail kommt nicht an) und man bei der Fehlersuche Hilfe von der anderen Seite benötigt. Sei es, weil es ein rechtliches Problem (mit den Inhalten einer Webseite) hat. Sei es, weil krimineller Missbrauch (Spam, DoS) mit der Ressource betrieben wird.

Vor allem für die Behebung der technischen Fehler wurde in der Frühzeit des Internets der Whois-Dienst erfunden. Man fragt einen speziellen Dienst, wer für die Ressource zuständig ist und bekommt Namen, Telefonnummer und E-Mail eines kompetenten Ansprechpartners zurück. Das war auch ziemlich einfach, weil es nur wenige Stellen gab, die die Ressourcen direkt zuteilten. All die Dinge wie Registry und Registrar existierten nicht als eigenständige Institutionen. Wenn man alles aus einer Hand betreibt, ist die Auskunft natürlich sehr leicht.

Aber wie baut man so einen Dienst, wenn der Zuteilungsprozess sich über viele Ebenen verteilt? Der immer noch bevorzugte Ansatz ist, die Daten zentral zu sammeln und so einen Thick-Whois-Dienst zentral zu betreiben. Das hat einige Vorteile:

  • Alle Anfragen können an die gleiche Stelle gestellt werden. Der Zugriff ist unmittelbar.
  • Man kann Massenabfragen machen, z.B. welche Ressourcen hat der Registrant noch alles noch?
  • Man kann sich bei der Absicherung der Daten gegen Angriffe auf eine Stelle beschränken und so effektiver schützen.

Mit der Zeit hat sich aber auch das Verständnis von Datenschutz verändert. Inzwischen sind sehr viele Personen mit dem Internet befasst und nutzen Ressourcen, die eigentlich keine Techniker sind. Sollen deren Daten auch im Whois sichtbar sein? Es gibt einige Fälle, bei denen die Ermittelung des Registranten sinnvoll ist, z.B. bei kriminellen Aktivitäten. Man kann aber nicht alle Nutzer unter diesen Anfangsverdacht stellen und proaktiv deren Daten veröffentlichen.

Richtig hoch gekocht ist es mit der Einführung des Europäischen Datenschutzes DSGVO. Jetzt ist selbst ICANN gefordert, zumindest für europäische Partner die dortigen Gesetze zu befolgen. Aber auch andere Länder bekommen eigenständige Datenschutzreglungen, die sich zum Teil erheblich von den Europäischen Reglungen unterscheiden.

Wie soll ICANN all diese Anforderungen gleichzeitig erfüllen?

Beispiel:

  • Ein Nutzer in Indien meldet bei einem indischen Registrar eine Domain an und betreibt damit eine Webseite.
  • Es gibt eine Anzeige in Pakistan gegen Inhalte auf der Seite.
  • Die pakistanischen Polizei will den Eigner der Domain ermitteln und befragt den Whois-Server nach den indischen Daten.
  • Nach welchem Recht soll der Dienst die Antwort zurück liefern? Die aktuelle Diskussion fordert von ICANN die Erfüllung des europäischen Rechts, um danach die Anfrage zu antworten.

Ein Lösungsvorschlag

Akzeptiert man die Tatsache, dass es in der Welt verschiedene Rechtssysteme gibt, und auch die hierarchische Struktur der Ressourcenvergabe, so gibt es nicht mehr viele Möglichkeiten das Problem zu lösen.

Der gesunde Menschenverstand empfielt, den Whois-Dienst komplett einzustellen. Man kann es damit begründen, dass er sich überlebt hat. Der Protest seitens der Strafverfolgungsbehörden, der Rechteindustrie und der Sicherheits-Community ist sicher.

Die offensichtliche, technische Lösung besteht daran, ein weltweit einheitliches Rechtssystem anzustreben. Das klingt seltsamer als es ist. Der Vorschlag eines Internet-Staates ist schon älter. Aber auch hier gibt es offenkundigen Protest.

Betrachtet man das Problem genauer, so schält sich die zentrale Datenspeicherung als eigentliches Problem heraus. Diese zentrale Datenbank erfordert, die Daten der Registranten aus deren Rechtssystemen heraus zu transportieren, um sie an anderer Stelle unter anderen rechtlichen Rahmenbedingen bereit zu stellen. Daraus ergibt sich auf natürliche Weise der folgende Vorschlag:

  • Der Whois-Dienst wird ebenfalls hierarchisch strukturiert.
  • Von jedem Beteiligten in der Vergabe-Hierarchie wird ein eigener Whois-Dienst betrieben.
  • Bei der Abfrage eines Whois-Dienst, bekommt man einen Verweis auf den Vertrag, über den die Registrierung erfolgte und den nächsten Whois-Server.
  • Folgt man der Kette, so fragt man schrittweise die Vertragsdaten ab, wobei jede Anfrage nach dem lokalen Recht des jeweiligen Betreibers beantwortet wird.

Bricht man die Kette nach den ersten Schritten ab, so nennt man das Thin-Whois. Jeder Beteiligter kann sich entscheiden, ob er den Whois-Dienst selbst betreibt oder der Registry übergibt. Dieses Modell gab es schon mal und wurde in dieser unpräzisen Umsetzung nicht weiter verfolgt.

Verpflichtet man jedoch alle Beteiligten vertraglich, die Whois-Dienste anzubieten und dort die Verträge offen zu legen, so bekommt man Ultra-Thin-Whois. Der neue Name soll klar die vertragliche Grundlage bis hin zum Registrar (und der Resellerkette) benennen.

Wie soll das funktionieren?

Es funktioniert bereits. Die zentrale Vergabestelle ist - wir erinnern uns - IANA. Also fragen wir doch mal deren Whois-Server.

$ whois -h whois.iana.org icann.org
refer:        whois.pir.org

domain:       ORG

organisation: Public Interest Registry (PIR)
address:      1775 Wiehle Avenue
address:      Suite 102A
address:      Reston Virginia 20190
address:      United States

Die Ausgabe geht noch deutlich weiter und enthält die Ansprechpartner dieses Vertragspartners.

Im Kern sagt IANA damit, dass der Teil des Namensraums "ORG" an die Registry delegiert wurde. Man soll dort nachfragen. Also tun wir das:

$ whois -h whois.pir.org icann.org
Domain Name: ICANN.ORG
Registry Domain ID: D2347548-LROR
Registrar WHOIS Server: whois.godaddy.com
Registrar URL: http://www.whois.godaddy.com
Updated Date: 2017-12-08T16:40:01Z
Creation Date: 1998-09-14T04:00:00Z
Registry Expiry Date: 2027-12-07T17:04:26Z
Registrar Registration Expiration Date:
Registrar: GoDaddy.com, LLC
Registrar IANA ID: 146

Die Registry verweist uns nun an den Registrar. Wir sollen dort nachfragen. Tun wir das:

$ whois -h whois.godaddy.com icann.org
Domain Name: ICANN.ORG
Registry Domain ID: D2347548-LROR
Registrant Organization: ICANN
Registrant State/Province: California
Registrant Country: US

Und wir sind am Ziel.

Jeder der Anfragen wurde an unterschiedliche Institutionen gestellt und jeweils unter dem jeweiligen nationalem Recht beantwortet.

Geht man also zu dem Beispiel zurück, so muss die pakistanische Polizei einen indischen Dienst befragen, um dort die Angaben zu erhalten. Der indische Anbieter berücksichtigt nun indisches Recht bei der Auskunft zu seinem indischen Kunden.

Warum sollten wir das  nicht mal versuchen?

Seit heute morgen häuften sich die Beschwerden über DNS Ausfälle im xDSL Netz. Die Meldungen waren zeitlich unspezifisch, gingen quer über die Produktpalette und betrafen auch Backend-Systeme. Was ist da los?

Sniffing zeigte, dass die Anfragen tatsächlich bei den DNS-Servern ankommen, diese aber schlicht nicht immer antworten. Restart der Prozesse brachte Linderung, allerdings nur kurz. Nach etwa einer halben Minute hörte jeder der Server auf, Antworten zu verschicken.

Im Sniffing zeigte jeder Server jedoch hektische DNS-Aktivität. Nur warum? Was macht der da?

Die Statistik jedes Servers zeigt, dass die Requestqueue, die Liste der offenen Anfragen überläuft. Und in der Liste der offenen Anfragen enthielt überraschenderweise aberhunderte Zeilen der folgenden Art

499 CNAME IN psai9edi.s3.amazonaws.com. 10.412281 iterator wait for 2600:9000:5300:1b00::1
500 CNAME IN psg62lat.s3.amazonaws.com. 41.447997 iterator wait for 2600:9000:5300:1b00::1
501 CNAME IN psgp7262.s3.amazonaws.com. 7.193284 iterator wait for 2600:9000:5300:1b00::1
502 CNAME IN pshziple.s3.amazonaws.com. 44.356077 iterator wait for 2600:9000:5300:1b00::1
503 CNAME IN psigagat.s3.amazonaws.com. 45.621077 iterator wait for 2600:9000:5300:1b00::1
504 CNAME IN pspcvsil.s3.amazonaws.com. 22.752443 iterator wait for 2600:9000:5300:1b00::1
505 CNAME IN pswgyera.s3.amazonaws.com. 52.993825 iterator wait for 2600:9000:5300:1b00::1
506 CNAME IN qa9c96lt.s3.amazonaws.com. 56.530104 iterator wait for 2600:9000:5300:1b00::1
507 CNAME IN qah-zkwp.s3.amazonaws.com. 1.215374 iterator wait for 2600:9000:5300:1b00::1
508 CNAME IN qavtvxcc.s3.amazonaws.com. 37.358889 iterator wait for 2600:9000:5300:1b00::1
509 CNAME IN v118gk69.s3.amazonaws.com. 38.617430 iterator wait for 2600:9000:5300:1b00::1
510 CNAME IN v12ggbel.s3.amazonaws.com. 8.180806 iterator wait for 2600:9000:5300:1b00::1
511 CNAME IN v19pg7un.s3.amazonaws.com. 45.592578 iterator wait for 2600:9000:5300:1b00::1
512 CNAME IN v1h66nam.s3.amazonaws.com. 20.184565 iterator wait for 2600:9000:5300:1b00::1
513 CNAME IN v1i5mjps.s3.amazonaws.com. 2.479473 iterator wait for 2600:9000:5300:1b00::1
514 CNAME IN v1t5buni.s3.amazonaws.com. 13.825713 iterator wait for 2600:9000:5300:1b00::1
515 CNAME IN x8cca8xn.s3.amazonaws.com. 20.251822 iterator wait for 2600:9000:5300:1b00::1
516 CNAME IN x8ln-0rj.s3.amazonaws.com. 50.227529 iterator wait for 2600:9000:5300:1b00::1
517 CNAME IN x8q9jrjv.s3.amazonaws.com. 50.693382 iterator wait for 2600:9000:5300:1b00::1
518 CNAME IN x8x9zq3n.s3.amazonaws.com. 44.069048 iterator wait for 2600:9000:5300:1b00::1
519 CNAME IN xk8t2xuy.s3.amazonaws.com. 52.709106 iterator wait for 2600:9000:5300:1b00::1
520 CNAME IN xkkh6w1b.s3.amazonaws.com. 48.943785 iterator wait for 2600:9000:5300:1b00::1
521 CNAME IN xkl4qcvv.s3.amazonaws.com. 43.927290 iterator wait for 2600:9000:5300:1b00::1
522 CNAME IN xksddwxf.s3.amazonaws.com. 11.812726 iterator wait for 2600:9000:5300:1b00::1
523 CNAME IN xkuf9dcb.s3.amazonaws.com. 49.485060 iterator wait for 2600:9000:5300:1b00::1

Was ist denn hier los?

Die externe Validierung eines dieser Einträge ergibt:

Dazu gehören die folgenden Fehlermeldungen:

  • amazonaws.com to s3-1-w.amazonaws.com
    The server(s) for the parent zone (amazonaws.com) responded with a referral instead of answering authoritatively for the DS RR type. (205.251.192.27, 205.251.195.199, 2600:9000:5300:1b00::1, 2600:9000:5303:c700::1, UDP_-_EDNS0_4096_D_K)
  • x8x9zq3n.s3.amazonaws.com/A
    No response was received from the server over UDP (tried 12 times). (156.154.64.10, 156.154.65.10, 2001:502:f3ff::10, 2610:a1:1014::10, UDP_-_NOEDNS_)
  • x8x9zq3n.s3.amazonaws.com/AAAA
    No response was received from the server over UDP (tried 12 times). (156.154.64.10, 156.154.65.10, 2001:502:f3ff::10, 2610:a1:1014::10, UDP_-_NOEDNS_)

Anders formuliert ist die Auflösung der Domain "amazonaws.com", die Arbeitsdomain von Amazon, kaputt.

Ein validierender Resolver dreht über mehrere Server mehrere Schleifen, bis er nach langer Zeit erfolglos und mit leeren Händen aufgibt. Da er kein Ergebnis zum cachen hat, geht er bei jeder Anfrage erneut in die Schleife und ist beschäftigt.

Jeder unserer Resolver hat 700 Worker-Slots pro CPU-Thread. Bei der Masse der Kunden und der Masse der Anfragen steht nach 20 bis 30 Sekunden kein freier Slot mehr für eine neue Anfrage zur Verfügung. Dann ist schlicht Schluss.

Die Lösung bestand darin, die Zone "amazonaws.com" auf einen nicht-validierenden Resolver umzulenken und diese Ergebnisse zu benutzen. Spontan enspannte sich die Situation wieder.

Aber was war wirklich passiert?

Zitat eines näher involvierten Technikers: Äh...was die da gerade eintragen ist aber ungültig und wird nicht lustig sein, wenn entsprechende TTLs auslaufen.

Wir haben seit einiger Zeit einen Unold 38915 Onyx Duplex Toaster. Der ist nun kaputt, d.h. er rastet nicht mehr ein, wenn man etwas toasten will. Leider sind im Netz keine Reparaturanleitungen vorhanden. Deswegen schreibe ich mal auf, wie man an die Innereien kommt und sich ein Wegschmeißen erspart.

Zunächst benötigt man Werkzeug: Zwei Messer, einen Vierkant- und einen Dreikant-Schraubendreher.

Werkzeug

Als erstes werden die seitlichen Griffe entfernt. Diese sind nur aufgesteckt (wenn auch stramm). Mit zwei Messern kann man gleichmäßig Druck ausüben ohne die Bleche zu verbiegen.

Griffe abhebeln
Griffe ab

Der Drehknopf hat eine Einkerbung, die man später wieder treffen muss, aber das ergibt sich von selbst.

Zunächst muss aber die Umhüllung fallen. Dazu dreht man den Toaster um und findet auf der Unterseite mehrere Schrauben. Die Vierkantschrauben sind mittig leicht zu finden. Die Dreikantschrauben verstecken sich unter den Gummifüßen, die zum Glück nicht verklebt sind.

Vierkant
Gummifuss
Dreikant

Die Vierkantschrauben halten ein paar straffe Metallohren, die man heraus ziehen muss und schon kann man das Gehäuse anheben. Aber vorsichtig und auf der Seite gegenüber der Griffe.

Anheben

Hat man die Diagonale erreicht, wird es sehr knapp. Mit Geschick und guten Willen bekommt man jedoch die Hülle über die Kante.

Kante

Nun ist der Toaster offen. Beim Aufklappen lässt man besser alle Drähte dran, denn dort ist nichts kaputt.

Offen
Innen

Die eigentliche Ursache, warum der Toaster nicht mehr einrastet, ist simpel. Es ist Dreck zwischen dem Magneten und dem Eisen. Das merkt man, wenn man mal runter drückt. Das Eisen muss dann flach auf dem Magneten aufliegen.

Magnet1
Magnet2

Wenn man mal das Gerät offen hat, kann man sich gleich noch ansehen, wie die Mechanik zum Absenken und Festhalten funktioniert. Es ist halt ein billiges Gerät, bei dem sich leicht was verbiegt. Sollte das der Fall sein, so korrigiert man das gleich mit.

Seilzug1
Seilzug2

Nach Säubern und Geradebiegen aller Komponenten kann das Gehäuse wieder drauf. Dabei gibt es noch einen Moment, der Aufmerksamkeit erfordert: Das Gehäuse muss oben in zwei Schlitze einrasten.

Einstecken

Abschließend alle Schrauben fest drehen und Gummifüße und Griffe wieder einstecken.

Fertig.

Typischerweise haben Server einen RAID-Controller, der einen Festplattenausfall vor dem Betriebssystem verstecken kann. Wenn also eine Platte den Geist aufgibt, kann man sie wechseln, ohne auf irgendeine Besonderheit des installierten Systems Rücksicht nehmen zu müssen. Das erleichtert die Arbeit im Rechenzentrum erheblich: Platte tot → Austauschen → Fertig.

Redundante Redundanz

Auf einen Satz Server ist Proxmox zum Spielen drauf gekommen. Der Installer richtet auf einer ausgewählten Platte das System ein. In dem Fall also auf dem Hardware-RAID. Soweit so gut.

Für das Storage möchte ich auf Ceph zurück greifen, auf Neudeutsch hyperkonvergent arbeiten. Deswegen habe ich den größten Teil der Platte frei gelassen um den Rest als OSD einbinden zu können.

Ceph verwaltet eine eigene Redundanz über die OSD-Datenträger. Es berücksichtigt dabei großräumigere Strukturen als nur Platten an einem Contoller. Dies führt zu massiver Platzverschwendung: Daten liegen nicht mindestens doppelt, sondern vier- bis sechsfach vor.

Wird das Hardware-RAID aufgebrochen, kann Ceph die einzelen Platte direkt verwalten und damit effizienter umgehen. Aber was wird  aus dem Basissystem?

Eine Neuinstallation später steht fest, dass der Proxmox-Installer das System ausschließlich auf eine der beiden Platte installiert hat. Konzeptionell ist das nachvollziehbar, da so mehr Platz für die OSDs bleibt. Geht die Systemplatte kaputt, installiert man den Knoten halt komplett neu und der Cluster erledigt den Rest.

Ich scheue aber den Aufwand, da die Netzwerkinstallation des Basissystem hier doch stärker vom Proxmox-Standard abweicht, als erwartet. Aber dazu ein andermal.

Aus eins mach zwei

Zuerst ein Blick auf die Situation direkt nach der Installation:

root@server21:~# fdisk /dev/sda

Welcome to fdisk (util-linux 2.29.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/sda: 279.4 GiB, 300000000000 bytes, 585937500 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 3B2CD483-DE39-45BA-BF6A-C35E2AD8F783

Device       Start      End  Sectors  Size Type
/dev/sda1       34     2047     2014 1007K BIOS boot
/dev/sda2     2048  1050623  1048576  512M EFI System
/dev/sda3  1050624 41943040 40892417 19.5G Linux LVM

Die Platte sdb ist leer.

Im ersten Schritt kopiere ich den Anfang der Platte incl. alle Boot-Informationen:

root@server21:~# dd if=/dev/sda of=/dev/sdb bs=512 count=1050623

Ja, die GPT ist auf der zweiten Platte kaputt, da die Kopie am Ende der Platte fehlt. Aber das behebt fdisk beim nächsten Aufruf.

So kann nun die zweite Platte so eingerichtet werden, wie später es sein soll:

root@server21:~# fdisk /dev/sdb
[...]
Command (m for help): t
Partition number (1-3, default 3): 
Hex code (type L to list all codes): 29

Changed type of partition 'Linux LVM' to 'Linux RAID'.

Command (m for help): p
Disk /dev/sdb: 279.4 GiB, 300000000000 bytes, 585937500 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 3B2CD483-DE39-45BA-BF6A-C35E2AD8F783

Device       Start      End  Sectors  Size Type
/dev/sdb1       34     2047     2014 1007K BIOS boot
/dev/sdb2     2048  1050623  1048576  512M EFI System
/dev/sdb3  1050624 41943040 40892417 19.5G Linux RAID

Und wenn wir schon mal hier sind, dann auch gleich das OSD vorbereiten

Command (m for help): n
Partition number (4-128, default 4): 
First sector (41943041-585937466, default 41945088): 
Last sector, +sectors or +size{K,M,G,T,P} (41945088-585937466, default 585937466): 

Created a new partition 4 of type 'Linux filesystem' and of size 259.4 GiB.

Command (m for help): t
Partition number (1-4, default 4): 
Hex code (type L to list all codes): 76

Changed type of partition 'Linux filesystem' to 'Ceph OSD'.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Damit sehen die Platten so aus:

lvm-raid1

Die grünen Partitionen sind schon in Ordnung, die grünen Inhalte ebenfalls. Rot markierte Teile sind noch beheben, die Blauen noch unbenutzt.

Aus zwei mach eins

Zunächst muss das RAID als solches fertig werden. Es soll ein Spiegel werden, aber anfangs nur mit einer Partition.

root@server21:~# mdadm --create /dev/md0 -n 1 -f -l 1 /dev/sdb3 
mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90

Sehr gute Frage! Ich habe den Boot-Kram in separaten Boot-Partitionen, was kann da schon schief gehen?

mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
root@server21:~# cat /proc/mdstat 
Personalities : [raid1] 
md0 : active raid1 sdb3[0]
      20429824 blocks super 1.2 [1/1] [U]
      
unused devices: <none>

Schaut gut aus. Jetzt nur noch die Daten des LVM rein kopieren.

Die Idee ist dabei, das LVM aufzublasen und dann die alte Partition wieder raus zu nehmen.

root@server21:~# pvcreate /dev/md0
  Physical volume "/dev/md0" successfully created.
root@server21:~# pvdisplay 
  --- Physical volume ---
  PV Name               /dev/sda3
  VG Name               pve
  PV Size               19.50 GiB / not usable 3.00 MiB
  Allocatable           yes 
  PE Size               4.00 MiB
  Total PE              4991
  Free PE               3775
  Allocated PE          1216
  PV UUID               ijshQ9-3z2d-oVxd-WJB2-8LUv-HEfL-lTZzEi
   
  "/dev/md0" is a new physical volume of "19.48 GiB"
  --- NEW Physical volume ---
  PV Name               /dev/md0
  VG Name               
  PV Size               19.48 GiB
  Allocatable           NO
  PE Size               0   
  Total PE              0
  Free PE               0
  Allocated PE          0
  PV UUID               G8kgdF-Gcec-ddbO-92ys-9rsf-dkpo-Za9C4a

root@server21:~# pvdisplay -C
  PV         VG  Fmt  Attr PSize  PFree 
  /dev/md0       lvm2 ---  19.48g 19.48g
  /dev/sda3  pve lvm2 a--  19.50g 14.75g

Es gibt nun zwei LVM-Datenträger, von denen einer das Volume enthält. Nun zum Umzug.

root@server21:~# vgextend pve /dev/md0
  Volume group "pve" successfully extended
root@server21:~# pvdisplay -C
  PV         VG  Fmt  Attr PSize  PFree 
  /dev/md0   pve lvm2 a--  19.48g 19.48g
  /dev/sda3  pve lvm2 a--  19.50g 14.75g
root@server21:~# vgdisplay -C
  VG  #PV #LV #SN Attr   VSize  VFree 
  pve   2   1   0 wz--n- 38.98g 34.23g
root@server21:~# vgreduce pve /dev/sda3 
  Physical volume "/dev/sda3" still in use

Vergrößern ging, aber die alte Partition lässt sich nicht entfernen!

Warum? Weil noch Daten drauf sind. Schließlich wurde ja nur neuer Platz hinzugefügt. Nur der ist unbenutzt.

root@server21:~# pvmove /dev/sda3
  /dev/sda3: Moved: 0.00%
  /dev/sda3: Moved: 18.17%
  /dev/sda3: Moved: 36.43%
  /dev/sda3: Moved: 54.52%
  /dev/sda3: Moved: 72.86%
  /dev/sda3: Moved: 91.04%
  /dev/sda3: Moved: 100.00%
root@server21:~# pvdisplay 
  --- Physical volume ---
  PV Name               /dev/sda3
  VG Name               pve
  PV Size               19.50 GiB / not usable 3.00 MiB
  Allocatable           yes 
  PE Size               4.00 MiB
  Total PE              4991
  Free PE               4991
  Allocated PE          0
  PV UUID               ijshQ9-3z2d-oVxd-WJB2-8LUv-HEfL-lTZzEi
   
  --- Physical volume ---
  PV Name               /dev/md0
  VG Name               pve
  PV Size               19.48 GiB / not usable 3.00 MiB
  Allocatable           yes 
  PE Size               4.00 MiB
  Total PE              4987
  Free PE               3771
  Allocated PE          1216
  PV UUID               G8kgdF-Gcec-ddbO-92ys-9rsf-dkpo-Za9C4a

Nun ist nichts mehr auf der alten Partition in Benutzung. "Allocated PE" ist 0.

Also müsste sich die Partition nun entfernen lassen.

root@server21:~# vgreduce pve /dev/sda3 
  Removed "/dev/sda3" from volume group "pve"
root@server21:~# pvdisplay -C
  PV         VG  Fmt  Attr PSize  PFree 
  /dev/md0   pve lvm2 a--  19.48g 14.73g
  /dev/sda3      lvm2 ---  19.50g 19.50g

Hurra! Nun noch die Partition dem LVM entziehen.

root@server21:~# pvremove /dev/sda3
  Labels on physical volume "/dev/sda3" successfully wiped.
root@server21:~# pvdisplay -C
  PV         VG  Fmt  Attr PSize  PFree 
  /dev/md0   pve lvm2 a--  19.48g 14.73g

Und die Partition umwidmen.

root@server21:~# fdisk /dev/sda

Welcome to fdisk (util-linux 2.29.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/sda: 279.4 GiB, 300000000000 bytes, 585937500 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 3B2CD483-DE39-45BA-BF6A-C35E2AD8F783

Device       Start      End  Sectors  Size Type
/dev/sda1       34     2047     2014 1007K BIOS boot
/dev/sda2     2048  1050623  1048576  512M EFI System
/dev/sda3  1050624 41943040 40892417 19.5G Linux LVM
Command (m for help): t
Partition number (1-3, default 3): 
Hex code (type L to list all codes): 29

Changed type of partition 'Linux LVM' to 'Linux RAID'.

Command (m for help): p
Disk /dev/sda: 279.4 GiB, 300000000000 bytes, 585937500 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 3B2CD483-DE39-45BA-BF6A-C35E2AD8F783

Device       Start      End  Sectors  Size Type
/dev/sda1       34     2047     2014 1007K BIOS boot
/dev/sda2     2048  1050623  1048576  512M EFI System
/dev/sda3  1050624 41943040 40892417 19.5G Linux RAID

Command (m for help): n
Partition number (4-128, default 4): 
First sector (41943041-585937466, default 41945088): 
Last sector, +sectors or +size{K,M,G,T,P} (41945088-585937466, default 585937466): 

Created a new partition 4 of type 'Linux filesystem' and of size 259.4 GiB.

Command (m for help): t
Partition number (1-4, default 4): 
Hex code (type L to list all codes): 76

Changed type of partition 'Linux filesystem' to 'Ceph OSD'.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Bei der Gelegenheit auch gleich noch die OSD Partition vorbereitet. Sehr schön.

Jetzt kann das RAID breit gezogen werden.

root@server21:~# mdadm -a /dev/md0 /dev/sda3
mdadm: added /dev/sda3
root@server21:~# cat /proc/mdstat 
Personalities : [raid1] 
md0 : active raid1 sda3[1](S) sdb3[0]
      20429824 blocks super 1.2 [1/1] [U]

Nein.Das ist kein "hot spare", das ist Bestandteil des RAIDs selbst. Ich hätte wohl gleich mit zwei Partitionen, davon eine als "none", anfangen sollen.

root@server21:~# mdadm -r /dev/md0 /dev/sda3
mdadm: hot removed /dev/sda3 from /dev/md0
root@server21:~# mdadm --grow /dev/md0 -n 2 -a /dev/sda3
mdadm: added /dev/sda3
raid_disks for /dev/md0 set to 2
root@server21:~# cat /proc/mdstat 
Personalities : [raid1] 
md0 : active raid1 sda3[1] sdb3[0]
      20429824 blocks super 1.2 [2/1] [U_]
      [>....................]  recovery =  3.2% (669248/20429824) finish=1.4min speed=223082K/sec 

Hurra! Jetzt schaut es so aus.

lvm-raid2

Reboot

Da nun alles im laufenden Betrieb umgezogen ist, bleibt nur noch der finale Reboot.

Klappt aber nicht, weil der Grub das LVM-Volume nicht mehr findet. Sollte /boot nicht separat liegen?! Mal beim Nachbar nachschauen:

root@server22:~# mount | grep boot
/dev/sda2 on /boot/efi type vfat ...

Ohje. Also nochmal von vorn und diesmal mit "--metadata=0.9". Siehe an, es geht.

Aber was ist eigentlich passiert? Zur Erinnerung nochmal die Warnung.

mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90

Die Metadaten des RAID können also am Anfang (neu) oder am Ende (alt) der Partition stehen. Wenn sie am Ende stehen, muss niemand irgendwas von dem RAID1 (Mirror) verstehen, um auf den Inhalt zugreifen zu können. Die Partition sieht halt aus, als gäbe es gar kein RAID.

Stehen die Daten am Anfang der Partition, muss die Software damit umgehen können. Im Prinzip ist das auch sehr einfach, weil man nur über die RAID-Kennung hinweg springen muss. Leider kann das Grub nicht und ist damit ein steter Quell von Problemen.

Wir fahren nach Berlin! Vom Leiden einer Reisebuchung am ersten April.

Es ist BCIX Stammtisch und ich darf dabei sein. Hurra!

Anreise

Dazu muss eine Reise gebucht werden und schon kommt das erste Problem auf: Ich kann auf dem Bahn-Portal nicht bezahlen. Die Zahlung per Kreditkarte wird abgelehnt.

Anruf beim Support: "Das ist zu Ihrer Sicherheit! Sie haben schon so oft mit Kreditkarte Bahnfahrkarten gekauft, das könnte auch ein Betrüger sein, also ist das nun gesperrt. Gegen Sie zum Schalter!"

Genau genommen funktioniert mit der Commzerbank das 3D Secure Verfahren nie, weswegen jeder, der ein Konto dort hat es ausschaltet. Nun besteht die Bahn spontan auf der Einhaltung des Verfahren, auch wenn die Bank es direkt als unnötig ablehnt. Die Bahn kann mit der Ablehnung aber nicht umgehen und die Buchung scheitert.

Was auch nicht geht, ist ein Umstellung auf Lastschrift. Nicht, weil der Prozess per Post und Papier abläuft, sondern weil er aus Sicherheitsgründen seitens der Bahn abgebrochen wird. Es gab ja eben erst eine fehlgeschlagene Kreditkartenbuchung.

Auf eine öffentliche Beschwerde hin, antwortet die Bahn:

Aktuell sind verstärkt betrügerische Aktivitäten basierend auf sogenannten Phishing-E-Mails zu verzeichnen. Als eine vorsorgliche Maßnahme zum Schutz unserer Kunden können deshalb Sparpreis-Tickets auf bahn.de und im DB Navigator bis auf Weiteres nur

  • per "Sofort." (SOFORT Überweisung) und
  • Kreditkarte unter Abfrage des 3D-Secure-Passwortes

bezahlt werden. Dafür bitten wir um Verständnis.

https://www.bahn.de/p/view/home/info/sonderkommunikation-phishing.shtml

Der Betrüger darf also den teuren Flexpreis gern zu meinen Lasten in Anspruch nehmen, nicht aber die Sparpreise. Super, Deutsche Bahn!

Und nein, ich werde meine Online-Banking-Daten nicht irgendeinem fremden Unternehmen in die Hände drücken. Das fällt aus.

Übernachtung

Und nun zu Teil zwei: Ich brauch ein Hotel, weil nach der Veranstaltung nichts mehr nach Hause fährt.

Bei Booking alles soweit kein Problem, nur gibt's keinen Ausdruck der Buchung mehr direkt auf die Hand. Das neue Verfahren generiert das PDF offenbar aus einem C64-Font heraus:

2019-04-01-150217_1278x1022_scrot

Echt retro.

Eine unglückliche Platzierung eines Datenschutzlinks wird unfreiwillig komisch, wenn dann Fehler auftreten.

Bildschirmfoto vom 2019-02-26 10.18.10

Genau. Kein Kamerabild, weil ... ganz unten "Powered by Datenschutz" steht.

Eigentlich ist es ganz anders.

Im Rahmen der DSGVO wurde der Webseite ein Footer mit einem Datenschutzlink verpasst. Allerdings beißt sich das mit dem bisherigen Design, bei dem die Sponsoren als "Powered by" aufgeführt sind.

Als dann die Kamera kein Bild liefern konnte, entstand dieser Schnappschuß-.

Ein Kunde beschwerte sich über Netzwerkprobleme. Zwischen zwei seiner Server würde immer wieder ein SMB/CIFS-Mount weg brechen. Nicht, dass es irgendwelche bemerkbaren Störungen gegeben hätte, aber es gäbe halt immer wieder diese Fehlermeldungen im Log.

Aufbau

Der Kunde hat in seinem Layer2-Segment eine ganze Latte Server stehen. Einer davon spielt Windows-Fileserver für eine Gruppe von Windows Rechnern. Ein anderer ist das Linux-Monitoring-System, das u.a. regelmäßig versucht auf die Freigabe zuzugreifen.

Das Ganze hat eine längere Vorgeschichte, die vor einem halben Jahr zur Empfehlung führte, doch konsequent auf SMB1 zu verzichten. Dieser Teil ist nicht von Belang, sondern das, was der Empfehlung folgte.

Denn seit der Umstellung hat das Monitoring-Linux seltsame Einträge im Kernellog stehen:

Feb 12 19:07:02 kernel: CIFS VFS: Server a.b.c.d has not responded in 120 seconds. Reconnecting...

Woher kommen die 120 Sekunden? Das Handbuch hilft weiter:

echo_interval=n

sets the interval at which echo requests are sent to the server on an idling
connection. This setting also affects the time required for a connection to
an unresponsive server to timeout. Here n is the echo interval in seconds.
The reconnection happens at twice the value of the echo_interval set for
an unresponsive server. If this option is not given then the default value of
60 seconds is used.

Das erklärt schon mal die 120 Sekunden: Alle Minute wird ein SMB Echo versendet und wenn zweimal kein Echo beantwortet wird, nimmt man an die Verbindung sei tot. Deswegen wird seitens des Kunden (verständlicherweise) vermutet, dass es Paketverlust im Netzwerk gäbe.

Allein diese Vermutung zu widerlegen, dauert Wochen. Dazu wird auf beiden Seiten der Datenverkehr mitgeschnitten und Paket für Paket nebeneinander gelegt.

Dem eigentlichen Problem ist man damit aber leider immer noch nicht näher gekommen.

Analyse

Immer wieder wird so ein Mitschnitt angeschaut, um irgendeine Auffälligkeit zu entdecken.

smb-tcp-flow

Es ist sehr schön zu sehen, wie jede Minute das Keep-Alive Request-Response Spiel passiert. Anschließend werden Daten übertragen.

Kurz darauf kommt die Kommunikation zum Erliegen und der im Log erwähnte Timeout schlägt zu. Zwischen 19:05:01.99... und 19:07:02.25... liegen etwas mehr als 120 Sekunden. Das passt so weit.

Auffällig ist der Übertragungsfehler (der mit dem TCP-Keepalive), der direkt vor dem Abbruch der Verbindung rein schlägt. Das muss untersucht werden!

Warum ist die Zeile schwarz? Weil der Keepalive genau ein Byte nochmal sendet, dass schon lange geackt wurde. Es ist eine sehr ungewöhnliche Kommunikation. Wireshark markiert solche Sequenzfehler mit schwarzer Farbe.

  • Um 19:05:01.949511 werden 128 Byte empfangen.
  • Um 19:05:01.997203 wird der Empfang dieser 128 Bytes bestätigt.
  • Um 19:07:01.999481 wird das letzte der 128 Bytes nochmal übertragen (zusammen mit dem Keepalive-Flag)

Wenn die Bestätigung des Datenempfangs (ACK) nicht angekommen wäre, würden die gesamten 128 Byte nochmal gesendet. Werden sie aber nicht.

Hat irgend eine Firewall unterwegs an den Sequenznummern gespielt? Es ist aber gar keine Firewall dazwischen, beide Server stehen im gleichen LAN und sehen sich direkt. Der Mitschitt auf der anderen Seite bestätigt, dass die Bestätigung komplett angekommen ist.

Warum sollte der Kernel also ein einzelnes Byte nochmal senden? Es stellt sich heraus, dass die Windows-Implementation des TCP-Keepalives genau ein Byte nochmal sendet, während die Linux-Implementation das Keepalive ohne Payload auskommt. Es handelt sich offenbar um eine Anpassung von Windows an kaputte Middleware, die TCP-Pakete ohne Payload verwirft.

Der Teil ist also in Ordnung. Aber was ist es dann?

Vielleicht hat es irgendwo Verzögerungen gegeben? Also schauen wir mal auf die Round-Trip-Zeiten. Vielleicht gibt es Ausreißer.

smb-tcp-rtt

Von der remote Stelle gibt es keine besonderen Auffälligkeiten. Alles schön.

smb-tcp-rtt2

Auch die lokale Verarbeitung ist völlig unauffällig: Das der Kernel direkt bearbeiten kann, ist nahezu in Nullzeit erledigt. Wenn die Anwendungssoftware involviert ist, dauert's länger. Man sieht sehr schön, wie regelmäßig die Messungen vorgenommen werden.

Auch hier ist nichts auffällig. Aber was ist es dann?

Beim genaueren Hinsehen fällt auf, dass um 19:06:00 ein Echo-Request hätte versendet werden sollen. Der fehlt aber!

Kernel Archäologie

Im Linux-Kernel ist für das Versenden der Echo-Requests die Funktion cifs_echo_request zuständig. Dort steht:

static void
cifs_echo_request(struct work_struct *work)
{
 int rc;
 struct TCP_Server_Info *server = container_of(work,
     struct TCP_Server_Info, echo.work);
 unsigned long echo_interval;

 /*
  * If we need to renegotiate, set echo interval to zero to
  * immediately call echo service where we can renegotiate.
  */
 if (server->tcpStatus == CifsNeedNegotiate)
  echo_interval = 0;
 else
  echo_interval = server->echo_interval;

 /*
  * We cannot send an echo if it is disabled.
  * Also, no need to ping if we got a response recently.
  */

 if (server->tcpStatus == CifsNeedReconnect ||
     server->tcpStatus == CifsExiting ||
     server->tcpStatus == CifsNew ||
     (server->ops->can_echo && !server->ops->can_echo(server)) ||
     time_before(jiffies, server->lstrp + echo_interval - HZ))
  goto requeue_echo;

 rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
 if (rc)
  cifs_dbg(FYI, "Unable to send echo request to server: %s\n",
    server->hostname);

requeue_echo:
 queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval);
}

Der Code ist in mehrfacher Hinsicht interessant:

  • Zum Einen wird ein Echo nur gesendet, wenn dafür eine echte Notwendigkeit besteht.
  • Zum Anderen handelt es sich um ein Daisy-Chain Scheduling: Erst nach getaner Arbeit wird die nächste Aktion geplant.

Was die Notwendigkeit betrifft, so werden keine Echos ausgesendet, solange die letzte Response kürzer als eine Echo-Abstand einging.

Aber was ist eine Response? Im Code wird der server-›lstrp immer dann auf die aktuelle Zeit gesetzt, wenn ein Paket empfangen wird. Unabhängig davon ob es ein Echo-Response oder normale Daten sind.

Das Abziehen von HZ (eine Sekunde) ist eine Maßnahme dagegen, dass die Echo-Response auf die eigene Anfrage schon als interessanter Traffic interpretiert wird. Dieser hartkodierte Wert gilt also unter der Annahme, dass die Antwort der Gegenstelle immer schneller da ist als in einer Sekunde.

Betreibt man einen solchen SMB Zugriff über eine längere Strecke oder gegen einen langsameren Server, so wird jeder zweite Echo-Request unterdrückt, wenn kein weiterer Traffic auftritt. Das führt direkt dazu, dass SMB nur im LAN wirklich funktioniert.

Aufgrund der Protokollarchitektur werden alle Anfragen sequentiell bearbeitet. Wenn also ein Echo-Request gesendet wird, während noch eine andere Abfrage an den Server läuft, so wird die Verarbeitung des Echo-Requests erst dann erfolgen, wenn der vorherige Request bearbeitet wurde. Eine asynchrone Bearbeitung wurde mit dem Wechsel von SMB1 zu SMB2 aufgegeben. Stattdessen soll der Server den Client mitteilen, dass die Bearbeitung einer aufwändigeren Anfrage noch andauert.

In diesem Fall war um 19:05:01.91... eine Antwort vom Server eingegangen. Das Echo-Paket hätte gegen 19:06:00 gesendet werden sollen. Das ist haarscharf daneben!

Die zweite Auffälligkeit ist das Scheduling. Erst, wenn alle Arbeiten (Echo-Paket versenden) erledigt sind, wird der nächste Echo-Versand geplant und zwar 60 Sekunden später. Die gesamte Verarbeitungszeit fehlt in der Planung. Das führt dazu, dass die Echo-Pakete nicht genau nach 60 Sekunden versendet werden, sondern immer ein Stück später.

Man sieht das im Mitschnitt sehr schön: 19:01:55.05, 19:02:56.49, 19:03:57.94, 19:04:59.37, 19:06:00.90, 19:07:02.27. Die Abstände betragen etwa 61,5 Sekunden. Also 1,5 Sekunden mehr als geplant. Im letzten Schritt ist das Intervall kürzer, weil das Echo-Paket ja nicht versendet werden musste.

Racecondition

Was wirklich passiert ist schnell erklärt: Das Unterdrücken des geplanten Echo-Requests ist fehlerhaft.

Im Detail:

smb-tcp-timing
  • Durch das Daisy-Chaining-Schedulung entsteht eine Lücke.
  • Kommt Traffic unglücklich kurz nach dem letzten Echo, so wird das geplante Echo unterdrückt.
  • Durch die Lücke verzögert sich der nächste Echo-Versand bis nach dem Timeout, das hart auf das doppelte Echo-Intervall gesetzt wird.

Besonderen Charme hat die Erkenntnis, dass der finale Echo-Request erst versendet wird und danach der Timeout zuschlägt, weil die erste Aktion beim Empfangen des Echo-Response ist, auf einen Timeout zu prüfen und abzubrechen. Ironischerweise hat also das erfolgreiche Echo den Abbruch getriggert.

Stellt sich nun die Frage, seit wann das Problem auftritt und wer dafür verantwortlich ist:

  • Im Patch c740 werden fest alle 60 Sekunden Echo-Requests versendet, wenn kein anderer Traffic auftrat. (Jan 11, 2011)
  • Im Patch fda3 werden diese Echos genutzt um nach dem fünffachen Intervall (konfigurierbar) ohne Traffic den Timeout auszulösen. (Jan 20, 2011)
  • Im Patch 6dae wird die Konfigurierbarkeit von 60s x variabel(5) zu variabel(60s) x zwei umgestellt. (Feb 21, 2012)

Die Lücke und das Unterdrücken wurde also am 11. Januar 2011 eingeführt. Allerdings hat diese keine Auswirkung, da der Timeout erst beim fünffachen Intervall eintritt.

Mit der Umstellung vom 21. Februar 2012 wirkt sich die Lücke nun aus, da man den Timeout hart auf das doppelte Intervall setzte.

Lösungen

Es gibt drei Möglichkeiten.

Zum einen kann man die Lücke beseitigen, indem man das Scheduling zu festen Zeiten macht (immer 60 Sekunden zum letzten Schedule-Zeitpunkt addieren). Es genügt übrigends nicht, das Rescheduling an den Anfang der Routine zu stellen. Damit wird die Lücke nur kürzer, bleibt aber bestehen.

Eine andere Möglichkeit wäre, den Echo-Request unabhängig von anderem Traffic in jedem Fall zu versenden. Motto: Wenn eh schon Traffic läuft, stört der zusätzliche Request auch nicht mehr. Es besteht dabei die potentielle Gefahr, dass sich ein Server an einem Echo zwischen anderen Requests verschluckt.

Und dann wäre es noch denkbar, dass man dem gesamten Problem aus dem Weg geht, indem man minimal die dreifache Intervall-Länge wartet.

Für einen Quick-Fix sollte man den dritten Weg beschreiten: Aus einer 2 eine 3 im Code machen:

static bool
server_unresponsive(struct TCP_Server_Info *server)
{
 /*
  * We need to wait 2 echo intervals to make sure we handle such
  * situations right:
  * 1s  client sends a normal SMB request
  * 2s  client gets a response
  * 30s echo workqueue job pops, and decides we got a response recently
  *     and don't need to send another
  * ...
  * 65s kernel_recvmsg times out, and we see that we haven't gotten
  *     a response in >60s.
  */
 if ((server->tcpStatus == CifsGood ||
     server->tcpStatus == CifsNeedNegotiate) &&
     time_after(jiffies, server->lstrp + 2 * server->echo_interval)) {
  cifs_dbg(VFS, "Server %s has not responded in %lu seconds. Reconnecting...\n",
    server->hostname, (2 * server->echo_interval) / HZ);
  cifs_reconnect(server);
  wake_up(&server->response_q);
  return true;
 }

 return false;
}

Bleibt nur noch die Frage nach dem TCP-Keepalive. Windows hat exakt 120s bis es auf einer TCP Session einen Keepalive schickt. Man sieht sehr schön, dass zuerst der Windows-Server den TCP-Keepalive schickt, weil er das letzte Paket eher abgesendet hat als es beim Linux ankam.

Danksagung

Die gesamte Analyse wurde von meinem Kollegen Jens durchgeführt, der mich nur bat, die Dinge zusammen zu schreiben. Was ich hier mit getan habe.

jetzt muss nur noch ein Bugreport an die richtige Stelle geschickt werden.

Microsoft stellt einen eigenen BGP Router für Windows bereit. Wir benötigen an einer Stelle, wo bisher nur ein paar Hyper-V fähige Kisten stehen einen unabhängigen Internetzugang. Was liegt also näher als alles aus einer Hand zu bauen?

Installation

Auf eine Windows VM kommt aus der RAS Rolle das Feature LAN Routing zum Einsatz. Damit kann die VM zwischen Interfaces routen.

Als nächstes wird die BGP Rolle installiert. Auch das ist problemlos, die benötigten Abhängigkeiten sind gleich dabei.

Alle folgenden Schritte benötigen die Powershell, was dem geneigten Admin sehr entgegen kommt. Zuerst also einen neuen BGP Router definieren.

PS> Add-BgpRouter -BgpIdentifier <IPAddress> -LocalASN <UInt32>

Man gibt also seine lokales AS Nummer an (zum experimentieren gibt private Nummern) und eine eindeutige IP Adresse, unter der der Router arbeiten wird. Am besten die IP eines Interfaces. Erfreulich ist die Verwendung eines UInt32, der auf die Unterstützung von 4-Byte ASNs hinweist.

Als nächstes kommt dann die Einrichtung eines BGP Peers, also eines Nachbar mit dem diese Maschine reden soll.

PS> Add-BgpPeer [-Name] <String> -LocalIPAddress <IPAddress> -PeerASN <UInt32> -PeerIPAddress <IPAddress>

Der Peer bekommt einen sprechenden Namen unter dem er später erscheinen soll. Des weiteren wird die AS-Nummer des Peers benötigt. Wenn diese die gleiche ist, wie die eigene, handelt es ich um ein i(nternal)BGP, sonst um ein e(xternal)BGP Peer.

Die Kommunikation erfolgt zwischen zwei IP Adressen, die auf beiden Seiten übereinstimmen müssen. I.d.R. werden da IP Adressen genommen, die sich im gleichen Netz befinden und direkt miteinander reden können. Die meisten eBGP Peers erwarten das. bei iBGP ist eine durchaus längere Routingstrecke zwischen den Peers nichts ungewöhnliches.

Cisco als Gegenstelle

Zum Test lasse ich den Server gegen einen normalen Cisco Router arbeiten.

router bgp 15725
 neighbor <WindowsIP> remote-as 65432
 address-family ipv4
  neighbor <WindowsIP> activate
  neighbor <WindowsIP> soft-reconfiguration inbound
  neighbor <WindowsIP> prefix-list from_windows in
  neighbor <WindowsIP> prefix-list to_windows out
 exit-address-family
!
ip prefix-list from_windows seq 5 deny 0.0.0.0/0 le 32
ip prefix-list to_windows seq 1 permit 185.98.236.0/22
ip prefix-list to_windows seq 5 deny 0.0.0.0/0 le 32

Für den Anfang gebe ich nur eine Route raus und nehme nichts an. Sicher ist sicher.

Die BGP Session kommt hoch und das Windows lernt eine Route!

Mit Get-BgpPeer und Get-BgpRouteInformation kann man sich die Ergebnisse ansehen. Die Route taucht sogar in der normalen Routing Tabelle auf.

Auffällig ist aber, dass die Cisco etwas seltsames anzeigt:

  Neighbor capabilities:
    Route refresh: advertised and received(new)
    Four-octets ASN Capability: advertised
    Address family IPv4 Unicast: advertised and received
    Graceful Restart Capability: advertised
    Enhanced Refresh Capability: advertised

Irgendwie fehlt da die 4-Byte ASN Funktionalität. Gleich mal ausprobieren.

PS> Add-BgpPeer test4byte -LocalIPAddress <IPAddress> -PeerASN 199932 -PeerIPAddress <IPAddress>
windows bgp 4byte asn

Oops. Das ist ein KO-Kriterium. Eigentlich müsste ich hier sofort aufhören zu evaluieren.

Full Table

Aber probieren wir mal was anderes. Was tut die Kiste, wenn sie der vollen Internet-Routingtabelle ausgesetzt wird?

(config)#ip prefix-list to_windows seq 2 perm 0.0.0.0/0 le 24

Die CPUs gehen auf Anschlag und bleiben dort eine ganze Weile. Trotzdem bleibt das System benutzbar. Der RAM Bedarf steigt um zwei GByte, was ziemlich okay ist.

Anschließend sind alle Kerne mit ca 50% CPU belastet ohne dass irgendeine Aktivität von außen rein kommt. Seltsam.

Ein Blick in die BGP Routingtabelle mit Get-BgpRouteInformation zeigt, dass alle knapp 750.000 Routen angekommen sind. Allerdings braucht die Maschine unter CPU Volllast für die Ausgabe geschlagene drei Minuten.

Die Überraschung gibt es aber mit dem Blick in die aktive Routing Tabelle:

windows bgp routing missing

Dies ist mit unvollständig nur unzureichend umschrieben.

Real fehlen abertausende Routen wie die folgenden:

B     206.0.0.0/15 [20/0] via 94.135.173.249, 4d15h
B     206.2.0.0/16 [20/0] via 94.135.173.249, 4d15h
B     206.2.76.0/24 [20/0] via 94.135.173.249, 00:36:24
B     206.3.0.0/19 [20/0] via 5.102.160.98, 4w0d
B     206.3.32.0/19 [20/0] via 94.135.173.249, 4d15h
B     206.3.42.0/24 [20/0] via 5.102.160.98, 19:43:50
B     206.3.64.0/18 [20/0] via 94.135.173.249, 4d15h
B     206.3.128.0/17 [20/0] via 94.135.173.249, 4d15h
B     206.4.0.0/14 [20/0] via 94.135.173.249, 4d15h
B     206.5.12.0/22 [20/0] via 94.135.173.249, 4d15h
B     206.8.0.0/14 [20/0] via 94.135.173.249, 4d15h
B     206.8.2.0/24 [20/0] via 94.135.173.249, 4d15h
B     206.8.88.0/24 [20/0] via 5.102.160.98, 19:43:49
B     206.8.120.0/24 [20/0] via 94.135.173.249, 4d15h
B     206.8.121.0/24 [20/0] via 94.135.173.249, 4d15h
B     206.8.122.0/24 [20/0] via 94.135.173.249, 4d15h

Das heißt, dass die Maschine nun nicht in der Lage die Pakete korrekt ans Ziel zuzustellen!

Das ist ein KO-Kriterium. Eigentlich müsste ich hier sofort aufhören zu evaluieren.

Wegräumen

Also nehme ich das Announcement der Routen wieder weg.

(config)#no ip prefix-list to_windows seq 2 perm 0.0.0.0/0 le 24

Nach erfreulich schnellen 40 Sekunden sinkt die CPU Last rapide auf nahe 0. Allerdings sind noch immer tausende von Routen im Kernel aktiv.

Als ich zur Kontrolle mir die BGP Routingtabelle mit Get-BgpRouteInformation anzeigen lassen will, behauptet das System, dass die betreffenden Dienste nicht laufen würden.

windows bgp rras dead

Erst im Ereignislog zeigt sich das ganze Ausmaß der Katastrophe:

windows bgp crash

Es hat so ziemlich jeden laufenden Dienst abgeschossen. Nicht nur Routing, sondern auch die Nutzerverwaltung, die Hardware Treiber etc. pp.

Das ist ein KO-Kriterium. Eigentlich müsste ich hier sofort aufhören zu evaluieren.

Zum Glück starten alle Services von alleine neu und ich kann Screenshots für den Blog machen.

IPv6 teste ich nicht mehr.