Wireshark thinks scapy packet's raw data is DNS (malformed packet) - networking

I'm trying to send a udp packet with scapy to the all the devices in my network with raw data: (hello everyone)
The packet looks like this:
packet = Ether(dst="ff:ff:ff:ff:ff:ff") / IP(dst="255.255.255.0") / UDP(sport=8118) / "hello everyone"
packet.show()
###[ Ethernet ]###
dst = ff:ff:ff:ff:ff:ff
src = (my mac address)
type = IPv4
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = udp
chksum = None
src = 192.168.0.105
dst = 255.255.255.0
\options \
###[ UDP ]###
sport = 8118
dport = domain
len = None
chksum = None
###[ Raw ]###
load = 'hello everyone'
When I send the packet (sendp(packet)), wireshark says this is a malformed DNS packet:
What is the problem?

I believe you're confusing Wireshark, due to you not specifying the destination port. If you don't specify a dport for UDP, it defaults to 53:
class UDP(Packet):
name = "UDP"
fields_desc = [ShortEnumField("sport", 53, UDP_SERVICES),
ShortEnumField("dport", 53, UDP_SERVICES),
ShortField("len", None),
XShortField("chksum", None), ]
Both ports actually do. 53 is for DNS though, so Wireshark is attempting to interpret your payload as DNS based on the port number.
Specify both sport and dport to ensure that your packet isn't misinterpreted as a DNS packet.

Related

Client sends [RST] after receive [SYN, ACK]

I am trying to do a handshake with a server I downloaded from the internet. But when the client receives [SYN, ACK] it sends back a [RST]. Have no idea what is happening. Already checked the acknowledge and sequence number but everything seems ok.
In wireshark I got this:
Here is the handshake client source code:
from scapy.all import *
src_ip = "192.168.43.34"
dst_ip = "192.168.43.115"
src_port = random.randint(1024, 65535)
dst_port = 502
seq_nr = random.randint(444, 8765432)
ack_nr = 0
# Create SYN packet
ip = IP (src = src_ip, dst = dst_ip)
syn = TCP(sport = src_port, dport = dst_port, flags='S', seq = seq_nr, ack = ack_nr)
pkt_syn = ip / syn
pkt_syn.show()
# send SYN packet and receive SYN/ACK packet
print('Sending SYN')
pkt_syn_ack = sr1(pkt_syn)
print('ACK received')
pkt_syn_ack.show()
# Create the ACK packet
ack_nr = pkt_syn_ack.seq + 1
seq_nr = seq_nr + 1
ack = TCP(sport = src_port, dport = dst_port, flags = 'A', seq = seq_nr, ack = ack_nr)
send(ip / ack)
...
The problem is that your OS is receiving the SYN-ACK packet, has no idea why it was sent (as the OS itself didn't start a handshake) and reset the connection.
You can find some solutions here (for Linux)-
Unwanted RST TCP packet with Scapy
Another option is to use a different IP than the OS's, or in Windows turn off the IP stack of the used interface (only if this is the only thing that you use this interface for!)

DPDK TX checksum offload for TCP has fixed offset from correct checksum

I try to compute checksum for IP and TCP via DPDK checksum offload. The code does the following:
ipv4_hdr->hdr_checksum = 0;
mb->l2_len = eth_hdr_len;
mb->l3_len = ipv4_hdr_len;
mb->ol_flags = PKT_TX_IPV4 | PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM;
tcp_hdr->cksum = rte_ipv4_phdr_cksum(ipv4_hdr, mb->ol_flags);
rte_eth_conf has been set:
port_conf.txmode.offloads = DEV_TX_OFFLOAD_IPV4_CKSUM | DEV_TX_OFFLOAD_TCP_CKSUM;
the IP checksum is right, TCP checksum has fixed offset from the correct checksum (under my environment, it is 4)
Any ideas?
Please refer to the answer DPDK HW and SW checksum. For enabling HW checksum one has to set the current checksum value to 0.
/* HW check sum */
ip_hdr->hdr_checksum = 0;
tcp_hdr = (struct tcp_hdr *)((char *)ip_hdr + info->l3_len);
tcp_hdr->cksum = 0;

Scenario where HTTP Response is not fragmented but request is fragmented

In our application, we are using apache tomcat webserver running in 8081.
We observed one behavior where HTTP POST Requests from clients are getting fragmented whereas HTTP Response "200 OK" is not getting fragmented. This we observed by collecting iptraces.
Our ETH MTU value is 1500 Bytes.
Sample POST Request which is fragmented:
===( 331 bytes received on interface en0 )==== 13:50:01.040393849
ETHERNET packet : [ 00:26:cb:eb:df:7f -> 00:1a:64:47:3c:02 ] type 800 (IP)
IP header breakdown:
< SRC = 172.30.17.142 >
< DST = 10.16.0.72 > (un51sv01_if0)
ip_v=4, ip_hl=20, ip_tos=0, ip_len=317, ip_id=57225, ip_off=0 DF
ip_ttl=62, ip_sum=942d, ip_p = 6 (TCP)
TCP header breakdown:
<source port=43795, destination port=8081 >
th_seq=2849969884, th_ack=3790782429
th_off=5, flags<PUSH | ACK>
th_win=49680, th_sum=ba63, th_urp=0
00000000 504f5354 202f6973 61736f61 702f656e |POST /isasoap/en|
00000010 64706f69 6e742f49 6e766f6b 65417070 |dpoint/InvokeApp|
00000020 53657276 69636549 46204854 54502f31 |ServiceIF HTTP/1|
00000030 2e310d0a 436f6e74 656e742d 54797065 |.1..Content-Type|
00000040 3a207465 78742f78 6d6c3b20 63686172 |: text/xml; char|
00000050 7365743d 7574662d 380d0a41 63636570 |set=utf-8..Accep|
00000060 743a2074 6578742f 786d6c2c 20746578 |t: text/xml, tex|
00000070 742f6874 6d6c2c20 696d6167 652f6769 |t/html, image/gi|
00000080 662c2069 6d616765 2f6a7065 672c202a |f, image/jpeg, *|
00000090 3b20713d 2e322c20 2a2f2a3b 20713d2e |; q=.2, */*; q=.|
000000a0 320d0a53 4f415041 6374696f 6e3a2022 |2..SOAPAction: "|
000000b0 220d0a55 7365722d 4167656e 743a204a |"..User-Agent: J|
000000c0 6176612f 312e362e 305f3831 0d0a486f |ava/1.6.0_81..Ho|
000000d0 73743a20 31302e31 362e302e 37323a38 |st: 10.16.0.72:8|
000000e0 3038310d 0a436f6e 6e656374 696f6e3a |081..Connection:|
000000f0 206b6565 702d616c 6976650d 0a436f6e | keep-alive..Con|
00000100 74656e74 2d4c656e 6774683a 20333634 |tent-Length: 364|
00000110 390d0a0d 0a |9.... |
Now the fragmented second packet start:
====( 1434 bytes received on interface en0 )==== 13:50:01.040697474
ETHERNET packet : [ 00:26:cb:eb:df:7f -> 00:1a:64:47:3c:02 ] type 800 (IP)
IP header breakdown:
< SRC = 172.30.17.142 >
< DST = 10.16.0.72 > (un51sv01_if0)
ip_v=4, ip_hl=20, ip_tos=0, ip_len=1420, ip_id=57227, ip_off=0 DF
ip_ttl=62, ip_sum=8fdc, ip_p = 6 (TCP)
TCP header breakdown:
<source port=43795, destination port=8081 >
th_seq=2849970161, th_ack=3790782429
th_off=5, flags<ACK>
th_win=49680, th_sum=f8e9, th_urp=0
00000000 3c3f786d 6c207665 7273696f 6e3d2231 |<?xml version="1|
00000010 2e302220 656e636f 64696e67 3d225554 |.0" encoding="UT|
00000020 462d3822 3f3e0a3c 656e763a 456e7665 |F-8"?>.<env:Enve|
00000030 6c6f7065 20786d6c 6e733a65 6e763d22 |lope xmlns:env="|
00000040 68747470 3a2f2f73 6368656d 61732e78 |http://schemas.x|
00000050 6d6c736f 61702e6f 72672f73 6f61702f |mlsoap.org/soap/|
00000060 656e7665 6c6f7065 2f222078 6d6c6e73 |envelope/" xmlns|
00000070 3a787364 3d226874 74703a2f 2f777777 |:xsd="http://www|
00000080 2e77332e 6f72672f 32303031 2f584d4c |.w3.org/2001/XML|
00000090 53636865 6d612220 786d6c6e 733a7873 |Schema" xmlns:xs|
Similar to above, there is another packet.
Now the Final packet of ~950 Octets:
====( 943 bytes received on interface en0 )==== 13:50:01.040713619
ETHERNET packet : [ 00:26:cb:eb:df:7f -> 00:1a:64:47:3c:02 ] type 800 (IP)
IP header breakdown:
< SRC = 172.30.17.142 >
< DST = 10.16.0.72 > (un51sv01_if0)
ip_v=4, ip_hl=20, ip_tos=0, ip_len=929, ip_id=57229, ip_off=0 DF
ip_ttl=62, ip_sum=91c5, ip_p = 6 (TCP)
TCP header breakdown:
<source port=43795, destination port=8081 >
th_seq=2849972921, th_ack=3790782429
th_off=5, flags<PUSH | ACK>
th_win=49680, th_sum=32ac, th_urp=0
00000000 74612069 643d2249 44313122 20787369 |ta id="ID11" xsi|
00000010 3a747970 653d226e 73313a49 6e766f6b |:type="ns1:Invok|
00000020 65417070 44617461 223e3c64 61746154 |eAppData"><dataT|
.....
After some internal processing by our application, we send 200 OK and HTTP Response.
HTTP 200 OK Response
====( 3209 bytes transmitted on interface en0 )==== 13:50:01.041804849
ETHERNET packet : [ 00:1a:64:47:3c:02 -> 00:00:0c:07:ac:32 ] type 800 (IP)
IP header breakdown:
< SRC = 10.16.0.72 > (un51sv01_if0)
< DST = 172.30.17.142 >
ip_v=4, ip_hl=20, ip_tos=0, ip_len=3195, ip_id=60324, ip_off=0
ip_ttl=60, ip_sum=ffff, ip_p = 6 (TCP)
TCP header breakdown:
<source port=8081, destination port=43574 >
th_seq=3486085705, th_ack=3771812741
th_off=5, flags<PUSH | ACK>
th_win=65535, th_sum=564, th_urp=0
00000000 48545450 2f312e31 20323030 204f4b0d |HTTP/1.1 200 OK.|
00000010 0a416363 6570743a 20746578 742f786d |.Accept: text/xm|
00000020 6c2c2074 6578742f 68746d6c 2c20696d |l, text/html, im|
00000030 6167652f 6769662c 20696d61 67652f6a |age/gif, image/j|
00000040 7065672c 202a3b20 713d2e32 2c202a2f |peg, *; q=.2, */|
00000050 2a3b2071 3d2e320d 0a436f6e 74656e74 |*; q=.2..Content|
Please note from above that the length of packet received on en0 is "3209" for 200 OK HTTP Response which is actually the entire Response. But it is exceeding MTU of ethernet...
Our MTU value is default - 1500 bytes
un51sv01# lsattr -El en0 |grep mtu
mtu 1500 Maximum IP Packet Size for This Device True
Can somebody throw light into why it is happening this way?
Change / Show Characteristics of an Ethernet Adapter
Type or select values in entry fields.
Press Enter AFTER making all desired changes.
[Entry Fields]
Ethernet Adapter ent0
Description Logical Host Ethernet Port (lp-hea)
Status Available
Location
Request Transmit and Receive Jumbo Frames no +
Enable hardware Transmit TCP segmentation yes +
Enable receive TCP segment aggregation yes +
Enable hardware Transmit checksum yes +
Enable hardware Receive checksum yes +
Requested media speed Auto_Negotiation +
Enable ALTERNATE ETHERNET address no +
ALTERNATE ETHERNET address [0x000000000000] +
Apply change to DATABASE only no +
The above indicate Jumbo Frames are not enabled.
Thanks,
Sashi

Correlation between ICMP error messages and IP packets that generated them

I need to send a bunch of IP packets that I'm sure will trigger an ICMP TTL-expired error message. How exactly can I associate each error message with the packet that generated it? What field in the ICMP header is used for this?
Should I rather use some custom ID number in the original IP header, so that I can tell which error message corresponds to which packet? If so, which field is most suitable for this?
The body of ICMP TTL Expired messages must include the IP header of the original packet (which includes the source-port / destination-port) and 64 bits beyond the original header.
Based on timing and that header information, you can derive which packet triggered the TTL-expired message.
I am including a sample triggered by an NTP packet below...
See RFC 792 (Page 5) for more details.
ICMP TTL-Expired Message
Ethernet II, Src: JuniperN_c3:a0:00 (b0:c6:9a:c3:a0:00), Dst: 78:2b:cb:37:4c:7a (78:2b:cb:37:4c:7a)
Destination: 78:2b:cb:37:4c:7a (78:2b:cb:37:4c:7a)
Address: 78:2b:cb:37:4c:7a (78:2b:cb:37:4c:7a)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
Source: JuniperN_c3:a0:00 (b0:c6:9a:c3:a0:00)
Address: JuniperN_c3:a0:00 (b0:c6:9a:c3:a0:00)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
Type: IP (0x0800)
Internet Protocol, Src: 172.25.116.254 (172.25.116.254), Dst: 172.25.116.10 (172.25.116.10)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00)
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..0. = ECN-Capable Transport (ECT): 0
.... ...0 = ECN-CE: 0
Total Length: 56
Identification: 0x86d7 (34519)
Flags: 0x02 (Don't Fragment)
0.. = Reserved bit: Not Set
.1. = Don't fragment: Set
..0 = More fragments: Not Set
Fragment offset: 0
Time to live: 255
Protocol: ICMP (0x01)
Header checksum: 0xb3b1 [correct]
[Good: True]
[Bad : False]
Source: 172.25.116.254 (172.25.116.254)
Destination: 172.25.116.10 (172.25.116.10)
Internet Control Message Protocol
Type: 11 (Time-to-live exceeded)
Code: 0 (Time to live exceeded in transit)
Checksum: 0x4613 [correct]
Internet Protocol, Src: 172.25.116.10 (172.25.116.10), Dst: 172.25.0.11 (172.25.0.11)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00)
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..0. = ECN-Capable Transport (ECT): 0
.... ...0 = ECN-CE: 0
Total Length: 36
Identification: 0x0001 (1)
Flags: 0x00
0.. = Reserved bit: Not Set
.0. = Don't fragment: Not Set
..0 = More fragments: Not Set
Fragment offset: 0
Time to live: 0
[Expert Info (Note/Sequence): "Time To Live" only 0]
[Message: "Time To Live" only 0]
[Severity level: Note]
[Group: Sequence]
Protocol: UDP (0x11)
Header checksum: 0xee80 [correct]
[Good: True]
[Bad : False]
Source: 172.25.116.10 (172.25.116.10)
Destination: 172.25.0.11 (172.25.0.11)
User Datagram Protocol, Src Port: telindus (1728), Dst Port: ntp (123)
Source port: telindus (1728)
Destination port: ntp (123)
Length: 16
Checksum: 0xa7a1 [unchecked, not all data available]
[Good Checksum: False]
[Bad Checksum: False]

How to find the largest UDP packet I can send without fragmenting?

I need to know what the largest UDP packet I can send to another computer is without fragmentation.
This size is commonly known as the MTU (Maximum Transmission Unit). Supposedly, between 2 computers, will be many routers and modems that may have different MTUs.
I read that the TCP implementation in windows automatically finds the maximum MTU in a path.
I was also experimenting, and I found out that the maximum MTU from my computer to a server was 57712 bytes+header. Anything above that was discarded. My computer is on a LAN, isn't the MTU supposed to be around 1500 bytes?
The following doesn't answer your question directly but you might find it interesting; it says that IP packets can be disassembled/reassembled, and therefore bigger than limit on the underling media (e.g. 1500-byte Ethernet): Resolve IP Fragmentation, MTU, MSS, and PMTUD Issues with GRE and IPSEC
More on this topic:
Re: UDP fragmentation says you should use ICMP instead of UDP to discover MTU
Path MTU Discovery says that a TCP connection might include implicit MTU negotiation via ICMP
I don't know about generating ICMP via an API on Windows: at one time such an API was proposed, and was controversial because people argued that would make it easy to write software that implements denial-of-service functionality by generating a flood of ICMP messages.
No, it looks like it is implemented: see for example Winsock Programmer's FAQ Examples: Ping: Raw Sockets Method.
So, to discover MTU, generate ping packets with the 'do not fragment' flag.
Maybe there's an easier API than this, I don't know; but I hope I've given you to understand the underlying protocol[s].
In addition to all the previous answers, quoting the classic:
IPv4 and IPv6 define minimum reassembly buffer size, the minimum datagram size that we are guaranteed any implementation must support. For IPv4, this is 576 bytes. IPv6 raises this to 1,280 bytes.
This pretty much means that you want to limit your datagram size to under 576 if you work over public internet and you control only one side of the exchange - that's what most of the standard UDP-based protocols do.
Also note that PMTU is a dynamic property of the path. This is one of the things TCP deals with for you. Unless you are ready to re-implement lots of sequencing, timing, and retransmission logic, use TCP for any critical networking. Benchmark, test, profile, i.e. prove that TCP is your bottleneck, only then consider UDP.
This is an interesting topic for me. Perhaps some practical results might be of interest when delivering chunky UDP data around the real world internet via UDP, and with a transmission rate of 1 packet a second, data continues to turn up with minimal packet loss up to about 2K. Over this and you start running into issues, but regularly we delivered 1600+ bytes packets without distress - this is over GPRS mobile networks as well as WAN world wide. At ~1K assuming the signal is stable (its not!) you get low packet loss.
Interestingly its not the odd packet, but often a squall of packets for a few seconds - which presumably is why VoIP calls just collapse occasionally.
Your own MTU is available in the registry, but the MTU in practice is going to the smallest MTU in the path between your machine and the destination. Its both variable and can only be determined empirically. There are a number of RFCs showing how to determine it.
LAN's can internally have very large MTU values, since the network hardware is typically homogeneous or at least centrally administrated.
For UDP applications you must handle end-to-end MTU yourself if you want to avoid IP fragmentation or dropped packets. The recommended approach for any application is to do your best to use PMTU to pick your maximum datagram, or send datagrams < minimum PMTU
https://www.rfc-editor.org/rfc/rfc5405#section-3.2
Unicast UDP Usage Guidelines for Application Designers "SHOULD NOT send datagrams that exceed the PMTU, SHOULD discover PMTU or send datagrams < minimum PMTU
Windows appears to settings and access to PMTU information via it's basic socket options interface:
You can make sure PMTU discover is on via IP_MTU_DISCOVER, and you can read the MTU via IP_MTU.
https://learn.microsoft.com/en-us/windows/desktop/winsock/ipproto-ip-socket-options
Here's a bit of Windows PowerShell that I wrote to check for Path MTU issues. (The general technique is not too hard to implement in other programming languages.) A lot of firewalls and routers are configured to drop all ICMP by people who don't know any better. Path MTU Discovery depends on being able to receive an ICMP Destination Unreachable message with Fragementation Needed set in response to sending a packet with Don't Fragment set. The Resolve IPv4 Fragmentation, MTU, MSS, and PMTUD Issues with GRE and IPsec actually does a really good job of explaining how discovery works.
function Test-IPAddressOrName($ipAddressOrName)
{
$ipaddress = $null
$isValidIPAddressOrName = [ipaddress]::TryParse($ipAddressOrName, [ref] $ipaddress)
if ($isValidIPAddressOrName -eq $false)
{
$hasResolveDnsCommand = $null -ne (Get-Command Resolve-DnsName -ErrorAction SilentlyContinue)
if ($hasResolveDnsCommand -eq $true)
{
$dnsResult = Resolve-DnsName -DnsOnly -Name $ipAddressOrName -ErrorAction SilentlyContinue
$isValidIPAddressOrName = $null -ne $dnsResult
}
}
return $isValidIPAddressOrName
}
function Get-NameAndIPAddress($ipAddressOrName)
{
$hasResolveDnsCommand = $null -ne (Get-Command Resolve-DnsName -ErrorAction SilentlyContinue)
$ipAddress = $null
$validIPAddress = [ipaddress]::TryParse($ipAddressOrName, [ref] $ipAddress)
$nameAndIp = [PSCustomObject] #{ 'Name' = $null; 'IPAddress' = $null }
if ($validIPAddress -eq $false)
{
if ($hasResolveDnsCommand -eq $true)
{
$dnsResult = Resolve-DnsName -DnsOnly $ipAddressOrName -Type A -ErrorAction SilentlyContinue
if ($null -ne $dnsResult -and $dnsResult.QueryType -eq 'A')
{
$nameAndIp.Name = $dnsResult.Name
$nameAndIp.IPAddress = $dnsResult.IPAddress
}
else
{
Write-Error "The name $($ipAddressOrName) could not be resolved."
$nameAndIp = $null
}
}
else
{
Write-Warning "Resolve-DnsName not present. DNS resolution check skipped."
}
}
else
{
$nameAndIp.IPAddress = $ipAddress
if ($hasResolveDnsCommand -eq $true)
{
$dnsResult = Resolve-DnsName -DnsOnly $ipAddress -Type PTR -ErrorAction SilentlyContinue
if ($null -ne $dnsResult -and $dnsResult.QueryType -eq 'PTR')
{
$nameAndIp.Name = $dnsResult.NameHost
}
}
}
return $nameAndIp
}
<#
.Synopsis
Performs a series of pings (ICMP echo requests) with Don't Fragment specified to discover the path MTU (Maximum Transmission Unit).
.Description
Performs a series of pings with Don't Fragment specified to discover the path MTU (Maximum Transmission Unit). An ICMP echo request
is sent with a random payload with a payload length specified by the PayloadBytesMinimun. ICMP echo requests of increasing size are
sent until a ping response status other than Success is received. If the response status is PackeTooBig, the last successful packet
length is returned as a reliable MTU; otherwise, if the respone status is TimedOut, the same size packet is retried up to the number
of retries specified. If all of the retries have been exhausted with a response status of TimedOut, the last successful packet
length is returned as the assumed MTU.
.Parameter UseDefaultGateway
If UseDefaultGateway is specified the default gateway reported by the network interface is used as the destination host.
.Parameter DestinationHost
The IP Address or valid fully qualified DNS name of the destination host.
.Parameter InitialTimeout
The number of milliseconds to wait for an ICMP echo reply. Internally, this is doubled each time a retry occurs.
.Parameter Retries
The number of times to try the ping in the event that no reply is recieved before the timeout.
.Parameter PayloadBytesMinimum
The minimum number of bytes in the payload to use. The minimum MTU for IPv4 is 68 bytes; however, in practice, it's extremely rare
to see an MTU size less than 576 bytes so the default value is 548 bytes (576 bytes total packet size minus an ICMP header of 28
bytes).
.Parameter PayloadBytesMaximum
The maximum number of bytes in the payload to use. An IPv4 MTU for jumbo frames is 9000 bytes. The default value is 8973 bytes (9001
bytes total packet size, which is 1 byte larger than the maximum IPv4 MTU for a jumbo frame, minus an ICMP header of 28 bytes).
.Example
Discover-PathMTU -UseDefaultGateway
.Example
Discover-PathMTU -DestinationHost '192.168.1.1'
.Example
Discover-PathMTU -DestinationHost 'www.google.com'
#>
function Discover-PathMtu
{
[CmdletBinding(SupportsShouldProcess = $false)]
param
(
[Parameter(Mandatory = $true, ParameterSetName = 'DefaultGateway')]
[switch] $UseDefaultGateway,
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = 'IPAddressOrName')]
[ValidateScript({ Test-IPAddressOrName $_ })]
[string] $DestinationHost,
[Parameter(ParameterSetName = 'IPAddressOrName')]
[Parameter(ParameterSetName = 'DefaultGateway')]
[int] $InitialTimeout = 3000,
[Parameter(ParameterSetName = 'IPAddressOrName')]
[Parameter(ParameterSetName = 'DefaultGateway')]
[int] $Retries = 3,
[Parameter(ParameterSetName = 'IPAddressOrName')]
[Parameter(ParameterSetName = 'DefaultGateway')]
$PayloadBytesMinimum = 548,
[Parameter(ParameterSetName = 'IPAddressOrName')]
[Parameter(ParameterSetName = 'DefaultGateway')]
$PayloadBytesMaximum = 8973
)
begin
{
$ipConfiguration = Get-NetIPConfiguration -Detailed | ?{ $_.NetProfile.Ipv4Connectivity -eq 'Internet' -and $_.NetAdapter.Status -eq 'Up' } | Sort { $_.IPv4DefaultGateway.InterfaceMetric } | Select -First 1
$gatewayIPAddress = $ipConfiguration.IPv4DefaultGateway.NextHop
$pingOptions = New-Object System.Net.NetworkInformation.PingOptions
$pingOptions.DontFragment = $true
$pinger = New-Object System.Net.NetworkInformation.Ping
$rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
}
process
{
$pingIpAddress = $null
if ($UseDefaultGateway -eq $true)
{
$DestinationHost = $gatewayIPAddress
}
$nameAndIP = Get-NameAndIPAddress $DestinationHost
if ($null -ne $nameAndIP)
{
Write-Host "Performing Path MTU discovery for $($nameAndIP.Name) $($nameAndIP.IPAddress)..."
$pingReply = $null
$payloadLength = $PayloadBytesMinimum
$workingPingTimeout = $InitialTimeout
do
{
$payloadLength++
# Use a random payload to prevent compression in the path from potentially causing a false MTU report.
[byte[]] $payloadBuffer = (,0x00 * $payloadLength)
$rng.GetBytes($payloadBuffer)
$pingCount = 1
do
{
$pingReply = $pinger.Send($nameAndIP.IPAddress, $workingPingTimeout, $payloadBuffer, $pingOptions)
if ($pingReply.Status -notin 'Success', 'PacketTooBig', 'TimedOut')
{
Write-Warning "An unexpected ping reply status, $($pingReply.Status), was received in $($pingReply.RoundtripTime) milliseconds on attempt $($pingCount)."
}
elseif ($pingReply.Status -eq 'TimedOut')
{
Write-Warning "The ping request timed out while testing a packet of size $($payloadLength + 28) using a timeout value of $($workingPingTimeout) milliseconds on attempt $($pingCount)."
$workingPingTimeout = $workingPingTimeout * 2
}
else
{
Write-Verbose "Testing packet of size $($payloadLength + 28). The reply was $($pingReply.Status) and was received in $($pingReply.RoundtripTime) milliseconds on attempt $($pingCount)."
$workingPingTimeout = $InitialTimeout
}
Sleep -Milliseconds 10
$pingCount++
} while ($pingReply.Status -eq 'TimedOut' -and $pingCount -le $Retries)
} while ($payloadLength -lt $PayloadBytesMaximum -and $pingReply -ne $null -and $pingReply.Status -eq 'Success')
if ($pingReply.Status -eq 'PacketTooBig')
{
Write-Host "Reported IPv4 MTU is $($ipConfiguration.NetIPv4Interface.NlMtu). The discovered IPv4 MTU is $($payloadLength + 27)."
}
elseif ($pingReply.Status -eq 'TimedOut')
{
Write-Host "Reported IPv4 MTU is $($ipConfiguration.NetIPv4Interface.NlMtu). The discovered IPv4 MTU is $($payloadLength + 27), but may not be reliable because the packet appears to have been discarded."
}
else
{
Write-Host "Reported IPv4 MTU is $($ipConfiguration.NetIPv4Interface.NlMtu). The discovered IPv4 MTU is $($payloadLength + 27), but may not be reliable, due to an unexpected ping reply status."
}
return $payloadLength + 27
}
else
{
Write-Error "The name $($DestinationHost) could not be resolved. No Path MTU discovery will be performed."
}
}
end
{
if ($null -ne $pinger)
{
$pinger.Dispose()
}
if ($null -ne $rng)
{
$rng.Dispose()
}
}
}

Resources