half-duplex pings

Kragen Sitaker kragen@pobox.com
Tue, 1 Jun 1999 10:34:31 -0400 (EDT)


One of the problems with 'ping' is that, when you lose packets, you
don't know which direction of transmission is having problems.  This is
especially a problem when firewalls are involved.

So here's a 'half-duplex ping' setup in a few lines of Perl.

These guys will (incidentally) tell you if there is clock skew between
the machines.

It is intended that the output from hdxping-recv be analyzed to find
intervals of sequence numbers without gaps (indicating perfect
reception), intervals with occasional lost packets (indicating network
congestion), and intervals with no packets (indicating a network
outage).  What I'm most interested in for my own purposes is network
outages.


hdxping-send:

#!/usr/bin/perl -w
use strict;
use IO::Socket;
use Socket;
=head1 hdxping-send -- send UDP packets

hdxping-send will send a UDP packet containing a sequence number and the
current time every n seconds to a specified destination port and
address.

Usage:

        hdxping-send destaddr destport [nseconds]

nseconds is the number of seconds to wait between packets; it defaults
to 5.

destaddr is the name of the host to which packets are to be sent.  It
can be a DNS name or a dotted-quad IP address.

destport is the UDP port number to which packets will be sent.  Errors
-- such as 'destination unreachable' -- will be reported, but the
process will not be aborted.

Each packet contains an ID string consisting of numeric digits,
intended to uniquely identify the sending process, some whitespace, a
sequence number that increments by 1 for each packet sent, and the time
the packet was sent.
=cut

my ($destaddr, $destport, $nseconds) = @ARGV;
$nseconds ||= 5;

if (not defined $destport) {
        die "$0: Usage: $0 destaddr destport [nseconds]\n";
}

my $proto = getprotobyname('udp');
socket(SENDSOCK, PF_INET, SOCK_DGRAM, $proto) or die "Can't open socket: $!";
my $iaddr = gethostbyname($destaddr);
if (not defined $iaddr) {
        die "Can't resolve $destaddr\n";
        # how the fuck do we find out why?  Shit.
}
my $sin = sockaddr_in($destport, $iaddr);
connect(SENDSOCK, $sin) or die "Can't connect socket: $!";

# generate an identifier guaranteed to be unique among identifiers from
# this host, as long as time doesn't run backwards or stand still
sleep(2);
my $time = time();
my $id = "$time-$$";

my $seqno = 0;

for (;;) {
        my $sendstr = "$id $seqno " . time();
        while (!send(SENDSOCK, $sendstr, 0)) {
                warn "error: $!\n";
        }
        $seqno ++;
        sleep $nseconds;
}


And here is hdxping-recv:

#!/usr/bin/perl -w
use strict;
use IO::Socket;
use Socket;
=head1 hdxping-recv -- send UDP packets

hdxping-send will receive UDP packets on a port and print them out.

Usage:

        hdxping-recv portnum

portnum is the port number on which packets will be received.

Each packet received will be printed out, as is, preceded by the IP
address and port of its origin and the numeric time() it was received,
and followed by a newline.  This means that packets with newlines in
them will look like two packets.

=cut

my ($portnum) = @ARGV;

if (not defined $portnum) {
        die "$0: Usage: $0 portnum\n";
}

my $proto = getprotobyname('udp');
socket(RECVSOCK, PF_INET, SOCK_DGRAM, $proto) or die "Can't open socket: $!";
my $sin = sockaddr_in($portnum, INADDR_ANY);
bind(RECVSOCK, $sin) or die "Can't bind socket: $!";

my $buf;
for (;;) {
        my ($port, $addr) = unpack_sockaddr_in recv(RECVSOCK, $buf, 2048, 0);
	# warn "addr is $addr dammit";
        print inet_ntoa($addr), " $port ", time(), " $buf\n";
}