Azure und Fragmentation

Diese Seite beschreibt die Fragmentierung von IP Paketen mit Azure VMs, Netzwerken und Load Balancern. Beachten Sie dazu auch die Seiten Maximum Transmission Unit (MTU) und IP Fragmentation.

Auslöser Azure LB

Die Seite ist auch wieder aufgrund eine Problemstellung entstanden, die eine tiefere Beschäftigung mit der Materie erforderlich machte. Diesmal geht es aber nicht um das Problem von Azure VPN und MTU sondern um den Azure Loadbalancer, mit dem Sie eine öffentliche IP-Adresse auf mehrere Server im Backend verteilen können. Aus der Dokumentation geht unter anderem hervor:


https://learn.microsoft.com/en-us/azure/load-balancer/components#limitations

In dem Fall ging es darum, zwei Audiocodes SBCs in Azure hinter einem Loadbalancer per UDP erreichbar zu machen. Ein Netzwerktrace hat aber gezeigt, dass die Pakete gar nicht angekommen sind. Das hat mich dann überrascht, denn ich hätte zumindest das erste Paket erwartet. Also geht es nun wieder in die Tiefen mit Wireshark und Co.

Für das Verständnis der weiteren Informationen sollten sie wissen, was die Maximum Transmission Unit (MTU) ist und wie IP Fragmentation funktioniert.

In den folgenden Kapiteln beschreibe ich die Fragmentierung von UDP-Paketen in Azure für ausgehende, eingehende und über Azure Load Balancer geroutete Pakete, wohlwissend. Beim Einsatz eines UDP-Echo-Servers sind die Ergebnisse natürlich zu relativieren, da die Rückrichtung eine eigene Fragmentierung nutzen kann. Als Gegenstelle habe ich den freien UDP-EchoServer 52.43.121.77 genutzt, der laut DNS ec2-52-43-121-77.us-west-2.compute.amazonaws bei Amazon in den USA gehostet ist. Meine AzureVM war in Europa, so dass dazwischen sicher einige Hops und Millisekunden vergehen.

Wenn ich in der Folge "DF=1" schreibe, dann bedeutet dies, dass das "Don't Fragment"-Bit im Paket gesetzt ist und damit der IP-Stack oder Router auf dem Weg explizit angewiesen werden, das Paket nicht zu fragmentieren

Azure als Client mit DF=1

Ich habe eine kleine "Standard_B2s"-Maschine in einem eigenen Subnetz und RDP-Zugriff für ca. 4ct/Stunde konfiguriert und Wireshark 4.0.2 installiert, um die Pakete zu analysieren. Fragmentiert wird immer, wenn das zu sendende Paket größer ist als die maximale Paketgröße auf dem Übertragungsweg. Meine Azure-VMs haben eigentlich ein Ethernet-Interface und eine MTU von 1500. Ich bin sehr sicher dass ich weder zuhause noch in der Firma mit 50GBit angeben kann.

PS C:\> Get-NetAdapter | ft name,status,linkspeed,mtusize

name     Status LinkSpeed mtusize
----     ------ --------- -------
Ethernet Up     50 Gbps      1500

Die VM selbst hat einen Ethernet-Anschluss mit 1514 Bytes und wenn ich größere UDP-Pakete versende, dann sollten diese nicht nur den PC verlassen sondern auch bei einer anderen Seite ankommen. Ich habe per PowerShell UDP-Pakete gesendet und bei 1473 Bytes Daten kommt mit "DF-Flag" die erwartete Fehlermeldung:

Ein UDP-Paket mit 1472 Bytes ist 1500 Bytes mit IP-Informationen und 1514 Bytes auf dem Ethernet. Mit 1473 Bytes passt das Paket nicht mehr auf das Ethernet und da ich "DF=1" gesetzt hatte, konnte der IP-Stack nur einen Fehler melden.

Interessant war hier, dass nicht schon vorher ein Fehler gekommen ist. Ich interpretiere das so, dass hier wirklich von meine VM bis zur Gegenstell und zurück überall "Ethernet" mit 1514 Bytes und entsprechend einer MTU von 1500 Bytes möglich sind.

Azure als Client mit DF=0

Für den UDP-Test mit "DF=0" brauchte ich keine gültige Gegenstelle, denn jeder Router mit einer kleineren MTU sollte mir eine entsprechende Information zurück senden. Wenn ich den Test mit DF-Flag=0 starte dann kann ich sehen, ab wann mein Client selbst das Paket fragmentiert. Ich kann aber nicht erkennen, wann ein Router auf dem Weg das Paket vielleicht fragmentiert. Um dies zu ermitteln, habe ich mir eine Gegenstelle gesucht, die die Paket wieder zu mir zurück sendet. Das ist zwar auch nicht 100% korrekt, denn auch auf dem Rückweg könnten Router fragmentieren und wieder zusammensetzen. Ich habe aber mal darauf gehofft, dass eine kleinere MTU-Size in der Empfangsrichtung aber zu fragmentierten eingehenden Paketen führt.

Die Firma digi.com betreibt z.B. einen UDP-EchoServer

Der Server stand zum Messzeitpunkt (Dez 2022) in einem Amazon-RZ. Auf dem Weg von meiner AzureVM über die Router und das Internet zu Amazon scheint es also keine Teilstecke zu geben, die eine geringere MTU hat. Allerdings hat mich der Traceroute schon etwas überrascht, weil wohl sehr viele Stationen im Microsoft-Netzwerk durchlaufen werden.

Tracing route to ec2-52-43-121-77.us-west-2.compute.amazonaws.com [52.43.121.77]
over a maximum of 30 hops:

  1     *        *        *     Request timed out.
  2     *        *        *     Request timed out.
  3     *        *        *     Request timed out.
  4     *        *        *     Request timed out.
  5     *        *        *     Request timed out.
  6     *        *        *     Request timed out.
  7   140 ms   139 ms   139 ms  be-148-0.ibr03.ams06.ntwk.msn.net [104.44.32.90]
  8   137 ms   137 ms   140 ms  be-3-0.ibr01.ams30.ntwk.msn.net [104.44.29.214]
  9   139 ms   140 ms   139 ms  be-15-0.ibr01.lon22.ntwk.msn.net [104.44.31.0]
 10   136 ms   136 ms   136 ms  be-10-0.ibr01.nyc30.ntwk.msn.net [104.44.18.152]
 11   136 ms     *        *     be-8-0.ibr03.bl20.ntwk.msn.net [104.44.19.194]
 12     *        *      140 ms  be-10-0.ibr01.bn6.ntwk.msn.net [104.44.30.118]
 13   136 ms   136 ms   136 ms  be-5-0.ibr01.atl30.ntwk.msn.net [104.44.29.179]
 14   139 ms   139 ms   140 ms  be-10-0.ibr01.sn1.ntwk.msn.net [104.44.18.221]
 15   136 ms   136 ms   136 ms  be-6-0.ibr01.phx10.ntwk.msn.net [104.44.16.49]
 16   137 ms   137 ms   136 ms  be-9-0.ibr01.lax30.ntwk.msn.net [104.44.7.43]
 17   139 ms   147 ms   142 ms  ae23-0.ear03.lax30.ntwk.msn.net [104.44.33.172]
 18   140 ms   139 ms   140 ms  104.44.55.74
 19   135 ms   135 ms   135 ms  104.44.57.10
 20     *        *        *     Request timed out.
 21     *        *        *     Request timed out.
 22     *        *        *     Request timed out.
 23     *        *        *     Request timed out.
 24     *        *        *     Request timed out.
 25     *        *        *     Request timed out.

Es ist aber natürlich eine Verbindung von einer VM in Europe West zu einem Amazon-Service in der USA, so dass >100ms schon passend sind und es ist auch gut zu sehen, dass Microsoft den Verkehr erst durch sein eigenes WAN routet. Dennoch sind alle Peerings und Übertragungswege mit einer MTU von 1500 Bytes oder höher ausgestattet.

Basierend auf diesen Informationen und um die Gegenseite nicht zu überlasten und in Wireshark die UDP-Pakete besser auseinanderhalten zu können, habe ich den Paketversand auf 1 Paket alle 2 Skeunden gedrosselt. Der Zeitstempel im folgenden Trace zeigt gut die 2000ms Abstand der Pakete 46,53,55,59,61 etc. Die Antwort auf die Pakete hat dann immer um die 120-170ms gedauert, was bei einem Transatlantik-Verbindung und Verarbeitung der Gegenseite passt. Aber der Trace zeigt noch etwas mehr:

  • Paket 46-62 Das sind normale UDP-Pakete mit 1468 -1472 Bytes die je in einem Paket gesendet und wieder empfangen werden.
  • Paket 63/64
    Das ist das erste UDP-Paket mit 1473, welches in zwei Pakete aufgeteilt wird.
  • Paket 65
    Das ist dann der ebenfalls fragmentierte erste Teil der Antwort aber es fehlt das zweite Paket dazu. Es wurde wohl irgendwo gedroppt
  • Paket 92/93
    Nun sendet meine Azure-VM ein Datenpaket mit 1474 Bytes in zwei Ethernet-Paketen
  • Paket 94
    Auch hier kommt nur das erste ebenfalls fragmentierte Paket aber das zweite Paket geht verloren.
  • Paket 95/96
    Die Azure-VM sendet ein Datenpaket mit 1475 Bytes in zwei Ethernet-Paketen.
  • Paket 97/98
    Diesmal kommen beide Rückantworten durch.
  • Paket 108/109/110
    Ein UDP-Paket mit 1476 Bytes wird gesendet aber die Rückantwort ist unvollständig.

Im Prinzip ist damit schon bewiesen, dass eine Azure-VM mit einer Amazon-AWS-Instanz auch UDP-Pakete über 1472 Bytes übertragen kann. Aber eine solche Häufung von Paketverlusten finde ich schon bemerkenswert. Ich kann leider nicht erkennen, wo genau die Pakete verloren gehen oder ob es an der Fragmentierung selbst liegt. Nicht fragmentierte Pakete kommen fast zu 100% an.

Wenn immer sie vorhaben, UDP zu nutzen und auf die Flusssicherung von TCP zu verzichten, dann sollten Sie Paketverluste bedenken. Bei Audio/Video sind geringe Verluste tolerierbar um überflüssige erneute Übertragungen zu vermeiden aber bei Daten müssen Sie abwägen.

Interessant ist, dass ich ich die Aussagen von Microsoft nicht bestätigen konnte.

The default MTU for Azure VMs is 1,500 bytes. The Azure Virtual Network stack will attempt to fragment a packet at 1,400 Bytes.
Quelle: https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-tcpip-performance-tuning#azure-and-vm-mtu

Wenn das der Fall wäre, dann müssten Pakete mit mehr als 1400 Bytes und gesetztem DF-Flag gar nicht ankommen. Es sei denn der Azure Netzwerkstack würde sich über das DF-Flag hinwegsetzen. Ich habe nun leider keine Gegenstelle zur Hand gehabt, bei der die MTU-Size kleiner war.

Azure Server/Load Balancer

Am Anfang habe ich darüber berichtet dass ich ein Thema beim Azure Loadbalancer analysieren will. Laut Microsoft unterstützt er keine Fragmentierung und das wollte ich besser verstehen. Also habe ich mir ein Lab in Azure aufgebaut, welches aus folgenden Komponenten besteht

  • Mein Client als Paket-Sender
    Auf meinem Desktop habe ich dann mit SendUDP.ps1 entsprechenden Pakete gesendet und lokal ebenfalls per Wireshark geschaut, was das passiert. Hierzu muss man wissen, dass der Client per Ethernet mit einer MTU=1500 angebunden war aber der Internetrouter (Fritz!Box" nach extern nur MTU=1492 nutzen konnte. Meine UDP-Pakete mit 1472 Bytes wurden auf 1464 Bytes verkleinert.
  • Azure Loadbalancer
    Dann habe ich einen einfachen Azure LB mit einer Public-IP angelegt, der per Load Balancing Rule die UDP-Pakete auf Port 5060 auf die private IP-Adresse des Servers weitergeleitet hat. Eine "Inbound NAT-Rule verhält sich nicht anders
  • AzureVM mit Windows 2022 Server und Wireshark
    Das war die gleiche VM, die ich schon bisher genutzt habe

Mit der Umgebung habe ich nun vom Client verschiedene UDP-Pakete zum Server direkt oder über den Loadbalancer gesendet. Interessant ist, welche Pakete beim Zielserver direkt oder über den Loadbalancer angekommen sind und ob diese auf dem Weg zum Ziel fragmentiert wurden und ob ich eine Rückantwort bekommen habe.

Achtung: Throttling
Anscheinend verschluckt Azure gerne mal UDP-Pakete, wenn es zu viele sind oder die Azure Firewall filtert eventuellen Missbrauch. Ohne Drosselung können Sie sehen, dass insbesondere große Pakete dann nur partiell ankommen.

Wenn Sie ein zu großes Paket senden, dann kommt zuerst das große fragmentierte und danach das kleinere Paket an. Das zweie Paket wird aber häufiger verschluckt. In dem Bild sind viel mehr erste fragmentierte Pakete zu sehen. Das zeigt aber auch, dass UDP wirklich nur für Daten genutzt werden sollte, die Verluste akzeptieren.

Meine Testserie enthält sowohl den Versand an den Server als auch den Loadbalancer sowohl mit DF=1 als auch DF=0.

Ziel Don't Fragment Bit Gesendet Angekommen

Server PublicP:5060

$false

1000-1472 OK
1473-1500 Fragmentiert
1000-1472 OK
1473-1500 OK, wenn gedrosselt.

via LB:5060

$false

1000-1472 OK
1473-1500 Fragmentiert
1000-1472
1473-1500 Missing!

Server PublicP:5060

$true

1000-1472 OK
1473-1500 Error, wird nicht gesendet
1000-1472
1473-1500 Missing, Erwartet

via LB:5060

$true

1000-1472 OK
1473-1500 Error, wird nicht gesendet
1000-1472
1473-1500 Missing!, Erwartet

Zuerst sollten Sie einsehen, dass UDP wirklich nicht "gesichert" ist und wenn ich das "DF=0" lasse, werden zu große Pakete beim Sender entsprechend fragmentiert, was erwartet ist. Allerdings kommen die zu großen Pakete beim Server nur an, wenn es nicht zu viele Pakete sind. Werden die Pakete an den Azure Load Balancer IP gesendet, kommen nur die nicht fragmentierten Pakete an. Alle Pakete, die zu groß sind und daher fragmentiert wurden, kommen gar nicht mehr beim Server hinter dem Loadbalancer an. Ich muss aber davon ausgehen, dass die Pakete sehr wohl beim Load Balancer noch ankommen. Der Loadbalancer kann ja sowohl das erste Teilpaket allein am Format erkennen (siehe IP Fragmentation) als auch das letzte Paket ist gut als Teil einer Fragmentgruppe oder eben auch als eigenständig zu erkennen.


Dieses Paket war nicht Teil einer Fragmentgruppe

Aus der Tabelle nehme ich daher folgende Eigenheiten von Azure-Diensten und UDP bei Fragmentierungen mit, wenn Pakete größer als 1500 Bytes werden.

  • LB leitet nur Pakete weiter deren UDP-Payloads 1472 Bytes oder weniger ist
    In Azure scheint ein Ethernet-Frame maximal 1514 Bytes groß zu sein, so dass nach Abzug der Ethernet-Frames ein Paket maximal 1500 Bytes sein darf. Mit dem UDP-Header bleiben damit 1472 Bytes für Nutzdaten übrig.
  • LB routet keine Fragment-Pakete
    Wenn ich als Absender größere Pakete sende und mein IP-Stack diese dann fragmentiert, dann werde diese vom LB komplett verworfen, d.h. sowohl das Fragment als auch das Teil-Paket. Es kommt einfach nichts per UDP an. Ich vermute mal, dass der LB Pakete mit einem "FragmentOffset <>0 " einfach verwerfen.
  • Fragmentierung direkt zum Server geht.
    Wenn ich größere Pakete vom Client direkt zum Server sende, kann dieser auch fragmentierte Pakete annehmen. Ein Hochverfügbarkeitsbetrieb ist so aber nicht möglich, wenn die Pakete per DNS-RoundRobin ohne IP-Failover gesendet werden oder eine Verlusterkennung über z.B. SIP stattfindet.
  • Vermutlich kein "fragmentation needed" vom LB zum Client
    Ich habe auf meinem Client kein "Fragmentation needed" vom Loadbalancer gesehen, was aber verständlich ist, da ich selbst ja mit einem MTU=1500 sende und ausgehend von der Fritz!Box fragmentiert und auch wieder defragmentiert werden dürfte.
  • Azure VM MTU-Size
    Auf dem Server selbst habe ich auch keine fragmentierten Pakete gesehen. Mein Server hat also nie zu Sender ein "Fragmentation needed" gesendet. Das war aber auch nicht zu erwarten.

Natürlich habe ich weiter recherchiert, ob ich vielleicht falsch gemessen oder eine unpassende Konfiguration gewählt habe. Dazu habe ich folgende Aussagen in anderen Quellen gefunden:

The default MTU for Azure VMs is 1,500 bytes. The Azure Virtual Network stack will attempt to fragment a packet at 1,400 bytes.
Quelle https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-tcpip-performance-tuning#azure-and-vm-mtu

Das kann ich so nicht bestätigen. Ich konnte über das Azure Netzwerk problemlos Pakete bis 1500 Bytes  senden, die weder bei mir auf dem Client noch durch den Server fragmentiert wurden und auch mit gesetzem "DF=1"-Flag nicht abgelehnt sondern zugestellt wurden.

In the fragmented packet only the first fragment will be the one having the UDP/IP header in it. The Azure Infrastructure doesn’t have any way of putting these IP fragments back together unless each of them has its own transport-layer header. Because of this is only the first fragmented segment is actually forwarded to the Azure VM behind , therefore breaking the UDP/IP traffic all together.
IP fragmented UDP packets of any length are getting dropped by Azure.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/310f791b-f19e-44a8-b69d-135bbde6e629/ip-fragmented-udp-packets-of-any-length-are-getting-dropped-by-azure

Dies kann ich so nicht bestätigen. Bei mir waren zuerst die als "Fragment" zu erkennenen Pakete zu sehen und das letzte Paket hat dann das Bündel mit Details zu den UDP-Ports abgeschlossen. Aber ich habe sehr wohl gesehen, dass der LB die Pakete nicht durchgereicht hat, wenn Sie fragmentiert wurden.

UDP public Endpoint is supported in Azure. Just wanted to check the size of the UDP payload that you use. If the UDP payload is > 1500 bytes then it will not work. This is by design at the moment as we do not support fragments to public VIP endpoints right now in the vswitch. The recommendation is for customers to keep UDP datagrams to under 1500. There’s really nothing short term we could do to allow fragments through without code changes – they’re fundamentally not supported in the NAT
msrini-MSIT: https://stackoverflow.com/questions/56163872/udp-packets-from-a-field-device-will-not-through-azure-infrastructure-to-my-serv

Diese Aussage entspricht auch meinen Messungen und Auswertungen.

Zum Thema Azure Loadbalancer gab es sogar noch einen Forenbeitrag, der die Problematik etwas weiter beschrieben hat:

As documented in the limitations for Azure Load Balancer here Forwarding IP fragments isn't supported on load-balancing rules. IP fragmentation of UDP and TCP packets isn't supported on load-balancing rules. HA ports load-balancing rules can be used to forward existing IP fragments. You can go through this documentation for additional details on HA ports. If a packet is already fragmented, it's forwarded based on the two-tuple distribution mode when enabled on HA ports load-balancing rules.
Quelle: Azure Internal(Private) Load Balancer does not seem to process/forward the IP Fragmented packets received over UDP Transport https://learn.microsoft.com/en-us/answers/questions/899257/azure-internalprivate-load-balancer-does-not-seem.html

Der Der interessante Satz am Ende sagt, dass bereits fragmentierte Pakete anhand eines "two-tuple distribution mode" geroutet werden.


Quelle: Azure Balancer distribution modes
https://learn.microsoft.com/en-us/azure/load-balancer/distribution-mode-concepts

Entsprechend sollte ich beim Loadbalancer die "Session persistence" von "None" auf "Client IP" umstellen.

Der Wechsel auf "Client IP" ist der bessere Weg. Sowohl bei einem "None" als auch bei einem "Client IP protocol" ist nicht sichergestellt, dass zwei Pakete des gleichen Streams beim gleichen Backend Server landen, denn die beiden Pakete haben ein unterschiedliches Protokoll. Das hilft daher nicht weiter.

Andere Dienste

UDP hat sicher seine Vorteile für RTP-Übertragungen, bei denen ein Retransmit verlorener oder verspäteter Pakete nicht erforderlich ist oder die Pflege einer TCP-Session zu "teuer" ist. UDP funktioniert daher gut mit Audio, Video aber auch NTP, DNS und anderen Protokollen, wo die darauf aufsetzende Applikation damit umgehen kann. Aber speziell bei größeren Datenmengen, die insbesondere auf dem Übertragungskanal noch fragmentiert werden müssen, ist UDP nicht mehr ratsam.

So schreibt Microsoft auf einer Seite zu Kerberos u.a. 

By default, Kerberos uses connectionless UDP datagram packets. Depending on a variety of factors including security identifier (SID) history and group membership, some accounts will have larger Kerberos authentication packet sizes. Depending on the virtual private network (VPN) hardware configuration, these larger packets have to be fragmented when going through a VPN. The problem is caused by fragmentation of these large UDP Kerberos packets. Because UDP is a connectionless protocol, fragmented UDP packets will be dropped if they arrive at the destination out of order.
Quelle: How to force Kerberos to use TCP instead of UDP in Windows  https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/force-kerberos-use-tcp-instead-udp

Zwischenstand

Angefangen hat alles mit einem SIP-Paket per UDP von einem SIP-Trunk Provider, welches durch die SIP-Header größer als 1500 Byte wurde. Der SBC des Providers hat das Paket dann fragmentiert und an den Azure LB gesendet, der die Pakete einfach verschluckt hat. Der SIP-Anruf kam einfach nicht an und das Verhalten mit UDP/TCP-Fragmenten ist von Microsoft auch entsprechend dokumentiert. Dennoch dürften viele dieses Verhalten als "überraschend" bezeichnen.

Natürlich können sie das Problem z.B. durch den Wechsel auf TCP, am besten noch per TLS-Absicherung lösen oder sie tunen den Sender dahingehend, dass er kleinere UDP-Pakete versendet. Aber sie dürfen nicht fragmentiert werden, wenn Sie über den Azure LB geleitet werden.

Damit bestätigt sich nicht nur die Aussage von Microsoft sondern ich habe wieder einmal mehr über Zusammenhänge von TCP/UDP/IP gelernt. Hoffentlich habe ich sie als Leser nicht während der Reise verloren.

Weitere Links