Eng ↴
  • Ger
  • Eng
  • Tag cloud
  • Site map
  • Login Login
    Forgot your password?
Lutz Donnerhacke
Navigation
  • Projekte
  • Veröffentlichungen
  • Blog
  • Impressum
  • GDPR
  • Search

Search

Search for "0" returned 92 matches

Patchwoche in Unternehmen

24/04/2025 7:42 pm Lutz Donnerhacke Tags: Microsoft , Ansible 0

Microsoft veröffentlich am zweiten Dienstag im Monat neue Patches. In größeren Unternehmen wird bei normalen Updates eine Woche auf Unverträglichkeiten getesten und dann die Patches unternehmensweit ausgerollt. In dieser "Patchwoche" sollen auch automatisch Updates ausgerollt werden, in anderen Wochen aber nicht.

Das Problem besteht offensichtlich darin, dass Patches, die auf einem Gerät am Mittwoch in der Patchwoche ausgerollt werden sollen, jeden Monat von Hand mit dem Datum verknüpft werden müssen. So kann der 1. des Monats ein Montag oder Dienstag sein, dann ist der relevante Tag der 16. oder 18. des Monats. Ist der 1. des Monats aber ein Mittwoch, so wäre der relevante Tag am 21. des Monats.

Wie automatisiert man sowas?

Offensichtlich ändert sich an der Situation nichts, wenn der Wochentag und gleichzeitig das Datum erhöht werden: Die gesuchte Woche ist charaterisiert durch die Differenz von Datum und Wochentag.

Diese Differenz bezieht sich auf den Montag (1) als Monatsanfang (1), denn da ist die Differenz 0. Nun soll der Wochenanfang so liegen, als sei der Dienstag der Wochenbeginn. Die Differenz ist für einen Dienstag (2) als Monatsanfang (1) aber -1. Um das wieder auf 0 zu bekommen, muss man diese 1 hinzu addieren.

Um die Woche zu ermitteln teilt man einfach durch 7 und verwirft den Rest. 0 ist dann die erste Woche, 1 die Woche, in der der Microsoft veröffentlicht und 2 die Woche, in der regulär gepatcht werden soll.

Die Formel lautet also: (Monatstag - Wochentag + 1)/7 == 2

Als Shellscript sieht das so aus:

$ cat patchwoche-mache
#! /usr/bin/bash

[ 2 -eq $[$(/usr/bin/date +'(%-d-%u+1)/7')] ] || exit
exec mache "$@"

Nun steht einem generischen Crontab nichts mehr im Wege:

# Daueraufgaben
0 2 * * 1      mache am montag
0 2 * * 2      mache am dienstag
0 2 * * 3      mache am mittwoch
0 2 * * 4      mache am donnerstag
0 2 * * 5      mache am freitag
...
0 2 * * 3      patchwoche-mache am mittwoch
...

Und tut.

Spikes im Grafana mit recoding_rules aus Prometheus

23/09/2022 11:20 pm Lutz Donnerhacke Tags: Cisco , Arista , Juniper , Prometheus , Grafana 0

So langsam muss ich wieder anfangen, über die kleinen Erfolge zu bloggen, die sich in den letzten Jahren ergeben haben. Hier geht es nun um unwillkommene Spikes im Trafficgraphen, wenn dieser sich aus einer generalisierten Metrik erzeugt.

Trafficcounter in heterogenden Netzen kommen aus vielen unterschiedlichen Quellen, die alle ihre eigene Syntax und Labelstruktur aufweisen. Mit sowas will man möglichst schnell nichts mehr zu tun haben. Stattdessen soll eine generalisierte Metrik common:interface:counter die ganzen hässlichen Details wegabstrahieren.

groups:
- name: Leitungen
  rules:
  - record: common:interface:counter
    expr: >-
      sum by (device,port,type,direction,description) (
        label_replace(label_replace(
          sum by (direction,instance,intf,type,description) (
            intfCounter + on(instance,intf) group_left(description)
              (0*label_replace(interfaceDescription,"intf","$1","interface","(.*)"))
          ) or sum by (direction,instance,intf,type) (
            label_replace(intfLagCounter,"type","$1","unnamedLabel4","(.*)")
          ),
          "device","$1","instance","(.*):.*"),
          "port","$1","intf","(.*)")
      or label_replace(label_replace(
          label_replace(label_replace(cisco_interface_receive_bytes,"direction","in","job",".*"),"type","Octets","job",".*") or
          label_replace(label_replace(cisco_interface_transmit_bytes,"direction","out","job",".*"),"type","Octets","job",".*") or
          label_replace(label_replace(cisco_interface_receive_errors,"direction","out","job",".*"),"type","Errors","job",".*") or
          label_replace(label_replace(cisco_interface_transmit_errors,"direction","out","job",".*"),"type","Errors","job",".*") or
          label_replace(label_replace(cisco_interface_receive_drops,"direction","out","job",".*"),"type","Discards","job",".*") or
          label_replace(label_replace(cisco_interface_transmit_drops,"direction","out","job",".*"),"type","Discards","job",".*"),
          "device","$1","target","(.*)"),
          "port","$1","name","(.*)")
      or label_replace(label_replace(
          label_replace(label_replace(junos_interface_receive_bytes,"direction","in","job",".*"),"type","Octets","job",".*") or
          label_replace(label_replace(junos_interface_transmit_bytes,"direction","out","job",".*"),"type","Octets","job",".*") or
          label_replace(label_replace(junos_interface_receive_errors,"direction","out","job",".*"),"type","Errors","job",".*") or
          label_replace(label_replace(junos_interface_transmit_errors,"direction","out","job",".*"),"type","Errors","job",".*") or
          label_replace(label_replace(junos_interface_receive_drops,"direction","out","job",".*"),"type","Discards","job",".*") or
          label_replace(label_replace(junos_interface_transmit_drops,"direction","out","job",".*"),"type","Discards","job",".*") ,
          "device","$1","target","(.*)"),
          "port","$1","name","(.*)")
      ) 

Das sieht zunächst komplett wahnsinnig aus, ist aber relativ schnell erklärt:

Mit or werden Metriken, die eigentlich nichts miteinander zu tun haben, zu einer Metrik vereinigt. Dabei bleiben die Detail-Labels erhalten, der Name der Metrik fällt nur weg. Da ich hinterher eine Metrik mit neuen Label-Bezeichnern haben will, muss die die neuen Label-Namen erzeugen und mit den Werten der alten Labels befüllen. Die Funktion label_replace tut dies: Sie befüllt ein neues Label mit den Treffern des regulären Ausdrucks eines existierenden Labels. Dies kann man besonders schön an den letzten Zeilen sehen, wo das neue Label port mit dem vollen Text des alten Labels name gefüllt wird. So werden also nacheinander die Labels direction, type, device und port befüllt.

Um hinterher nicht mit einem Zoo weiterer Alt-Labels rumlaufen zu müssen, wir per sum by (labels) einfach die "Summe" ohne die unnötigen Labels ausgeführt: Da sie verbleibenden Labels eindeutig sind, ist es eine Summe über jeweils genau einen Eintrag, der einfach die nicht benötigten Labels weg wirft. Das ist ein typisches Idiom in PromQL.

Schwieriger ist der erst Teil, bei dem die Zählerstände zwar schon schön vorliegen, aber die Informationen wie description fehlen. Man muss also hier die Labels aus einer ganz anderen Metrik passend dazu pappen. Das zugehörige Idiom addiert nun also zwei Metriken (unter Berücksichtigung ausgewählter Labels als passend angesehen) und übernimmt das Label description. Damit die Summe aber nichts kaputt macht, multipliziert man die zweite Metrik mit 0.

Auf diese Weise entsteht eine neue Metrik common:interface:counter mit den Labels device, port, type, direction und description, die nun universell einsetzbar ist.

Schaut man sich die Traffiggraphen solcher Metriken an, so haben die häuftige und störende Ausreißer:

Screenshot 2022-09-23 235337
Screenshot 2022-09-23 235455

Beiden Bildern ist anzusehen, wie zittrig oder sprunghaft der Graph ist. Dieser Effekt stört mich seit mindestens einem Jahr und ich hatte ich bisher nicht in den Griff bekommen.

Mwhr durch Zufall kam ich heute dazu, den generierte Metrik und die Originalmetrik mal übereinander zu legen. Das Ergebis war, dass die Metriken fast identisch sind. Jedoch läuft die originale Metrik der generierten Metrik etwas voraus, bis die generierte Metrik den Fehler nach Minuten oder Stunden wieder einholt, dann aber sprunghaft.

Wie kann das sein? Wann wird denn die generierte Metrik erzeugt? Sicher nicht immer dann, wenn irgendeine der Metriken irgendeinen Update bekommt. Nein, das geschieht in regelmäßigen Intervallen durch den Prometheus selbst.

global:
  scrape_interval:     59s
  evaluation_interval: 1m 

Den Werte habe ich so gesetzt, damit er kurz nach neu eintreffenden Werten diese verarbeiten kann und diese nicht verpasst. Im Laufe der Zeit hatten sich dann andere Geräte mit scrape_interval von 29s bis 3m eingefunden. Die Daten kommen also zu sehr unterschiedlichen Zeiten rein.

Aber selbst diese Sekunde Unterschied bedeutet, dass die generierende Metrik jede Stunde (60x aufgerufen) doppelte Daten sieht. Diesen Takt kann man in der gelben Kurve ziemlich gut erkennen. Es kommen dann ja auch noch so Dinge rein, wie Verzögerungen beim Auslesen der Metriken, etc. pp.

Worin besteht also die Lösung? Viel häufiger die Metrik generieren! Das neue evaluation_interval beträgt nun die Hälfte des kleines Scrape-Intervals also 10 Sekunden.

Glücklicherweise ist Prometheus sehr effizient mit dem Abspeichern von "unveränderlichen" Passagen von Metriken. So ist das zumindest kein Plattenfresser.

Achja, ganz rechts im Bild ist die Änderung wirksam. Die Kurven haben aufgehört zu zittern.

Über sieben Brücken musst Du gehn

11/01/2021 9:42 am Lutz Donnerhacke Tags: BSD 0

Das Modul ng_bridge erlaubt es, verschiedene Datenkanäle über die MAC Adressen zu separieren. Dies funktioniert nicht besonders gut, wenn an einer Stelle sehr große Netze mit sehr vielen Teilnehmern angeschlossen sind. Dort möchte man die MAC Adressen nicht lernen. Mit interessanten Konsequenzen.

Problem

Das netgraph Modul ng_bridge verbindet verschiedene Teile des Netgraph-Netzwerkes wie ein Switch in einem Serverraum. Die einzelnen Teile sollen dabei nur die Daten bekommen, die auch für sie gedacht sich. Leider hat das Modul ein paar Schwächen in der Praxis.

  • Zunächst mal konnte das Modul nur mit einer Handvoll Anschlüssen umgehen. Die Limitierung habe ich aufgehoben.
  • Dann lernt das Modul sämtliche MAC Adressen an allen Anschlüssen, was hier ein Problem ist. Auch dafür gibt es eine Lösung.
  • Das Modul behandelt sämtliche Multicast- und Broadcast-Frames gleich: Sie gehen an jeden Anschluss raus. Das ist ein Problem.
  • Aufgrund der inneren Architektur ist das Modul nur single-threaded, was einen begrenzten Datendurchsatz mit sich bringt. Das ist ein weiteres Problem.

Sobald man an einigen Anschlüssen schwachbrüstige Leitungen hängt, treten Überlastungsprobleme auf. Traffic, der dort hin geschickt wird, sollte weitestgehends auch dort hingehen sollen. Traffic, der dort nichts zu suchen hat, sollte gar nicht erst in diese Richtung geschoben werden.

Mit der Einführung von uplink-Ports verringert sich massiv der interne Verwaltungsaufwand. Die MAC-Adressen in Richtung des Uplinks müssen nicht mehr gelernt werden. Und das hat Konsequenzen: Was passiert mit Frames, die an den Uplink geschickt werden müssen?

Eigentlich ist das ganz einfach, denn das Modul kennt die Ziel-MAC ja nicht, schickt es also an alle Links raus, auch an den Uplink. Damit ist der Frame dort, wo er hin soll. Allerdings landet er auch dort, wo er nicht hin soll: Alle anderen Teilnehmer an der Bridge bekommen den kompletten Uplink-Traffic aller ihrer Nachbar zu sehen.

Test

Also teste ich das mal aus. Zunächst wird eine bridge eingerichtet, die drei Downlinks (link1, link2, link3) und zwei Uplinks (uplink1 und uplink2) hat, die alle an virtuellen Ethernet-Interfaces hängen.

ngctl -f- <<END
mkpeer bridge x link10
mkpeer x eiface uplink1 ether
mkpeer x eiface link1 ether
mkpeer x eiface link2 ether
mkpeer x eiface link3 ether
mkpeer x eiface uplink2 ether
END

link10 ist nur temporär zur Erzeugung des gesamten Konstrukts vorhanden, er geht automatisch weg, wenn das Tool ngctl sich beendet

Alle Interfaces werden mit unterschiedlichen MAC Adressen bestückt.

ifconfig ngeth0 ether 00:00:00:00:00:01
ifconfig ngeth1 ether 00:00:00:00:01:01
ifconfig ngeth3 ether 00:00:00:00:02:01
ifconfig ngeth2 ether 00:00:00:00:02:01
ifconfig ngeth3 ether 00:00:00:00:03:01
ifconfig ngeth4 ether 00:00:00:00:04:01

Und dann wird der komplette Datenverkehr mit geschnitten.

tcpdump -eni ngeth0 > bridge.e0 &
tcpdump -eni ngeth1 > bridge.e1 &
tcpdump -eni ngeth2 > bridge.e2 &
tcpdump -eni ngeth3 > bridge.e3 &
tcpdump -eni ngeth4 > bridge.e4 & 

Um nicht jedes Interface in separate Routing-Umgebungen werfen zu müssen, nehme ich einfach für alle Interfaces unterschiedliche IP-Netze. Damit weiß der Kernel, welches Interface ich benutzen möchte.

ifconfig ngeth0 inet 192.168.0.10/24
ifconfig ngeth1 inet 192.168.1.11/24
ifconfig ngeth2 inet 192.168.2.12/24
ifconfig ngeth3 inet 192.168.3.13/24
ifconfig ngeth4 inet 192.168.4.14/24

Und dann muss der Kernel noch wissen, welche MAC Adresse er von welchem Interface aus ansprechen soll. Das erfolgt mit Hilfe von statischen ARP-Einträgen.

arp -s 192.168.0.11 00:00:00:00:01:01
arp -s 192.168.0.12 00:00:00:00:02:01
arp -s 192.168.0.13 00:00:00:00:03:01
arp -s 192.168.0.14 00:00:00:00:04:01
arp -s 192.168.0.20 00:00:00:00:20:00
arp -s 192.168.1.10 00:00:00:00:00:01
arp -s 192.168.1.12 00:00:00:00:02:01
arp -s 192.168.1.13 00:00:00:00:03:01
arp -s 192.168.1.14 00:00:00:00:04:01
arp -s 192.168.1.20 00:00:00:00:20:01
arp -s 192.168.2.10 00:00:00:00:00:01
arp -s 192.168.2.11 00:00:00:00:01:01
arp -s 192.168.2.13 00:00:00:00:03:01
arp -s 192.168.2.14 00:00:00:00:04:01
arp -s 192.168.2.20 00:00:00:00:20:02
arp -s 192.168.3.10 00:00:00:00:00:01
arp -s 192.168.3.11 00:00:00:00:01:01
arp -s 192.168.3.12 00:00:00:00:02:01
arp -s 192.168.3.14 00:00:00:00:04:01
arp -s 192.168.3.20 00:00:00:00:20:03
arp -s 192.168.4.10 00:00:00:00:00:01
arp -s 192.168.4.11 00:00:00:00:01:01
arp -s 192.168.4.12 00:00:00:00:02:01
arp -s 192.168.4.13 00:00:00:00:03:01
arp -s 192.168.4.20 00:00:00:00:20:03

Wie man erkennen kann sind hier mit den 20-er Adressen auch Einträge dabei, deren MACs dem System unbekannt sind. Spreche ich dagegen eine IP an, für die kein statischer ARP Eintrag vor liegt, wird es einen ARP-Broadcast geben. Damit sind alle Fälle abgedeckt.

Und dann kommen die Tests pro Interface:

ping -c 3 -W 1 192.168.0.10
ping -c 3 -W 1 192.168.0.11
ping -c 3 -W 1 192.168.0.12
ping -c 3 -W 1 192.168.0.13
ping -c 3 -W 1 192.168.0.14
ping -c 3 -W 1 192.168.0.20
ping -c 3 -W 1 192.168.0.21
ping -c 3 -W 1 192.168.1.10
ping -c 3 -W 1 192.168.1.11
ping -c 3 -W 1 192.168.1.12
ping -c 3 -W 1 192.168.1.13
ping -c 3 -W 1 192.168.1.14
ping -c 3 -W 1 192.168.1.20
ping -c 3 -W 1 192.168.1.21
ping -c 3 -W 1 192.168.2.10
ping -c 3 -W 1 192.168.2.11
ping -c 3 -W 1 192.168.2.12
ping -c 3 -W 1 192.168.2.13
ping -c 3 -W 1 192.168.2.14
ping -c 3 -W 1 192.168.2.20
ping -c 3 -W 1 192.168.2.21
ping -c 3 -W 1 192.168.3.10
ping -c 3 -W 1 192.168.3.11
ping -c 3 -W 1 192.168.3.12
ping -c 3 -W 1 192.168.3.13
ping -c 3 -W 1 192.168.3.14
ping -c 3 -W 1 192.168.3.20
ping -c 3 -W 1 192.168.3.21
ping -c 3 -W 1 192.168.4.10
ping -c 3 -W 1 192.168.4.11
ping -c 3 -W 1 192.168.4.12
ping -c 3 -W 1 192.168.4.13
ping -c 3 -W 1 192.168.4.14
ping -c 3 -W 1 192.168.4.20
ping -c 3 -W 1 192.168.4.21

Nachdem die Tests durch sind, kann das Testbed weg geworfen werden. Dazu genügt es, die virtuellen Ethernet-Interfaces abzuschalten. Ohne Verbindungen nach außen löscht sich die Bridge von allein.

ngctl shutdown ngeth0:
ngctl shutdown ngeth1:
ngctl shutdown ngeth2:
ngctl shutdown ngeth3:
ngctl shutdown ngeth4:

Mit dem Verschwinden der Interfaces beenden sich auch die Sniffer und schreiben die letzten empfangen Daten noch ins Logfile.

Was kommt dabei raus? Hier ein kleiner Blick in das Logfile von ngeth0:

12:35:07.109300 00:00:00:00:00:01 > 00:00:00:00:01:01, ethertype IPv4 (0x0800), length 98: 192.168.0.10 > 192.168.0.11: ICMP echo request, id 7173, seq 0, length 64
12:35:08.176378 00:00:00:00:00:01 > 00:00:00:00:01:01, ethertype IPv4 (0x0800), length 98: 192.168.0.10 > 192.168.0.11: ICMP echo request, id 7173, seq 1, length 64
12:35:09.197277 00:00:00:00:00:01 > 00:00:00:00:01:01, ethertype IPv4 (0x0800), length 98: 192.168.0.10 > 192.168.0.11: ICMP echo request, id 7173, seq 2, length 64 

Zu sehen sind die ersten drei Pings von 192.168.0.10 an 192.168.0.11. Man erkennt die MAC Adressen aus den statischen ARP Einträgen und natürlich kommt keine Antwort zurück, weil die IP ja absichtlich auf dem Zielinterface nicht konfiguriert wurde.

Im Logfile von ngeth1 finden sich die Gegenstücke:

12:35:07.109336 00:00:00:00:00:01 > 00:00:00:00:01:01, ethertype IPv4 (0x0800), length 98: 192.168.0.10 > 192.168.0.11: ICMP echo request, id 7173, seq 0, length 64
12:35:08.176528 00:00:00:00:00:01 > 00:00:00:00:01:01, ethertype IPv4 (0x0800), length 98: 192.168.0.10 > 192.168.0.11: ICMP echo request, id 7173, seq 1, length 64
12:35:09.197342 00:00:00:00:00:01 > 00:00:00:00:01:01, ethertype IPv4 (0x0800), length 98: 192.168.0.10 > 192.168.0.11: ICMP echo request, id 7173, seq 2, length 64

aber auch die Broadcasts kommen hier an

12:35:23.132078 00:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.0.21 tell 192.168.0.10, length 28
12:35:24.196809 00:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.0.21 tell 192.168.0.10, length 28
12:35:25.268910 00:00:00:00:00:01 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.0.21 tell 192.168.0.10, length 28

Auswertung

Alle Logfiles einzeln durch zu gehen, ist viel zu anstrengend, deswegen gibt es eine Kreuztabelle für jeden Test.

bridge classic 10

Die Tabelle enthält oben links die Quell-IP, in den Spalten die Ziel-IPs und in den Zeilen die Messtelle am jeweiligen Interface. Blau hinterlegt sind die Felder, die in jedem Fall erwartungskonform sind. Entweder wurden die Pakete dort versendet oder sollen dort ankommen.

Es zeigt in der ersten Zeile, dass von Interface mit der IP ...10 (ngeth0) Pakete an 11, 12, 13, 14 sowie an unbekannt (20) und broadcast (21) geschickt und gesehen werden. Pakete an sich selbst sieht das Interface nicht, denn das handelt der Kernel intern ab.

In der zweiten Zeile finden sich Pakete, die von der IP ...10 ausgehen und am Interface ngeth1 (also mit der IP ...11) gesehen werden. Das sind zunächst mal das Paket an das Interface selbst (korrekterweise), die Pakete an die anderen Ziele (12 und 13) sind nicht zu sehen, weil die Bridge die Ziel-MACs kennt. Das Paket an die 14 (einem weiteren Uplink) kennt Bridge ebensowenig wie die unbekannte MAC (20). Bei der 21 gibt es einen Broadcast, der ebenso zu sehen ist.

bridge classic 11

Bei den Paketen von den normalen Links wird es nun bunt.

Gelb hinterlegt sind hier diskussionswürdige Felder, beispielsweise die vom Downlink in Richtung Uplink gehen sollten. Da kein MAC Learning am Uplink stattfindet kann die Bridge nicht wissen, welche Pakete an welchen Uplink raus gehen sollen. Das gilt insbesondere für komplett unbekannte MACs, die ja auch in Richtung des Downlinks liegen könnten, aber noch nicht gelernt wurden.

Rot hinterlegt sind Fälle, die man eigentlich gar nicht haben möchte. Pakete, die vom Downlink in Richtung Uplink gehen, sollen nicht zu anderen Downlinks wieder raus gehen.

In gleicher Weise findet sich das Spiel auch für die anderen Quell-IPs darstellen:

bridge classic 12
bridge classic 13
bridge classic 14

Offenbar ist die Behandlung der unbekannten MACs ein ernsthaftes Problem.

Änderungen

Dank OpenSource ist eine Änderung möglich: Wenn das erste Interface, das an die Bridge gebunden wird, ein uplink-Interface ist, schaltet die Bridge in einen Restrictive Mode. Hier werden die unbekannten MACs nicht mehr an die Downlinks weiter gegeben.

Das Verhalten ist nicht unproblematisch, denn nun können Geräte am Downlink nicht mehr erreicht werden, wenn deren MAC längere Zeit nicht zu sehen war. Das kann besonders bei pausierenden Geräten zu einem Problem werden. Die MAC-Haltezeit sollte also in jedem Fall größer sein als die ARP-Timeouts. So sollten die die MAC-Tabellen ausreichend oft aktualisiert werden.

Alles was sich am Testbed nun ändert, ist der Anfang:

ngctl -f- <<END
mkpeer bridge x uplink10
mkpeer x eiface uplink1 ether
mkpeer x eiface link1 ether
mkpeer x eiface link2 ether
mkpeer x eiface link3 ether
mkpeer x eiface uplink2 ether
END 

Aus link10 wird uplink10. Das ist alles. Aber die Wirkung ist verblüffend:

bridge restrict 11

Alle gelben und roten Felder für die Quelle 11 sind weg. Es werden in Richtung des Downlinks nur noch entweder die bekannten MACs oder die Broadcasts geschickt. Das reduziert den Traffic erheblich.

Hier die Zusammenstellung aller Ergebnisse im restrictive Mode:

bridge restrict 10
bridge restrict 11
bridge restrict 12
bridge restrict 13
bridge restrict 14

Bis auf die Uplinks bekommt kein Link mehr Frames mit unbekannter Ziel-MAC.

Ein weiteres Teilziel geschafft.

Komplikationen in der OSPF Umstellung

29/12/2020 10:01 am Lutz Donnerhacke Tags: IPv6 , Internet 0

Wie angekündigt, wurde in einem Kundennetz das OSPF komplett umgekrempelt. Dabei gab es mehrere Überraschungen, die mit zwei ernsthaften Zwischenfällen einher gingen. Ein Rückblick.

Vorgehensweise

Der geplante Ansatz war, das Routing der BGP-Außenrouter untereinander in eine extra OSPF Instanz zu verschieben und die Default-Route in die jeweilige Standort-Area einzuspeisen, sowie die notwendigen Routen aus der Standort-Area zu lernen.

Dieser Teil des Konzeptes wurde wie folgt geändert:

  • Die BGP-Außenrouter kommen in die Area 0 statt in die Standortareas. Hintergrund ist, dass das Erlernen der (meist statischen) Routen aus einer normalen Area heraus nicht möglich ist, da redistributierte Routen im OSPF area-übergreifend sind.
  • Anstatt die Loopback-IPs der BGP-Außenrouter zwischen zwei OSPF-Instanzen zu redistributieren, wurde die Verbindung der BGP-Außenrouter untereinander in eine extra Area verschoben, deren Intra-Area-Routen gegenüber den anderen OSPF-Routen stets bevorzugt wird. Damit ist die Routing-Trennung erfolgreich durchgeführt.
  • Um die Standorte zu trennen wird auf den Leitungen zwischen den Standorten eine OSPF-Cost von 1000 eingetragen. Lokale verfügbare Routen haben damit eine Metrik von kleiner 1000 und können per "match metric 450 +- 500" in route-maps leicht erkannt werden.

Die Umstellung erfolgt schrittweise:

  1. Die aktuelle Redistribution von OSPF nach BGP wird von Standortinformationen befreit. Alle Routen werden an allen Standorten weiter injeziert. Damit kann man umbauen, ohne die Außenverbindung zu verlieren.
  2. Die Standort-Router wandern zusätzlich in die Area 0, ohne die Standorte direkt miteinander zu verbinden. Auf diese Weise bleibt das Routing über die BGP-Außenrouter bestehen.
  3. Aggregierung verschiebt sich von den BGP-Außenroutern zu den Standort-Routern.
  4. Die BGP-Außenrouter verlieren ihre Verbindung in die jeweilige Standortarea. Sie kommunizieren nun nur noch über die Area 0.
  5. Die für das BGP benötigten Interfaces (Loopback) wandern in eine neue Area,  die aktuell fragmentiert ist. Da über die Area 0 aber nur Interarea-Routen ohne Angabe der originalen Area-Nummer verteilt werden, bekommen die Router weiterhin ihre BGP-Nachbarn erlernt.
  6. Die BGP-Außenrouter werden nach und nach mit extra VLANs zu einer eigenen OSPF-Wolke verbunden. Sie erreichen nun ihre BGP-Nachbarn direkt über diese neue OSPF-Area, nicht mehr über die Area 0.
  7. Die Area 0 kann zwischen den Standorten direkt verbunden werden. BGP-Quertraffic geht ja nun nicht mehr über diese Area.
  8. Die Area 0 zwischen den BGP-Außenroutern wird entfernt. Sie reden nun in der Area 0 nur noch mit den jeweiligen Standort-Routern.
  9. Die Kosten auf der Querverbindung der Standorte werden angehoben. Man kann nun anhand der Metrik die Lokalität der Routen erkennen.
  10. Routen aus dem OSPF werden nun anhand der Metrik gefiltert, so dass der alte Zustand (lokale Routen am Standort announcen) wieder hergestellt ist.

Soweit der Plan. Aber in der Praxis sieht das alles noch etwas anders aus.

Überraschungen

Zunächst einmal ist fest zu stellen, dass der oben stehende Plan sich erst während der Umstellung heraus bildete. Jeder einzelne Schritt wurde in der Praxis so ausgeführt, dass das Monitoring des gesamten Netzes auf sehr feine Details achten konnte. So wurde binnen einer Minute erkannt, wenn mehr als 10 von 70000 versorgten Endgeräten unzureichende Kommunikation hatten.

Um sich nicht selbst abzuschießen, erfolgte der Management-Zugriff bei IPv4 Änderungen über IPv6 und umgekehrt. (Vorzugsweise gibt es Management nur über IPv6.)

Der Ablauf des Umbaus war also folgendermaßen:

  • Vornehmen einer einzelnen Änderung an einem einzelnen Gerät.
  • Beobachten des Monitorings für ca. 10 min.
  • Gibt es einen Abfall, wird die Änderung zurück genommen. Kehrt das Monitoring zu normalen Werten zurück, muss man nachdenken, was man gerade falsch gemacht hat.
  • Bleibt der Abfall bestehen, wird rumtelefoniert, ob jemand anders am Netz ebenfalls Änderungen vornimmt oder ungeplanten Ausfälle erkennbar sind. Ist das der Fall, wird Stabilität im Netz abgewartet und die Änderung nochmal versucht.

Auf diese Weise hat sich der Umbau über fast zwei Wochen hin gezogen. Dabei durften Zwischenstände mit massiv eingeschränkter Performance (wenn der gesamte Traffic nur über eine statt vier Leitungen läuft) nicht über die Hochlastzeiten bestehen bleiben.

Einer der Fälle, die auf diese Weise aufgefallen ist, war: Man trenne die Area0 zwischen den BGP-Routern nicht, bevor es eine direkte Area0 Kopplung der Standorte gibt!

Aber es gab auch schwerere Fälle, die besonders lehrreich sind.

Zum einen hatten wir einen Standort-Router mit einer kaputten OSPF-Datenbank.

Das ist im Normalbetrieb nicht aufgefallen, als aber größere Umstellungen passierten, routete er nicht so, wie man es erwarten sollte. Es gab Kreisrouting und Paketverlust. Nach einem ganzen Tag ständiger Versuche, konnte der Übeltäter eingekreist werden. Ein beherztes "clear ip ospf process" am kommenden Morgen brachte spontane Besserung. Alle bis dahin unerklärlichen Vorfälle waren verschwunden und die Konfigurationsänderungen zeigten die erwünschte Wirkung.

Da nicht alle Geräte zur gleichen Zeit einer Änderung folgen konnten, wurde die Umstellung durch Aufbau von parallelen Verbindungen nach einem neuen VLAN-Konzept durch geführt. Dabei wechseln die beteiligten Geräte einzeln in ein neues VLAN mit neuen IP Adressen. Viele Point-to-Point Verbindungen sind jedoch als "unnumbered" Interface ausgeführt um Adressen zu sparen.

Der interessante Effekt tritt auf, wenn das referenzierte Interface seine iP-Adresse verliert (durch den Umzug auf ein anderes VLAN). In dem Fall bleibt das unnumbered Interface im OSPF aktiv in der Area. Es verbreitet weiter Routing-Informationen und spricht mit den OSPF Nachbarn. Sollen dann aber Datenpakete über dieses Interface geschickt werden, so verwirft der Cisco-L3-Switch diese Pakete kommentarlos. Das unnumbered Interface wird zum aktiven Blackhole. Erst, als das Interface auf shutdown gesetzt wurde, war der Spuk vorbei.

Weil's so wichtig ist, hier nochmal das Rezept:

  • Man erzeuge ein VLAN zwischen zwei Geräten, dass "ip unnumbered xxx" als Point-2-Point Interface  auf beiden Seiten eingerichtet wird.
  • Man nehme das Interface XXX und damit das Vlan-Interface per "network"-statement ins OSPF auf.
  • Man etabliere eine OSPF Kopplung über das VLAN-Interface zum Nachbarn.
  • Nun lösche man die IP Adresse vom Interface XXX mit "no ip address".
  • Das Vlan-Interface verbleibt voll funktional im OSPF: Es hält Nachbarschaftsbeziehungen und Routing-Updates aufrecht.
  • Ein- und ausgehende Datenpakete über das Interface werden verworfen: Black Hole.

Die von dem Verhalten ausgehenden Störungen waren sporadisch, aber merkbar. Es hat ziemlich lange gedauert, das Phänomen zu verstehen und gezielt in Angriff zu nehmen. Auslöser der Erkenntnis war dann eine Fehlereingrenzung auf einen deutlich merkbaren, stabilen Stör-Pfad, bei dem Interfaces stückweise shutdown genommen wurden.

Und dann war da noch der wirklich heftige Ausfall, der einen Standort komplett erdete. An diesem Standort gab es noch Überreste einer älteren NSSA, die nun wirklich weg geräumt werden sollte. Aber wie wechselt man bei einem Gerät, das unter Last steht, die OSPF Area? In dem Fall handelt es sich um die Anycast-DNS Server, die mit vier externen Beinen sowohl ihre DNS Anfragen machen, als auch ihre OSPF-Uplinks bedienen. Die Interfaces auszuschalten, kam wegen der DNS-Nutzung nicht in Frage.  So kam die Idee auf, diese Maschinen in zwei regionale Areas gleichzeitig zu stellen.

Grundsätzlich ist es möglich, einen Router in zwei Areas zu haben, auch wenn keine davon die Area 0 ist. Dies wird ausführlich in verschiedenen Design Dokumenten von OSPF diskutiert, und zwar wird dabei betont, dass dieser Router keine Routen von der einen in die andere Area weiter leiten wird. Das wäre ja sogar ein gewünschtes Verhalten. Die Software bird auf FreeBSD fand die Idee auch erstmal ganz gut und importierte aus beiden Areas die Routen.

Allerdings injezierte sie die nun widersprüchlichen Ziele für die exteren Routen nicht in die Kernel-Routing-Tabelle. Auf diese Weise verloren die DNS-Server ihre Default Route und konnten keine neuen DNS Namen mehr auflösen. Das fiel auf die Schnelle natürlich nicht auf, dann aber um so heftiger. Ohne DNS brach die Internet-Versorgung des Standorts zusammen.

Der Fehler war nach einer guten Viertelstunde behoben und der alte Zustand wieder her gestellt. Bis sich der Rest beruhigte, dauerte es noch ein wenig länger. Die eigentliche Lösung bestand dann darin, mit einem Schlag die Area zu wechseln. Das klappte dann problemlos.

Nicht ganz so optimale Wege durch OSPF

07/12/2020 10:17 pm Lutz Donnerhacke Tags: 0

Ein Kundennetz ist im Laufe der Zeit organisch gewachsen. Irgendwann kommt dann der Augenblick, wo man es in verschiedene Standorte trennen muss. Denn es gibt ungeplante Überlastungssituationen.

Ausgangslage

standort ospf 1

Das Netz besteht aus einigen Au0enroutern an den Rändern und innen aus einer Reihe von Layer3-Switchen. L3-Switche sind Geräte, die genauso schnell routen wie switchen können, allerdings nur mit einer beschränkten Anzahl von Routen, weil sonst die Hardwarekosten explodieren.

Seit einiger Zeit sind einige der Geräte an einen anderen Standort umgezogen. Nun kann man nicht mehr bei Bedarf einfach wild Querverbindungen zwischen den Geräten patchen. Es gibt drei Leitungen zwischen den Standorten sowie separate Uplinks und das war es.

Geroutet wird per OSPF. Streng nach Design Guide gibt es zwei lokale Areas, in denen Ziele bevorzugt direkt erreichen werden können. Und dazwischen gibt es eine Area 0, den Backbone, der alles aufnimmt, was in den einzelen Areas nicht zu finden ist.

Konsequenterweise ist die Area 0, also die Resterampe des Routings, die Quelle der Default Route, auch das Heim der BGP Router mit Außenanbindung. Diese kennen so viele Routen, dass man diese den Switchen keinesfalls  erzählen darf, sollen die nicht spontan an Herzdrücken versterben. So gesehen, sind die Außenrouter eigentlich in der Mitte des OSPF Protokolls.

standort ospf 2

Die Area 0, der Backbone, darf nicht mit seinen externem Routen die L3-Switche berühren. Denn diese würden die Pakete per Default Route einfach nur wieder zurück zum lokalen BGP Router schicken, und der wieder ... ein Routing-Loop. Deswegen besteht die Area 0 eigentlich aus switchübergreifenden VLANs, in denen die BGP-Router je ein Bein stehen haben.

standort ospf 3

Für das OSPF sieht es also so aus, als gäbe es drei LAN-Segmente, die die Router direkt miteinander verbinden.

In der Realität ist dem allerdings nicht so, denn die OSPF-Idee eines LAN-Segmentes kommt noch aus der Zeit des echten Ethernets, wo alle Geräte im wahrsten Sinne an einem gemeinsam genutzten Strang hängen. Dabei ist es komplett egal, wer mit wem redet, das Medium ist für alle gleich teuer in der Benutzung.

In der Realität der heutigen, strukturierten Ethernet-Verkablung sieht es allerdings ganz anders aus. Hier wandert ein Datenpaket zwischen zwei Routern in Form eines Ethernet-Frames mehrfach über verschiedene Leitungen, oft sogar mehrfach über die gleiche physikalische Strippe.

standort ospf 4

Eine solche Mehrfachbenutzung ist bei begrenzten Ressourcen ein Problem. Konkret aufgefallen ist es als sieben Gbps über mehrere Stunden transportiert werden mussten. Dann blieb auf den 10G-Leitungen der Router kaum noch was für anderen Traffic frei.

Lösungen

Die einfachste Lösung ist, neue Technik zu kaufen, die um den Faktor 4 oder 10 höhere Leitungskapazitäten anbietet. Leider versackte die Bestellung und wird auch so schnell nicht realisiert werden.

Eine zweite Lösung wäre, die Standorte als eigenständige BGP Inseln zu betreiben.

standort ospf 7

Das hält den Traffic lokal und der Datenverkehr zwischen den Bereichen lässt sich feingranular steuern. Selbst Loadbalancing über mehrere Wege ist kein Problem. Zusätzlich reizt an dem Ansatz, dass die extern sichtbaren Routen schon an der Stelle fertig im BGP erzeugt werden können, an den sie entstehen.

Leider fehlt für die Benutzung von BGP die Lizenz auf den L3-Switchen. Deren Beschaffung ist auch nicht so einfach, denn auch hier hängen Bestellprozesse dran.

Bleibt also nur der Versuch, die Datenflüsse innerhalb des OSPF selbst neu zu sortieren. Dabei gibt es einige grundlegende Konzepte:

  • Die Area0 dient als Bindeglied zwischen den anderen Area.
  • Traffic, der in einer Area zugestellt werden kann, verlässt diese Area nicht (intra-Area first).
  • Traffic, den sich die BGP-Router gegenseitig zustellen, darf mit keinen anderen Routern in Berührung kommen.
  • Traffic, der nicht extern zugestellt werden soll, soll schnellstmöglich an die L3-Switche übergeben werden.

Damit ergibt sich, dass das gesamte Netz auf den Kopf zu stellen ist:

  • Die Area 0 kommt ganz nach innen, sie bildet die Standortkopplung.
  • Pro Standort gibt es ein eigne Area, die den Traffic am Standort fest hält.
  • Die BGP-Außenrouter gehören zwar zum Standort, den sie bedienen, kommunizieren aber untereinander über eine komplett eigene Struktur.
standort ospf 5

Die Interaktion zwischen den Außenroutern und der jeweiligen Area besteht grundsätzlich darin, dass die BGP-Router eine Default-Route in die lokale Area injizieren und aus der Area lernen, welche Netze in beiden Standorten bedient werden können.

standort ospf 6

Das Injizieren der Default-Route durch die BGP-Router in die jeweilge lokale Area ist kein Problem. Die BGP-Router werden somit zu ASBRs. Im Gegenzug werden alle Geräte am Standort zu einem lokal nahe gelegenen BGP-Router routen. Traffic, der das Netz verlässt, tut es am gleichen Standort.

Umgekehrt lernen die BGP-Router die internen Routen aus dem OSPF und können sie ins BGP redistributieren. Dabei setzen sie Communities etc. pp. Dies sind vor allem die extern sichtbaren Aggregates des Autonomen Systems. Erzeugt man die Aggragate (per Null-Route) lokale auf dem BGP-Router, besteht die Gefahr des Blackholings, insbesondere wenn der BGP-Router keine funktionierende Verbindung nach innen hat.

In der rot gepunkteten Area tauschen die BGP-Router untereinander ihre für die BGP relevanten Loopback-Adressen aus. Über diese sprechen sie internal BGP miteinander. Es sind die Next-Hop Adressen der externen Ziele, die ein BGP-Router anderswo los werden muss. Dabei gibt es einiges zu beachten:

  • Die vom internen BGP erzeugten Routen haben als Next-Hop eine (Loopback-)IP die im rot gepunkteten Bereich auftaucht. Diese IPs dürfen keinesfalls durch das interne OSPF hindurch erreichbar sein, weil das zu Routing-Loops für externe Ziele führt.
  • Die Loopback-IPs der BGP-Router sollen aus dem internen Bereich erreichbar sein,  auch wenn sie von einer internen Sammel-(Aggregate)-Route überdeckt werden.
  • Bestimmte externe (Eyeball-)Ziele sollen auch standortübergreifend auf dem jeweils besten Weg erreicht werden. Diese Ziele sollen direkt zum zuständigen BGP-Router gelangen, nicht erst eine extra Runde im rot gepunkteten Bereich laufen müssen.

Um diese Anforderungen zu erreichen, muss man zwischen verschiedenen Routing-Protokollen redistributieren. Gleichzeitig, darf die rot gepunktete Area nicht einfach Bestandteil der gleichen OSPF Instanz sein, die auch die internen Areas bedient. Zwischen zwei nicht-backbone Areas, die am gleichen Router anliegen erfolgt kein Austausch von Routen, deswegen würden die Loopback-IPs nie in den internen Bereich gelangen. Mit zwei OSPF Instanzen geht das aber problemlos.

Die BGP-Router haben nur vier verschiedene Quellen für Routing-Informationen:

  • externes BGP
  • OSPF zwischen den BGP-Routern
  • OSPF im internen Netz
  • internes BGP

Externes BGP kann aufgrund der Eingangsfilter keine netzinternen Routen enthalten. Dieser Teil ist für (interne) Routingprobleme also irrelevant.

OSPF zwischen den BGP-Routern muss immer zuerst berücksichtigt werden. Egal welche Metriken eine Route hier hat, es ist ein BGP-Peer und damit auf diesem Weg zu erreichen. Anderenfalls gibt es Routingloops für externe Ziele.

Wenn ein anderes internes Ziel erreicht werden soll, dann ist das immer unmittelbar an die inneren Router abzugeben. Anderenfalls gibt es das eingangs skizzierte Problem der Überlastung von Leitungen.

Interne Ziele, die über iBGP (von anderen BGP-Routern gelernt und redistributiert) erreichbar sind, müssen zwar an externe Partner übermittelt werden, damit das Netz extern erreichbar bleibt. Traffic an interne Ziele sollten aber nur dann an andere BGP-Nachbarn geroutet werden, wenn man diese Ziele nicht über interne Wege erreichen konnte.

Es beitet sich also an, für das OSPF zwischen den BGP-Routern eine eigene Instanz zu betreiben. Aus dieser Instanz kann in die andere OSPF-Instranz redistributiert werden. Und diese wird mit einer niedrigeren administrativen Distanz versehen:

Routingquelle Standard Benutzt
Lokal angeschlossenes Netz 0 0
Lokale statische Route 1 1
externes BGP 20 20
OSPF zwischen BGP-Routern 110 105
OSPF im internen Netz 110 110
internes BGP 200 200

Und nun bleibt die Aufgabe, dieses Umstellung im laufenden Betrieb vor zu nehmen.

TCAM full - Überraschender Totalausfall

07/11/2020 9:52 am Lutz Donnerhacke Tags: IPv6 , Internet , Cisco , WTF , Broadband , Multicast , QoS , ARP 5

Gestern gegen Mittag gab es bei einem Kunden eine großen Ausfall, drei zentrale Switche (von sechs) haben beinahe gleichzeitig nicht mehr ordnungsgemäß funktioniert. Es folgte stundenlange eine Odyssee, denn nach einem Reboot eines Switches hatte man knapp eine halbe Stunde bis er wieder ausfiel.

Ausgangslage

Nichts geht mehr. Spontan. Ohne Vorwarnung.

Alle Geräte, die hinter der Ausfallstelle sich befinden sind nicht mehr erreichbar. Kunden, Infrastruktur, Monitoring. Da auch DNS Server betroffen sind, gestaltet sich jeder Zugriff extrem zäh. Man wartet also teilweise eine Minute, bis man die Ergebnisse eines Kommandos zu Gesicht bekommt.

Eigentlich ist Redundanz gegeben: Wenn ein Gerät ausfällt, übernimmt ein anderes. Jetzt ist es aber anders. denn drei Geräte sind zeitgleich ausgefallen. Diese dienen dem Großteil der Infrastruktur als Uplink, was die Diagnosemöglichkeiten drastisch einschränkt.

In der Annahme, es handle sich um ein Problem, dass von einer defekten Komponente (z.B. ein Layer2-Loop) an einer Stelle eingespeist wird, wurde zunächst systematisch versucht, diese Stelle einzukreisen. Erfolglos.

Es blieb nichts weiter übrig, als einem der betroffenen Switche das Chassis stromlos zu machen, um Zugriff auf das Gerät zu bekommen und damit mehr Informationen zu erlangen. Vor einem solchen Hart-Reset scheut man normalerweise zurück, weil der komplette Reboot knapp 20 Minuten dauert.

Der Zugriff auf das frisch gebootet Gerät zeigt ... keine Auffälligkeiten. Das ist schlecht. Sehr schlecht. Denn damit gibt es keinen Ansatzpunkt, wo der Fehler her kommen könnte. Nach wenigen Minuten friert jedoch die Verbindung zum Gerät ein und es geht wieder nichts mehr.

In einer der Phasen gelingt es, Zugriff auf das Monitoring zu erlangen, dort findet sich ein erster Hinweis:

Nov  6 12:36:41 172.27.44.252 %C4K_L3HWFORWARDING-4-TCAMFULL: FLC Tcam full, packets will be forwarded in software at reduced rate.  Failure due to: add tcam space failed
Nov  6 12:38:41 172.27.44.254 %C4K_L3HWFORWARDING-4-TCAMFULL: FLC Tcam full, packets will be forwarded in software at reduced rate.  Failure due to: add tcam space failed
Nov  6 12:52:12 172.27.44.253 %C4K_L3HWFORWARDING-4-TCAMFULL: FLC Tcam full, packets will be forwarded in software at reduced rate.  Failure due to: add tcam space failed

Oops. Die Meldung besagt, dass eine Hardware-Komponente voll gelaufen ist und nun die gesamte Funktion des Gerätes durch Software emuliert werden muss. Dabei die die CPU des Gerätes für eine solche Last gar nicht ausgelegt, sie soll eigentlich nur die Hardware konfigurieren, so dass die Datenströme die CPU selbst nie treffen.

Das erklärt einige der Effekte: Zum einen fällt das Gerät selbst nicht aus, so dass die Umschaltung auf die Redundanztechnik nicht stattfinden kann. Zum anderen erfüllt es seine Funktion nicht mehr ausreichend. Wir haben Paketverluste von 99%, was einer Unbenutzbarkeit gleich kommt.

TCAM - ein teurer Spaß

Was ist nun dieses ominöse TCAM? TCAM ist im Endeffekt eine Hardware, in der Key-Value-Paare abgelegt werden können. Man kann direkt nach dem Schlüssel (Key) suchen und bekommt in einem Takt das Ergebnis aus dem TCAM ausgelesen. Das ist natürlich irre schnell.

Diese gute Einführung in die Funktion des TCAM enthält das schöne Funktionsbild:

cam-architecture

Auf einem TCAM Chip sind also Speicherzellen, die parallel die anliegende Datenanfrage mit den abgelegten Werten vergleichen und – wenn alles überein stimmt – die komplette Zeile aktivieren. Kann der Vergleicher nur 0/1, spricht man von CAM – Content Addressable Memory. Kann der Vergleicher auch den anliegenden Wert ignorieren, also einen dritten Zustand abbilden, so spricht man von TCAM – Tenary CAM.

Normalerweise müsste eine CPU den normalen Speicher nach dem Suchwert durchkämmen, um das Ergebnis zu ermitteln. Je nach Algorithmus dauert das, logarithmisches Wachstum der Suchzeit ist aber typisch. Dagegen hat der TCAM eine konstante Suchzeit von eins, das Ergebnis ist instantan verfügbar.

TCAM macht also die Geräte schnell, jedoch auch teuer. Deswegen versucht man den TCAM so effektiv wie möglich zu nutzen. Dabei trifft der Hersteller eine Reihe von Kompromissen.

Ein Teil des TCAM wird für QoS, ein Teil für ACLs, ein Teil für Layer2 (MAC Adressen), ein Teil für Layer3 (Routing), etc. pp, benutzt.

Wer hat meinen TCAM belegt?

Die Meldung im Log deutet darauf hin, dass der Layer3, also der Routing-Teil zu voll wird.

Das Datenblatt des Geräts schreibt:

  • 128,000 Flexible NetFlow entries in hardware
  • 64,000/32,000 IPv4/IPv6 routing entries for campus access and aggregation deployments
  • IPv6 in hardware, providing wire-rate forwarding for IPv6 networks and support for dual stack with innovative resource usage
  • Dynamic hardware forwarding-table allocations for ease of IPv4-to-IPv6 migration
  • Scalable routing (IPv4, IPv6, and multicast) tables, Layer 2 tables, and access-control-list (ACL) and quality-of-service (QoS) entries to make use of 8 queues per port and comprehensive security policies per port

All diese Punkte haben mit TCAM Kapazitäten zu tun.

Wir haben in dem Netz ca. 2500 IP4 Routen (meist für Kunden mit statischen IPs) und ca. 8000 IPv6 Routen (hauptsächlich IPv6-PD). Das liegt weit unter den Limits.

Wir haben pro Gerät höchstens 35k MAC Adressen, das haben wir aber schon lange im Blick und sorgen dafür, dass diese Werte nicht in kritische Bereiche ansteigen.

Wir haben NetFlow aktiv. Das ist verzichtbar, also wird das abgeschaltet. Keine Besserung.

Wir haben Multipath-Routing aktiv, um Datenpakete über mehrere Leitungswege zu verteilen. Das ist meist auch verzichtbar und wird dort abgeschaltet. Keine Besserung.

Wir haben anti-spoofing ACLs aktiv, die schon wichtig sind. Aber vor der Wahl – gar keine Funktion oder Bösewichter aufhalten – fällt die Entscheidung leicht, besonders, da wir es bisher nicht mit Bösewichtern zu tun hatten und es noch weitere Filter tiefer im Netz gibt. Allerdings auch wieder keine Besserung.

Was bleibt denn noch?

Schauen wir uns das TCAM für Layer3 mal genauer an:

#sh platform hardware ip route summary

block#  start   end     mode    entries used    free    group   type
0       80 Bit  0       4095    4096    4096    0       3       Dst
1       160 Bit 4096    8190    2048    104     1944    5       Dst
2       160 Bit 8192    12286   2048    439     1609    4       Dst
3       80 Bit  12288   16383   4096    4096    0       3       Dst
4       80 Bit  16384   20479   4096    4096    0       3       Dst
5       80 Bit  20480   24575   4096    4095    1       3       Dst
6       80 Bit  24576   28671   4096    4096    0       3       Dst
7       80 Bit  28672   32767   4096    4094    2       3       Dst
8       80 Bit  32768   36863   4096    4096    0       3       Dst
9       80 Bit  36864   40959   4096    3454    642     3       Dst
10      Unused  40960   45055   4096    0       4096    -       -
11      Unused  45056   49151   4096    0       4096    -       -
12      Unused  49152   53247   4096    0       4096    -       -
13      Unused  53248   57343   4096    0       4096    -       -
14      Unused  57344   61439   4096    0       4096    -       -
15      Unused  61440   65535   4096    0       4096    -       -

Das TCAM ist in 16 Blöcke unterteilt, von denen noch sechs Blöcke frei sind. Die anderen Blöcke sind entweder mit 80bit oder 160bit Schlüsselbreite konfiguriert und verschiedenen Funktionsgruppen zugeordnet. Sie liefern Ziele (vermutlich für Routingentscheidungen).

Auffällig ist, dass die 160bit Blöcke nur die Hälfte der Einträge fassen können, wie die 80bit Blöcke. Es ist jedoch nur logisch, da für die Schlüssel doppelt so viel Platz brauchen.

group#  inUse   mode    type      lookup  entries free    util%   rangeId
0       yes     80 Bit  uRPF Ipv4 Src     0       0       100     0
1       yes     160 Bit uRPF Ipv6 Src     0       0       100     1
2       yes     160 Bit SpecSrc   Src     0       0       100     255
3       yes     80 Bit  UC Ipv4   Dst     32768   645     98      0
4       yes     160 Bit MC Ipv4   Dst     2048    1609    21      1
5       yes     160 Bit UC Ipv6   Dst     2048    1944    5       2
6       yes     320 Bit MC Ipv6   Dst     0       0       100     3
7       yes     160 Bit SpecDst   Dst     0       0       100     255
8       yes     160 Bit OtherL3   Otr     0       0       100     4

        range
0       [ipv4: 0.0.0.0 - ipv4: 255.255.255.255]
1       [ipv6: :: - ipv6: FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]
2       [null]
3       [ipv4: 0.0.0.0 - ipv4: 223.255.255.255]
4       [ipv4: 224.0.0.0 - ipv4: 239.255.255.255]
5       [ipv6: :: - ipv6: FEFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]
6       [ipv6: FF00:: - ipv6: FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]
7       [null]
8       [ipv4: 0.0.0.0 - ipv6: FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]

Hier haben wir die Verwendungszwecke der TCAM Blöcke. Für reverse Path Filtering (das jetzt abgeschaltet ist) wird also die Source-Adresse als Schlüssel verwendet. Für IPv4/v6 Routing wird die Zieladresse als Schlüssel benutzt. Soweit so wenig überraschend.

Eher überraschend ist, dass Multicast doppelte Breite benötigt. Multicast Routing funktioniert anhand der Zielgruppe (einer IP) und der Quell-IP (einer IP), es ist also doch keine Überraschung.

Was Spec und OtherL3 ist, weiß ich nicht. Das kommt hier schlicht nicht vor.

        blocks
0       (0)
1       (0)
2       (0)
3       (8)  0, 3, 4, 5, 6, 7, 8, 9
4       (1)  2
5       (1)  1
6       (0)
7       (0)
8       (0)

Auch die Zuordung der logischen Gruppen zu den physikalischen Blöcken ist nachvollziehbar. Es ist genau das, was bei der Auflistung der Blöcke schon stand.

entity        total     used      free      util%
Entries       61440     32639     28801     53
  uRPF Ipv4   0         0         0         0
  uRPF Ipv6   0         0         0         0
  UC Ipv4     32768     32091     677       97
  MC Ipv4     2048      438       1610      21
  UC Ipv6     2048      110       1938      5
  MC Ipv6     0         0         0         0
  SpecDst     0         0         0         0
  SpecSrc     0         0         0         0
  OtherL3     0         0         0         0
  unused      24576     24576     0         100

Diese Zusammenfassung zeigt die relevanten Kenngrößen.

  • Von den (80bit)-Einheiten des TCAM sind 53% belegt.
  • IPv4 Routing hat 32091 Einträge aktiv, damit sind von den aktuell allozierten Blöcken 97% belegt.
  • IPv4 Multicast hast 438 Einträge, was von dem einzigen allozierten Block 21% ausmacht.
  • IPv6 Routing belegt mit 110 Einträgen in dem einzigen allozierten Block 5%

Wesentlich ist hier fest zu halten, das die Aussage im Datenblatt etwas euphemistisch formuliert wurde. Das TCAM wird für alle Protokolle gemeinsam benutzt, dabei passen maximal 32k IPv6 Routen rein, wenn nichts anderes auf der Maschine läuft. 64k IPv4 Routen passen nur, wenn nichts anderes damit gemacht wird. Sobald IPv4 und IPv6 oder Multicast oder uRPF gemischt werden, sinken die entsprechenden freien Blöcke rasant.

Die relevante Kenntgröße zur Überwachung ist also die Prozentzahl rechts oben (53%) oder noch besser die Zahl der freien Blöcke (die man sich als unused/4096 berechnen muss).

Gestern sah das anders aus. Wir hatten über 20000 Einträge bei IPv6 Routing und dazu noch die uRPF Belegungen. Damit war der der TCAM erschöpft.

Weiter oben sprach ich von ca 2500 IPv4 Routen, hier stehen nun 32091. Warum? Mit IPv6 war es gestern ähnlich, warum?

#sh ip route summary
IP routing table name is default (0x0)
IP routing table maximum-paths is 8
Route Source    Networks    Subnets     Replicates  Overhead    Memory (bytes)
application     0           0           0           0           0
connected       0           50          0           3640        9200
static          2           525         0           40264       96968
ospf 1          2           2084        0           150192      392168
  Intra-area: 156 Inter-area: 68 External-1: 1565 External-2: 0
  NSSA External-1: 297 NSSA External-2: 0
internal        18                                              108332
Total           22          2659        0           194096      606668

Die 2681 passen gar nicht mit den 32091 zusammen. Was ist da los?

Der Catalyst routet nicht wie ein normaler Router, sondern ist ein Layer3-Switch. Er benutzt das TCAM, um in einem einzelnen Schritt, das Ziel eines eingehenden Paketes zu ermitteln. Das Verfahren nennt sich Cisco Express Forwarding und ist der Grund dafür, dass die Geräte ebenso schnell switchen wie routen.

#sh ip cef summary
IPv4 CEF is enabled for distributed and running
VRF Default
 32097 prefixes (32093/4 fwd/non-fwd)
 Table id 0x0
 Database epoch:        2 (32097 entries at this epoch)

Das passt sehr gut (die Werte sind dynamisch und der TCAM Wert zeigt nun 32124). Aber was enthält CEF nun genau?

# sh ip cef
Prefix               Next Hop             Interface
5.102.167.0/26       192.168.255.18       Vlan29
5.102.167.128/29     192.168.255.18       Vlan29
10.10.10.10/32       217.17.207.163       Vlan115
10.100.0.0/16        attached             Vlan142
10.100.0.0/32        receive              Vlan142
10.100.0.1/32        receive
10.100.0.3/32        receive              Vlan142
10.100.0.4/32        attached             Vlan142
10.100.0.5/32        attached             Vlan142
10.100.1.1/32        attached             Vlan142
10.100.1.2/32        attached             Vlan142
10.100.1.3/32        attached             Vlan142
10.100.1.7/32        attached             Vlan142
10.100.1.9/32        attached             Vlan142
...

Es enthält die Routing-Einträge (wie erwartet) und alle Nachbarschaftseinträge (unerwartet). Damit ist mit einem Lookup im TCAM sofort ein neuer Layer2-Header verfügbar, der an einem bestimmten Interface raus geblasen werden kann. Er verweist entweder direkt auf die MAC Adresse des Zieles oder auf die MAC Adresse des nächsten Routers.

Dazu kommt, dass diese CEF hier distriuted ist, d.h. die verschiedenen eingesteckten Line-Cards haben ihr eigenes TCAM, dass analog befüllt wurde. Im Idealfall verläßt also eine eingehendes Paket nicht mal die Linecard sondern geht direkt auf einem anderen Port der gleichen Karte wieder raus.

Da diese Einträge im CEF auch die MAC Adressen enthalten, haben sie direkt mit den ARP Tabellen zu tun.

#sh arp summary
Total number of entries in the ARP table: 29979.
Total number of Dynamic ARP entries: 29800.
Total number of Incomplete ARP entries: 143.
Total number of Interface ARP entries: 36.
Total number of Static ARP entries: 0.
Total number of Alias ARP entries: 0.
Total number of Simple Application ARP entries: 0.
Total number of Application Alias ARP entries: 0.
Total number of Application Timer ARP entries: 0.

Das passt also sehr gut zusammen.

Und wieder ist die Dokumentation zumindest ungenau. Es ist also nur dann 64k IPv4 Routen möglich, wenn die Ziele alle zu wenigen externen Geräte weiter geleitet werden müssen. Sobald es verschiedene Ziele für eine Route gibt, reicht das nicht mehr aus. Im Extremfall kann man also höchstens ein fast voll besetztes /16 lokal anbinden und schon geht gar nichts mehr.

Bei IPv6 halbieren sich die erreichbaren Zahlen. Bei Multicast noch einmal.

Ich bin solche Mogel-Aussagen von Cisco nicht gewöhnt. Ich bin ehrlichweise etwas ungehalten.

Lösung

Was nun tun?

Von den Kisten muss zuerst einmal das IPv6 runter. Große Layer2-Segmente mit SLAAC füllen das TCAM so schnell, dass es irgendwann platzt. Deswegen waren auch nur Geräte betroffen, die zwei solche Anbindungen versorgen. Mittlerweile ist die IPv6 Adoption der Endgeräte so weit angestiegen, dass es die TCAM Grenzen gerissen hat. Natürlich ist das allein kein Grund, dass alle drei Geräte gleichzeitig betroffen waren, sie waren aber alle in anderen Konstellationen kurz vor der Grenze und haben deswegen einen weiteren Anstieg anderswo im Netz nicht verkraftet.

IPv6 ist aktuell kein Vertragsbestandteil beim Kunden, deswegen muss für die IPv6-Versorgung eine andere Lösung gefunden werden, die nicht am TCAM knabbert. Wie genau die aussehen wird, ist noch nicht klar.

Nach einem beherzten "no ipv6 nd prefix ..." und einem "no ipv6 address 2a01:..../64" auf den dicken Kunden-Leitungen, ist Ruhe eingekehrt.

Und dabei bleibt es jetzt erst einmal.

Bleibt noch die Frage, ob sich das System selbst heilen kann. Was passiert also, wenn die TCAM-Blöcke nicht mehr von der zugeordneten Gruppe benötigt werden?

block#  start   end     mode    entries used    free    group   type
0       80 Bit  0       4095    4096    4096    0       3       Dst
1       160 Bit 4096    8190    2048    56      1992    5       Dst
2       160 Bit 8192    12286   2048    195     1853    4       Dst
3       80 Bit  12288   16383   4096    4094    2       3       Dst
4       80 Bit  16384   20479   4096    4096    0       3       Dst
5       80 Bit  20480   24575   4096    4092    4       3       Dst
6       80 Bit  24576   28671   4096    4019    77      3       Dst
7       160 Bit 28672   32766   2048    0       2048    5       Dst
8       80 Bit  32768   36863   4096    3383    713     3       Dst
9       160 Bit 36864   40958   2048    1       2047    5       Dst
10      160 Bit 40960   45054   2048    3       2045    5       Dst
11      160 Bit 45056   49150   2048    1       2047    5       Dst
12      160 Bit 49152   53246   2048    2       2046    5       Dst
13      Unused  53248   57343   4096    0       4096    -       -
14      Unused  57344   61439   4096    0       4096    -       -
15      Unused  61440   65535   4096    0       4096    -       -

Ganz einfach. Sie sind verloren.

Block 7 wurde für IPv6 Routing allokiert und nun nicht mehr benötigt. Er wird aber nicht als unbenutzt zurück gegeben. Er ist verloren. Ebenso wie die anderen Blöcke 9 bis 12.

Die einzige Möglichkeit TCAM wieder frei zu bekommen, ist also ein Reboot des Geräts. Das kann man aber planen.

Auch für die Kennzahlen der Überwachung ist dieser Zustand lehrreich:

entity        total     used      free      util%
Entries       51200     24004     27196     46
  uRPF Ipv4   0         0         0         0
  uRPF Ipv6   0         0         0         0
  UC Ipv4     24576     23767     809       96
  MC Ipv4     2048      174       1874      8
  UC Ipv6     12288     63        12225     0
  MC Ipv6     0         0         0         0
  SpecDst     0         0         0         0
  SpecSrc     0         0         0         0
  OtherL3     0         0         0         0
  unused      12288     12288     0         100

Mit 46% frei schaut es doch gar nicht so schlecht aus, oder? Aber es sind nur die Hälfte der Blöcke frei, wie beim anderen, weniger belasteten, Gerät.

Wie man mit tcpdump auf doppelt getagged Pakete prüft

20/02/2020 4:03 pm Lutz Donnerhacke Tags: BSD 0

Für einen bpf Filter benötige ich die Möglichkeit mehrfach mit VLANs getaggte Pakete zu erkennen und in deren Inhalt nach protokollspezifischen Werten zu durchsuchen. Natürlich möchte ich die binären Regeln nicht komplett von Hand schreiben.

Der einfache Ansatz ist sich das Regelwerk durch tcpdump selbst erzeugen zu lassen.

# tcpdump -s 0 -p -d 'ip and udp and src port 12345'
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 10
(002) ldb      [23]
(003) jeq      #0x11            jt 4    jf 10
(004) ldh      [20]
(005) jset     #0x1fff          jt 10   jf 6
(006) ldxb     4*([14]&0xf)
(007) ldh      [x + 14]
(008) jeq      #0x3039          jt 9    jf 10
(009) ret      #262144
(010) ret      #0

Dieser Code macht folgendes:

  • Im Ethernet-Header (an Position 12) wird der Ethertype ermittelt.
  • Ist der IPv4 (0800), so geht's bei 2 weiter, anderenfalls Abbruch zu 10.
  • Im IP-Header (an Position 23 aus Sicht des kompletten Frames) steht die Protokollnummer.
  • Ist diese UDP (17 = 0x11), geht's weiter.
  • Nach dem variabel langen IP-Header (Länge in 32bit Worten an Position 14) folgt der UDP Header.
  • Dort steht an (der variablen) Position die Portnummer.
  • Entspricht die dem gewünschten Wert, gibt's einen positiven Rückgabewert (der i.d.R. die Länge der zu exahierenden Daten entspricht).
  • Anderenfalls gibt es den Fehlercode 0 zurück (oder auch 0 verwertbare Bytes).

Oder direkt binär, so wie ich es brauche:

# tcpdump -s 0 -p -ddd 'ip and udp and src port 12345'
11
40 0 0 12
21 0 8 2048
48 0 0 23
21 0 6 17
40 0 0 20
69 4 0 8191
177 0 0 14
72 0 0 14
21 0 1 12345
6 0 0 262144
6 0 0 0 

Dieser BPF Code nimmt an, dass das Paket direkt mit IP beginnt, es gibt keine VLAN Frames (die jeweils 4 Byte kosten).

Ich lönnte jetzt also an allen Stellen, wo auf eine Position im Paket Bezug genommen wird, einen entsprechenden Offset manuell hinzufügen. Dieses manuelle Nachpatchen ist jedoch nicht sonderlich wartungsfreundlich.

Nach einigem Suchen fand ich den undokumentierten Befehl vlan. Wenn man den vor den Ausdruck stellt, passt das tcpdump den BPF-Filter passend an.

# root@a10nsp:~ # tcpdump -s 0 -p -d 'vlan 123 and ip and udp and src port 12345'
(000) ldh      [12]
(001) jeq      #0x8100          jt 4    jf 2
(002) jeq      #0x88a8          jt 4    jf 3
(003) jeq      #0x9100          jt 4    jf 17
(004) ldh      [14]
(005) and      #0xfff
(006) jeq      #0x7b            jt 7    jf 17
(007) ldh      [16]
(008) jeq      #0x800           jt 9    jf 17
(009) ldb      [27]
(010) jeq      #0x11            jt 11   jf 17
(011) ldh      [24]
(012) jset     #0x1fff          jt 17   jf 13
(013) ldxb     4*([18]&0xf)
(014) ldh      [x + 18]
(015) jeq      #0x3039          jt 16   jf 17
(016) ret      #262144
(017) ret      #0 

Man sieht sehr schön, dass die Offsets für die Paketanalyse schön um vier Bytes verschoben wurden.

Gehen auch zwei VLANs?

# tcpdump -s 0 -p -d 'vlan 123 and vlan 456 and ip and udp and src port 12345'
(000) ldh      [12]
(001) jeq      #0x8100          jt 4    jf 2
(002) jeq      #0x88a8          jt 4    jf 3
(003) jeq      #0x9100          jt 4    jf 24
(004) ldh      [14]
(005) and      #0xfff
(006) jeq      #0x7b            jt 7    jf 24
(007) ldh      [16]
(008) jeq      #0x8100          jt 11   jf 9
(009) jeq      #0x88a8          jt 11   jf 10
(010) jeq      #0x9100          jt 11   jf 24
(011) ldh      [18]
(012) and      #0xfff
(013) jeq      #0x1c8           jt 14   jf 24
(014) ldh      [20]
(015) jeq      #0x800           jt 16   jf 24
(016) ldb      [31]
(017) jeq      #0x11            jt 18   jf 24
(018) ldh      [28]
(019) jset     #0x1fff          jt 24   jf 20
(020) ldxb     4*([22]&0xf)
(021) ldh      [x + 22]
(022) jeq      #0x3039          jt 23   jf 24
(023) ret      #262144
(024) ret      #0

Hervorragend: Nun werden doppelt getaggte Pakete ausgewertet. Allerdings muss man die VLAN Tags exakt kennen.

Ich möchte aber nur double tagged VLANs mit variablen VLAN-Nummern bearbeiten, kenne diese also nicht. Wie kann man das beschreiben? Vielleicht als Negation? Nicht VLAN 123? Gibt es eine VLAN Nummer die sicher nicht auftreten kann?

ja, die Null. Eine VLAN Header mit der VLAN-ID 0 ist definiert als untagged, gestattet aber QoS Parameter. Untagged will ich aber nicht, das ist also okay.

# tcpdump -s 0 -p -d 'not vlan 0 and not vlan 0 and ip and udp and src port 12345'
(000) ldh      [12]
(001) jeq      #0x8100          jt 4    jf 2
(002) jeq      #0x88a8          jt 4    jf 3
(003) jeq      #0x9100          jt 4    jf 6
(004) ldh      [14]
(005) jset     #0xfff           jt 6    jf 22
(006) ldh      [16]
(007) jeq      #0x8100          jt 10   jf 8
(008) jeq      #0x88a8          jt 10   jf 9
(009) jeq      #0x9100          jt 10   jf 12
(010) ldh      [18]
(011) jset     #0xfff           jt 12   jf 22
(012) ldh      [20]
(013) jeq      #0x800           jt 14   jf 22
(014) ldb      [31]
(015) jeq      #0x11            jt 16   jf 22
(016) ldh      [28]
(017) jset     #0x1fff          jt 22   jf 18
(018) ldxb     4*([22]&0xf)
(019) ldh      [x + 22]
(020) jeq      #0x3039          jt 21   jf 22
(021) ret      #262144
(022) ret      #0

Und tut!

Aufspaltung eines RAID

14/06/2019 9:08 am Lutz Donnerhacke Tags: Linux , Boot , Verfügbarkeit 0

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.

Groundhog Day with SMB remount

13/02/2019 5:36 pm Lutz Donnerhacke Tags: Linux , WTF , Microsoft 5

A customer complained about network problems. An SMB/CIFS mount would always break down between two of his servers. Not that there were any noticeable problems, but there are still some error messages in the log.

Setup

The customer has a certain number of servers in his Layer2 segment. One of them is a Windows file server for a group of Windows computers. Another is the Linux monitoring system, which regularly tries to access the share.

The whole thing has a longer history, which led half a year ago to the recommendation to abandon SMB1. This part is not relevant, it is what followed the recommendation.
Since the transition, the monitoring Linux has had strange entries in the kernel log:

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

Where do these 120 seconds come from? The man page does help:

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.

That explains the 120 seconds: Every minute an SMB echo is sent and if no echo is answered twice, the connection is believed to be dead. Therefore, the customer (understandably) thinks that there would be packet loss in the network.

To prove this assumption wrong takes weeks. For this purpose, the data traffic is recorded on both sides and put next to each other packet by packet.

Unfortunately, this still doesn't solve the real problem.

Analysis

Again and again such a recording is examined, in order to discover any unusual phenomenon.

smb-tcp-flow

It's awesome to see each minute the keep-alive request-response game is happening. Then data is sent.

The communication comes to a sudden pause and the time-out stated in the log occurs. Between 19:05:01.99... and 19:07:02.25... there are a little more than 120 seconds. So far that seems to fit.

The transmission error (the one with the TCP keep-alive) is remarkable, because it hits the server directly before the connection is terminated. This must be investigated!

Why are the lines black? Because the keep-alive sends exactly one byte over again, which has been acked for a long time. It is a very unusual conversation. Wireshark colours such sequence errors in black.

  • At 19:05:01.949511 128 bytes are received.
  • At 19:05:01.997203 the reception of these 128 bytes is confirmed.
  • At 19:07:01.999481 the final of the 128 bytes is transmitted again (together with the keep-alive flag).

If the acknowledgement of the data reception (ACK) had not arrived, the entire 128 bytes would be sent again. But they are not.

Did a firewall gamble on the sequence numbers underway? But there is no firewall in between, both servers are in the same LAN and see each other directly. But the other side verifies that the acknowledgement has arrived completely.

So why should the kernel resend a single byte? It turns out that the Windows implementation of the TCP keepalive sends exactly one byte again, while the Linux implementation sends the keepalive without payload. This is apparently an adjustment of Windows to broken middleware, which drops TCP packets without payload.

So the part is fine. But what is it then?

Maybe there are some delays somewhere? So let's take a look at the round-trip times. Maybe there are outliers.

smb-tcp-rtt

There are no particular irregularities with the remote location. Everything looks nice.

smb-tcp-rtt2

Even the local processing is completely inconspicuous: Everything that the kernel can process directly is done in almost no time. If the application software is involved, it takes longer. One can see very clearly how regular the measurements are.

Nothing is striking here either. But what is it then?

A closer look reveals that an echo request should have been sent at 19:06:00. But that is missing!

Kernel Archaeology

The function cifs_echo_request is responsible for sending the echo requests in the Linux kernel. There it says:

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);
}

The code is interesting in several ways:

  • On the one hand, an echo is sent only if there exists a real need to do so.
  • On the other hand, this is a daisy chain scheduling: Only after work has been done the next action is planned.

As far as the needs are concerned, no echoes are sent out as long as the last response was received earlier than one echo interval.

But what is a response? In the code, the server→lstrp is always set to the current time whenever a packet is received. Regardless wether it is an echo response or normal data.

Subtracting HZ (one second) is a countermeasure preventing the echo response to your own request from being interpreted as interesting traffic. So this hard-coded value is calculated assuming that the answer from the remote station comes always more quickly than in a second.

If such an SMB connection runs over a longer distance or against a slower server, every second echo request will be suppressed if no further traffic occurs. This directly leads to the fact that SMB only works in the LAN.

Due to the protocol architecture, all requests are processed sequentially. If an echo request is sent while another request is running on the server, the echo request will not be processed until the previous request has been completed. Asynchronous processing was abandoned with the transition from SMB1 to SMB2. Now the server should tell the client that a more complex request is still being processed.

In this case an answer was received at 19:05:01.91... from the server. The echo packet should have been sent around 19:06:00. That's a very precise mistake!

The second problem is the scheduling. Only when all the work has been done (sending the echo packet) the next echo is scheduled to be sent 60 seconds later. The entire processing time is missing from the planning. This means that the echo packets are not sent exactly after 60 seconds, but always a bit later.

The recording shows this very clearly: 19:01:55.05, 19:02:56.49, 19:03:57.94, 19:04:59.37, 19:06:00.90, 19:07:02.27. The intervals are about 61.5 seconds. So 1.5 seconds more than planned. In the last step the interval is shorter, because the echo packet was not sent.

Racecondition

But what really happens is explained quickly: Suppressing the planned echo request is faulty.

In detail:

smb-tcp-timing
  • Daisy chaining scheduling creates a gap.
  • If traffic comes unfortunate shortly after the last echo, the planned echo is suppressed.
  • Due to the gap, the next echo transmission is delayed until after the timeout, which is set hard to twice the echo interval.

It is particularly charming to realize that the final echo request is first sent and then the time-out strikes, because the first action when receiving the echo response is to check for a time-out and terminate it. Ironically, the successful echo triggered the abort.

Now the question arises since when the problem occurred and who is responsible for it:

  • The patch c740 will send permanently echo requests every 60 seconds if no other traffic occurred. (Jan 11, 2011)
  • The patch fda3 all these echoes are used to trigger a timeout at five times the interval (configurable) without traffic. (Jan 20, 2011)
  • The patch 6dae switches from the configurable 60s x variable(5) to variable(60s) x two. (Feb 21, 2012)

The gap and suppression was introduced on 11 January 2011. However, this has no effect, as the time-out starts at five times the interval.

With the changeover on 21 February 2012, the gap now takes effect, as the time-out was set hard to twice the interval.

Solutions

There are three options.

First, eliminate the gap by scheduling at fixed times (always add 60 seconds to the last schedule time). By the way, it is not enough to move the rescheduling to the beginning of the routine. This only shortens the gap, but it will not disappear.

Another possibility is to send the echo requests in any case regardless of other traffic. In other words, if traffic is already running, the additional request will not bother any more. There is a potential risk that a server will choke on an echo between other requests.

Furthermore, avoiding the entire problem by waiting at least three times the interval length seems plausible.

If you want a quick fix, you should choose the third approach: Change a 2 to a 3 in the code:

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;
}

All that remains is the question of the TCP keepalive. Windows will wait exactly 120s until it sends a keepalive on a TCP session. Note that the Windows server sends the TCP keepalive first, because it transmitted the last packet before it arrived at Linux.

Acknowledgement

The entire analysis was done by my colleague Jens, who just asked me to write things together. This is what I did here.

The only thing left to do now is to send a bug report to the right place.

Microsofts BGP Router im Internet

25/01/2019 4:57 pm Lutz Donnerhacke Tags: Internet , WTF , BGP , Microsoft , Windows 2

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.

Next » 1 2 3 4 5 6 7 ...10