xref: /original-bsd/usr.sbin/timed/timed/measure.c (revision 7562ff97)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)measure.c	2.7 (Berkeley) 06/01/90";
10 #endif /* not lint */
11 
12 #include "globals.h"
13 #include <protocols/timed.h>
14 #include <netinet/in_systm.h>
15 #include <netinet/ip.h>
16 #include <netinet/ip_icmp.h>
17 
18 #define BIASP	 	43199999
19 #define BIASN		-43200000
20 #define MODULO	 	86400000
21 #define PROCESSING_TIME	5 	/* ms. to reduce error in measurement */
22 
23 #define PACKET_IN	1024
24 
25 extern int id;
26 int measure_delta;
27 extern int sock_raw;
28 static n_short seqno = 0;
29 
30 /*
31  * Measures the differences between machines' clocks using
32  * ICMP timestamp messages.
33  */
34 
35 measure(wait, addr)
36 struct timeval *wait;
37 struct sockaddr_in *addr;
38 {
39 	int length;
40 	int status;
41 	int msgcount, trials;
42 	int cc, count;
43 	fd_set ready;
44 	long sendtime, recvtime, histime;
45 	long min1, min2, diff;
46 	register long delta1, delta2;
47 	struct timeval tv1, tout;
48 	u_char packet[PACKET_IN], opacket[64];
49 	register struct icmp *icp = (struct icmp *) packet;
50 	register struct icmp *oicp = (struct icmp *) opacket;
51 	struct ip *ip = (struct ip *) packet;
52 
53 	min1 = min2 = 0x7fffffff;
54 	status = HOSTDOWN;
55 	measure_delta = HOSTDOWN;
56 
57 /* empties the icmp input queue */
58 	FD_ZERO(&ready);
59 empty:
60 	tout.tv_sec = tout.tv_usec = 0;
61 	FD_SET(sock_raw, &ready);
62 	if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
63 		length = sizeof(struct sockaddr_in);
64 		cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
65 		    (struct sockaddr_in *)NULL, &length);
66 		if (cc < 0)
67 			return(-1);
68 		goto empty;
69 	}
70 
71 	/*
72 	 * To measure the difference, select MSGS messages whose round-trip
73 	 * time is smaller than RANGE if ckrange is 1, otherwise simply
74 	 * select MSGS messages regardless of round-trip transmission time.
75 	 * Choose the smallest transmission time in each of the two directions.
76 	 * Use these two latter quantities to compute the delta between
77 	 * the two clocks.
78 	 */
79 
80 	length = sizeof(struct sockaddr_in);
81 	oicp->icmp_type = ICMP_TSTAMP;
82 	oicp->icmp_code = 0;
83 	oicp->icmp_cksum = 0;
84 	oicp->icmp_id = id;
85 	oicp->icmp_rtime = 0;
86 	oicp->icmp_ttime = 0;
87 	FD_ZERO(&ready);
88 	msgcount = 0;
89 	for (trials = 0; msgcount < MSGS && trials < TRIALS; ++trials) {
90 		oicp->icmp_seq = ++seqno;
91 		oicp->icmp_cksum = 0;
92 
93 		tout.tv_sec = wait->tv_sec;
94 		tout.tv_usec = wait->tv_usec;
95 
96     		(void)gettimeofday (&tv1, (struct timezone *)0);
97 		sendtime = oicp->icmp_otime = (tv1.tv_sec % (24*60*60)) * 1000
98 							+ tv1.tv_usec / 1000;
99 		oicp->icmp_cksum = in_cksum((u_short *)oicp, sizeof(*oicp));
100 
101 		count = sendto(sock_raw, (char *)opacket, sizeof(*oicp), 0,
102 				addr, sizeof(struct sockaddr_in));
103 		if (count < 0) {
104 			status = UNREACHABLE;
105 			return(-1);
106 		}
107 		for (;;) {
108 			FD_SET(sock_raw, &ready);
109 			if ((count = select(FD_SETSIZE, &ready, (fd_set *)0,
110 			    (fd_set *)0, &tout)) <= 0)
111 				break;
112 			cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
113 			    (struct sockaddr_in *)NULL, &length);
114 			(void)gettimeofday(&tv1, (struct timezone *)0);
115 			if (cc < 0)
116 				return(-1);
117 			icp = (struct icmp *)(packet + (ip->ip_hl << 2));
118 			if((icp->icmp_type == ICMP_TSTAMPREPLY) &&
119 			    icp->icmp_id == id && icp->icmp_seq == seqno)
120 				break;
121 		}
122 		if (count <= 0)
123 			continue;		/* resend */
124 		recvtime = (tv1.tv_sec % (24*60*60)) * 1000 +
125 		    tv1.tv_usec / 1000;
126 		diff = recvtime - sendtime;
127 		/*
128 		 * diff can be less than 0 aroud midnight
129 		 */
130 		if (diff < 0)
131 			continue;
132 		msgcount++;
133 		histime = ntohl((u_long)icp->icmp_rtime);
134 		/*
135 		 * a hosts using a time format different from
136 		 * ms. since midnight UT (as per RFC792) should
137 		 * set the high order bit of the 32-bit time
138 		 * value it transmits.
139 		 */
140 		if ((histime & 0x80000000) != 0) {
141 			status = NONSTDTIME;
142 			break;
143 		}
144 		status = GOOD;
145 		delta1 = histime - sendtime;
146 		/*
147 		 * Handles wrap-around to avoid that around
148 		 * midnight small time differences appear
149 		 * enormous. However, the two machine's clocks
150 		 * must be within 12 hours from each other.
151 		 */
152 		if (delta1 < BIASN)
153 			delta1 += MODULO;
154 		else if (delta1 > BIASP)
155 			delta1 -= MODULO;
156 		delta2 = recvtime - histime;
157 		if (delta2 < BIASN)
158 			delta2 += MODULO;
159 		else if (delta2 > BIASP)
160 			delta2 -= MODULO;
161 		if (delta1 < min1)
162 			min1 = delta1;
163 		if (delta2 < min2)
164 			min2 = delta2;
165 		if (diff < RANGE) {
166 			min1 = delta1;
167 			min2 = delta2;
168 			break;
169 		}
170 	}
171 
172 	/*
173 	 * If no answer is received for TRIALS consecutive times,
174 	 * the machine is assumed to be down
175 	 */
176 	 if (status == GOOD) {
177 		measure_delta = (min1 - min2)/2 + PROCESSING_TIME;
178 	}
179 	return(status);
180 }
181