summaryrefslogtreecommitdiff
path: root/sys/src/cmd/cifs/ping.c
blob: 0aaaaa7e8647fa0e3ec63a053d2572855c87ef3a (plain)
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;
}