xref: /netbsd/usr.sbin/timed/timed/slave.c (revision c4a72b64)
1 /*	$NetBSD: slave.c,v 1.12 2002/09/19 00:01:33 mycroft Exp $	*/
2 
3 /*-
4  * Copyright (c) 1985, 1993 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)slave.c	8.1 (Berkeley) 6/6/93";
40 #else
41 __RCSID("$NetBSD: slave.c,v 1.12 2002/09/19 00:01:33 mycroft Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #include "globals.h"
46 #include <setjmp.h>
47 #include "pathnames.h"
48 
49 extern jmp_buf jmpenv;
50 extern int Mflag;
51 extern int justquit;
52 
53 extern u_short sequence;
54 
55 static char master_name[MAXHOSTNAMELEN+1];
56 static struct netinfo *old_slavenet;
57 static int old_status;
58 
59 static void schgdate(struct tsp *, char *);
60 static void setmaster(struct tsp *);
61 static void answerdelay(void);
62 
63 extern void logwtmp(char *, char *, char *);
64 
65 
66 int
67 slave(void)
68 {
69 	int tries;
70 	long electiontime, refusetime, looktime, looptime, adjusttime;
71 	u_short seq;
72 	long fastelection;
73 #define FASTTOUT 3
74 	struct in_addr cadr;
75 	struct timeval otime;
76 	struct sockaddr_in taddr;
77 	char tname[MAXHOSTNAMELEN];
78 	struct tsp *msg, to;
79 	struct timeval ntime, wait, tmptv;
80 	time_t tmpt;
81 	struct tsp *answer;
82 	char olddate[32];
83 	char newdate[32];
84 	struct netinfo *ntp;
85 	struct hosttbl *htp;
86 
87 
88 	old_slavenet = 0;
89 	seq = 0;
90 	refusetime = 0;
91 	adjusttime = 0;
92 
93 	(void)gettimeofday(&ntime, 0);
94 	electiontime = ntime.tv_sec + delay2;
95 	fastelection = ntime.tv_sec + FASTTOUT;
96 	if (justquit)
97 		looktime = electiontime;
98 	else
99 		looktime = fastelection;
100 	looptime = fastelection;
101 
102 	if (slavenet)
103 		xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
104 	if (status & MASTER) {
105 		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
106 			if (ntp->status == MASTER)
107 				masterup(ntp);
108 		}
109 	}
110 
111 loop:
112 	get_goodgroup(0);
113 	(void)gettimeofday(&ntime, (struct timezone *)0);
114 	if (ntime.tv_sec > electiontime) {
115 		if (trace)
116 			fprintf(fd, "election timer expired\n");
117 		longjmp(jmpenv, 1);
118 	}
119 
120 	if (ntime.tv_sec >= looktime) {
121 		if (trace)
122 			fprintf(fd, "Looking for nets to master\n");
123 
124 		if (Mflag && nignorednets > 0) {
125 			for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
126 				if (ntp->status == IGNORE
127 				    || ntp->status == NOMASTER) {
128 					lookformaster(ntp);
129 					if (ntp->status == MASTER) {
130 						masterup(ntp);
131 					} else if (ntp->status == MASTER) {
132 						ntp->status = NOMASTER;
133 					}
134 				}
135 				if (ntp->status == MASTER
136 				    && --ntp->quit_count < 0)
137 					ntp->quit_count = 0;
138 			}
139 			makeslave(slavenet);	/* prune extras */
140 			setstatus();
141 		}
142 		(void)gettimeofday(&ntime, 0);
143 		looktime = ntime.tv_sec + delay2;
144 	}
145 	if (ntime.tv_sec >= looptime) {
146 		if (trace)
147 			fprintf(fd, "Looking for loops\n");
148 		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
149 		    if (ntp->status == MASTER) {
150 			to.tsp_type = TSP_LOOP;
151 			to.tsp_vers = TSPVERSION;
152 			to.tsp_seq = sequence++;
153 			to.tsp_hopcnt = MAX_HOPCNT;
154 			(void)strcpy(to.tsp_name, hostname);
155 			bytenetorder(&to);
156 			if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
157 				   (struct sockaddr*)&ntp->dest_addr,
158 				   sizeof(ntp->dest_addr)) < 0) {
159 				trace_sendto_err(ntp->dest_addr.sin_addr);
160 			}
161 		    }
162 		}
163 		(void)gettimeofday(&ntime, 0);
164 		looptime = ntime.tv_sec + delay2;
165 	}
166 
167 	wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
168 	if (wait.tv_sec < 0)
169 		wait.tv_sec = 0;
170 	wait.tv_sec += FASTTOUT;
171 	wait.tv_usec = 0;
172 	msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
173 
174 	if (msg != NULL) {
175 		/*
176 		 * filter stuff not for us
177 		 */
178 		switch (msg->tsp_type) {
179 		case TSP_SETDATE:
180 		case TSP_TRACEOFF:
181 		case TSP_TRACEON:
182 			/*
183 			 * XXX check to see they are from ourself
184 			 */
185 			break;
186 
187 		case TSP_TEST:
188 		case TSP_MSITE:
189 			break;
190 
191 		case TSP_MASTERUP:
192 			if (!fromnet) {
193 				if (trace) {
194 					fprintf(fd, "slave ignored: ");
195 					print(msg, &from);
196 				}
197 				goto loop;
198 			}
199 			break;
200 
201 		default:
202 			if (!fromnet
203 			    || fromnet->status == IGNORE
204 			    || fromnet->status == NOMASTER) {
205 				if (trace) {
206 					fprintf(fd, "slave ignored: ");
207 					print(msg, &from);
208 				}
209 				goto loop;
210 			}
211 			break;
212 		}
213 
214 
215 		/*
216 		 * now process the message
217 		 */
218 		switch (msg->tsp_type) {
219 
220 		case TSP_ADJTIME:
221 			if (fromnet != slavenet)
222 				break;
223 			if (!good_host_name(msg->tsp_name)) {
224 				syslog(LOG_NOTICE,
225 				   "attempted time adjustment by %s",
226 				       msg->tsp_name);
227 				suppress(&from, msg->tsp_name, fromnet);
228 				break;
229 			}
230 			/*
231 			 * Speed up loop detection in case we have a loop.
232 			 * Otherwise the clocks can race until the loop
233 			 * is found.
234 			 */
235 			(void)gettimeofday(&otime, 0);
236 			if (adjusttime < otime.tv_sec)
237 				looptime -= (looptime-otime.tv_sec)/2 + 1;
238 
239 			setmaster(msg);
240 			if (seq != msg->tsp_seq) {
241 				seq = msg->tsp_seq;
242 				synch(tvtomsround(msg->tsp_time));
243 			}
244 			(void)gettimeofday(&ntime, 0);
245 			electiontime = ntime.tv_sec + delay2;
246 			fastelection = ntime.tv_sec + FASTTOUT;
247 			adjusttime = ntime.tv_sec + SAMPLEINTVL*2;
248 			break;
249 
250 		case TSP_SETTIME:
251 			if (fromnet != slavenet)
252 				break;
253 			if (seq == msg->tsp_seq)
254 				break;
255 			seq = msg->tsp_seq;
256 
257 			/* adjust time for residence on the queue */
258 			(void)gettimeofday(&otime, 0);
259 			adj_msg_time(msg,&otime);
260 
261 			/*
262 			 * the following line is necessary due to syslog
263 			 * calling ctime() which clobbers the static buffer
264 			 */
265 			(void)strcpy(olddate, date());
266 			tmpt = msg->tsp_time.tv_sec;
267 			(void)strcpy(newdate, ctime(&tmpt));
268 
269 			if (!good_host_name(msg->tsp_name)) {
270 				syslog(LOG_NOTICE,
271 			    "attempted time setting by untrusted %s to %s",
272 				       msg->tsp_name, newdate);
273 				suppress(&from, msg->tsp_name, fromnet);
274 				break;
275 			}
276 
277 			setmaster(msg);
278 			timersub(&msg->tsp_time, &otime, &ntime);
279 			if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
280 				/*
281 				 * do not change the clock if we can adjust it
282 				 */
283 				synch(tvtomsround(ntime));
284 			} else {
285 				logwtmp("|", "date", "");
286 				tmptv.tv_sec = msg->tsp_time.tv_sec;
287 				tmptv.tv_usec = msg->tsp_time.tv_usec;
288 				(void)settimeofday(&tmptv, 0);
289 				logwtmp("}", "date", "");
290 
291 				syslog(LOG_NOTICE,
292 				       "date changed by %s from %s",
293 					msg->tsp_name, olddate);
294 				if (status & MASTER)
295 					spreadtime();
296 			}
297 			(void)gettimeofday(&ntime, 0);
298 			electiontime = ntime.tv_sec + delay2;
299 			fastelection = ntime.tv_sec + FASTTOUT;
300 
301 /* This patches a bad protocol bug.  Imagine a system with several networks,
302  * where there are a pair of redundant gateways between a pair of networks,
303  * each running timed.  Assume that we start with a third machine mastering
304  * one of the networks, and one of the gateways mastering the other.
305  * Imagine that the third machine goes away and the non-master gateway
306  * decides to replace it.  If things are timed just 'right,' we will have
307  * each gateway mastering one network for a little while.  If a SETTIME
308  * message gets into the network at that time, perhaps from the newly
309  * masterful gateway as it was taking control, the SETTIME will loop
310  * forever.  Each time a gateway receives it on its slave side, it will
311  * call spreadtime to forward it on its mastered network.  We are now in
312  * a permanent loop, since the SETTIME msgs will keep any clock
313  * in the network from advancing.  Normally, the 'LOOP' stuff will detect
314  * and correct the situation.  However, with the clocks stopped, the
315  * 'looptime' timer cannot expire.  While they are in this state, the
316  * masters will try to saturate the network with SETTIME packets.
317  */
318 			looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
319 			break;
320 
321 		case TSP_MASTERUP:
322 			if (slavenet && fromnet != slavenet)
323 				break;
324 			if (!good_host_name(msg->tsp_name)) {
325 				suppress(&from, msg->tsp_name, fromnet);
326 				if (electiontime > fastelection)
327 					electiontime = fastelection;
328 				break;
329 			}
330 			makeslave(fromnet);
331 			setmaster(msg);
332 			setstatus();
333 			answerdelay();
334 			xmit(TSP_SLAVEUP, 0, &from);
335 			(void)gettimeofday(&ntime, 0);
336 			electiontime = ntime.tv_sec + delay2;
337 			fastelection = ntime.tv_sec + FASTTOUT;
338 			refusetime = 0;
339 			break;
340 
341 		case TSP_MASTERREQ:
342 			if (fromnet->status != SLAVE)
343 				break;
344 			(void)gettimeofday(&ntime, 0);
345 			electiontime = ntime.tv_sec + delay2;
346 			break;
347 
348 		case TSP_SETDATE:
349 			tmpt = msg->tsp_time.tv_sec;
350 			(void)strcpy(newdate, ctime(&tmpt));
351 			schgdate(msg, newdate);
352 			break;
353 
354 		case TSP_SETDATEREQ:
355 			if (fromnet->status != MASTER)
356 				break;
357 			tmpt = msg->tsp_time.tv_sec;
358 			(void)strcpy(newdate, ctime(&tmpt));
359 			htp = findhost(msg->tsp_name);
360 			if (0 == htp) {
361 				syslog(LOG_WARNING,
362 				       "DATEREQ from uncontrolled machine");
363 				break;
364 			}
365 			if (!htp->good) {
366 				syslog(LOG_WARNING,
367 				"attempted date change by untrusted %s to %s",
368 				       htp->name, newdate);
369 				spreadtime();
370 				break;
371 			}
372 			schgdate(msg, newdate);
373 			break;
374 
375 		case TSP_TRACEON:
376 			traceon();
377 			break;
378 
379 		case TSP_TRACEOFF:
380 			traceoff("Tracing ended at %s\n");
381 			break;
382 
383 		case TSP_SLAVEUP:
384 			newslave(msg);
385 			break;
386 
387 		case TSP_ELECTION:
388 			if (fromnet->status == SLAVE) {
389 				(void)gettimeofday(&ntime, 0);
390 				electiontime = ntime.tv_sec + delay2;
391 				fastelection = ntime.tv_sec + FASTTOUT;
392 				seq = 0;
393 				if (!good_host_name(msg->tsp_name)) {
394 					syslog(LOG_NOTICE,
395 					       "suppress election of %s",
396 					       msg->tsp_name);
397 					to.tsp_type = TSP_QUIT;
398 					electiontime = fastelection;
399 				} else if (cadr.s_addr != from.sin_addr.s_addr
400 					   && ntime.tv_sec < refusetime) {
401 /* if the candidate has to repeat itself, the old code would refuse it
402  * the second time.  That would prevent elections.
403  */
404 					to.tsp_type = TSP_REFUSE;
405 				} else {
406 					cadr.s_addr = from.sin_addr.s_addr;
407 					to.tsp_type = TSP_ACCEPT;
408 					refusetime = ntime.tv_sec + 30;
409 				}
410 				taddr = from;
411 				(void)strcpy(tname, msg->tsp_name);
412 				(void)strcpy(to.tsp_name, hostname);
413 				answerdelay();
414 				if (!acksend(&to, &taddr, tname,
415 					     TSP_ACK, 0, 0))
416 					syslog(LOG_WARNING,
417 					     "no answer from candidate %s\n",
418 					       tname);
419 
420 			} else {	/* fromnet->status == MASTER */
421 				htp = addmach(msg->tsp_name, &from,fromnet);
422 				to.tsp_type = TSP_QUIT;
423 				(void)strcpy(to.tsp_name, hostname);
424 				if (!acksend(&to, &htp->addr, htp->name,
425 					     TSP_ACK, 0, htp->noanswer)) {
426 					syslog(LOG_ERR,
427 					  "no reply from %s to ELECTION-QUIT",
428 					       htp->name);
429 					(void)remmach(htp);
430 				}
431 			}
432 			break;
433 
434 		case TSP_CONFLICT:
435 			if (fromnet->status != MASTER)
436 				break;
437 			/*
438 			 * After a network partition, there can be
439 			 * more than one master: the first slave to
440 			 * come up will notify here the situation.
441 			 */
442 			(void)strcpy(to.tsp_name, hostname);
443 
444 			/* The other master often gets into the same state,
445 			 * with boring results.
446 			 */
447 			ntp = fromnet;	/* (acksend() can leave fromnet=0 */
448 			for (tries = 0; tries < 3; tries++) {
449 				to.tsp_type = TSP_RESOLVE;
450 				answer = acksend(&to, &ntp->dest_addr,
451 						 ANYADDR, TSP_MASTERACK,
452 						 ntp, 0);
453 				if (answer == NULL)
454 					break;
455 				htp = addmach(answer->tsp_name,&from,ntp);
456 				to.tsp_type = TSP_QUIT;
457 				answer = acksend(&to, &htp->addr, htp->name,
458 						 TSP_ACK, 0, htp->noanswer);
459 				if (!answer) {
460 					syslog(LOG_WARNING,
461 				  "conflict error: no reply from %s to QUIT",
462 						htp->name);
463 					(void)remmach(htp);
464 				}
465 			}
466 			masterup(ntp);
467 			break;
468 
469 		case TSP_MSITE:
470 			if (!slavenet)
471 				break;
472 			taddr = from;
473 			to.tsp_type = TSP_MSITEREQ;
474 			to.tsp_vers = TSPVERSION;
475 			to.tsp_seq = 0;
476 			(void)strcpy(to.tsp_name, hostname);
477 			answer = acksend(&to, &slavenet->dest_addr,
478 					 ANYADDR, TSP_ACK,
479 					 slavenet, 0);
480 			if (answer != NULL
481 			    && good_host_name(answer->tsp_name)) {
482 				setmaster(answer);
483 				to.tsp_type = TSP_ACK;
484 				(void)strcpy(to.tsp_name, answer->tsp_name);
485 				bytenetorder(&to);
486 				if (sendto(sock, (char *)&to,
487 					   sizeof(struct tsp), 0,
488 					   (struct sockaddr*)&taddr, sizeof(taddr)) < 0) {
489 					trace_sendto_err(taddr.sin_addr);
490 				}
491 			}
492 			break;
493 
494 		case TSP_MSITEREQ:
495 			break;
496 
497 		case TSP_ACCEPT:
498 		case TSP_REFUSE:
499 		case TSP_RESOLVE:
500 			break;
501 
502 		case TSP_QUIT:
503 			doquit(msg);		/* become a slave */
504 			break;
505 
506 		case TSP_TEST:
507 			electiontime = 0;
508 			break;
509 
510 		case TSP_LOOP:
511 			/* looking for loops of masters */
512 			if (!(status & MASTER))
513 				break;
514 			if (fromnet->status == SLAVE) {
515 			    if (!strcmp(msg->tsp_name, hostname)) {
516 				/*
517 				 * Someone forwarded our message back to
518 				 * us.  There must be a loop.  Tell the
519 				 * master of this network to quit.
520 				 *
521 				 * The other master often gets into
522 				 * the same state, with boring results.
523 				 */
524 				ntp = fromnet;
525 				for (tries = 0; tries < 3; tries++) {
526 				    to.tsp_type = TSP_RESOLVE;
527 				    answer = acksend(&to, &ntp->dest_addr,
528 						     ANYADDR, TSP_MASTERACK,
529 						     ntp,0);
530 				    if (answer == NULL)
531 					break;
532 				    taddr = from;
533 				    (void)strcpy(tname, answer->tsp_name);
534 				    to.tsp_type = TSP_QUIT;
535 				    (void)strcpy(to.tsp_name, hostname);
536 				    if (!acksend(&to, &taddr, tname,
537 						 TSP_ACK, 0, 1)) {
538 					syslog(LOG_ERR,
539 					"no reply from %s to slave LOOP-QUIT",
540 						 tname);
541 				    } else {
542 					electiontime = 0;
543 				    }
544 				}
545 				(void)gettimeofday(&ntime, 0);
546 				looptime = ntime.tv_sec + FASTTOUT;
547 			    } else {
548 				if (msg->tsp_hopcnt-- < 1)
549 				    break;
550 				bytenetorder(msg);
551 				for (ntp = nettab; ntp != 0; ntp = ntp->next) {
552 				    if (ntp->status == MASTER
553 					&& 0 > sendto(sock, (char *)msg,
554 						      sizeof(struct tsp), 0,
555 					      (struct sockaddr*)&ntp->dest_addr,
556 						      sizeof(ntp->dest_addr)))
557 				    trace_sendto_err(ntp->dest_addr.sin_addr);
558 				}
559 			    }
560 			} else {	/* fromnet->status == MASTER */
561 			    /*
562 			     * We should not have received this from a net
563 			     * we are master on.  There must be two masters,
564 			     * unless the packet was really from us.
565 			     */
566 			    if (from.sin_addr.s_addr
567 				== fromnet->my_addr.s_addr) {
568 				if (trace)
569 				    fprintf(fd,"discarding forwarded LOOP\n");
570 				break;
571 			    }
572 
573 			    /*
574 			     * The other master often gets into the same
575 			     * state, with boring results.
576 			     */
577 			    ntp = fromnet;
578 			    for (tries = 0; tries < 3; tries++) {
579 				to.tsp_type = TSP_RESOLVE;
580 				answer = acksend(&to, &ntp->dest_addr,
581 						 ANYADDR, TSP_MASTERACK,
582 						ntp,0);
583 				if (!answer)
584 					break;
585 				htp = addmach(answer->tsp_name,
586 					      &from,ntp);
587 				to.tsp_type = TSP_QUIT;
588 				(void)strcpy(to.tsp_name, hostname);
589 				if (!acksend(&to,&htp->addr,htp->name,
590 					     TSP_ACK, 0, htp->noanswer)) {
591 					syslog(LOG_ERR,
592 				    "no reply from %s to master LOOP-QUIT",
593 					       htp->name);
594 					(void)remmach(htp);
595 				}
596 			    }
597 			    (void)gettimeofday(&ntime, 0);
598 			    looptime = ntime.tv_sec + FASTTOUT;
599 			}
600 			break;
601 		default:
602 			if (trace) {
603 				fprintf(fd, "garbage message: ");
604 				print(msg, &from);
605 			}
606 			break;
607 		}
608 	}
609 	goto loop;
610 }
611 
612 
613 /*
614  * tell the world who our master is
615  */
616 static void
617 setmaster(struct tsp *msg)
618 {
619 	if (slavenet
620 	    && (slavenet != old_slavenet
621 		|| strcmp(msg->tsp_name, master_name)
622 		|| old_status != status)) {
623 		(void)strcpy(master_name, msg->tsp_name);
624 		old_slavenet = slavenet;
625 		old_status = status;
626 
627 		if (status & MASTER) {
628 			syslog(LOG_NOTICE, "submaster to %s", master_name);
629 			if (trace)
630 				fprintf(fd, "submaster to %s\n", master_name);
631 
632 		} else {
633 			syslog(LOG_NOTICE, "slave to %s", master_name);
634 			if (trace)
635 				fprintf(fd, "slave to %s\n", master_name);
636 		}
637 	}
638 }
639 
640 
641 
642 /*
643  * handle date change request on a slave
644  */
645 static void
646 schgdate(struct tsp *msg, char *newdate)
647 {
648 	struct tsp to;
649 	u_short seq;
650 	struct sockaddr_in taddr;
651 	struct timeval otime;
652 
653 	if (!slavenet)
654 		return;			/* no where to forward */
655 
656 	taddr = from;
657 	seq = msg->tsp_seq;
658 
659 	syslog(LOG_INFO,
660 	       "forwarding date change by %s to %s",
661 	       msg->tsp_name, newdate);
662 
663 	/* adjust time for residence on the queue */
664 	(void)gettimeofday(&otime, 0);
665 	adj_msg_time(msg, &otime);
666 
667 	to.tsp_type = TSP_SETDATEREQ;
668 	to.tsp_time = msg->tsp_time;
669 	(void)strcpy(to.tsp_name, hostname);
670 	if (!acksend(&to, &slavenet->dest_addr,
671 		     ANYADDR, TSP_DATEACK,
672 		     slavenet, 0))
673 		return;			/* no answer */
674 
675 	xmit(TSP_DATEACK, seq, &taddr);
676 }
677 
678 
679 /*
680  * Used before answering a broadcast message to avoid network
681  * contention and likely collisions.
682  */
683 static void
684 answerdelay(void)
685 {
686 	struct timespec timeout;
687 
688 	timeout.tv_sec = 0;
689 	timeout.tv_nsec = delay1 * 1000;
690 
691 	nanosleep(&timeout, NULL);
692 	return;
693 }
694