diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1994-05-26 05:23:31 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1994-05-26 05:23:31 +0000 |
commit | 862fdf11a2ede45dec0da01ed575525d79468981 (patch) | |
tree | 7a1c88ccb8006476bd4b4a548a6ad48fbfc33a01 /usr.sbin/timed | |
parent | 8e1a19ddde0df113b0b26b0ea621afd61dbaf91f (diff) | |
download | FreeBSD-src-862fdf11a2ede45dec0da01ed575525d79468981.zip FreeBSD-src-862fdf11a2ede45dec0da01ed575525d79468981.tar.gz |
BSD 4.4 Lite usr.sbin Sources
Diffstat (limited to 'usr.sbin/timed')
34 files changed, 7339 insertions, 0 deletions
diff --git a/usr.sbin/timed/Makefile b/usr.sbin/timed/Makefile new file mode 100644 index 0000000..a89ab16 --- /dev/null +++ b/usr.sbin/timed/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +SUBDIR= timed timedc + +.include <bsd.subdir.mk> diff --git a/usr.sbin/timed/SMM.doc/timed/Makefile b/usr.sbin/timed/SMM.doc/timed/Makefile new file mode 100644 index 0000000..22ff6e6 --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timed/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +DIR= smm/12.timed +SRCS= timed.ms +MACROS= -ms +PRINTER=Pdp + +paper.${PRINTER}: ${SRCS} + ${SOELIM} ${SRCS} | ${TBL} | ${ROFF} > ${.TARGET} + +.include <bsd.doc.mk> diff --git a/usr.sbin/timed/SMM.doc/timed/date b/usr.sbin/timed/SMM.doc/timed/date new file mode 100644 index 0000000..e4e4d58 --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timed/date @@ -0,0 +1,53 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)date 8.1 (Berkeley) 6/8/93 +.\" +.ft B +.TS +center; +ce | ce | ce | ce +| c | c | c | s | +| c s s s |. +Byte 1 Byte 2 Byte 3 Byte 4 += +Type Version No. Sequence No. +_ +Seconds of Time to Set +_ +Microseconds of Time to Set +_ +Machine Name +_ +\&. . . +_ +.TE +.ft R diff --git a/usr.sbin/timed/SMM.doc/timed/loop b/usr.sbin/timed/SMM.doc/timed/loop new file mode 100644 index 0000000..11ccb4d --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timed/loop @@ -0,0 +1,54 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)loop 8.1 (Berkeley) 6/8/93 +.\" +.ft B +.TS +center; +ce | ce | ce | ce +| c | c | c | s | +| c | c s s | +| c s s s |. +Byte 1 Byte 2 Byte 3 Byte 4 += +Type Version No. Sequence No. +_ +Hop Count ( unused ) +_ +( unused ) +_ +Machine Name +_ +\&. . . +_ +.TE +.ft R diff --git a/usr.sbin/timed/SMM.doc/timed/spell.ok b/usr.sbin/timed/SMM.doc/timed/spell.ok new file mode 100644 index 0000000..8ecfe15 --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timed/spell.ok @@ -0,0 +1,34 @@ +ACK +ADJTIME +Adjtime +CS +CSELT +Candidature +DATEACK +DoD +Gusella +MASTERACK +MASTERREQ +MASTERUP +MSITE +MSITEREQ +Protocol''SMM:22 +Riccardo +SETDATE +SETDATEREQ +SETTIME +SLAVEUP +SMM:22 +Stefano +TRACEOFF +TRACEON +TSP +Timedc +UDP +USENIX +Zatti +candidature +ce +daemon +daemons +timedc diff --git a/usr.sbin/timed/SMM.doc/timed/time b/usr.sbin/timed/SMM.doc/timed/time new file mode 100644 index 0000000..619d171 --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timed/time @@ -0,0 +1,53 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)time 8.1 (Berkeley) 6/8/93 +.\" +.ft B +.TS +center; +ce | ce | ce | ce +| c | c | c | s | +| c s s s |. +Byte 1 Byte 2 Byte 3 Byte 4 += +Type Version No. Sequence No. +_ +Seconds of Adjustment +_ +Microseconds of Adjustment +_ +Machine Name +_ +\&. . . +_ +.TE +.ft R diff --git a/usr.sbin/timed/SMM.doc/timed/timed.ms b/usr.sbin/timed/SMM.doc/timed/timed.ms new file mode 100644 index 0000000..f5a3b19 --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timed/timed.ms @@ -0,0 +1,504 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)timed.ms 8.1 (Berkeley) 6/8/93 +.\" +.TL +The Berkeley +.UX +.br +Time Synchronization Protocol +.AU +Riccardo Gusella, Stefano Zatti, and James M. Bloom +.AI +Computer Systems Research Group +Computer Science Division +Department of Electrical Engineering and Computer Science +University of California, Berkeley +Berkeley, CA 94720 +.FS +This work was sponsored by the Defense Advanced Research Projects Agency +(DoD), monitored by the Naval Electronics Systems +Command under contract No. N00039-84-C-0089, and by the Italian CSELT +Corporation. +The views and conclusions contained in this document are those of the +authors and should not be interpreted as representing official policies, +either expressed or implied, of the Defense Research Projects Agency, +of the US Government, or of CSELT. +.FE +.LP +.OH 'The Berkeley UNIX Time Synchronization Protocol''SMM:12-%' +.EH 'SMM:12-%''The Berkeley UNIX Time Synchronization Protocol' +.SH +Introduction +.PP +The Time Synchronization Protocol (TSP) +has been designed for specific use by the program \fItimed\fP, +a local area network clock synchronizer for +the UNIX 4.3BSD operating +system. +Timed is built on the DARPA UDP protocol [4] and +is based on a master slave scheme. +.PP +TSP serves a dual purpose. +First, it supports messages for the synchronization of the clocks +of the various hosts in a local area network. +Second, it supports messages for the election that occurs +among slave time daemons when, for any reason, the master disappears. +The synchronization mechanism and the election procedure +employed by the program timed are described +in other documents [1,2,3]. +.PP +Briefly, the synchronization software, which works in a +local area network, consists of a collection of \fItime daemons\fP +(one per machine) and is based on a master-slave +structure. +The present implementation keeps processor clocks synchronized +within 20 milliseconds. +A \fImaster time daemon\fP measures the time +difference between the clock of the machine on which it +is running and those of all other machines. The current implementation +uses ICMP \fITime Stamp Requests\fP [5] to measure the clock difference +between machines. +The master computes the \fInetwork time\fP as the average of the +times provided by nonfaulty clocks.\** +.FS +A clock is considered to be faulty when its value +is more than a small specified +interval apart from the majority of the clocks +of the machines on the same network. +See [1,2] for more details. +.FE +It then sends to each \fIslave time daemon\fP the +correction that should be performed on the clock of its machine. +This process is repeated periodically. +Since the correction is expressed as a time difference rather than an +absolute time, transmission delays do not interfere with synchronization. +When a machine comes up and joins the network, +it starts a slave time daemon, which +will ask the master for the correct time and will reset the machine's clock +before any user activity can begin. +The time daemons therefore maintain a single network time in spite of +the drift of clocks away from each other. +.PP +Additionally, a time daemon on gateway machines may run as +a \fIsubmaster\fP. +A submaster time daemon functions as a slave on one network that +already has a master and as master on other networks. +In addition, a submaster is responsible for propagating broadcast +packets from one network to the other. +.PP +To ensure that service provided is continuous and reliable, +it is necessary to implement an election algorithm that will elect a +new master should the machine running the current master crash, the master +terminate (for example, because of a run-time error), or the network be +partitioned. +Under our algorithm, slaves are able to realize when the master has +stopped functioning and to elect a new master from among themselves. +It is important to note that since the failure of the master results +only in a gradual divergence of clock values, the election +need not occur immediately. +.PP +All the communication occurring among time daemons uses the TSP +protocol. +While some messages need not be sent in a reliable way, +most communication in TSP requires reliability not provided by the underlying +protocol. +Reliability is achieved by the use of acknowledgements, sequence numbers, and +retransmission when message losses occur. +When a message that requires acknowledgment is not acknowledged after +multiple attempts, +the time daemon that has sent the message will assume that the +addressee is down. +This document will not describe the details of how reliability is +implemented, but will only point out when +a message type requires a reliable transport mechanism. +.PP +The message format in TSP is the same for all message types; +however, in some instances, one or more fields are not used. +The next section describes the message format. +The following sections describe +in detail the different message types, their use and the contents +of each field. NOTE: The message format is likely to change in +future versions of timed. +.sp 2 +.SH +Message Format +.PP +All fields are based upon 8-bit bytes. Fields should be sent in +network byte order if they are more than one byte long. +The structure of a TSP message is the following: +.IP 1) +A one byte message type. +.IP 2) +A one byte version number, specifying the protocol version which the +message uses. +.IP 3) +A two byte sequence number to be used for recognizing duplicate messages +that occur when messages are retransmitted. +.IP 4) +Eight bytes of packet specific data. This field contains two 4 byte time +values, a one byte hop count, or may be unused depending on the type +of the packet. +.IP 5) +A zero-terminated string of up to 256 \s-2ASCII\s+2 characters with the name of +the machine sending the message. +.PP +The following charts describe the message types, +show their fields, and explain their usages. +For the purpose of the following discussion, a time daemon can +be considered to be in +one of three states: slave, master, or candidate for election to master. +Also, the term \fIbroadcast\fP refers to +the sending of a message to all active time daemons. +.sp 1 +.DS L +.SH +Adjtime Message +.so time +.LP +Type: TSP_ADJTIME (1) +.sp 1 +.PP +The master sends this message to a slave to communicate +the difference between +the clock of the slave and +the network time the master has just computed. +The slave will accordingly +adjust the time of its machine. +This message requires an acknowledgment. +.sp 1 +.DE +.DS L +.SH +Acknowledgment Message +.so unused +.LP +Type: TSP_ACK (2) +.sp 1 +.PP +Both the master and the slaves use this message for +acknowledgment only. +It is used in several different contexts, for example +in reply to an Adjtime message. +.sp 1 +.DE +.DS L +.SH +Master Request Message +.so unused +.LP +Type: TSP_MASTERREQ (3) +.sp 1 +.PP +A newly-started time daemon broadcasts this message to +locate a master. No other action is implied by this packet. +It requires a Master Acknowledgment. +.sp 1 +.DE +.DS L +.SH +Master Acknowledgement +.so unused +.LP +Type: TSP_MASTERACK (4) +.sp 1 +.PP +The master sends this message to acknowledge the Master Request message +and the Conflict Resolution Message. +.sp 1 +.DE +.DS L +.SH +Set Network Time Message +.so date +.LP +Type: TSP_SETTIME (5) +.sp 1 +.PP +The master sends this message to slave time daemons to set their time. +This packet is sent to newly started time daemons and when the network +date is changed. +It contains the master's time as an approximation of the network time. +It requires an acknowledgment. +The next +synchronization round will eliminate the small time difference +caused by the random delay in the communication channel. +.sp 1 +.DE +.DS L +.SH +Master Active Message +.so unused +.LP +Type: TSP_MASTERUP (6) +.sp 1 +.PP +The master broadcasts this message to +solicit the names of the active slaves. +Slaves will reply with a Slave Active message. +.sp 1 +.DE +.DS L +.SH +Slave Active Message +.so unused +.LP +Type: TSP_SLAVEUP (7) +.sp 1 +.PP +A slave sends this message to the master in answer to a Master Active message. +This message is also sent when a new slave starts up to inform the master that +it wants to be synchronized. +.sp 1 +.DE +.DS L +.SH +Master Candidature Message +.so unused +.LP +Type: TSP_ELECTION (8) +.sp 1 +.PP +A slave eligible to become a master broadcasts this message when its election +timer expires. +The message declares that the slave wishes to become the new master. +.sp 1 +.DE +.DS L +.SH +Candidature Acceptance Message +.so unused +.LP +Type: TSP_ACCEPT (9) +.sp 1 +.PP +A slave sends this message to accept the candidature of the time daemon +that has broadcast an Election message. +The candidate will add the slave's name to the list of machines that it +will control should it become the master. +.sp 1 +.DE +.DS L +.SH +Candidature Rejection Message +.so unused +.LP +Type: TSP_REFUSE (10) +.sp 1 +.PP +After a slave accepts the candidature of a time daemon, it will reply +to any election messages from other slaves +with this message. +This rejects any candidature other than the first received. +.sp 1 +.DE +.DS L +.SH +Multiple Master Notification Message +.so unused +.LP +Type: TSP_CONFLICT (11) +.sp 1 +.PP +When two or more masters reply to a Master Request message, the slave +uses this message to inform one of them that more than one master exists. +.sp 1 +.DE +.DS L +.SH +Conflict Resolution Message +.so unused +.LP +Type: TSP_RESOLVE (12) +.sp 1 +.PP +A master which has been informed of the existence of other masters +broadcasts this message to determine who the other masters are. +.sp 1 +.DE +.DS L +.SH +Quit Message +.so unused +.LP +Type: TSP_QUIT (13) +.sp 1 +.PP +This message is sent by the master in three different contexts: +1) to a candidate that broadcasts an Master Candidature message, +2) to another master when notified of its existence, +3) to another master if a loop is detected. +In all cases, the recipient time daemon will become a slave. +This message requires an acknowledgement. +.sp 1 +.DE +.DS L +.SH +Set Date Message +.so date +.LP +Type: TSP_SETDATE (22) +.sp 1 +.PP +The program \fIdate\fP\|(1) sends this message to the local time daemon +when a super-user wants to set the network date. +If the local time daemon is the master, it will set the date; +if it is a slave, it will communicate the desired date to the master. +.sp 1 +.DE +.DS L +.SH +Set Date Request Message +.so date +.LP +Type: TSP_SETDATEREQ (23) +.sp 1 +.PP +A slave that has received a Set Date message will communicate the +desired date to the master using this message. +.sp 1 +.DE +.DS L +.SH +Set Date Acknowledgment Message +.so unused +.LP +Type: TSP_DATEACK (16) +.sp 1 +.PP +The master sends this message to a slave in acknowledgment of a +Set Date Request Message. +The same message is sent by the local time daemon to the program +\fIdate(1)\fP to confirm that the network date has been set by the +master. +.sp 1 +.DE +.DS L +.SH +Start Tracing Message +.so unused +.LP +Type: TSP_TRACEON (17) +.sp 1 +.PP +The controlling program \fItimedc\fP sends this message to the local +time daemon to start the recording in a system file of +all messages received. +.sp 1 +.DE +.DS L +.SH +Stop Tracing Message +.so unused +.LP +Type: TSP_TRACEOFF (18) +.sp 1 +.PP +\fITimedc\fP sends this message to the local +time daemon to stop the recording of +messages received. +.sp 1 +.DE +.DS L +.SH +Master Site Message +.so unused +.LP +Type: TSP_MSITE (19) +.sp 1 +.PP +\fITimedc\fP sends this message to the local time daemon to find out +where the master is running. +.sp 1 +.DE +.DS L +.SH +Remote Master Site Message +.so unused +.LP +Type: TSP_MSITEREQ (20) +.sp 1 +.PP +A local time daemon broadcasts this message to find the location +of the master. +It then uses the Acknowledgement message to +communicate this location to \fItimedc\fP. +.sp 1 +.DE +.DS L +.SH +Test Message +.so unused +.LP +Type: TSP_TEST (21) +.sp 1 +.PP +For testing purposes, \fItimedc\fP sends this message to a slave +to cause its election timer to expire. NOTE: \fItimed\fP +is not normally compiled to support this. +.sp 1 +.DE +.SH +.DS L +.SH +Loop Detection Message +.so loop +.LP +Type: TSP_LOOP (24) +.sp 1 +.PP +This packet is initiated by all masters occasionally to attempt to detect loops. +All submasters forward this packet onto the networks over which they are master. +If a master receives a packet it sent out initially, +it knows that a loop exists and tries to correct the problem. +.DE +.SH +References +.IP 1. +R. Gusella and S. Zatti, +\fITEMPO: A Network Time Controller for Distributed Berkeley UNIX System\fP, +USENIX Summer Conference Proceedings, Salt Lake City, June 1984. +.IP 2. +R. Gusella and S. Zatti, \fIClock Synchronization in a Local Area Network\fP, +University of California, Berkeley, Technical Report, \fIto appear\fP. +.IP 3. +R. Gusella and S. Zatti, +\fIAn Election Algorithm for a Distributed Clock Synchronization Program\fP, +University of California, Berkeley, CS Technical Report #275, Dec. 1985. +.IP 4. +Postel, J., \fIUser Datagram Protocol\fP, RFC 768. +Network Information Center, SRI International, Menlo Park, California, +August 1980. +.IP 5. +Postel, J., \fIInternet Control Message Protocol\fP, RFC 792. +Network Information Center, SRI International, Menlo Park, California, +September 1981. diff --git a/usr.sbin/timed/SMM.doc/timed/unused b/usr.sbin/timed/SMM.doc/timed/unused new file mode 100644 index 0000000..adadfc3 --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timed/unused @@ -0,0 +1,53 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)unused 8.1 (Berkeley) 6/8/93 +.\" +.ft B +.TS +center; +ce | ce | ce | ce +| c | c | c | s | +| c s s s |. +Byte 1 Byte 2 Byte 3 Byte 4 += +Type Version No. Sequence No. +_ +( unused ) +_ +( unused ) +_ +Machine Name +_ +\&. . . +_ +.TE +.ft R diff --git a/usr.sbin/timed/SMM.doc/timedop/Makefile b/usr.sbin/timed/SMM.doc/timedop/Makefile new file mode 100644 index 0000000..ae43850 --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timedop/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +DIR= smm/11.timedop +SRCS= timed.ms +MACROS= -ms + +.include <bsd.doc.mk> diff --git a/usr.sbin/timed/SMM.doc/timedop/timed.ms b/usr.sbin/timed/SMM.doc/timedop/timed.ms new file mode 100644 index 0000000..feea0b5 --- /dev/null +++ b/usr.sbin/timed/SMM.doc/timedop/timed.ms @@ -0,0 +1,279 @@ +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)timed.ms 8.1 (Berkeley) 6/8/93 +.\" +.TL +Timed Installation and Operation Guide +.AU +Riccardo Gusella, Stefano Zatti, James M. Bloom +.AI +Computer Systems Research Group +Computer Science Division +Department of Electrical Engineering and Computer Science +University of California, Berkeley +Berkeley, CA 94720 +.AU +Kirk Smith +.AI +Engineering Computer Network +Department of Electrical Engineering +Purdue University +West Lafayette, IN 47906 +.FS +This work was sponsored by the Defense Advanced Research Projects Agency +(DoD), monitored by the Naval Electronics Systems +Command under contract No. N00039-84-C-0089, and by the CSELT +Corporation of Italy. +The views and conclusions contained in this document are those of the +authors and should not be interpreted as representing official policies, +either expressed or implied, of the Defense Research Projects Agency, +of the US Government, or of CSELT. +.FE +.LP +.EH 'SMM:11-%''Timed Installation and Operation' +.OH 'Timed Installation and Operation''SMM:11-%' +.SH +Introduction +.PP +The clock synchronization service for +the UNIX 4.3BSD operating system is composed of a collection of +time daemons (\fItimed\fP) running on the machines in a local +area network. +The algorithms implemented by the service is based on a master-slave scheme. +The time daemons communicate with each other using the +\fITime Synchronization Protocol\fP (TSP) which +is built on the DARPA UDP protocol and described in detail in [4]. +.PP +A time daemon has a twofold function. +First, it supports the synchronization of the clocks +of the various hosts in a local area network. +Second, it starts (or takes part in) the election that occurs +among slave time daemons when, for any reason, the master disappears. +The synchronization mechanism and the election procedure +employed by the program \fItimed\fP are described +in other documents [1,2,3]. +The next paragraphs are a brief overview of how the time daemon works. +This document is mainly concerned with the administrative and technical +issues of running \fItimed\fP at a particular site. +.PP +A \fImaster time daemon\fP measures the time +differences between the clock of the machine on which it +is running and those of all other machines. +The master computes the \fInetwork time\fP as the average of the +times provided by nonfaulty clocks.\** +.FS +A clock is considered to be faulty when its value +is more than a small specified +interval apart from the majority of the clocks +of the other machines [1,2]. +.FE +It then sends to each \fIslave time daemon\fP the +correction that should be performed on the clock of its machine. +This process is repeated periodically. +Since the correction is expressed as a time difference rather than an +absolute time, transmission delays do not interfere with +the accuracy of the synchronization. +When a machine comes up and joins the network, +it starts a slave time daemon which +will ask the master for the correct time and will reset the machine's clock +before any user activity can begin. +The time daemons are able to maintain a single network time in spite of +the drift of clocks away from each other. +The present implementation keeps processor clocks synchronized +within 20 milliseconds. +.PP +To ensure that the service provided is continuous and reliable, +it is necessary to implement an election algorithm to elect a +new master should the machine running the current master crash, the master +terminate (for example, because of a run-time error), or +the network be partitioned. +Under our algorithm, slaves are able to realize when the master has +stopped functioning and to elect a new master from among themselves. +It is important to note that, since the failure of the master results +only in a gradual divergence of clock values, the election +need not occur immediately. +.PP +The machines that are gateways between distinct local area +networks require particular care. +A time daemon on such machines may act as a \fIsubmaster\fP. +This artifact depends on the current inability of +transmission protocols to broadcast a message on a network +other than the one to which the broadcasting machine is connected. +The submaster appears as a slave on one network, and as a master +on one or more of the other networks to which it is connected. +.PP +A submaster classifies each network as one of three types. +A \fIslave network\fP is a network on which the submaster acts as a slave. +There can only be one slave network. +A \fImaster network\fP is a network on which the submaster acts as a master. +An \fIignored network\fP is any other network which already has a valid master. +The submaster tries periodically to become master on an ignored +network, but gives up immediately if a master already exists. +.SH +Guidelines +.PP +While the synchronization algorithm is quite general, the election +one, requiring a broadcast mechanism, puts constraints on +the kind of network on which time daemons can run. +The time daemon will only work on networks with broadcast capability +augmented with point-to-point links. +Machines that are only connected to point-to-point, +non-broadcast networks may not use the time daemon. +.PP +If we exclude submasters, there will normally be, at most, one master time +daemon in a local area internetwork. +During an election, only one of the slave time daemons +will become the new master. +However, because of the characteristics of its machine, +a slave can be prevented from becoming the master. +Therefore, a subset of machines must be designated as potential +master time daemons. +A master time daemon will require CPU resources +proportional to the number of slaves, in general, more than +a slave time daemon, so it may be advisable to limit master time +daemons to machines with more powerful processors or lighter loads. +Also, machines with inaccurate clocks should not be used as masters. +This is a purely administrative decision: an organization may +well allow all of its machines to run master time daemons. +.PP +At the administrative level, a time daemon on a machine +with multiple network interfaces, may be told to ignore all +but one network or to ignore one network. +This is done with the \fI\-n network\fP and \fI\-i network\fP +options respectively at start-up time. +Typically, the time daemon would be instructed to ignore all but +the networks belonging to the local administrative control. +.PP +There are some limitations to the current +implementation of the time daemon. +It is expected that these limitations will be removed in future releases. +The constant NHOSTS in /usr/src/etc/timed/globals.h limits the +maximum number of machines that may be directly controlled by one +master time daemon. +The current maximum is 29 (NHOSTS \- 1). +The constant must be changed and the program recompiled if a site wishes to +run \fItimed\fP on a larger (inter)network. +.PP +In addition, there is a \fIpathological situation\fP to +be avoided at all costs, that might occur when +time daemons run on multiply-connected local area networks. +In this case, as we have seen, time daemons running on gateway machines +will be submasters and they will act on some of those +networks as master time daemons. +Consider machines A and B that are both gateways between +networks X and Y. +If time daemons were started on both A and B without constraints, it would be +possible for submaster time daemon A to be a slave on network X +and the master on network Y, while submaster time daemon B is a slave on +network Y and the master on network X. +This \fIloop\fP of master time daemons will not function properly +or guarantee a unique time on both networks, and will cause +the submasters to use large amounts of system resources in the form +of network bandwidth and CPU time. +In fact, this kind of \fIloop\fP can also be generated with more +than two master time daemons, +when several local area networks are interconnected. +.SH +Installation +.PP +In order to start the time daemon on a given machine, +the following lines should be +added to the \fIlocal daemons\fP section in the file \fI/etc/rc.local\fP: +.sp 2 +.in 1i +.nf +if [ -f /etc/timed ]; then + /etc/timed \fIflags\fP & echo -n ' timed' >/dev/console +fi +.fi +.in -1i +.sp +.LP +In any case, they must appear after the network +is configured via ifconfig(8). +.PP +Also, the file \fI/etc/services\fP should contain the following +line: +.sp 2 +.ti 1i +timed 525/udp timeserver +.sp +.LP +The \fIflags\fP are: +.IP "-n network" 13 +to consider the named network. +.IP "-i network" +to ignore the named network. +.IP -t +to place tracing information in \fI/usr/adm/timed.log\fP. +.IP -M +to allow this time daemon to become a master. +A time daemon run without this option will be forced in the state of +slave during an election. +.SH +Daily Operation +.PP +\fITimedc(8)\fP is used to control the operation of the time daemon. +It may be used to: +.IP \(bu +measure the differences between machines' clocks, +.IP \(bu +find the location where the master \fItimed\fP is running, +.IP \(bu +cause election timers on several machines to expire at the same time, +.IP \(bu +enable or disable tracing of messages received by \fItimed\fP. +.LP +See the manual page on \fItimed\fP\|(8) and \fItimedc\fP\|(8) +for more detailed information. +.PP +The \fIdate(1)\fP command can be used to set the network date. +In order to set the time on a single machine, the \fI-n\fP flag +can be given to date(1). +.bp +.SH +References +.IP 1. +R. Gusella and S. Zatti, +\fITEMPO: A Network Time Controller for Distributed Berkeley UNIX System\fP, +USENIX Summer Conference Proceedings, Salt Lake City, June 1984. +.IP 2. +R. Gusella and S. Zatti, \fIClock Synchronization in a Local Area Network\fP, +University of California, Berkeley, Technical Report, \fIto appear\fP. +.IP 3. +R. Gusella and S. Zatti, +\fIAn Election Algorithm for a Distributed Clock Synchronization Program\fP, +University of California, Berkeley, CS Technical Report #275, Dec. 1985. +.IP 4. +R. Gusella and S. Zatti, +\fIThe Berkeley UNIX 4.3BSD Time Synchronization Protocol\fP, +UNIX Programmer's Manual, 4.3 Berkeley Software Distribution, Volume 2c. diff --git a/usr.sbin/timed/timed/CHANGES b/usr.sbin/timed/timed/CHANGES new file mode 100644 index 0000000..773f477 --- /dev/null +++ b/usr.sbin/timed/timed/CHANGES @@ -0,0 +1,144 @@ +# @(#)CHANGES 5.1 (Berkeley) 5/11/93 + +This new version is almost identical to the timed and timedc code +that has been shipped for years by a workstation vendor. + +Among the many changes: + +improve `timedc msite` to accept a list of hostnames. + +change slave-masters to answer the packets generated by `timedc msite` + with the name of the real master, not their own. This makes it + possible to "chase the chain" of slave servers to the ultimate + master. + +much improve the log caused by `timedc trace on`: + -made `timed -t` work. + -suppression of repeated entries, which both slowed down the daemon + (sometimes catastrophically) and tended to make disks fill up + even more quickly. + -better time stamps on log entries + -more messages + -dump information about slaves, master, and so on each time + a message asking the log be turned on is received, and + when the log is turned off. + -fewer CPU cycles + +use a hash table to keep track of slaves, instead of the stupid linear + list. This becomes handy with hundreds of slaves, instead of + the original design limit of "a room with a few VAX's." + +separate the main protocol timer from that used to look for other networks + to master. + +time stamp packets received by the daemon, so that time corrections + are not made (even more) inaccurate by waiting in the internal, + timed queue while the daemon is processing other messages. + +made -n and -i work with subnets not named in /etc/networks + +compute the median of the measured clocks, instead of the average + of "good" times. + +vastly improve the accuracy of the clock difference measure by + `timedc clockdiff`. + +use adjtime() when possible, and directly set the clock only when + necessary. + +when the requested adjustment is small, perform only part of it, to + damp oscillations and improve the long term accuracy of the + adjustments. + +fix uncounted core-dumps on machines that do not allow dereferencing 0 + in both the daemon and timedc. + +fix "master loop detection". + +fix several cases in which multi-homed masters could get into shouting + matches, consuming all available network bandwidth and CPU cycles + (which ever runs out first), and convincing all bystanders to stop + advancing their own clocks. + +refuse to behave badly when other machines do. Instead of arguing forever, + go off and sulk when other machines refuse to play by the rules. + +increase the maximum number of clients. + +add "-F host,host2,..." to "freerun" or "trust" only some hosts. This + is handy both when only some machines should be trusted to let + root use the `date` command to change time in the network. + + It is also handy when one machine has some other way of adjusting + its clock, whether NTP or a direct radio or atomic connection. + "-F localhost" causes `timed` to "trust" only itself. + + It is also handy to build a hierarchy of timed masters crossing + networks. The TSP protocol has no provision of "goodness of clock", + no natural way to completely heal network paritions. Judicious + use of -F or -G can cause each gateway to trust only itself and + machines closer to a central machine with a radio or atomic clock. + +add #ifdef code that supports NIS "netgroups" of trusted hosts, which + can be easier to administer than -F. + +add #ifdef code to compute an aged total adjustment. This can be used + in systems that can make long term changes in their system clock + frequency, e.g. "timetrim" in the Silicon Graphics kernel. + + +Problems observed by others that are unresolved include: + +Practically any users can send to the master TSP messages and this + way corrupt the reliability of the system. Authentication + of messages should be provided. Unfortunately, that would + require changing the protocol with all of the implied + compatiblity problems. Fortunately, the new -F and -G args + can be used to cause the daemon to ignore time changes from + untrusted machines. + +MAN. The limit of 1013 on the number of slaves hosts should be doc'ed. + + It should be dynamically allocated with no limit. On a + large network, one host could possibly master over many + more than 30 hosts. Given the timers in the code and + effectively in the protocol, and the time required by each + master to talk to each slave, it is not practical to have + more than 200-300 slaves. The master cannot keep up because + the slave-chatting is single-threaded. when the master + gets behind, slaves start demanding elections. To + significantly increase the number of slaves would require + multi-treading things, and given that a network with more + than 300 directly addressable machines has worse problems + than keep the time of day right, not worth worrying about. + +UGLY,CODE. timedc/cmds.c has a lots of repeated code in it. + +**** The first thing is that each command is set up as if it + were an individual program taking argc and argv. A more + conventional calling style should be used. I don't think + any of the routines take more than a couple arguments. + +UGLY. fxn definition syntax does't follow convention: + has type on same line. + +**** It needs to be fixed at least enough that tags + will work on it. An entire cleanup might be nice later, but + is noncritical. + +LOBBY(mildly),CODE: Would be very convenient if date(1) took a + +-<number> argument to set the time relatively. With + the advent of timed it is now reasonable to synchronize + with WWV, which is nearly impossible to do "by hand" + with just an absolute date, and scripts are too slow. + format could be +-nn...nn.ss, where the '.' is required + to remove ambiguity. + +**** If you want to do it go ahead. It sounds useful. As far as + syntax goes, the normal format for the date should work just + fine for this. If the date is preceeded by a plus or minus, + the change is relative, otherwise it is absolute. + + +Vernon Schryver. +vjs@sgi.com diff --git a/usr.sbin/timed/timed/Makefile b/usr.sbin/timed/timed/Makefile new file mode 100644 index 0000000..08b41f3 --- /dev/null +++ b/usr.sbin/timed/timed/Makefile @@ -0,0 +1,13 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 +# +# optional flags are: MEASURE TESTING DEBUG + +PROG= timed +SRCS= acksend.c candidate.c correct.c master.c networkdelta.c readmsg.c \ + slave.c timed.c byteorder.c measure.c cksum.c +DPADD= ${LIBUTIL} +LDADD= -lutil +MAN8= timed.0 + +.include "../../Makefile.inc" +.include <bsd.prog.mk> diff --git a/usr.sbin/timed/timed/acksend.c b/usr.sbin/timed/timed/acksend.c new file mode 100644 index 0000000..c84f52d --- /dev/null +++ b/usr.sbin/timed/timed/acksend.c @@ -0,0 +1,132 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)acksend.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.6 $" +#endif + +#include "globals.h" + +struct tsp *answer; + +extern u_short sequence; + +void +xmit(type, seq, addr) + int type; + u_int seq; + struct sockaddr_in *addr; +{ + static struct tsp msg; + + msg.tsp_type = type; + msg.tsp_seq = seq; + msg.tsp_vers = TSPVERSION; + (void)strcpy(msg.tsp_name, hostname); + bytenetorder(&msg); + if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0, + (struct sockaddr*)addr, sizeof(struct sockaddr)) < 0) { + trace_sendto_err(addr->sin_addr); + } +} + + +/* + * Acksend implements reliable datagram transmission by using sequence + * numbers and retransmission when necessary. + * If `name' is ANYADDR, this routine implements reliable broadcast. + * + * Because this function calls readmsg(), none of its args may be in + * a message provided by readmsg(). + */ +struct tsp * +acksend(message, addr, name, ack, net, bad) + struct tsp *message; /* this message */ + struct sockaddr_in *addr; /* to here */ + char *name; + int ack; /* look for this ack */ + struct netinfo *net; /* receive from this network */ + int bad; /* 1=losing patience */ +{ + struct timeval twait; + int count; + long msec; + + message->tsp_vers = TSPVERSION; + message->tsp_seq = sequence; + if (trace) { + fprintf(fd, "acksend: to %s: ", + (name == ANYADDR ? "broadcast" : name)); + print(message, addr); + } + bytenetorder(message); + + msec = 200; + count = bad ? 1 : 5; /* 5 packets in 6.4 seconds */ + answer = 0; + do { + if (!answer) { + /* do not go crazy transmitting just because the + * other guy cannot keep our sequence numbers + * straight. + */ + if (sendto(sock, (char *)message, sizeof(struct tsp), + 0, (struct sockaddr*)addr, + sizeof(struct sockaddr)) < 0) { + trace_sendto_err(addr->sin_addr); + break; + } + } + + mstotvround(&twait, msec); + answer = readmsg(ack, name, &twait, net); + if (answer != 0) { + if (answer->tsp_seq != sequence) { + if (trace) + fprintf(fd,"acksend: seq # %u!=%u\n", + answer->tsp_seq, sequence); + continue; + } + break; + } + + msec *= 2; + } while (--count > 0); + sequence++; + + return(answer); +} diff --git a/usr.sbin/timed/timed/byteorder.c b/usr.sbin/timed/timed/byteorder.c new file mode 100644 index 0000000..9cbd665 --- /dev/null +++ b/usr.sbin/timed/timed/byteorder.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)byteorder.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.3 $" +#endif + +#include "globals.h" + +/* + * Two routines to do the necessary byte swapping for timed protocol + * messages. Protocol is defined in /usr/include/protocols/timed.h + */ +void +bytenetorder(ptr) + struct tsp *ptr; +{ + ptr->tsp_seq = htons((u_short)ptr->tsp_seq); + switch (ptr->tsp_type) { + + case TSP_SETTIME: + case TSP_ADJTIME: + case TSP_SETDATE: + case TSP_SETDATEREQ: + ptr->tsp_time.tv_sec = htonl((u_long)ptr->tsp_time.tv_sec); + ptr->tsp_time.tv_usec = htonl((u_long)ptr->tsp_time.tv_usec); + break; + + default: + break; /* nothing more needed */ + } +} + +void +bytehostorder(ptr) + struct tsp *ptr; +{ + ptr->tsp_seq = ntohs((u_short)ptr->tsp_seq); + switch (ptr->tsp_type) { + + case TSP_SETTIME: + case TSP_ADJTIME: + case TSP_SETDATE: + case TSP_SETDATEREQ: + ptr->tsp_time.tv_sec = ntohl((u_long)ptr->tsp_time.tv_sec); + ptr->tsp_time.tv_usec = ntohl((u_long)ptr->tsp_time.tv_usec); + break; + + default: + break; /* nothing more needed */ + } +} diff --git a/usr.sbin/timed/timed/candidate.c b/usr.sbin/timed/timed/candidate.c new file mode 100644 index 0000000..294eb9b --- /dev/null +++ b/usr.sbin/timed/timed/candidate.c @@ -0,0 +1,167 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)candidate.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.9 $" +#endif + +#include "globals.h" + +/* + * `election' candidates a host as master: it is called by a slave + * which runs with the -M option set when its election timeout expires. + * Note the conservative approach: if a new timed comes up, or another + * candidate sends an election request, the candidature is withdrawn. + */ +int +election(net) + struct netinfo *net; +{ + struct tsp *resp, msg; + struct timeval then, wait; + struct tsp *answer; + struct hosttbl *htp; + char loop_lim = 0; + +/* This code can get totally confused if it gets slightly behind. For + * example, if readmsg() has some QUIT messages waiting from the last + * round, we would send an ELECTION message, get the stale QUIT, + * and give up. This results in network storms when several machines + * do it at once. + */ + wait.tv_sec = 0; + wait.tv_usec = 0; + while (0 != readmsg(TSP_REFUSE, ANYADDR, &wait, net)) { + if (trace) + fprintf(fd, "election: discarded stale REFUSE\n"); + } + while (0 != readmsg(TSP_QUIT, ANYADDR, &wait, net)) { + if (trace) + fprintf(fd, "election: discarded stale QUIT\n"); + } + +again: + syslog(LOG_INFO, "This machine is a candidate time master"); + if (trace) + fprintf(fd, "This machine is a candidate time master\n"); + msg.tsp_type = TSP_ELECTION; + msg.tsp_vers = TSPVERSION; + (void)strcpy(msg.tsp_name, hostname); + bytenetorder(&msg); + if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0, + (struct sockaddr*)&net->dest_addr, + sizeof(struct sockaddr)) < 0) { + trace_sendto_err(net->dest_addr.sin_addr); + return(SLAVE); + } + + (void)gettimeofday(&then, 0); + then.tv_sec += 3; + for (;;) { + (void)gettimeofday(&wait, 0); + timevalsub(&wait,&then,&wait); + resp = readmsg(TSP_ANY, ANYADDR, &wait, net); + if (!resp) + return(MASTER); + + switch (resp->tsp_type) { + + case TSP_ACCEPT: + (void)addmach(resp->tsp_name, &from,fromnet); + break; + + case TSP_MASTERUP: + case TSP_MASTERREQ: + /* + * If another timedaemon is coming up at the same + * time, give up, and let it be the master. + */ + if (++loop_lim < 5 + && !good_host_name(resp->tsp_name)) { + (void)addmach(resp->tsp_name, &from,fromnet); + suppress(&from, resp->tsp_name, net); + goto again; + } + rmnetmachs(net); + return(SLAVE); + + case TSP_QUIT: + case TSP_REFUSE: + /* + * Collision: change value of election timer + * using exponential backoff. + * + * Fooey. + * An exponential backoff on a delay starting at + * 6 to 15 minutes for a process that takes + * milliseconds is silly. It is particularly + * strange that the original code would increase + * the backoff without bound. + */ + rmnetmachs(net); + return(SLAVE); + + case TSP_ELECTION: + /* no master for another round */ + htp = addmach(resp->tsp_name,&from,fromnet); + msg.tsp_type = TSP_REFUSE; + (void)strcpy(msg.tsp_name, hostname); + answer = acksend(&msg, &htp->addr, htp->name, + TSP_ACK, 0, htp->noanswer); + if (!answer) { + syslog(LOG_ERR, "error in election from %s", + htp->name); + } + break; + + case TSP_SLAVEUP: + (void)addmach(resp->tsp_name, &from,fromnet); + break; + + case TSP_SETDATE: + case TSP_SETDATEREQ: + break; + + default: + if (trace) { + fprintf(fd, "candidate: "); + print(resp, &from); + } + break; + } + } +} diff --git a/usr.sbin/timed/timed/cksum.c b/usr.sbin/timed/timed/cksum.c new file mode 100644 index 0000000..3d15af3 --- /dev/null +++ b/usr.sbin/timed/timed/cksum.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cksum.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.3 $" +#endif + +#include <sys/types.h> + +/* + * I N _ C K S U M + * + * Checksum routine for Internet Protocol family headers (C Version) + * + * There is no profit in a specialized version of the checksum + * function for any machine where int's are 32 bits and shorts are 16. + * + * All timed packets are smaller than 32K shorts, so there is no need to + * worry about carries except at the end. + */ +int +in_cksum(addr, len) + u_short *addr; + int len; +{ + register int nleft = len; + register u_short *w = addr; + register u_short answer; + register int sum = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while( nleft > 1 ) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if( nleft == 1 ) + sum += (*(u_char *)w) << 8; + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} diff --git a/usr.sbin/timed/timed/correct.c b/usr.sbin/timed/timed/correct.c new file mode 100644 index 0000000..569d2e1 --- /dev/null +++ b/usr.sbin/timed/timed/correct.c @@ -0,0 +1,294 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)correct.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.16 $" +#endif + +#include "globals.h" +#include <math.h> +#include <sys/types.h> +#include <sys/times.h> +#ifdef sgi +#include <sys/syssgi.h> +#endif /* sgi */ + +static void adjclock __P((struct timeval *)); + +/* + * sends to the slaves the corrections for their clocks after fixing our + * own + */ +void +correct(avdelta) + long avdelta; +{ + struct hosttbl *htp; + int corr; + struct timeval adjlocal; + struct tsp to; + struct tsp *answer; + + mstotvround(&adjlocal, avdelta); + + for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { + if (htp->delta != HOSTDOWN) { + corr = avdelta - htp->delta; +/* If the other machine is off in the weeds, set its time directly. + * If a slave gets the wrong day, the original code would simply + * fix the minutes. If you fix a network partition, you can get + * into such situations. + */ + if (htp->need_set + || corr >= MAXADJ*1000 + || corr <= -MAXADJ*1000) { + htp->need_set = 0; + (void)gettimeofday(&to.tsp_time,0); + timevaladd(&to.tsp_time, &adjlocal); + to.tsp_type = TSP_SETTIME; + } else { + mstotvround(&to.tsp_time, corr); + to.tsp_type = TSP_ADJTIME; + } + (void)strcpy(to.tsp_name, hostname); + answer = acksend(&to, &htp->addr, htp->name, + TSP_ACK, 0, 0); + if (!answer) { + htp->delta = HOSTDOWN; + syslog(LOG_WARNING, + "no reply to time correction from %s", + htp->name); + if (++htp->noanswer >= LOSTHOST) { + if (trace) { + fprintf(fd, + "purging %s for not answering\n", + htp->name); + (void)fflush(fd); + } + htp = remmach(htp); + } + } + } + } + + /* + * adjust our own clock now that we are not sending it out + */ + adjclock(&adjlocal); +} + + +static void +adjclock(corr) + struct timeval *corr; +{ + static int passes = 0; + static int smoother = 0; + long delta; /* adjustment in usec */ + long ndelta; + struct timeval now; + struct timeval adj; + + if (!timerisset(corr)) + return; + + adj = *corr; + if (adj.tv_sec < MAXADJ && adj.tv_sec > - MAXADJ) { + delta = adj.tv_sec*1000000 + adj.tv_usec; + /* If the correction is less than the minimum round + * trip time for an ICMP packet, and thus + * less than the likely error in the measurement, + * do not do the entire correction. Do half + * or a quarter of it. + */ + + if (delta > -MIN_ROUND*1000 + && delta < MIN_ROUND*1000) { + if (smoother <= 4) + smoother++; + ndelta = delta >> smoother; + if (trace) + fprintf(fd, + "trimming delta %ld usec to %ld\n", + delta, ndelta); + adj.tv_usec = ndelta; + adj.tv_sec = 0; + } else if (smoother > 0) { + smoother--; + } + if (0 > adjtime(corr, 0)) { + syslog(LOG_ERR, "adjtime: %m"); + } + if (passes > 1 + && (delta < -BIG_ADJ || delta > BIG_ADJ)) { + smoother = 0; + passes = 0; + syslog(LOG_WARNING, + "large time adjustment of %+.3f sec", + delta/1000000.0); + } + } else { + syslog(LOG_WARNING, + "clock correction %d sec too large to adjust", + adj.tv_sec); + (void) gettimeofday(&now, 0); + timevaladd(&now, corr); + if (settimeofday(&now, 0) < 0) + syslog(LOG_ERR, "settimeofday: %m"); + } + +#ifdef sgi + /* Accumulate the total change, and use it to adjust the basic + * clock rate. + */ + if (++passes > 2) { +#define F_USEC_PER_SEC (1000000*1.0) /* reduce typos */ +#define F_NSEC_PER_SEC (F_USEC_PER_SEC*1000.0) + + extern char *timetrim_fn; + extern char *timetrim_wpat; + extern long timetrim; + extern double tot_adj, hr_adj; /* totals in nsec */ + extern double tot_ticks, hr_ticks; + + static double nag_tick; + double cur_ticks, hr_delta_ticks, tot_delta_ticks; + double tru_tot_adj, tru_hr_adj; /* nsecs of adjustment */ + double tot_trim, hr_trim; /* nsec/sec */ + struct tms tm; + FILE *timetrim_st; + + cur_ticks = times(&tm); + tot_adj += delta*1000.0; + hr_adj += delta*1000.0; + + tot_delta_ticks = cur_ticks-tot_ticks; + if (tot_delta_ticks >= 16*SECDAY*CLK_TCK) { + tot_adj -= rint(tot_adj/16); + tot_ticks += rint(tot_delta_ticks/16); + tot_delta_ticks = cur_ticks-tot_ticks; + } + hr_delta_ticks = cur_ticks-hr_ticks; + + tru_hr_adj = hr_adj + timetrim*rint(hr_delta_ticks/CLK_TCK); + tru_tot_adj = (tot_adj + + timetrim*rint(tot_delta_ticks/CLK_TCK)); + + if (hr_delta_ticks >= SECDAY*CLK_TCK + || (tot_delta_ticks < 4*SECDAY*CLK_TCK + && hr_delta_ticks >= SECHR*CLK_TCK) + || (trace && hr_delta_ticks >= (SECHR/10)*CLK_TCK)) { + + tot_trim = rint(tru_tot_adj*CLK_TCK/tot_delta_ticks); + hr_trim = rint(tru_hr_adj*CLK_TCK/hr_delta_ticks); + + if (trace + || (abs(timetrim - hr_trim) > 100000.0 + && 0 == timetrim_fn + && ((cur_ticks - nag_tick) + >= 24*SECDAY*CLK_TCK))) { + nag_tick = cur_ticks; + syslog(LOG_NOTICE, + "%+.3f/%.2f or %+.3f/%.2f sec/hr; timetrim=%+.0f or %+.0f", + tru_tot_adj/F_NSEC_PER_SEC, + tot_delta_ticks/(SECHR*CLK_TCK*1.0), + tru_hr_adj/F_NSEC_PER_SEC, + hr_delta_ticks/(SECHR*CLK_TCK*1.0), + tot_trim, + hr_trim); + } + + if (tot_trim < -MAX_TRIM || tot_trim > MAX_TRIM) { + tot_ticks = hr_ticks; + tot_adj = hr_adj; + } else if (0 > syssgi(SGI_SETTIMETRIM, + (long)tot_trim)) { + syslog(LOG_ERR, "SETTIMETRIM(%d): %m", + (long)tot_trim); + } else { + if (0 != timetrim_fn) { + timetrim_st = fopen(timetrim_fn, "w"); + if (0 == timetrim_st) { + syslog(LOG_ERR, "fopen(%s): %m", + timetrim_fn); + } else { + if (0 > fprintf(timetrim_st, + timetrim_wpat, + (long)tot_trim, + tru_tot_adj, + tot_delta_ticks)) { + syslog(LOG_ERR, + "fprintf(%s): %m", + timetrim_fn); + } + (void)fclose(timetrim_st); + } + } + + tot_adj -= ((tot_trim - timetrim) + * rint(tot_delta_ticks/CLK_TCK)); + timetrim = tot_trim; + } + + hr_ticks = cur_ticks; + hr_adj = 0; + } + } +#endif /* sgi */ +} + + +/* adjust the time in a message by the time it + * spent in the queue + */ +void +adj_msg_time(msg, now) + struct tsp *msg; + struct timeval *now; +{ + msg->tsp_time.tv_sec += (now->tv_sec - from_when.tv_sec); + msg->tsp_time.tv_usec += (now->tv_usec - from_when.tv_usec); + + while (msg->tsp_time.tv_usec < 0) { + msg->tsp_time.tv_sec--; + msg->tsp_time.tv_usec += 1000000; + } + while (msg->tsp_time.tv_usec >= 1000000) { + msg->tsp_time.tv_sec++; + msg->tsp_time.tv_usec -= 1000000; + } +} diff --git a/usr.sbin/timed/timed/extern.h b/usr.sbin/timed/timed/extern.h new file mode 100644 index 0000000..09dfaaa --- /dev/null +++ b/usr.sbin/timed/timed/extern.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +struct hosttbl; +struct netinfo; +struct sockaddr_in; +struct timeval; +struct tsp; + +struct hosttbl *addmach __P((char *, struct sockaddr_in *, struct netinfo *)); +struct hosttbl *findhost __P((char *)); +struct hosttbl *remmach __P((struct hosttbl *)); + +struct tsp *readmsg __P((int, + char *, struct timeval *, struct netinfo *)); +struct tsp *acksend __P((struct tsp *, + struct sockaddr_in *, char *, int, struct netinfo *, int)); + +void addnetname __P((char *)); +void adj_msg_time __P((struct tsp *, struct timeval *)); +void bytehostorder __P((struct tsp *)); +void bytenetorder __P((struct tsp *)); +void byteorder __P((struct tsp *)); +long casual __P((long, long)); +int cksum __P((u_short *, int)); +void correct __P((long)); +char *date __P((void)); +void doquit __P((struct tsp *)); +int election __P((struct netinfo *)); +void get_goodgroup __P((int)); +int good_host_name __P((char *)); +void ignoreack __P((void)); +int in_cksum __P((u_short *, int)); +void lookformaster __P((struct netinfo *)); +void makeslave __P((struct netinfo *)); +int master __P((void)); +void masterack __P((void)); +void masterup __P((struct netinfo *)); +int measure __P((u_long, u_long, char *, struct sockaddr_in *, int)); +void msterup __P((struct netinfo *)); +void mstotvround __P((struct timeval *, long)); +long networkdelta __P((void)); +void newslave __P((struct tsp *)); +void print __P((struct tsp *, struct sockaddr_in *)); +void prthp __P((clock_t)); +void rmnetmachs __P((struct netinfo *)); +void setstatus __P((void)); +int slave __P((void)); +void slaveack __P((void)); +void spreadtime __P((void)); +void suppress __P((struct sockaddr_in *, char *, struct netinfo *)); +void synch __P((long)); +void timevaladd __P((struct timeval *, struct timeval *)); +void timevalsub __P((struct timeval *, struct timeval *, struct timeval *)); +void traceoff __P((char *)); +void traceon __P((void)); +void xmit __P((int, u_int, struct sockaddr_in *)); diff --git a/usr.sbin/timed/timed/globals.h b/usr.sbin/timed/timed/globals.h new file mode 100644 index 0000000..92aed3e --- /dev/null +++ b/usr.sbin/timed/timed/globals.h @@ -0,0 +1,186 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)globals.h 8.1 (Berkeley) 6/6/93 + */ + +#ifdef sgi +#ident "$Revision: 1.15 $" +#endif + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <syslog.h> +#include <unistd.h> + +#include <protocols/timed.h> +#ifdef sgi +#include <bstring.h> +#include <sys/clock.h> +/* use the constant HZ instead of the function CLK_TCK */ +#undef CLK_TCK +#define CLK_TCK HZ +#else +#define SECHR (60*60) +#define SECDAY (24*SECHR) +#endif /* sgi */ + +extern int errno; +extern int sock; + +/* Best expected round trip for a measurement. + * This is essentially the number of milliseconds per CPU tick (CLK_TCK?). + * All delays shorter than this are usually reported as 0. + */ +#define MIN_ROUND ((1000-1)/CLK_TCK) + + +#define SAMPLEINTVL 240 /* synch() freq for master in sec */ +#define MAXADJ 20 /* max adjtime() correction in sec */ + +#define MAX_TRIM 3000000 /* max drift in nsec/sec, 0.3% */ +#define BIG_ADJ (MAX_TRIM/1000*SAMPLEINTVL*2) /* max good adj */ + +#define MINTOUT 360 /* election delays, 6-15 minutes */ +#define MAXTOUT 900 + +#define BAD_STATUS (-1) +#define GOOD 1 +#define UNREACHABLE 2 +#define NONSTDTIME 3 +#define HOSTDOWN 0x7fffffff + +#define OFF 0 +#define ON 1 + +#define MAX_HOPCNT 10 /* max value for tsp_hpcnt */ + +#define LOSTHOST 3 /* forget after this many failures */ + +#define VALID_RANGE (MAXADJ*1000) /* good times in milliseconds */ +#define GOOD_RANGE (MIN_ROUND*2) +#define VGOOD_RANGE (MIN_ROUND-1) + + +/* + * Global and per-network states. + */ +#define NOMASTER 0 /* no good master */ +#define SLAVE 1 +#define MASTER 2 +#define IGNORE 4 +#define ALL (SLAVE|MASTER|IGNORE) +#define SUBMASTER (SLAVE|MASTER) + +#define NHOSTS 1013 /* max of hosts controlled by timed + * This must be a prime number. + */ +struct hosttbl { + struct hosttbl *h_bak; /* hash chain */ + struct hosttbl *h_fwd; + struct hosttbl *l_bak; /* "sequential" list */ + struct hosttbl *l_fwd; + struct netinfo *ntp; + struct sockaddr_in addr; + char name[MAXHOSTNAMELEN+1]; + u_char head; /* 1=head of hash chain */ + u_char good; /* 0=trusted host, for averaging */ + u_char noanswer; /* count of failures to answer */ + u_char need_set; /* need a SETTIME */ + u_short seq; + long delta; +}; + +/* closed hash table with internal chaining */ +extern struct hosttbl hosttbl[NHOSTS+1]; +#define self hosttbl[0] +#define hostname (self.name) + + +struct netinfo { + struct netinfo *next; + struct in_addr net; + u_long mask; + struct in_addr my_addr; + struct sockaddr_in dest_addr; /* broadcast addr or point-point */ + long status; + struct timeval slvwait; /* delay before sending our time */ + int quit_count; /* recent QUITs */ +}; + +#include "extern.h" + +#define tvtomsround(tv) ((tv).tv_sec*1000 + ((tv).tv_usec + 500)/1000) + +extern struct netinfo *nettab; +extern int status; +extern int trace; +extern int sock; +extern struct sockaddr_in from; +extern struct timeval from_when; /* when the last msg arrived */ +extern u_short sequence; /* TSP message sequence number */ +extern struct netinfo *fromnet, *slavenet; +extern FILE *fd; +extern long delay1, delay2; +extern int nslavenets; /* nets were I could be a slave */ +extern int nmasternets; /* nets were I could be a master */ +extern int nignorednets; /* ignored nets */ +extern int nnets; /* nets I am connected to */ + + +#define trace_msg(msg) {if (trace) fprintf(fd, msg);} + +#define trace_sendto_err(addr) { \ + int st_errno = errno; \ + syslog(LOG_ERR, "%s %d: sendto %s: %m", \ + __FILE__, __LINE__, inet_ntoa(addr)); \ + if (trace) \ + fprintf(fd, "%s %d: sendto %s: %d", __FILE__, __LINE__, \ + inet_ntoa(addr), st_errno); \ +} + + +# define max(a,b) (a<b ? b : a) +# define min(a,b) (a>b ? b : a) +# define abs(x) (x>=0 ? x : -(x)) diff --git a/usr.sbin/timed/timed/master.c b/usr.sbin/timed/timed/master.c new file mode 100644 index 0000000..3397173 --- /dev/null +++ b/usr.sbin/timed/timed/master.c @@ -0,0 +1,907 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)master.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.21 $" +#endif + +#include "globals.h" +#include <sys/file.h> +#include <sys/types.h> +#include <sys/times.h> +#include <setjmp.h> +#ifdef sgi +#include <sys/schedctl.h> +#endif /* sgi */ +#include <utmp.h> +#include "pathnames.h" + +extern int measure_delta; +extern jmp_buf jmpenv; +extern int Mflag; +extern int justquit; + +static int dictate; +static int slvcount; /* slaves listening to our clock */ + +static void mchgdate __P((struct tsp *)); + +#ifdef sgi +extern void logwtmp __P((struct timeval *, struct timeval *)); +#else +extern void logwtmp __P((char *, char *, char *)); +#endif /* sgi */ + +/* + * The main function of `master' is to periodically compute the differences + * (deltas) between its clock and the clocks of the slaves, to compute the + * network average delta, and to send to the slaves the differences between + * their individual deltas and the network delta. + * While waiting, it receives messages from the slaves (i.e. requests for + * master's name, remote requests to set the network time, ...), and + * takes the appropriate action. + */ +int +master() +{ + struct hosttbl *htp; + long pollingtime; +#define POLLRATE 4 + int polls; + struct timeval wait, ntime; + struct tsp *msg, *answer, to; + char newdate[32]; + struct sockaddr_in taddr; + char tname[MAXHOSTNAMELEN]; + struct netinfo *ntp; + int i; + + syslog(LOG_NOTICE, "This machine is master"); + if (trace) + fprintf(fd, "This machine is master\n"); + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if (ntp->status == MASTER) + masterup(ntp); + } + (void)gettimeofday(&ntime, 0); + pollingtime = ntime.tv_sec+3; + if (justquit) + polls = 0; + else + polls = POLLRATE-1; + +/* Process all outstanding messages before spending the long time necessary + * to update all timers. + */ +loop: + (void)gettimeofday(&ntime, 0); + wait.tv_sec = pollingtime - ntime.tv_sec; + if (wait.tv_sec < 0) + wait.tv_sec = 0; + wait.tv_usec = 0; + msg = readmsg(TSP_ANY, ANYADDR, &wait, 0); + if (!msg) { + (void)gettimeofday(&ntime, 0); + if (ntime.tv_sec >= pollingtime) { + pollingtime = ntime.tv_sec + SAMPLEINTVL; + get_goodgroup(0); + +/* If a bogus master told us to quit, we can have decided to ignore a + * network. Therefore, periodically try to take over everything. + */ + polls = (polls + 1) % POLLRATE; + if (0 == polls && nignorednets > 0) { + trace_msg("Looking for nets to re-master\n"); + for (ntp = nettab; ntp; ntp = ntp->next) { + if (ntp->status == IGNORE + || ntp->status == NOMASTER) { + lookformaster(ntp); + if (ntp->status == MASTER) { + masterup(ntp); + polls = POLLRATE-1; + } + } + if (ntp->status == MASTER + && --ntp->quit_count < 0) + ntp->quit_count = 0; + } + if (polls != 0) + setstatus(); + } + + synch(0L); + + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + to.tsp_type = TSP_LOOP; + to.tsp_vers = TSPVERSION; + to.tsp_seq = sequence++; + to.tsp_hopcnt = MAX_HOPCNT; + (void)strcpy(to.tsp_name, hostname); + bytenetorder(&to); + if (sendto(sock, (char *)&to, + sizeof(struct tsp), 0, + (struct sockaddr*)&ntp->dest_addr, + sizeof(ntp->dest_addr)) < 0) { + trace_sendto_err(ntp->dest_addr.sin_addr); + } + } + } + + + } else { + switch (msg->tsp_type) { + + case TSP_MASTERREQ: + break; + + case TSP_SLAVEUP: + newslave(msg); + break; + + case TSP_SETDATE: + /* + * XXX check to see it is from ourself + */ +#ifdef sgi + (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); +#else + (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec)); +#endif /* sgi */ + if (!good_host_name(msg->tsp_name)) { + syslog(LOG_NOTICE, + "attempted date change by %s to %s", + msg->tsp_name, newdate); + spreadtime(); + break; + } + + mchgdate(msg); + (void)gettimeofday(&ntime, 0); + pollingtime = ntime.tv_sec + SAMPLEINTVL; + break; + + case TSP_SETDATEREQ: + if (!fromnet || fromnet->status != MASTER) + break; +#ifdef sgi + (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); +#else + (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec)); +#endif /* sgi */ + htp = findhost(msg->tsp_name); + if (htp == 0) { + syslog(LOG_ERR, + "attempted SET DATEREQ by uncontrolled %s to %s", + msg->tsp_name, newdate); + break; + } + if (htp->seq == msg->tsp_seq) + break; + htp->seq = msg->tsp_seq; + if (!htp->good) { + syslog(LOG_NOTICE, + "attempted SET DATEREQ by untrusted %s to %s", + msg->tsp_name, newdate); + spreadtime(); + break; + } + + mchgdate(msg); + (void)gettimeofday(&ntime, 0); + pollingtime = ntime.tv_sec + SAMPLEINTVL; + break; + + case TSP_MSITE: + xmit(TSP_ACK, msg->tsp_seq, &from); + break; + + case TSP_MSITEREQ: + break; + + case TSP_TRACEON: + traceon(); + break; + + case TSP_TRACEOFF: + traceoff("Tracing ended at %s\n"); + break; + + case TSP_ELECTION: + if (!fromnet) + break; + if (fromnet->status == MASTER) { + pollingtime = 0; + (void)addmach(msg->tsp_name, &from,fromnet); + } + taddr = from; + (void)strcpy(tname, msg->tsp_name); + to.tsp_type = TSP_QUIT; + (void)strcpy(to.tsp_name, hostname); + answer = acksend(&to, &taddr, tname, + TSP_ACK, 0, 1); + if (answer == NULL) { + syslog(LOG_ERR, "election error by %s", + tname); + } + break; + + case TSP_CONFLICT: + /* + * After a network partition, there can be + * more than one master: the first slave to + * come up will notify here the situation. + */ + if (!fromnet || fromnet->status != MASTER) + break; + (void)strcpy(to.tsp_name, hostname); + + /* The other master often gets into the same state, + * with boring results if we stay at it forever. + */ + ntp = fromnet; /* (acksend() can leave fromnet=0 */ + for (i = 0; i < 3; i++) { + to.tsp_type = TSP_RESOLVE; + (void)strcpy(to.tsp_name, hostname); + answer = acksend(&to, &ntp->dest_addr, + ANYADDR, TSP_MASTERACK, + ntp, 0); + if (!answer) + break; + htp = addmach(answer->tsp_name,&from,ntp); + to.tsp_type = TSP_QUIT; + msg = acksend(&to, &htp->addr, htp->name, + TSP_ACK, 0, htp->noanswer); + if (msg == NULL) { + syslog(LOG_ERR, + "no response from %s to CONFLICT-QUIT", + htp->name); + } + } + masterup(ntp); + pollingtime = 0; + break; + + case TSP_RESOLVE: + if (!fromnet || fromnet->status != MASTER) + break; + /* + * do not want to call synch() while waiting + * to be killed! + */ + (void)gettimeofday(&ntime, (struct timezone *)0); + pollingtime = ntime.tv_sec + SAMPLEINTVL; + break; + + case TSP_QUIT: + doquit(msg); /* become a slave */ + break; + + case TSP_LOOP: + if (!fromnet || fromnet->status != MASTER + || !strcmp(msg->tsp_name, hostname)) + break; + /* + * We should not have received this from a net + * we are master on. There must be two masters. + */ + htp = addmach(msg->tsp_name, &from,fromnet); + to.tsp_type = TSP_QUIT; + (void)strcpy(to.tsp_name, hostname); + answer = acksend(&to, &htp->addr, htp->name, + TSP_ACK, 0, 1); + if (!answer) { + syslog(LOG_WARNING, + "loop breakage: no reply from %s=%s to QUIT", + htp->name, inet_ntoa(htp->addr.sin_addr)); + (void)remmach(htp); + } + + case TSP_TEST: + if (trace) { + fprintf(fd, + "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n", + nnets, nmasternets, nslavenets, nignorednets); + setstatus(); + } + pollingtime = 0; + polls = POLLRATE-1; + break; + + default: + if (trace) { + fprintf(fd, "garbage message: "); + print(msg, &from); + } + break; + } + } + goto loop; +} + + +/* + * change the system date on the master + */ +static void +mchgdate(msg) + struct tsp *msg; +{ + char tname[MAXHOSTNAMELEN]; + char olddate[32]; + struct timeval otime, ntime; + + (void)strcpy(tname, msg->tsp_name); + + xmit(TSP_DATEACK, msg->tsp_seq, &from); + + (void)strcpy(olddate, date()); + + /* adjust time for residence on the queue */ + (void)gettimeofday(&otime, 0); + adj_msg_time(msg,&otime); + + timevalsub(&ntime, &msg->tsp_time, &otime); + if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) { + /* + * do not change the clock if we can adjust it + */ + dictate = 3; + synch(tvtomsround(ntime)); + } else { +#ifdef sgi + if (0 > settimeofday(&msg->tsp_time, 0)) { + syslog(LOG_ERR, "settimeofday(): %m"); + } + logwtmp(&otime, &msg->tsp_time); +#else + logwtmp("|", "date", ""); + (void)settimeofday(&msg->tsp_time, 0); + logwtmp("}", "date", ""); +#endif /* sgi */ + spreadtime(); + } + + syslog(LOG_NOTICE, "date changed by %s from %s", + tname, olddate); +} + + +/* + * synchronize all of the slaves + */ +void +synch(mydelta) + long mydelta; +{ + struct hosttbl *htp; + int measure_status; + struct timeval check, stop, wait; +#ifdef sgi + int pri; +#endif /* sgi */ + + if (slvcount > 0) { + if (trace) + fprintf(fd, "measurements starting at %s\n", date()); + (void)gettimeofday(&check, 0); +#ifdef sgi + /* run fast to get good time */ + pri = schedctl(NDPRI,0,NDPHIMIN); + if (pri < 0) + syslog(LOG_ERR, "schedctl(): %m"); +#endif /* sgi */ + for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { + if (htp->noanswer != 0) { + measure_status = measure(500, 100, + htp->name, + &htp->addr,0); + } else { + measure_status = measure(3000, 100, + htp->name, + &htp->addr,0); + } + if (measure_status != GOOD) { + /* The slave did not respond. We have + * just wasted lots of time on it. + */ + htp->delta = HOSTDOWN; + if (++htp->noanswer >= LOSTHOST) { + if (trace) { + fprintf(fd, + "purging %s for not answering ICMP\n", + htp->name); + (void)fflush(fd); + } + htp = remmach(htp); + } + } else { + htp->delta = measure_delta; + } + (void)gettimeofday(&stop, 0); + timevalsub(&stop, &stop, &check); + if (stop.tv_sec >= 1) { + if (trace) + (void)fflush(fd); + /* + * ack messages periodically + */ + wait.tv_sec = 0; + wait.tv_usec = 0; + if (0 != readmsg(TSP_TRACEON,ANYADDR, + &wait,0)) + traceon(); + (void)gettimeofday(&check, 0); + } + } +#ifdef sgi + if (pri >= 0) + (void)schedctl(NDPRI,0,pri); +#endif /* sgi */ + if (trace) + fprintf(fd, "measurements finished at %s\n", date()); + } + if (!(status & SLAVE)) { + if (!dictate) { + mydelta = networkdelta(); + } else { + dictate--; + } + } + if (trace && (mydelta != 0 || (status & SLAVE))) + fprintf(fd,"local correction of %ld ms.\n", mydelta); + correct(mydelta); +} + +/* + * sends the time to each slave after the master + * has received the command to set the network time + */ +void +spreadtime() +{ + struct hosttbl *htp; + struct tsp to; + struct tsp *answer; + +/* Do not listen to the consensus after forcing the time. This is because + * the consensus takes a while to reach the time we are dictating. + */ + dictate = 2; + for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { + to.tsp_type = TSP_SETTIME; + (void)strcpy(to.tsp_name, hostname); + (void)gettimeofday(&to.tsp_time, 0); + answer = acksend(&to, &htp->addr, htp->name, + TSP_ACK, 0, htp->noanswer); + if (answer == 0) { + /* We client does not respond, then we have + * just wasted lots of time on it. + */ + syslog(LOG_WARNING, + "no reply to SETTIME from %s", htp->name); + if (++htp->noanswer >= LOSTHOST) { + if (trace) { + fprintf(fd, + "purging %s for not answering", + htp->name); + (void)fflush(fd); + } + htp = remmach(htp); + } + } + } +} + +void +prthp(delta) + clock_t delta; +{ + static time_t next_time; + time_t this_time; + struct tms tm; + struct hosttbl *htp; + int length, l; + int i; + + if (!fd) /* quit if tracing already off */ + return; + + this_time = times(&tm); + if (this_time + delta < next_time) + return; + next_time = this_time + CLK_TCK; + + fprintf(fd, "host table: %d entries at %s\n", slvcount, date()); + htp = self.l_fwd; + length = 1; + for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) { + l = strlen(htp->name) + 1; + if (length+l >= 80) { + fprintf(fd, "\n"); + length = 0; + } + length += l; + fprintf(fd, " %s", htp->name); + } + fprintf(fd, "\n"); +} + + +static struct hosttbl *newhost_hash; +static struct hosttbl *lasthfree = &hosttbl[0]; + + +struct hosttbl * /* answer or 0 */ +findhost(name) + char *name; +{ + int i, j; + struct hosttbl *htp; + char *p; + + j= 0; + for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++) + j = (j << 2) ^ *p; + newhost_hash = &hosttbl[j % NHOSTS]; + + htp = newhost_hash; + if (htp->name[0] == '\0') + return(0); + do { + if (!strcmp(name, htp->name)) + return(htp); + htp = htp->h_fwd; + } while (htp != newhost_hash); + return(0); +} + +/* + * add a host to the list of controlled machines if not already there + */ +struct hosttbl * +addmach(name, addr, ntp) + char *name; + struct sockaddr_in *addr; + struct netinfo *ntp; +{ + struct hosttbl *ret, *p, *b, *f; + + ret = findhost(name); + if (ret == 0) { + if (slvcount >= NHOSTS) { + if (trace) { + fprintf(fd, "no more slots in host table\n"); + prthp(CLK_TCK); + } + syslog(LOG_ERR, "no more slots in host table"); + Mflag = 0; + longjmp(jmpenv, 2); /* give up and be a slave */ + } + + /* if our home hash slot is occupied, find a free entry + * in the hash table + */ + if (newhost_hash->name[0] != '\0') { + do { + ret = lasthfree; + if (++lasthfree > &hosttbl[NHOSTS]) + lasthfree = &hosttbl[1]; + } while (ret->name[0] != '\0'); + + if (!newhost_hash->head) { + /* Move an interloper using our home. Use + * scratch pointers in case the new head is + * pointing to itself. + */ + f = newhost_hash->h_fwd; + b = newhost_hash->h_bak; + f->h_bak = ret; + b->h_fwd = ret; + f = newhost_hash->l_fwd; + b = newhost_hash->l_bak; + f->l_bak = ret; + b->l_fwd = ret; + bcopy(newhost_hash,ret,sizeof(*ret)); + ret = newhost_hash; + ret->head = 1; + ret->h_fwd = ret; + ret->h_bak = ret; + } else { + /* link to an existing chain in our home + */ + ret->head = 0; + p = newhost_hash->h_bak; + ret->h_fwd = newhost_hash; + ret->h_bak = p; + p->h_fwd = ret; + newhost_hash->h_bak = ret; + } + } else { + ret = newhost_hash; + ret->head = 1; + ret->h_fwd = ret; + ret->h_bak = ret; + } + ret->addr = *addr; + ret->ntp = ntp; + (void)strncpy(ret->name, name, sizeof(ret->name)); + ret->good = good_host_name(name); + ret->l_fwd = &self; + ret->l_bak = self.l_bak; + self.l_bak->l_fwd = ret; + self.l_bak = ret; + slvcount++; + + ret->noanswer = 0; + ret->need_set = 1; + + } else { + ret->noanswer = (ret->noanswer != 0); + } + + /* need to clear sequence number anyhow */ + ret->seq = 0; + return(ret); +} + +/* + * remove the machine with the given index in the host table. + */ +struct hosttbl * +remmach(htp) + struct hosttbl *htp; +{ + struct hosttbl *lprv, *hnxt, *f, *b; + + if (trace) + fprintf(fd, "remove %s\n", htp->name); + + /* get out of the lists */ + htp->l_fwd->l_bak = lprv = htp->l_bak; + htp->l_bak->l_fwd = htp->l_fwd; + htp->h_fwd->h_bak = htp->h_bak; + htp->h_bak->h_fwd = hnxt = htp->h_fwd; + + /* If we are in the home slot, pull up the chain */ + if (htp->head && hnxt != htp) { + if (lprv == hnxt) + lprv = htp; + + /* Use scratch pointers in case the new head is pointing to + * itself. + */ + f = hnxt->h_fwd; + b = hnxt->h_bak; + f->h_bak = htp; + b->h_fwd = htp; + f = hnxt->l_fwd; + b = hnxt->l_bak; + f->l_bak = htp; + b->l_fwd = htp; + hnxt->head = 1; + bcopy(hnxt, htp, sizeof(*htp)); + lasthfree = hnxt; + } else { + lasthfree = htp; + } + + lasthfree->name[0] = '\0'; + lasthfree->h_fwd = 0; + lasthfree->l_fwd = 0; + slvcount--; + + return lprv; +} + + +/* + * Remove all the machines from the host table that exist on the given + * network. This is called when a master transitions to a slave on a + * given network. + */ +void +rmnetmachs(ntp) + struct netinfo *ntp; +{ + struct hosttbl *htp; + + if (trace) + prthp(CLK_TCK); + for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { + if (ntp == htp->ntp) + htp = remmach(htp); + } + if (trace) + prthp(CLK_TCK); +} + +void +masterup(net) + struct netinfo *net; +{ + xmit(TSP_MASTERUP, 0, &net->dest_addr); + + /* + * Do not tell new slaves our time for a while. This ensures + * we do not tell them to start using our time, before we have + * found a good master. + */ + (void)gettimeofday(&net->slvwait, 0); +} + +void +newslave(msg) + struct tsp *msg; +{ + struct hosttbl *htp; + struct tsp *answer, to; + struct timeval now; + + if (!fromnet || fromnet->status != MASTER) + return; + + htp = addmach(msg->tsp_name, &from,fromnet); + htp->seq = msg->tsp_seq; + if (trace) + prthp(0); + + /* + * If we are stable, send our time to the slave. + * Do not go crazy if the date has been changed. + */ + (void)gettimeofday(&now, 0); + if (now.tv_sec >= fromnet->slvwait.tv_sec+3 + || now.tv_sec < fromnet->slvwait.tv_sec) { + to.tsp_type = TSP_SETTIME; + (void)strcpy(to.tsp_name, hostname); + (void)gettimeofday(&to.tsp_time, 0); + answer = acksend(&to, &htp->addr, + htp->name, TSP_ACK, + 0, htp->noanswer); + if (answer) { + htp->need_set = 0; + } else { + syslog(LOG_WARNING, + "no reply to initial SETTIME from %s", + htp->name); + htp->noanswer = LOSTHOST; + } + } +} + + +/* + * react to a TSP_QUIT: + */ +void +doquit(msg) + struct tsp *msg; +{ + if (fromnet->status == MASTER) { + if (!good_host_name(msg->tsp_name)) { + if (fromnet->quit_count <= 0) { + syslog(LOG_NOTICE,"untrusted %s told us QUIT", + msg->tsp_name); + suppress(&from, msg->tsp_name, fromnet); + fromnet->quit_count = 1; + return; + } + syslog(LOG_NOTICE, "untrusted %s told us QUIT twice", + msg->tsp_name); + fromnet->quit_count = 2; + fromnet->status = NOMASTER; + } else { + fromnet->status = SLAVE; + } + rmnetmachs(fromnet); + longjmp(jmpenv, 2); /* give up and be a slave */ + + } else { + if (!good_host_name(msg->tsp_name)) { + syslog(LOG_NOTICE, "untrusted %s told us QUIT", + msg->tsp_name); + fromnet->quit_count = 2; + } + } +} + +void +traceon() +{ + if (!fd) { + fd = fopen(_PATH_TIMEDLOG, "w"); + if (!fd) { + trace = 0; + return; + } + fprintf(fd,"Tracing started at %s\n", date()); + } + trace = 1; + get_goodgroup(1); + setstatus(); + prthp(CLK_TCK); +} + + +void +traceoff(msg) + char *msg; +{ + get_goodgroup(1); + setstatus(); + prthp(CLK_TCK); + if (trace) { + fprintf(fd, msg, date()); + (void)fclose(fd); + fd = 0; + } +#ifdef GPROF + moncontrol(0); + _mcleanup(); + moncontrol(1); +#endif + trace = OFF; +} + + +#ifdef sgi +void +logwtmp(otime, ntime) + struct timeval *otime, *ntime; +{ + static struct utmp wtmp[2] = { + {"","",OTIME_MSG,0,OLD_TIME,0,0,0}, + {"","",NTIME_MSG,0,NEW_TIME,0,0,0} + }; + static char *wtmpfile = WTMP_FILE; + int f; + + wtmp[0].ut_time = otime->tv_sec + (otime->tv_usec + 500000) / 1000000; + wtmp[1].ut_time = ntime->tv_sec + (ntime->tv_usec + 500000) / 1000000; + if (wtmp[0].ut_time == wtmp[1].ut_time) + return; + + setutent(); + (void)pututline(&wtmp[0]); + (void)pututline(&wtmp[1]); + endutent(); + if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) { + (void) write(f, (char *)wtmp, sizeof(wtmp)); + (void) close(f); + } +} +#endif /* sgi */ diff --git a/usr.sbin/timed/timed/measure.c b/usr.sbin/timed/timed/measure.c new file mode 100644 index 0000000..4066d10 --- /dev/null +++ b/usr.sbin/timed/timed/measure.c @@ -0,0 +1,353 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)measure.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.8 $" +#endif + +#include "globals.h" +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> + +#define MSEC_DAY (SECDAY*1000) + +#define PACKET_IN 1024 + +#define MSGS 5 /* timestamps to average */ +#define TRIALS 10 /* max # of timestamps sent */ + +extern int sock_raw; + +int measure_delta; + +static n_short seqno = 0; + +/* + * Measures the differences between machines' clocks using + * ICMP timestamp messages. + */ +int /* status val defined in globals.h */ +measure(maxmsec, wmsec, hname, addr, print) + u_long maxmsec; /* wait this many msec at most */ + u_long wmsec; /* msec to wait for an answer */ + char *hname; + struct sockaddr_in *addr; + int print; /* print complaints on stderr */ +{ + int length; + int measure_status; + int rcvcount, trials; + int cc, count; + fd_set ready; + long sendtime, recvtime, histime1, histime2; + long idelta, odelta, total; + long min_idelta, min_odelta; + struct timeval tdone, tcur, ttrans, twait, tout; + u_char packet[PACKET_IN], opacket[64]; + register struct icmp *icp = (struct icmp *) packet; + register struct icmp *oicp = (struct icmp *) opacket; + struct ip *ip = (struct ip *) packet; + + min_idelta = min_odelta = 0x7fffffff; + measure_status = HOSTDOWN; + measure_delta = HOSTDOWN; + errno = 0; + + /* open raw socket used to measure time differences */ + if (sock_raw < 0) { + sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sock_raw < 0) { + syslog(LOG_ERR, "opening raw socket: %m"); + goto quit; + } + } + + + /* + * empty the icmp input queue + */ + FD_ZERO(&ready); + for (;;) { + tout.tv_sec = tout.tv_usec = 0; + FD_SET(sock_raw, &ready); + if (select(sock_raw+1, &ready, 0,0, &tout)) { + length = sizeof(struct sockaddr_in); + cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, + 0,&length); + if (cc < 0) + goto quit; + continue; + } + break; + } + + /* + * Choose the smallest transmission time in each of the two + * directions. Use these two latter quantities to compute the delta + * between the two clocks. + */ + + oicp->icmp_type = ICMP_TSTAMP; + oicp->icmp_code = 0; + oicp->icmp_id = getpid(); + oicp->icmp_rtime = 0; + oicp->icmp_ttime = 0; + oicp->icmp_seq = seqno; + + FD_ZERO(&ready); + +#ifdef sgi + sginap(1); /* start at a clock tick */ +#endif /* sgi */ + + (void)gettimeofday(&tdone, 0); + mstotvround(&tout, maxmsec); + timevaladd(&tdone, &tout); /* when we give up */ + + mstotvround(&twait, wmsec); + + rcvcount = 0; + trials = 0; + while (rcvcount < MSGS) { + (void)gettimeofday(&tcur, 0); + + /* + * keep sending until we have sent the max + */ + if (trials < TRIALS) { + trials++; + oicp->icmp_otime = ((tcur.tv_sec % SECDAY) * 1000 + + tcur.tv_usec / 1000); + oicp->icmp_cksum = 0; + oicp->icmp_cksum = in_cksum((u_short*)oicp, + sizeof(*oicp)); + + count = sendto(sock_raw, opacket, sizeof(*oicp), 0, + (struct sockaddr*)addr, + sizeof(struct sockaddr)); + if (count < 0) { + if (measure_status == HOSTDOWN) + measure_status = UNREACHABLE; + goto quit; + } + ++oicp->icmp_seq; + + ttrans = tcur; + timevaladd(&ttrans, &twait); + } else { + ttrans = tdone; + } + + while (rcvcount < trials) { + timevalsub(&tout, &ttrans, &tcur); + if (tout.tv_sec < 0) + tout.tv_sec = 0; + + FD_SET(sock_raw, &ready); + count = select(sock_raw+1, &ready, (fd_set *)0, + (fd_set *)0, &tout); + (void)gettimeofday(&tcur, (struct timezone *)0); + if (count <= 0) + break; + + length = sizeof(struct sockaddr_in); + cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, + 0,&length); + if (cc < 0) + goto quit; + + /* + * got something. See if it is ours + */ + icp = (struct icmp *)(packet + (ip->ip_hl << 2)); + if (cc < sizeof(*ip) + || icp->icmp_type != ICMP_TSTAMPREPLY + || icp->icmp_id != oicp->icmp_id + || icp->icmp_seq < seqno + || icp->icmp_seq >= oicp->icmp_seq) + continue; + + + sendtime = ntohl(icp->icmp_otime); + recvtime = ((tcur.tv_sec % SECDAY) * 1000 + + tcur.tv_usec / 1000); + + total = recvtime-sendtime; + if (total < 0) /* do not hassle midnight */ + continue; + + rcvcount++; + histime1 = ntohl(icp->icmp_rtime); + histime2 = ntohl(icp->icmp_ttime); + /* + * a host using a time format different from + * msec. since midnight UT (as per RFC792) should + * set the high order bit of the 32-bit time + * value it transmits. + */ + if ((histime1 & 0x80000000) != 0) { + measure_status = NONSTDTIME; + goto quit; + } + measure_status = GOOD; + + idelta = recvtime-histime2; + odelta = histime1-sendtime; + + /* do not be confused by midnight */ + if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY; + else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY; + + if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY; + else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY; + + /* save the quantization error so that we can get a + * measurement finer than our system clock. + */ + if (total < MIN_ROUND) { + measure_delta = (odelta - idelta)/2; + goto quit; + } + + if (idelta < min_idelta) + min_idelta = idelta; + if (odelta < min_odelta) + min_odelta = odelta; + + measure_delta = (min_odelta - min_idelta)/2; + } + + if (tcur.tv_sec > tdone.tv_sec + || (tcur.tv_sec == tdone.tv_sec + && tcur.tv_usec >= tdone.tv_usec)) + break; + } + +quit: + seqno += TRIALS; /* allocate our sequence numbers */ + + /* + * If no answer is received for TRIALS consecutive times, + * the machine is assumed to be down + */ + if (measure_status == GOOD) { + if (trace) { + fprintf(fd, + "measured delta %4d, %d trials to %-15s %s\n", + measure_delta, trials, + inet_ntoa(addr->sin_addr), hname); + } + } else if (print) { + if (errno != 0) + fprintf(stderr, "measure %s: %s\n", hname, + strerror(errno)); + } else { + if (errno != 0) { + syslog(LOG_ERR, "measure %s: %m", hname); + } else { + syslog(LOG_ERR, "measure: %s did not respond", hname); + } + if (trace) { + fprintf(fd, + "measure: %s failed after %d trials\n", + hname, trials); + (void)fflush(fd); + } + } + + return(measure_status); +} + + + + + +/* + * round a number of milliseconds into a struct timeval + */ +void +mstotvround(res, x) + struct timeval *res; + long x; +{ +#ifndef sgi + if (x < 0) + x = -((-x + 3)/5); + else + x = (x+3)/5; + x *= 5; +#endif /* sgi */ + res->tv_sec = x/1000; + res->tv_usec = (x-res->tv_sec*1000)*1000; + if (res->tv_usec < 0) { + res->tv_usec += 1000000; + res->tv_sec--; + } +} + +void +timevaladd(tv1, tv2) + struct timeval *tv1, *tv2; +{ + tv1->tv_sec += tv2->tv_sec; + tv1->tv_usec += tv2->tv_usec; + if (tv1->tv_usec >= 1000000) { + tv1->tv_sec++; + tv1->tv_usec -= 1000000; + } + if (tv1->tv_usec < 0) { + tv1->tv_sec--; + tv1->tv_usec += 1000000; + } +} + +void +timevalsub(res, tv1, tv2) + struct timeval *res, *tv1, *tv2; +{ + res->tv_sec = tv1->tv_sec - tv2->tv_sec; + res->tv_usec = tv1->tv_usec - tv2->tv_usec; + if (res->tv_usec >= 1000000) { + res->tv_sec++; + res->tv_usec -= 1000000; + } + if (res->tv_usec < 0) { + res->tv_sec--; + res->tv_usec += 1000000; + } +} diff --git a/usr.sbin/timed/timed/networkdelta.c b/usr.sbin/timed/timed/networkdelta.c new file mode 100644 index 0000000..d64640a --- /dev/null +++ b/usr.sbin/timed/timed/networkdelta.c @@ -0,0 +1,264 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)networkdelta.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.4 $" +#endif + +#include "globals.h" + +static long median __P((float, float *, long *, long *, unsigned int)); + +/* + * Compute a corrected date. + * Compute the median of the reasonable differences. First compute + * the median of all authorized differences, and then compute the + * median of all differences that are reasonably close to the first + * median. + * + * This differs from the original BSD implementation, which looked for + * the largest group of machines with essentially the same date. + * That assumed that machines with bad clocks would be uniformly + * distributed. Unfortunately, in real life networks, the distribution + * of machines is not uniform among models of machines, and the + * distribution of errors in clocks tends to be quite consistent + * for a given model. In other words, all model VI Supre Servres + * from GoFast Inc. tend to have about the same error. + * The original BSD implementation would chose the clock of the + * most common model, and discard all others. + * + * Therefore, get best we can do is to try to average over all + * of the machines in the network, while discarding "obviously" + * bad values. + */ +long +networkdelta() +{ + struct hosttbl *htp; + long med; + long lodelta, hidelta; + long logood, higood; + long x[NHOSTS]; + long *xp; + int numdelta; + float eps; + + /* + * compute the median of the good values + */ + med = 0; + numdelta = 1; + xp = &x[0]; + *xp = 0; /* account for ourself */ + for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { + if (htp->good + && htp->noanswer == 0 + && htp->delta != HOSTDOWN) { + med += htp->delta; + numdelta++; + *++xp = htp->delta; + } + } + + /* + * If we are the only trusted time keeper, then do not change our + * clock. There may be another time keeping service active. + */ + if (numdelta == 1) + return 0; + + med /= numdelta; + eps = med - x[0]; + if (trace) + fprintf(fd, "median of %d values starting at %ld is about ", + numdelta, med); + med = median(med, &eps, &x[0], xp+1, VALID_RANGE); + + /* + * compute the median of all values near the good median + */ + hidelta = med + GOOD_RANGE; + lodelta = med - GOOD_RANGE; + higood = med + VGOOD_RANGE; + logood = med - VGOOD_RANGE; + xp = &x[0]; + htp = &self; + do { + if (htp->noanswer == 0 + && htp->delta >= lodelta + && htp->delta <= hidelta + && (htp->good + || (htp->delta >= logood + && htp->delta <= higood))) { + *xp++ = htp->delta; + } + } while (&self != (htp = htp->l_fwd)); + + if (xp == &x[0]) { + if (trace) + fprintf(fd, "nothing close to median %ld\n", med); + return med; + } + + if (xp == &x[1]) { + if (trace) + fprintf(fd, "only value near median is %ld\n", x[0]); + return x[0]; + } + + if (trace) + fprintf(fd, "median of %d values starting at %ld is ", + xp-&x[0], med); + return median(med, &eps, &x[0], xp, 1); +} + + +/* + * compute the median of an array of signed integers, using the idea + * in <<Numerical Recipes>>. + */ +static long +median(a, eps_ptr, x, xlim, gnuf) + float a; /* initial guess for the median */ + float *eps_ptr; /* spacing near the median */ + long *x, *xlim; /* the data */ + unsigned int gnuf; /* good enough estimate */ +{ + long *xptr; + float ap = LONG_MAX; /* bounds on the median */ + float am = -LONG_MAX; + float aa; + int npts; /* # of points above & below guess */ + float xp; /* closet point above the guess */ + float xm; /* closet point below the guess */ + float eps; + float dum, sum, sumx; + int pass; +#define AMP 1.5 /* smoothing constants */ +#define AFAC 1.5 + + eps = *eps_ptr; + if (eps < 1.0) { + eps = -eps; + if (eps < 1.0) + eps = 1.0; + } + + for (pass = 1; ; pass++) { /* loop over the data */ + sum = 0.0; + sumx = 0.0; + npts = 0; + xp = LONG_MAX; + xm = -LONG_MAX; + + for (xptr = x; xptr != xlim; xptr++) { + float xx = *xptr; + + dum = xx - a; + if (dum != 0.0) { /* avoid dividing by 0 */ + if (dum > 0.0) { + npts++; + if (xx < xp) + xp = xx; + } else { + npts--; + if (xx > xm) + xm = xx; + dum = -dum; + } + dum = 1.0/(eps + dum); + sum += dum; + sumx += xx * dum; + } + } + + if (ap-am < gnuf || sum == 0) { + if (trace) + fprintf(fd, + "%ld in %d passes; early out balance=%d\n", + (long)a, pass, npts); + return a; /* guess was good enough */ + } + + aa = (sumx/sum-a)*AMP; + if (npts >= 2) { /* guess was too low */ + am = a; + aa = xp + max(0.0, aa);; + if (aa > ap) + aa = (a + ap)/2; + + } else if (npts <= -2) { /* guess was two high */ + ap = a; + aa = xm + min(0.0, aa);; + if (aa < am) + aa = (a + am)/2; + + } else { + break; /* got it */ + } + + if (a == aa) { + if (trace) + fprintf(fd, + "%ld in %d passes; force out balance=%d\n", + (long)a, pass, npts); + return a; + } + eps = AFAC*abs(aa - a); + *eps_ptr = eps; + a = aa; + } + + if (((x - xlim) % 2) != 0) { /* even number of points? */ + if (npts == 0) /* yes, return an average */ + a = (xp+xm)/2; + else if (npts > 0) + a = (a+xp)/2; + else + a = (xm+a)/2; + + } else if (npts != 0) { /* odd number of points */ + if (npts > 0) + a = xp; + else + a = xm; + } + + if (trace) + fprintf(fd, "%ld in %d passes\n", (long)a, pass); + return a; +} diff --git a/usr.sbin/timed/timed/pathnames.h b/usr.sbin/timed/timed/pathnames.h new file mode 100644 index 0000000..ae5e1c5 --- /dev/null +++ b/usr.sbin/timed/timed/pathnames.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#include <paths.h> + +#ifdef sgi +#define _PATH_MASTERLOG "/usr/adm/timed.masterlog" +#define _PATH_TIMEDLOG "/usr/adm/timed.log" +#else +#define _PATH_MASTERLOG "/var/log/timed.masterlog" +#define _PATH_TIMEDLOG "/var/log/timed.log" +#endif diff --git a/usr.sbin/timed/timed/readmsg.c b/usr.sbin/timed/timed/readmsg.c new file mode 100644 index 0000000..3e8f4d2 --- /dev/null +++ b/usr.sbin/timed/timed/readmsg.c @@ -0,0 +1,488 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)readmsg.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.17 $" +#endif + +#include "globals.h" + +extern char *tsptype[]; + +/* + * LOOKAT checks if the message is of the requested type and comes from + * the right machine, returning 1 in case of affirmative answer + */ +#define LOOKAT(msg, mtype, mfrom, netp, froms) \ + (((mtype) == TSP_ANY || (mtype) == (msg).tsp_type) && \ + ((mfrom) == 0 || !strcmp((mfrom), (msg).tsp_name)) && \ + ((netp) == 0 || \ + ((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net.s_addr)) + +struct timeval rtime, rwait, rtout; +struct tsp msgin; +static struct tsplist { + struct tsp info; + struct timeval when; + struct sockaddr_in addr; + struct tsplist *p; +} msgslist; +struct sockaddr_in from; +struct netinfo *fromnet; +struct timeval from_when; + +/* + * `readmsg' returns message `type' sent by `machfrom' if it finds it + * either in the receive queue, or in a linked list of previously received + * messages that it maintains. + * Otherwise it waits to see if the appropriate message arrives within + * `intvl' seconds. If not, it returns NULL. + */ + +struct tsp * +readmsg(type, machfrom, intvl, netfrom) + int type; + char *machfrom; + struct timeval *intvl; + struct netinfo *netfrom; +{ + int length; + fd_set ready; + static struct tsplist *head = &msgslist; + static struct tsplist *tail = &msgslist; + static int msgcnt = 0; + struct tsplist *prev; + register struct netinfo *ntp; + register struct tsplist *ptr; + + if (trace) { + fprintf(fd, "readmsg: looking for %s from %s, %s\n", + tsptype[type], machfrom == NULL ? "ANY" : machfrom, + netfrom == NULL ? "ANYNET" : inet_ntoa(netfrom->net)); + if (head->p != 0) { + length = 1; + for (ptr = head->p; ptr != 0; ptr = ptr->p) { + /* do not repeat the hundreds of messages */ + if (++length > 3) { + if (ptr == tail) { + fprintf(fd,"\t ...%d skipped\n", + length); + } else { + continue; + } + } + fprintf(fd, length > 1 ? "\t" : "queue:\t"); + print(&ptr->info, &ptr->addr); + } + } + } + + ptr = head->p; + prev = head; + + /* + * Look for the requested message scanning through the + * linked list. If found, return it and free the space + */ + + while (ptr != NULL) { + if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) { +again: + msgin = ptr->info; + from = ptr->addr; + from_when = ptr->when; + prev->p = ptr->p; + if (ptr == tail) + tail = prev; + free((char *)ptr); + fromnet = NULL; + if (netfrom == NULL) + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if ((ntp->mask & from.sin_addr.s_addr) == + ntp->net.s_addr) { + fromnet = ntp; + break; + } + } + else + fromnet = netfrom; + if (trace) { + fprintf(fd, "readmsg: found "); + print(&msgin, &from); + } + +/* The protocol can get far behind. When it does, it gets + * hopelessly confused. So delete duplicate messages. + */ + for (ptr = prev; (ptr = ptr->p) != NULL; prev = ptr) { + if (ptr->addr.sin_addr.s_addr + == from.sin_addr.s_addr + && ptr->info.tsp_type == msgin.tsp_type) { + if (trace) + fprintf(fd, "\tdup "); + goto again; + } + } + msgcnt--; + return(&msgin); + } else { + prev = ptr; + ptr = ptr->p; + } + } + + /* + * If the message was not in the linked list, it may still be + * coming from the network. Set the timer and wait + * on a select to read the next incoming message: if it is the + * right one, return it, otherwise insert it in the linked list. + */ + + (void)gettimeofday(&rtout, 0); + timevaladd(&rtout, intvl); + FD_ZERO(&ready); + for (;;) { + (void)gettimeofday(&rtime, 0); + timevalsub(&rwait, &rtout, &rtime); + if (rwait.tv_sec < 0) + rwait.tv_sec = rwait.tv_usec = 0; + else if (rwait.tv_sec == 0 + && rwait.tv_usec < 1000000/CLK_TCK) + rwait.tv_usec = 1000000/CLK_TCK; + + if (trace) { + fprintf(fd, "readmsg: wait %ld.%6ld at %s\n", + rwait.tv_sec, rwait.tv_usec, date()); + /* Notice a full disk, as we flush trace info. + * It is better to flush periodically than at + * every line because the tracing consists of bursts + * of many lines. Without care, tracing slows + * down the code enough to break the protocol. + */ + if (rwait.tv_sec != 0 + && EOF == fflush(fd)) + traceoff("Tracing ended for cause at %s\n"); + } + + FD_SET(sock, &ready); + if (!select(sock+1, &ready, (fd_set *)0, (fd_set *)0, + &rwait)) { + if (rwait.tv_sec == 0 && rwait.tv_usec == 0) + return(0); + continue; + } + length = sizeof(from); + if (recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 0, + (struct sockaddr*)&from, &length) < 0) { + syslog(LOG_ERR, "recvfrom: %m"); + exit(1); + } + (void)gettimeofday(&from_when, (struct timezone *)0); + bytehostorder(&msgin); + + if (msgin.tsp_vers > TSPVERSION) { + if (trace) { + fprintf(fd,"readmsg: version mismatch\n"); + /* should do a dump of the packet */ + } + continue; + } + + fromnet = NULL; + for (ntp = nettab; ntp != NULL; ntp = ntp->next) + if ((ntp->mask & from.sin_addr.s_addr) == + ntp->net.s_addr) { + fromnet = ntp; + break; + } + + /* + * drop packets from nets we are ignoring permanently + */ + if (fromnet == NULL) { + /* + * The following messages may originate on + * this host with an ignored network address + */ + if (msgin.tsp_type != TSP_TRACEON && + msgin.tsp_type != TSP_SETDATE && + msgin.tsp_type != TSP_MSITE && + msgin.tsp_type != TSP_TEST && + msgin.tsp_type != TSP_TRACEOFF) { + if (trace) { + fprintf(fd,"readmsg: discard null net "); + print(&msgin, &from); + } + continue; + } + } + + /* + * Throw away messages coming from this machine, + * unless they are of some particular type. + * This gets rid of broadcast messages and reduces + * master processing time. + */ + if (!strcmp(msgin.tsp_name, hostname) + && msgin.tsp_type != TSP_SETDATE + && msgin.tsp_type != TSP_TEST + && msgin.tsp_type != TSP_MSITE + && msgin.tsp_type != TSP_TRACEON + && msgin.tsp_type != TSP_TRACEOFF + && msgin.tsp_type != TSP_LOOP) { + if (trace) { + fprintf(fd, "readmsg: discard own "); + print(&msgin, &from); + } + continue; + } + + /* + * Send acknowledgements here; this is faster and + * avoids deadlocks that would occur if acks were + * sent from a higher level routine. Different + * acknowledgements are necessary, depending on + * status. + */ + if (fromnet == NULL) /* do not de-reference 0 */ + ignoreack(); + else if (fromnet->status == MASTER) + masterack(); + else if (fromnet->status == SLAVE) + slaveack(); + else + ignoreack(); + + if (LOOKAT(msgin, type, machfrom, netfrom, from)) { + if (trace) { + fprintf(fd, "readmsg: "); + print(&msgin, &from); + } + return(&msgin); + } else if (++msgcnt > NHOSTS*3) { + +/* The protocol gets hopelessly confused if it gets too far +* behind. However, it seems able to recover from all cases of lost +* packets. Therefore, if we are swamped, throw everything away. +*/ + if (trace) + fprintf(fd, + "readmsg: discarding %d msgs\n", + msgcnt); + msgcnt = 0; + while ((ptr=head->p) != NULL) { + head->p = ptr->p; + free((char *)ptr); + } + tail = head; + } else { + tail->p = (struct tsplist *) + malloc(sizeof(struct tsplist)); + tail = tail->p; + tail->p = NULL; + tail->info = msgin; + tail->addr = from; + /* timestamp msgs so SETTIMEs are correct */ + tail->when = from_when; + } + } +} + +/* + * Send the necessary acknowledgements: + * only the type ACK is to be sent by a slave + */ +void +slaveack() +{ + switch(msgin.tsp_type) { + + case TSP_ADJTIME: + case TSP_SETTIME: + case TSP_ACCEPT: + case TSP_REFUSE: + case TSP_TRACEON: + case TSP_TRACEOFF: + case TSP_QUIT: + if (trace) { + fprintf(fd, "Slaveack: "); + print(&msgin, &from); + } + xmit(TSP_ACK,msgin.tsp_seq, &from); + break; + + default: + if (trace) { + fprintf(fd, "Slaveack: no ack: "); + print(&msgin, &from); + } + break; + } +} + +/* + * Certain packets may arrive from this machine on ignored networks. + * These packets should be acknowledged. + */ +void +ignoreack() +{ + switch(msgin.tsp_type) { + + case TSP_TRACEON: + case TSP_TRACEOFF: + case TSP_QUIT: + if (trace) { + fprintf(fd, "Ignoreack: "); + print(&msgin, &from); + } + xmit(TSP_ACK,msgin.tsp_seq, &from); + break; + + default: + if (trace) { + fprintf(fd, "Ignoreack: no ack: "); + print(&msgin, &from); + } + break; + } +} + +/* + * `masterack' sends the necessary acknowledgments + * to the messages received by a master + */ +void +masterack() +{ + struct tsp resp; + + resp = msgin; + resp.tsp_vers = TSPVERSION; + (void)strcpy(resp.tsp_name, hostname); + + switch(msgin.tsp_type) { + + case TSP_QUIT: + case TSP_TRACEON: + case TSP_TRACEOFF: + case TSP_MSITEREQ: + if (trace) { + fprintf(fd, "Masterack: "); + print(&msgin, &from); + } + xmit(TSP_ACK,msgin.tsp_seq, &from); + break; + + case TSP_RESOLVE: + case TSP_MASTERREQ: + if (trace) { + fprintf(fd, "Masterack: "); + print(&msgin, &from); + } + xmit(TSP_MASTERACK,msgin.tsp_seq, &from); + break; + + default: + if (trace) { + fprintf(fd,"Masterack: no ack: "); + print(&msgin, &from); + } + break; + } +} + +/* + * Print a TSP message + */ +void +print(msg, addr) + struct tsp *msg; + struct sockaddr_in *addr; +{ + char tm[26]; + switch (msg->tsp_type) { + + case TSP_LOOP: + fprintf(fd, "%s %d %-6u #%d %-15s %s\n", + tsptype[msg->tsp_type], + msg->tsp_vers, + msg->tsp_seq, + msg->tsp_hopcnt, + inet_ntoa(addr->sin_addr), + msg->tsp_name); + break; + + case TSP_SETTIME: + case TSP_SETDATE: + case TSP_SETDATEREQ: +#ifdef sgi + (void)cftime(tm, "%D %T", &msg->tsp_time.tv_sec); +#else + strncpy(tm, ctime(&msg->tsp_time.tv_sec)+3+1, sizeof(tm)); + tm[15] = '\0'; /* ugh */ +#endif /* sgi */ + fprintf(fd, "%s %d %-6u %s %-15s %s\n", + tsptype[msg->tsp_type], + msg->tsp_vers, + msg->tsp_seq, + tm, + inet_ntoa(addr->sin_addr), + msg->tsp_name); + break; + + case TSP_ADJTIME: + fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n", + tsptype[msg->tsp_type], + msg->tsp_vers, + msg->tsp_seq, + msg->tsp_time.tv_sec, + msg->tsp_time.tv_usec, + inet_ntoa(addr->sin_addr), + msg->tsp_name); + break; + + default: + fprintf(fd, "%s %d %-6u %-15s %s\n", + tsptype[msg->tsp_type], + msg->tsp_vers, + msg->tsp_seq, + inet_ntoa(addr->sin_addr), + msg->tsp_name); + break; + } +} diff --git a/usr.sbin/timed/timed/slave.c b/usr.sbin/timed/timed/slave.c new file mode 100644 index 0000000..3bb6d85 --- /dev/null +++ b/usr.sbin/timed/timed/slave.c @@ -0,0 +1,715 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)slave.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.20 $" +#endif + +#include "globals.h" +#include <setjmp.h> +#include "pathnames.h" + +extern jmp_buf jmpenv; +extern int Mflag; +extern int justquit; + +extern u_short sequence; + +static char master_name[MAXHOSTNAMELEN+1]; +static struct netinfo *old_slavenet; +static int old_status; + +static void schgdate __P((struct tsp *, char *)); +static void setmaster __P((struct tsp *)); +static void answerdelay __P((void)); + +#ifdef sgi +extern void logwtmp __P((struct timeval *, struct timeval *)); +#else +extern void logwtmp __P((char *, char *, char *)); +#endif /* sgi */ + +int +slave() +{ + int tries; + long electiontime, refusetime, looktime, looptime, adjtime; + u_short seq; + long fastelection; +#define FASTTOUT 3 + struct in_addr cadr; + struct timeval otime; + struct sockaddr_in taddr; + char tname[MAXHOSTNAMELEN]; + struct tsp *msg, to; + struct timeval ntime, wait; + struct tsp *answer; + int timeout(); + char olddate[32]; + char newdate[32]; + struct netinfo *ntp; + struct hosttbl *htp; + + + old_slavenet = 0; + seq = 0; + refusetime = 0; + adjtime = 0; + + (void)gettimeofday(&ntime, 0); + electiontime = ntime.tv_sec + delay2; + fastelection = ntime.tv_sec + FASTTOUT; + if (justquit) + looktime = electiontime; + else + looktime = fastelection; + looptime = fastelection; + + if (slavenet) + xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr); + if (status & MASTER) { + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if (ntp->status == MASTER) + masterup(ntp); + } + } + +loop: + get_goodgroup(0); + (void)gettimeofday(&ntime, (struct timezone *)0); + if (ntime.tv_sec > electiontime) { + if (trace) + fprintf(fd, "election timer expired\n"); + longjmp(jmpenv, 1); + } + + if (ntime.tv_sec >= looktime) { + if (trace) + fprintf(fd, "Looking for nets to master\n"); + + if (Mflag && nignorednets > 0) { + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if (ntp->status == IGNORE + || ntp->status == NOMASTER) { + lookformaster(ntp); + if (ntp->status == MASTER) { + masterup(ntp); + } else if (ntp->status == MASTER) { + ntp->status = NOMASTER; + } + } + if (ntp->status == MASTER + && --ntp->quit_count < 0) + ntp->quit_count = 0; + } + makeslave(slavenet); /* prune extras */ + setstatus(); + } + (void)gettimeofday(&ntime, 0); + looktime = ntime.tv_sec + delay2; + } + if (ntime.tv_sec >= looptime) { + if (trace) + fprintf(fd, "Looking for loops\n"); + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if (ntp->status == MASTER) { + to.tsp_type = TSP_LOOP; + to.tsp_vers = TSPVERSION; + to.tsp_seq = sequence++; + to.tsp_hopcnt = MAX_HOPCNT; + (void)strcpy(to.tsp_name, hostname); + bytenetorder(&to); + if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, + (struct sockaddr*)&ntp->dest_addr, + sizeof(ntp->dest_addr)) < 0) { + trace_sendto_err(ntp->dest_addr.sin_addr); + } + } + } + (void)gettimeofday(&ntime, 0); + looptime = ntime.tv_sec + delay2; + } + + wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec; + if (wait.tv_sec < 0) + wait.tv_sec = 0; + wait.tv_sec += FASTTOUT; + wait.tv_usec = 0; + msg = readmsg(TSP_ANY, ANYADDR, &wait, 0); + + if (msg != NULL) { + /* + * filter stuff not for us + */ + switch (msg->tsp_type) { + case TSP_SETDATE: + case TSP_TRACEOFF: + case TSP_TRACEON: + /* + * XXX check to see they are from ourself + */ + break; + + case TSP_TEST: + case TSP_MSITE: + break; + + case TSP_MASTERUP: + if (!fromnet) { + if (trace) { + fprintf(fd, "slave ignored: "); + print(msg, &from); + } + goto loop; + } + break; + + default: + if (!fromnet + || fromnet->status == IGNORE + || fromnet->status == NOMASTER) { + if (trace) { + fprintf(fd, "slave ignored: "); + print(msg, &from); + } + goto loop; + } + break; + } + + + /* + * now process the message + */ + switch (msg->tsp_type) { + + case TSP_ADJTIME: + if (fromnet != slavenet) + break; + if (!good_host_name(msg->tsp_name)) { + syslog(LOG_NOTICE, + "attempted time adjustment by %s", + msg->tsp_name); + suppress(&from, msg->tsp_name, fromnet); + break; + } + /* + * Speed up loop detection in case we have a loop. + * Otherwise the clocks can race until the loop + * is found. + */ + (void)gettimeofday(&otime, 0); + if (adjtime < otime.tv_sec) + looptime -= (looptime-otime.tv_sec)/2 + 1; + + setmaster(msg); + if (seq != msg->tsp_seq) { + seq = msg->tsp_seq; + synch(tvtomsround(msg->tsp_time)); + } + (void)gettimeofday(&ntime, 0); + electiontime = ntime.tv_sec + delay2; + fastelection = ntime.tv_sec + FASTTOUT; + adjtime = ntime.tv_sec + SAMPLEINTVL*2; + break; + + case TSP_SETTIME: + if (fromnet != slavenet) + break; + if (seq == msg->tsp_seq) + break; + seq = msg->tsp_seq; + + /* adjust time for residence on the queue */ + (void)gettimeofday(&otime, 0); + adj_msg_time(msg,&otime); +#ifdef sgi + (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); + (void)cftime(olddate, "%D %T", &otime.tv_sec); +#else + /* + * the following line is necessary due to syslog + * calling ctime() which clobbers the static buffer + */ + (void)strcpy(olddate, date()); + (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec)); +#endif /* sgi */ + + if (!good_host_name(msg->tsp_name)) { + syslog(LOG_NOTICE, + "attempted time setting by untrusted %s to %s", + msg->tsp_name, newdate); + suppress(&from, msg->tsp_name, fromnet); + break; + } + + setmaster(msg); + timevalsub(&ntime, &msg->tsp_time, &otime); + if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) { + /* + * do not change the clock if we can adjust it + */ + synch(tvtomsround(ntime)); + } else { +#ifdef sgi + if (0 > settimeofday(&msg->tsp_time, 0)) { + syslog(LOG_ERR,"settimeofdate(): %m"); + break; + } + logwtmp(&otime, &msg->tsp_time); +#else + logwtmp("|", "date", ""); + (void)settimeofday(&msg->tsp_time, 0); + logwtmp("}", "date", ""); +#endif /* sgi */ + syslog(LOG_NOTICE, + "date changed by %s from %s", + msg->tsp_name, olddate); + if (status & MASTER) + spreadtime(); + } + (void)gettimeofday(&ntime, 0); + electiontime = ntime.tv_sec + delay2; + fastelection = ntime.tv_sec + FASTTOUT; + +/* This patches a bad protocol bug. Imagine a system with several networks, + * where there are a pair of redundant gateways between a pair of networks, + * each running timed. Assume that we start with a third machine mastering + * one of the networks, and one of the gateways mastering the other. + * Imagine that the third machine goes away and the non-master gateway + * decides to replace it. If things are timed just 'right,' we will have + * each gateway mastering one network for a little while. If a SETTIME + * message gets into the network at that time, perhaps from the newly + * masterful gateway as it was taking control, the SETTIME will loop + * forever. Each time a gateway receives it on its slave side, it will + * call spreadtime to forward it on its mastered network. We are now in + * a permanent loop, since the SETTIME msgs will keep any clock + * in the network from advancing. Normally, the 'LOOP' stuff will detect + * and correct the situation. However, with the clocks stopped, the + * 'looptime' timer cannot expire. While they are in this state, the + * masters will try to saturate the network with SETTIME packets. + */ + looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1; + break; + + case TSP_MASTERUP: + if (slavenet && fromnet != slavenet) + break; + if (!good_host_name(msg->tsp_name)) { + suppress(&from, msg->tsp_name, fromnet); + if (electiontime > fastelection) + electiontime = fastelection; + break; + } + makeslave(fromnet); + setmaster(msg); + setstatus(); + answerdelay(); + xmit(TSP_SLAVEUP, 0, &from); + (void)gettimeofday(&ntime, 0); + electiontime = ntime.tv_sec + delay2; + fastelection = ntime.tv_sec + FASTTOUT; + refusetime = 0; + break; + + case TSP_MASTERREQ: + if (fromnet->status != SLAVE) + break; + (void)gettimeofday(&ntime, 0); + electiontime = ntime.tv_sec + delay2; + break; + + case TSP_SETDATE: +#ifdef sgi + (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); +#else + (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec)); +#endif /* sgi */ + schgdate(msg, newdate); + break; + + case TSP_SETDATEREQ: + if (fromnet->status != MASTER) + break; +#ifdef sgi + (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec); +#else + (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec)); +#endif /* sgi */ + htp = findhost(msg->tsp_name); + if (0 == htp) { + syslog(LOG_WARNING, + "DATEREQ from uncontrolled machine"); + break; + } + if (!htp->good) { + syslog(LOG_WARNING, + "attempted date change by untrusted %s to %s", + htp->name, newdate); + spreadtime(); + break; + } + schgdate(msg, newdate); + break; + + case TSP_TRACEON: + traceon(); + break; + + case TSP_TRACEOFF: + traceoff("Tracing ended at %s\n"); + break; + + case TSP_SLAVEUP: + newslave(msg); + break; + + case TSP_ELECTION: + if (fromnet->status == SLAVE) { + (void)gettimeofday(&ntime, 0); + electiontime = ntime.tv_sec + delay2; + fastelection = ntime.tv_sec + FASTTOUT; + seq = 0; + if (!good_host_name(msg->tsp_name)) { + syslog(LOG_NOTICE, + "suppress election of %s", + msg->tsp_name); + to.tsp_type = TSP_QUIT; + electiontime = fastelection; + } else if (cadr.s_addr != from.sin_addr.s_addr + && ntime.tv_sec < refusetime) { +/* if the candidate has to repeat itself, the old code would refuse it + * the second time. That would prevent elections. + */ + to.tsp_type = TSP_REFUSE; + } else { + cadr.s_addr = from.sin_addr.s_addr; + to.tsp_type = TSP_ACCEPT; + refusetime = ntime.tv_sec + 30; + } + taddr = from; + (void)strcpy(tname, msg->tsp_name); + (void)strcpy(to.tsp_name, hostname); + answerdelay(); + if (!acksend(&to, &taddr, tname, + TSP_ACK, 0, 0)) + syslog(LOG_WARNING, + "no answer from candidate %s\n", + tname); + + } else { /* fromnet->status == MASTER */ + htp = addmach(msg->tsp_name, &from,fromnet); + to.tsp_type = TSP_QUIT; + (void)strcpy(to.tsp_name, hostname); + if (!acksend(&to, &htp->addr, htp->name, + TSP_ACK, 0, htp->noanswer)) { + syslog(LOG_ERR, + "no reply from %s to ELECTION-QUIT", + htp->name); + (void)remmach(htp); + } + } + break; + + case TSP_CONFLICT: + if (fromnet->status != MASTER) + break; + /* + * After a network partition, there can be + * more than one master: the first slave to + * come up will notify here the situation. + */ + (void)strcpy(to.tsp_name, hostname); + + /* The other master often gets into the same state, + * with boring results. + */ + ntp = fromnet; /* (acksend() can leave fromnet=0 */ + for (tries = 0; tries < 3; tries++) { + to.tsp_type = TSP_RESOLVE; + answer = acksend(&to, &ntp->dest_addr, + ANYADDR, TSP_MASTERACK, + ntp, 0); + if (answer == NULL) + break; + htp = addmach(answer->tsp_name,&from,ntp); + to.tsp_type = TSP_QUIT; + answer = acksend(&to, &htp->addr, htp->name, + TSP_ACK, 0, htp->noanswer); + if (!answer) { + syslog(LOG_WARNING, + "conflict error: no reply from %s to QUIT", + htp->name); + (void)remmach(htp); + } + } + masterup(ntp); + break; + + case TSP_MSITE: + if (!slavenet) + break; + taddr = from; + to.tsp_type = TSP_MSITEREQ; + to.tsp_vers = TSPVERSION; + to.tsp_seq = 0; + (void)strcpy(to.tsp_name, hostname); + answer = acksend(&to, &slavenet->dest_addr, + ANYADDR, TSP_ACK, + slavenet, 0); + if (answer != NULL + && good_host_name(answer->tsp_name)) { + setmaster(answer); + to.tsp_type = TSP_ACK; + (void)strcpy(to.tsp_name, answer->tsp_name); + bytenetorder(&to); + if (sendto(sock, (char *)&to, + sizeof(struct tsp), 0, + (struct sockaddr*)&taddr, sizeof(taddr)) < 0) { + trace_sendto_err(taddr.sin_addr); + } + } + break; + + case TSP_MSITEREQ: + break; + + case TSP_ACCEPT: + case TSP_REFUSE: + case TSP_RESOLVE: + break; + + case TSP_QUIT: + doquit(msg); /* become a slave */ + break; + + case TSP_TEST: + electiontime = 0; + break; + + case TSP_LOOP: + /* looking for loops of masters */ + if (!(status & MASTER)) + break; + if (fromnet->status == SLAVE) { + if (!strcmp(msg->tsp_name, hostname)) { + /* + * Someone forwarded our message back to + * us. There must be a loop. Tell the + * master of this network to quit. + * + * The other master often gets into + * the same state, with boring results. + */ + ntp = fromnet; + for (tries = 0; tries < 3; tries++) { + to.tsp_type = TSP_RESOLVE; + answer = acksend(&to, &ntp->dest_addr, + ANYADDR, TSP_MASTERACK, + ntp,0); + if (answer == NULL) + break; + taddr = from; + (void)strcpy(tname, answer->tsp_name); + to.tsp_type = TSP_QUIT; + (void)strcpy(to.tsp_name, hostname); + if (!acksend(&to, &taddr, tname, + TSP_ACK, 0, 1)) { + syslog(LOG_ERR, + "no reply from %s to slave LOOP-QUIT", + tname); + } else { + electiontime = 0; + } + } + (void)gettimeofday(&ntime, 0); + looptime = ntime.tv_sec + FASTTOUT; + } else { + if (msg->tsp_hopcnt-- < 1) + break; + bytenetorder(msg); + for (ntp = nettab; ntp != 0; ntp = ntp->next) { + if (ntp->status == MASTER + && 0 > sendto(sock, (char *)msg, + sizeof(struct tsp), 0, + (struct sockaddr*)&ntp->dest_addr, + sizeof(ntp->dest_addr))) + trace_sendto_err(ntp->dest_addr.sin_addr); + } + } + } else { /* fromnet->status == MASTER */ + /* + * We should not have received this from a net + * we are master on. There must be two masters, + * unless the packet was really from us. + */ + if (from.sin_addr.s_addr + == fromnet->my_addr.s_addr) { + if (trace) + fprintf(fd,"discarding forwarded LOOP\n"); + break; + } + + /* + * The other master often gets into the same + * state, with boring results. + */ + ntp = fromnet; + for (tries = 0; tries < 3; tries++) { + to.tsp_type = TSP_RESOLVE; + answer = acksend(&to, &ntp->dest_addr, + ANYADDR, TSP_MASTERACK, + ntp,0); + if (!answer) + break; + htp = addmach(answer->tsp_name, + &from,ntp); + to.tsp_type = TSP_QUIT; + (void)strcpy(to.tsp_name, hostname); + if (!acksend(&to,&htp->addr,htp->name, + TSP_ACK, 0, htp->noanswer)) { + syslog(LOG_ERR, + "no reply from %s to master LOOP-QUIT", + htp->name); + (void)remmach(htp); + } + } + (void)gettimeofday(&ntime, 0); + looptime = ntime.tv_sec + FASTTOUT; + } + break; + default: + if (trace) { + fprintf(fd, "garbage message: "); + print(msg, &from); + } + break; + } + } + goto loop; +} + + +/* + * tell the world who our master is + */ +static void +setmaster(msg) + struct tsp *msg; +{ + if (slavenet + && (slavenet != old_slavenet + || strcmp(msg->tsp_name, master_name) + || old_status != status)) { + (void)strcpy(master_name, msg->tsp_name); + old_slavenet = slavenet; + old_status = status; + + if (status & MASTER) { + syslog(LOG_NOTICE, "submaster to %s", master_name); + if (trace) + fprintf(fd, "submaster to %s\n", master_name); + + } else { + syslog(LOG_NOTICE, "slave to %s", master_name); + if (trace) + fprintf(fd, "slave to %s\n", master_name); + } + } +} + + + +/* + * handle date change request on a slave + */ +static void +schgdate(msg, newdate) + struct tsp *msg; + char *newdate; +{ + struct tsp to; + u_short seq; + struct sockaddr_in taddr; + struct timeval otime; + + if (!slavenet) + return; /* no where to forward */ + + taddr = from; + seq = msg->tsp_seq; + + syslog(LOG_INFO, + "forwarding date change by %s to %s", + msg->tsp_name, newdate); + + /* adjust time for residence on the queue */ + (void)gettimeofday(&otime, 0); + adj_msg_time(msg, &otime); + + to.tsp_type = TSP_SETDATEREQ; + to.tsp_time = msg->tsp_time; + (void)strcpy(to.tsp_name, hostname); + if (!acksend(&to, &slavenet->dest_addr, + ANYADDR, TSP_DATEACK, + slavenet, 0)) + return; /* no answer */ + + xmit(TSP_DATEACK, seq, &taddr); +} + + +/* + * Used before answering a broadcast message to avoid network + * contention and likely collisions. + */ +static void +answerdelay() +{ +#ifdef sgi + sginap(delay1); +#else + struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = delay1; + + (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, + &timeout); + return; +#endif /* sgi */ +} diff --git a/usr.sbin/timed/timed/timed.8 b/usr.sbin/timed/timed/timed.8 new file mode 100644 index 0000000..8f8798d --- /dev/null +++ b/usr.sbin/timed/timed/timed.8 @@ -0,0 +1,219 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)timed.8 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt TIMED 8 +.Os BSD 4.3 +.Sh NAME +.Nm timed +.Nd time server daemon +.Sh SYNOPSIS +.Nm timed +.Op Fl M +.Op Fl t +.Op Fl d +.Op Fl i Ar network +.Op Fl n Ar network +.Op Fl F Ar host1 host2 ... +.Sh DESCRIPTION +This +is a time server daemon and is normally invoked +at boot time from the +.Xr rc 8 +file. +It synchronizes the host's time with the time of other +machines in a local area network running +.Nm timed 8 . +These time servers will slow down the clocks of some machines +and speed up the clocks of others to bring them to the average network time. +The average network time is computed from measurements of clock differences +using the +.Tn ICMP +timestamp request message. +.Pp +The service provided by +.Nm timed +is based on a master-slave +scheme. +When +.Nm timed 8 +is started on a machine, it asks the master for the network time +and sets the host's clock to that time. +After that, it accepts synchronization messages periodically sent by +the master and calls +.Xr adjtime 2 +to perform the needed corrections on the host's clock. +.Pp +It also communicates with +.Xr date 1 +in order to set the date globally, +and with +.Xr timedc 8 , +a timed control program. +If the machine running the master crashes, then the slaves will elect +a new master from among slaves running with the +.Fl M +flag. +A +.Nm timed +running without the +.Fl M +or +.Fl F +flags will remain a slave. +The +.Fl t +flag enables +.Nm timed +to trace the messages it receives in the +file +.Pa /var/log/timed.log . +Tracing can be turned on or off by the program +.Xr timedc 8 . +The +.Fl d +flag is for debugging the daemon. +It causes the program to not put itself into the background. +Normally +.Nm timed +checks for a master time server on each network to which +it is connected, except as modified by the options described below. +It will request synchronization service from the first master server +located. +If permitted by the +.Fl M +flag, it will provide synchronization service on any attached networks +on which no current master server was detected. +Such a server propagates the time computed by the top-level master. +The +.Fl n +flag, followed by the name of a network which the host is connected to +(see +.Xr networks 5 ) , +overrides the default choice of the +network addresses made by the program. +Each time the +.Fl n +flag appears, that network name is added to a list of valid networks. +All other networks are ignored. +The +.Fl i +flag, followed by the name of a network to which the host is connected +(see +.Xr networks 5 ) , +overrides the default choice of the network addresses made by the program. +Each time the +.Fl i +flag appears, that network name is added to a list of networks to ignore. +All other networks are used by the time daemon. +The +.Fl n +and +.Fl i +flags are meaningless if used together. +.Pp +.Nm Timed +checks for a master time server on each network to which +it is connected, except as modified by the +.Fl n +and +.Fl i +options described above. +If it finds masters on more than one network, it chooses one network +on which to be a "slave," and then periodically checks the other +networks to see if the masters there have disappeared. +.Pp +One way to synchronize a group of machines is to use an NTP daemon to +synchronize the clock of one machine to a distant standard or a radio +receiver and +.Fl F Ar hostname +to tell its timed daemon to trust only itself. +.Pp +Messages printed by the kernel on the system console occur with +interrupts disabled. +This means that the clock stops while they are printing. +A machine with many disk or network hardware problems and consequent +messages cannot keep good time by itself. Each message typically causes +the clock to lose a dozen milliseconds. A time daemon can +correct the result. +.Pp +Messages in the system log about machines that failed to respond +usually indicate machines that crashed or were turned off. +Complaints about machines that failed to respond to initial time +settings are often associated with "multi-homed" machines +that looked for time masters on more than one network and eventually +chose to become a slave on the other network. +.SH WARNING +If two or more time daemons, whether +.Nm timed , +.Xr NTP , +try to adjust the same clock, temporal chaos will result. +If both +.Nm +and another time daemon are run on the same machine, +ensure that the +.Fl F +flag is used, so that +.Nm timed +never attempts to adjust the local clock. +.Pp +The protocol is based on UDP/IP broadcasts. All machines within +the range of a broadcast that are using the TSP protocol must cooperate. +There cannot be more than a single administrative domain using the +.Fl F +flag among all machines reached by a broadcast packet. +Failure to follow this rule is usually indicated by complaints concerning +"untrusted" machines in the system log. +.Sh FILES +.Bl -tag -width /var/log/timed.masterlog -compact +.It Pa /var/log/timed.log +tracing file for timed +.It Pa /var/log/timed.masterlog +log file for master timed +.El +.Sh SEE ALSO +.Xr date 1 , +.Xr adjtime 2 , +.Xr gettimeofday 2 , +.Xr icmp 4 , +.Xr timedc 8 , +.Rs +.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD" +.%A R. Gusella +.%A S. Zatti +.Re +.Sh HISTORY +The +.Nm +daemon appeared in +.Bx 4.3 . diff --git a/usr.sbin/timed/timed/timed.c b/usr.sbin/timed/timed/timed.c new file mode 100644 index 0000000..b8f7dc7 --- /dev/null +++ b/usr.sbin/timed/timed/timed.c @@ -0,0 +1,980 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)timed.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.25 $" +#endif /* sgi */ + +#define TSPTYPES +#include "globals.h" +#include <net/if.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <setjmp.h> +#include "pathnames.h" +#include <math.h> +#include <sys/types.h> +#include <sys/times.h> +#ifdef sgi +#include <unistd.h> +#include <sys/syssgi.h> +#include <sys/schedctl.h> +#endif /* sgi */ + +int trace = 0; +int sock, sock_raw = -1; +int status = 0; +u_short sequence; /* sequence number */ +long delay1; +long delay2; + +int nslavenets; /* nets were I could be a slave */ +int nmasternets; /* nets were I could be a master */ +int nignorednets; /* ignored nets */ +int nnets; /* nets I am connected to */ + +FILE *fd; /* trace file FD */ + +jmp_buf jmpenv; + +struct netinfo *nettab = 0; +struct netinfo *slavenet; +int Mflag; +int justquit = 0; +int debug; + +static struct nets { + char *name; + long net; + struct nets *next; +} *nets = 0; + +struct hosttbl hosttbl[NHOSTS+1]; /* known hosts */ + +static struct goodhost { /* hosts that we trust */ + char name[MAXHOSTNAMELEN+1]; + struct goodhost *next; + char perm; +} *goodhosts; + +static char *goodgroup; /* net group of trusted hosts */ +static void checkignorednets __P((void)); +static void pickslavenet __P((struct netinfo *)); +static void add_good_host __P((char *, int)); + +#ifdef sgi +char *timetrim_fn; +char *timetrim_wpat = "long timetrim = %ld;\ndouble tot_adj = %.0f;\ndouble tot_ticks = %.0f;\n/* timed version 2 */\n"; +char *timetrim_rpat = "long timetrim = %ld;\ndouble tot_adj = %lf;\ndouble tot_ticks = %lf;"; +long timetrim; +double tot_adj, hr_adj; /* totals in nsec */ +double tot_ticks, hr_ticks; + +int bufspace = 60*1024; +#endif + + +/* + * The timedaemons synchronize the clocks of hosts in a local area network. + * One daemon runs as master, all the others as slaves. The master + * performs the task of computing clock differences and sends correction + * values to the slaves. + * Slaves start an election to choose a new master when the latter disappears + * because of a machine crash, network partition, or when killed. + * A resolution protocol is used to kill all but one of the masters + * that happen to exist in segments of a partitioned network when the + * network partition is fixed. + * + * Authors: Riccardo Gusella & Stefano Zatti + * + * overhauled at Silicon Graphics + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int on; + int ret; + int nflag, iflag; + struct timeval ntime; + struct servent *srvp; + char buf[BUFSIZ], *cp, *cplim; + struct ifconf ifc; + struct ifreq ifreq, ifreqf, *ifr; + register struct netinfo *ntp; + struct netinfo *ntip; + struct netinfo *savefromnet; + struct netent *nentp; + struct nets *nt; + struct sockaddr_in server; + u_short port; + char c; + extern char *optarg; + extern int optind, opterr; +#ifdef sgi + FILE *timetrim_st; +#endif + +#define IN_MSG "timed: -i and -n make no sense together\n" +#ifdef sgi + struct tms tms; +#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp] [-P trimfile]\n" +#else +#ifdef HAVENIS +#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n" +#else +#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...]\n" +#endif /* HAVENIS */ +#endif /* sgi */ + +#ifdef lint + ntip = NULL; +#endif + + on = 1; + nflag = OFF; + iflag = OFF; + +#ifdef sgi + if (0 > syssgi(SGI_GETTIMETRIM, &timetrim)) { + perror("timed: syssgi(GETTIMETRIM)"); + timetrim = 0; + } + tot_ticks = hr_ticks = times(&tms); +#endif /* sgi */ + + opterr = 0; + while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != EOF) { + switch (c) { + case 'M': + Mflag = 1; + break; + + case 't': + trace = 1; + break; + + case 'n': + if (iflag) { + fprintf(stderr, IN_MSG); + exit(1); + } else { + nflag = ON; + addnetname(optarg); + } + break; + + case 'i': + if (nflag) { + fprintf(stderr, IN_MSG); + exit(1); + } else { + iflag = ON; + addnetname(optarg); + } + break; + + case 'F': + add_good_host(optarg,1); + while (optind < argc && argv[optind][0] != '-') + add_good_host(argv[optind++], 1); + break; + + case 'd': + debug = 1; + break; + case 'G': + if (goodgroup != 0) { + fprintf(stderr,"timed: only one net group\n"); + exit(1); + } + goodgroup = optarg; + break; +#ifdef sgi + case 'P': + timetrim_fn = optarg; + timetrim_st = fopen(timetrim_fn, "r+"); + if (0 == timetrim_st) { + if (errno != ENOENT) { + (void)fprintf(stderr,"timed: "); + perror(timetrim_fn); + timetrim_fn = 0; + } + } else { + int i; + long trim; + double adj, ticks; + + i = fscanf(timetrim_st, timetrim_rpat, + &trim, &adj, &ticks); + if (i < 1 + || trim > MAX_TRIM + || trim < -MAX_TRIM + || i == 2 + || (i == 3 + && trim != rint(adj*CLK_TCK/ticks))) { + if (trace && i != EOF) + (void)fprintf(stderr, + "timed: unrecognized contents in %s\n", + timetrim_fn); + } else { + if (0 > syssgi(SGI_SETTIMETRIM, + trim)) { + perror("timed: syssgi(SETTIMETRIM)"); + } else { + timetrim = trim; + } + if (i == 3) { + tot_adj = adj; + tot_ticks -= ticks; + } + } + (void)fclose(timetrim_st); + } + break; +#endif /* sgi */ + + default: + fprintf(stderr, USAGE); + exit(1); + break; + } + } + if (optind < argc) { + fprintf(stderr, USAGE); + exit(1); + } + + /* If we care about which machine is the master, then we must + * be willing to be a master + */ + if (0 != goodgroup || 0 != goodhosts) + Mflag = 1; + + if (gethostname(hostname, sizeof(hostname) - 1) < 0) { + perror("gethostname"); + exit(1); + } + self.l_bak = &self; + self.l_fwd = &self; + self.h_bak = &self; + self.h_fwd = &self; + self.head = 1; + self.good = 1; + + if (goodhosts != 0) /* trust ourself */ + add_good_host(hostname,1); + + srvp = getservbyname("timed", "udp"); + if (srvp == 0) { + fprintf(stderr, "unknown service 'timed/udp'\n"); + exit(1); + } + port = srvp->s_port; + server.sin_port = srvp->s_port; + server.sin_family = AF_INET; + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + perror("socket"); + exit(1); + } + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on, + sizeof(on)) < 0) { + perror("setsockopt"); + exit(1); + } + if (bind(sock, (struct sockaddr*)&server, sizeof(server))) { + if (errno == EADDRINUSE) + fprintf(stderr,"timed: time daemon already running\n"); + else + perror("bind"); + exit(1); + } +#ifdef sgi + /* + * handle many slaves with our buffer + */ + if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufspace, + sizeof(bufspace))) { + perror("setsockopt"); + exit(1); + } +#endif /* sgi */ + + /* choose a unique seed for random number generation */ + (void)gettimeofday(&ntime, 0); + srandom(ntime.tv_sec + ntime.tv_usec); + + sequence = random(); /* initial seq number */ + +#ifndef sgi + /* rounds kernel variable time to multiple of 5 ms. */ + ntime.tv_sec = 0; + ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000; + (void)adjtime(&ntime, (struct timeval *)0); +#endif /* sgi */ + + for (nt = nets; nt; nt = nt->next) { + nentp = getnetbyname(nt->name); + if (nentp == 0) { + nt->net = inet_network(nt->name); + if (nt->net != INADDR_NONE) + nentp = getnetbyaddr(nt->net, AF_INET); + } + if (nentp != 0) { + nt->net = nentp->n_net; + } else if (nt->net == INADDR_NONE) { + fprintf(stderr, "timed: unknown net %s\n", nt->name); + exit(1); + } else if (nt->net == INADDR_ANY) { + fprintf(stderr, "timed: bad net %s\n", nt->name); + exit(1); + } else { + fprintf(stderr, + "timed: warning: %s unknown in /etc/networks\n", + nt->name); + } + + if (0 == (nt->net & 0xff000000)) + nt->net <<= 8; + if (0 == (nt->net & 0xff000000)) + nt->net <<= 8; + if (0 == (nt->net & 0xff000000)) + nt->net <<= 8; + } + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) { + perror("timed: get interface configuration"); + exit(1); + } + ntp = NULL; +#ifdef sgi +#define size(p) (sizeof(*ifr) - sizeof(ifr->ifr_name)) /* XXX hack. kludge */ +#else +#define size(p) max((p).sa_len, sizeof(p)) +#endif + cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */ + for (cp = buf; cp < cplim; + cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) { + ifr = (struct ifreq *)cp; + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + if (!ntp) + ntp = (struct netinfo*)malloc(sizeof(struct netinfo)); + bzero(ntp,sizeof(*ntp)); + ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; + ntp->status = NOMASTER; + ifreq = *ifr; + ifreqf = *ifr; + + if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) { + perror("get interface flags"); + continue; + } + if ((ifreqf.ifr_flags & IFF_UP) == 0) + continue; + if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 && + (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) { + continue; + } + + + if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) { + perror("get netmask"); + continue; + } + ntp->mask = ((struct sockaddr_in *) + &ifreq.ifr_addr)->sin_addr.s_addr; + + if (ifreqf.ifr_flags & IFF_BROADCAST) { + if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { + perror("get broadaddr"); + continue; + } + ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr; + /* What if the broadcast address is all ones? + * So we cannot just mask ntp->dest_addr. */ + ntp->net = ntp->my_addr; + ntp->net.s_addr &= ntp->mask; + } else { + if (ioctl(sock, SIOCGIFDSTADDR, + (char *)&ifreq) < 0) { + perror("get destaddr"); + continue; + } + ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr; + ntp->net = ntp->dest_addr.sin_addr; + } + + ntp->dest_addr.sin_port = port; + + for (nt = nets; nt; nt = nt->next) { + if (ntp->net.s_addr == nt->net) + break; + } + if (nflag && !nt || iflag && nt) + continue; + + ntp->next = NULL; + if (nettab == NULL) { + nettab = ntp; + } else { + ntip->next = ntp; + } + ntip = ntp; + ntp = NULL; + } + if (ntp) + (void) free((char *)ntp); + if (nettab == NULL) { + fprintf(stderr, "timed: no network usable\n"); + exit(1); + } + + +#ifdef sgi + (void)schedctl(RENICE,0,10); /* run fast to get good time */ + + /* ticks to delay before responding to a broadcast */ + delay1 = casual(0, CLK_TCK/10); +#else + + /* microseconds to delay before responding to a broadcast */ + delay1 = casual(1, 100*1000); +#endif /* sgi */ + + /* election timer delay in secs. */ + delay2 = casual(MINTOUT, MAXTOUT); + + +#ifdef sgi + (void)_daemonize(debug ? _DF_NOFORK|_DF_NOCHDIR : 0, sock, -1, -1); +#else + if (!debug) + daemon(debug, 0); +#endif /* sgi */ + + if (trace) + traceon(); + openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON); + + /* + * keep returning here + */ + ret = setjmp(jmpenv); + savefromnet = fromnet; + setstatus(); + + if (Mflag) { + switch (ret) { + + case 0: + checkignorednets(); + pickslavenet(0); + break; + case 1: + /* Just lost our master */ + if (slavenet != 0) + slavenet->status = election(slavenet); + if (!slavenet || slavenet->status == MASTER) { + checkignorednets(); + pickslavenet(0); + } else { + makeslave(slavenet); /* prune extras */ + } + break; + + case 2: + /* Just been told to quit */ + justquit = 1; + pickslavenet(savefromnet); + break; + } + + setstatus(); + if (!(status & MASTER) && sock_raw != -1) { + /* sock_raw is not being used now */ + (void)close(sock_raw); + sock_raw = -1; + } + + if (status == MASTER) + master(); + else + slave(); + + } else { + if (sock_raw != -1) { + (void)close(sock_raw); + sock_raw = -1; + } + + if (ret) { + /* we just lost our master or were told to quit */ + justquit = 1; + } + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if (ntp->status == MASTER) + rmnetmachs(ntp); + ntp->status = NOMASTER; + } + checkignorednets(); + pickslavenet(0); + setstatus(); + + slave(); + } + /* NOTREACHED */ +#ifdef lint + return(0); +#endif +} + +/* + * suppress an upstart, untrustworthy, self-appointed master + */ +void +suppress(addr, name,net) + struct sockaddr_in *addr; + char *name; + struct netinfo *net; +{ + struct sockaddr_in tgt; + char tname[MAXHOSTNAMELEN]; + struct tsp msg; + static struct timeval wait; + + if (trace) + fprintf(fd, "suppress: %s\n", name); + tgt = *addr; + (void)strcpy(tname, name); + + while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) { + if (trace) + fprintf(fd, "suppress:\tdiscarded packet from %s\n", + name); + } + + syslog(LOG_NOTICE, "suppressing false master %s", tname); + msg.tsp_type = TSP_QUIT; + (void)strcpy(msg.tsp_name, hostname); + (void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1); +} + +void +lookformaster(ntp) + struct netinfo *ntp; +{ + struct tsp resp, conflict, *answer; + struct timeval ntime; + char mastername[MAXHOSTNAMELEN]; + struct sockaddr_in masteraddr; + + get_goodgroup(0); + ntp->status = SLAVE; + + /* look for master */ + resp.tsp_type = TSP_MASTERREQ; + (void)strcpy(resp.tsp_name, hostname); + answer = acksend(&resp, &ntp->dest_addr, ANYADDR, + TSP_MASTERACK, ntp, 0); + if (answer != 0 && !good_host_name(answer->tsp_name)) { + suppress(&from, answer->tsp_name, ntp); + ntp->status = NOMASTER; + answer = 0; + } + if (answer == 0) { + /* + * Various conditions can cause conflict: races between + * two just started timedaemons when no master is + * present, or timedaemons started during an election. + * A conservative approach is taken. Give up and became a + * slave, postponing election of a master until first + * timer expires. + */ + ntime.tv_sec = ntime.tv_usec = 0; + answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp); + if (answer != 0) { + if (!good_host_name(answer->tsp_name)) { + suppress(&from, answer->tsp_name, ntp); + ntp->status = NOMASTER; + } + return; + } + + ntime.tv_sec = ntime.tv_usec = 0; + answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp); + if (answer != 0) { + if (!good_host_name(answer->tsp_name)) { + suppress(&from, answer->tsp_name, ntp); + ntp->status = NOMASTER; + } + return; + } + + ntime.tv_sec = ntime.tv_usec = 0; + answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp); + if (answer != 0) { + if (!good_host_name(answer->tsp_name)) { + suppress(&from, answer->tsp_name, ntp); + ntp->status = NOMASTER; + } + return; + } + + if (Mflag) + ntp->status = MASTER; + else + ntp->status = NOMASTER; + return; + } + + ntp->status = SLAVE; + (void)strcpy(mastername, answer->tsp_name); + masteraddr = from; + + /* + * If network has been partitioned, there might be other + * masters; tell the one we have just acknowledged that + * it has to gain control over the others. + */ + ntime.tv_sec = 0; + ntime.tv_usec = 300000; + answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp); + /* + * checking also not to send CONFLICT to ack'ed master + * due to duplicated MASTERACKs + */ + if (answer != NULL && + strcmp(answer->tsp_name, mastername) != 0) { + conflict.tsp_type = TSP_CONFLICT; + (void)strcpy(conflict.tsp_name, hostname); + if (!acksend(&conflict, &masteraddr, mastername, + TSP_ACK, 0, 0)) { + syslog(LOG_ERR, + "error on sending TSP_CONFLICT"); + } + } +} + +/* + * based on the current network configuration, set the status, and count + * networks; + */ +void +setstatus() +{ + struct netinfo *ntp; + + status = 0; + nmasternets = nslavenets = nnets = nignorednets = 0; + if (trace) + fprintf(fd, "Net status:\n"); + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + switch ((int)ntp->status) { + case MASTER: + nmasternets++; + break; + case SLAVE: + nslavenets++; + break; + case NOMASTER: + case IGNORE: + nignorednets++; + break; + } + if (trace) { + fprintf(fd, "\t%-16s", inet_ntoa(ntp->net)); + switch ((int)ntp->status) { + case NOMASTER: + fprintf(fd, "NOMASTER\n"); + break; + case MASTER: + fprintf(fd, "MASTER\n"); + break; + case SLAVE: + fprintf(fd, "SLAVE\n"); + break; + case IGNORE: + fprintf(fd, "IGNORE\n"); + break; + default: + fprintf(fd, "invalid state %d\n", + (int)ntp->status); + break; + } + } + nnets++; + status |= ntp->status; + } + status &= ~IGNORE; + if (trace) + fprintf(fd, + "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%d\n", + nnets, nmasternets, nslavenets, nignorednets, delay2); +} + +void +makeslave(net) + struct netinfo *net; +{ + register struct netinfo *ntp; + + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if (ntp->status == SLAVE && ntp != net) + ntp->status = IGNORE; + } + slavenet = net; +} + +/* + * Try to become master over ignored nets.. + */ +static void +checkignorednets() +{ + register struct netinfo *ntp; + + for (ntp = nettab; ntp != NULL; ntp = ntp->next) { + if (!Mflag && ntp->status == SLAVE) + break; + + if (ntp->status == IGNORE || ntp->status == NOMASTER) { + lookformaster(ntp); + if (!Mflag && ntp->status == SLAVE) + break; + } + } +} + +/* + * choose a good network on which to be a slave + * The ignored networks must have already been checked. + * Take a hint about for a good network. + */ +static void +pickslavenet(ntp) + struct netinfo *ntp; +{ + if (slavenet != 0 && slavenet->status == SLAVE) { + makeslave(slavenet); /* prune extras */ + return; + } + + if (ntp == 0 || ntp->status != SLAVE) { + for (ntp = nettab; ntp != 0; ntp = ntp->next) { + if (ntp->status == SLAVE) + break; + } + } + makeslave(ntp); +} + +/* + * returns a random number in the range [inf, sup] + */ +long +casual(inf, sup) + long inf, sup; +{ + double value; + + value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0); + return(inf + (sup - inf)*value); +} + +char * +date() +{ +#ifdef sgi + struct timeval tv; + static char tm[32]; + + (void)gettimeofday(&tv, (struct timezone *)0); + (void)cftime(tm, "%D %T", &tv.tv_sec); + return (tm); +#else + struct timeval tv; + + (void)gettimeofday(&tv, (struct timezone *)0); + return (ctime(&tv.tv_sec)); +#endif /* sgi */ +} + +void +addnetname(name) + char *name; +{ + register struct nets **netlist = &nets; + + while (*netlist) + netlist = &((*netlist)->next); + *netlist = (struct nets *)malloc(sizeof **netlist); + if (*netlist == 0) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + bzero((char *)*netlist, sizeof(**netlist)); + (*netlist)->name = name; +} + +/* note a host as trustworthy */ +static void +add_good_host(name, perm) + char *name; + int perm; /* 1=not part of the netgroup */ +{ + register struct goodhost *ghp; + register struct hostent *hentp; + + ghp = (struct goodhost*)malloc(sizeof(*ghp)); + if (!ghp) { + syslog(LOG_ERR, "malloc failed"); + exit(1); + } + + bzero((char*)ghp, sizeof(*ghp)); + (void)strncpy(&ghp->name[0], name, sizeof(ghp->name)); + ghp->next = goodhosts; + ghp->perm = perm; + goodhosts = ghp; + + hentp = gethostbyname(name); + if (0 == hentp && perm) + (void)fprintf(stderr, "unknown host %s\n", name); +} + + +/* update our image of the net-group of trustworthy hosts + */ +void +get_goodgroup(force) + int force; +{ +# define NG_DELAY (30*60*CLK_TCK) /* 30 minutes */ + static unsigned long last_update = -NG_DELAY; + unsigned long new_update; + struct hosttbl *htp; + struct goodhost *ghp, **ghpp; + char *mach, *usr, *dom; + struct tms tm; + + + /* if no netgroup, then we are finished */ + if (goodgroup == 0 || !Mflag) + return; + + /* Do not chatter with the netgroup master too often. + */ + new_update = times(&tm); + if (new_update < last_update + NG_DELAY + && !force) + return; + last_update = new_update; + + /* forget the old temporary entries */ + ghpp = &goodhosts; + while (0 != (ghp = *ghpp)) { + if (!ghp->perm) { + *ghpp = ghp->next; + free((char*)ghp); + } else { + ghpp = &ghp->next; + } + } + +#ifdef HAVENIS + /* quit now if we are not one of the trusted masters + */ + if (!innetgr(goodgroup, &hostname[0], 0,0)) { + if (trace) + (void)fprintf(fd, "get_goodgroup: %s not in %s\n", + &hostname[0], goodgroup); + return; + } + if (trace) + (void)fprintf(fd, "get_goodgroup: %s in %s\n", + &hostname[0], goodgroup); + + /* mark the entire netgroup as trusted */ + (void)setnetgrent(goodgroup); + while (getnetgrent(&mach,&usr,&dom)) { + if (0 != mach) + add_good_host(mach,0); + } + (void)endnetgrent(); + + /* update list of slaves */ + for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { + htp->good = good_host_name(&htp->name[0]); + } +#endif /* HAVENIS */ +} + + +/* see if a machine is trustworthy + */ +int /* 1=trust hp to change our date */ +good_host_name(name) + char *name; +{ + register struct goodhost *ghp = goodhosts; + register char c; + + if (!ghp || !Mflag) /* trust everyone if no one named */ + return 1; + + c = *name; + do { + if (c == ghp->name[0] + && !strcasecmp(name, ghp->name)) + return 1; /* found him, so say so */ + } while (0 != (ghp = ghp->next)); + + if (!strcasecmp(name,hostname)) /* trust ourself */ + return 1; + + return 0; /* did not find him */ +} diff --git a/usr.sbin/timed/timedc/Makefile b/usr.sbin/timed/timedc/Makefile new file mode 100644 index 0000000..a0b41ef --- /dev/null +++ b/usr.sbin/timed/timedc/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= timedc +SRCS= cmds.c cmdtab.c timedc.c byteorder.c measure.c cksum.c +MAN8= timedc.0 +BINOWN= root +BINMODE=4555 +.PATH: ${.CURDIR}/../timed + +.include "../../Makefile.inc" +.include <bsd.prog.mk> diff --git a/usr.sbin/timed/timedc/cmds.c b/usr.sbin/timed/timedc/cmds.c new file mode 100644 index 0000000..557f721 --- /dev/null +++ b/usr.sbin/timed/timedc/cmds.c @@ -0,0 +1,526 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cmds.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.10 $" +#endif + +#include "timedc.h" +#include <sys/file.h> + +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> + +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> + +#define TSPTYPES +#include <protocols/timed.h> + +#ifdef sgi +#include <bstring.h> +#include <sys/clock.h> +#else +#define SECHR (60*60) +#define SECDAY (24*SECHR) +#endif /* sgi */ + +# define DATE_PROTO "udp" +# define DATE_PORT "time" + + +int sock; +int sock_raw; +char myname[MAXHOSTNAMELEN]; +struct hostent *hp; +struct sockaddr_in server; +struct sockaddr_in dayaddr; +extern int measure_delta; + +void bytenetorder(struct tsp *); +void bytehostorder(struct tsp *); + + +#define BU ((unsigned long)2208988800) /* seconds before UNIX epoch */ + + +/* compute the difference between our date and another machine + */ +static int /* difference in days from our time */ +daydiff(hostname) + char *hostname; +{ + int i; + int trials; + struct timeval tout, now; + fd_set ready; + struct sockaddr from; + int fromlen; + unsigned long sec; + + + /* wait 2 seconds between 10 tries */ + tout.tv_sec = 2; + tout.tv_usec = 0; + for (trials = 0; trials < 10; trials++) { + /* ask for the time */ + sec = 0; + if (sendto(sock, &sec, sizeof(sec), 0, + (struct sockaddr*)&dayaddr, sizeof(dayaddr)) < 0) { + perror("sendto(sock)"); + return 0; + } + + for (;;) { + FD_ZERO(&ready); + FD_SET(sock, &ready); + i = select(sock+1, &ready, (fd_set *)0, + (fd_set *)0, &tout); + if (i < 0) { + if (errno = EINTR) + continue; + perror("select(date read)"); + return 0; + } + if (0 == i) + break; + + fromlen = sizeof(from); + if (recvfrom(sock,&sec,sizeof(sec),0, + &from,&fromlen) < 0) { + perror("recvfrom(date read)"); + return 0; + } + + sec = ntohl(sec); + if (sec < BU) { + fprintf(stderr, + "%s says it is before 1970: %lu", + hostname, sec); + return 0; + } + sec -= BU; + + (void)gettimeofday(&now, (struct timezone*)0); + return (sec - now.tv_sec); + } + } + + /* if we get here, we tried too many times */ + fprintf(stderr,"%s will not tell us the date\n", hostname); + return 0; +} + + +/* + * Clockdiff computes the difference between the time of the machine on + * which it is called and the time of the machines given as argument. + * The time differences measured by clockdiff are obtained using a sequence + * of ICMP TSTAMP messages which are returned to the sender by the IP module + * in the remote machine. + * In order to compare clocks of machines in different time zones, the time + * is transmitted (as a 32-bit value) in milliseconds since midnight UT. + * If a hosts uses a different time format, it should set the high order + * bit of the 32-bit quantity it transmits. + * However, VMS apparently transmits the time in milliseconds since midnight + * local time (rather than GMT) without setting the high order bit. + * Furthermore, it does not understand daylight-saving time. This makes + * clockdiff behaving inconsistently with hosts running VMS. + * + * In order to reduce the sensitivity to the variance of message transmission + * time, clockdiff sends a sequence of messages. Yet, measures between + * two `distant' hosts can be affected by a small error. The error can, + * however, be reduced by increasing the number of messages sent in each + * measurement. + */ +void +clockdiff(argc, argv) + int argc; + char *argv[]; +{ + int measure_status; + extern int measure(u_long, u_long, char *, struct sockaddr_in*, int); + register int avg_cnt; + register long avg; + struct servent *sp; + + if (argc < 2) { + printf("Usage: clockdiff host ... \n"); + return; + } + + (void)gethostname(myname,sizeof(myname)); + + /* get the address for the date ready */ + sp = getservbyname(DATE_PORT, DATE_PROTO); + if (!sp) { + (void)fprintf(stderr, "%s/%s is an unknown service\n", + DATE_PORT, DATE_PROTO); + dayaddr.sin_port = 0; + } else { + dayaddr.sin_port = sp->s_port; + } + + while (argc > 1) { + argc--; argv++; + hp = gethostbyname(*argv); + if (hp == NULL) { + fprintf(stderr, "timedc: %s: ", *argv); + herror(0); + continue; + } + + server.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length); + for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) { + measure_status = measure(10000,100, *argv, &server, 1); + if (measure_status != GOOD) + break; + avg += measure_delta; + } + if (measure_status == GOOD) + measure_delta = avg/avg_cnt; + + switch (measure_status) { + case HOSTDOWN: + printf("%s is down\n", hp->h_name); + continue; + case NONSTDTIME: + printf("%s transmitts a non-standard time format\n", + hp->h_name); + continue; + case UNREACHABLE: + printf("%s is unreachable\n", hp->h_name); + continue; + } + + /* + * Try to get the date only after using ICMP timestamps to + * get the time. This is because the date protocol + * is optional. + */ + if (dayaddr.sin_port != 0) { + dayaddr.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr, + hp->h_length); + avg = daydiff(*argv); + if (avg > SECDAY) { + printf("time on %s is %ld days ahead %s\n", + hp->h_name, avg/SECDAY, myname); + continue; + } else if (avg < -SECDAY) { + printf("time on %s is %ld days behind %s\n", + hp->h_name, -avg/SECDAY, myname); + continue; + } + } + + if (measure_delta > 0) { + printf("time on %s is %d ms. ahead of time on %s\n", + hp->h_name, measure_delta, myname); + } else if (measure_delta == 0) { + printf("%s and %s have the same time\n", + hp->h_name, myname); + } else { + printf("time on %s is %d ms. behind time on %s\n", + hp->h_name, -measure_delta, myname); + } + } + return; +} + + +/* + * finds location of master timedaemon + */ +void +msite(argc, argv) + int argc; + char *argv[]; +{ + int cc; + fd_set ready; + struct sockaddr_in dest; + int i, length; + struct sockaddr from; + struct timeval tout; + struct tsp msg; + struct servent *srvp; + char *tgtname; + + if (argc < 1) { + printf("Usage: msite [hostname]\n"); + return; + } + + srvp = getservbyname("timed", "udp"); + if (srvp == 0) { + fprintf(stderr, "udp/timed: unknown service\n"); + return; + } + dest.sin_port = srvp->s_port; + dest.sin_family = AF_INET; + + (void)gethostname(myname, sizeof(myname)); + i = 1; + do { + tgtname = (i >= argc) ? myname : argv[i]; + hp = gethostbyname(tgtname); + if (hp == 0) { + fprintf(stderr, "timedc: %s: ", tgtname); + herror(0); + continue; + } + bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length); + + (void)strcpy(msg.tsp_name, myname); + msg.tsp_type = TSP_MSITE; + msg.tsp_vers = TSPVERSION; + bytenetorder(&msg); + if (sendto(sock, &msg, sizeof(struct tsp), 0, + (struct sockaddr*)&dest, + sizeof(struct sockaddr)) < 0) { + perror("sendto"); + continue; + } + + tout.tv_sec = 15; + tout.tv_usec = 0; + FD_ZERO(&ready); + FD_SET(sock, &ready); + if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, + &tout)) { + length = sizeof(struct sockaddr); + cc = recvfrom(sock, &msg, sizeof(struct tsp), 0, + &from, &length); + if (cc < 0) { + perror("recvfrom"); + continue; + } + bytehostorder(&msg); + if (msg.tsp_type == TSP_ACK) { + printf("master timedaemon at %s is %s\n", + tgtname, msg.tsp_name); + } else { + printf("received wrong ack: %s\n", + tsptype[msg.tsp_type]); + } + } else { + printf("communication error with %s\n", tgtname); + } + } while (++i < argc); +} + +/* + * quits timedc + */ +void +quit() +{ + exit(0); +} + + +/* + * Causes the election timer to expire on the selected hosts + * It sends just one udp message per machine, relying on + * reliability of communication channel. + */ +void +testing(argc, argv) + int argc; + char *argv[]; +{ + struct servent *srvp; + struct sockaddr_in sin; + struct tsp msg; + + if (argc < 2) { + printf("Usage: election host1 [host2 ...]\n"); + return; + } + + srvp = getservbyname("timed", "udp"); + if (srvp == 0) { + fprintf(stderr, "udp/timed: unknown service\n"); + return; + } + + while (argc > 1) { + argc--; argv++; + hp = gethostbyname(*argv); + if (hp == NULL) { + fprintf(stderr, "timedc: %s: ", *argv); + herror(0); + argc--; argv++; + continue; + } + sin.sin_port = srvp->s_port; + sin.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length); + + msg.tsp_type = TSP_TEST; + msg.tsp_vers = TSPVERSION; + (void)gethostname(myname, sizeof(myname)); + (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name)); + bytenetorder(&msg); + if (sendto(sock, &msg, sizeof(struct tsp), 0, + (struct sockaddr*)&sin, + sizeof(struct sockaddr)) < 0) { + perror("sendto"); + } + } +} + + +/* + * Enables or disables tracing on local timedaemon + */ +void +tracing(argc, argv) + int argc; + char *argv[]; +{ + int onflag; + int length; + int cc; + fd_set ready; + struct sockaddr_in dest; + struct sockaddr from; + struct timeval tout; + struct tsp msg; + struct servent *srvp; + + if (argc != 2) { + printf("Usage: tracing { on | off }\n"); + return; + } + + srvp = getservbyname("timed", "udp"); + if (srvp == 0) { + fprintf(stderr, "udp/timed: unknown service\n"); + return; + } + dest.sin_port = srvp->s_port; + dest.sin_family = AF_INET; + + (void)gethostname(myname,sizeof(myname)); + hp = gethostbyname(myname); + bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length); + + if (strcmp(argv[1], "on") == 0) { + msg.tsp_type = TSP_TRACEON; + onflag = ON; + } else { + msg.tsp_type = TSP_TRACEOFF; + onflag = OFF; + } + + (void)strcpy(msg.tsp_name, myname); + msg.tsp_vers = TSPVERSION; + bytenetorder(&msg); + if (sendto(sock, &msg, sizeof(struct tsp), 0, + (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) { + perror("sendto"); + return; + } + + tout.tv_sec = 5; + tout.tv_usec = 0; + FD_ZERO(&ready); + FD_SET(sock, &ready); + if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) { + length = sizeof(struct sockaddr); + cc = recvfrom(sock, &msg, sizeof(struct tsp), 0, + &from, &length); + if (cc < 0) { + perror("recvfrom"); + return; + } + bytehostorder(&msg); + if (msg.tsp_type == TSP_ACK) + if (onflag) + printf("timed tracing enabled\n"); + else + printf("timed tracing disabled\n"); + else + printf("wrong ack received: %s\n", + tsptype[msg.tsp_type]); + } else + printf("communication error\n"); +} + +int +priv_resources() +{ + int port; + struct sockaddr_in sin; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + perror("opening socket"); + return(-1); + } + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = 0; + for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) { + sin.sin_port = htons((u_short)port); + if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0) + break; + if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) { + perror("bind"); + (void) close(sock); + return(-1); + } + } + if (port == IPPORT_RESERVED / 2) { + fprintf(stderr, "all reserved ports in use\n"); + (void) close(sock); + return(-1); + } + + sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sock_raw < 0) { + perror("opening raw socket"); + (void) close(sock); + return(-1); + } + return(1); +} diff --git a/usr.sbin/timed/timedc/cmdtab.c b/usr.sbin/timed/timedc/cmdtab.c new file mode 100644 index 0000000..ad5bbb6 --- /dev/null +++ b/usr.sbin/timed/timedc/cmdtab.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "timedc.h" + +char clockdiffhelp[] = "measures clock differences between machines"; +char helphelp[] = "gets help on commands"; +char msitehelp[] = "finds location of master"; +char quithelp[] = "exits timedc"; +char testinghelp[] = "causes election timers to expire"; +char tracinghelp[] = "turns tracing on or off"; + +struct cmd cmdtab[] = { + { "clockdiff", clockdiffhelp, clockdiff, 0 }, + { "election", testinghelp, testing, 1 }, + { "help", helphelp, help, 0 }, + { "msite", msitehelp, msite, 0 }, + { "quit", quithelp, quit, 0 }, + { "trace", tracinghelp, tracing, 1 }, + { "?", helphelp, help, 0 }, +}; + +int NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]); diff --git a/usr.sbin/timed/timedc/extern.h b/usr.sbin/timed/timedc/extern.h new file mode 100644 index 0000000..7f33362 --- /dev/null +++ b/usr.sbin/timed/timedc/extern.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +#if __STDC__ +struct tsp; +#endif + +extern struct cmd cmdtab[]; + +void bytehostorder __P((struct tsp *)); +void bytenetorder __P((struct tsp *)); +void clockdiff __P((int, char *[])); +void help __P((int, char *[])); +void intr __P((int)); +void makeargv __P((void)); +void msite __P((int, char *[])); +int priv_resources __P((void)); +void quit __P((void)); +void testing __P((int, char *[])); +void tracing __P((int, char *[])); diff --git a/usr.sbin/timed/timedc/timedc.8 b/usr.sbin/timed/timedc/timedc.8 new file mode 100644 index 0000000..fd495e7 --- /dev/null +++ b/usr.sbin/timed/timedc/timedc.8 @@ -0,0 +1,145 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)timedc.8 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt TIMEDC 8 +.Os BSD 4.3 +.ad +.Sh NAME +.Nm timedc +.Nd timed control program +.Sh SYNOPSIS +.Nm timedc +.Oo Ar command\ \& +.Op Ar argument ... +.Oc +.Sh DESCRIPTION +.Nm Timedc +is used to control the operation of the +.Xr timed 8 +program. +It may be used to: +.Bl -bullet +.It +Measure the differences between machines' clocks, +.It +Find the location where the master time server is running, +.It +Enable or disable tracing of messages received by +.Xr timed , +and +.It +Perform various debugging actions. +.El +.Pp +Without any arguments, +.Nm timedc +will prompt for commands from the standard input. +If arguments are supplied, +.Nm timedc +interprets the first argument as a command and the remaining +arguments as parameters to the command. The standard input +may be redirected causing +.Nm timedc +to read commands from a file. +Commands may be abbreviated; +recognized commands are: +.Pp +.Bl -tag -width Ds -compact +.It Ic \&? Op Ar command ... +.Pp +.It Ic help Op Ar command ... +Print a short description of each command specified in the argument list, +or, if no arguments are given, a list of the recognized commands. +.Pp +.It Ic clockdiff Ar host ... +Compute the differences between the clock of the host machine +and the clocks of the machines given as arguments. +.Pp +.It Ic msite Op Ar host ... +Show the master time server for specified host(s). +.Pp +.It Xo +.Ic trace +.Li \&{ Ar on Li \&| +.Ar off \&} +.Xc +Enable or disable the tracing of incoming messages to +.Xr timed +in the file +.Pa /var/log/timed.log. +.Pp +.It Ic election Ar host +Asks the daemon +on the target host to reset its "election" timers and to ensure that +a time master has been elected. +.Pp +.It Ic quit +Exit from timedc. +.El +.Pp +Other commands may be included for use in testing and debugging +.Xr timed ; +the help command and +the program source may be consulted for details. +.Sh FILES +.Bl -tag -width /var/log/timed.masterlog -compact +.It Pa /var/log/timed.log +tracing file for timed +.It Pa /var/log/timed.masterlog +log file for master timed +.El +.Sh SEE ALSO +.Xr date 1 , +.Xr adjtime 2 , +.Xr icmp 4 , +.Xr timed 8 , +.Rs +.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD" +.%A R. Gusella +.%A S. Zatti +.Re +.Sh DIAGNOSTICS +.Bl -tag -width Ds -compact +.It ?Ambiguous command +abbreviation matches more than one command +.It ?Invalid command +no match found +.It ?Privileged command +command can be executed by root only +.El +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/usr.sbin/timed/timedc/timedc.c b/usr.sbin/timed/timedc/timedc.c new file mode 100644 index 0000000..02d086a --- /dev/null +++ b/usr.sbin/timed/timedc/timedc.c @@ -0,0 +1,261 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)timedc.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef sgi +#ident "$Revision: 1.6 $" +#endif + +#include "timedc.h" +#include <strings.h> +#include <signal.h> +#include <ctype.h> +#include <setjmp.h> +#include <unistd.h> +#include <stdlib.h> +#include <syslog.h> + +int trace = 0; +FILE *fd = 0; +int margc; +int fromatty; +char *margv[20]; +char cmdline[200]; +jmp_buf toplevel; +static struct cmd *getcmd __P((char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct cmd *c; + + openlog("timedc", LOG_ODELAY, LOG_AUTH); + + /* + * security dictates! + */ + if (priv_resources() < 0) { + fprintf(stderr, "Could not get privileged resources\n"); + exit(1); + } + (void) setuid(getuid()); + + if (--argc > 0) { + c = getcmd(*++argv); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + exit(1); + } + if (c == 0) { + printf("?Invalid command\n"); + exit(1); + } + if (c->c_priv && getuid()) { + printf("?Privileged command\n"); + exit(1); + } + (*c->c_handler)(argc, argv); + exit(0); + } + + fromatty = isatty(fileno(stdin)); + if (setjmp(toplevel)) + putchar('\n'); + (void) signal(SIGINT, intr); + for (;;) { + if (fromatty) { + printf("timedc> "); + (void) fflush(stdout); + } + if (fgets(cmdline, sizeof(cmdline), stdin) == 0) + quit(); + if (cmdline[0] == 0) + break; + makeargv(); + if (margv[0] == 0) + continue; + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + if (c->c_priv && getuid()) { + printf("?Privileged command\n"); + continue; + } + (*c->c_handler)(margc, margv); + } + return 0; +} + +void +intr(signo) + int signo; +{ + if (!fromatty) + exit(0); + longjmp(toplevel, 1); +} + + +static struct cmd * +getcmd(name) + char *name; +{ + register char *p, *q; + register struct cmd *c, *found; + register int nmatches, longest; + extern int NCMDS; + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { + p = c->c_name; + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return(c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return((struct cmd *)-1); + return(found); +} + +/* + * Slice a string up into argc/argv. + */ +void +makeargv() +{ + register char *cp; + register char **argp = margv; + + margc = 0; + for (cp = cmdline; *cp;) { + while (isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *argp++ = cp; + margc += 1; + while (*cp != '\0' && !isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *cp++ = '\0'; + } + *argp++ = 0; +} + +#define HELPINDENT (sizeof ("directory")) + +/* + * Help command. + */ +void +help(argc, argv) + int argc; + char *argv[]; +{ + register struct cmd *c; + + if (argc == 1) { + register int i, j, w; + int columns, width = 0, lines; + extern int NCMDS; + + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { + int len = strlen(c->c_name); + + if (len > width) + width = len; + } + width = (width + 8) &~ 7; + columns = 80 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + for (j = 0; j < columns; j++) { + c = cmdtab + j * lines + i; + printf("%s", c->c_name); + if (c + lines >= &cmdtab[NCMDS]) { + printf("\n"); + break; + } + w = strlen(c->c_name); + while (w < width) { + w = (w + 8) &~ 7; + putchar('\t'); + } + } + } + return; + } + while (--argc > 0) { + register char *arg; + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + printf("?Ambiguous help command %s\n", arg); + else if (c == (struct cmd *)0) + printf("?Invalid help command %s\n", arg); + else + printf("%-*s\t%s\n", (int)HELPINDENT, + c->c_name, c->c_help); + } +} diff --git a/usr.sbin/timed/timedc/timedc.h b/usr.sbin/timed/timedc/timedc.h new file mode 100644 index 0000000..6771680 --- /dev/null +++ b/usr.sbin/timed/timedc/timedc.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)timedc.h 8.1 (Berkeley) 6/6/93 + */ + +#include <sys/param.h> +#include <sys/time.h> +#ifdef sgi +#include <sys/uio.h> +#endif +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> +#include <stdio.h> + +extern int errno; + +#define ON 1 +#define OFF 0 + +#define GOOD 1 +#define UNREACHABLE 2 +#define NONSTDTIME 3 +#define HOSTDOWN 0x7fffffff + +struct cmd { + char *c_name; /* command name */ + char *c_help; /* help message */ + void (*c_handler)(); /* routine to do the work */ + int c_priv; /* privileged command */ +}; + +#include "extern.h" |