#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <libsec.h>
#include <9p.h>
extern char *Debug;
typedef struct Pingcache Pingcache;
struct Pingcache {
Pingcache*next;
long rtt;
char *host;
long expire;
};
typedef struct {
uchar vihl; /* Version and header length */
uchar tos; /* Type of service */
uchar length[2]; /* packet length */
uchar id[2]; /* Identification */
uchar frag[2]; /* Fragment information */
uchar ttl; /* Time to live */
uchar proto; /* Protocol */
uchar ipcksum[2]; /* Header checksum */
uchar src[4]; /* Ip source */
uchar dst[4]; /* Ip destination */
uchar type;
uchar code;
uchar cksum[2];
uchar icmpid[2];
uchar seq[2];
uchar data[1];
} Icmp;
enum { /* Packet Types */
EchoReply = 0,
Unreachable = 3,
SrcQuench = 4,
EchoRequest = 8,
TimeExceed = 11,
Timestamp = 13,
TimestampReply = 14,
InfoRequest = 15,
InfoReply = 16,
ICMP_IPSIZE = 20,
ICMP_HDRSIZE = 8,
Npings = 8,
Payload = 32,
Cachetime = 60,
};
static Pingcache *Cache;
/*
* We ignore the first result as that is probably bigger
* than expected due to IP sorting out the routing to the host
*/
int
ping(char *host, int timeout)
{
int rtt, fd, i, seq;
long now;
vlong then;
uchar buf[128];
Icmp *ip;
Pingcache *c;
now = time(nil);
for(c = Cache; c; c = c->next)
if(strcmp(c->host, host) == 0 && now < c->expire){
if(Debug && strstr(Debug, "dfs") != nil)
print("\t\tping host=%s timeout=%d - cache hit\n",
host, timeout);
return c->rtt;
}
rtt = -1;
ip = (Icmp*)buf;
if((fd = dial(netmkaddr(host, "icmp", "1"), 0, 0, 0)) == -1)
goto fail;
for(seq = 0; seq < Npings; seq++){
then = nsec();
for(i = Payload; i < sizeof buf; i++)
buf[i] = i + seq;
ip->type = EchoRequest;
ip->code = 0;
ip->seq[0] = seq;
ip->seq[1] = seq;
alarm(timeout);
if(write(fd, ip, sizeof buf) != sizeof buf ||
read(fd, ip, sizeof buf) != sizeof buf)
goto fail;
alarm(0);
if(ip->type != EchoReply || ip->code != 0 ||
ip->seq[0] != seq || ip->seq[1] != seq)
goto fail;
for(i = Payload; i < sizeof buf; i++)
if((uchar)buf[i] != (uchar)(i + seq))
goto fail;
rtt = (rtt + nsec() - then) / 2;
}
fail:
if(fd != -1)
close(fd);
if(Debug && strstr(Debug, "dfs") != nil)
print("\t\tping host=%s timeout=%d rtt=%d - failed\n",
host, timeout, rtt);
/*
* failures get cached too
*/
for(c = Cache; c; c = c->next)
if(strcmp(c->host, host) == 0)
break;
if(c == nil){
c = emalloc9p(sizeof(Pingcache));
c->host = estrdup9p(host);
c->next = Cache;
Cache = c;
}
c->rtt = rtt;
c->expire = now+Cachetime;
return rtt;
}
|