1 #include <u.h>
2 #include <libc.h>
3 #include <fcall.h>
4 #include <thread.h>
5 #include <libsec.h>
6 #include <9p.h>
7
8 extern char *Debug;
9
10 typedef struct Pingcache Pingcache;
11 struct Pingcache {
12 Pingcache*next;
13 long rtt;
14 char *host;
15 long expire;
16 };
17
18 typedef struct {
19 uchar vihl; /* Version and header length */
20 uchar tos; /* Type of service */
21 uchar length[2]; /* packet length */
22 uchar id[2]; /* Identification */
23 uchar frag[2]; /* Fragment information */
24 uchar ttl; /* Time to live */
25 uchar proto; /* Protocol */
26 uchar ipcksum[2]; /* Header checksum */
27 uchar src[4]; /* Ip source */
28 uchar dst[4]; /* Ip destination */
29 uchar type;
30 uchar code;
31 uchar cksum[2];
32 uchar icmpid[2];
33 uchar seq[2];
34 uchar data[1];
35 } Icmp;
36
37 enum { /* Packet Types */
38 EchoReply = 0,
39 Unreachable = 3,
40 SrcQuench = 4,
41 EchoRequest = 8,
42 TimeExceed = 11,
43 Timestamp = 13,
44 TimestampReply = 14,
45 InfoRequest = 15,
46 InfoReply = 16,
47
48 ICMP_IPSIZE = 20,
49 ICMP_HDRSIZE = 8,
50
51 Npings = 8,
52 Payload = 32,
53
54 Cachetime = 60,
55 };
56
57 static Pingcache *Cache;
58
59 /*
60 * We ignore the first result as that is probably bigger
61 * than expected due to IP sorting out the routing to the host
62 */
63 int
ping(char * host,int timeout)64 ping(char *host, int timeout)
65 {
66 int rtt, fd, i, seq;
67 long now;
68 vlong then;
69 uchar buf[128];
70 Icmp *ip;
71 Pingcache *c;
72
73 now = time(nil);
74 for(c = Cache; c; c = c->next)
75 if(strcmp(c->host, host) == 0 && now < c->expire){
76 if(Debug && strstr(Debug, "dfs") != nil)
77 print("\t\tping host=%s timeout=%d - cache hit\n",
78 host, timeout);
79 return c->rtt;
80 }
81
82 rtt = -1;
83 ip = (Icmp*)buf;
84
85 if((fd = dial(netmkaddr(host, "icmp", "1"), 0, 0, 0)) == -1)
86 goto fail;
87
88 for(seq = 0; seq < Npings; seq++){
89 then = nsec();
90 for(i = Payload; i < sizeof buf; i++)
91 buf[i] = i + seq;
92 ip->type = EchoRequest;
93 ip->code = 0;
94 ip->seq[0] = seq;
95 ip->seq[1] = seq;
96 alarm(timeout);
97 if(write(fd, ip, sizeof buf) != sizeof buf ||
98 read(fd, ip, sizeof buf) != sizeof buf)
99 goto fail;
100 alarm(0);
101 if(ip->type != EchoReply || ip->code != 0 ||
102 ip->seq[0] != seq || ip->seq[1] != seq)
103 goto fail;
104 for(i = Payload; i < sizeof buf; i++)
105 if((uchar)buf[i] != (uchar)(i + seq))
106 goto fail;
107 rtt = (rtt + nsec() - then) / 2;
108 }
109 fail:
110 if(fd != -1)
111 close(fd);
112
113 if(Debug && strstr(Debug, "dfs") != nil)
114 print("\t\tping host=%s timeout=%d rtt=%d - failed\n",
115 host, timeout, rtt);
116
117 /*
118 * failures get cached too
119 */
120 for(c = Cache; c; c = c->next)
121 if(strcmp(c->host, host) == 0)
122 break;
123 if(c == nil){
124 c = emalloc9p(sizeof(Pingcache));
125 c->host = estrdup9p(host);
126 c->next = Cache;
127 Cache = c;
128 }
129 c->rtt = rtt;
130 c->expire = now+Cachetime;
131 return rtt;
132 }
133