Vous êtes sur la page 1sur 4

#!/usr/bin/perl #This utility is designed to test a remote host to see if it's network interface is running at #full or half duplex.

# #We have had a lot of problems with this, since we've got a batch of switches #that don't negotiate properly. # #It's loosely based on Net::Ping, since there were a number of useful reference #points in there (like packet checksumming). #in essence, what it does is send a rapid burst of packets that are over the eth ernet #MTU size. This triggers a burst of responses from the remote end. (if not firew alled etc.) #A machine that is running half duplex, will respond to significantly less of th ese packets, #than a machine that is running full duplex. use strict; use warnings; use IO::Select; use Socket; use FileHandle; #configuration stuff my $data_size = 10240; my $pid = $$ && 0xffff; my $data = "E"x10240; my $debug = 0; use constant PINGCOUNT => 500; use constant TOO_FEW => 0.4; #what proportion of packets must get through in o rder to be considered #'full duplex'. Yes, 60% packet loss is horrible, but so is hitting things #with 10k of fragmented ICMP packets. use constant PORT => 1; et functions need one. use constant SIZE => 1500; use constant TIMEOUT => 5; use use use use use use constant constant constant constant constant constant ICMP_ECHO ICMP_ECHOREPLY ICMP_STRUCT ICMP_FLAGS ICMP_PORT SUBCODE #set to 1 because ICMP has no port, but the sock #MTU for ethernet #2 seconds => => => => => => 8; 0; "C2 S3 A"; #minimal packet. 0; #no special flags in this case. 0; #icmp has no port. 0;

# end of config my $buff; if ( $< != 0 ) {

die "This program needs to be run as root.\n"; } my @targets = ("Line 1\n141.196.100.208\n", "Line 2\n141.196.100.201\n", ) ; if ( $#ARGV >= 0 ) { @targets = @ARGV; } else { print "usage: $0 <hosts to ping>\n"; } foreach my $target ( @targets ) { my $result = 0; $result = burst_ping($target); #send lots of pings at the target, and count t he results. #If result = 0, then no response at all from host - down or firewalled. #if result > PINGCOUNT * TOO_FEW (eg. more than 40% of packets have been recie ved) then #the remote host is probably full duplex. #If less responses are recieved, but more than 0, then the system is probably half duplex. #typical results for half duplex are <10% if ( $result > 0 ) { print $target,": ", $result, "/", PINGCOUNT, $result < PINGCOUNT * TOO_FEW ? " system is running at half-duplex.\n" : " system is running full-duplex.\n" ; } else { print "$target is down\n"; } if ( $result < PINGCOUNT * TOO_FEW ) { sleep TIMEOUT } #to allow ICMP messages to catch up #since this script is going to be run on a batch of hosts a t once. } sub burst_ping { my ( $target ) = @_; #open a file handler 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 "cou ldn't open socket: $!"; my $sent = 0; my $received = 0;

#we need to use select, because recv is a blocking call - if there's nothing t o recv, then it'll #wait. We don't want to do that. my $select = IO::Select -> new ( $pinger ) or die "Could not init select : $!" ; #get a socket flavour address. my $target_addr = sockaddr_in( ICMP_PORT, inet_aton("$target") ); #Now generate a packet to send. Loosely stolen from Net::Ping. 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, $checksum, $pid, $sent % 65536, $data ); while ( $sent < PINGCOUNT ) { #if there's data to be read, then we do if ( $select -> can_read( 0 ) ) { my $remote = recv ( $pinger, $buff, SIZE, ICMP_FLAGS ); if ( $debug ) { print "result from ", unpack("C*",$remote), ":", unpack ( "C*", $target_addr), "\n"; } #sometimes we get ICMP traffic from other sources, so we want to filter it. if ( $remote eq $target_addr ) { ++$received; } } else #no io waiting, so we can send another packet. { ++$sent; send ( $pinger, $msg, ICMP_FLAGS, $target_addr ); } } #sent an few, and probably caught most of them. Here is to tidy up #note that this time we wait for TIMEOUT, to allow any of the slower packets t o get back to #us. while ( $select -> can_read( TIMEOUT ) ) { my $remote = recv ( $pinger, $buff, SIZE, ICMP_FLAGS ); if ( $remote eq $target_addr ) { ++$received; } } if ( $debug ) { print "$received/$sent\n";} $pinger -> flush(); close ( $pinger ) ; return $received; } #sub

sub checksum { # Description: Do a checksum on the message. Basically sum all of # the short words and fold the high order bits into the low order bits. #stolen from Net::Ping. Thanks to Rob Brown. Not essential, but much more #elegant than sending packets with bad checksums. my ( $msg ) = @_; my ($len_msg, $num_short, $short, $chk ); # # # # # the packet to checksum Length of the message The number of short words in the message One short word The checksum

$len_msg = length($msg); $num_short = int($len_msg / 2); $chk = 0; foreach $short (unpack("S$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 low return(~(($chk >> 16) + $chk) & 0xffff); # Again and complement }

Vous aimerez peut-être aussi