Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Check network MTU size

by 5mi11er (Deacon)
on May 22, 2013 at 14:47 UTC ( [id://1034759]=CUFP: print w/replies, xml ) Need Help??

I recently ran into a bunch of problems where our MPLS provider inadvertently modified the MTU sizes of several of our locations. Unfortunately at about the same time we had swapped out our firewall, so after spending significant time thinking we had weird issues with the new firewall, we finally discovered the actual problem was an incorrect MTU size for those sites.

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
    ++ We recently had this happen to us and it took a while to figure out what was going on. I shall have to try this.
      I've had to rethink the "check for MTU larger than it should be" section. Checking for a larger MTU size is not as simple as this, because it will only work if the network segment (vlan) connected to the computer running the check is the one with the MTU set too large.

      As an example imagine the semi-simple case of a network like this

      -------A--------B--------C-------
        (1)     (2)       (3)     (4)
      
      Each of the four numbered, dashed areas is a network segment (vlan), the letters being routers.

      If our computer is located on segment 1, and sends out a ping larger than the actual MTU size allowed for segment 1, router A will drop that packet and not forward it on. So, if segment 2 or 3 or 4 is set to have an MTU size larger than segment 1, we would not be able to tell that from segment 1.

      To actually find out if the MTU size of any of the segments were larger than 1500, we would have to perform the large MTU ping test from each of the routers A, B and C.

      The testing for an MTU size being smaller in one network than the rest, is still valid, but it is unable to tell you which segment was at fault.

      -Scott

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://1034759]
Approved by ww
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2024-04-25 19:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found