1 /*-
2 * Copyright (c) 1985, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char sccsid[] = "@(#)measure.c 8.2 (Berkeley) 03/26/95";
10 #endif /* not lint */
11
12 #ifdef sgi
13 #ident "$Revision: 1.8 $"
14 #endif
15
16 #include "globals.h"
17 #include <netinet/in_systm.h>
18 #include <netinet/ip.h>
19 #include <netinet/ip_icmp.h>
20
21 #define MSEC_DAY (SECDAY*1000)
22
23 #define PACKET_IN 1024
24
25 #define MSGS 5 /* timestamps to average */
26 #define TRIALS 10 /* max # of timestamps sent */
27
28 extern int sock_raw;
29
30 int measure_delta;
31
32 static n_short seqno = 0;
33
34 /*
35 * Measures the differences between machines' clocks using
36 * ICMP timestamp messages.
37 */
38 int /* status val defined in globals.h */
measure(maxmsec,wmsec,hname,addr,print)39 measure(maxmsec, wmsec, hname, addr, print)
40 u_long maxmsec; /* wait this many msec at most */
41 u_long wmsec; /* msec to wait for an answer */
42 char *hname;
43 struct sockaddr_in *addr;
44 int print; /* print complaints on stderr */
45 {
46 int length;
47 int measure_status;
48 int rcvcount, trials;
49 int cc, count;
50 fd_set ready;
51 long sendtime, recvtime, histime1, histime2;
52 long idelta, odelta, total;
53 long min_idelta, min_odelta;
54 struct timeval tdone, tcur, ttrans, twait, tout;
55 u_char packet[PACKET_IN], opacket[64];
56 register struct icmp *icp = (struct icmp *) packet;
57 register struct icmp *oicp = (struct icmp *) opacket;
58 struct ip *ip = (struct ip *) packet;
59
60 min_idelta = min_odelta = 0x7fffffff;
61 measure_status = HOSTDOWN;
62 measure_delta = HOSTDOWN;
63 errno = 0;
64
65 /* open raw socket used to measure time differences */
66 if (sock_raw < 0) {
67 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
68 if (sock_raw < 0) {
69 syslog(LOG_ERR, "opening raw socket: %m");
70 goto quit;
71 }
72 }
73
74
75 /*
76 * empty the icmp input queue
77 */
78 FD_ZERO(&ready);
79 for (;;) {
80 tout.tv_sec = tout.tv_usec = 0;
81 FD_SET(sock_raw, &ready);
82 if (select(sock_raw+1, &ready, 0,0, &tout)) {
83 length = sizeof(struct sockaddr_in);
84 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
85 0,&length);
86 if (cc < 0)
87 goto quit;
88 continue;
89 }
90 break;
91 }
92
93 /*
94 * Choose the smallest transmission time in each of the two
95 * directions. Use these two latter quantities to compute the delta
96 * between the two clocks.
97 */
98
99 oicp->icmp_type = ICMP_TSTAMP;
100 oicp->icmp_code = 0;
101 oicp->icmp_id = getpid();
102 oicp->icmp_rtime = 0;
103 oicp->icmp_ttime = 0;
104 oicp->icmp_seq = seqno;
105
106 FD_ZERO(&ready);
107
108 #ifdef sgi
109 sginap(1); /* start at a clock tick */
110 #endif /* sgi */
111
112 (void)gettimeofday(&tdone, 0);
113 mstotvround(&tout, maxmsec);
114 timevaladd(&tdone, &tout); /* when we give up */
115
116 mstotvround(&twait, wmsec);
117
118 rcvcount = 0;
119 trials = 0;
120 while (rcvcount < MSGS) {
121 (void)gettimeofday(&tcur, 0);
122
123 /*
124 * keep sending until we have sent the max
125 */
126 if (trials < TRIALS) {
127 trials++;
128 oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
129 + tcur.tv_usec / 1000);
130 oicp->icmp_cksum = 0;
131 oicp->icmp_cksum = in_cksum((u_short*)oicp,
132 sizeof(*oicp));
133
134 count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
135 (struct sockaddr*)addr,
136 sizeof(struct sockaddr));
137 if (count < 0) {
138 if (measure_status == HOSTDOWN)
139 measure_status = UNREACHABLE;
140 goto quit;
141 }
142 ++oicp->icmp_seq;
143
144 ttrans = tcur;
145 timevaladd(&ttrans, &twait);
146 } else {
147 ttrans = tdone;
148 }
149
150 while (rcvcount < trials) {
151 timevalsub(&tout, &ttrans, &tcur);
152 if (tout.tv_sec < 0)
153 tout.tv_sec = 0;
154
155 FD_SET(sock_raw, &ready);
156 count = select(sock_raw+1, &ready, (fd_set *)0,
157 (fd_set *)0, &tout);
158 (void)gettimeofday(&tcur, (struct timezone *)0);
159 if (count <= 0)
160 break;
161
162 length = sizeof(struct sockaddr_in);
163 cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
164 0,&length);
165 if (cc < 0)
166 goto quit;
167
168 /*
169 * got something. See if it is ours
170 */
171 icp = (struct icmp *)(packet + (ip->ip_hl << 2));
172 if (cc < sizeof(*ip)
173 || icp->icmp_type != ICMP_TSTAMPREPLY
174 || icp->icmp_id != oicp->icmp_id
175 || icp->icmp_seq < seqno
176 || icp->icmp_seq >= oicp->icmp_seq)
177 continue;
178
179
180 sendtime = ntohl(icp->icmp_otime);
181 recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
182 tcur.tv_usec / 1000);
183
184 total = recvtime-sendtime;
185 if (total < 0) /* do not hassle midnight */
186 continue;
187
188 rcvcount++;
189 histime1 = ntohl(icp->icmp_rtime);
190 histime2 = ntohl(icp->icmp_ttime);
191 /*
192 * a host using a time format different from
193 * msec. since midnight UT (as per RFC792) should
194 * set the high order bit of the 32-bit time
195 * value it transmits.
196 */
197 if ((histime1 & 0x80000000) != 0) {
198 measure_status = NONSTDTIME;
199 goto quit;
200 }
201 measure_status = GOOD;
202
203 idelta = recvtime-histime2;
204 odelta = histime1-sendtime;
205
206 /* do not be confused by midnight */
207 if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
208 else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
209
210 if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
211 else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
212
213 /* save the quantization error so that we can get a
214 * measurement finer than our system clock.
215 */
216 if (total < MIN_ROUND) {
217 measure_delta = (odelta - idelta)/2;
218 goto quit;
219 }
220
221 if (idelta < min_idelta)
222 min_idelta = idelta;
223 if (odelta < min_odelta)
224 min_odelta = odelta;
225
226 measure_delta = (min_odelta - min_idelta)/2;
227 }
228
229 if (tcur.tv_sec > tdone.tv_sec
230 || (tcur.tv_sec == tdone.tv_sec
231 && tcur.tv_usec >= tdone.tv_usec))
232 break;
233 }
234
235 quit:
236 seqno += TRIALS; /* allocate our sequence numbers */
237
238 /*
239 * If no answer is received for TRIALS consecutive times,
240 * the machine is assumed to be down
241 */
242 if (measure_status == GOOD) {
243 if (trace) {
244 fprintf(fd,
245 "measured delta %4d, %d trials to %-15s %s\n",
246 measure_delta, trials,
247 inet_ntoa(addr->sin_addr), hname);
248 }
249 } else if (print) {
250 if (errno != 0)
251 fprintf(stderr, "measure %s: %s\n", hname,
252 strerror(errno));
253 } else {
254 if (errno != 0) {
255 syslog(LOG_ERR, "measure %s: %m", hname);
256 } else {
257 syslog(LOG_ERR, "measure: %s did not respond", hname);
258 }
259 if (trace) {
260 fprintf(fd,
261 "measure: %s failed after %d trials\n",
262 hname, trials);
263 (void)fflush(fd);
264 }
265 }
266
267 return(measure_status);
268 }
269
270
271
272
273
274 /*
275 * round a number of milliseconds into a struct timeval
276 */
277 void
mstotvround(res,x)278 mstotvround(res, x)
279 struct timeval *res;
280 long x;
281 {
282 #ifndef sgi
283 if (x < 0)
284 x = -((-x + 3)/5);
285 else
286 x = (x+3)/5;
287 x *= 5;
288 #endif /* sgi */
289 res->tv_sec = x/1000;
290 res->tv_usec = (x-res->tv_sec*1000)*1000;
291 if (res->tv_usec < 0) {
292 res->tv_usec += 1000000;
293 res->tv_sec--;
294 }
295 }
296
297 void
timevaladd(tv1,tv2)298 timevaladd(tv1, tv2)
299 struct timeval *tv1, *tv2;
300 {
301 tv1->tv_sec += tv2->tv_sec;
302 tv1->tv_usec += tv2->tv_usec;
303 if (tv1->tv_usec >= 1000000) {
304 tv1->tv_sec++;
305 tv1->tv_usec -= 1000000;
306 }
307 if (tv1->tv_usec < 0) {
308 tv1->tv_sec--;
309 tv1->tv_usec += 1000000;
310 }
311 }
312
313 void
timevalsub(res,tv1,tv2)314 timevalsub(res, tv1, tv2)
315 struct timeval *res, *tv1, *tv2;
316 {
317 res->tv_sec = tv1->tv_sec - tv2->tv_sec;
318 res->tv_usec = tv1->tv_usec - tv2->tv_usec;
319 if (res->tv_usec >= 1000000) {
320 res->tv_sec++;
321 res->tv_usec -= 1000000;
322 }
323 if (res->tv_usec < 0) {
324 res->tv_sec--;
325 res->tv_usec += 1000000;
326 }
327 }
328