This wasn't the first time the MTU sizes had been monkeyed with, so I decided to throw something together that might help us identify MTU problems more quickly.
I'd found Network Duplex speed test so, I used much of that for the actual ping packet creation, which referenced Net::Ping code, so I also compared the current version of that against what was given in the duplex test node. Then I had to figure out how to turn on the don't fragment flags of the packet. Anyway, the results are below.
Hopefully someone else will find this useful.
-Scott
#!/usr/bin/perl # Test if our network is running at the standard Ethernet MTU size use strict; use warnings; use IO::Select; use Socket; use FileHandle; # configuration stuff # Packet size = Data size + 28 bytes of header info my $good_data_size = 1472; my $fail_data_size = 1473; my $pid = $$ && 0xffff; my $debug = 0; use constant PINGCOUNT => 5; use constant PORT => 1; # ICMP has no port, but the s +ocket functions need one. use constant SIZE => 1520; # MTU for ethernet + some ext +ra use constant TIMEOUT => 1; #in seconds use constant ICMP_ECHO => 8; use constant ICMP_ECHOREPLY => 0; use constant ICMP_STRUCT => "C2 n3 A"; #minimal packet. use constant ICMP_FLAGS => 2; #do not fragment flag use constant RCV_FLAGS => 0; #no special flags use constant ICMP_PORT => 0; #icmp has no port. use constant SUBCODE => 0; use constant IPPROTO_IP => 0; use constant IP_MTU_DISCOVER => 10; use constant IP_PMTUDISC_DO => 2; # end of config my $buff; if ( $< != 0 ) { die "This program needs to be run as root.\n"; } my @targets = () ; if ( $#ARGV >= 0 ) { @targets = @ARGV; } else { print "usage: $0 <hosts to ping>\n"; } foreach my $target ( @targets ) { my ($ping_result,$good_result,$fail_result) = 0; $ping_result = send_pings($target,66); #send normalish sized +ping if ( ! $ping_result ) { print "$target is down\n"; } else { # send max ping that should be allowed $good_result = send_pings($target,$good_data_size); # send ping that should fail $fail_result = send_pings($target,$fail_data_size); if ( $good_result > 0 && $fail_result == 0 ) { print "$target MTU is good\n"; } if ($good_result == 0) { print "$target MTU is too small!\n"; } if ($fail_result > 0) { print "$target MTU is too large!\n"; } } } # Send raw ping packets to a target with $data_size bytes. Mostly stol +en from Net::Ping. sub send_pings { my ( $target, $data_size ) = @_; my $data = "E"x$data_size; # open a socket file handle to use for sending and recieveing +ICMP messages. my $pinger = FileHandle -> new(); $pinger -> autoflush(1); socket ( $pinger, PF_INET, SOCK_RAW, (getprotobyname('icmp'))[ +2] ) or die "couldn't open socket: $!"; # Turn off fragmentation so we can attempt to figure out what +the MTU size is # Set the IPPROTO_IP (0) IP_MTU_DISCOVER (10) option to IP_PMT +UDISC_DO (2) #setsockopt($pinger, 0, 10, pack("I*", 2)); setsockopt($pinger, IPPROTO_IP, IP_MTU_DISCOVER, pack("I*", IP +_PMTUDISC_DO)); my $sent = 0; my $received = 0; # keep from blocking if there's nothing to read from the netwo +rk my $select = IO::Select -> new ( $pinger ) or die "Could not i +nit select : $!"; #get address in network format my $target_addr = sockaddr_in( ICMP_PORT, inet_aton("$target") + ); # generate a packet to send. my $checksum = 0; my $msg = pack(ICMP_STRUCT . $data_size, ICMP_ECHO, SUBCODE, $ +checksum, $pid, $sent % 65536, $data ); $checksum = checksum($msg); $msg = pack(ICMP_STRUCT . $data_size, ICMP_ECHO, SUBCODE, $che +cksum, $pid, $sent % 65536, $data ); #Send at most PINGCOUNT pings, if we receive any valid replies +, we're done while ( $sent < PINGCOUNT && (!$received) ) { # if there's data to be read, then get the data if ( $select -> can_read( 0 ) ) { my $remote = recv ( $pinger, $buff, SIZE, RCV_ +FLAGS ); if ( $debug ) { print "result from ", unpack("C*",$rem +ote), ":", unpack ( "C*", $target_addr), " +\n"; } # sometimes ICMP replies come from other devic +es, filter those out if ( $remote eq $target_addr ) { $received++; } } else { # there's no I/O is waiting, so we can send an +other packet. $sent++; send ( $pinger, $msg, ICMP_FLAGS, $target_addr + ); # sleep two tenths of a second before sending +another packet # to keep from creating a DOS attack select(undef, undef, undef, 0.2); } } # we've sent some packets, and probably caught most of them, # see if we catch any more within TIMEOUT while ( $received < $sent && $select -> can_read( TIMEOUT ) ) +{ my $remote = recv ( $pinger, $buff, SIZE, RCV_FLAGS ); if ( $remote eq $target_addr ) { $received++; } } if ( $debug ) { print "$received/$sent\n";} $pinger -> flush(); close ( $pinger ) ; return $received; } #sub sub checksum { # Calculate the checksum on the message. Basically sum all of # the short words and fold the high order bits into the low or +der bits. # Stolen from Net::Ping my ( $msg ) = @_; # the packet to checksum my ( $len_msg, # Length of the message $num_short, # The number of short words in the mes +sage $short, # One short word $chk # The checksum ); $len_msg = length($msg); $num_short = int($len_msg / 2); $chk = 0; foreach $short (unpack("n$num_short", $msg)) { $chk += $short; } # Add the odd byte + in $chk += (unpack("C", substr($msg, $len_msg - 1, 1)) << 8) if $ +len_msg % 2; $chk = ($chk >> 16) + ($chk & 0xffff); # Fold high into l +ow return(~(($chk >> 16) + $chk) & 0xffff); # Again and comple +ment }
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Check network MTU size
by VinsWorldcom (Prior) on May 22, 2013 at 19:01 UTC | |
Re: Check network MTU size
by gsiems (Deacon) on May 23, 2013 at 22:07 UTC | |
by 5mi11er (Deacon) on Jun 04, 2013 at 13:46 UTC |