xref: /plan9-contrib/sys/src/cmd/cifs/ping.c (revision 671dfc474d1a5bcbeda8be1356d2abfa05b91489)
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