Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Comment on

( #3333=superdoc: 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 }

In reply to Check network MTU size by 5mi11er

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others drinking their drinks and smoking their pipes about the Monastery: (8)
    As of 2014-08-23 18:33 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      The best computer themed movie is:











      Results (177 votes), past polls