Azure VPN und MTU

Wir nutzen natürlich Server und Dienste in Azure und haben dazu unser LAN über die Firewall mit einem IPSec-Tunnel zu einem Azure-Netzwerk verbunden. Eigentlich hat alles funktioniert aber dann gab es doch immer mal die ein oder andere Unterbrechungen. Webseiten wurden nicht ausgeliefert, RDP war nicht immer möglich. Das Fehlerbild wechselte und nach einiger Forschung haben wir das MTU Discovery dingfest gemacht. Doch lest selbst und beachtet dazu auch die Seiten Maximum Transmission Unit (MTU) und IP Fragmentation und Azure Fragmentation

Erste Analyse

Dass es immer mal irgendwo stockt, haben wir schon gemerkt aber es gehörte auch das Glück dazu, dass ein Client immer wieder das Problem hatte, so dass wir mit Wireshark auf Client und Server die Pakete untersuchen konnten. So haben wir bei einer HTTPS-Verbindung erkannt, dass der Server in Azure Pakete erneut sendet aber nie eine Antwort des Clients sieht.

Interessanterweise kamen aber auch kein "ICMP MTU Exceeded" Meldungen zurück. Wir haben dann von der AzureVm einfach PING-Pakete mit unterschiedlichen Größen gesendet.

ping 192.168.102.12 -f -l <paket>

Da Antwortverhalten dazu war aber dann doch wieder komisch. Dazu habe ich auch den Wireshark-Trace:

Paketgröße Wireshark Pakete Antwort Interpretation

1394

9/11

Reply from 192.168.102.12: bytes=1394 time=33ms TTL=126

Der erste Ping mit 1394 Bytes wird vom Server versendet und von der Gegenstelle auch beantwortet. Die Antwort ist leider kleiner, so dass ich die MTU-Size in Gegenrichtung so gar nicht testen kann. Das ist schon mal komisch, denn normalerweise enthält die Rückantwort die gleichen Daten.

1395

26

Request timed out.

Ein Paket mit 1395 Bytes geht auch noch auf die Reise aber es kommt keine Antwort an. Es verschwindet einfach in einem schwarzen Loch. Auf der Gegenseite habe ich das Paket aber nicht gefunden. Es ist also auf der Übertragung zum Ziel verloren gegangen, ohne dass ein System eine Meldung gesendet hat. Das ist natürlich sehr schlecht.

1396

37/39

Packet needs to be fragmented but DF set.

Diese Paket ist noch ein Byte größer aber wird aktiv von der Gegenstelle abgelehnt.

Kleine Pakete kommen durch und größer werden verworfen aber das ein Paket nicht ankommt, ist schon komisch.

MTU Check gegen die Route

Natürlich wollte ich wissen, wo der Fehler ist und haben dann per PING die weiteren Teilstecken analysiert.

Wieder ist PING meine Wahl aber da einige Stationen nicht auf ein PING reagieren, habe ich mir mit einem TTL Expired geholfen, indem ich den TTL reduziert habe. So kann man bei konformen Geräte auch eine Route samt Latenzzeit messen.

ping 192.168.102.12  -f -l 1393 -n 1 -i 1

Ein "TTL Expired" bedeutet indirekt, dass mein Paket zumindest bis dorthin gekommen ist.

Paketgröße TTL=1 (172.17.0.4) TTL=2-19 (?) TTL=20 (192.168.102.12)

1372

TTL Expired

Timeout

Success

1373

TTL Expired

Timeout

Fragmentation needed

1394

TTL Expired

Timeout

Fragmentation needed

1395

TTL Expired

Timeout

Fragmentation needed

1396

Fragmentation needed

Fragmentation needed

Fragmentation needed

Die Tabelle liefert erste Hinweise, denn das Default Gateway, was ich in Azure nicht mal per PING erreichen kann, verrät sich aber schon durch den "Fragmentation needed". Allerdings habe ich erst mit einem TTL=20 wieder eine Antwort bekommen. Die Verbindung sieht also wie folgt aus:

Jede Teilstrecke kann abweichende maximale MTUs haben und das kann sich bei Änderungen der Route sogar dynamisch wieder ändern. Daher ist es ja so wichtig, dass die ICMP-Statusmessages durchgelassen werden.

Bitte keine ICMP-Pakete blockieren, die "ICMP Fragmentation needed" melden!

Weiter habe ich diese Verbindung nicht analysiert. Eine Abhilfe könnte es ja sein, die MTU auf dem Server zu reduzieren, so dass er generell keine zu großen Pakete sendet. Für IPSec empfiehlt z.B. Cisco eine MTU-Size von 1400 Bytes.


Quelle: https://www.cisco.com/c/en/us/support/docs/ip/generic-routing-encapsulation-gre/25885-pmtud-ipfrag.html

Hierzu schreibt Microsoft auch:

Azure and VM MTU
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
Note that the Virtual Network stack isn't inherently inefficient because it fragments packets at 1,400 bytes even though VMs have an MTU of 1,500.
A large percentage of network packets are much smaller than 1,400 or 1,500 bytes.
Quelle: https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-tcpip-performance-tuning#azure-and-vm-mtu

Effektive Größe ermitteln

Da die MTU-Size in jede Richtung individuell ermittelt wird und eine Blockade von "ICMP not reachable" eine Erkennung blockiert, kann man nur auf der einen Seite Pakete senden und auf der anderen Seite sehen, ob diese ankommen. Ein test per ICMP funktioniert leider nicht, da das "Antwortpaket" von der Gegenseite ja genauso groß ist.

Ich müsste also ein System finden, mit dem ich ein großes Paket auf die Reise sende aber die Gegenseite mit einem kleinen Paket antwortet, was auf jeden Fall wieder zurück kommt. Leider kenne ich keine eingebaute Funktion, weswegen ich mir dann wieder mit Wireshark behelfe:

  • Sender schickt Pakete mit der Größe
    Das kann ein "PING -f -l 1500 <ipadresse>" sein, damit
  • Empfänger schaut mit Wireshark nach
    Dort sollte ich die eingehenden ICMP-Pakete finden und wenn sie ausbleiben, dann war es wohl zu groß

Um das schnell zu ermitteln, kann ich das per Powershell recht einfach per Loop machen. Mit etwas Glück habe ich auch die Rückantwort und den ICMP not Reachable

PS C:\> 1460..1500 | %{Test-Connection -Ping -IPv4 192.168.178.1 -BufferSize $_ -DontFragment -Count 1}

   Destination: 192.168.178.1

Ping Source           Address                   Latency BufferSize Status
                                                   (ms)        (B)
---- ------           -------                   ------- ---------- ------
   1 FC-T480S         192.168.178.1                   1       1460 Success
   1 FC-T480S         192.168.178.1                   1       1461 Success
   1 FC-T480S         192.168.178.1                   1       1462 Success
   1 FC-T480S         192.168.178.1                   1       1463 Success
   1 FC-T480S         192.168.178.1                   1       1464 Success
   1 FC-T480S         192.168.178.1                   1       1465 Success
   1 FC-T480S         192.168.178.1                   1       1466 Success
   1 FC-T480S         192.168.178.1                   1       1467 Success
   1 FC-T480S         192.168.178.1                   1       1468 Success
   1 FC-T480S         192.168.178.1                   1       1469 Success
   1 FC-T480S         192.168.178.1                   1       1470 Success
   1 FC-T480S         192.168.178.1                   1       1471 Success
   1 FC-T480S         192.168.178.1                   1       1472 Success
   1 FC-T480S         *                               0       1473 PacketTooBig
   1 FC-T480S         *                               0       1474 PacketTooBig
   1 FC-T480S         *                               0       1475 PacketTooBig
   1 FC-T480S         *                               0       1476 PacketTooBig
   1 FC-T480S         *                               0       1477 PacketTooBig
   1 FC-T480S         *                               0       1478 PacketTooBig
   1 FC-T480S         *                               0       1479 PacketTooBig
   1 FC-T480S         *                               0       1480 PacketTooBig
   1 FC-T480S         *                               0       1481 PacketTooBig
   1 FC-T480S         *                               0       1482 PacketTooBig
   1 FC-T480S         *                               0       1483 PacketTooBig
   1 FC-T480S         *                               0       1484 PacketTooBig

Das geht auch über mehrere Hops hinweg z.B. zu outlook.office365.com.

PS C:\> 1460..1500 | %{Test-Connection -Ping -IPv4 outlook.office365.com  -BufferSize $_ -DontFragment -Count 1}

   Destination: outlook.office365.com

Ping Source           Address                   Latency BufferSize Status
                                                   (ms)        (B)
---- ------           -------                   ------- ---------- ------
   1 FC-T480S         52.97.201.82                   12       1460 Success
   1 FC-T480S         52.97.201.82                   12       1461 Success
   1 FC-T480S         52.97.201.82                   13       1462 Success
   1 FC-T480S         52.97.201.82                   12       1463 Success
   1 FC-T480S         52.97.201.82                   13       1464 Success
   1 FC-T480S         52.97.201.82                   13       1465 Success
   1 FC-T480S         52.97.201.82                   12       1466 Success
   1 FC-T480S         52.97.201.82                   12       1467 Success
   1 FC-T480S         52.97.201.82                   12       1468 Success
   1 FC-T480S         52.97.201.82                   13       1469 Success
   1 FC-T480S         52.97.201.82                   12       1470 Success
   1 FC-T480S         52.97.201.82                   12       1471 Success
   1 FC-T480S         52.97.201.82                   13       1472 Success
   1 FC-T480S         *                               0       1473 PacketTooBig
   1 FC-T480S         *                               0       1474 PacketTooBig
   1 FC-T480S         *                               0       1475 PacketTooBig
   1 FC-T480S         *                               0       1476 PacketTooBig
   1 FC-T480S         *                               0       1477 PacketTooBig

In diesen beiden Fällen ist nun alles in Ordnung und ich bekomme ein ICMP PacketToBig zurück. Wenn das Paket aber einfach so verloren geht, dann sehe ich "nur" einen Timeout ohne mehr zu wissen oder es kann ja sein, dass der Rückkanal nur in der Größe beschränkt ist nicht funktioniert. In dem Fall bekomme ich dann "nur" einen Timeout.

PS C:\> 1460..1500 | %{Test-Connection -Ping -IPv4 192.168.180.100 -BufferSize $_ -DontFragment -Count 1}

   Destination: 192.168.178.100

Ping Source           Address                   Latency BufferSize Status
                                                   (ms)        (B)
---- ------           -------                   ------- ---------- ------
   1 FC-T480S         52.97.201.82                   12       1460 Success
   1 FC-T480S         52.97.201.82                   13       1461 Success
   1 FC-T480S         *                               0       1462 DestinationHost…
   1 FC-T480S         *                               0       1463 DestinationHost…

Da kann ich dann raten, ob das Paket auf der Gegenseite angekommen ist oder die Rückantwort zu groß war. Dann ist der Blick mit Wireshark auf der Empfängerseite eine Option.

Natürlich könnte ich mir auch selbst einen Service schreiben, der Pakete annimmt und eine kleine Antwort zurückliefert.

MTU und Azure

Wenn aber MTU-Discovery und ICMP funktioniert, müssten sich die Partner auch ohne Hilfe auf dem IPSec-Tunnel auf eine MTU-Size einigen können. Ich habe also erst einmal die Azure-Dokumentationen durchforstet und einige interessante Aussagen 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

... The default MTU used on Azure VMs, and the default setting on most network devices globally, is 1,500 bytes...
Quelle: TCP/IP performance tuning for Azure VMs https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-tcpip-performance-tuning

... We don't encourage customers to increase VM MTUs. ...
Quelle: TCP/IP performance tuning for Azure VMs https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-tcpip-performance-tuning

For Azure, we recommend that you set TCP MSS clamping to 1,350 bytes and tunnel interface MTU to 1,400
Quelle: TCP/IP performance tuning for Azure VMs
https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-tcpip-performance-tuning

We don't recommend that Azure customers change the default MTU value on virtual machines.
Quelle: TCP/IP performance tuning for Azure VMs
https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-tcpip-performance-tuning

Alle Aussagen kann ich verstehen und passen auch auf meine Umgebung. Eine Änderung der MTU-Size auf den Computern selbst wird nicht empfohlen und würde ich auch nur im Notfall umsetzen wollen. Damit störe ich ja auch den Verkehr zwischen Azure Maschinen. Also habe ich mal per PowerShell getestet.

1..5 | `
%{(Test-Connection `
      -Ping 172.16.0.4 `
      -Count 1 `
      -BufferSize 1300 `
      -DontFragment -Ttl $_ `
      -TimeoutSeconds 1`
  ).reply `
} | `
select status,address,RoundtripTime `
| ft -AutoSize

    Status Address       RoundtripTime
    ------ -------       -------------
TtlExpired 192.168.100.5             0
  TimedOut 0.0.0.0                   0
   Success 172.16.0.4               35
   Success 172.16.0.4               35
   Success 172.16.0.4               35

Mit verschiedenen Paketgrößen bekomme ich dann unterschiedliche Antworten.

Paketgröße/Station IP-Adresse RoundTripTime 1300 1372 1480
1 192.168.100.5 0

TTLExpired

TTLExpired

PacketToBig

2 0.0.0.0 0

TimeOut

PacketToBig

PacketToBig

3 172.16.0.4 35

Success

PacketToBig

PacketToBig

4 172.16.0.4 35

Success

PacketToBig

PacketToBig

5 172.16.0.4 35

Success

PacketToBig

PacketToBig

Zwischen Hop 1 und 3 liegen natürlich mehr Stationen. Durch das VPN sind die Zwischenstationen aber natürlich nicht direkt sichtbar. Über die Latenzzeit kann man aber schon mutmaßen, dass es Zwischenstationen gibt.

IPSec Path Maximum Transmission Units

Auf der Liste der Empfehlungen fällt eine Aussage bezüglich des Tunnels auf:

For Azure, we recommend that you set TCP MSS clamping to 1,350 bytes and tunnel interface MTU to 1,400
Quelle: TCP/IP performance tuning for Azure VMs
https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-tcpip-performance-tuning

Leider kann ich in der Azure auf dem VPN-Gateway keine MTU-Size einstellen. Hier wäre es aber schon wichtig, denn Azure verschlüsselt die Pakete und sendet sie über das Internet zu meinem On-Premises-System. Wenn die Pakete da aber nicht ankommen, sollten die Router im Internet ein "Fragmentation Needed" senden und das AzureVPN-Gateway selbst die Pakete fragmentieren oder den "ICMP fragmentation needed" an meinen Sender weiter geben. Das letztere Verfahren ist präferiert um optimale Paketgrößen zu senden und ineffiziente Fragmentierungen zu sparen. Mit IPv6 wird eh nicht mehr fragmentiert.

Es könnte aber natürlich auch die On-Premises VPN-Seite sein, bei der das Paket ankommt und als zu groß abgelehnt wird. Auf einer Sophos UTM9 gibt es dazu eine Einstellung im IPSec-Remote Gateway:

Eine Änderung beim Fehlerbild habe ich damit aber leider nicht feststellen können. Früher gab es wohl auch eine Option, um in der GUI die MTUSize und das Verhalten auf ICMP zu manipulieren.

Der Eintrag ist aber schon so alt, dass die Menüs nicht mehr vorhanden sind. In einem anderen Blog-Beitrag finde ich nur den Hinweis auf die neue Version

To fix this issue, we proposed a XG appliance instead of SG. XG appliance is possible to edit both MTU and MSS on the WEB UI.
Quelle: https://community.sophos.com/products/unified-threat-management/f/general-discussion/89663/issue-of-mss-on-ipsec-vpn

Workaround

Damit die Dienste aber nun mal schnell funktionieren, habe ich entgegen der Empfehlung von Microsoft auf der AzureVM die MTU-Size von 1500 auf 1400 Bytes reduziert.

Achtung: Das Interface kann sich bei Azure ändern, wenn die VM auf einem anderen Azure-Host neu gestartet wird. Die Einstellung ist also "flüchtig"

C:\>netsh interface ipv4 show interface

Idx     Met         MTU          State                Name
---  ----------  ----------  ------------  ---------------------------
  1          75  4294967295  connected     Loopback Pseudo-Interface 1
  4          10        1500  connected     Ethernet

C:> netsh interface ipv4 set subinterface "4" mtu=1400 store=persistent

C:\>netsh interface ipv4 show interface

Idx     Met         MTU          State                Name
---  ----------  ----------  ------------  ---------------------------
  1          75  4294967295  connected     Loopback Pseudo-Interface 1
  4          10        1400  connected     Ethernet

Damit war dann eine reibungslose Kommunikation mit dem Server möglich.

Eine finale Lösung steht natürlich noch aus. Vor allem kann das ja mit jeder weiteren AzureVM wieder passieren. Hier habe ich den "Fehler" nur einkreisen können, weil er beständig war. Die gleichen Zugriffe auf einen anderen AzureVM-Server im gleichen AzureVM-Subnetz haben keine Probleme aufgezeigt und ein anderer Client konnte auch vorher mit dem Server arbeiten

Es ist also nicht immer einfach so eine Unstimmigkeit zu analysieren und fertig bin ich damit auch nicht.

Checkliste

Wie so oft zeigen solche Support-Anfragen, dass es nicht damit getan ist, eine Konfiguration einzurichten. Sie muss auch robust getestet werden. Dazu muss man natürlich wissen, was man testen soll. Ab sofort gehört auf jeden Fall aber ein MTU-Test per PING dazu.

Prüfpunkt Erledigt

Kleiner PING

Zuerst nutze ich einen einfachen PING, der normalerweise nur 64 Byte sendet, um die generelle Durchlässigkeit zu testen

PING Kette

Und dann muss ich halte ein PING starten, der unterschiedliche Paketgrößen vorsieht. Per PowerShell ist das am einfachsten: Hier einmal mit Google als Gegenstelle

 1300..1500| %{Test-Connection -Ping 8.8.8.8 -Count 1 -BufferSize $_ -DontFragment}

Ping Source Address Latency BufferSize Status
(ms) (B)
---- ------ ------- ------- ---------- ------
....
1 FC-T480S 8.8.8.8       16 1466       Success
1 FC-T480S 8.8.8.8       18 1467       Success
1 FC-T480S 8.8.8.8       16 1468       Success
1 FC-T480S 8.8.8.8       14 1469       Success
1 FC-T480S 8.8.8.8       18 1470       Success
1 FC-T480S 8.8.8.8       16 1471       Success
1 FC-T480S 8.8.8.8       15 1472       Success
1 FC-T480S *              0 1473       PacketTooBig
1 FC-T480S *              0 1474       PacketTooBig
1 FC-T480S *              0 1475       PacketTooBig
1 FC-T480S *              0 1476       PacketTooBig
...

Sie sehen hier gut, dass die maximale Paketgröße bei 1472 Bytes liegt. Allerdings ist hier auch die Spalte "Latency" interessant, die zur gleichen Zeit auf 0 geht. Hier dürfte als eher die MTU-Size meines lokalen Computers (1500 = 1472 Bytes Daten + 28 Bytes Ethernet) größere Paket verhindern.

TTL PING

Sofern möglich interessiert mich natürlich auch, wo der "Engpass" ist. Also nehme ich mir den Grenzwert und laufe mit einem aufsteigenden TTL den Weg ab.

1..30 `
| %{(Test-Connection -Ping 8.8.8.8 -Count 1 -BufferSize 1473 -DontFragment -Ttl $_).reply} `
| select status,address

      Status Address
      ------ -------
  TtlExpired 192.168.178.1
  TtlExpired 100.68.0.1
PacketTooBig 100.127.1.13

Irgendwann kommt das "PacketTooBig" zurück, der zumindest den Weg bis dorthin dokumentiert. Wenn es "dahinter" wieder mit größeren Paketen weiter geht, dann kann das so nicht ermittelt werden.

Weitere Links