1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
#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;
}
|