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