From d9768fd9dbb9ffb082896c373f5e78f968079f68 Mon Sep 17 00:00:00 2001 From: wollman Date: Sun, 3 Apr 1994 19:50:51 +0000 Subject: xntp 3.3p from Delaware --- usr.sbin/xntpd/COPYRIGHT | 4 +- usr.sbin/xntpd/Config.local.dist | 4 + usr.sbin/xntpd/Makefile | 4 +- usr.sbin/xntpd/VERSION | 2 +- usr.sbin/xntpd/authstuff/Makefile.tmpl | 6 +- usr.sbin/xntpd/compilers/hpux-adj.cc | 1 + usr.sbin/xntpd/compilers/hpux-adj.gcc | 1 + usr.sbin/xntpd/compilers/hpux.cc | 3 +- usr.sbin/xntpd/compilers/hpux.gcc | 3 +- usr.sbin/xntpd/compilers/irix4.cc | 2 + usr.sbin/xntpd/compilers/linux.gcc | 2 +- usr.sbin/xntpd/conf/Config.plain | 190 +++ usr.sbin/xntpd/doc/README.kern | 2622 +++++++++++++++++++++--------- usr.sbin/xntpd/doc/notes.txt | 2 +- usr.sbin/xntpd/doc/ntpdate.8 | 10 +- usr.sbin/xntpd/doc/ntpq.8 | 10 +- usr.sbin/xntpd/doc/ntptrace.8 | 10 +- usr.sbin/xntpd/doc/tickadj.8 | 10 +- usr.sbin/xntpd/doc/xntpd.8 | 69 +- usr.sbin/xntpd/doc/xntpdc.8 | 38 +- usr.sbin/xntpd/hints/hpux | 68 +- usr.sbin/xntpd/hints/linux | 32 +- usr.sbin/xntpd/hints/solaris | 3 +- usr.sbin/xntpd/include/ntp.h | 15 +- usr.sbin/xntpd/include/ntp_machine.h | 40 +- usr.sbin/xntpd/include/ntp_request.h | 31 + usr.sbin/xntpd/include/ntp_stdlib.h | 1 + usr.sbin/xntpd/include/ntp_timex.h | 110 +- usr.sbin/xntpd/include/ntpd.h | 4 +- usr.sbin/xntpd/include/parse.h | 82 +- usr.sbin/xntpd/lib/Makefile.tmpl | 6 +- usr.sbin/xntpd/lib/clocktypes.c | 2 +- usr.sbin/xntpd/lib/netof.c | 25 + usr.sbin/xntpd/lib/systime.c | 3 +- usr.sbin/xntpd/machines/aux2 | 2 +- usr.sbin/xntpd/machines/aux3 | 2 +- usr.sbin/xntpd/machines/bsdi | 2 +- usr.sbin/xntpd/machines/convexos10 | 2 +- usr.sbin/xntpd/machines/convexos9 | 2 +- usr.sbin/xntpd/machines/decosf1 | 2 +- usr.sbin/xntpd/machines/dell.svr4 | 2 +- usr.sbin/xntpd/machines/domainos | 2 +- usr.sbin/xntpd/machines/hpux-adj | 8 + usr.sbin/xntpd/machines/i386 | 2 +- usr.sbin/xntpd/machines/i386svr4 | 2 +- usr.sbin/xntpd/machines/mips | 4 +- usr.sbin/xntpd/machines/next | 2 +- usr.sbin/xntpd/machines/sequent | 2 +- usr.sbin/xntpd/machines/sinix-m | 2 +- usr.sbin/xntpd/machines/sony | 2 +- usr.sbin/xntpd/machines/sunos4.bsd | 2 +- usr.sbin/xntpd/machines/sunos5.1 | 2 +- usr.sbin/xntpd/machines/sunos5.2 | 2 +- usr.sbin/xntpd/machines/svr4 | 2 +- usr.sbin/xntpd/machines/ultrix.bsd | 2 +- usr.sbin/xntpd/machines/ultrix.posix | 2 +- usr.sbin/xntpd/machines/vax | 2 +- usr.sbin/xntpd/parse/Makefile.kernel | 2 +- usr.sbin/xntpd/parse/README.new_clocks | 212 +++ usr.sbin/xntpd/parse/README.parse_clocks | 263 +++ usr.sbin/xntpd/parse/clk_dcf7000.c | 7 +- usr.sbin/xntpd/parse/clk_meinberg.c | 22 +- usr.sbin/xntpd/parse/clk_rawdcf.c | 16 +- usr.sbin/xntpd/parse/clk_schmid.c | 12 +- usr.sbin/xntpd/parse/clk_trimble.c | 5 +- usr.sbin/xntpd/parse/parse.c | 20 +- usr.sbin/xntpd/parse/parse_conf.c | 7 +- usr.sbin/xntpd/parse/parsesolaris.c | 35 +- usr.sbin/xntpd/parse/parsestreams.c | 78 +- usr.sbin/xntpd/parse/util/parsetest.c | 18 +- usr.sbin/xntpd/parse/util/testdcf.c | 4 +- usr.sbin/xntpd/refclocks/rclk.TRAK | 29 + usr.sbin/xntpd/scripts/Guess.sh | 6 +- usr.sbin/xntpd/scripts/README | 2 +- usr.sbin/xntpd/scripts/hpadjtime.sh | 18 + usr.sbin/xntpd/scripts/support/bin/monl | 3 +- usr.sbin/xntpd/util/ntptime.c | 46 +- usr.sbin/xntpd/util/tickadj.c | 55 +- usr.sbin/xntpd/xntpd/Makefile.tmpl | 9 +- usr.sbin/xntpd/xntpd/ntp_config.c | 69 +- usr.sbin/xntpd/xntpd/ntp_control.c | 2 +- usr.sbin/xntpd/xntpd/ntp_loopfilter.c | 60 +- usr.sbin/xntpd/xntpd/ntp_monitor.c | 51 +- usr.sbin/xntpd/xntpd/ntp_proto.c | 19 +- usr.sbin/xntpd/xntpd/ntp_request.c | 11 +- usr.sbin/xntpd/xntpd/ntp_restrict.c | 166 +- usr.sbin/xntpd/xntpd/ntp_unixclock.c | 11 +- usr.sbin/xntpd/xntpd/ntpd.c | 18 +- usr.sbin/xntpd/xntpd/refclock_chu.c | 19 + usr.sbin/xntpd/xntpd/refclock_conf.c | 8 +- usr.sbin/xntpd/xntpd/refclock_irig.c | 2 +- usr.sbin/xntpd/xntpd/refclock_parse.c | 46 +- usr.sbin/xntpd/xntpd/refclock_trak.c | 967 +++++++++++ usr.sbin/xntpd/xntpdc/ntpdc.c | 6 +- usr.sbin/xntpd/xntpdc/ntpdc_ops.c | 71 +- 95 files changed, 4718 insertions(+), 1146 deletions(-) create mode 100644 usr.sbin/xntpd/compilers/hpux-adj.cc create mode 100644 usr.sbin/xntpd/compilers/hpux-adj.gcc create mode 100644 usr.sbin/xntpd/compilers/irix4.cc create mode 100644 usr.sbin/xntpd/conf/Config.plain create mode 100644 usr.sbin/xntpd/lib/netof.c create mode 100644 usr.sbin/xntpd/machines/hpux-adj create mode 100644 usr.sbin/xntpd/parse/README.new_clocks create mode 100644 usr.sbin/xntpd/parse/README.parse_clocks create mode 100644 usr.sbin/xntpd/refclocks/rclk.TRAK create mode 100644 usr.sbin/xntpd/scripts/hpadjtime.sh create mode 100644 usr.sbin/xntpd/xntpd/refclock_trak.c (limited to 'usr.sbin') diff --git a/usr.sbin/xntpd/COPYRIGHT b/usr.sbin/xntpd/COPYRIGHT index 711a8f1..321bc56 100644 --- a/usr.sbin/xntpd/COPYRIGHT +++ b/usr.sbin/xntpd/COPYRIGHT @@ -1,6 +1,6 @@ /****************************************************************************** * * - * Copyright (c) David L. Mills 1992, 1993, 1994 * + * Copyright (c) David L. Mills 1992, 1993, 1994 * * * * Permission to use, copy, modify, and distribute this software and its * * documentation for any purpose and without fee is hereby granted, provided * @@ -55,4 +55,4 @@ * Torsten Duwe (Linux Port) * Paul A Vixie (TrueTime GPS driver) * Jim Jagielski (A/UX port) -*/ + */ diff --git a/usr.sbin/xntpd/Config.local.dist b/usr.sbin/xntpd/Config.local.dist index 97c01c0..7e540a3 100644 --- a/usr.sbin/xntpd/Config.local.dist +++ b/usr.sbin/xntpd/Config.local.dist @@ -114,6 +114,10 @@ DEFS_LOCAL= $(DEFS_OPT) #GREEN -DREFCLOCK #TEST -DPPSPPS -DKERNEL_PLL # by default when using the "make makeconfig" script and greenhorn # configuraiton. # +# Define -DTRAK for a 8810 GPS Receiver with Buffered RS-232-C Interface +# Module. The driver supports both the CLK and PPS modes. It should work +# in all systems with a serial port. +# # Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver # supports both the CLK and PPS modes. It should work in all systems # with a serial port. diff --git a/usr.sbin/xntpd/Makefile b/usr.sbin/xntpd/Makefile index 1e9c0b1..ba92f47 100644 --- a/usr.sbin/xntpd/Makefile +++ b/usr.sbin/xntpd/Makefile @@ -144,7 +144,7 @@ xntpres/xntpres: lib/libntp.a xntpres/Makefile FRC @echo '###' creating XNTPRES utility @cd xntpres && $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" MAKE="$(MAKE)" -util/tickadj: util/Makefile FRC +util/tickadj: util/Makefile lib/libntp.a FRC @echo @echo '###' creating TICKADJ utility @cd util && $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" MAKE="$(MAKE)" @@ -162,7 +162,7 @@ parse/libparse.a: parse/*.c parse/Makefile parse/util/Makefile lib/libntp.a FRC: savebin: - @test -d bin || mkdir bin + -@test -d bin || mkdir bin @echo @echo '### saving $(TARGETS) $(OPTTARG) in bin' -@for f in $(TARGETS) $(OPTTARG); \ diff --git a/usr.sbin/xntpd/VERSION b/usr.sbin/xntpd/VERSION index 85051bd..084bc1a 100644 --- a/usr.sbin/xntpd/VERSION +++ b/usr.sbin/xntpd/VERSION @@ -1 +1 @@ -version=3.3c (beta) +version=3.3p (beta) diff --git a/usr.sbin/xntpd/authstuff/Makefile.tmpl b/usr.sbin/xntpd/authstuff/Makefile.tmpl index e5f0310..4c8ca57 100644 --- a/usr.sbin/xntpd/authstuff/Makefile.tmpl +++ b/usr.sbin/xntpd/authstuff/Makefile.tmpl @@ -38,13 +38,13 @@ SOURCE= authcert.c authspeed.c keyparity.c makeIPFP.c makePC1.c \ all: $(PROGRAM) authcert: $(CRTOBJS) $(LIB) - $(CC) $(COPTS) -o $@ $(CRTOBJS) $(LIB) + $(CC) $(COPTS) -o $@ $(CRTOBJS) $(LIB) $(COMPAT) $(RESLIB) authspeed: $(SPDOBJS) $(LIB) $(CC) $(COPTS) -o $@ $(SPDOBJS) $(LIB) $(COMPAT) $(RESLIB) keyparity: $(PAROBJS) $(LIB) - $(CC) $(COPTS) -o $@ $(PAROBJS) $(LIB) + $(CC) $(COPTS) -o $@ $(PAROBJS) $(LIB) $(COMPAT) $(RESLIB) makeIPFP: $(IFPOBJS) $(CC) $(COPTS) -o $@ $(IFPOBJS) @@ -68,7 +68,7 @@ unixcert: $(UNXBJS) $(CC) $(COPTS) -o $@ $(UNXBJS) md5: $(MD5OBJS) - $(CC) $(COPTS) -o $@ $(MD5OBJS) $(LIB) + $(CC) $(COPTS) -o $@ $(MD5OBJS) $(LIB) $(COMPAT) $(RESLIB) tags: ctags *.c *.h diff --git a/usr.sbin/xntpd/compilers/hpux-adj.cc b/usr.sbin/xntpd/compilers/hpux-adj.cc new file mode 100644 index 0000000..cf058ef --- /dev/null +++ b/usr.sbin/xntpd/compilers/hpux-adj.cc @@ -0,0 +1 @@ +COMPILER=cc +O1 diff --git a/usr.sbin/xntpd/compilers/hpux-adj.gcc b/usr.sbin/xntpd/compilers/hpux-adj.gcc new file mode 100644 index 0000000..e085a80 --- /dev/null +++ b/usr.sbin/xntpd/compilers/hpux-adj.gcc @@ -0,0 +1 @@ +COMPILER=gcc -O2 diff --git a/usr.sbin/xntpd/compilers/hpux.cc b/usr.sbin/xntpd/compilers/hpux.cc index e3c27af..cf058ef 100644 --- a/usr.sbin/xntpd/compilers/hpux.cc +++ b/usr.sbin/xntpd/compilers/hpux.cc @@ -1,2 +1 @@ -COMPILER=cc -COPTS=+O1 +COMPILER=cc +O1 diff --git a/usr.sbin/xntpd/compilers/hpux.gcc b/usr.sbin/xntpd/compilers/hpux.gcc index ecd0372..e085a80 100644 --- a/usr.sbin/xntpd/compilers/hpux.gcc +++ b/usr.sbin/xntpd/compilers/hpux.gcc @@ -1,2 +1 @@ -COMPILER=gcc -COPTS=-O2 +COMPILER=gcc -O2 diff --git a/usr.sbin/xntpd/compilers/irix4.cc b/usr.sbin/xntpd/compilers/irix4.cc new file mode 100644 index 0000000..c5ae3af --- /dev/null +++ b/usr.sbin/xntpd/compilers/irix4.cc @@ -0,0 +1,2 @@ +COMPILER= cc -cckr +COPTS= -O2 diff --git a/usr.sbin/xntpd/compilers/linux.gcc b/usr.sbin/xntpd/compilers/linux.gcc index 501568c..1051fcb 100644 --- a/usr.sbin/xntpd/compilers/linux.gcc +++ b/usr.sbin/xntpd/compilers/linux.gcc @@ -1,2 +1,2 @@ COMPILER= gcc -DUSE_PROTOTYPES -Wall -COPTS= -O6 -finline-functions -fomit-frame-pointer +COPTS= -O2 -finline-functions -fomit-frame-pointer diff --git a/usr.sbin/xntpd/conf/Config.plain b/usr.sbin/xntpd/conf/Config.plain new file mode 100644 index 0000000..c57c578 --- /dev/null +++ b/usr.sbin/xntpd/conf/Config.plain @@ -0,0 +1,190 @@ +# This is the local configure file (distribution version). +# You must modify it to fit your particular configuration +# and name it Config.local +# The following configuratiions can be auto-generated: +# +# make Config.local.green +# make a Config.local that supports a local clock +# (i.e. allow fallback to use of the CPU's own clock) +# make Config.local.NO.clock +# make a Config.local that supports no clocks +# +# +# NOTE TO GREENHORNS +# +# For plug-'n-play and no radios or other complicated gadgetry, +# use "make Config.local.green" as above. +# +# Following defines can be set in the DEFS_OPT= define: +# +# The flag -DDEBUG includes some debugging code. To use this, include +# the define and start the daemon with one or more -d flags, depending +# on your calibration of pearannoya. The daemon will not detach your +# terminal in this case. Judicious use of grep will reduce the speaker +# volume to bearable levels. +# +# To change the location of the configuration file, use a +# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar. +# +# The -DSYSLOG_FILE defines allows logging messages that are normally +# reported via syslof() in a file. The file name can be configured using +# the configuration line "logfile " in CONFIG_FILE. +# +# There are three serial port system software interfaces, each of +# which is peculiar to one or more Unix versions. Define +# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM +# for POSIX compatibility including System V Streams, and +# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three +# should be defined. If none are defined, HAVE_BSD_TTYS is assumed. +# Usually these defines are already set correctly. +# +DEFS_OPT=-DDEBUG + +# +# The DEFS_LOCAL define picks up all flags from DEFS_OPT (do not delete that) +# and one of the following: +# +# The flag -DREFCLOCK causes the basic reference clock support to be +# compiled into the daemon. If you set this you may also want to +# configure the particular clock drivers you want in the CLOCKDEFS= line +# below. This flag affects xntpd only. This define is included by +# default when using the "make makeconfig" script. +# +# The next two sets of defines are meaningful only when radio clock +# drivers or special 1-pps signals are to be used. For systems without +# these features, these delicious complexities can be avoided. Ordinarily, +# the "make makeconfig" script figures out which ones to use, but your +# mileage may vary. +# +# There are three ways to utilize external 1-pps signals. Define +# -DPPS to include just the pps routine, such as used by the DCF77(PARSE) +# clock driver. Define -DPPSCLK to include a serial device driver +# which avoids much of the jitter due to upper level port +# processing. This requires a dedicated serial port and either the +# tty_clock line discipline or tty_clk_streams module, both of +# which are in the ./kernel directory. Define -DPPSCD to include a +# special driver which intercepts carrier-detect transitions +# generated by the pps signal. This requires a nondedicated serial +# port and the ppsclock streams module in the ./kernel directory. +# Only one of these three flags should be defined. +# +# The flag KERNEL_PLL causes code to be compiled for a special feature of +# the kernel that (a) implements the phase-lock loop and (b) provides +# a user interface to learn time, maximum error and estimated error. +# See the file README.kern in the doc directory for further info. +# This code is activated only if the relevant kernel features have +# been configured; it does not affect operation of unmodified kernels. +# To compile it, however, requires a few header files from the +# special distribution. +# +# Note: following line must always start with DEFS_LOCAL= $(DEFS_OPT) +DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL + +# +# Radio clock support definitions (these only make sense if -DREFCLOCK +# used), which is normally the case. Note that a configuration can include +# no clocks, more than one type of clock and even multiple clocks of the +# same type. +# +# For most radio clocks operating with serial ports, accuracy can +# be considerably improved through use of the tty_clk line +# discipline or tty_clk_STREAMS streams module found in the +# ./kernel directory. These gizmos capture a timestamp upon +# occurrence of an intercept character and stuff it in the data +# stream for the clock driver to munch. To select this mode, +# postfix the driver name with the string CLK; that is, WWVB +# becomes WWVBCLK. If more than one clock is in use, the CLK +# postfix can be used with any or all of them. +# +# Alternatively, for the best accuracy, use the ppsclock streams +# module in the ./ppsclock directory to steal the carrier-detect +# transition and capture a precision timestamp. At present this +# works only with SunOS 4.1.1 or later. To select this mode, +# postfix the driver name with the string PPS; that is, AS2201 +# becomes AS2201PPS. If more than one clock is in use, the PPS +# postfix should be used with only one of them. If any PPS +# postfix is defined, the -DPPSPPS define should be used on the +# DEFS above. +# +# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a +# reference clock for those subnets without access to the real thing. +# Works in all systems and requires no hardware support. This is defined +# by default when using the "make makeconfig" script and greenhorn +# configuraiton. +# +# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver +# supports both the CLK and PPS modes. It should work in all systems +# with a serial port. +# +# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It +# should work in all systems with a serial port. The driver supports +# both the CLK and PPS modes if the requisite kernel support is installed. +# +# Define -DCHU for a special CHU receiver using an ordinary shortwave +# radio. This requires the chu_clk line discipline or chu_clk_STREAMS +# module in the ./kernel directory. At present, this driver works only +# on SunOS4.1.x; operation in other systems has not been confirmed. +# Construction details for a suitable modem can be found in the ./gadget +# directory. The driver supports # neither the CLK nor PPS modes. +# +# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance +# this requires a special parsestreams STREAMS (SunOS 4.x) module in the +# ./parse directory. Define -DPARSEPPS for PPS support via the +# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above. +# Define: -DCLOCK_MEINBERG for Meinberg clocks +# -DCLOCK_SCHMID for Schmid receivers +# -DCLOCK_DCF7000 for ELV DCF7000 +# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx) +# -DCLOCK_TRIMSV6 for Trimble SV6 GPS receiver +# +# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this +# driver works only on SunOS4.1.x with CPU serial ports only. The PPS +# mode is required. +# +# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should +# work in all systems with a serial port. The driver does not support the +# CLK mode, but does support the PPS mode. If the radio is connected to +# more than one machine, the PPS mode is required. +# +# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This +# driver is known to work with some other TrueTime products as well, +# including the GPS-DC GPS receiver. It should work in all systems with +# a serial port. The driver does not support the CLK mode, but does +# support the PPS mode. +# +# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It +# should work in all systems with a serial port. The driver does not +# support the CLK mode, but does support the PPS mode. +# +# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This +# requires the SunOS interface driver available from KSI. The driver +# supports neither the CLK nor PPS modes. +# +# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for +# the HP 5061B Cesium Clock. It should work in all systems with a serial +# port. The driver does not support the CLK mode, but does support the +# PPS mode. +# +# Define -DMSFEESPPS for an EES M201 MSF receiver. It currently only works +# under SunOS 4.x with the PPSCD (ppsclock) STREAMS module, but the RCS +# files on cl.cam.ac.uk still has support for CLK and CBREAK modes. +# +# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of +# the Sun SPARCstations. This requires a modified BSD audio driver and +# exclusive access to the audio port. A memo describing how it works and +# how to install the driver is in the README.irig file in the ./doc +# directory. +# +# Note: The following defines result in compilation of all the above radio +# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and +# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes +# are removed and the IRIG, PARSE* and CLOCK* deleted, all of the rest compile +# under Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC +# OSF/1 Alpha. +# +CLOCKDEFS= -DLOCAL_CLOCK -DCHU -DGOES -DOMEGA -DPST -DWWVB -DLEITCH + +# +# Directory into which binaries should be installed (default /usr/local) +# +BINDIR= /usr/local/bin diff --git a/usr.sbin/xntpd/doc/README.kern b/usr.sbin/xntpd/doc/README.kern index 1b791c3..4f8df16 100644 --- a/usr.sbin/xntpd/doc/README.kern +++ b/usr.sbin/xntpd/doc/README.kern @@ -1,775 +1,1857 @@ - Unix Kernel Modifications for Precision Timekeeping +Network Working Group D.L. Mills +Request for Comments: xxxx University of Delaware +Obsoletes: none February 1994 + + + + A Kernel Model for Precision Timekeeping + + + +Status of this Memorandum + + This memorandum describes an engineering model which implements a + precision time-of-day function for a generic operating system. The + model is based on the principles of disciplined oscillators and + phase-lock loops (PLL) and frequency-lock loops (FLL) often found in + the engineering literature. It has been implemented in the Unix + kernel for several workstations, including those made by Sun + Microsystems and Digital Equipment. The model changes the way the + system clock is adjusted in time and frequency, as well as provides + mechanisms to discipline its frequency to an external precision + timing source. The model incorporates a generic system-call interface + for use with the Network Time Protocol (NTP) or similar time + synchronization protocol. The NTP Version 3 daemon xntpd operates + with this model to provide synchronization limited in principle only + by the accuracy and stability of the external timing source. + + This memorandum does not obsolete or update any RFC. It does not + propose a standard protocol, specification or algorithm. It is + intended to provoke comment, refinement and implementations for + kernels not considered herein. While a working knowledge of NTP is + not required for an understanding of the design principles or + implementation of the model, it may be helpful in understanding how + the model behaves in a fully functional timekeeping system. The + architecture and design of NTP is described in [MIL91], while the + current NTP Version 3 protocol specification is given in RFC-1305 + [MIL92a] and a subset of the protocol, the Simple Network Time + Protocol (SNTP), is given in RFC-1361 [MIL92c]. + + The model has been implemented in three Unix kernels for Sun + Microsystems and Digital Equipment workstations. In addition, for the + Digital machines the model provides improved precision to one + microsecond (us). Since these specific implementations involve + modifications to licensed code, they cannot be provided directly. + Inquiries should be directed to the manufacturer's representatives. + However, the engineering model for these implementations, including a + simulator with code segments almost identical to the implementations, + but not involving licensed code, is available via anonymous FTP from + host louie.udel.edu in the directory pub/ntp and compressed tar + archive kernel.tar.Z. The NTP Version 3 distribution can be obtained + via anonymous ftp from the same host and directory in the compressed + tar archive xntp3.3l.tar.Z, where the version number shown as 3.3l + may be adjusted for new versions as they occur. + + + +Mills [Page 1] + +RFC February 1994 - Revised 3 December 1993 +1. Introduction -Note: This information file is included in the distributions for the -SunOS, Ultrix and OSF/1 kernels and in the NTP Version 3 distribution -(xntp3.tar.Z) as the file README.kern. Availability of the kernel -distributions, which involve licensed code, will be announced -separately. The NTP Version 3 distribution can be obtained via anonymous -ftp from louie.udel.edu in the directory pub/ntp. In order to utilize -all features of this distribution, the NTP version number should be 3.3 -or later. + This memorandum describes a model and programming interface for + generic operating system software that manages the system clock and + timer functions. The model provides improved accuracy and stability + for most computers using the Network Time Protocol (NTP) or similar + time synchronization protocol. This memorandum describes the design + principles and implementations of the model, while related technical + reports discuss the design approach, engineering analysis and + performance evaluation of the model as implemented in Unix kernels + for modern workstations. The NTP Version 3 daemon xntpd operates with + these implementations to provide improved accuracy and stability, + together with diminished overhead in the operating system and + network. In addition, the model supports the use of external timing + sources, such as precision pulse-per-second (PPS) signals and the + industry standard IRIG timing signals. The NTP daemon automatically + detects the presence of the new features and utilizes them when + available. + + There are three prototype implementations of the model presented in + this memorandum, one each for the Sun Microsystems SPARCstation with + the SunOS 4.1.x kernel, Digital Equipment DECstation 5000 with the + Ultrix 4.x kernel and Digital Equipment 3000 AXP Alpha with the OSF/1 + V1.x kernel. In addition, for the DECstation 5000/240 and 3000 AXP + Alpha machines, a special feature provides improved precision to 1 us + (stock Sun kernels already do provide this precision). Other than + improving the system clock accuracy, stability and precision, these + implementations do not change the operation of existing Unix system + calls which manage the system clock, such as gettimeofday(), + settimeofday() and adjtime(); however, if the new features are in + use, the operations of gettimeofday() and adjtime() can be controlled + instead by new system calls ntp_gettime() and ntp_adjtime() as + described below. + + A detailed description of the variables and algorithms that operate + upon them is given in the hope that similar functionality can be + incorporated in Unix kernels for other machines. The algorithms + involve only minor changes to the system clock and interval timer + routines and include interfaces for application programs to learn the + system clock status and certain statistics of the time + synchronization process. Detailed installation instructions are given + in a specific README files included in the kernel distributions. + + In this memorandum, NTP Version 3 and the Unix implementation xntp3 + are used as an example application of the new system calls for use by + a synchronization daemon. In principle, the new system calls can be + used by other protocols and implementations as well. Even in cases + where the local time is maintained by periodic exchanges of messages + at relatively long intervals, such as using the NIST Automated + Computer Time Service [LEV89], the ability to precisely adjust the + system clock frequency simplifies the synchronization procedures and + allows the telephone call frequency to be considerably reduced. + + + + +Mills [Page 2] + +RFC February 1994 + +2. Design Approach + + While not strictly necessary for an understanding or implementation + of the model, it may be helpful to briefly describe how NTP operates + to control the system clock in a client computer. As described in + [MIL91], the NTP protocol exchanges timestamps with one or more peers + sharing a synchronization subnet to calculate the time offsets + between peer clocks and the local clock. These offsets are processed + by several algorithms which refine and combine the offsets to produce + an ensemble average, which is then used to adjust the local clock + time and frequency. The manner in which the local clock is adjusted + represents the main topic of this memorandum. The goal in the + enterprise is the most accurate and stable system clock possible with + the available computer hardware and kernel software. + + In order to understand how the new model works, it is useful to + review how most Unix kernels maintain the system time. In the Unix + design a hardware counter interrupts the kernel at a fixed rate: 100 + Hz in the SunOS kernel, 256 Hz in the Ultrix kernel and 1024 Hz in + the OSF/1 kernel. Since the Ultrix timer interval (reciprocal of the + rate) does not evenly divide one second in microseconds, the Ultrix + kernel adds 64 microseconds once each second, so the timescale + consists of 255 advances of 3906 us plus one of 3970 us. Similarly, + the OSF/1 kernel adds 576 us once each second, so its timescale + consists of 1023 advances of 976 us plus one of 1552 us. + + 2.1. Mechanisms to Adjust Time and Frequency + + In most Unix kernels it is possible to slew the system clock to a + new offset relative to the current time by using the adjtime() + system call. To do this the clock frequency is changed by adding + or subtracting a fixed amount (tickadj) at each timer interrupt + (tick) for a calculated number of interrupts. Since this + calculation involves dividing the requested offset by tickadj, it + is possible to slew to a new offset with a precision only of + tickadj, which is usually in the neighborhood of 5 us, but + sometimes much more. This results in a roundoff error which can + accumulate to an unacceptable degree, so that special provisions + must be made in the clock adjustment procedures of the + synchronization daemon. + + In order to implement a frequency discipline function, it is + necessary to provide time offset adjustments to the kernel at + regular adjustment intervals using the adjtime() system call. In + order to reduce the system clock jitter to the regime consistent + with the model, it is necessary that the adjustment interval be + relatively small, in the neighborhood of 1 s. However, the Unix + adjtime() implementation requires each offset adjustment to + complete before another one can be begun, which means that large + adjustments must be amortized over possibly many adjustment + intervals. The requirement to implement the adjustment interval + and compensate for roundoff error considerably complicates the + synchronizing daemon implementation. + + + +Mills [Page 3] + +RFC February 1994 + + In the new model this scheme is replaced by another that + represents the system clock as a multiple-word, precision-time + variable in order to provide very precise clock adjustments. At + each timer interrupt a precisely calibrated quantity is added to + the kernel time variable and overflows propagated as required. The + quantity is computed as in the NTP local clock model described in + [MIL92b], which operates as an adaptive-parameter, first-order, + type-II phase-lock loop (PLL). In principle, this PLL design can + provide precision control of the system clock oscillator within 1 + us and frequency to within parts in 10^11. While precisions of + this order are surely well beyond the capabilities of the CPU + clock oscillator used in typical workstations, they are + appropriate using precision external oscillators as described + below. + + The PLL design is identical to the one originally implemented in + NTP and described in [MIL92b]. In this design the software daemon + simulates the PLL using the adjtime() system call; however, the + daemon implementation is considerably complicated by the + considerations described above. The modified kernel routines + implement the PLL in the kernel using precision time and frequency + representions, so that these complications are avoided. A new + system call ntp_adjtime() is called only as each new time update + is determined, which in NTP occurs at intervals of from 16 s to + 1024 s. In addition, doing frequency compensation in the kernel + means that the system time runs true even if the daemon were to + cease operation or the network paths to the primary + synchronization source fail. + + In the new model the new ntp_adjtime() operates in a way similar + to the original adjtime() system call, but does so independently + of adjtime(), which continues to operate in its traditional + fashion. When used with NTP, it is the design intent that + settimeofday() or adjtime() be used only for system time + adjustments greater than +-128 ms, although the dynamic range of + the new model is much larger at +-512 ms. It has been the Internet + experience that the need to change the system time in increments + greater than +-128 ms is extremely rare and is usually associated + with a hardware or software malfunction or system reboot. + + The easiest way to set the time is with the settimeofday() system + call; however, this can under some conditions cause the clock to + jump backwards. If this cannot be tolerated, adjtime() can be used + to slew the clock to the new value without running backward or + affecting the frequency discipline process. Once the system clock + has been set within +-128 ms, the ntp_adjtime() system call is + used to provide periodic updates including the time offset, + maximum error, estimated error and PLL time constant. With NTP the + update interval and time constant depend on the measured delay and + dispersion; however, the scheme is quite forgiving and neither + moderate loss of updates nor variations in the update interval are + serious. + + + + +Mills [Page 4] + +RFC February 1994 + + 2.2 Daemon and Application Interface + + Unix application programs can read the system clock using the + gettimeofday() system call, which returns only the system time and + timezone data. For some applications it is useful to know the + maximum error of the reported time due to all causes, including + clock reading errors, oscillator frequency errors and accumulated + latencies on the path to the primary synchronization source. + However, in the new model the PLL adjusts the system clock to + compensate for its intrinsic frequency error, so that the time + errors expected in normal operation will usually be much less than + the maximum error. The programming interface includes a new system + call ntp_gettime(), which returns the system time, as well as the + maximum error and estimated error. This interface is intended to + support applications that need such things, including distributed + file systems, multimedia teleconferencing and other real-time + applications. The programming interface also includes a new system + call ntp_adjtime(), which can be used to read and write kernel + variables for time and frequency adjustment, PLL time constant, + leap-second warning and related data. + + In addition, the kernel adjusts the maximum error to grow by an + amount equal to the oscillator frequency tolerance times the + elapsed time since the last update. The default engineering + parameters have been optimized for update intervals in the order + of 64 s. As shown in [MIL93], this is near the optimum interval + for NTP used with ordinary room-temperature quartz oscillators. + For other intervals the PLL time constant can be adjusted to + optimize the dynamic response over intervals of 16-1024 s. + Normally, this is automatically done by NTP. In any case, if + updates are suspended, the PLL coasts at the frequency last + determined, which usually results in errors increasing only to a + few tens of milliseconds over a day using typical modern + workstations. + + While any synchronization daemon can in principle be modified to + use the new system calls, the most likely will be users of the NTP + Version 3 daemon xntpd. The xntpd code determines whether the new + system calls are implemented and automatically reconfigures as + required. When implemented, the daemon reads the frequency offset + from a file and provides it and the initial time constant via + ntp_adjtime(). In subsequent calls to ntp_adjtime(), only the time + offset and time constant are affected. The daemon reads the + frequency from the kernel using ntp_adjtime() at intervals of + about one hour and writes it to a system file. This information is + recovered when the daemon is restarted after reboot, for example, + so the sometimes extensive training period to learn the frequency + separately for each system can be avoided. + + 2.3. Precision Clocks for DECstation 5000/240 and 3000 AXP Alpha + + The stock microtime() routine in the Ultrix kernel returns system + time to the precision of the timer interrupt interval, which is in + the 1-4 ms range. However, in the DECstation 5000/240 and possibly + + +Mills [Page 5] + +RFC February 1994 + + other machines of that family, there is an undocumented IOASIC + hardware register that counts system bus cycles at a rate of 25 + MHz. The new microtime() routine for the Ultrix kernel uses this + register to interpolate system time between timer interrupts. This + results in a precision of 1 us for all time values obtained via + the gettimeofday() and ntp_gettime() system calls. For the Digital + Equipment 3000 AXP Alpha, the architecture provides a hardware + Process Cycle Counter and a machine instruction rpcc to read it. + This counter operates at the fundamental frequency of the CPU + clock or some submultiple of it, 133.333 MHz for the 3000/400 for + example. The new microtime() routine for the OSF/1 kernel uses + this counter in the same fashion as the Ultrix routine. + + In both the Ultrix and OSF/1 kernels the gettimeofday() and + ntp_gettime() system call use the new microtime() routine, which + returns the actual interpolated value, but does not change the + kernel time variable. Therefore, other routines that access the + kernel time variable directly and do not call either + gettimeofday(), ntp_gettime() or microtime() will continue their + present behavior. The microtime() feature is independent of other + features described here and is operative even if the kernel PLL or + new system calls have not been implemented. + + The SunOS kernel already includes a system clock with 1-us + resolution; so, in principle, no microtime() routine is necessary. + An existing kernel routine uniqtime() implements this function, + but it is coded in the C language and is rather slow at 42-85 us + per call on a SPARCstation IPC. A replacement microtime() routine + coded in assembler language is available in the NTP Version 3 + distribution and is much faster at about 3 us per call. Note that + this routine must be called at an interrupt priority level not + greater than that of the timer interrupt routine. Otherwise, it is + possible to miss a tick increment, with result the time returned + can be early by one tick. This is always true in the case of + gettimeofday() and ntp_gettime(), but might not be true in other + cases. + + 2.4. External Time and Frequency Discipline + + The overall accuracy of a time synchronization subnet with respect + to Coordinated Universal Time (UTC) depends on the accuracy and + stability of the primary synchronization source, usually a radio + or satellite receiver, and the CPU clock oscillator of the primary + server. As discussed in [MIL93], the traditional interface using + an RS232 protocol and serial port precludes the full accuracy of + most radio clocks. In addition, the poor frequency stability of + typical CPU clock oscillators limits the accuracy, whether or not + precision time sources are available. There are, however, several + ways in which the system clock accuracy and stability can be + improved to the degree limited only by the accuracy and stability + of the synchronization source and the jitter of the operating + system. + + + + +Mills [Page 6] + +RFC February 1994 + + Many radio clocks produce special signals that can be used by + external equipment to precisely synchronize time and frequency. + Most produce a pulse-per-second (PPS) signal that can be read via + a modem-control lead of a serial port and some produce a special + IRIG signal that can be read directly by a bus peripheral, such as + the KSI/Odetics TPRO IRIG SBus interface, or indirectly via the + audio codec of some workstations, as described in [MIL93]. In the + NTP Version 3 daemon xntpd, the PPS signal can be used to augment + the less precise ASCII serial timecode to improve accuracy to the + order of a few tens of microseconds. Support is also included in + the NTP distribution for the TPRO interface, as well as the audio + codec; however, the latter requires a modified kernel audio driver + contained in the bsd_audio.tar.Z distribution in the same host and + directory as the NTP Version 3 distribution mentioned previously. + + 2.4.1. PPS Signal + + The NTP Version 3 distribution includes a special ppsclock + module for the SunOS 4.1.x kernel that captures the PPS signal + presented via a modem-control lead of a serial port. Normally, + the ppsclock module produces a timestamp at each transition of + the PPS signal and provides it to the synchronization daemon + for integration with the serial ASCII timecode, also produced + by the radio clock. With the conventional PLL implementation in + either the daemon or the kernel as described above, the + accuracy of this scheme is limited by the intrinsic stability + of the CPU clock oscillator to a millisecond or two, depending + on environmental temperature variations. + + The ppsclock module has been modified to in addition call a new + kernel routine hardpps() once each second. The kernel routine + compares the timestamp with a sample of the CPU clock + oscillator to develop a frequency offset estimate. This offset + is used to discipline the oscillator frequency, nominally to + within a few parts in 10^8, which is about two orders of + magnitude better than the undisciplined oscillator. The new + feature is conditionally compiled in the code described below + only if the PPS_SYNC option is used in the kernel configuration + file. + + When using the PPS signal to adjust the time, there is a + problem with the SunOS implementation which is very delicate to + fix. The Sun serial port interrupt routine operates at + interrupt priority level 12, while the timer interrupt routine + operates at priority 10. Thus, it is possible that the PPS + signal interrupt can occur during the timer interrupt routine, + with result that a tick increment can be missed and the + returned time early by one tick. It may happen that, if the CPU + clock oscillator is within a few ppm of the PPS oscillator, + this condition can persist for two or more successive PPS + interrupts. A useful workaround has been to use a median filter + to process the PPS sample offsets. In this filter the sample + offsets in a window of 20 samples are sorted by offset and the + + + +Mills [Page 7] + +RFC February 1994 + + six highest and six lowest outlyers discarded. The average of + the eight samples remaining becomes the output of the filter. + + The problem is not nearly so serious when using the PPS signal + to discipline the frequency of the CPU clock oscillator. In + this case the quantity of interest is the contents of the + microseconds counter only, which does not depend on the kernel + time variable. + + 2.4.2. External Clocks + + It is possible to replace the system clock function with an + external bus peripheral. The TPRO device mentioned previously + can be used to provide IRIG-synchronized time with a precision + of 1 us. A driver for this device tprotime.c and header file + tpro.h are included in the kernel.tar.Z distribution mentioned + previously. Using this device, the system clock is read + directly from the interface; however, the device does not + record the year, so special provisions have been made to obtain + the year from the kernel time variable and initialize the + driver accordingly. This feature is conditionally compiled in + the code described below only if the EXT_CLOCK and TPRO options + are used in the kernel configuration file. + + While the system clock function is provided directly by the + microtime() routine in the driver, the kernel time variable + must be disciplined as well, since not all system timing + functions use the microtime() routine. This is done by + measuring the difference between the microtime() clock and + kernel time variable and using the difference to adjust the + kernel PLL as if the adjustment were provided by an external + peer and NTP. + + A good deal of error checking is done in the TPRO driver, since + the system clock is vulnerable to a misbehaving radio clock, + IRIG signal source, interface cables and TPRO device itself. + Unfortunately, there is no easy way to utilize the extensive + diversity and redundancy capabilities available in the NTP + synchronization daemon. In order to avoid disruptions that + might occur if the TPRO time is far different from the kernel + time variable, the latter is used instead of the former if the + difference between the two exceeds 1000 s; presumably in that + case operator intervention is required. + + 2.4.2. External Oscillators + + Even if a source of PPS or IRIG signals is not available, it is + still possible to improve the stability of the system clock + through the use of a specialized bus peripheral. In order to + explore the benefits of such an approach, a special SBus + peripheral caled HIGHBALL has been constructed. The device + includes a pair of 32-bit hardware counters in Unix timeval + format, together with a precision, oven-controlled quartz + oscillator with a stability of a few parts in 10^9. A driver + + +Mills [Page 8] + +RFC February 1994 + + for this device hightime.c and header file high.h are included + in the kernel.tar.Z distribution mentioned previously. This + feature is conditionally compiled in the code described below + only if the EXT_CLOCK and HIGHBALL options are used in the + kernel configuration file. + + Unlike the external clock case, where the system clock function + is provided directly by the microtime() routine in the driver, + the HIGHBALL counter offsets with respect to UTC must be + provided first. This is done using the ordinary kernel PLL, but + controlling the counter offsets directly, rather than the + kernel time variable. At first, this might seem to defeat the + purpose of the design, since the jitter and wander of the + synchronization source will affect the counter offsets and thus + the accuracy of the time. However, the jitter is much reduced + by the PLL and the wander is small, especially if using a radio + clock or another primary server disciplined in the same way. In + practice, the scheme works to reduce the incidental wander to a + few parts in 10^8, or about the same as using the PPS signal. + + As in the previous case, the kernel time variable must be + disciplined as well, since not all system timing functions use + the microtime() routine. However, the kernel PLL cannot be used + for this, since it is already in use providing offsets for the + HIGHBALL counters. Therefore, a special correction is + calculated from the difference between the microtime() clock + and the kernel time variable and used to adjust the kernel time + variable at the next timer interrupt. This somewhat roundabout + approach is necessary in order that the adjustment does not + cause the kernel time variable to jump backwards and possibly + lose or duplicate a timer event. + + 2.5 Other Features + + It is a design feature of the NTP architecture that the system + clocks in a synchronization subnet are to read the same or nearly + the same values before during and after a leap-second event, as + declared by national standards bodies. The new model is designed + to implement the leap event upon command by an ntp_adjtime() + argument. The intricate and sometimes arcane details of the model + and implementation are discussed in [MIL91b] and [MIL93]. Further + details are given in the technical summary later in this + memorandum. + +3. Technical Summary + + In order to more fully understand the workings of the model, a stand- + alone simulator kern.c and header file timex.h are included in the + kernel.tar.Z distribution mentioned previously. In addition, an + example kernel module kern_ntptime.c which implements the + ntp_gettime() and ntp_adjtime() system calls is included. Neither of + these programs incorporate licensed code. Since the distribution is + somewhat large, due to copious comments and ornamentation, it is + impractical to include a listing of these programs in this + + +Mills [Page 9] + +RFC February 1994 + + memorandum. In any case, implementors may choose to snip portions of + the simulator for use in new kernel designs, but, due to formatting + conventions, this would be difficult if included in this memorandum. + + The kern.c program is an implementation of an adaptive-parameter, + first-order, type-II phase-lock loop. The system clock is implemented + using a set of variables and algorithms defined in the simulator and + driven by explicit offsets generated by a driver program also + included in the program. The algorithms include code fragments almost + identical to those in the machine-specific kernel implementations and + operate in the same way, but the operations can be understood + separately from any licensed source code into which these fragments + may be integrated. The code fragments themselves are not derived from + any licensed code. The following discussion assumes that the + simulator code is available for inspection. + + 3.1. PLL Simulation + + The simulator operates in conformance with the analytical model + described in [MIL92b]. The main() program operates as a driver for + the fragments hardupdate(), hardclock(), second_overflow(), + hardpps() and microtime(), although not all functions implemented + in these fragments are simulated. The program simulates the PLL at + each timer interrupt and prints a summary of critical program + variables at each time update. + + There are three defined options in the kernel configuration file + specific to each implementation. The PPS_SYNC option provides + support for a pulse-per-second (PPS) signal, which is used to + discipline the frequency of the CPU clock oscillator. The + EXT_CLOCK option provides support for an external kernel-readable + clock, such as the KSI/Odetics TPRO IRIG interface or HIGHBALL + precision oscillator, both for the SBus. The TPRO option provides + support for the former, while the HIGHBALL option provides support + for the latter. External clocks are implemented as the microtime() + clock driver, with the specific source code selected by the kernel + configuration file. + + 3.1.1. The hardupdate() Fragment + + The hardupdate() fragment is called by ntp_adjtime() as each + update is computed to adjust the system clock phase and + frequency. Note that the time constant is in units of powers of + two, so that multiplies can be done by simple shifts. The phase + variable is computed as the offset divided by the time + constant. Then, the time since the last update is computed and + clamped to a maximum (for robustness) and to zero if + initializing. The offset is multiplied (sorry about the ugly + multiply) by the result and divided by the square of the time + constant and then added to the frequency variable. Note that + all shifts are assumed to be positive and that a shift of a + signed quantity to the right requires a little dance. + + + + +Mills [Page 10] + +RFC February 1994 + + With the defines given in the program and header file, the + maximum time offset is determined by the size in bits of the + long type (32 or 64) less the SHIFT_UPDATE scale factor (12) or + at least 20 bits (signed). The scale factor is chosen so that + there is no loss of significance in later steps, which may + involve a right shift up to SHIFT_UPDATE bits. This results in + a time adjustment range over +-512 ms. Since time_constant must + be greater than or equal to zero, the maximum frequency offset + is determined by the SHIFT_USEC scale factor (16) or at least + 16 bits (signed). This results in a frequency adjustment range + over +-31,500 ppm. + + In the addition step, the value of offset * mtemp is not + greater than MAXPHASE * MAXSEC = 31 bits (signed), which will + not overflow a long add on a 32-bit machine. There could be a + loss of precision due to the right shift of up to 12 bits, + since time_constant is bounded at 6. This results in a net + worst-case frequency resolution of about .063 ppm, which is not + significant for most quartz oscillators. The worst case could + be realized only if the NTP peer misbehaves according to the + protocol specification. + + The time_offset value is clamped upon entry. The time_phase + variable is an accumulator, so is clamped to the tolerance on + every call. This helps to damp transients before the oscillator + frequency has been determined, as well as to satisfy the + correctness assertions if the time synchronization protocol or + implementation misbehaves. + + 3.1.2. The hardclock() Fragment + + The hardclock() fragment is inserted in the hardware timer + interrupt routine at the point the system clock is to be + incremented by the value of tick. Previous to this fragment the + time_update variable has been initialized to the value computed + by the adjtime() system call in the stock Unix kernel, normally + plus/minus the tickadj value, which is usually in the order of + 5 us. The time_phase variable, which represents the + instantaneous phase of the system clock, is advanced by + time_adj, which is calculated in the second_overflow() fragment + described below. If the value of time_phase exceeds 1 us in + scaled units, time_update is increased by the (signed) excess + and time_phase retains the residue. + + Except in the case of an external oscillator such as the + HIGHBALL interface, the hardclock() fragment advances the + system clock by the value of tick plus time_update. However, in + the case of an external oscillator, the system clock is + obtained directly from the interface and time_update used to + discipline that interface instead. However, the system clock + must still be disciplined as explained previously, so the value + of clock_cpu computed by the second_overflow() fragment is used + instead. + + + +Mills [Page 11] + +RFC February 1994 + + 3.1.3. The second_overflow() Fragment + + The second_overflow() fragment is inserted at the point where + the microseconds field of the system time variable is being + checked for overflow. Upon overflow the maximum error + time_maxerror is increased by time_tolerance to reflect the + maximum time offset due to oscillator frequency error. Then, + the increment time_adj to advance the kernel time variable is + calculated from the (scaled) time_offset and time_freq + variables updated at the last call to the hardclock() fragment. + + The phase adjustment is calculated as a (signed) fraction of + the time_offset remaining, where the fraction is added to + time_adj, then subtracted from time_offset. This technique + provides a rapid convergence when offsets are high, together + with good resolution when offsets are low. The frequency + adjustment is the sum of the (scaled) time_freq variable, an + adjustment necessary when the tick interval does not evenly + divide one second fixtick and PPS frequency adjustment pps_ybar + (if configured). + + The scheme of approximating exact multiply/divide operations + with shifts produces good results, except when an exact + calculation is required, such as when the PPS signal is being + used to discipling the CPU clock oscillator frequency as + described below. As long as the actual oscillator frequency is + a power of two in Hz, no correction is required. However, in + the SunOS kernel the clock frequency is 100 Hz, which results + in an error factor of 0.78. In this case the code increases + time_adj by a factor of 1.25, which results in an overall error + less than three percent. + + On rollover of the day, the leap-second state machine described + below determines whether a second is to be inserted or deleted + in the timescale. The microtime() routine insures that the + reported time is always monotonically increasing. + + 3.1.4. The hardpps() Fragment + + The hardpps() fragment is operative only if the PPS_SYNC option + is specified in the kernel configuration file. It is called + from the serial port driver or equivalent interface at the on- + time transition of the PPS signal. The fragment operates as a + first-order, type-I, frequency-lock loop (FLL) controlled by + the difference between the frequency represented by the + pps_ybar variable and the frequency of the hardware clock + oscillator. + + In order to avoid calling the microtime() routine more than + once for each PPS transition, the interface requires the + calling program to capture the system time and hardware counter + contents at the on-time transition of the PPS signal and + provide a pointer to the timestamp (Unix timeval) and counter + contents as arguments to the hardpps() call. The hardware + + +Mills [Page 12] + +RFC February 1994 + + counter contents can be determined by saving the microseconds + field of the system time, calling the microtime() routine, and + subtracting the saved value. If a microseconds overflow has + occured during the process, the resulting microseconds value + will be negative, in which case the caller adds 1000000 to + normalize the microseconds field. + + The frequency of the hardware oscillator can be determined from + the difference in hardware counter readings at the beginning + and end of the calibration interval divided by the duration of + the interval. However, the oscillator frequency tolerance, as + much as 100 ppm, may cause the difference to exceed the tick + value, creating an ambiguity. In order to avoid this ambiguity, + the hardware counter value at the beginning of the interval is + increased by the current pps_ybar value once each second, but + computed modulo the tick value. At the end of the interval, the + difference between this value and the value computed from the + hardware counter is used as a control signal sample for the + FLL. + + Control signal samples which exceed the frequency tolerance are + discarded, as well as samples resulting from excessive interval + duration jitter. Surviving samples are then processed by a + three-stage median filter. The signal which drives the FLL is + derived from the median sample, while the average of the + differences between the other two samples is used as a measure + of dispersion. If the dispersion is below the threshold + pps_dispmax, the median is used to correct the pps_ybar value + with a weight expressed as a shift PPS_AVG (2). In addition to + the averaging function, pps_disp is increased by the amount + pps_dispinc once each second. The result is that, should the + dispersion be exceptionally high, or if the PPS signal fails + for some reason, the pps_disp will eventually exceed + pps_dispmax and raise an alarm. + + Initially, an approximate value for pps_ybar is not known, so + the duration of the calibration interval must be kept small to + avoid overflowing the tick. The time difference at the end of + the calibration interval is measured. If greater than tick/4, + the interval is reduced by half. If less than this fraction for + four successive calibration intervals, the interval is doubled. + This design automatically adapts to nominal jitter in the PPS + signal, as well as the value of tick. The duration of the + calibration interval is set by the pps_shift variable as a + shift in powers of two. The minimum value PPS_SHIFT (2) is + chosen so that with the highest CPU oscillator frequency 1024 + Hz and frequency tolerance 100 ppm the tick will not overflow. + The maximum value PPS_SHIFTMAX (8) is chosen such that the + maximum averaging time is about 1000 s as determined by + measurements of Allan variance [MIL93]. + + Should the PPS signal fail, the current frequency estimate + pps_ybar continues to be used, so the nominal frequency remains + correct subject only to the instability of the undisciplined + + +Mills [Page 13] + +RFC February 1994 + + oscillator. The procedure to save and restore the frequency + estimate works as follows. When setting the frequency from a + file, the time_freq value is set as the file value minus the + pps_ybar value; when retrieving the frequency, the two values + are added before saving in the file. This scheme provides a + seamless interface should the PPS signal fail or the kernel + configuration change. Note that the frequency discipline is + active whether or not the synchronization daemon is active. + Since all Unix systems take some time after reboot to build a + running system, usually by that time the discipline process has + already settled down and the initial transients due to + frequency discipline have damped out. + + 3.1.4. External Clock Interface + + The external clock driver interface is implemented with two + routines, microtime(), which returns the current clock time, + and clock_set(), which furnishes the apparent system time + derived from the kernel time variable. The latter routine is + called only when the clock is set using the settimeofday() + system call, but can be called from within the driver, such as + when the year rolls over, for example. + + In the stock SunOS kernel and modified Ultrix and OSF/1 + kernels, the microtime() routine returns the kernel time + variable plus an interpolation between timer interrupts based + on the contents of a hardware counter. In the case of an + external clock, such as described above, the system clock is + read directly from the hardware clock registers. Examples of + external clock drivers are in the tprotime.c and hightime.c + routines included in the kernel.tar.Z distribution. + + The external clock routines return a status code which + indicates whether the clock is operating correctly and the + nature of the problem, if not. The return code is interpreted + by the ntp_gettime() system call, which transitions the status + state machine to the TIME_ERR state if an error code is + returned. This is the only error checking implemented for the + external clock in the present version of the code. + + The simulator has been used to check the PLL operation over the + design envelope of +-512 ms in time error and +-100 ppm in + frequency error. This confirms that no overflows occur and that + the loop initially converges in about 15 minutes for timer + interrupt rates from 50 Hz to 1024 Hz. The loop has a normal + overshoot of a few percent and a final convergence time of several + hours, depending on the initial time and frequency error. + + 3.2. Leap Seconds + + It does not seem generally useful in the user application + interface to provide additional details private to the kernel and + synchronization protocol, such as stratum, reference identifier, + reference timestamp and so forth. It would in principle be + + +Mills [Page 14] + +RFC February 1994 + + possible for the application to independently evaluate the quality + of time and project into the future how long this time might be + "valid." However, to do that properly would duplicate the + functionality of the synchronization protocol and require + knowledge of many mundane details of the platform architecture, + such as the subnet configuration, reachability status and related + variables. For the curious, the ntp_adjtime() system call can be + used to reveal some of these mysteries. + + However, the user application may need to know whether a leap + second is scheduled, since this might affect interval calculations + spanning the event. A leap-warning condition is determined by the + synchronization protocol (if remotely synchronized), by the + timecode receiver (if available), or by the operator (if awake). + This condition is set by the synchronization daemon on the day the + leap second is to occur (30 June or 31 December, as announced) by + specifying in a ntp_adjtime() system call a clock status of either + TIME_DEL, if a second is to be deleted, or TIME_INS, if a second + is to be inserted. Note that, on all occasions since the inception + of the leap-second scheme, there has never been a deletion + occasion, nor is there likely to be one in future. If the value is + TIME_DEL, the kernel adds one second to the system time + immediately following second 23:59:58 and resets the clock status + to TIME_OK. If the value is TIME_INS, the kernel subtracts one + second from the system time immediately following second 23:59:59 + and resets the clock status to TIME_OOP, in effect causing system + time to repeat second 59. Immediately following the repeated + second, the kernel resets the clock status to TIME_OK. + + Depending upon the system call implementation, the reported time + during a leap second may repeat (with the TIME_OOP return code set + to advertise that fact) or be monotonically adjusted until system + time "catches up" to reported time. With the latter scheme the + reported time will be correct before and shortly after the leap + second (depending on the number of microtime() calls during the + leap second), but freeze or slowly advance during the leap second + itself. However, Most programs will probably use the ctime() + library routine to convert from timeval (seconds, microseconds) + format to tm format (seconds, minutes,...). If this routine is + modified to use the ntp_gettime() system call and inspect the + return code, it could simply report the leap second as second 60. + + 3.3. Clock Status State Machine + + The various options possible with the system clock model described + in this memorandum require a careful examination of the state + transitions, status indications and recovery procedures should a + crucial signal or interface fail. In this section is presented a + prototype state machine designed to support leap second insertion + and deletion, as well as reveal various kinds of errors in the + synchronization process. The states of this machine are decoded as + follows: + + + + +Mills [Page 15] + +RFC February 1994 + + TIME_OK If an external clock is present, it is working properly + and the system clock is derived from it. If no external + clock is present, the synchronization daemon is working + properly and the system clock is synchronized to a radio + clock or one or more peers. + + TIME_INS An insertion of one second in the system clock has been + declared following the last second of the current day, + but has not yet been executed. + + TIME_DEL A deletion of the last second of the current day has + been declared, but not yet executed. + + TIME_OOP An insertion of one second in the system clock has been + declared following the last second of the current day. + The second is in progress, but not yet completed. + Library conversion routines should interpret this second + as 23:59:60. + + TIME_BAD Either (a) the synchronization daemon has declared the + protocol is not working properly, (b) all sources of + outside synchronization have been lost or (c) an + external clock is present and it has just become + operational following a non-operational condition. + + TIME_ERR An external clock is present, but is in a non- + operational condition. + + In all except the TIME_ERR state the system clock is derived from + either an external clock, if present, or the kernel time variable, + if not. In the TIME_ERR state the external clock is present, but + not working properly, so the system clock may be derived from the + kernel time variable. The following diagram indicates the normal + transitions of the state machine. Not all valid transitions are + shown. + + +--------+ +--------+ +--------+ +--------+ + | | | | | | | | + |TIME_BAD|---->|TIME_OK |<----|TIME_OOP|<----|TIME_INS| + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + A A + | | + | | + +--------+ +--------+ + | | | | + |TIME_ERR| |TIME_DEL| + | | | | + +--------+ +--------+ + + The state machine makes a transition once each second at an + instant where the microseconds field of the kernel time variable + overflows and one second is added to the seconds field. However, + this condition is checked at each timer interrupt, which may not + + +Mills [Page 16] + +RFC February 1994 + + exactly coincide with the actual instant of overflow. This may + lead to some interesting anomalies, such as a status indication of + a leap second in progress (TIME_OOP) when actually the leap second + had already expired. + + The following state transitions are executed automatically by the + kernel: + + any state -> TIME_ERR This transition occurs when an external + clock is present and an attempt is made to + read it when in a non-operational + condition. + + TIME_INS -> TIME_OOP This transition occurs immediately + following second 86,400 of the current day + when an insert-second event has been + declared. + + TIME_OOP -> TIME_OK This transition occurs immediately + following second 86,401 of the current + day; that is, one second after entry to + the TIME_OOP state. + + TIME_DEL -> TIME_OK This transition occurs immediately + following second 86,399 of the current day + when a delete-second event has been + declared. + + The following state transitions are executed by specific + ntp_adjtime() system calls: + + TIME_OK -> TIME_INS This transition occurs as the result of a + ntp_adjtime() system call to declare an + insert-second event. + + TIME_OK -> TIME_DEL This transition occurs as the result of a + ntp_adjtime() system call to declare a + delete-second event. + + any state -> TIME_BAD This transition occurs as the result of a + ntp_adjtime() system call to declare loss + of all sources of synchronization or in + other cases of error. + + The following table summarizes the actions just before, during and + just after a leap-second event. Each line in the table shows the + UTC and NTP times at the beginning of the second. The left column + shows the behavior when no leap event is to occur. In the middle + column the state machine is in TIME_INS at the end of UTC second + 23:59:59 and the NTP time has just reached 400. The NTP time is + set back one second to 399 and the machine enters TIME_OOP. At the + end of the repeated second the machine enters TIME_OK and the UTC + and NTP times are again in correspondence. In the right column the + state machine is in TIME_DEL at the end of UTC second 23:59:58 and + + +Mills [Page 17] + +RFC February 1994 + + the NTP time has just reached 399. The NTP time is incremented, + the machine enters TIME_OK and both UTC and NTP times are again in + correspondence. + + No Leap Leap Insert Leap Delete + UTC NTP UTC NTP UTC NTP + --------------------------------------------- + 23:59:58|398 23:59:58|398 23:59:58|398 + | | | + 23:59:59|399 23:59:59|399 00:00:00|400 + | | | + 00:00:00|400 23:59:60|399 00:00:01|401 + | | | + 00:00:01|401 00:00:00|400 00:00:02|402 + | | | + 00:00:02|402 00:00:01|401 00:00:03|403 + | | | + + To determine local midnight without fuss, the kernel code simply + finds the residue of the time.tv_sec (or time.tv_sec + 1) value + mod 86,400, but this requires a messy divide. Probably a better + way to do this is to initialize an auxiliary counter in the + settimeofday() routine using an ugly divide and increment the + counter at the same time the time.tv_sec is incremented in the + timer interrupt routine. For future embellishment. + +4. Programming Model and Interfaces + + This section describes the programming model for the synchronization + daemon and user application programs. The ideas are based on + suggestions from Jeff Mogul and Philip Gladstone and a similar + interface designed by the latter. It is important to point out that + the functionality of the original Unix adjtime() system call is + preserved, so that the modified kernel will work as the unmodified + one, should the new features not be in use. In this case the + ntp_adjtime() system call can still be used to read and write kernel + variables that might be used by a synchronization daemon other than + NTP, for example. + + 4.1. The ntp_gettime() System Call + + The syntax and semantics of the ntp_gettime() call are given in + the following fragment of the timex.h header file. This file is + identical, except for the SHIFT_HZ define, in the SunOS, Ultrix + and OSF/1 kernel distributions. (The SHIFT_HZ define represents + the logarithm to the base 2 of the clock oscillator frequency + specific to each system type.) Note that the timex.h file calls + the syscall.h system header file, which must be modified to define + the SYS_ntp_gettime system call specific to each system type. The + kernel distributions include directions on how to do this. + + /* + * This header file defines the Network Time Protocol (NTP) + * interfaces for user and daemon application programs. These are + + +Mills [Page 18] + +RFC February 1994 + + * implemented using private system calls and data structures and + * require specific kernel support. + * + * NAME + * ntp_gettime - NTP user application interface + * + * SYNOPSIS + * #include + * + * int system call(SYS_ntp_gettime, tptr) + * + * int SYS_ntp_gettime defined in syscall.h header file + * struct ntptimeval *tptr pointer to ntptimeval structure + * + * NTP user interface - used to read kernel clock values + * Note: maximum error = NTP synch distance = dispersion + delay / + * 2 + * estimated error = NTP dispersion. + */ + struct ntptimeval { + struct timeval time; /* current time */ + long maxerror; /* maximum error (us) */ + long esterror; /* estimated error (us) */ + }; + + The ntp_gettime() system call returns three values in the + ntptimeval structure: the current time in unix timeval format plus + the maximum and estimated errors in microseconds. While the 32-bit + long data type limits the error quantities to something more than + an hour, in practice this is not significant, since the protocol + itself will declare an unsynchronized condition well below that + limit. In the NTP Version 3 specification, if the protocol + computes either of these values in excess of 16 seconds, they are + clamped to that value and the system clock declared + unsynchronized. + + Following is a detailed description of the ntptimeval structure + members. + + struct timeval time; /* current time */ + + This member returns the current system time, expressed as a + Unix timeval structure. The timeval structure consists of two + 32-bit words; the first returns the number of seconds past 1 + January 1970, while the second returns the number of + microseconds. + + long maxerror; /* maximum error (us) */ + + This member returns the time_maxerror kernel variable in + microseconds. See the entry for this variable in section 5 for + additional information. + + + + +Mills [Page 19] + +RFC February 1994 + + long esterror; /* estimated error (us) */ + + This member returns the time_esterror kernel variable in + microseconds. See the entry for this variable in section 5 for + additional information. + + 4.2. The ntp_adjtime() System Call + + The syntax and semantics of the ntp_adjtime() call are given in + the following fragment of the timex.h header file. Note that, as + in the ntp_gettime() system call, the syscall.h system header file + must be modified to define the SYS_ntp_adjtime system call + specific to each system type. + + /* + * NAME + * ntp_adjtime - NTP daemon application interface + * + * SYNOPSIS + * #include + * + * int system call(SYS_ntp_adjtime, mode, tptr) + * + * int SYS_ntp_adjtime defined in syscall.h header file + * struct timex *tptr pointer to timex structure + * + * NTP daemon interface - used to discipline kernel clock + * oscillator + */ + struct timex { + int mode; /* mode selector */ + long offset; /* time offset (us) */ + long frequency; /* frequency offset (scaled ppm) */ + long maxerror; /* maximum error (us) */ + long esterror; /* estimated error (us) */ + int status; /* clock command/status */ + long time_constant; /* pll time constant */ + long precision; /* clock precision (us) (read only) + */ + long tolerance; /* clock frequency tolerance (scaled + * ppm) (read only) */ + /* + * The following read-only structure members are implemented + * only if the PPS signal discipline is configured in the + * kernel. + */ + long ybar; /* frequency estimate (scaled ppm) */ + long disp; /* dispersion estimate (scaled ppm) + */ + int shift; /* interval duration (s) (shift) */ + long calcnt; /* calibration intervals */ + long jitcnt; /* jitter limit exceeded */ + long discnt; /* dispersion limit exceeded */ + }; + + +Mills [Page 20] + +RFC February 1994 + + The ntp_adjtime() system call is used to read and write certain + time-related kernel variables summarized in this and subsequent + sections. Writing these variables can only be done in superuser + mode. To write a variable, the mode structure member is set with + one or more bits, one of which is assigned each of the following + variables in turn. The current values for all variables are + returned in any case; therefore, a mode argument of zero means to + return these values without changing anything. + + Following is a description of the timex structure members. + + int mode; /* mode selector */ -1. Introduction + This is a bit-coded variable selecting one or more structure + members, with one bit assigned each member. If a bit is set, + the value of the associated member variable is copied to the + corresponding kernel variable; if not, the member is ignored. + The bits are assigned as given in the following fragment of the + timex.h header file. Note that the precision and tolerance are + determined by the kernel and cannot be changed by + ntp_adjtime(). + + /* + * Mode codes (timex.mode) + */ + #define ADJ_OFFSET 0x0001 /* time offset */ + #define ADJ_FREQUENCY 0x0002 /* frequency offset */ + #define ADJ_MAXERROR 0x0004 /* maximum time error */ + #define ADJ_ESTERROR 0x0008 /* estimated time error */ + #define ADJ_STATUS 0x0010 /* clock status */ + #define ADJ_TIMECONST 0x0020 /* pll time constant */ + + long offset; /* time offset (us) */ + + If selected, this member replaces the value of the time_offset + kernel variable in microseconds. The absolute value must be + less than MAXPHASE microseconds defined in the timex.h header + file. See the entry for this variable in section 5 for + additional information. + + If within range and the PPS signal and/or external oscillator + are configured and operating properly, the clock status is + automatically set to TIME_OK. + + long time_constant; /* pll time constant */ + + If selected, this member replaces the value of the + time_constant kernel variable. The value must be between zero + and MAXTC defined in the timex.h header file. See the entry for + this variable in section 5 for additional information. + + + + + + +Mills [Page 21] + +RFC February 1994 + + long frequency; /* frequency offset (scaled ppm) */ + + If selected, this member replaces the value of the + time_frequency kernel variable. The value is in ppm, with the + integer part in the high order 16 bits and fraction in the low + order 16 bits. The absolute value must be in the range less + than MAXFREQ ppm defined in the timex.h header file. See the + entry for this variable in section 5 for additional + information. + + long maxerror; /* maximum error (us) */ + + If selected, this member replaces the value of the + time_maxerror kernel variable in microseconds. See the entry + for this variable in section 5 for additional information. + + long esterror; /* estimated error (us) */ + + If selected, this member replaces the value of the + time_esterror kernel variable in microseconds. See the entry + for this variable in section 5 for additional information. + + int status; /* clock command/status */ + + If selected, this member replaces the value of the time_status + kernel variable. See the entry for this variable in section 5 + for additional information. + + In order to set this variable by ntp_adjtime(), either (a) the + current clock status must be TIME_OK or (b) the member value is + TIME_BAD; that is, the ntp_adjtime() call can always set the + clock to the unsynchronized state or, if the clock is running + correctly, can set it to any state. In any case, the + ntp_adjtime() call always returns the current state in this + member, so the caller can determine whether or not the request + succeeded. + + long time_constant; /* pll time constant */ -This memo describes modifications to certain SunOS, Ultrix and OSF/1 -kernel software that manage the system clock and timer functions. They -provide improved accuracy and stability through the use of a disciplined -clock interface for use with the Network Time Protocol (NTP) or similar -time-synchronization protocol. In addition, for the DEC 3000 AXP (Alpha) -and DECstation 5000/240 machines, the modifications provide improved -precision within one microsecond (us) (SunOS 4.1.x already does provide -precision to this order). The NTP Version 3 daemon xntpd operates with -these kernel modifications to provide synchronization in principle to -within this order, but in practice this is limited by the short-term -stability of the timer oscillator to within the order of 100 usec. - -This memo describes the principles behind the design and operation of -the new software. There are three versions: one that operates with the -SunOS 4.1.x kernels, a second that operates with the Ultrix 4.x kernels -and a third that operates with the OSF/1 V1.x kernels. A detailed -description of the variables and algorithms is given in the hope that -similar functionality can be incorporated in Unix kernels for other -machines. The algorithms involve only minor changes to the system clock -and interval timer routines and include interfaces for application -programs to learn the system clock status and certain statistics of the -time-synchronization process. Detailed installation instructions are -given in a companion README.install file included in the kernel -distributions. The kernel software itself is not provided for public -distribution, since it involves licensed code. Detailed instructions on -how to obtain it for either SunOS, Ultrix or OSF/1 will be given -separately. - -The principal feature added to the Unix kernels is to change the way the -system clock is controlled, in order to provide precision time and -frequency adjustments. Another feature utilizes an undocumented bus- -cycle counter in the DEC 3000 AXP and DECstation 5000/240 to provide -precise time to the microsecond. This feature can in principle be used -with any DEC machine that has this counter, although this has not been -verified. The addition of these features does not affect the operation -of existing Unix system calls such as gettimeofday(), settimeofday() and -adjtime(); however, if the new features are in use, the operations of -adjtime() are controlled instead by a new system call ntp_adjtime(). - -Most Unix programs read the system clock using the gettimeofday() system -call, which returns only the system time and timezone data. For some -applications it is useful to know the maximum error of the reported time -due to all causes, including clock reading errors, oscillator frequency -errors and accumulated latencies on the path to a primary reference -source. However, the new software can adjust the system clock to -compensate for its intrinsic frequency error, so that the timing errors -expected in normal operation will usually be much less than the maximum -error. The user application interface includes a new system call -ntp_gettime(), which returns the system time, as well as the maximum -error and estimated error. This interface is intended to support -applications that need such things, including distributed file systems, -multimedia teleconferencing and other real-time applications. The -protocol daemon application interface includes a new system call -ntp_adjtime(), which can be used to read and write kernel variables used -for precision timekeeping, including time and frequency adjustments, -controlling time constant, leap-second warning and related data. - -In this memo, NTP Version 3 and the Unix implementation xntpd are used -as an example application of the new system calls for use by a protocol -daemon. In principle, the new system calls can be used by other -protocols and daemon implementations as well. Even in cases where the -local time is maintained by periodic exchanges of messages at relatively -long intervals, such as using the NIST Automated Computer Time Service, -the ability to precisely adjust the local clock frequency simplifies the -synchronization procedures and allows the call frequency to be -considerably reduced. - -2. Design Principles - -In order to understand how the new software works, it is useful to -consider how most Unix systems maintain the system time. In the original -design a hardware timer interrupts the kernel at a fixed rate: 100 Hz in -the SunOS kernel, 256 Hz in the Ultrix kernel and 1024 Hz in the OSF/1 -kernel. Since the Ultrix kernel rate does not evenly divide one second -in microseconds, the kernel adds 64 microseconds once each second, so -the timescale consists of 255 advances of 3906 usec plus one of 3970 -usec. Similarly, the OSF/1 kernel adds 576 usec once each second, so its -timescale consists of 1023 advances of 976 usec plus one of 1552 usec. - -In all Unix kernels considered in this memo, it is possible to slew the -system clock to a new offset using the standard Unix adjtime() system -call. To do this the clock frequency is changed by adding or subtracting -a fixed amount (tickadj) at each timer interrupt (tick) for a calculated -number of ticks. Since this calculation involves dividing the requested -offset by tickadj, it is possible to slew to a new offset with a -precision only of tickadj, which is usually in the neighborhood of 5 us, -but sometimes much higher. This results in an amortization error which -can accumulate to unacceptable levels, so that special provisions must -be made in the clock adjustment procedures of the protocol daemon. - -In order to maintain the system clock within specified bounds with this -scheme, it is necessary to call adjtime() on a regular basis. For -instance, let the bound be set at 100 usec, which is a reasonable value -for NTP-synchronized hosts on a local network, and let the onboard -oscillator tolerance be 100 parts-per-million (ppm), which is a -reasonably conservative assumption. This requires that adjtime() be -called at intervals not exceeding 1 second (s), which is in fact what -the unmodified NTP software daemon does. - -In the new software this scheme is replaced by another that extends the -low-order bits of the system clock to provide very precise clock -adjustments. At each timer interrupt a precisely calibrated quantity is -added to the composite time value and overflows handled as required. The -quantity is computed from the measured clock offset and in addition a -frequency adjustment, which is automatically calculated from previous -time adjustments. This implementation operates as an adaptive-parameter -first-order, type-II, phase-lock loop (PLL), which in principle provides -precision control of the system clock phase to within +-1 us and -frequency to within +-5 nanoseconds (ns) per day. - -This PLL model is identical to the one implemented in NTP, except that -in NTP the software daemon has to simulate the PLL using only the -original adjtime() system call. The daemon is considerably complicated -by the need to parcel time adjustments at frequent intervals in order to -maintain the accuracy to specified bounds. The modified kernel routines -do this directly, allowing vast gobs of ugly daemon code to be avoided -at the expense of only a small amount of new code in the kernel. In -fact, the amount of code added to the kernel for the new scheme is about -the amount needed to implement the old scheme. A new system call -ntp_adjtime(), which operates in a way similar to the original -adjtime(), is called only as each new time update is determined, which -in NTP occurs at intervals of from 16 s to 1024 s. In addition, doing -the frequency correction in the kernel means that the system time runs -true even if the daemon were to cease operation or the network paths to -the primary reference source fail. The addition of the new ntp_adjtime() -system call does not affect the original adjtime() system call, which -continues to operate in its traditional fashion. However, the two system -calls canot be used at the same time; only one of the two should be used -on any given system. - -It is the intent in the design that settimeofday() be used for changes -in system time greater than +-128 ms. It has been the Internet -experience that the need to change the system time in increments greater -than +-128 milliseconds is extremely rare and is usually associated with -a hardware or software malfunction or system reboot. Once the system -clock has been set in this way, the ntp_adjtime() system call is used to -provide periodic updates including the time offset, maximum error, -estimated error and PLL time constant. With NTP the update interval -depends on the measured error and time constant; however, the scheme is -quite forgiving and neither moderate loss of updates nor variations in -the length of the polling interval are serious. - -In addition, the kernel adjusts the maximum error to grow by an amount -equal to the oscillator frequency tolerance times the elapsed time since -the last update. The default engineering parameters have been optimized -for intervals not greater than about 16 s. For longer intervals the PLL -time constant can be adjusted to optimize the dynamic response up to -intervals of 1024 s. Normally, this is automatically done by NTP. In any -case, if updates are suspended, the PLL coasts at the frequency last -determined, which usually results in errors increasing only to a few -tens of milliseconds over a day. - -The new code needs to know the initial frequency offset and time -constant for the PLL, and the daemon needs to know the current frequency -offset computed by the kernel for monitoring purposes. These data are -exchanged between the kernel and protocol daemon using ntp_adjtime() as -documented later in this memo. Provisions are made to exchange related -timing information, such as the maximum error and estimated error, -between the kernel and daemon and between the kernel and application -programs. - -In the DEC 3000 AXP, DECstation 5000/240 and possibly other DEC -machines there is an undocumented hardware register that counts system -bus cycles at a rate of 25 MHz. The new kernel microtime() routine tests -for the CPU type and, in the case of these machines, use this register -to interpolate system time between hardware timer interrupts. This -results in a precision of +-1 us for all time values obtained via the -gettimeofday() and ntp_gettime() system calls. These routines call the -microtime() routine, which returns the actual interpolated value but -does not change the kernel time variable. Therefore, other kernel -routines that access the kernel time variable directly and do not call -either gettimeofday(), ntp_gettime() or microtime() will continue their -present behavior. The microtime() feature is independent of other -features described here and is operative even if the kernel PLL or new -system calls have not been implemented. - -While any protocol daemon can in principle be modified to use the new -system calls, the most likely will be users of the NTP Version 3 daemon -xntpd. The xntpd code determines whether the new system calls are -implemented and automatically reconfigures as required. When -implemented, the daemon reads the frequency offset from a file and -provides it and the initial time constant via ntp_adjtime(). In -subsequent calls to ntp_adjtime(), only the time adjustment and time -constant are affected. The daemon reads the frequency from the kernel -using ntp_adjtime() at intervals of about one hour and writes it to the -system log file. This information is recovered when the daemon is -restarted after reboot, for example, so the sometimes extensive training -period to learn the frequency separately for each system can be avoided. - -3. Kernel Interfaces - -This section describes the kernel interfaces to the protocol daemon and -user applications. The ideas are based on suggestions from Jeff Mogul -and Philip Gladstone and a similar interface designed by the latter. It -is important to point out that the functionality of the original Unix -adjtime() system call is preserved, so that the modified kernel will -work as the unmodified one should the kernel PLL not be in use. In this -case the ntp_adjtime() system call can still be used to read and write -kernel variables that might be used by a protocol daemon other than NTP, -for example. - -3.1. The ntp_gettime() System Call - -The syntax and semantics of the ntp_gettime() call are given in the -following fragment of the timex.h header file. This file is identical in -the SunOS, Ultrix and OSF/1 kernel distributions. Note that the timex.h -file calls the syscall.h system header file, which must be modified to -define the SYS_ntp_gettime system call specific to each system type. The -kernel distributions include directions on how to do this. - -/* - * This header file defines the Network Time Protocol (NTP) interfaces - * for user and daemon application programs. These are implemented using - * private system calls and data structures and require specific kernel - * support. - * - * NAME - * ntp_gettime - NTP user application interface - * - * SYNOPSIS - * #include - * - * int system call(SYS_ntp_gettime, tptr) - * - * int SYS_ntp_gettime defined in syscall.h header file - * struct ntptimeval *tptr pointer to ntptimeval structure - * - * NTP user interface - used to read kernel clock values - * Note: maximum error = NTP synch distance = dispersion + delay / 2; - * estimated error = NTP dispersion. - */ -struct ntptimeval { - struct timeval time; /* current time */ - long maxerror; /* maximum error (usec) */ - long esterror; /* estimated error (usec) */ -}; - -The ntp_gettime() system call returns three values in the ntptimeval -structure: the current time in unix timeval format plus the maximum and -estimated errors in microseconds. While the 32-bit long data type limits -the error quantities to something more than an hour, in practice this is -not significant, since the protocol itself will declare an -unsynchronized condition well below that limit. If the protocol computes -either of these values in excess of 16 seconds, they are clamped to that -value and the local clock declared unsynchronized. - -Following is a detailed description of the ntptimeval structure members. - -struct timeval time; - - This member is set to the current system time, expressed as a Unix - timeval structure. The timeval structure consists of two 32-bit - words, one for the number of seconds past 1 January 1970 and the - other the number of microseconds past the most recent second's - epoch. - -long maxerror; - - This member is set to the value of the time_maxerror kernel - variable, which establishes the maximum error of the indicated time - relative to the primary reference source, in microseconds. This - variable can also be set and read by the ntp_adjtime() system call. - For NTP, the value is determined as the synchronization distance, - which is equal to the root dispersion plus one-half the root delay. - It is increased by a small amount (time_tolerance) each second to - reflect the clock frequency tolerance. This variable is computed by - the time-synchronization daemon and the kernel and returned in a - ntp_gettime() system call, but is otherwise not used by the kernel. - -long esterror; - - This member is set to the value of the time_esterror kernel - variable, which establishes the expected error of the indicated - time relative to the primary reference source, in microseconds. - This variable can also be set and read by the ntp_adjtime() system - call. For NTP, the value is determined as the root dispersion, - which represents the best estimate of the actual error of the - system clock based on its past behavior, together with observations - of multiple clocks within the peer group. This variable is computed - by the time-synchronization daemon and returned in a ntp_gettime() - system call, but is otherwise not used by the kernel. - -3.2. The ntp_adjtime() System Call - -The syntax and semantics of the ntp_adjtime() call is given in the -following fragment of the timex.h header file. Note that, as in the -ntp_gettime() system call, the the syscall.h system header file must be -modified to define the SYS_ntp_adjtime system call specific to each -system type. - -/* - * NAME - * ntp_adjtime - NTP daemon application interface - * - * SYNOPSIS - * #include - * - * int system call(SYS_ntp_adjtime, mode, tptr) - * - * int SYS_ntp_adjtime defined in syscall.h header file - * struct timex *tptr pointer to timex structure - * - * NTP daemon interface - used to discipline kernel clock oscillator - */ -struct timex { - int mode; /* mode selector */ - long offset; /* time offset (usec) */ - long frequency; /* frequency offset (scaled ppm) */ - long maxerror; /* maximum error (usec) */ - long esterror; /* estimated error (usec) */ - int status; /* clock command/status */ - long time_constant; /* pll time constant */ - long precision; /* clock precision (usec) (read only) */ - long tolerance; /* clock frequency tolerance (ppm) - * (read only) + If selected, this member replaces the value of the + time_constant kernel variable. The value must be between zero + and MAXTC defined in the timex.h header file. See the entry for + this variable in section 5 for additional information. + + long precision; /* clock precision (us) (read only) */ + + This member returns the time_precision kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + + + + + +Mills [Page 22] + +RFC February 1994 + + long tolerance; /* clock frequency tolerance (scaled ppm) */ -}; - -The ntp_adjtime() system call is used to read and write certain time- -related kernel variables summarized in this and subsequent sections. -Writing these variables can only be done in superuser mode. To write a -variable, the mode structure member is set with one or more bits, one of -which is assigned each of the following variables in turn. The current -values for all variables are returned in any case; therefore, a mode -argument of zero means to return these values without changing anything. - -Following is a description of the timex structure members. - -int mode; - - This is a bit-coded variable selecting one or more structure - members, with one bit assigned each member. If a bit is set, the - value of the associated member variable is copied to the - corresponding kernel variable; if not, the member is ignored. The - bits are assigned as given in the following fragment of the timex.h - header file. Note that the precision and tolerance are intrinsic - properties of the kernel configuration and cannot be changed. - - /* - * Mode codes (timex.mode) - */ - #define ADJ_OFFSET 0x0001 /* time offset */ - #define ADJ_FREQUENCY 0x0002 /* frequency offset */ - #define ADJ_MAXERROR 0x0004 /* maximum time error */ - #define ADJ_ESTERROR 0x0008 /* estimated time error */ - #define ADJ_STATUS 0x0010 /* clock status */ - #define ADJ_TIMECONST 0x0020 /* pll time constant */ - -long offset; - - If selected, this member (scaled) replaces the value of the - time_offset kernel variable, which defines the current time offset - of the phase-lock loop. The value must be in the range +-512 ms in - the present implementation. If so, the clock status is - automatically set to TIME_OK. - -long time_constant; - - If selected, this member replaces the value of the time_constant - kernel variable, which establishes the bandwidth of "stiffness" of - the kernel PLL. The value is used as a shift, with the effective - PLL time constant equal to a multiple of (1 << time_constant), in - seconds. The optimum value for the time_constant variable is - log2(update_interval) - 4, where update_interval is the nominal - interval between clock updates, in seconds. With an ordinary crystal - oscillator the optimum value for time_constant is about 2, giving - an update_interval of 4 (64 s). Values of time_constant between zero - and 2 can be used if quick convergence is necessary; values between - 2 and 6 can be used to reduce network load, but at a modest cost in - accuracy. Values above 6 are appropriate only if a precision - oscillator is available. - -long frequency; - - If selected, this member (scaled) replaces the value of the - time_frequency kernel variable, which establishes the intrinsic - frequency of the local clock oscillator. This variable is scaled by - (1 << SHIFT_USEC) in parts-per-million (ppm), giving it a maximum - value of about +-31 ms/s and a minimum value (frequency resolution) - of about 2e-11, which is appropriate for even the best quartz - oscillator. - -long maxerror; - - If selected, this member replaces the value of the time_maxerror - kernel variable, which establishes the maximum error of the - indicated time relative to the primary reference source, in - microseconds. This variable can also be read by the ntp_gettime() - system call. For NTP, the value is determined as the - synchronization distance, which is equal to the root dispersion - plus one-half the root delay. It is increased by a small amount - (time_tolerance) each second to reflect the clock frequency - tolerance. This variable is computed by the time-synchronization - daemon and the kernel and returned in a ntp_gettime() system call, - but is otherwise not used by the kernel. - -long esterror; - - If selected, this member replaces the value of the time_esterror - kernel variable, which establishes the expected error of the - indicated time relative to the primary reference source, in - microseconds. This variable can also be read by the ntp_gettime() - system call. For NTP, the value is determined as the root - dispersion, which represents the best estimate of the actual error - of the system clock based on its past behavior, together with - observations of multiple clocks within the peer group. This - variable is computed by the time-synchronization daemon and - returned in a ntp_gettime() system call, but is otherwise not used - by the kernel. - -int status; - - If selected, this member replaces the value of the time_status - kernel variable, which records whether the clock is synchronized, - waiting for a leap second, etc. In order to set this variable - explicitly, either (a) the current clock status is TIME_OK or (b) - the member value is TIME_BAD; that is, the ntp_adjtime() call can - always set the clock to the unsynchronized state or, if the clock - is running correctly, can set it to any state. In any case, the - ntp_adjtime() call always returns the current state in this member, - so the caller can determine whether or not the request succeeded. - -long precision; - - This member is set equal to the time_precision kernel in - microseconds variable upon return from the system call. The - time_precision variable cannot be written. This variable represents - the maximum error in reading the system clock, which is ordinarily - equal to the kernel variable tick, 10000 usec in the SunOS kernel, - 3906 usec in Ultrix kernel and 976 usec in the OSF/1 kernel. - However, in cases where the time can be interpolated with - microsecond resolution, such as in the SunOS kernel and modified - Ultrix and OSF/1 kernels, the precision is specified as 1 usec. - This variable is computed by the kernel for use by the time- - synchronization daemon, but is otherwise not used by the kernel. - -long tolerance; - - This member is set equal to the time_tolerance kernel variable in - parts-per-million (ppm) upon return from the system call. The - time_tolerance variable cannot be written. This variable represents - the maximum frequency error or tolerance of the particular platform - and is a property of the architecture and manufacturing process. - -3.3. Command/Status Codes - -The kernel routines use the system clock status variable time_status, -which records whether the clock is synchronized, waiting for a leap -second, etc. The value of this variable is returned as the result code -by both the ntp_gettime() and ntp_adjtime() system calls. In addition, -it can be explicitly read and written using the ntp_adjtime() system -call, but can be written only in superuser mode. Values presently -defined in the timex.h header file are as follows: - -/* - * Clock command/status codes (timex.status) - */ -#define TIME_OK 0 /* clock synchronized */ -#define TIME_INS 1 /* insert leap second */ -#define TIME_DEL 2 /* delete leap second */ -#define TIME_OOP 3 /* leap second in progress */ -#define TIME_BAD 4 /* clock not synchronized */ - -A detailed description of these codes as used by the leap-second state -machine is given later in this memo. In case of a negative result code, -the kernel has intercepted an invalid address or (in case of the -ntp_adjtime() system call), a superuser violation. - -4. Technical Summary - -In order to more fully understand the workings of the PLL, a stand-alone -simulator kern.c is included in the kernel distributions. This is an -implementation of an adaptive-parameter, first-order, type-II phase-lock -loop. The system clock is implemented using a set of variables and -algorithms defined in the simulator and driven by explicit offsets -generated by the simulator. The algorithms include code fragments -identical to those in the modified kernel routines and operate in the -same way, but the operations can be understood separately from any -licensed source code into which these fragments may be integrated. The -code segments themselves are not derived from any licensed code. - -4.1. PLL Simulation - -In the simulator the hardupdate() fragment is called by ntp_adjtime() as -each update is computed to adjust the system clock phase and frequency. -Note that the time constant is in units of powers of two, so that -multiplies can be done by simple shifts. The phase variable is computed -as the offset multiplied by the time constant. Then, the time since the -last update is computed and clamped to a maximum (for robustness) and to -zero if initializing. The offset is multiplied (sorry about the ugly -multiply) by the result and by the square of the time constant and then -added to the frequency variable. Finally, the frequency variable is -clamped not to exceed the tolerance. Note that all shifts are assumed to -be positive and that a shift of a signed quantity to the right requires -a little dance. - -With the defines given, the maximum time offset is determined by the -size in bits of the long type (32) less the SHIFT_UPDATE scale factor or -18 bits (signed). The scale factor is chosen so that there is no loss of -significance in later steps, which may involve a right shift up to 14 -bits. This results in a maximum offset of about +-130 ms. Since -time_constant must be greater than or equal to zero, the maximum -frequency offset is determined by the SHIFT_KF (20) scale factor, or -about +-130 ppm. In the addition step, the value of offset * mtemp is -represented in 18 + 10 = 28 bits, which will not overflow a long add. -There could be a loss of precision due to the right shift of up to eight -bits, since time_constant is bounded at 6. This results in a net worst- -case frequency error of about 2^-16 us or well down into the oscillator -phase noise. While the time_offset value is assumed checked before -entry, the time_phase variable is an accumulator, so is clamped to the -tolerance on every call. This helps to damp transients before the -oscillator frequency has been determined, as well as to satisfy the -correctness assertions if the time-synchronization protocol comes -unstuck. - -The hardclock() fragment is inserted in the hardware timer interrupt -routine at the point the system clock is to be incremented. Previous to -this fragment the time_update variable has been initialized to the value -computed by the adjtime() system call in the stock Unix kernel, normally -the value of tick plus/minus the tickadj value, which is usually in the -order of 5 microseconds. When the kernel PLL is in use, adjtime() is -not, so the time_update value at this point is the value of tick. This -value, the phase adjustment (time_adj) and the clock phase (time_phase) -are summed and the total tested for overflow of the microsecond. If an -overflow occurs, the microsecond (tick) is incremented or decremented, -depending on the sign of the overflow. - -The second_overflow() fragment is inserted at the point where the -microseconds field of the system time variable is being checked for -overflow. On rollover of the second the maximum error is increased by -the tolerance and the time offset is divided by the phase weight -(SHIFT_KG) and time constant. The time offset is then reduced by the -result and the result is scaled and becomes the value of the phase -adjustment. The phase adjustment is then corrected for the calculated -frequency offset and a fixed offset determined from the fixtick variable -in some kernel implementations. On rollover of the day, the leap-warning -indicator is checked and the apparent time adjusted +-1 s accordingly. -The microtime() routine insures that the reported time is always -monotonically increasing. - -The simulator has been used to check the PLL operation over the design -envelope of +-128 ms in time error and +-100 ppm in frequency error. -This confirms that no overflows occur and that the loop initially -converges in about 15 minutes for timer interrupt rates from 50 Hz to -1024 Hz. The loop has a normal overshoot of about seven percent and a -final convergence time of several hours, depending on the initial time -and frequency error. - -4.2. Leap Seconds - -It does not seem generally useful in the user application interface to -provide additional details private to the kernel and synchronization -protocol, such as stratum, reference identifier, reference timestamp and -so forth. It would in principle be possible for the application to -independently evaluate the quality of time and project into the future -how long this time might be "valid." However, to do that properly would -duplicate the functionality of the synchronization protocol and require -knowledge of many mundane details of the platform architecture, such as -the subnet configuration, reachability status and related variables. -However, for the curious, the ntp_adjtime() system call can be used to -reveal some of these mysteries. - -However, the user application may need to know whether a leap second is -scheduled, since this might affect interval calculations spanning the -event. A leap-warning condition is determined by the synchronization -protocol (if remotely synchronized), by the timecode receiver (if -available), or by the operator (if awake). This condition is set by the -protocol daemon on the day the leap second is to occur (30 June or 31 -December, as announced) by specifying in a ntp_adjtime() system call a -clock status of either TIME_DEL, if a second is to be deleted, or -TIME_INS, if a second is to be inserted. Note that, on all occasions -since the inception of the leap-second scheme, there has never been a -deletion occasion. If the value is TIME_DEL, the kernel adds one second -to the system time immediately following second 23:59:58 and resets the -clock status to TIME_OK. If the value is TIME_INS, the kernel subtracts -one second from the system time immediately following second 23:59:59 -and resets the clock status to TIME_OOP, in effect causing system time -to repeat second 59. Immediately following the repeated second, the -kernel resets the clock status to TIME_OK. - -Depending upon the system call implementation, the reported time during -a leap second may repeat (with the TIME_OOP return code set to advertise -that fact) or be monotonically adjusted until system time "catches up" -to reported time. With the latter scheme the reported time will be -correct before and shortly after the leap second (depending on the -number of microtime() calls during the leap second itself), but freeze -or slowly advance during the leap second itself. However, Most programs -will probably use the ctime() library routine to convert from timeval -(seconds, microseconds) format to tm format (seconds, minutes,...). If -this routine is modified to use the ntp_gettime() system call and -inspect the return code, it could simply report the leap second as -second 60. - -To determine local midnight without fuss, the kernel simply finds the -residue of the time.tv_sec value mod 86,400, but this requires a messy -divide. Probably a better way to do this is to initialize an auxiliary -counter in the settimeofday() routine using an ugly divide and increment -the counter at the same time the time.tv_sec is incremented in the timer -interrupt routine. For future embellishment. - -4.2. Kernel Variables - -The following kernel variables are defined by the new code: - -long time_offset = 0; /* time adjustment (us) */ - - This variable is used by the PLL to adjust the system time in small - increments. It is scaled by (1 << SHIFT_UPDATE) in binary - microseconds. The maximum value that can be represented is about +- - 512 ms and the minimum value or precision is one microsecond. - -long time_constant = 0; /* pll time constant */ - - This variable determines the bandwidth or "stiffness" of the PLL. - It is used as a shift, with the effective value in positive powers - of two. The default value (0) corresponds to a PLL time constant of - about 4 minutes. - -long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ - - This variable represents the maximum frequency error or tolerance - of the particular platform and is a property of the architecture. - It is expressed as a positive number greater than zero in parts- - per-million (ppm). The default MAXFREQ (100) is appropriate for - conventional workstations. - -long time_precision = 1000000 / HZ; /* clock precision (us) */ - - This variable represents the maximum error in reading the system - clock. It is expressed as a positive number greater than zero in - microseconds and is usually based on the number of microseconds - between timer interrupts, 3906 usec for the Ultrix kernel, 976 usec - for the OSF/1 kernel. However, in cases where the time can be - interpolated between timer interrupts with microsecond resolution, - such as in the unmodified SunOS kernel and modified Ultrix and - OSF/1 kernels, the precision is specified as 1 usec. This variable - is computed by the kernel for use by the time-synchronization - daemon, but is otherwise not used by the kernel. - -long time_maxerror; /* maximum error */ - - This variable establishes the maximum error of the indicated time - relative to the primary reference source, in microseconds. For NTP, - the value is determined as the synchronization distance, which is - equal to the root dispersion plus one-half the root delay. It is - increased by a small amount (time_tolerance) each second to reflect - the clock frequency tolerance. This variable is computed by the - time-synchronization daemon and the kernel, but is otherwise not - used by the kernel. - -long time_esterror; /* estimated error */ - - This variable establishes the expected error of the indicated time - relative to the primary reference source, in microseconds. For NTP, - the value is determined as the root dispersion, which represents - the best estimate of the actual error of the system clock based on - its past behavior, together with observations of multiple clocks - within the peer group. This variable is computed by the time- - synchronization daemon and returned in system calls, but is - otherwise not used by the kernel. - -long time_phase = 0; /* phase offset (scaled us) */ -long time_freq = 0; /* frequency offset (scaled ppm) */ -time_adj = 0; /* tick adjust (scaled 1 / HZ) */ - - These variables control the phase increment and the frequency - increment of the system clock at each tick. The time_phase variable - is scaled by (1 << SHIFT_SCALE) (24) in microseconds, giving a - maximum adjustment of about +-128 us/tick and a resolution of about - 60 femtoseconds/tick. The time_freq variable is scaled by (1 << - SHIFT_KF) in parts-per-million (ppm), giving it a maximum value of - over +-2000 ppm and a minimum value (frequency resolution) of about - 1e-5 ppm. The time_adj variable is the actual phase increment in - scaled microseconds to add to time_phase once each tick. It is - computed from time_phase and time_freq once per second. - -long time_reftime = 0; /* time at last adjustment (s) */ - - This variable is the second's portion of the system time on the - last call to adjtime(). It is used to adjust the time_freq variable - as the time since the last update increases. - -int fixtick = 1000000 % HZ; /* amortization factor */ - - In some systems such as the Ultrix and OSF/1 kernels, the local - clock runs at some frequency that does not divide the number of - microseconds in the second. In order that the clock runs at a - precise rate, it is necessary to introduce an amortization factor - into the local timescale, in effect a leap-multimicrosecond. This - is not a new kernel variable, but a new use of an existing kernel - variable. - -4.3. Architecture Constants - -Following is a list of the important architecture constants that -establish the response and stability of the PLL and provide maximum -bounds on behavior in order to satisfy correctness assertions made in -the protocol specification. - -#define HZ 256 /* timer interrupt frequency (Hz) */ -#define SHIFT_HZ 8 /* log2(HZ) */ - - The HZ define (a variable in some kernels) establishes the timer - interrupt frequency, 100 Hz for the SunOS kernel, 256 Hz for the - Ultrix kernel and 1024 Hz for the OSF/1 kernel. The SHIFT_HZ define - expresses the same value as the nearest power of two in order to - avoid hardware multiply operations. These are the only parameters - that need to be changed for different kernel timer interrupt - frequencies. - -#define SHIFT_KG 6 /* shift for phase increment */ -#define SHIFT_KF 16 /* shift for frequency increment */ -#define MAXTC 6 /* maximum time constant (shift) */ - - These defines establish the response and stability characteristics - of the PLL model. The SHIFT_KG and SHIFT_KF defines establish the - damping of the PLL and are chosen by analysis for a slightly - underdamped convergence characteristic. The MAXTC define - establishes the maximum time constant of the PLL. - -#define SHIFT_SCALE (SHIFT_KF + SHIFT_HZ) /* shift for scale factor */ -#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* shift for offset scale - * factor */ -#define SHIFT_USEC 16 /* shift for 1 us in external units */ -#define FINEUSEC (1 << SHIFT_SCALE) /* 1 us in scaled units */ - - The SHIFT_SCALE define establishes the decimal point on the - time_phase variable which serves as a an extension to the low-order - bits of the system clock variable. The SHIFT_UPDATE define - establishes the decimal point of the phase portion of the - ntp_adjtime() update. The SHIFT_USEC define represents 1 us in - external units (shift), while the FINEUSEC define represents 1 us - in internal units. - -#define MAXPHASE 128000 /* max phase error (usec) */ -#define MAXFREQ 100 /* max frequency error (ppm) */ -#define MINSEC 16 /* min interval between updates (s) */ -#define MAXSEC 1200 /* max interval between updates (s) */ - - These defines establish the performance envelope of the PLL, one to - bound the maximum phase error, another to bound the maximum - frequency error and two others to bound the minimum and maximum - time between updates. The intent of these bounds is to force the - PLL to operate within predefined limits in order to conform to the - correctness models assumed by time-synchronization protocols like - NTP and DTSS. An excursion which exceeds these bounds is clamped to - the bound and operation proceeds accordingly. In practice, this can - occur only if something has failed or is operating out of - tolerance, but otherwise the PLL continues to operate in a stable - mode. Note that the MAXPHASE define conforms to the maximum offset - allowed in NTP before the system time is reset (by settimeofday(), - rather than incrementally adjusted (by ntp_adjtime(). - -David L. Mills -Electrical Engineering Department -University of Delaware -Newark, DE 19716 -302 831 8247 fax 302 831 4316 - -1 April 1992 + + This member returns the time_tolerance kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long ybar; /* frequency estimate (scaled ppm) */ + + This member returns the pps_ybar kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long disp; /* dispersion estimate (scaled ppm) */ + + This member returns the pps_disp kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + int shift; /* interval duration (s) (shift) */ + + This member returns the pps_shift kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long calcnt; /* calibration intervals */ + + This member returns the pps_calcnt kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long jitcnt; /* jitter limit exceeded */ + + This member returns the pps_jittcnt kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long discnt; /* dispersion limit exceeded */ + + This member returns the pps_discnt kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + 4.3. Command/Status Codes + + The kernel routines use the system clock status variable + time_status, which records whether the clock is synchronized, + + +Mills [Page 23] + +RFC February 1994 + + waiting for a leap second, etc. The value of this variable is + returned as the result code by both the ntp_gettime() and + ntp_adjtime() system calls. In addition, it can be explicitly read + and written using the ntp_adjtime() system call, but can be + written only in superuser mode. Values presently defined in the + timex.h header file are as follows: + + /* + * Clock command/status codes (timex.status) + */ + #define TIME_OK 0 /* clock synchronized */ + #define TIME_INS 1 /* insert leap second */ + #define TIME_DEL 2 /* delete leap second */ + #define TIME_OOP 3 /* leap second in progress */ + #define TIME_BAD 4 /* kernel clock not synchronized */ + #define TIME_ERR 5 /* external oscillator not + synchronized */ + + A detailed description of these codes as used by the leap-second + state machine is given later in this memorandum. In case of a + negative result code, the kernel has intercepted an invalid + address or (in case of the ntp_adjtime() system call), a superuser + violation. + +5. Kernel Variables + + This section contains a list of kernel variables and a detailed + description of their function, initial value, scaling and limits. + + 5.1. Interface Variables + + The following variables are read and set by the ntp_adjtime() + system call. Additional automatic variables are used as + temporaries as described in the code fragments. + + int time_status = TIME_BAD; + + This variable controls the state machine used to insert or + delete leap seconds and show the status of the timekeeping + system, PPS signal and external oscillator, if configured. + + long time_offset = 0; + + This variable is used by the PLL to adjust the system time in + small increments. It is scaled by (1 << SHIFT_UPDATE) (12) in + microseconds. The maximum value that can be represented is + about +-512 ms and the minimum value or precision is a few + parts in 10^10 s. + + long time_constant = 0; /* pll time constant */ + + This variable determines the bandwidth or "stiffness" of the + PLL. The value is used as a shift between zero and MAXTC (6), + with the effective PLL time constant equal to a multiple of (1 + + +Mills [Page 24] + +RFC February 1994 + + << time_constant) in seconds. For room-temperature quartz + oscillator the recommended default value is 2, which + corresponds to a PLL time constant of about 900 s and a maximum + update interval of about 64 s. The maximum update interval + scales directly with the time constant, so that at the maximum + time constant of 6, the update interval can be as large as 1024 + s. + + Values of time_constant between zero and 2 can be used if quick + convergence is necessary; values between 2 and 6 can be used to + reduce network load, but at a modest cost in accuracy. Values + above 6 are appropriate only if an external oscillator is + present. + + long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ + + This variable represents the maximum frequency error or + tolerance in ppm of the particular CPU clock oscillator and is + a property of the architecture; however, in principle it could + change as result of the presence of external discipline + signals, for instance. It is expressed as a positive number + greater than zero in parts-per-million (ppm). + + The recommended value of MAXFREQ is 200 ppm is appropriate for + room-temperature quartz oscillators used in typical + workstations. However, it can change due to the operating + condition of the PPS signal and/or external oscillator. With + either the PPS signal or external oscillator, the recommended + value for MAXFREQ is 100 ppm. + + long time_precision = 1000000 / HZ; /* clock precision (us) */ + + This variable represents the maximum error in reading the + system clock in microseconds. It is usually based on the number + of microseconds between timer interrupts, 10000 us for the + SunOS kernel, 3906 us for the Ultrix kernel, 976 us for the + OSF/1 kernel. However, in cases where the time can be + interpolated between timer interrupts with microsecond + resolution, such as in the unmodified SunOS kernel and modified + Ultrix and OSF/1 kernels, the precision is specified as 1 us. + In cases where a PPS signal or external oscillator is + available, the precision can depend on the operating condition + of the signal or oscillator. This variable is determined by the + kernel for use by the synchronization daemon, but is otherwise + not used by the kernel. + + long time_maxerror = MAXPHASE; /* maximum error */ + + This variable establishes the maximum error of the indicated + time relative to the primary synchronization source in + microseconds. For NTP, the value is initialized by a + ntp_adjtime() call to the synchronization distance, which is + equal to the root dispersion plus one-half the root delay. It + is increased by a small amount (time_tolerance) each second to + + +Mills [Page 25] + +RFC February 1994 + + reflect the clock frequency tolerance. This variable is + computed by the synchronization daemon and the kernel, but is + otherwise not used by the kernel. + + long time_esterror = MAXPHASE; /* estimated error */ + + This variable establishes the expected error of the indicated + time relative to the primary synchronization source in + microseconds. For NTP, the value is determined as the root + dispersion, which represents the best estimate of the actual + error of the system clock based on its past behavior, together + with observations of multiple clocks within the peer group. + This variable is computed by the synchronization daemon and + returned in system calls, but is otherwise not used by the + kernel. + + 5.2. Phase-Lock Loop Variables + + The following variables establish the state of the PLL and the + residual time and frequency offset of the system clock. Additional + automatic variables are used as temporaries as described in the + code fragments. + + long time_phase = 0; /* phase offset (scaled us) */ + + The time_phase variable represents the phase of the kernel time + variable at each tick of the clock. This variable is scaled by + (1 << SHIFT_SCALE) (23) in microseconds, giving a maximum + adjustment of about +-256 us/tick and a resolution less than + one part in 10^12. + + long time_offset = 0; /* time offset (scaled us) */ + + The time_offset variable represents the time offset of the CPU + clock oscillator. It is recalculated as each update to the + system clock is received via the hardupdate() routine and at + each second in the seconds_overflow routine. This variable is + scaled by (1 << SHIFT_UPDATE) (12) in microseconds, giving a + maximum adjustment of about +-512 ms and a resolution of a few + parts in 10^10 s. + + long time_freq = 0; /* frequency offset (scaled ppm) */ + + The time_freq variable represents the frequency offset of the + CPU clock oscillator. It is recalculated as each update to the + system clock is received via the hardupdate() routine. It can + also be set via ntp_adjtime() from a value stored in a file + when the synchronization daemon is first started. It can be + retrieved via ntp_adjtime() and written to the file about once + per hour by the daemon. The time_freq variable is scaled by (1 + << SHIFT_KF) (16) ppm, giving it a maximum value well in excess + of the limit of +-256 ppm imposed by other constraints. The + precision of this representation (frequency resolution) is + + + +Mills [Page 26] + +RFC February 1994 + + parts in 10^11, which is adequate for all but the best external + oscillators. + + time_adj = 0; /* tick adjust (scaled 1 / HZ) */ + + The time_adj variable is the adjustment added to the value of + tick at each timer interrupt. It is computed once each second + from the time_offset, time_freq and, if the PPS signal is + present, the ps_ybar variable once each second. + + long time_reftime = 0; /* time at last adjustment (s) */ + + This variable is the seconds portion of the system time on the + last update received by the hardupdate() routine. It is used to + compute the time_freq variable as the time since the last + update increases. + + int fixtick = 1000000 % HZ; /* amortization factor */ + + In the Ultrix and OSF/1 kernels, the interval between timer + interrupts does not evenly divide the number of microseconds in + the second. In order that the clock runs at a precise rate, it + is necessary to introduce an amortization factor into the local + timescale. In the original Unix code, the value of fixtick is + amortized once each second, introducing an additional source of + jitter; in the new model the value is amortized at each tick of + the system clock, reducing the jitter by the reciprocal of the + clock oscillator frequency. This is not a new kernel variable, + but a new use of an existing kernel variable. + + 5.3. Pulse-per-second (PPS) Frequency-Lock Loop Variables + + The following variables are used only if a pulse-per-second (PPS) + signal is available and connected via a modem-control lead, such + as produced by the optional ppsclock feature incorporated in the + serial port driver. They establish the design parameters of the + PPS frequency-lock loop used to discipline the CPU clock + oscillator to an external PPS signal. Additional automatic + variables are used as temporaries as described in the code + fragments. + + long pps_usec; /* microseconds at last pps */ + + The pps_usec variable is latched from a high resolution counter + or external oscillator at each PPS interrupt. In determining + this value, only the hardware counter contents are used, not + the contents plus the kernel time variable, as returned by the + microtime() routine. + + long pps_ybar = 0; /* pps frequency offset estimate */ + + The pps_ybar variable is the average CPU clock oscillator + frequency offset relative to the PPS disciplining signal. It is + scaled in the same units as the time_freq variable. + + +Mills [Page 27] + +RFC February 1994 + + pps_disp = MAXFREQ; /* dispersion estimate (scaled ppm) */ + + The pps_disp variable represents the average sample dispersion + measured over the last three samples. It is scaled in the same + units as the time_freq variable. + + pps_dispmax = MAXFREQ / 2; /* dispersion threshold */ + + The pps_dispmax variable is used as a dispersion threshold. If + pps_disp is less than this threshold, the median sample is used + to update the pps_ybar estimate; if not, the sample is + discarded. + + pps_dispinc = MAXFREQ >> (PPS_SHIFT + 4); /* pps dispersion + increment/sec */ + + The pps_dispinc variable is the increment to add to pps_disp + once each second. It is computed such that, if no PPS samples + have arrived for several calibration intervals, the value of + pps_disp will exceed the pps_dispmax threshold and raise an + alarm. + + int pps_mf[] = {0, 0, 0}; /* pps median filter */ + + The pps-mf[] array is used as a median filter to detect and + discard jitter in the PPS signal. + + int pps_count = 0; /* pps calibrate interval counter */ + + The pps_count variable measures the length of the calibration + interval used to calculate the frequency. It normally counts + from zero to the value 1 << pps_shift. + + pps_shift = PPS_SHIFT; /* interval duration (s) (shift) */ + + The pps_shift variable determines the duration of the + calibration interval, 1 << pps_shift s. + + pps_intcnt = 0; /* intervals at current duration */ + + The pps_intcnt variable counts the number of calibration + intervals at the current interval duration. It is reset to zero + after four intervals and when the interval duration is changed. + + 5.4. External Oscillator Variables + + The following variables are used only if an external oscillator + (HIGHBALL or TPRO) is present. Additional automatic variables are + used as temporaries as described in the code fragments. + + + + + + + +Mills [Page 28] + +RFC February 1994 + + int clock_count = 0; /* CPU clock counter */ + + The clock_count variable counts the seconds between adjustments + to the kernel time variable to discipline it to the external + clock. + + struct timeval clock_offset; /* HIGHBALL clock offset */ + + The clock_offset variable defines the offset between system + time and the HIGHBALL counters. + + long clock_cpu = 0; /* CPU clock adjust */ + + The clock_cpu variable contains the offset between the system + clock and the HIGHBALL clock for use in disciplining the kernel + time variable. + +6. Architecture Constants + + Following is a list of the important architecture constants that + establish the response and stability of the PLL and provide maximum + bounds on behavior in order to satisfy correctness assertions made in + the protocol specification. Additional definitions are given in the + timex.h header file. + + 6.1. Phase-lock loop (PLL) definitions + + The following defines establish the performance envelope of the + PLL. They establish the maximum phase error (MAXPHASE), maximum + frequency error (MAXFREQ), minimum interval between updates + (MINSEC) and maximum interval between updates (MAXSEC). The intent + of these bounds is to force the PLL to operate within predefined + limits in order to satisfy correctness assertions of the + synchronization protocol. An excursion which exceeds these bounds + is clamped to the bound and operation proceeds normally. In + practice, this can occur only if something has failed or is + operating out of tolerance, but otherwise the PLL continues to + operate in a stable mode. + + MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms), + as defined in the NTP specification. CLOCK.MAX establishes the + maximum time offset allowed before the system time is reset, + rather than incrementally adjusted. Here, the maximum offset is + clamped to MAXPHASE only in order to prevent overflow errors due + to defective programming. + + MAXFREQ reflects the manufacturing frequency tolerance of the CPU + oscillator plus the maximum slew rate allowed by the protocol. It + should be set to at least the intrinsic frequency tolerance of the + oscillator plus 100 ppm for vernier frequency adjustments. If the + kernel frequency discipline code is installed (PPS_SYNC), the CPU + oscillator frequency is disciplined to an external source, + presumably with negligible frequency error. + + + +Mills [Page 29] + +RFC February 1994 + + #define MAXPHASE 512000 /* max phase error (us) */ + #ifdef PPS_SYNC + #define MAXFREQ 100 /* max frequency error (ppm) */ + #else + #define MAXFREQ 200 /* max frequency error (ppm) */ + #endif /* PPS_SYNC */ + #define MINSEC 16 /* min interval between updates (s) + */ + #define MAXSEC 1200 /* max interval between updates (s) + */ + + 6.2. Pulse-per-second (PPS) Frequency-lock Loop (FLL) Definitions + + The following defines and declarations are used only if a pulse- + per-second (PPS) signal is available and connected via a modem- + control lead, such as produced by the optional ppsclock feature + incorporated in the serial port driver. They establish the design + parameters of the frequency-lock loop (FLL) used to discipline the + CPU clock oscillator to the PPS oscillator. + + PPS_AVG is the averaging constant used to update the FLL from + frequency samples measured for each calibration interval. + PPS_SHIFT and PPS_SHIFTMAX are the minimum and maximem, + respectively, of the calibration interval represented as a power + of two. The PPS_DISPINC is the initial increment to pps_disp at + each second. + + #define PPS_AVG 2 /* pps averaging constant (shift) */ + #define PPS_SHIFT 2 /* min interval duration (s) (shift) + */ + #define PPS_SHIFTMAX 6 /* max interval duration (s) (shift) + */ + #define PPS_DISPINC 0 /* dispersion increment (us/s) */ + + 6.3. External Oscillator Definitions + + The following definitions and declarations are used only if an + external oscillator (HIGHBALL or TPRO) is configured on the + system. + + #define CLOCK_INTERVAL 30 /* CPU clock update interval (s) */ + +7. References + + [LEV89] Levine, J., M. Weiss, D.D. Davis, D.W. Allan, and D.B. + Sullivan. The NIST automated computer time service. J. Research + National Institute of Standards and Technology 94, 5 (September- + October 1989), 311-321. + + [MIL91] Mills, D.L. Internet time synchronization: the Network Time + Protocol. IEEE Trans. Communications COM-39, 10 (October 1991), 1482- + 1493. Also in: Yang, Z., and T.A. Marsland (Eds.). Global States and + Time in Distributed Systems, IEEE Press, Los Alamitos, CA, 91-102. + + + +Mills [Page 30] + +RFC February 1994 + + [MIL92a] Mills, D.L. Network Time Protocol (Version 3) specification, + implementation and analysis. DARPA Network Working Group Report RFC- + 1305, University of Delaware, March 1992, 113 pp. + + [MIL92b] Mills, D.L. Modelling and analysis of computer network + clocks. Electrical Engineering Department Report 92-5-2, University + of Delaware, May 1992, 29 pp. + + [MIL92c] Mills, D.L. Simple Network Time Protocol (SNTP). DARPA + Network Working Group Report RFC-1361, University of Delaware, August + 1992, 10 pp. + + [MIL93] Mills, D.L. Precision synchronizatin of computer network + clocks. Electrical Engineering Department Report 93-11-1, University + of Delaware, November 1993, 66 pp. + +Author's Address + + David L. Mills + Electrical Engineering Department + University of Delaware + Newark, DE 19716 + + Phone: (302) 831-8247 + + EMail: mills@udel.edu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mills [Page 31] diff --git a/usr.sbin/xntpd/doc/notes.txt b/usr.sbin/xntpd/doc/notes.txt index 5ea2b33..1dd59f2 100644 --- a/usr.sbin/xntpd/doc/notes.txt +++ b/usr.sbin/xntpd/doc/notes.txt @@ -785,7 +785,7 @@ configuration functionality (though the only thing you can currently do with mode-6 messages is set the leap-second warning bits) and the ntpq program provides generic support for the latter. The leap bits that can be set in the leap_warning variable (up to one month ahead) and in the -leap_indication variable have a slighly different encoding than the +leap_indication variable have a slightly different encoding than the usual interpretation: Value Action diff --git a/usr.sbin/xntpd/doc/ntpdate.8 b/usr.sbin/xntpd/doc/ntpdate.8 index 05be737..e738cab 100644 --- a/usr.sbin/xntpd/doc/ntpdate.8 +++ b/usr.sbin/xntpd/doc/ntpdate.8 @@ -20,13 +20,13 @@ ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. -''' Bell System Logo is used as a dummy character. +''' Greek uppercase omega is used as a dummy character. ''' -.tr \(bs-|\(bv\*(Tr +.tr \(*W-|\(bv\*(Tr .ie n \{\ -.ds -- \(bs- -.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch -.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch +.ds -- \(*W- +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' diff --git a/usr.sbin/xntpd/doc/ntpq.8 b/usr.sbin/xntpd/doc/ntpq.8 index 81aca09..1ddb829 100644 --- a/usr.sbin/xntpd/doc/ntpq.8 +++ b/usr.sbin/xntpd/doc/ntpq.8 @@ -20,13 +20,13 @@ ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. -''' Bell System Logo is used as a dummy character. +''' Greek uppercase omega is used as a dummy character. ''' -.tr \(bs-|\(bv\*(Tr +.tr \(*W-|\(bv\*(Tr .ie n \{\ -.ds -- \(bs- -.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch -.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch +.ds -- \(*W- +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' diff --git a/usr.sbin/xntpd/doc/ntptrace.8 b/usr.sbin/xntpd/doc/ntptrace.8 index fb93ebb..a79a679 100644 --- a/usr.sbin/xntpd/doc/ntptrace.8 +++ b/usr.sbin/xntpd/doc/ntptrace.8 @@ -20,13 +20,13 @@ ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. -''' Bell System Logo is used as a dummy character. +''' Greek uppercase omega is used as a dummy character. ''' -.tr \(bs-|\(bv\*(Tr +.tr \(*W-|\(bv\*(Tr .ie n \{\ -.ds -- \(bs- -.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch -.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch +.ds -- \(*W- +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' diff --git a/usr.sbin/xntpd/doc/tickadj.8 b/usr.sbin/xntpd/doc/tickadj.8 index 5fce088..8726288 100644 --- a/usr.sbin/xntpd/doc/tickadj.8 +++ b/usr.sbin/xntpd/doc/tickadj.8 @@ -20,13 +20,13 @@ ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. -''' Bell System Logo is used as a dummy character. +''' Greek uppercase omega is used as a dummy character. ''' -.tr \(bs-|\(bv\*(Tr +.tr \(*W-|\(bv\*(Tr .ie n \{\ -.ds -- \(bs- -.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch -.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch +.ds -- \(*W- +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' diff --git a/usr.sbin/xntpd/doc/xntpd.8 b/usr.sbin/xntpd/doc/xntpd.8 index ff6386e..c141df9 100644 --- a/usr.sbin/xntpd/doc/xntpd.8 +++ b/usr.sbin/xntpd/doc/xntpd.8 @@ -20,13 +20,13 @@ ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. -''' Bell System Logo is used as a dummy character. +''' Greek uppercase omega is used as a dummy character. ''' -.tr \(bs-|\(bv\*(Tr +.tr \(*W-|\(bv\*(Tr .ie n \{\ -.ds -- \(bs- -.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch -.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch +.ds -- \(*W- +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' @@ -374,7 +374,8 @@ facility will be disabled. Certain changes can be made to the .I xntpd server via mode 6 control messages, in particular the setting of -leap second indications in a server with a radio clock. The +leap second indications in a server with a radio clock. +The .B controlkey statement specifies an encription key number to be used for authenticating such messages. Omitting this statement will cause control messages @@ -446,6 +447,22 @@ useful as future synchronization partners. .Ip notrust 10 Treat these hosts normally in other respects, but never use them as synchronization sources. +.Ip limited 10 +These hosts are subject to limitation of number of clients from the +same net. Net in this context refers to the IP notion of net (class A, +class B, class C, etc.). Only the first \*(L"client_limit\*(R" hosts +that have shown up at the server and that have been active during the +last \*(L"client_limit_period\*(R" seconds are accepted. Requests from +other clients from the same net are rejected. Only time request +packets are taken into account. \*(L"Private\*(R", \*(L"control\*(R", +and \*(L"broadcast\*(R" packets are not subject to client limitation +and therefore are not contributing to client count. History of clients +is kept using the monitoring capability of +.IR xntpd . +Thus, monitoring is active as long as there is a restriction entry +with the \*(L"limited\*(R" flag. The default value for +\*(L"client_limit\*(R" is 3. The default value for +\*(L"client_limit_period\*(R" is 3600 seconds. .Ip ntpport 10 This is actually a match algorithm modifier, rather than a restriction flag. Its presence causes the restriction entry to be matched only if @@ -469,6 +486,21 @@ broken remote time servers from affecting your own, it should not be considered an alternative to the standard NTP authentication facility. Source address based restrictions are easily circumvented by a determined cracker. .PP +.B clientlimit +.I limit +.PP +Sets \*(L"client_limit\*(R" to \*(L"limit\*(R", allows configuration +of client limitation policy. This variable defines the number of +clients from the same network that are allowed to use the server. +.PP +.B clientperiod +.I period +.PP +Sets \*(L"client_limit_period\*(R", allows configuration of client +limitation policy. This variable specifies the number +of seconds after which a client is considered inactive and thus no +longer is counted for client limit restriction. +.PP .B trap .I host_address [ @@ -1370,6 +1402,31 @@ If flag2 is set, then leaphold is set. If flag3 is set, then the sample information is dumped. If flag4 is set, then the input data is smoothed, and all data points are used. +.PP +.SH VARIABLES +Most variables used by the NTP protocol can be examined with the xntpdc +(mode 7 messages) and the ntpq (mode 6 messages). Currently very few variables +can be modified via mode 6 messages. These variables are either created with the +.I setvar +directive or the leap warning variables. The leap warning bits that can be +set in the +.B leapwarning +variable (up to one month ahead). Both, the +.B leapwarning and in the +.B leapindication +variable, have a slightly different encoding than the usual +.B leap +bits interpretation: +.P +.Ip 00 8 +The daemon passes the leap bits of its synchronisation source (usual mode of +operation). +.Ip 01/10 8 +A leap second is added/deleted (operator forced leap second). +.Ip 11 8 +Leap information from the sychronisation source is ignored (thus LEAP_NOWARNING +is passed on). +.PP .SH FILES .Ip /etc/ntp.conf 20 the default name of the configuration file diff --git a/usr.sbin/xntpd/doc/xntpdc.8 b/usr.sbin/xntpd/doc/xntpdc.8 index 20209b5..65450e3 100644 --- a/usr.sbin/xntpd/doc/xntpdc.8 +++ b/usr.sbin/xntpd/doc/xntpdc.8 @@ -20,13 +20,13 @@ ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. -''' Bell System Logo is used as a dummy character. +''' Greek uppercase omega is used as a dummy character. ''' -.tr \(bs-|\(bv\*(Tr +.tr \(*W-|\(bv\*(Tr .ie n \{\ -.ds -- \(bs- -.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch -.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch +.ds -- \(*W- +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' @@ -539,6 +539,17 @@ requests) from the source. Time service is not affected. Ignore all NTP mode 7 packets which attempt to modify the state of the server (i.e. run time reconfiguration). Queries which return information are permitted. +.Ip notrap 10 +Decline to provide mode 6 control message trap service to matching +hosts. The trap service is a subsystem of the mode 6 control message +protocol which is intended for use by remote event logging programs. +.Ip lowpriotrap 10 +Declare traps set by matching hosts to be low priority. The number +of traps a server can maintain is limited (the current limit is 3). +Traps are usually assigned on a first come, first served basis, with +later trap requestors being denied service. This flag modifies the +assignment algorithm by allowing low priority traps to be overridden +by later requests for normal priority traps. .Ip noserve 10 Ignore NTP packets whose mode is other than 7. In effect, time service is denied, though queries may still be permitted. @@ -549,6 +560,23 @@ useful as future synchronization partners. .Ip notrust 10 Treat these hosts normally in other respects, but never use them as synchronization sources. +.Ip limited 10 +These hosts are subject to limitation of number of clients from the +same net. Net in this context refers to the IP notion of net (class A, +class B, class C, etc.). Only the first \*(L"client_limit\*(R" hosts +that have shown up at the server and that have been active during the +last \*(L"client_limit_period\*(R" seconds are accepted. Requests from +other clients from the same net are rejected. Only time request +packets are taken into account. \*(L"Private\*(R", \*(L"control\*(R", +and \*(L"broadcast\*(R" packets are not subject to client limitation +and therefore are not contributing to client count. History of clients +is kept using the monitoring capability of +.IR xntpd. +Thus, monitoring is active as long as there is a restriction entry +with the \*(L"limited\*(R" flag. The default value for +\*(L"client_limit\*(R" is 3. The default value for +\*(L"client_limit_period\*(R" is 3600 seconds. Currently both +variables are not runtime configurable. .Ip ntpport 10 This is actually a match algorithm modifier, rather than a restriction flag. Its presence causes the restriction entry to be matched only if diff --git a/usr.sbin/xntpd/hints/hpux b/usr.sbin/xntpd/hints/hpux index f0b3271..f0e231d 100644 --- a/usr.sbin/xntpd/hints/hpux +++ b/usr.sbin/xntpd/hints/hpux @@ -1,59 +1,63 @@ +Last update: Sun Mar 13 15:05:31 PST 1994 + This file hopefully describes the whatever and however of how to get xntp -running on hpux 8.0 and later s300, s700, and s800. +running on hpux 7.0 and later s300. s400, s700, and s800. First off, all the standard disclaimers hold here ... HP doesn't have anthing to do with this stuff. I fool with it in my spare time because we use it and because I like to. We just happen to have a lot of HP machines around here :-) -Xntp has been in use here for several months and has a fair amount of mileage +Xntpd has been in use here for several years and has a fair amount of mileage on various HP platforms within the company. I can't really guarantee bug fixes but I'd certainly like to hear about bugs and I won't hestitate to look at any fixes sent to me. -Now lets talk OS. If you don't have 8.0 or later, pretty much hang it up now. -This stuff has run here on 8.0 s300, s700, and s800. Its possible that it -runs on 7.0 but I have not tried v3 code on 7.0 at all. +Now lets talk OS. If you don't have 7.0 or later, pretty much hang it up now. +This stuff has run here on pretty much everything from 8.0 upward on s300, +s700, and s800. It is known to run on 7.0 s300/s400 but all reports are +from the field and not my personal experience. -[Note that recent reports state that this release does in fact run on HP -300 and 400 boxes, which run 7.0 - Ed.] +If you are lucky enough to have a s300 or s400 with 9.03, then you no longer +have to worry about adjtimed as HP-UX now has adjtime(2). The rest of you +will have to wait on 10.0 which will have adjtime(2) and a supported though +a bit older version of xntpd. -Next, let me explain a bit about how this stuff works on HP-UX since we don't +Next, let me explain a bit about how this stuff works on HP-UX's that do not have adjtime(2). The directory adjtime contains libadjtime.a and the adjtimed daemon. Instead of the adjtime(2) system call, we use a library routine to talk to adjtimed thru message queues. Adjtimed munges into /dev/kmem and causes the clock to skew properly as needed. PLEASE NOTE that the adjtime code provided here is NOT a general replacement for adjtime(2) ... use of -this adjtime(3)/adjtimed(8) other than here may yield very odd results. +this adjtime(3)/adjtimed(8) other than with xntpd may yield very odd results. What to do to get this stuff running ? - * cd .. - * Say "make makeconfig" + * If you are running an OS less than 10.0 or do not have a s300/s400 + with 9.03 or better + -> cd machines + -> vi hpux + -> (change -DSYS_HPUX=? to match whatever you are running [7,8,9]) + -> cd .. + + * Say "make makeconfig" - * cd .. - * Say "make", sit back for a few minutes. + * Say "make", sit back for a few minutes. * cd authstuff * Say "./authcert < certdata" and check the output. Every line should end with "OK" ... if not, we got trouble. * Now try "./authspeed auth.samplekeys". What we want to remember here is the "authentication delay in CPU time" + * cd .. - * cd .. - * Now we need to install this stuff ... make install will not work - unless you have replaced the SYSV install command with a BSD - compatible version. So ... the simplest thing to do is run - make -n install and do manually what it would have done. + * Say "make install" * I'd suggest reading the xntp docs about now :-) ... seriously !! - * Check out the docs and the stuff in xntp/conf and build a config - file ... put it in /usr/local/etc/xntp.conf (or where ever you - defined the config file to be in Config). One thing we have - added to this version of xntpd is a way to select config files - if you are sharing /usr/local thru NFS or whatever. If the - file /usr/local/etc/xntp.conf happens to be a directory, the files - in that directory are searched until a match is found. The rules - for a match are: + * One thing I have added to this version of xntpd is a way to select + config files if you are sharing /usr/local thru NFS or whatever. + If the file /usr/local/etc/xntp.conf happens to be a directory, the + files in that directory are searched until a match is found. The + rules for a match are: 1. Our hostname 2. default. (as in default.375 or default.850) @@ -73,4 +77,16 @@ Possible problems ? * On some 320's and 835's we have had to run adjtimed with "-p 45" or so to get rid of syslog messages about "last adjust did not finish". + * At 9.0, there is a problem with DIAGMON (patch available from the + response center) which causes it to delete the message queue that + adjtimed/xntpd use to communicate. (see next note for result) + + * Xntpd has been known to get really ticked off when adjtime() fails + which is usually only while running the emulation code on HP-UX. + When it gets mad, it usually jumps the clock into never never land. + Possible reasons for this are adjtimed being killed or just never + started or adjtimed being completely swapped out on a really busy + machine (newer adjtimed try to lock themselves in memory to prevent + this one). + Anything else ... just drop me a line at ken@sdd.hp.com diff --git a/usr.sbin/xntpd/hints/linux b/usr.sbin/xntpd/hints/linux index f243bd2..0efc12b 100644 --- a/usr.sbin/xntpd/hints/linux +++ b/usr.sbin/xntpd/hints/linux @@ -1,29 +1,9 @@ -Requirements: kernel 0.99.14 or newer, libc 4.5 or newer +Requirements: kernel 0.99.14y or newer, libc 4.5.21 or newer ------------ - With this configuration, xntp should build an run right out of the -box (see generic hints for how-to), with one big limitation: tickadj doesn't -work yet. This is especially painful since PCs are usually equipped with -untuned, badly-drifting quartzes, values up to 200 ppm being no exception. -Because the loop filter algorithms are limited to compensating no more than -100 ppm, currently only one workaround is possible: - Compile your own kernel and adjust linux/include/linux/timex.h, -line 67 (in pl14): - -#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ - - Since this is surely not true for your hardware, adjust the hundreds -to match your quartz. Adding 100 compensates for a drift of -83.8 ppm -(1/CLOCK_TICK_RATE). The number gets rounded to the nearest 100 so don't -bother to tune any finer. - -Fixing tickadj is already in my work queue, so the previous comment should be -obsolete RSN. If you really need to run xntp on any earlier versions of the -kernel or libc, or have any other question not covered in the READMEs / hint -files (sorry, necessary comment in the Linux community ;-) feel free to ask -me (duwe@informatik.uni-erlangen.de) - -xntp3.3b of 1993/12/06 : remember to change #define ntp_adjtime adjtimex to -__adjtimex in the Linux section (line 316). This is hopefully done if you -(don't :-) see this paragraph in the xntp3.x distribution. + With this configuration, xntp should build an run right out of the box +(see generic hints for how-to). If you really need to run xntp on any earlier +versions of the kernel or libc, or have any other question not covered in the +READMEs / hint files (sorry, necessary comment in the Linux community ;-) feel +free to ask me (duwe@informatik.uni-erlangen.de) diff --git a/usr.sbin/xntpd/hints/solaris b/usr.sbin/xntpd/hints/solaris index 038d65f..1d0e47f 100644 --- a/usr.sbin/xntpd/hints/solaris +++ b/usr.sbin/xntpd/hints/solaris @@ -2,8 +2,7 @@ If you are running Solaris 2.0, you should upgrade to a later version of Solaris immediately. - If you are running Solaris 2.1, you should use Config.solaris2.1 - If you are running Solaris 2.2 or later, you should use Config.solaris2.2 + If you are running Solaris 2.1 or later, all should be fine (i hope) Solaris 2.1 contains fairly traditional clock code, with tick and tickadj. Solaris 2.2 and later contains completely re-written clock code to provide diff --git a/usr.sbin/xntpd/include/ntp.h b/usr.sbin/xntpd/include/ntp.h index 310353f..b3fd0ce 100644 --- a/usr.sbin/xntpd/include/ntp.h +++ b/usr.sbin/xntpd/include/ntp.h @@ -346,7 +346,7 @@ struct peer { */ #define REFCLK_NONE 0 /* unknown or missing */ #define REFCLK_LOCALCLOCK 1 /* external (e.g., ACTS) */ -#define REFCLK_WWV_HEATH 2 /* Heath GC-1000 WWV/H */ +#define REFCLK_GPS_TRAK 2 /* TRAK 8810 GPS Receiver */ #define REFCLK_WWV_PST 3 /* PST/Traconex 1020 WWV/H */ #define REFCLK_WWVB_SPECTRACOM 4 /* Spectracom 8170/Netclock WWVB */ #define REFCLK_GOES_TRUETIME 5 /* TrueTime 468-DC GOES */ @@ -612,6 +612,9 @@ struct mon_data { struct mon_data *hash_prev; /* previous structure in hash list */ struct mon_data *mru_next; /* next structure in MRU list */ struct mon_data *mru_prev; /* previous structure in MRU list */ + struct mon_data *fifo_next; /* next structure in FIFO list */ + struct mon_data *fifo_prev; /* previous structure in FIFO list */ + U_LONG lastdrop; /* last time dropped due to RES_LIMIT*/ U_LONG lasttime; /* last time data updated */ U_LONG firsttime; /* time structure initialized */ U_LONG count; /* count we have seen */ @@ -621,7 +624,12 @@ struct mon_data { u_char version; /* version of incoming packet */ }; - +/* + * Values used with mon_enabled to indicate reason for enabling monitoring + */ +#define MON_OFF 0x00 /* no monitoring */ +#define MON_ON 0x01 /* monitoring explicitly enabled */ +#define MON_RES 0x02 /* implicit monitoring for RES_LIMITED */ /* * Structure used for restrictlist entries */ @@ -645,10 +653,11 @@ struct restrictlist { #define RES_NOPEER 0x20 /* don't allocate memory resources */ #define RES_NOTRAP 0x40 /* don't allow him to set traps */ #define RES_LPTRAP 0x80 /* traps set by him are low priority */ +#define RES_LIMITED 0x100 /* limit per net number of clients */ #define RES_ALLFLAGS \ (RES_IGNORE|RES_DONTSERVE|RES_DONTTRUST|RES_NOQUERY\ - |RES_NOMODIFY|RES_NOPEER|RES_NOTRAP|RES_LPTRAP) + |RES_NOMODIFY|RES_NOPEER|RES_NOTRAP|RES_LPTRAP|RES_LIMITED) /* * Match flags diff --git a/usr.sbin/xntpd/include/ntp_machine.h b/usr.sbin/xntpd/include/ntp_machine.h index 1c3983e..4c7c3ea 100644 --- a/usr.sbin/xntpd/include/ntp_machine.h +++ b/usr.sbin/xntpd/include/ntp_machine.h @@ -44,11 +44,16 @@ Signaled IO - Signled IO defines. WHICH TERMINAL MODEL TO USE - I would assume HAVE_TERMIOS if NTP_POSIX_SOURCE was set but can't. The posix tty driver is too restrictive on most systems. - It defined if you define STREAMS. + It is defined if you define STREAMS. + We do not put these defines in the ntp_machine.h as some systems + offer multiple interfaces and refclock configuration likes to + peek into the configuration defines for tty model restrictions. + Thus all tty definitions should be in the files in the machines directory. + + HAVE_TERMIOS - Use POSIX termios.h HAVE_SYSV_TTYS - Use SYSV termio.h HAVE_BSD_TTYS - Use BSD stty.h - HAVE_TERMIOS - Use POSIX termios.h THIS MAKES PORTS TO NEW SYSTEMS EASY - You only have to wory about kernel mucking. @@ -274,7 +279,6 @@ in this file. #ifndef STR_SYSTEM #define STR_SYSTEM "UNIX/Ultrix" #endif -#define HAVE_TERMIOS #endif /* @@ -297,9 +301,9 @@ in this file. #define FORCE_NTPDATE_STEP #define RETSIGTYPE void #define HAVE_ATT_SETPGRP -#define HAVE_BSD_TTYS #define LOG_NTP LOG_LOCAL1 #define HAVE_SIGNALED_IO +#define NTP_NEED_BOPS #ifndef STR_SYSTEM #define STR_SYSTEM "UNIX/AUX" #endif @@ -309,6 +313,7 @@ in this file. * Next */ #if defined(SYS_NEXT) +#define RETSIGTYPE void #define DOSYNCTODR #define HAVE_READKMEM #define HAVE_BSD_NICE @@ -329,8 +334,12 @@ in this file. #define setlinebuf(f) setvbuf(f, NULL, _IOLBF, 0) #define NO_SIGNED_CHAR_DECL #define LOCK_PROCESS -#define HAVE_NO_NICE /* HPUX uses rtprio instead */ #define RETSIGTYPE void +#if (SYS_HPUX < 9) +#define HAVE_NO_NICE /* HPUX uses rtprio instead */ +#else +#define HAVE_BSD_NICE /* new at 9.X */ +#endif #if (SYS_HPUX < 10) #define NOKMEM #else @@ -352,8 +361,6 @@ in this file. #ifndef STR_SYSTEM #define STR_SYSTEM "UNIX/BSDI" #endif -#define HAVE_BSD_TTYS -#define HAVE_TERMIOS #endif /* @@ -441,9 +448,6 @@ in this file. */ #if defined(SYS_PTX) #define NO_SIGNED_CHAR_DECL -#ifndef HAVE_SYSV_TTYS -#define HAVE_SYSV_TTYS -#endif #define STREAMS_TLI #define HAVE_ATT_SETPGRP #define HAVE_SIGNALED_IO @@ -458,6 +462,7 @@ in this file. #define HAVE_READKMEM #define UDP_WILDCARD_DELIVERY #define NTP_POSIX_SOURCE +#define memmove(x, y, z) memcpy(x, y, z) struct timezone { int __0; }; /* unused placebo */ /* * no comment !@! @@ -527,7 +532,6 @@ typedef unsigned long u_long; #define HAVE_BSD_NICE #define NOKMEM #define HAVE_SIGNALED_IO -#define HAVE_BSD_TTYS #define NTP_SYSCALLS_STD #define USE_PROTOTYPES #define UDP_WILDCARD_DELIVERY @@ -565,6 +569,20 @@ typedef unsigned long u_long; ERROR You_must_define_one_of_the_HAVE_xx_NICE_defines #endif +/* + * use only one tty model - no use in initialising + * a tty in three ways + * HAVE_TERMIOS is preferred over HAVE_SYSV_TTYS over HAVE_BSD_TTYS + */ +#ifdef HAVE_TERMIOS +#undef HAVE_BSD_TTYS +#undef HAVE_SYSV_TTYS +#endif + +#ifdef HAVE_SYSV_TTYS +#undef HAVE_BSD_TTYS +#endif + #if !defined(HAVE_SYSV_TTYS) \ && !defined(HAVE_BSD_TTYS) \ && !defined(HAVE_TERMIOS) diff --git a/usr.sbin/xntpd/include/ntp_request.h b/usr.sbin/xntpd/include/ntp_request.h index e94cb45..b791c18 100644 --- a/usr.sbin/xntpd/include/ntp_request.h +++ b/usr.sbin/xntpd/include/ntp_request.h @@ -429,6 +429,24 @@ struct info_sys_stats { U_LONG processed; /* packets processed */ U_LONG badauth; /* packets dropped because of authorization */ U_LONG wanderhold; + U_LONG limitrejected; /* rejected because of client limitation */ +}; + + +/* + * System stats - old version + */ +struct old_info_sys_stats { + U_LONG timeup; /* time we have been up and running */ + U_LONG timereset; /* time since these were last cleared */ + U_LONG badstratum; /* packets claiming an invalid stratum */ + U_LONG oldversionpkt; /* old version packets received */ + U_LONG newversionpkt; /* new version packets received */ + U_LONG unknownversion; /* don't know version packets */ + U_LONG badlength; /* packets with bad length */ + U_LONG processed; /* packets processed */ + U_LONG badauth; /* packets dropped because of authorization */ + U_LONG wanderhold; }; @@ -546,6 +564,7 @@ struct conf_restrict { struct info_monitor { U_LONG lasttime; /* last packet from this host */ U_LONG firsttime; /* first time we received a packet */ + U_LONG lastdrop; /* last time we rejected a packet due to client limitation policy */ U_LONG count; /* count of packets received */ U_LONG addr; /* host address */ u_short port; /* port number of last reception */ @@ -553,6 +572,18 @@ struct info_monitor { u_char version; /* version number of last packet */ }; +/* + * Structure used for returning monitor data (old format + */ +struct old_info_monitor { + U_LONG lasttime; /* last packet from this host */ + U_LONG firsttime; /* first time we received a packet */ + U_LONG count; /* count of packets received */ + U_LONG addr; /* host address */ + u_short port; /* port number of last reception */ + u_char mode; /* mode of last packet */ + u_char version; /* version number of last packet */ +}; /* * Structure used for passing indication of flags to clear diff --git a/usr.sbin/xntpd/include/ntp_stdlib.h b/usr.sbin/xntpd/include/ntp_stdlib.h index 0ec0625..f68c768 100644 --- a/usr.sbin/xntpd/include/ntp_stdlib.h +++ b/usr.sbin/xntpd/include/ntp_stdlib.h @@ -79,6 +79,7 @@ extern char * inttoa P((LONG)); extern char * mfptoa P((U_LONG, U_LONG, int)); extern char * mfptoms P((U_LONG, U_LONG, int)); extern char * modetoa P((int)); +extern U_LONG netof P((U_LONG)); extern char * numtoa P((U_LONG)); extern char * numtohost P((U_LONG)); extern int octtoint P((const char *, U_LONG *)); diff --git a/usr.sbin/xntpd/include/ntp_timex.h b/usr.sbin/xntpd/include/ntp_timex.h index 1756e2e..43b6ae7 100644 --- a/usr.sbin/xntpd/include/ntp_timex.h +++ b/usr.sbin/xntpd/include/ntp_timex.h @@ -1,6 +1,6 @@ /****************************************************************************** * * - * Copyright (c) David L. Mills 1993 * + * Copyright (c) David L. Mills 1993, 1994 * * * * Permission to use, copy, modify, and distribute this software and its * * documentation for any purpose and without fee is hereby granted, provided * @@ -17,11 +17,13 @@ /* * Modification history timex.h * - * 28 Nov 93 David L. Mills - * Adjusted parameters to improve stability and increase poll interval + * 20 Feb 94 David L. Mills + * Revised status codes and structures for external clock and PPS + * signal discipline. * - * 10 Oct 93 Torsten Duwe - * Changed to ntp_timex.h (#ifdef'd HAVE_SYS_TIMEX_H) + * 28 Nov 93 David L. Mills + * Adjusted parameters to improve stability and increase poll + * interval. * * 17 Sep 93 David L. Mills * Created file @@ -41,7 +43,7 @@ * int syscall(SYS_ntp_gettime, tptr) * * int SYS_ntp_gettime defined in syscall.h header file - * struct ntptimeval *tptr pointer to ntptimeval structure + * struct ntptimeval *tptr pointer to ntptimeval structure * * NAME * ntp_adjtime - NTP daemon application interface @@ -55,55 +57,43 @@ * struct timex *tptr pointer to timex structure * */ -#ifndef _NTP_TIMEX_H -#define _NTP_TIMEX_H - -/* - * Include system timex.h (if appropriate) - */ -#ifdef HAVE_SYS_TIMEX_H -#include -#else /* provide definitions */ #include -extern int syscall P((int, void *, ...)); - -#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t)) -#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t)) - /* * The following defines establish the engineering parameters of the PLL - * model. The HZ variable establishes the timer interrupt frequency, 100 Hz - * for the SunOS kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the - * OSF/1 kernel. The SHIFT_HZ define expresses the same value as the - * nearest power of two in order to avoid hardware multiply operations. + * model. The hz variable is defined in the kernel build environment. It + * establishes the timer interrupt frequency, 100 Hz for the SunOS + * kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the OSF/1 + * kernel. The SHIFT_HZ define expresses the same value as the nearest + * power of two in order to avoid hardware multiply operations. */ -#define SHIFT_HZ 7 /* log2(HZ) */ +#define SHIFT_HZ 7 /* log2(hz) */ /* * The SHIFT_KG and SHIFT_KF defines establish the damping of the PLL * and are chosen by analysis for a slightly underdamped convergence - * characteristic. The MAXTC define establishes the maximum time constant - * of the PLL. With the parameters given and the default time constant of - * zero, the PLL will converge in about 15 minutes. + * characteristic. The MAXTC define establishes the maximum time + * constant of the PLL. With the parameters given and the minimum time + * constant of zero, the PLL will converge in about 15 minutes. */ -#define SHIFT_KG 6 /* shift for phase increment */ -#define SHIFT_KF 16 /* shift for frequency increment */ +#define SHIFT_KG 6 /* phase factor (shift) */ +#define SHIFT_KF 16 /* frequency factor (shift) */ #define MAXTC 6 /* maximum time constant (shift) */ /* - * The SHIFT_SCALE define establishes the decimal point of the time_phase - * variable which serves as a an extension to the low-order bits of the - * system clock variable. The SHIFT_UPDATE define establishes the decimal - * point of the time_offset variable which represents the current offset - * with respect to standard time. The SHIFT_USEC define represents 1 us in - * external units (shift), while the FINEUSEC define represents 1 us in - * internal units. + * SHIFT_SCALE defines the scaling (shift) of the time_phase variable, + * which serves as a an extension to the low-order bits of the system + * clock variable time.tv_usec. SHIFT_UPDATE defines the scaling (shift) + * of the time_offset variable, which represents the current time offset + * with respect to standard time. SHIFT_USEC defines the scaling (shift) + * of the time_freq and time_tolerance variables, which represent the + * current frequency offset and frequency tolerance. FINEUSEC is 1 us in + * SHIFT_UPDATE units of the time_phase variable. */ -#define SHIFT_SCALE 23 /* shift for phase scale factor */ -#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* shift for offset scale factor */ -#define SHIFT_USEC 16 /* shift for 1 us in external units */ -#define FINEUSEC (1 << SHIFT_SCALE) /* 1 us in internal units */ +#define SHIFT_SCALE 23 /* phase scale (shift) */ +#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */ +#define SHIFT_USEC 16 /* frequency offset scale (shift) */ +#define FINEUSEC (1 << SHIFT_SCALE) /* 1 us in phase units */ /* * Mode codes (timex.mode) @@ -122,8 +112,8 @@ extern int syscall P((int, void *, ...)); #define TIME_INS 1 /* insert leap second */ #define TIME_DEL 2 /* delete leap second */ #define TIME_OOP 3 /* leap second in progress */ -#define TIME_BAD 4 /* clock not synchronized */ - +#define TIME_BAD 4 /* kernel clock not synchronized */ +#define TIME_ERR 5 /* external clock not synchronized */ /* * NTP user interface - used to read kernel clock values * Note: maximum error = NTP synch distance = dispersion + delay / 2; @@ -131,8 +121,8 @@ extern int syscall P((int, void *, ...)); */ struct ntptimeval { struct timeval time; /* current time */ - long maxerror; /* maximum error (usec) */ - long esterror; /* estimated error (usec) */ + long maxerror; /* maximum error (us) */ + long esterror; /* estimated error (us) */ }; /* @@ -140,19 +130,25 @@ struct ntptimeval { */ struct timex { int mode; /* mode selector */ - long offset; /* time offset (usec) */ + long offset; /* time offset (us) */ long frequency; /* frequency offset (scaled ppm) */ - long maxerror; /* maximum error (usec) */ - long esterror; /* estimated error (usec) */ + long maxerror; /* maximum error (us) */ + long esterror; /* estimated error (us) */ int status; /* clock command/status */ long time_constant; /* pll time constant */ - long precision; /* clock precision (usec) (read only) */ - long tolerance; /* clock frequency tolerance (ppm) - * (read only) - */ -}; - -#endif /* HAVE_SYS_TIMEX_H */ - -#endif /* _NTP_TIMEX_H */ + long precision; /* clock precision (us) (read only) */ + long tolerance; /* clock frequency tolerance (scaled + * ppm) (read only) */ + /* + * The following read-only structure members are implemented + * only if the PPS signal discipline is configured in the + * kernel. + */ + long ybar; /* frequency estimate (scaled ppm) */ + long disp; /* dispersion estimate (scaled ppm) */ + int shift; /* interval duration (s) (shift) */ + long calcnt; /* calibration intervals */ + long jitcnt; /* jitter limit exceeded */ + long discnt; /* dispersion limit exceeded */ +}; diff --git a/usr.sbin/xntpd/include/ntpd.h b/usr.sbin/xntpd/include/ntpd.h index f2c56af..590aaae 100644 --- a/usr.sbin/xntpd/include/ntpd.h +++ b/usr.sbin/xntpd/include/ntpd.h @@ -93,8 +93,8 @@ extern int pps_sample P((l_fp *)); /* ntp_monitor.c */ extern void init_mon P((void)); -extern void mon_start P((void)); -extern void mon_stop P((void)); +extern void mon_start P((int)); +extern void mon_stop P((int)); extern void monitor P((struct recvbuf *)); /* ntp_peer.c */ diff --git a/usr.sbin/xntpd/include/parse.h b/usr.sbin/xntpd/include/parse.h index 31041af..6ce3f19 100644 --- a/usr.sbin/xntpd/include/parse.h +++ b/usr.sbin/xntpd/include/parse.h @@ -1,7 +1,7 @@ /* - * /src/NTP/REPOSITORY/v3/include/parse.h,v 3.13 1994/01/25 19:04:21 kardel Exp + * /src/NTP/REPOSITORY/v3/include/parse.h,v 3.17 1994/03/03 09:27:20 kardel Exp * - * parse.h,v 3.13 1994/01/25 19:04:21 kardel Exp + * parse.h,v 3.17 1994/03/03 09:27:20 kardel Exp * * Copyright (c) 1989,1990,1991,1992,1993,1994 * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg @@ -15,7 +15,7 @@ #ifndef __PARSE_H__ #define __PARSE_H__ #if !(defined(lint) || defined(__GNUC__)) - static char parsehrcsid[]="parse.h,v 3.13 1994/01/25 19:04:21 kardel Exp FAU"; + static char parsehrcsid[]="parse.h,v 3.17 1994/03/03 09:27:20 kardel Exp"; #endif #include "ntp_types.h" @@ -81,35 +81,55 @@ extern int debug; /* * state flags */ -#define PARSEB_ANNOUNCE 0x0001 /* switch time zone warning (DST switch) */ -#define PARSEB_POWERUP 0x0002 /* no synchronisation */ -#define PARSEB_NOSYNC 0x0004 /* timecode currently not confirmed */ -#define PARSEB_DST 0x0008 /* DST in effect */ -#define PARSEB_UTC 0x0010 /* UTC time */ -#define PARSEB_LEAP 0x0020 /* LEAP warning (1 hour prior to occurence) */ -#define PARSEB_ALTERNATE 0x0040 /* alternate antenna used */ -#define PARSEB_POSITION 0x0080 /* position available */ -#define PARSEB_LEAPSECOND 0x0100 /* actual leap second */ - -#define PARSEB_S_LEAP 0x0200 /* supports LEAP */ -#define PARSEB_S_ANTENNA 0x0400 /* supports antenna information */ -#define PARSEB_S_PPS 0x0800 /* supports PPS time stamping */ -#define PARSEB_S_POSITION 0x1000 /* supports position information (GPS) */ - -#define PARSEB_TIMECODE 0x2000 /* valid time code sample */ -#define PARSEB_PPS 0x4000 /* valid PPS sample */ +#define PARSEB_POWERUP 0x00000001 /* no synchronisation */ +#define PARSEB_NOSYNC 0x00000002 /* timecode currently not confirmed */ + +/* + * time zone information + */ +#define PARSEB_ANNOUNCE 0x00000010 /* switch time zone warning (DST switch) */ +#define PARSEB_DST 0x00000020 /* DST in effect */ +#define PARSEB_UTC 0x00000040 /* UTC time */ + +/* + * leap information + */ +#define PARSEB_LEAPDEL 0x00000100 /* LEAP deletion warning */ +#define PARSEB_LEAPADD 0x00000200 /* LEAP addition warning */ +#define PARSEB_LEAPS 0x00000300 /* LEAP warnings */ +#define PARSEB_LEAPSECOND 0x00000400 /* actual leap second */ +/* + * optional status information + */ +#define PARSEB_ALTERNATE 0x00001000 /* alternate antenna used */ +#define PARSEB_POSITION 0x00002000 /* position available */ + +/* + * feature information + */ +#define PARSEB_S_LEAP 0x00010000 /* supports LEAP */ +#define PARSEB_S_ANTENNA 0x00020000 /* supports antenna information */ +#define PARSEB_S_PPS 0x00040000 /* supports PPS time stamping */ +#define PARSEB_S_POSITION 0x00080000 /* supports position information (GPS) */ + +/* + * time stamp availality + */ +#define PARSEB_TIMECODE 0x10000000 /* valid time code sample */ +#define PARSEB_PPS 0x20000000 /* valid PPS sample */ #define PARSE_TCINFO (PARSEB_ANNOUNCE|PARSEB_POWERUP|PARSEB_NOSYNC|PARSEB_DST|\ - PARSEB_UTC|PARSEB_LEAP|PARSEB_ALTERNATE|PARSEB_S_LEAP|\ + PARSEB_UTC|PARSEB_LEAPS|PARSEB_ALTERNATE|PARSEB_S_LEAP|\ PARSEB_S_LOCATION|PARSEB_TIMECODE) -#define PARSE_POWERUP(x) ((x) & PARSEB_POWERUP) -#define PARSE_NOSYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == PARSEB_NOSYNC) -#define PARSE_SYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == 0) -#define PARSE_ANNOUNCE(x) ((x) & PARSEB_ANNOUNCE) -#define PARSE_DST(x) ((x) & PARSEB_DST) +#define PARSE_POWERUP(x) ((x) & PARSEB_POWERUP) +#define PARSE_NOSYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == PARSEB_NOSYNC) +#define PARSE_SYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == 0) +#define PARSE_ANNOUNCE(x) ((x) & PARSEB_ANNOUNCE) +#define PARSE_DST(x) ((x) & PARSEB_DST) #define PARSE_UTC(x) ((x) & PARSEB_UTC) -#define PARSE_LEAP(x) (PARSE_SYNC(x) && ((x) & PARSEB_LEAP)) +#define PARSE_LEAPADD(x) (PARSE_SYNC(x) && (((x) & PARSEB_LEAPS) == PARSEB_LEAPADD)) +#define PARSE_LEAPDEL(x) (PARSE_SYNC(x) && (((x) & PARSEB_LEAPS) == PARSEB_LEAPDEL)) #define PARSE_ALTERNATE(x) ((x) & PARSEB_ALTERNATE) #define PARSE_LEAPSECOND(x) (PARSE_SYNC(x) && ((x) & PARSEB_LEAP_SECOND)) @@ -118,9 +138,9 @@ extern int debug; #define PARSE_S_PPS(x) ((x) & PARSEB_S_PPS) #define PARSE_S_POSITION(x) ((x) & PARSEB_S_POSITION) -#define PARSE_TIMECODE(x) ((x) & PARSEB_TIMECODE) +#define PARSE_TIMECODE(x) ((x) & PARSEB_TIMECODE) #define PARSE_PPS(x) ((x) & PARSEB_PPS) -#define PARSE_POSITION(x) ((x) & PARSEB_POSITION) +#define PARSE_POSITION(x) ((x) & PARSEB_POSITION) /* * operation flags - some are also fudge flags @@ -281,6 +301,7 @@ struct clocktime /* clock time broken up from time code */ LONG second; LONG usecond; LONG utcoffset; /* in seconds */ + time_t utctime; /* the actual time - alternative to date/time */ LONG flags; /* current clock status */ }; @@ -365,6 +386,9 @@ extern unsigned LONG pps_simple P((parse_t *, int status, timestamp_t *)); * History: * * parse.h,v + * Revision 3.17 1994/03/03 09:27:20 kardel + * rcs ids fixed + * * Revision 3.13 1994/01/25 19:04:21 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/lib/Makefile.tmpl b/usr.sbin/xntpd/lib/Makefile.tmpl index 697edcf..997aa8c 100644 --- a/usr.sbin/xntpd/lib/Makefile.tmpl +++ b/usr.sbin/xntpd/lib/Makefile.tmpl @@ -31,7 +31,7 @@ SOURCE= atoint.c atolfp.c atouint.c auth12crypt.c authdecrypt.c authdes.c \ uglydate.c uinttoa.c utvtoa.c machines.c clocktypes.c \ md5.c a_md5encrypt.c a_md5decrypt.c \ a_md512crypt.c decodenetnum.c systime.c msyslog.c syssignal.c \ - findconfig.c + findconfig.c netof.c OBJS= atoint.o atolfp.o atouint.o auth12crypt.o authdecrypt.o authdes.o \ authencrypt.o authkeys.o authparity.o authreadkeys.o authusekey.o \ @@ -44,12 +44,12 @@ OBJS= atoint.o atolfp.o atouint.o auth12crypt.o authdecrypt.o authdes.o \ uglydate.o uinttoa.o utvtoa.o machines.o clocktypes.o \ md5.o a_md5encrypt.o a_md5decrypt.o \ a_md512crypt.o decodenetnum.o systime.o msyslog.o syssignal.o \ - findconfig.o + findconfig.o netof.o $(LIBNAME).a: $(OBJS) ar rv $@ $? -rm -f $? - @if ( hp-pa || hp-mc680x0 ) > /dev/null 2>&1; then \ + @if ( ../scripts/hpadjtime.sh ) > /dev/null 2>&1; then \ ( cd ../adjtime && $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" MAKE="$(MAKE)" ) && ar rv $@ ../adjtime/adjtime.o; \ else \ :; \ diff --git a/usr.sbin/xntpd/lib/clocktypes.c b/usr.sbin/xntpd/lib/clocktypes.c index 2701e3f..816ef07 100644 --- a/usr.sbin/xntpd/lib/clocktypes.c +++ b/usr.sbin/xntpd/lib/clocktypes.c @@ -11,7 +11,7 @@ struct clktype clktypes[] = { { REFCLK_NONE, "unspecified type (0)", "UNKNOWN" }, { REFCLK_LOCALCLOCK, "local clock synchronization (1)", "LOCAL" }, - { REFCLK_WWV_HEATH, "Heathkit WWV clock (2)", "WWV_HEATH" }, + { REFCLK_GPS_TRAK, "TRAK 8810 GPS Receiver (2)", "GPS_TRAK" }, { REFCLK_WWV_PST, "Precision Standard Time WWV clock (3)", "WWV_PST" }, { REFCLK_WWVB_SPECTRACOM, "Spectracom WWVB clock (4)", "WWVB_SPEC" }, { REFCLK_GOES_TRUETIME, "True Time GPS/GOES clock (5)", "GPS_GOES_TRUE" }, diff --git a/usr.sbin/xntpd/lib/netof.c b/usr.sbin/xntpd/lib/netof.c new file mode 100644 index 0000000..286a584 --- /dev/null +++ b/usr.sbin/xntpd/lib/netof.c @@ -0,0 +1,25 @@ +/* + * netof - return the net address part of an ip address + * (zero out host part) + */ +#include + +#include "ntp_fp.h" +#include "ntp_stdlib.h" + +U_LONG +netof(num) + U_LONG num; +{ + register U_LONG netnum; + + netnum = num; + + if(IN_CLASSC(netnum)) + netnum &= IN_CLASSC_NET; + else if (IN_CLASSB(netnum)) + netnum &= IN_CLASSB_NET; + else /* treat als other like class A */ + netnum &= IN_CLASSA_NET; + return netnum; +} diff --git a/usr.sbin/xntpd/lib/systime.c b/usr.sbin/xntpd/lib/systime.c index ea15734..1d6c59a 100644 --- a/usr.sbin/xntpd/lib/systime.c +++ b/usr.sbin/xntpd/lib/systime.c @@ -47,12 +47,13 @@ extern int debug; * We also remember the clock precision we computed from the kernel in * case someone asks us. */ + LONG sys_clock; + LONG adj_precision; /* adj precision in usec (tickadj) */ LONG tvu_maxslew; /* maximum adjust doable in 1</OBJ) into the kernel" parsestreams.o.$(KARCH): parsestreams.c ../lib/libntp.a libparse_kernel.a ../include/parse.h ../include/sys/parsestreams.h cc -c $(DEFS) -I../include parsestreams.c diff --git a/usr.sbin/xntpd/parse/README.new_clocks b/usr.sbin/xntpd/parse/README.new_clocks new file mode 100644 index 0000000..8fdd7cb --- /dev/null +++ b/usr.sbin/xntpd/parse/README.new_clocks @@ -0,0 +1,212 @@ +Here is an attempt to sketch out what you need to do in order to +add another clock to the parse driver: + +Prerequisites: +- Does the system you want the clock connect to have + termio.h or termios.h ? (You need that for the parse driver) + +What to do: + +Make a conversion module (parse/clk_*.c) + +- What ist the time code format ? + - find year, month, day, hour, minute, second, status (synchronised or + not), possibly time zone information (you need to give the offset to UTC) + You will have to convert the data from a string into a struct clocktime: + struct clocktime /* clock time broken up from time code */ + { + LONG day; + LONG month; + LONG year; + LONG hour; + LONG minute; + LONG second; + LONG usecond; + LONG utcoffset; /* in seconds */ + time_t utcoffset; /* true utc time instead of date/time */ + LONG flags; /* current clock status */ + }; + + Conversion is usually simple and straight forward. For the flags following + values can be OR'ed together: + + PARSEB_ANNOUNCE switch time zone warning (informational only) + PARSEB_POWERUP no synchronisation - clock confused (must set then) + PARSEB_NOSYNC timecode currently not confirmed (must set then) + usually on reception error when there is still a + chance the the generated time is still ok. + + PARSEB_DST DST in effect (informational only) + PARSEB_UTC timecode contains UTC time (informational only) + PARSEB_LEAPADD LEAP addition warning (prior to leap happening - must set when imminent) + also used for time code that do not encode the + direction (as this is currently the default). + PARSEB_LEAPDEL LEAP deletion warning (prior to leap happening - must set when imminent) + PARSEB_ALTERNATE backup transmitter (informational only) + PARSEB_POSITION geographic position available (informational only) + PARSEB_LEAPSECOND actual leap second (this time code is the leap + second - informational only) + + These are feature flags denoting items that are supported by the clock: + PARSEB_S_LEAP supports LEAP - might set PARSEB_LEAP + PARSEB_S_ANTENNA supports ANTENNA - might set PARSEB_ALTERNATE + PARSEB_S_PPS supports PPS time stamping + PARSEB_S_POSITION supports position information (GPS) + + If the utctime field is non zero this value will be take as + time code value. This allows for conversion routines that + already have the utc time value. The utctime field gives the seconds + since Jan 1st 1970, 0:00:00. The useconds field gives the respective + usec value. The fields for date and time (down to second resolution) + will be ignored. + + Conversion is done in the cvt_* routine in parse/clk_*.c files. look in + them for examples. The basic structure is: + + struct clockformat _format = { + lots of fields for you to fill out (see below) + }; + + static cvt_() + ... + { + if () { + return CVT_NONE; + } else { + if () { + ; + return CVT_OK; + } else { + return CVT_FAIL|CVT_BADFMT; + } + } + + The struct clockformat is the interface to the rest of the parse + driver - it holds all information necessary for finding the + clock message and doing the appropriate time stamping. + +struct clockformat +{ + unsigned LONG (*convert)(); + /* conversion routine - your routine - cvt_ */ + void (*syncevt)(); + /* routine for handling RS232 sync events (time stamps) - usually sync_simple */ + unsigned LONG (*syncpps)(); + /* PPS input routine - usually pps_simple */ + unsigned LONG (*synth)(); + /* time code synthesizer - usually not used - (LONG (*)())0 */ + void *data; + /* local parameters - any parameters/data/configuration info your conversion + routine might need */ + char *name; + /* clock format name - Name of the time code */ + unsigned short length; + /* maximum length of data packet for your clock format */ + unsigned LONG flags; + /* information for the parser what to look for */ + struct timeval timeout; + /* buffer restart after timeout (us) - some clocks preceede new data by + a longer period of silence - unsually not used */ + unsigned char startsym; + /* start symbol - character at the beginning of the clock data */ + unsigned char endsym; + /* end symbol - character at the end of the clock data */ + unsigned char syncsym; + /* sync symbol - character that is "on time" - where the time stamp should be taken */ +}; + + The flags: + F_START use startsym to find the beginning of the clock data + F_END use endsym to find the end of the clock data + SYNC_TIMEOUT packet restart after timeout in timeout field + SYNC_START packet start is sync event (time stamp at paket start) + SYNC_END packet end is sync event (time stamp at paket end) + SYNC_CHAR special character (syncsym) is sync event + SYNC_ONE PPS synchronize on 'ONE' transition + SYNC_ZERO PPS synchronize on 'ZERO' transition + SYNC_SYNTHESIZE generate intermediate time stamps (very special case!) + CVT_FIXEDONLY convert only in fixed configuration - (data format not + suitable for auto-configuration) + + + The above should have given you some hints on how to build a clk_*.c + file with the time code conversion. See the examples and pick a clock + closest to yours and tweak the code to match your clock. + + In order to make your clk_*.c file usable a reference to the clockformat + structure must be put into parse_conf.c. + +TTY setup and initialisation/configuration will be done in +xntpd/refclock_parse.c + +- Find out the exact tty settings for your clock (baud rate, parity, + stop bits, character size, ...) and note them in terms of + termio*.h c_cflag macros. + +- in xntpd/refclock_parse.c fill out a new the struct clockinfo element + (that allocates a new "IP" address - see comments) + (see all the other clocks for example) + struct clockinfo + { + U_LONG cl_flags; /* operation flags (io modes) */ + PARSE_F_NOPOLLONLY always do async io - read whenever input comes + PARSE_F_POLLONLY never do async io - only read when expecting data + PARSE_F_PPSPPS use loopfilter PPS code (CIOGETEV) + PARSE_F_PPSONSECOND PPS pulses are on second + usually flags stay 0 as they are used only for special setups + + void (*cl_poll)(); /* active poll routine */ + The routine to call when the clock needs data sent to it in order to + get a time code from the clock (e.g. Trimble clock) + int (*cl_init)(); /* active poll init routine */ + The routine to call for very special initializations. + void (*cl_end)(); /* active poll end routine */ + The routine to call to undo any special initialisation (free memory/timers) + void *cl_data; /* local data area for "poll" mechanism */ + local data for polling routines + u_fp cl_rootdelay; /* rootdelay */ + NTP rottdelay estimate (usually 0) + U_LONG cl_basedelay; /* current offset - unsigned l_fp fractional par + time (fraction) by which the RS232 time code is delayed from the actual time. + t */ + U_LONG cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional + time (fraction) by which the PPS time stamp is delayed (usually 0) + part */ + char *cl_id; /* ID code (usually "DCF") */ + Refclock id - (max 4 chars) + char *cl_description; /* device name */ + Name of this device. + char *cl_format; /* fixed format */ + If the data format cann not ne detected automatically this is the name + as in clk_*.c clockformat. + u_char cl_type; /* clock type (ntp control) */ + Type if clock as in clock status word (ntp control messages) - usually 0 + U_LONG cl_maxunsync; /* time to trust oscillator after loosing synch + */ + seconds a clock can be trusted after loosing synchronisation. + + U_LONG cl_cflag; /* terminal io flags */ + U_LONG cl_iflag; /* terminal io flags */ + U_LONG cl_oflag; /* terminal io flags */ + U_LONG cl_lflag; /* terminal io flags */ + termio*.h tty modes. + } clockinfo[] = { + ...,,... + { < your parameters> }, + }; + + +Well, this is very sketchy, i know. But I hope it helps a little bit. +The best way is to look which clock comes closest to your and tweak that +code. +Two sorts of clocks are used with parse. Clocks that automatically send +their time code (once a second) do not need entries in the poll routines because +they send the data all the time. The second sort are the clocks that need a +command sent to them in order to reply with a time code (like the Trimble +clock). + +For questions: kardel@informatik.uni-erlangen.de. Please include +an exact description on how your clock works. (initialisation, +TTY modes, strings to be sent to it, responses received from the clock). + +Frank Kardel diff --git a/usr.sbin/xntpd/parse/README.parse_clocks b/usr.sbin/xntpd/parse/README.parse_clocks new file mode 100644 index 0000000..cf8d77e --- /dev/null +++ b/usr.sbin/xntpd/parse/README.parse_clocks @@ -0,0 +1,263 @@ +The parse driver currently supports several clocks with different +query mechanisms. In order for you to find a sample that might be +similar to a clock you might want to integrate into parse i'll sum +up the major features of the clocks (this information is distributed +in the parse/clk_*.c and xntpd/refclock_parse.c files). + +--- + Meinberg: 127.127.8. 0- 3 (PZF535TCXO) + 127.127.8. 4- 7 (PZF535OCXO) + 127.127.8. 8-11 (DCFUA31) + 127.127.8.28-31 (GPS166) + Meinberg: start=, end=, sync on start + pattern="\2D: . . ;T: ;U: . . ; \3" + pattern="\2 . . ; ; : : ; \3" + pattern="\2 . . ; ; : : ; : ; ; . . " + + Meinberg is a german manufacturer of time code receivers. Those clocks + have a pretty common output format in the stock version. In order to + support NTP Meinberg was so kind to produce some special versions of + the firmware for the use with NTP. So, if you are going to use a + Meinberg clock please ask whether there is a special Uni Erlangen + version. + + General characteristics: + Meinberg clocks primarily output pulse per second and a describing + ASCII string. This string can be produced in two modes. either upon + the reception of a question mark or every second. NTP uses the latter + mechanism. The DCF77 variants have a pretty good relationship between + RS232 time code and the PPS signal while the GPS receiver has no fixed + timeing between the datagram and the pulse (you need to use PPS with + GPS!) on DCF77 you might get away without the PPS signal. + + The preferred tty setting for Meinberg is: + CFLAG (B9600|CS7|PARENB|CREAD|HUPCL) + IFLAG (IGNBRK|IGNPAR|ISTRIP) + OFLAG 0 + LFLAG 0 + + The clock is run at datagram once per second. + Stock dataformat is: + + D:
..;T:;U:::; + pos: 0 00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2 2 3 3 3 + 1 23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8 9 0 1 2 + + = '\002' ASCII start of text + = '\003' ASCII end of text +
,, = day, month, year(2 digits!!) + = day of week (sunday= 0) + ,, = hour, minute, second + = '#' if never synced since powerup else ' ' for DCF U/A 31 + '#' if not PZF sychronisation available else ' ' for PZF 535 + = '*' if time comes from internal quartz else ' ' + = 'S' if daylight saving time is active else ' ' + = '!' during the hour preceeding an daylight saving time + start/end change + + For the university of Erlangen a special format was implemented to support + LEAP announcement and anouncement of alternate antenna. + + Version for UNI-ERLANGEN Software is: PZFUERL V4.6 (Meinberg) + + The use of this software release (or higher) is *ABSOLUTELY* + recommended (ask for PZFUERL version as some minor HW fixes have + been introduced) due to the LEAP second support and UTC indication. + The standard timecode does not indicate when the timecode is in + UTC (by front panel configuration) thus we have no chance to find + the correct utc offset. For the standard format do not ever use + UTC display as this is not detectable in the time code !!! + +
..; ; ::; + pos: 0 00 0 00 0 00 11 1 11 11 1 11 2 22 22 2 2 2 2 2 3 3 3 + 1 23 4 56 7 89 01 2 34 56 7 89 0 12 34 5 6 7 8 9 0 1 2 + = '\002' ASCII start of text + = '\003' ASCII end of text +
,, = day, month, year(2 digits!!) + = day of week (sunday= 0) + ,, = hour, minute, second + = 'U' UTC time display + = '#' if never synced since powerup else ' ' for DCF U/A 31 + '#' if not PZF sychronisation available else ' ' for PZF 535 + = '*' if time comes from internal quartz else ' ' + = 'S' if daylight saving time is active else ' ' + = '!' during the hour preceeding an daylight saving time + start/end change + = 'A' LEAP second announcement + = 'R' alternate antenna + + Meinberg GPS166 receiver + + You must get the Uni-Erlangen firmware for the GPS receiver support + to work to full satisfaction ! + +
..; ; ::; <+/-><00:00>; ; + * + 000000000111111111122222222223333333333444444444455555555556666666 + 123456789012345678901234567890123456789012345678901234567890123456 + \x0209.07.93; 5; 08:48:26; +00:00; ; 49.5736N 11.0280E 373m\x03 + * + + = '\002' ASCII start of text + = '\003' ASCII end of text +
,, = day, month, year(2 digits!!) + = day of week (sunday= 0) + ,, = hour, minute, second + <+/->,<00:00> = offset to UTC + = '#' if never synced since powerup else ' ' for DCF U/A 31 + '#' if not PZF sychronisation available else ' ' for PZF 535 + = 'U' UTC time display + = '*' if time comes from internal quartz else ' ' + = 'S' if daylight saving time is active else ' ' + = '!' during the hour preceeding an daylight saving time + start/end change + = 'A' LEAP second announcement + = 'R' alternate antenna (reminiscent of PZF535) usually ' ' + = 'L' on 23:59:60 + + + For the Meinberg parse look into clock_meinberg.c + +--- + RAWDCF: 127.127.8.20-23 (Conrad receiver module - delay 210ms) + 127.127.8.24-27 (FAU receiver - delay 258ms) + RAWDCF: end=TIMEOUT>1.5s, sync each char (any char),generate psuedo time + codes, fixed format + + direct DCF77 code input + In Europe it is relatively easy/cheap the receive the german time code + transmitter DCF77. The simplest version to process its signal is to + feed the 100/200ms pulse of the demodulated AM signal via a level + converter to an RS232 port at 50Baud. parse/clk_rawdcf.c holds all + necessary decoding logic for the time code which is transmitted each + minute for one minute. A bit of the time code is sent once a second. + + The preferred tty setting is: + CFLAG (B50|CS8|CREAD|CLOCAL) + IFLAG 0 + OFLAG 0 + LFLAG 0 + + DCF77 raw time code + + From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig + und Berlin, Maerz 1989 + + Timecode transmission: + AM: + time marks are send every second except for the second before the + next minute mark + time marks consist of a reduction of transmitter power to 25% + of the nominal level + the falling edge is the time indication (on time) + time marks of a 100ms duration constitute a logical 0 + time marks of a 200ms duration constitute a logical 1 + FM: + see the spec. (basically a (non-)inverted psuedo random phase shift) + + Encoding: + Second Contents + 0 - 10 AM: free, FM: 0 + 11 - 14 free + 15 R - alternate antenna + 16 A1 - expect zone change (1 hour before) + 17 - 18 Z1,Z2 - time zone + 0 0 illegal + 0 1 MEZ (MET) + 1 0 MESZ (MED, MET DST) + 1 1 illegal + 19 A2 - expect leap insertion/deletion (1 hour before) + 20 S - start of time code (1) + 21 - 24 M1 - BCD (lsb first) Minutes + 25 - 27 M10 - BCD (lsb first) 10 Minutes + 28 P1 - Minute Parity (even) + 29 - 32 H1 - BCD (lsb first) Hours + 33 - 34 H10 - BCD (lsb first) 10 Hours + 35 P2 - Hour Parity (even) + 36 - 39 D1 - BCD (lsb first) Days + 40 - 41 D10 - BCD (lsb first) 10 Days + 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday) + 45 - 49 MO - BCD (lsb first) Month + 50 MO0 - 10 Months + 51 - 53 Y1 - BCD (lsb first) Years + 54 - 57 Y10 - BCD (lsb first) 10 Years + 58 P3 - Date Parity (even) + 59 - usually missing (minute indication), except for leap insertion + +--- + Schmid clock: 127.127.8.16-19 + Schmid clock: needs poll, binary input, end='\xFC', sync start + + The Schmid clock is a DCF77 receiver that sends a binary + time code at the reception of a flag byte. The contents + if the flag byte determined the time code format. The + binary time code is delimited by the byte 0xFC. + + TTY setup is: + CFLAG (B1200|CS8|CREAD|CLOCAL) + IFLAG 0 + OFLAG 0 + LFLAG 0 + + The command to Schmid's DCF77 clock is a single byte; each bit + allows the user to select some part of the time string, as follows (the + output for the lsb is sent first). + + Bit 0: time in MEZ, 4 bytes *binary, not BCD*; hh.mm.ss.tenths + Bit 1: date 3 bytes *binary, not BCD: dd.mm.yy + Bit 2: week day, 1 byte (unused here) + Bit 3: time zone, 1 byte, 0=MET, 1=MEST. (unused here) + Bit 4: clock status, 1 byte, 0=time invalid, + 1=time from crystal backup, + 3=time from DCF77 + Bit 5: transmitter status, 1 byte, + bit 0: backup antenna + bit 1: time zone change within 1h + bit 3,2: TZ 01=MEST, 10=MET + bit 4: leap second will be + added within one hour + bits 5-7: Zero + Bit 6: time in backup mode, units of 5 minutes (unused here) + + +--- + Trimble SV6: 127.127.8.32-35 + Trimble SV6: needs poll, ascii timecode, start='>', end='<', + query='>QTM<', eol='<' + + Trimble SV6 is a GPS receiver with PPS output. It needs to be polled. + It also need a special tty mode setup (EOL='<'). + + TTY setup is: + CFLAG (B4800|CS8|CREAD) + IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON) + OFLAG (OPOST|ONLCR) + LFLAG (ICANON|ECHOK) + + Special flags are: + PARSE_F_PPSPPS - use CIOGETEV for PPS time stamping + PARSE_F_PPSONSECOND - the time code is not related to + the PPS pulse (so use the time code + only for the second epoch) + + Timecode + 0000000000111111111122222222223333333 / char + 0123456789012345678901234567890123456 \ posn + >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual + ----33445566600112222BB7__-_____--99- Parse + >RTM 1 ;* <", Check + +--- + ELV DCF7000: 127.127.8.12-15 + ELV DCF7000: end='\r', pattern=" - - - - - - - \r" + + The ELV DCF7000 is a cheap DCF77 receiver sending each second + a time code (though not very precise!) delimited by '`r' + + Timecode + YY-MM-DD-HH-MM-SS-FF\r + + FF&0x1 - DST + FF&0x2 - DST switch warning + FF&0x4 - unsynchronised + diff --git a/usr.sbin/xntpd/parse/clk_dcf7000.c b/usr.sbin/xntpd/parse/clk_dcf7000.c index 5655d0a..8b55e2f 100644 --- a/usr.sbin/xntpd/parse/clk_dcf7000.c +++ b/usr.sbin/xntpd/parse/clk_dcf7000.c @@ -1,8 +1,8 @@ #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_DCF7000) /* - * /src/NTP/REPOSITORY/v3/parse/clk_dcf7000.c,v 3.10 1994/01/25 19:05:07 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/clk_dcf7000.c,v 3.11 1994/02/02 17:45:14 kardel Exp * - * clk_dcf7000.c,v 3.10 1994/01/25 19:05:07 kardel Exp + * clk_dcf7000.c,v 3.11 1994/02/02 17:45:14 kardel Exp * * ELV DCF7000 module * @@ -121,6 +121,9 @@ cvt_dcf7000(buffer, size, format, clock) * History: * * clk_dcf7000.c,v + * Revision 3.11 1994/02/02 17:45:14 kardel + * rcs ids fixed + * * Revision 3.6 1993/10/09 15:01:27 kardel * file structure unified * diff --git a/usr.sbin/xntpd/parse/clk_meinberg.c b/usr.sbin/xntpd/parse/clk_meinberg.c index b08dfec..69f88b9 100644 --- a/usr.sbin/xntpd/parse/clk_meinberg.c +++ b/usr.sbin/xntpd/parse/clk_meinberg.c @@ -1,8 +1,8 @@ #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_MEINBERG) /* - * /src/NTP/REPOSITORY/v3/parse/clk_meinberg.c,v 3.11 1994/01/25 19:05:10 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/clk_meinberg.c,v 3.14 1994/02/20 13:04:37 kardel Exp * - * clk_meinberg.c,v 3.11 1994/01/25 19:05:10 kardel Exp + * clk_meinberg.c,v 3.14 1994/02/20 13:04:37 kardel Exp * * Meinberg clock support * @@ -284,8 +284,13 @@ cvt_meinberg(buffer, size, format, clock) clock->flags |= PARSEB_S_LEAP; clock->flags |= PARSEB_S_ANTENNA; + /* + * DCF77 does not encode the direction - + * so we take the current default - + * earth slowing down + */ if (f[4] == 'A') - clock->flags |= PARSEB_LEAP; + clock->flags |= PARSEB_LEAPADD; if (f[5] == 'R') clock->flags |= PARSEB_ALTERNATE; @@ -394,9 +399,12 @@ cvt_mgps(buffer, size, format, clock) /* * oncoming leap second + * data format does not (yet) specify whether + * to add or to delete a second - thus we + * pick the current default */ if (f[5] == 'A') - clock->flags |= PARSEB_LEAP; + clock->flags |= PARSEB_LEAPADD; /* * this is the leap second @@ -414,6 +422,12 @@ cvt_mgps(buffer, size, format, clock) * History: * * clk_meinberg.c,v + * Revision 3.14 1994/02/20 13:04:37 kardel + * parse add/delete second support + * + * Revision 3.13 1994/02/02 17:45:21 kardel + * rcs ids fixed + * * Revision 3.11 1994/01/25 19:05:10 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/parse/clk_rawdcf.c b/usr.sbin/xntpd/parse/clk_rawdcf.c index 2fd3cae..6b02031 100644 --- a/usr.sbin/xntpd/parse/clk_rawdcf.c +++ b/usr.sbin/xntpd/parse/clk_rawdcf.c @@ -1,8 +1,8 @@ #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_RAWDCF) /* - * /src/NTP/REPOSITORY/v3/parse/clk_rawdcf.c,v 3.9 1994/01/25 19:05:12 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/clk_rawdcf.c,v 3.13 1994/03/10 19:00:43 kardel Exp * - * clk_rawdcf.c,v 3.9 1994/01/25 19:05:12 kardel Exp + * clk_rawdcf.c,v 3.13 1994/03/10 19:00:43 kardel Exp * * Raw DCF77 pulse clock support * @@ -245,6 +245,7 @@ static unsigned LONG convert_rawdcf(buffer, size, dcfparam, clock) parseprintf(DD_RAWDCF,("parse: convert_rawdcf: parity check passed\n")); clock->flags = PARSEB_S_ANTENNA|PARSEB_S_LEAP; + clock->utctime= 0; clock->usecond= 0; clock->second = 0; clock->minute = ext_bf(buffer, DCF_M10, dcfparam->zerobits); @@ -278,7 +279,7 @@ static unsigned LONG convert_rawdcf(buffer, size, dcfparam, clock) clock->flags |= PARSEB_ANNOUNCE; if (ext_bf(buffer, DCF_A2, dcfparam->zerobits)) - clock->flags |= PARSEB_LEAP; + clock->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */ if (ext_bf(buffer, DCF_R, dcfparam->zerobits)) clock->flags |= PARSEB_ALTERNATE; @@ -529,6 +530,15 @@ static unsigned LONG snt_rawdcf(parseio, ptime) * History: * * clk_rawdcf.c,v + * Revision 3.13 1994/03/10 19:00:43 kardel + * clear utctime field to avoid confusion on synthesize time stamps + * + * Revision 3.12 1994/02/20 13:04:39 kardel + * parse add/delete second support + * + * Revision 3.11 1994/02/02 17:45:23 kardel + * rcs ids fixed + * * Revision 3.9 1994/01/25 19:05:12 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/parse/clk_schmid.c b/usr.sbin/xntpd/parse/clk_schmid.c index a8ec8a6..8129474 100644 --- a/usr.sbin/xntpd/parse/clk_schmid.c +++ b/usr.sbin/xntpd/parse/clk_schmid.c @@ -1,8 +1,8 @@ #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_SCHMID) /* - * /src/NTP/REPOSITORY/v3/parse/clk_schmid.c,v 3.10 1994/01/25 19:05:15 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/clk_schmid.c,v 3.13 1994/02/20 13:04:41 kardel Exp * - * clk_schmid.c,v 3.10 1994/01/25 19:05:15 kardel Exp + * clk_schmid.c,v 3.13 1994/02/20 13:04:41 kardel Exp * * Schmid clock support * @@ -152,7 +152,7 @@ cvt_schmid(buffer, size, format, clock) if (buffer[8] & WS_LEAP) { - clock->flags |= PARSEB_LEAP; + clock->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */ } } @@ -168,6 +168,12 @@ cvt_schmid(buffer, size, format, clock) * History: * * clk_schmid.c,v + * Revision 3.13 1994/02/20 13:04:41 kardel + * parse add/delete second support + * + * Revision 3.12 1994/02/02 17:45:25 kardel + * rcs ids fixed + * * Revision 3.10 1994/01/25 19:05:15 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/parse/clk_trimble.c b/usr.sbin/xntpd/parse/clk_trimble.c index bfbf1e6..187aed5 100644 --- a/usr.sbin/xntpd/parse/clk_trimble.c +++ b/usr.sbin/xntpd/parse/clk_trimble.c @@ -1,6 +1,6 @@ #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMSV6) /* - * /src/NTP/REPOSITORY/v3/parse/clk_trimble.c,v 3.7 1994/01/25 19:05:17 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/clk_trimble.c,v 3.9 1994/02/02 17:45:27 kardel Exp * * Trimble SV6 clock support */ @@ -106,6 +106,9 @@ cvt_trimsv6(buffer, size, format, clock) * History: * * clk_trimble.c,v + * Revision 3.9 1994/02/02 17:45:27 kardel + * rcs ids fixed + * * Revision 3.7 1994/01/25 19:05:17 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/parse/parse.c b/usr.sbin/xntpd/parse/parse.c index ed5fd9a..dd70f1d 100644 --- a/usr.sbin/xntpd/parse/parse.c +++ b/usr.sbin/xntpd/parse/parse.c @@ -1,8 +1,8 @@ #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) /* - * /src/NTP/REPOSITORY/v3/parse/parse.c,v 3.19 1994/01/25 19:05:20 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/parse.c,v 3.22 1994/02/25 12:34:49 kardel Exp * - * parse.c,v 3.19 1994/01/25 19:05:20 kardel Exp + * parse.c,v 3.22 1994/02/25 12:34:49 kardel Exp * * Parser module for reference clock * @@ -556,6 +556,9 @@ parse_to_unixtime(clock, cvtrtc) register int i; time_t t; + if (clock->utctime) + return clock->utctime; /* if the conversion routine gets it right away - why not */ + if (clock->year < 100) clock->year += 1900; @@ -628,6 +631,9 @@ parse_to_unixtime(clock, cvtrtc) t += clock->utcoffset; /* warp to UTC */ /* done */ + + clock->utctime = t; /* documentray only */ + return t; } @@ -890,6 +896,8 @@ timepacket(parseio) if (parseio->parse_flags & PARSE_FIXED_FMT) { + clock.utctime = 0; + switch ((cvtrtc = clockformats[format]->convert ? clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock) : CVT_NONE) & CVT_MASK) { case CVT_FAIL: @@ -941,6 +949,8 @@ timepacket(parseio) { do { + clock.utctime = 0; + switch ((cvtrtc = (clockformats[format]->convert && !(clockformats[format]->flags & CVT_FIXEDONLY)) ? clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock) : CVT_NONE) & CVT_MASK) @@ -1148,6 +1158,12 @@ parse_setcs(dct, parse) * History: * * parse.c,v + * Revision 3.22 1994/02/25 12:34:49 kardel + * allow for converter generated utc times + * + * Revision 3.21 1994/02/02 17:45:30 kardel + * rcs ids fixed + * * Revision 3.19 1994/01/25 19:05:20 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/parse/parse_conf.c b/usr.sbin/xntpd/parse/parse_conf.c index 0281c9d..238dd12 100644 --- a/usr.sbin/xntpd/parse/parse_conf.c +++ b/usr.sbin/xntpd/parse/parse_conf.c @@ -1,8 +1,8 @@ #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) /* - * /src/NTP/REPOSITORY/v3/parse/parse_conf.c,v 3.13 1994/01/25 19:05:23 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/parse_conf.c,v 3.15 1994/02/02 17:45:32 kardel Exp * - * parse_conf.c,v 3.13 1994/01/25 19:05:23 kardel Exp + * parse_conf.c,v 3.15 1994/02/02 17:45:32 kardel Exp * * Parser configuration module for reference clocks * @@ -81,6 +81,9 @@ unsigned short nformats = sizeof(clockformats) / sizeof(clockformats[0]) - 1; * History: * * parse_conf.c,v + * Revision 3.15 1994/02/02 17:45:32 kardel + * rcs ids fixed + * * Revision 3.13 1994/01/25 19:05:23 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/parse/parsesolaris.c b/usr.sbin/xntpd/parse/parsesolaris.c index 09fac28..23bc252 100644 --- a/usr.sbin/xntpd/parse/parsesolaris.c +++ b/usr.sbin/xntpd/parse/parsesolaris.c @@ -1,7 +1,7 @@ /* - * /src/NTP/REPOSITORY/v3/parse/parsesolaris.c,v 3.9 1994/01/25 19:05:26 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/parsesolaris.c,v 3.15 1994/02/15 22:20:51 kardel Exp * - * parsesolaris.c,v 3.9 1994/01/25 19:05:26 kardel Exp + * parsesolaris.c,v 3.15 1994/02/15 22:20:51 kardel Exp * * STREAMS module for reference clocks * (SunOS5.x - not fully tested - buyer beware ! - OS KILLERS may still be @@ -19,7 +19,7 @@ */ #ifndef lint -static char rcsid[] = "parsesolaris.c,v 3.9 1994/01/25 19:05:26 kardel Exp"; +static char rcsid[] = "parsesolaris.c,v 3.15 1994/02/15 22:20:51 kardel Exp"; #endif /* @@ -65,7 +65,7 @@ static struct fmodsw fmod_templ = { "parse", /* module name */ &parseinfo, /* module information */ - 0, /* not clean yet */ + D_NEW|D_MP|D_MTQPAIR, /* exclusive for q pair */ /* lock ptr */ }; @@ -139,7 +139,7 @@ int Strcmp(s, t) /*ARGSUSED*/ int _init(void) { - static char revision[] = "3.9"; + static char revision[] = "3.15"; char *s, *S, *t; /* @@ -413,6 +413,8 @@ static int parseopen(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp) parse->parse_ppsclockev.tv.tv_usec = 0; parse->parse_ppsclockev.serial = 0; + qprocson(q); + parseprintf(DD_OPEN,("parse: OPEN - initializing io subsystem q=%x\n", q)); if (!parse_ioinit(&parse->parse_io)) @@ -420,6 +422,8 @@ static int parseopen(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp) /* * ok guys - beat it */ + qprocsoff(q); + kmem_free((caddr_t)parse, sizeof(parsestream_t)); parsebusy--; @@ -441,7 +445,7 @@ static int parseopen(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp) */ if (!notice) { - printf("%s: Copyright (c) 1991-1993, Frank Kardel\n", modlstrmod.strmod_linkinfo); + printf("%s: Copyright (c) 1991-1994, Frank Kardel\n", modlstrmod.strmod_linkinfo); notice = 1; } @@ -449,7 +453,12 @@ static int parseopen(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp) } else { + qprocsoff(q); + + kmem_free((caddr_t)parse, sizeof(parsestream_t)); + parsebusy--; + return EIO; } } @@ -462,6 +471,8 @@ static int parseclose(queue_t *q, int flags) parseprintf(DD_CLOSE,("parse: CLOSE\n")); + qprocsoff(q); + s = splhigh(); if (parse->parse_dqueue) @@ -1179,6 +1190,18 @@ static void zs_xsisr(struct zscom *zs) * History: * * parsesolaris.c,v + * Revision 3.15 1994/02/15 22:20:51 kardel + * rcsid fixed + * + * Revision 3.14 1994/02/15 22:06:04 kardel + * added qprocsx & flags for MT capability + * + * Revision 3.13 1994/02/13 19:16:47 kardel + * updated verbose Copyright message + * + * Revision 3.12 1994/02/02 17:45:35 kardel + * rcs ids fixed + * * Revision 3.9 1994/01/25 19:05:26 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/parse/parsestreams.c b/usr.sbin/xntpd/parse/parsestreams.c index b371aed..45d2963 100644 --- a/usr.sbin/xntpd/parse/parsestreams.c +++ b/usr.sbin/xntpd/parse/parsestreams.c @@ -1,7 +1,7 @@ /* - * /src/NTP/REPOSITORY/v3/parse/parsestreams.c,v 3.12 1994/01/25 19:05:30 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/parsestreams.c,v 3.19 1994/02/24 16:33:54 kardel Exp * - * parsestreams.c,v 3.12 1994/01/25 19:05:30 kardel Exp + * parsestreams.c,v 3.19 1994/02/24 16:33:54 kardel Exp * * STREAMS module for reference clocks * (SunOS4.x) @@ -16,7 +16,7 @@ */ #ifndef lint -static char rcsid[] = "parsestreams.c,v 3.12 1994/01/25 19:05:30 kardel Exp"; +static char rcsid[] = "parsestreams.c,v 3.19 1994/02/24 16:33:54 kardel Exp"; #endif #include "sys/types.h" @@ -195,7 +195,7 @@ int xxxinit(fc, vdp, vdi, vds) } else { - static char revision[] = "3.12"; + static char revision[] = "3.19"; char *s, *S, *t; strncpy(ifm->f_name, mname, FMNAMESZ); @@ -527,7 +527,7 @@ static int parseopen(q, dev, flag, sflag) */ if (!notice) { - printf("%s: Copyright (c) 1991-1993, Frank Kardel\n", parsesync_vd.Drv_name); + printf("%s: Copyright (c) 1991-1994, Frank Kardel\n", parsesync_vd.Drv_name); notice = 1; } @@ -535,6 +535,8 @@ static int parseopen(q, dev, flag, sflag) } else { + kmem_free((caddr_t)parse, sizeof(parsestream_t)); + #ifdef VDDRV parsebusy--; #endif @@ -1074,6 +1076,11 @@ static void close_zs_linemon(q, my_q) #define MAXDEPTH 50 /* maximum allowed stream crawl */ +#ifdef PPS_SYNC +extern hardpps(); +extern struct timeval time; +#endif + /* * take external status interrupt (only CD interests us) */ @@ -1085,15 +1092,18 @@ static void zs_xsisr(zs) register queue_t *q; register unsigned char zsstatus; register int loopcheck; - register unsigned char cdstate; register char *dname; +#ifdef PPS_SYNC + register int s; + register long usec; +#endif /* * pick up current state */ zsstatus = zsaddr->zscc_control; - if (za->za_rr0 ^ (cdstate = zsstatus & ZSRR0_CD)) + if ((za->za_rr0 ^ zsstatus) & (ZSRR0_CD|ZSRR0_SYNC)) { timestamp_t cdevent; register int status; @@ -1101,26 +1111,44 @@ static void zs_xsisr(zs) /* * CONDITIONAL external measurement support */ - SET_LED(cdstate); /* + SET_LED(zsstatus & (ZSRR0_CD|ZSRR0_SYNC)); /* * inconsistent with upper SET_LED, but this * is for oscilloscope business anyway and we * are just interested in edge delays in the * lower us range */ - +#ifdef PPS_SYNC + s = splclock(); + usec = time.tv_usec; +#endif /* * time stamp */ uniqtime(&cdevent.tv); - - TIMEVAL_USADD(&cdevent.tv, xsdelay); - - q = za->za_ttycommon.t_readq; + +#ifdef PPS_SYNC + splx(s); +#endif /* * logical state */ - status = cd_invert ? cdstate == 0 : cdstate != 0; + status = cd_invert ? (zsstatus & (ZSRR0_CD|ZSRR0_SYNC)) == 0 : (zsstatus & (ZSRR0_CD|ZSRR0_SYNC)) != 0; + +#ifdef PPS_SYNC + if (status) + { + usec = cdevent.tv.tv_usec - usec; + if (usec < 0) + usec += 1000000; + + hardpps(&cdevent.tv, usec); + } +#endif + + TIMEVAL_USADD(&cdevent.tv, xsdelay); + + q = za->za_ttycommon.t_readq; /* * ok - now the hard part - find ourself @@ -1177,10 +1205,10 @@ static void zs_xsisr(zs) /* * only pretend that CD has been handled */ - za->za_rr0 = za->za_rr0 & ~ZSRR0_CD | zsstatus & ZSRR0_CD; + za->za_rr0 = za->za_rr0 & ~(ZSRR0_CD|ZSRR0_SYNC) | zsstatus & (ZSRR0_CD|ZSRR0_SYNC); ZSDELAY(2); - if (!((za->za_rr0 ^ zsstatus) & ~ZSRR0_CD)) + if (!((za->za_rr0 ^ zsstatus) & ~(ZSRR0_CD|ZSRR0_SYNC))) { /* * all done - kill status indication and return @@ -1258,6 +1286,24 @@ static void zs_xsisr(zs) * History: * * parsestreams.c,v + * Revision 3.19 1994/02/24 16:33:54 kardel + * CD events can also be posted on sync flag + * + * Revision 3.18 1994/02/24 14:12:58 kardel + * initial PPS_SYNC support version + * + * Revision 3.17 1994/02/20 15:18:02 kardel + * rcs id cleanup + * + * Revision 3.16 1994/02/15 22:39:50 kardel + * memory leak on open failure closed + * + * Revision 3.15 1994/02/13 19:16:50 kardel + * updated verbose Copyright message + * + * Revision 3.14 1994/02/02 17:45:38 kardel + * rcs ids fixed + * * Revision 3.12 1994/01/25 19:05:30 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/parse/util/parsetest.c b/usr.sbin/xntpd/parse/util/parsetest.c index 9028b4c..21e4128 100644 --- a/usr.sbin/xntpd/parse/util/parsetest.c +++ b/usr.sbin/xntpd/parse/util/parsetest.c @@ -1,7 +1,7 @@ /* - * /src/NTP/REPOSITORY/v3/kernel/parsetest.c,v 3.4 1993/03/17 17:16:57 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/util/parsetest.c,v 3.13 1994/02/20 13:04:46 kardel Exp * - * parsetest.c,v 3.10 1994/01/23 17:22:18 kardel Exp + * parsetest.c,v 3.13 1994/02/20 13:04:46 kardel Exp * * Copyright (c) 1989,1990,1991,1992,1993,1994 * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg @@ -11,15 +11,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * parsetest.c,v - * Revision 3.4 1993/03/17 17:16:57 kardel - * DEC OSF/1 ALPHA Integration - 930314 + * Revision 3.13 1994/02/20 13:04:46 kardel + * parse add/delete second support * - * Revision 3.3 1993/01/18 09:24:33 kardel - * updated copyright conditions in conjunction with - * conditions set up in the COPYRIGHT file - * - * Revision 3.2 1993/01/17 13:43:00 kardel - * 1993 initial update + * Revision 3.12 1994/02/02 17:45:51 kardel + * rcs ids fixed * */ @@ -198,7 +194,7 @@ main(argc, argv) parsetime_t parsetime; struct strioctl strioc; - printf("parsetest.c,v 3.10 1994/01/23 17:22:18 kardel Exp\n"); + printf("parsetest.c,v 3.13 1994/02/20 13:04:46 kardel Exp\n"); while (ioctl(fd, I_POP, 0) == 0) ; diff --git a/usr.sbin/xntpd/parse/util/testdcf.c b/usr.sbin/xntpd/parse/util/testdcf.c index ebdfd2f..560ab27 100644 --- a/usr.sbin/xntpd/parse/util/testdcf.c +++ b/usr.sbin/xntpd/parse/util/testdcf.c @@ -1,7 +1,7 @@ /* - * /src/NTP/REPOSITORY/v3/parse/util/testdcf.c,v 3.9 1994/01/25 19:05:45 kardel Exp + * /src/NTP/REPOSITORY/v3/parse/util/testdcf.c,v 3.11 1994/02/02 17:45:55 kardel Exp * - * testdcf.c,v 3.9 1994/01/25 19:05:45 kardel Exp + * testdcf.c,v 3.11 1994/02/02 17:45:55 kardel Exp * * simple DCF77 100/200ms pulse test program (via 50Baud serial line) * diff --git a/usr.sbin/xntpd/refclocks/rclk.TRAK b/usr.sbin/xntpd/refclocks/rclk.TRAK new file mode 100644 index 0000000..188ffd4 --- /dev/null +++ b/usr.sbin/xntpd/refclocks/rclk.TRAK @@ -0,0 +1,29 @@ +#!/bin/sh - +CMD="$1" +shift; + +. refclocks/setup + +case "$CMD" in + info) + echo " TRAK - TRAK 8810 GPS station clock" + ;; + check) + if check "$RCONFIG" '$0 ~ /TRAK/'; then + echo "TRAK - TRAK 8810 GPS station clock" + fi + ;; + config) + if check "$REFCONF" '$0 ~ /TRAK/' || + ( [ ! "$REFCONF" ] && query "Include TRAK 8810 GPS station clock (TRAK)" n); then + if check "$PPSFEATURES" '$0 ~ /CD/' && + [ "$PPSOK" -eq 1 ] && + (check "$REFCONF" '$0 ~ /TRAKPPS/' || + ( [ ! "$REFCONF" ] && query " Use TRAK for PPS" n)); then + echo "-DTRAKPPS" >> $RCONFIG + else + echo "-DTRAK" >> $RCONFIG + fi + fi + ;; +esac diff --git a/usr.sbin/xntpd/scripts/Guess.sh b/usr.sbin/xntpd/scripts/Guess.sh index c2be569..ba01e56 100755 --- a/usr.sbin/xntpd/scripts/Guess.sh +++ b/usr.sbin/xntpd/scripts/Guess.sh @@ -40,7 +40,11 @@ if [ -f /bin/uname -o -f /usr/bin/uname ]; then guess="ultrix" ;; hp-ux) case "$3" in - *.10.*) guess="hpux10+" ;; + *.10.*) guess="hpux-adj" ;; + *.09.03) case "$5" in + 9000/3*) guess="hpux-adj" ;; + *) guess="hpux" ;; + esac ;; *) guess="hpux" ;; esac ;; diff --git a/usr.sbin/xntpd/scripts/README b/usr.sbin/xntpd/scripts/README index 79f1792..7439c6c 100644 --- a/usr.sbin/xntpd/scripts/README +++ b/usr.sbin/xntpd/scripts/README @@ -11,7 +11,7 @@ Guess.sh script to figure out what machine and operating system autoconf awesome script swiped from Jeff Johnson (who may have swiped it from GNU) which delves deep into the system files to reveal dark secrets necessary to port NTP to - everything exceptt sewing machines. Unfinished work. + everything except sewing machines. Unfinished work. makeconfig.sh shell script that calles Guess.sh and then figures out what compiler is available, then builds the diff --git a/usr.sbin/xntpd/scripts/hpadjtime.sh b/usr.sbin/xntpd/scripts/hpadjtime.sh new file mode 100644 index 0000000..07773dc --- /dev/null +++ b/usr.sbin/xntpd/scripts/hpadjtime.sh @@ -0,0 +1,18 @@ +#! /bin/sh +val=1 +if [ -f /bin/uname -o -f /usr/bin/uname ]; then + set `uname -a | tr '[A-Z]' '[a-z]'` + case "$1" in + hp-ux) case "$3" in + *.10.*) val=1 ;; + *.09.03) case "$5" in + 9000/3*) val=1 ;; + *) val=0 ;; + esac ;; + *) val=0 ;; + esac + ;; + *) + esac +fi +exit $val diff --git a/usr.sbin/xntpd/scripts/support/bin/monl b/usr.sbin/xntpd/scripts/support/bin/monl index 44201d0..f0c48db 100755 --- a/usr.sbin/xntpd/scripts/support/bin/monl +++ b/usr.sbin/xntpd/scripts/support/bin/monl @@ -143,7 +143,8 @@ foreach $hostname (@ARGV) { chop; split; - ($host, $count, $mode, $version, $lasttime, $firsttime) = (@_[$[, $[+2 .. $[+6]); + ($host, $count, $mode, $version, $lasttime, $firsttime) = + (@_[$[, $[+2 .. $[+4, $#_-1,$#_]); $Seen{$host, $mode} = 1; diff --git a/usr.sbin/xntpd/util/ntptime.c b/usr.sbin/xntpd/util/ntptime.c index c0512df..dc60d6f 100644 --- a/usr.sbin/xntpd/util/ntptime.c +++ b/usr.sbin/xntpd/util/ntptime.c @@ -15,27 +15,29 @@ #include #include -#include - #include "ntp_fp.h" #include "ntp_unixtime.h" #include "ntp_stdlib.h" #ifndef SYS_DECOSF1 #define BADCALL -1 /* this is supposed to be a bad syscall */ -#endif -#include "ntp_timex.h" +#endif /* SYS_DECOSF1 */ -#ifdef KERNEL_PLL -#ifndef SYS_ntp_adjtime +#ifdef KERNEL_PLL +#include +#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t)) +#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t)) +#else /* KERNEL_PLL */ +#include "ntp_timex.h" #define SYS_ntp_adjtime NTP_SYSCALL_ADJ -#endif -#ifndef SYS_ntp_gettime #define SYS_ntp_gettime NTP_SYSCALL_GET -#endif -#endif /* KERNEL_PLL */ +#endif /* KERNEL_PLL */ +/* + * Function prototypes + */ extern int sigvec P((int, struct sigvec *, struct sigvec *)); +extern int syscall P((int, void *, ...)); void pll_trap P((void)); static struct sigvec newsigsys; /* new sigvec status */ @@ -56,14 +58,14 @@ main(argc, argv) struct ntptimeval ntv; struct timex ntx, _ntx; int times[20]; - double ftemp; + double ftemp, gtemp; l_fp ts; int c; int errflg = 0; int cost = 0; int rawtime = 0; - ntx.mode = 0; + memset((char *)&ntx, 0, sizeof(ntx)); progname = argv[0]; while ((c = ntp_getopt(argc, argv, optargs)) != EOF) switch (c) { case 'c': @@ -157,7 +159,7 @@ main(argc, argv) } } (void)ntp_gettime(&ntv); - ntx.mode = 0; /* Ensure nothing is set */ + _ntx.mode = 0; /* Ensure nothing is set */ (void)ntp_adjtime(&_ntx); if (pll_control < 0) { printf("NTP user interface routines are not configured in this kernel.\n"); @@ -191,13 +193,23 @@ main(argc, argv) printf("ntp_adjtime() returns code %d\n", status); ftemp = ntx.frequency; ftemp /= (1 << SHIFT_USEC); - printf(" mode: %02x, offset: %ld usec, frequency: %6.3f ppm,\n", + printf(" mode: %02x, offset: %ld usec, frequency:%8.3f ppm,\n", ntx.mode, ntx.offset, ftemp); printf(" confidence interval: %ld usec, estimated error: %ld usec,\n", ntx.maxerror, ntx.esterror); - printf(" status: %d, time constant: %ld, precision: %ld usec, tolerance: %ld usec\n", - ntx.status, ntx.time_constant, ntx.precision, - ntx.tolerance); + ftemp = ntx.tolerance; + ftemp /= (1 << SHIFT_USEC); + printf(" status: %d, time constant: %ld, precision: %ld usec, tolerance:%4.0f ppm\n", + ntx.status, ntx.time_constant, ntx.precision, ftemp); + if (ntx.shift == 0) + return; + ftemp = ntx.ybar; + ftemp /= (1 << SHIFT_USEC); + gtemp = ntx.disp; + gtemp /= (1 << SHIFT_USEC); + printf(" pps frequency%8.3f ppm, pps dispersion:%8.3f ppm, interval:%4d sec,\n intervals:%5ld, jitter exceeded:%4ld, dispersion exceeded:%4ld\n", + ftemp, gtemp, 1 << ntx.shift, ntx.calcnt, ntx.jitcnt, + ntx.discnt); } /* diff --git a/usr.sbin/xntpd/util/tickadj.c b/usr.sbin/xntpd/util/tickadj.c index ab10b37..b3b6ba9 100644 --- a/usr.sbin/xntpd/util/tickadj.c +++ b/usr.sbin/xntpd/util/tickadj.c @@ -1,4 +1,4 @@ -/* tickadj.c,v 3.1 1993/07/06 01:11:05 jbj Exp +/* * tickadj - read, and possibly modify, the kernel `tick' and * `tickadj' variables, as well as `dosynctodr'. Note that * this operates on the running kernel only. I'd like to be @@ -6,6 +6,41 @@ * mastered this yet. */ #include + +#ifdef SYS_LINUX +#include + +struct timex txc; + +int +main(int argc, char ** argv) +{ + if (argc > 2) + { + fprintf(stderr, "Usage: %s [tick_value]\n", argv[0]); + exit(-1); + } + else if (argc == 2) + { + if ( (txc.tick = atoi(argv[1])) < 1 ) + { + fprintf(stderr, "Silly value for tick: %s\n", argv[1]); + exit(-1); + } + txc.mode = ADJ_TICK; + } + else + txc.mode = 0; + + if (__adjtimex(&txc) < 0) + perror("adjtimex"); + else + printf("tick = %d\n", txc.tick); + + return(0); +} +#else /* not Linux... kmem tweaking: */ + #include #include #include @@ -353,6 +388,22 @@ getoffsets(filex, tick_off, tickadj_off, dosync_off, noprintf_off) #endif #endif +#if defined(SYS_HPUX) +#define X_TICKADJ 0 +#define X_TICK 1 +#define X_DEF + static struct nlist nl[] = +#ifdef hp9000s300 + { {"_tickadj"}, + {"_old_tick"}, +#else + { {"tickadj"}, + {"old_tick"}, +#endif + {""}, + }; +#endif + #if !defined(X_DEF) #define X_TICKADJ 0 #define X_TICK 1 @@ -373,6 +424,7 @@ getoffsets(filex, tick_off, tickadj_off, dosync_off, noprintf_off) "/kernel/unix", "/386bsd", "/netbsd", + "/hp-ux", NULL }; struct stat stbuf; @@ -513,3 +565,4 @@ readvar(fd, off, var) exit(1); } } +#endif /* not Linux */ diff --git a/usr.sbin/xntpd/xntpd/Makefile.tmpl b/usr.sbin/xntpd/xntpd/Makefile.tmpl index ec600ac..6a5a2b1 100644 --- a/usr.sbin/xntpd/xntpd/Makefile.tmpl +++ b/usr.sbin/xntpd/xntpd/Makefile.tmpl @@ -34,7 +34,8 @@ SOURCE= ntp_config.c ntp_control.c ntp_io.c ntp_leap.c \ refclock_wwvb.c refclock_goes.c refclock_mx4200.c \ refclock_parse.c refclock_as2201.c refclock_omega.c \ refclock_tpro.c refclock_leitch.c refclock_irig.c \ - refclock_msfees.c refclock_gpstm.c ntp_intres.c ntp_filegen.c + refclock_msfees.c refclock_gpstm.c refclock_trak.c \ + ntp_intres.c ntp_filegen.c OBJS= ntp_config.o ntp_control.o ntp_io.o ntp_leap.o \ ntp_loopfilter.o ntp_monitor.o ntp_peer.o ntp_proto.o \ @@ -44,7 +45,8 @@ OBJS= ntp_config.o ntp_control.o ntp_io.o ntp_leap.o \ refclock_wwvb.o refclock_goes.o refclock_mx4200.o \ refclock_parse.o refclock_as2201.o refclock_omega.o \ refclock_tpro.o refclock_leitch.o refclock_irig.o \ - refclock_msfees.o refclock_gpstm.o ntp_intres.o ntp_filegen.o + refclock_msfees.o refclock_gpstm.o refclock_trak.o \ + ntp_intres.o ntp_filegen.o all: $(PROGRAM) @@ -137,5 +139,8 @@ refclock_irig.o: refclock_irig.c refclock_msfees.o: refclock_msfees.c $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c +refclock_trak.o: refclock_trak.c + $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c + refclock_gpstm.o: refclock_gpstm.c $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c diff --git a/usr.sbin/xntpd/xntpd/ntp_config.c b/usr.sbin/xntpd/xntpd/ntp_config.c index 1b716f6..cfa47a8 100644 --- a/usr.sbin/xntpd/xntpd/ntp_config.c +++ b/usr.sbin/xntpd/xntpd/ntp_config.c @@ -58,6 +58,7 @@ * statsdir /var/NTP/ * filegen peerstats [ file peerstats ] [ type day ] [ link ] * resolver /path/progname + * netlimit integer * * And then some. See the manual page. */ @@ -94,6 +95,8 @@ #define CONFIG_PIDFILE 25 #define CONFIG_LOGFILE 26 #define CONFIG_SETVAR 27 +#define CONFIG_CLIENTLIMIT 28 +#define CONFIG_CLIENTPERIOD 29 #define CONF_MOD_VERSION 1 #define CONF_MOD_KEY 2 @@ -114,6 +117,7 @@ #define CONF_RES_NOTRAP 8 #define CONF_RES_LPTRAP 9 #define CONF_RES_NTPPORT 10 +#define CONF_RES_LIMITED 11 #define CONF_TRAP_PORT 1 #define CONF_TRAP_INTERFACE 2 @@ -179,6 +183,8 @@ static struct keyword keywords[] = { { "pidfile", CONFIG_PIDFILE }, { "logfile", CONFIG_LOGFILE }, { "setvar", CONFIG_SETVAR }, + { "clientlimit", CONFIG_CLIENTLIMIT }, + { "clientperiod", CONFIG_CLIENTPERIOD }, { "", CONFIG_UNKNOWN } }; @@ -217,6 +223,7 @@ static struct keyword res_keywords[] = { { "notrap", CONF_RES_NOTRAP }, { "lowpriotrap", CONF_RES_LPTRAP }, { "ntpport", CONF_RES_NTPPORT }, + { "limited", CONF_RES_LIMITED }, { "", CONFIG_UNKNOWN } }; @@ -817,9 +824,9 @@ getconfig(argc, argv) errflg = 0; if (ntokens >= 2) { if (STREQ(tokens[1], "yes")) - mon_start(); + mon_start(MON_ON); else if (STREQ(tokens[1], "no")) - mon_stop(); + mon_stop(MON_ON); else errflg++; } else { @@ -965,6 +972,10 @@ getconfig(argc, argv) peerkey |= RESM_NTPONLY; break; + case CONF_RES_LIMITED: + peerversion |= RES_LIMITED; + break; + case CONFIG_UNKNOWN: errflg++; break; @@ -1414,6 +1425,60 @@ getconfig(argc, argv) ((((ntokens > 2) && !strcmp(tokens[2], "default"))) ? DEF : 0)); } break; + + case CONFIG_CLIENTLIMIT: + if (ntokens < 2) + { + syslog(LOG_ERR, + "no value for clientlimit command - line ignored"); + } + else + { + U_LONG i; + if (!atouint(tokens[1], &i) || !i) + { + syslog(LOG_ERR, + "illegal value for clientlimit command - line ignored"); + } + else + { + extern U_LONG client_limit; + char bp[80]; + + sprintf(bp, "client_limit=%d", i); + set_sys_var(bp, strlen(bp)+1, RO); + + client_limit = i; + } + } + break; + + case CONFIG_CLIENTPERIOD: + if (ntokens < 2) + { + syslog(LOG_ERR, + "no value for clientperiod command - line ignored"); + } + else + { + U_LONG i; + if (!atouint(tokens[1], &i) || i < 64) + { + syslog(LOG_ERR, + "illegal value for clientperiod command - line ignored"); + } + else + { + extern U_LONG client_limit_period; + char bp[80]; + + sprintf(bp, "client_limit_period=%d", i); + set_sys_var(bp, strlen(bp)+1, RO); + + client_limit_period = i; + } + } + break; } } (void) fclose(fp); diff --git a/usr.sbin/xntpd/xntpd/ntp_control.c b/usr.sbin/xntpd/xntpd/ntp_control.c index f638367..976ebd4 100644 --- a/usr.sbin/xntpd/xntpd/ntp_control.c +++ b/usr.sbin/xntpd/xntpd/ntp_control.c @@ -301,7 +301,7 @@ static char str_processor[] = STR_PROCESSOR; static u_char clocktypes[] = { CTL_SST_TS_NTP, /* REFCLK_NONE */ CTL_SST_TS_UNSPEC, /* REFCLK_LOCALCLOCK */ - CTL_SST_TS_HF, /* REFCLK_WWV_HEATH */ + CTL_SST_TS_HF, /* REFCLK_GPS_TRAK */ CTL_SST_TS_HF, /* REFCLK_WWV_PST */ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM */ CTL_SST_TS_UHF, /* REFCLK_GOES_TRUETIME */ diff --git a/usr.sbin/xntpd/xntpd/ntp_loopfilter.c b/usr.sbin/xntpd/xntpd/ntp_loopfilter.c index 6d79c5d..e0411fa 100644 --- a/usr.sbin/xntpd/xntpd/ntp_loopfilter.c +++ b/usr.sbin/xntpd/xntpd/ntp_loopfilter.c @@ -9,9 +9,6 @@ #include "ntpd.h" #include "ntp_io.h" -#if defined(KERNEL_PLL) -#include "ntp_timex.h" -#endif /* KERNEL_PLL */ #include "ntp_unixtime.h" #if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) @@ -46,11 +43,11 @@ #include "ntp_stdlib.h" -#ifdef KERNEL_PLL -#ifndef SYS_ntp_adjtime -#define SYS_ntp_adjtime NTP_SYSCALL_ADJ -#endif -#endif /* KERNEL_PLL */ +#ifdef KERNEL_PLL +#include +#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t)) +#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t)) +#endif /* KERNEL_PLL */ /* * The loop filter is implemented in slavish adherence to the @@ -158,8 +155,8 @@ static l_fp pps_delay; /* pps tuning offset */ #define PPS_MAXUPDATE 600 /* seconds after which we disbelieve timecode */ #define PPS_DEV "/dev/pps" /* pps port */ #define PPS_FAC 3 /* pps shift (log2 trimmed samples) */ -#define NPPS 12 /* pps filter size (1<sin, ctlsysstatus(), &pps_offset, - sys_rootdelay, sys_rootdispersion); + /* + * Note the peerstats file will contain the gross dispersion in + * the delay field. Temporaty hack. + */ + record_peer_stats(&loopback_interface->sin, ctlsysstatus(), + &pps_offset, utemp, pps_maxd); return (0); } #endif /* PPS || PPSCLK || PPSPPS */ diff --git a/usr.sbin/xntpd/xntpd/ntp_monitor.c b/usr.sbin/xntpd/xntpd/ntp_monitor.c index 63af8d4..e2d2eb5 100644 --- a/usr.sbin/xntpd/xntpd/ntp_monitor.c +++ b/usr.sbin/xntpd/xntpd/ntp_monitor.c @@ -58,7 +58,7 @@ static struct mon_data *mon_hash; /* Pointer to array of hash buckets */ static int *mon_hash_count; /* Point to hash count stats keeper */ struct mon_data mon_mru_list; - + struct mon_data mon_fifo_list; /* * List of free structures structures, and counters of free and total * structures. The free structures are linked with the hash_next field. @@ -93,7 +93,7 @@ init_mon() * Don't do much of anything here. We don't allocate memory * until someone explicitly starts us. */ - mon_enabled = 0; + mon_enabled = MON_OFF; mon_have_memory = 0; mon_free_mem = 0; @@ -103,6 +103,7 @@ init_mon() mon_hash = 0; mon_hash_count = 0; memset((char *)&mon_mru_list, 0, sizeof mon_mru_list); + memset((char *)&mon_fifo_list, 0, sizeof mon_fifo_list); } @@ -110,13 +111,18 @@ init_mon() * mon_start - start up the monitoring software */ void -mon_start() +mon_start(mode) + int mode; { register struct mon_data *md; register int i; - if (mon_enabled) + if (mon_enabled != MON_OFF) { + mon_enabled |= mode; return; + } + if (mode == MON_OFF) + return; /* Ooops.. */ if (!mon_have_memory) { mon_hash = (struct mon_data *) @@ -142,7 +148,10 @@ mon_start() mon_mru_list.mru_next = &mon_mru_list; mon_mru_list.mru_prev = &mon_mru_list; - mon_enabled = 1; + mon_fifo_list.fifo_next = &mon_fifo_list; + mon_fifo_list.fifo_prev = &mon_fifo_list; + + mon_enabled = mode; } @@ -150,12 +159,19 @@ mon_start() * mon_stop - stop the monitoring software */ void -mon_stop() +mon_stop(mode) + int mode; { register struct mon_data *md; register int i; - if (!mon_enabled) + if (mon_enabled == MON_OFF) + return; + if ((mon_enabled & mode) == 0 || mode == MON_OFF) + return; + + mon_enabled &= ~mode; + if (mon_enabled != MON_OFF) return; /* @@ -176,7 +192,8 @@ mon_stop() mon_mru_list.mru_next = &mon_mru_list; mon_mru_list.mru_prev = &mon_mru_list; - mon_enabled = 0; + mon_fifo_list.fifo_next = &mon_fifo_list; + mon_fifo_list.fifo_prev = &mon_fifo_list; } @@ -194,7 +211,7 @@ monitor(rbufp) register int mode; register struct mon_data *mdhash; - if (!mon_enabled) + if (mon_enabled == MON_OFF) return; pkt = &rbufp->recv_pkt; @@ -220,6 +237,7 @@ monitor(rbufp) md->mru_prev = &mon_mru_list; mon_mru_list.mru_next->mru_prev = md; mon_mru_list.mru_next = md; + return; } md = md->hash_next; @@ -240,6 +258,12 @@ monitor(rbufp) md->hash_next->hash_prev = md->hash_prev; md->hash_prev->hash_next = md->hash_next; *(mon_hash_count + MON_HASH(md->rmtadr)) -= 1; + /* + * Get it from FIFO list + */ + md->fifo_prev->fifo_next = md->fifo_next; + md->fifo_next->fifo_prev = md->fifo_prev; + } else { if (mon_free_mem == 0) mon_getmoremem(); @@ -252,6 +276,7 @@ monitor(rbufp) * Got one, initialize it */ md->lasttime = md->firsttime = current_time; + md->lastdrop = 0; md->count = 1; md->rmtadr = netnum; md->rmtport = NSRCPORT(&rbufp->recv_srcadr); @@ -260,7 +285,8 @@ monitor(rbufp) /* * Shuffle him into the hash table, inserting him at the - * end. Also put him on top of the MRU list. + * end. Also put him on top of the MRU list + * and at bottom of FIFO list */ mdhash = mon_hash + MON_HASH(netnum); md->hash_next = mdhash; @@ -273,6 +299,11 @@ monitor(rbufp) md->mru_prev = &mon_mru_list; mon_mru_list.mru_next->mru_prev = md; mon_mru_list.mru_next = md; + + md->fifo_prev = mon_fifo_list.fifo_prev; + md->fifo_next = &mon_fifo_list; + mon_fifo_list.fifo_prev->fifo_next = md; + mon_fifo_list.fifo_prev = md; } diff --git a/usr.sbin/xntpd/xntpd/ntp_proto.c b/usr.sbin/xntpd/xntpd/ntp_proto.c index 88e95bb..d71550d 100644 --- a/usr.sbin/xntpd/xntpd/ntp_proto.c +++ b/usr.sbin/xntpd/xntpd/ntp_proto.c @@ -25,7 +25,7 @@ l_fp sys_reftime; /* time we were last updated */ l_fp sys_refskew; /* accumulated skew since last update */ struct peer *sys_peer; /* our current peer */ u_char sys_poll; /* log2 of desired system poll interval */ -LONG sys_clock; /* second part of current time */ +extern LONG sys_clock; /* second part of current time - now in systime.c */ LONG sys_lastselect; /* sys_clock at last synch-dist update */ /* @@ -49,6 +49,7 @@ U_LONG sys_badlength; /* packets with bad length */ U_LONG sys_processed; /* packets processed */ U_LONG sys_badauth; /* packets dropped because of authorization */ U_LONG sys_wanderhold; /* sys_peer held to prevent wandering */ +U_LONG sys_limitrejected; /* pkts rejected due toclient count per net */ /* * Imported from ntp_timer.c @@ -373,6 +374,21 @@ receive(rbufp) return; /* + * See if we only accept limited number of clients + * from the net this guy is from. + * Note: the flag is determined dynamically within restrictions() + */ + if (restrict & RES_LIMITED) { + extern U_LONG client_limit; + + sys_limitrejected++; + syslog(LOG_NOTICE, + "rejected mode %d request from %s - per net client limit (%d) exceeded", + PKT_MODE(pkt->li_vn_mode), + ntoa(&rbufp->recv_srcadr), client_limit); + return; + } + /* * Dump anything with a putrid stratum. These will most likely * come from someone trying to poll us with ntpdc. */ @@ -2165,4 +2181,5 @@ proto_clr_stats() sys_badauth = 0; sys_wanderhold = 0; sys_stattime = current_time; + sys_limitrejected = 0; } diff --git a/usr.sbin/xntpd/xntpd/ntp_request.c b/usr.sbin/xntpd/xntpd/ntp_request.c index c19d192..c87fdef 100644 --- a/usr.sbin/xntpd/xntpd/ntp_request.c +++ b/usr.sbin/xntpd/xntpd/ntp_request.c @@ -916,6 +916,7 @@ sys_stats(srcadr, inter, inpkt) extern U_LONG sys_processed; extern U_LONG sys_badauth; extern U_LONG sys_wanderhold; + extern U_LONG sys_limitrejected; ss = (struct info_sys_stats *)prepare_pkt(srcadr, inter, inpkt, sizeof(struct info_sys_stats)); @@ -930,7 +931,7 @@ sys_stats(srcadr, inter, inpkt) ss->processed = htonl(sys_processed); ss->badauth = htonl(sys_badauth); ss->wanderhold = htonl(sys_wanderhold); - + ss->limitrejected = htonl(sys_limitrejected); (void) more_pkt(); flush_pkt(); } @@ -1311,7 +1312,7 @@ do_monitor(srcadr, inter, inpkt) struct interface *inter; struct req_pkt *inpkt; { - mon_start(); + mon_start(MON_ON); req_ack(srcadr, inter, inpkt, INFO_OKAY); } @@ -1325,7 +1326,7 @@ do_nomonitor(srcadr, inter, inpkt) struct interface *inter; struct req_pkt *inpkt; { - mon_stop(); + mon_stop(MON_ON); req_ack(srcadr, inter, inpkt, INFO_OKAY); } @@ -1497,6 +1498,10 @@ mon_getlist(srcadr, inter, inpkt) md = md->mru_next) { im->lasttime = htonl(current_time - md->lasttime); im->firsttime = htonl(current_time - md->firsttime); + if (md->lastdrop) + im->lastdrop = htonl(current_time - md->lastdrop); + else + im->lastdrop = 0; im->count = htonl(md->count); im->addr = md->rmtadr; im->port = md->rmtport; diff --git a/usr.sbin/xntpd/xntpd/ntp_restrict.c b/usr.sbin/xntpd/xntpd/ntp_restrict.c index 51d8968..43f01f2 100644 --- a/usr.sbin/xntpd/xntpd/ntp_restrict.c +++ b/usr.sbin/xntpd/xntpd/ntp_restrict.c @@ -1,4 +1,4 @@ -/* ntp_restrict.c,v 3.1 1993/07/06 01:11:28 jbj Exp +/* * ntp_restrict.c - find out what restrictions this host is running under */ #include @@ -60,6 +60,21 @@ U_LONG res_not_found; U_LONG res_timereset; /* + * Parameters of the RES_LIMITED restriction option. + * client_limit is the number of hosts allowed per source net + * client_limit_period is the number of seconds after which an entry + * is no longer considered for client limit determination + */ +U_LONG client_limit; +U_LONG client_limit_period; +/* + * count number of restriction entries referring to RES_LIMITED + * controls activation/deactivation of monitoring + * (with respect ro RES_LIMITED control) + */ +U_LONG res_limited_refcnt; + +/* * Our initial allocation of list entries. */ static struct restrictlist resinit[INITRESLIST]; @@ -70,12 +85,18 @@ static struct restrictlist resinit[INITRESLIST]; extern U_LONG current_time; /* + * debug flag + */ +extern int debug; + +/* * init_restrict - initialize the restriction data structures */ void init_restrict() { register int i; + char bp[80]; /* * Zero the list and put all but one on the free list @@ -108,6 +129,18 @@ init_restrict() res_found = 0; res_not_found = 0; res_timereset = 0; + + /* + * set default values for RES_LIMIT functionality + */ + client_limit = 3; + client_limit_period = 3600; + res_limited_refcnt = 0; + + sprintf(bp, "client_limit=%d", client_limit); + set_sys_var(bp, strlen(bp)+1, RO); + sprintf(bp, "client_limit_period=%d", client_limit_period); + set_sys_var(bp, strlen(bp)+1, RO); } @@ -150,6 +183,120 @@ restrictions(srcadr) else res_found++; + /* + * The following implements limiting the number of clients + * accepted from a given network. The notion of "same network" + * is determined by the mask and addr fields of the restrict + * list entry. The monitor mechanism has to be enabled for + * collecting info on current clients. + * + * The policy is as follows: + * - take the list of clients recorded + * from the given "network" seen within the last + * client_limit_period seconds + * - if there are at most client_limit entries: + * --> access allowed + * - otherwise sort by time first seen + * - current client among the first client_limit seen + * hosts? + * if yes: access allowed + * else: eccess denied + */ + if (match->flags & RES_LIMITED) { + int lcnt; + struct mon_data *md, *this_client; + extern int mon_enabled; + extern struct mon_data mon_fifo_list, mon_mru_list; + +#ifdef DEBUG + if (debug > 2) + printf("limited clients check: %d clients, period %d seconds, net is 0x%X\n", + client_limit, client_limit_period, + netof(hostaddr)); +#endif /*DEBUG*/ + if (mon_enabled == MON_OFF) { +#ifdef DEBUG + if (debug > 4) + printf("no limit - monitoring is off\n"); +#endif + return (int)(match->flags & ~RES_LIMITED); + } + + /* + * How nice, MRU list provides our current client as the + * first entry in the list. + * Monitoring was verified to be active above, thus we + * know an entry for our client must exist, or some + * brain dead set the memory limit for mon entries to ZERO!!! + */ + this_client = mon_mru_list.mru_next; + + for (md = mon_fifo_list.fifo_next,lcnt = 0; + md != &mon_fifo_list; + md = md->fifo_next) { + if ((current_time - md->lasttime) + > client_limit_period) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: ignore: too old: %d\n", + numtoa(md->rmtadr), + current_time - md->lasttime); +#endif + continue; + } + if (md->mode == MODE_BROADCAST || + md->mode == MODE_CONTROL || + md->mode == MODE_PRIVATE) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: ignore mode %d\n", + numtoa(md->rmtadr), + md->mode); +#endif + continue; + } + if (netof(md->rmtadr) != + netof(hostaddr)) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: different net 0x%X\n", + numtoa(md->rmtadr), + netof(md->rmtadr)); +#endif + continue; + } + lcnt++; + if (lcnt > client_limit || + md->rmtadr == hostaddr) { +#ifdef DEBUG + if (debug > 5) + printf("considering %s: found host\n", + numtoa(md->rmtadr)); +#endif + break; + } +#ifdef DEBUG + else { + if (debug > 5) + printf("considering %s: same net\n", + numtoa(md->rmtadr)); + } +#endif + + } +#ifdef DEBUG + if (debug > 4) + printf("this one is rank %d in list, limit is %d: %s\n", + lcnt, client_limit, + (lcnt <= client_limit) ? "ALLOW" : "REJECT"); +#endif + if (lcnt <= client_limit) { + this_client->lastdrop = 0; + return (int)(match->flags & ~RES_LIMITED); + } else { + this_client->lastdrop = current_time; + } + } return (int)match->flags; } @@ -257,6 +404,10 @@ restrict(op, resaddr, resmask, mflags, flags) rlprev->next = rl; restrictcount++; } + if ((rl->flags ^ (u_short)flags) & RES_LIMITED) { + res_limited_refcnt++; + mon_start(MON_RES); /* ensure data gets collected */ + } rl->flags |= (u_short)flags; break; @@ -265,8 +416,14 @@ restrict(op, resaddr, resmask, mflags, flags) * Remove some bits from the flags. If we didn't * find this one, just return. */ - if (rl != 0) + if (rl != 0) { + if ((rl->flags ^ (u_short)flags) & RES_LIMITED) { + res_limited_refcnt--; + if (res_limited_refcnt == 0) + mon_stop(MON_RES); + } rl->flags &= (u_short)~flags; + } break; case RESTRICT_REMOVE: @@ -280,6 +437,11 @@ restrict(op, resaddr, resmask, mflags, flags) && !(rl->mflags & RESM_INTERFACE)) { rlprev->next = rl->next; restrictcount--; + if (rl->flags & RES_LIMITED) { + res_limited_refcnt--; + if (res_limited_refcnt == 0) + mon_stop(MON_RES); + } memset((char *)rl, 0, sizeof(struct restrictlist)); rl->next = resfree; diff --git a/usr.sbin/xntpd/xntpd/ntp_unixclock.c b/usr.sbin/xntpd/xntpd/ntp_unixclock.c index 6b66183..815e09c 100644 --- a/usr.sbin/xntpd/xntpd/ntp_unixclock.c +++ b/usr.sbin/xntpd/xntpd/ntp_unixclock.c @@ -556,13 +556,18 @@ clock_parms(tickadj, tick) #endif /* SOLARIS */ #ifdef SYS_LINUX -/* XXX should look this up somewhere ! */ +#include static void clock_parms(tickadj, tick) U_LONG *tickadj; U_LONG *tick; { - *tickadj = (U_LONG)1; - *tick = (U_LONG)10000; + struct timex txc; + + txc.mode = 0; + __adjtimex(&txc); + + *tickadj = (U_LONG)1; /* our adjtime is accurate */ + *tick = (U_LONG)txc.tick; } #endif /* SYS_LINUX */ diff --git a/usr.sbin/xntpd/xntpd/ntpd.c b/usr.sbin/xntpd/xntpd/ntpd.c index abe86d9..6c9d640 100644 --- a/usr.sbin/xntpd/xntpd/ntpd.c +++ b/usr.sbin/xntpd/xntpd/ntpd.c @@ -28,8 +28,12 @@ #include "ntp_stdlib.h" #ifdef LOCK_PROCESS +#ifdef SYS_SOLARIS +#include +#else #include #endif +#endif /* * Signals we catch for debugging. If not debugging we ignore them. @@ -139,7 +143,7 @@ main(argc, argv) (void) dup2(0, 1); (void) dup2(0, 2); #ifdef NTP_POSIX_SOURCE -#if defined(SOLARIS) || defined(SYS_PTX) || defined(SYS_AUX3) || defined(SYS_AIX) +#if defined(SOLARIS) || defined(SYS_PTX) || defined(SYS_AUX3) || defined(SYS_AIX) || defined(SYS_ULTRIX) (void) setsid(); #else (void) setpgid(0, 0); @@ -219,13 +223,23 @@ main(argc, argv) if (rtprio(0, 120) < 0) syslog(LOG_ERR, "rtprio() error: %m"); #else -#if defined(PROCLOCK) && defined(LOCK_PROCESS) +#if defined(LOCK_PROCESS) +#if defined(MCL_CURRENT) && defined(MCL_FUTURE) + /* + * lock the process into memory + */ + if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) + syslog(LOG_ERR, "mlockall(): %m"); +#else +#if defined(PROCLOCK) /* * lock the process into memory */ if (plock(PROCLOCK) < 0) syslog(LOG_ERR, "plock(): %m"); #endif +#endif +#endif #if defined(NTPD_PRIO) && NTPD_PRIO != 0 /* * Set the priority. diff --git a/usr.sbin/xntpd/xntpd/refclock_chu.c b/usr.sbin/xntpd/xntpd/refclock_chu.c index 1c7c3bf..e73229e 100644 --- a/usr.sbin/xntpd/xntpd/refclock_chu.c +++ b/usr.sbin/xntpd/xntpd/refclock_chu.c @@ -599,6 +599,25 @@ chu_receive(rbufp) chu->lastupdate = current_time; /* + * Just for fun, we can debug the whole frame if + * we want. + */ + +#ifndef NO_CHU_DEBUG + syslog(LOG_DEBUG, "CHU %s packet:", (chuc->chutype == CHU_YEAR)? + "year":"time"); + for (i=0; i < NCHUCHARS; i++) { + char c[64]; + + sprintf(c,"%c%c %s",hexstring[chuc->codechars[i]&0xf], + hexstring[chuc->codechars[i]>>4], + ctime(&(chuc->codetimes[i].tv_sec))); + c[strlen(c)-1]=0; /* ctime() adds a damn \n */ + syslog(LOG_DEBUG, "%s .%06d", c, chuc->codetimes[i].tv_usec); + } +#endif + + /* * At this point we're assured that both halves of the * data match because of what the kernel has done. * But there's more than one data format. We need to diff --git a/usr.sbin/xntpd/xntpd/refclock_conf.c b/usr.sbin/xntpd/xntpd/refclock_conf.c index 430c4a6..535ca27 100644 --- a/usr.sbin/xntpd/xntpd/refclock_conf.c +++ b/usr.sbin/xntpd/xntpd/refclock_conf.c @@ -20,6 +20,12 @@ extern struct refclock refclock_local; #define refclock_local refclock_none #endif +#if defined(TRAK) || defined(TRAKCLK) || defined(TRAKPPS) +extern struct refclock refclock_trak; +#else +#define refclock_trak refclock_none +#endif + #if defined(PST) || defined(PSTCLK) || defined(PSTPPS) extern struct refclock refclock_pst; #else @@ -107,7 +113,7 @@ extern struct refclock refclock_gpstm; struct refclock *refclock_conf[] = { &refclock_none, /* 0 REFCLK_NONE */ &refclock_local, /* 1 REFCLK_LOCAL */ - &refclock_none, /* 2 REFCLK_WWV_HEATH */ + &refclock_trak, /* 2 REFCLK_GPS_TRAK */ &refclock_pst, /* 3 REFCLK_WWV_PST */ &refclock_wwvb, /* 4 REFCLK_WWVB_SPECTRACOM */ &refclock_goes, /* 5 REFCLK_GOES_TRUETIME */ diff --git a/usr.sbin/xntpd/xntpd/refclock_irig.c b/usr.sbin/xntpd/xntpd/refclock_irig.c index 40345bb..d3c2f12 100644 --- a/usr.sbin/xntpd/xntpd/refclock_irig.c +++ b/usr.sbin/xntpd/xntpd/refclock_irig.c @@ -19,7 +19,7 @@ * This driver supports the IRIG audio decoder. This clever gadget uses * a modified BSD audio driver for the Sun SPARCstation which provides * a timestamp, raw binary timecode, status byte and decoded ASCII - # timecode. The data are represented in the structure: + * timecode. The data are represented in the structure: * * struct irig_time { * struct timeval stamp; timestamp diff --git a/usr.sbin/xntpd/xntpd/refclock_parse.c b/usr.sbin/xntpd/xntpd/refclock_parse.c index 69e4837..f6ad067 100644 --- a/usr.sbin/xntpd/xntpd/refclock_parse.c +++ b/usr.sbin/xntpd/xntpd/refclock_parse.c @@ -1,8 +1,8 @@ #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) /* - * /src/NTP/REPOSITORY/v3/xntpd/refclock_parse.c,v 3.45 1994/01/25 19:06:27 kardel Exp + * /src/NTP/REPOSITORY/v3/xntpd/refclock_parse.c,v 3.51 1994/03/03 09:49:54 kardel Exp * - * refclock_parse.c,v 3.45 1994/01/25 19:06:27 kardel Exp + * refclock_parse.c,v 3.51 1994/03/03 09:49:54 kardel Exp * * generic reference clock driver for receivers * @@ -129,7 +129,7 @@ CURRENTLY NO BSD TTY SUPPORT #include "parse.h" #if !defined(NO_SCCSID) && !defined(lint) && !defined(__GNUC__) -static char rcsid[]="refclock_parse.c,v 3.45 1994/01/25 19:06:27 kardel Exp"; +static char rcsid[]="refclock_parse.c,v 3.51 1994/03/03 09:49:54 kardel Exp"; #endif /**=========================================================================== @@ -1653,7 +1653,8 @@ local_poll(parse) * done if no more characters are available */ FD_SET(fd, &fdmask); - if (select(fd + 1, &fdmask, 0, 0, &null_time) == 0) + if ((i == 0) && + (select(fd + 1, &fdmask, 0, 0, &null_time) == 0)) return; } } @@ -1706,7 +1707,8 @@ parsestate(state, buffer) { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" }, { PARSEB_DST, "DST" }, { PARSEB_UTC, "UTC DISPLAY" }, - { PARSEB_LEAP, "LEAP WARNING" }, + { PARSEB_LEAPADD, "LEAP ADD WARNING" }, + { PARSEB_LEAPDEL, "LEAP DELETE WARNING" }, { PARSEB_LEAPSECOND, "LEAP SECOND" }, { PARSEB_ALTERNATE,"ALTERNATE ANTENNA" }, { PARSEB_TIMECODE, "TIME CODE" }, @@ -2539,9 +2541,10 @@ static void parse_leap() { /* - * PARSE does encode a leap warning... we are aware but not afraid of that - * as long as we get a little help for the direction from the operator until * PARSE encodes the LEAP correction direction. + * For timecodes that do not pass on the leap correction direction + * the default PARSEB_LEAPADD must be used. It may then be modified + * with a fudge flag (flag2). */ } @@ -2821,7 +2824,7 @@ parse_control(unit, in, out) sprintf(tt, "refclock_iomode=\"%s\"", parse->binding->bd_description); tt = add_var(&out->kv_list, 128, RO); - sprintf(tt, "refclock_driver_version=\"refclock_parse.c,v 3.45 1994/01/25 19:06:27 kardel Exp\""); + sprintf(tt, "refclock_driver_version=\"refclock_parse.c,v 3.51 1994/03/03 09:49:54 kardel Exp\""); out->lencode = strlen(outstatus); out->lastcode = outstatus; @@ -3245,14 +3248,24 @@ parse_process(parse, parsetime) } else { - if (PARSE_LEAP(parsetime->parse_state)) + if (PARSE_LEAPADD(parsetime->parse_state)) { + /* + * we pick this state also for time code that pass leap warnings + * without direction information (as earth is currently slowing + * down). + */ leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND; } else - { - leap = LEAP_NOWARNING; - } + if (PARSE_LEAPDEL(parsetime->parse_state)) + { + leap = LEAP_DELSECOND; + } + else + { + leap = LEAP_NOWARNING; + } } refclock_receive(parse->peer, &off, 0, LFPTOFP(&dispersion), &reftime, &rectime, leap); @@ -3396,6 +3409,15 @@ trimble_init(parse) * History: * * refclock_parse.c,v + * Revision 3.49 1994/02/20 13:26:00 kardel + * rcs id cleanup + * + * Revision 3.48 1994/02/20 13:04:56 kardel + * parse add/delete second support + * + * Revision 3.47 1994/02/02 17:44:30 kardel + * rcs ids fixed + * * Revision 3.45 1994/01/25 19:06:27 kardel * 94/01/23 reconcilation * diff --git a/usr.sbin/xntpd/xntpd/refclock_trak.c b/usr.sbin/xntpd/xntpd/refclock_trak.c new file mode 100644 index 0000000..39fb7d4 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_trak.c @@ -0,0 +1,967 @@ +/* + * refclock_trak.c - clock driver for the TRAK 8810 GPS STATION CLOCK + * Tsuruoka Tomoaki Oct 30, 1993 + * + */ +#if defined(REFCLOCK) && (defined(TRAK) || defined(TRAKCLK) || defined(TRAKPPS)) + +#include +#include +#include + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" + +#if defined(HAVE_BSD_TTYS) +#include +#endif /* HAVE_BSD_TTYS */ + +#if defined(HAVE_SYSV_TTYS) +#include +#endif /* HAVE_SYSV_TTYS */ + +#if defined(STREAM) +#include +#include +#if defined(TRAKCLK) +#include +#endif /* TRAKCLK */ +#endif /* STREAM */ + +#if defined (TRAKPPS) +#include +#endif /* TRAKPPS */ + +#include "ntp_stdlib.h" + +/* + * This driver supports the TRAK 8810 GPS Receiver with + * Buffered RS-232-C Interface Module. + * + * Most of codes are copied from refclock_as2201.c, Thanks a lot. + * + * The program expects the radio responses once per seccond + * ( by "rqts,u" command or panel control ) + * of the form "*RQTS U,ddd:hh:mm:ss.0,Q\r\n for UTC" where + * ddd= day of year + * hh= hours + * mm= minutes + * ss= seconds + * Q= Quality byte. Q=0 Phase error > 20 us + * Q=6 Pahse error < 20 us + * > 10 us + * Q=5 Pahse error < 10 us + * > 1 us + * Q=4 Pahse error < 1 us + * > 100 ns + * Q=3 Pahse error < 100 ns + * > 10 ns + * Q=2 Pahse error < 10 ns + * (note that my clock almost stable at 1 us per 10 hours) + * + */ + +/* + * Definitions + */ +#define MAXUNITS 4 /* max number of GPS units */ +#define GPS232 "/dev/gps%d" /* name of radio device */ +#define SPEED232 B9600 /* uart speed (9600 baud) */ + +/* + * Radio interface parameters + */ +#define GPSPRECISION (-20) /* precision assumed (about 1 us) */ +#define GPSREFID "GPS" /* reference id */ +#define GPSDESCRIPTION "TRAK 8810 GPS station clock" /* who we are */ +#define GPSHSREFID 0x7f7f110a /* 127.127.17.10 refid hi strata */ +#define GMT 0 /* hour offset from Greenwich */ +#define NCODES 3 /* stages of median filter */ +#define LENTOC 25 /* *RQTS U,ddd:hh:mm:ss.0,Q datecode length */ +#define BMAX 100 /* timecode buffer length */ +#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ + +/* + * Hack to avoid excercising the multiplier. I have no pride. + */ +#define MULBY10(x) (((x)<<3) + ((x)<<1)) + +/* + * Imported from ntp_timer module + */ +extern U_LONG current_time; /* current time (s) */ + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * GPS unit control structure. + */ +struct gpsunit { + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + l_fp lastrec; /* last data receive time */ + l_fp lastref; /* last timecode time */ + l_fp offset[NCODES]; /* recent sample offsets */ + char lastcode[BMAX]; /* last timecode received */ + u_short polled; /* when polled, means a last sample */ + u_char lencode; /* length of last received ASCII string */ + U_LONG lasttime; /* last time clock heard from */ +#ifdef TRAKPPS + U_LONG lastev; /* last ppsclock second */ +#endif /* TRAKPPS */ + u_char unit; /* unit number for this guy */ + u_char status; /* clock status */ + u_char lastevent; /* last clock event */ + u_char reason; /* reason for last abort */ + u_char year; /* year of eternity */ + u_short day; /* day of year */ + u_char hour; /* hour of day */ + u_char minute; /* minute of hour */ + u_char second; /* seconds of minute */ + u_short msec; /* milliseconds of second */ + u_char leap; /* leap indicators */ + U_LONG yearstart; /* start of current year */ + /* + * Status tallies + */ + U_LONG polls; /* polls sent */ + U_LONG noreply; /* no replies to polls */ + U_LONG coderecv; /* timecodes received */ + U_LONG badformat; /* bad format */ + U_LONG baddata; /* bad data */ + U_LONG timestarted; /* time we started this */ +}; + + +/* + * Data space for the unit structures. Note that we allocate these on + * the fly, but never give them back. + */ +static struct gpsunit *gpsunits[MAXUNITS]; +static u_char unitinuse[MAXUNITS]; + +/* + * Keep the fudge factors separately so they can be set even + * when no clock is configured. + */ +static l_fp fudgefactor[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static u_char sloppyclockflag[MAXUNITS]; + +/* + * Function prototypes + */ +static void trak_init P(()); +static int trak_start P((u_int, struct peer *)); +static void trak_shutdown P((int)); +static void trak_report_event P((struct gpsunit *, int)); +static void trak_receive P((struct recvbuf *)); +static char trak_process P((struct gpsunit *, l_fp *, u_fp *)); +static void trak_poll P((int unit, struct peer *)); +static void trak_control P((u_int, struct refclockstat *, struct refclockstat *)); +static void trak_buginfo P((int, struct refclockbug *)); + +/* + * Transfer vector + */ +struct refclock refclock_trak = { + trak_start, trak_shutdown, trak_poll, + trak_control, trak_init, trak_buginfo, NOFLAGS +}; + +/* + * trak_init - initialize internal gps driver data + */ +static void +trak_init() +{ + register int i; + /* + * Just zero the data arrays + */ + bzero((char *)gpsunits, sizeof gpsunits); + bzero((char *)unitinuse, sizeof unitinuse); + + /* + * Initialize fudge factors to default. + */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor[i].l_ui = 0; + fudgefactor[i].l_uf = 0; + stratumtouse[i] = 0; + sloppyclockflag[i] = 0; + } +} + + +/* + * trak_start - open the GPS devices and initialize data for processing + */ +static int +trak_start(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct gpsunit *gps; + register int i; + int fd232; + char trakdev[20]; +#ifdef TRAKPPS + struct ppsclockev ev; +#endif /* TRAKPPS */ + + /* + * Check configuration info + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "trak_start: unit %d invalid", unit); + return (0); + } + if (unitinuse[unit]) { + syslog(LOG_ERR, "trak_start: unit %d in use", unit); + return (0); + } + + /* + * Open serial port + */ + (void) sprintf(trakdev, GPS232, unit); + fd232 = open(trakdev, O_RDWR, 0777); + if (fd232 == -1) { + syslog(LOG_ERR, "trak_start: open of %s: %m", trakdev); + return (0); + } + +#if defined(HAVE_SYSV_TTYS) + /* + * System V serial line parameters (termio interface) + * + */ + { struct termio ttyb; + if (ioctl(fd232, TCGETA, &ttyb) < 0) { + syslog(LOG_ERR, + "trak_start: ioctl(%s, TCGETA): %m", trakdev); + goto screwed; + } + ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL; + ttyb.c_oflag = 0; + ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD; + ttyb.c_lflag = ICANON; + ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0'; + if (ioctl(fd232, TCSETA, &ttyb) < 0) { + syslog(LOG_ERR, + "trak_start: ioctl(%s, TCSETA): %m", trakdev); + goto screwed; + } + } +#endif /* HAVE_SYSV_TTYS */ +#if defined(STREAM) + /* + * POSIX/STREAMS serial line parameters (termios interface) + * + * The TRAKCLK option provides timestamping at the driver level. + * It requires the tty_clk streams module. + * + * The TRAKPPS option provides timestamping at the driver level. + * It uses a 1-pps signal and level converter (gadget box) and + * requires the ppsclock streams module and SunOS 4.1.1 or + * later. + */ + { struct termios ttyb, *ttyp; + + ttyp = &ttyb; + if (tcgetattr(fd232, ttyp) < 0) { + syslog(LOG_ERR, + "trak_start: tcgetattr(%s): %m", trakdev); + goto screwed; + } + ttyp->c_iflag = IGNBRK|IGNPAR; + ttyp->c_oflag = 0; + ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD; + ttyp->c_lflag = ICANON; + ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; + if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { + syslog(LOG_ERR, + "trak_start: tcsetattr(%s): %m", trakdev); + goto screwed; + } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(LOG_ERR, + "trak_start: tcflush(%s): %m", trakdev); + goto screwed; + } +#if defined(TRAKCLK) + if (ioctl(fd232, I_PUSH, "clk") < 0) + syslog(LOG_ERR, + "trak_start: ioctl(%s, I_PUSH, clk): %m", trakdev); + if (ioctl(fd232, CLK_SETSTR, "*") < 0) + syslog(LOG_ERR, + "trak_start: ioctl(%s, CLK_SETSTR): %m", trakdev); +#endif /* TRAKCLK */ +#if defined(TRAKPPS) + if (ioctl(fd232, I_PUSH, "ppsclock") < 0) + syslog(LOG_ERR, + "trak_start: ioctl(%s, I_PUSH, ppsclock): %m", trakdev); + else + fdpps = fd232; +#endif /* TRAKPPS */ + } +#endif /* STREAM */ +#if defined(HAVE_BSD_TTYS) + /* + * 4.3bsd serial line parameters (sgttyb interface) + * + * The TRAKCLK option provides timestamping at the driver level. + * It requires the tty_clk line discipline and 4.3bsd or later. + */ + { struct sgttyb ttyb; +#if defined(TRAKCLK) + int ldisc = CLKLDISC; +#endif /* TRAKCLK */ + + if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { + syslog(LOG_ERR, + "trak_start: ioctl(%s, TIOCGETP): %m", trakdev); + goto screwed; + } + ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; +#if defined(TRAKCLK) + ttyb.sg_erase = ttyb.sg_kill = '\r'; + ttyb.sg_flags = RAW; +#else + ttyb.sg_erase = ttyb.sg_kill = '\0'; + ttyb.sg_flags = EVENP|ODDP|CRMOD; +#endif /* TRAKCLK */ + if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { + syslog(LOG_ERR, + "trak_start: ioctl(%s, TIOCSETP): %m", trakdev); + goto screwed; + } +#if defined(TRAKCLK) + if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { + syslog(LOG_ERR, + "trak_start: ioctl(%s, TIOCSETD): %m",trakdev); + goto screwed; + } +#endif /* TRAKCLK */ + } +#endif /* HAVE_BSD_TTYS */ + + /* + * Allocate unit structure + */ + if (gpsunits[unit] != 0) { + gps = gpsunits[unit]; /* The one we want is okay */ + } else { + for (i = 0; i < MAXUNITS; i++) { + if (!unitinuse[i] && gpsunits[i] != 0) + break; + } + if (i < MAXUNITS) { + /* + * Reclaim this one + */ + gps = gpsunits[i]; + gpsunits[i] = 0; + } else { + gps = (struct gpsunit *) + emalloc(sizeof(struct gpsunit)); + } + } + bzero((char *)gps, sizeof(struct gpsunit)); + gpsunits[unit] = gps; + + /* + * Set up the structures + */ + gps->peer = peer; + gps->unit = (u_char)unit; + gps->timestarted = current_time; + + gps->io.clock_recv = trak_receive; + gps->io.srcclock = (caddr_t)gps; + gps->io.datalen = 0; + gps->io.fd = fd232; +#ifdef TRAKPPS + if (ioctl(fd232, CIOGETEV, (caddr_t)&ev) < 0) { + syslog(LOG_ERR, + "trak_start: ioctl(%s, CIOGETEV): %m", trakdev); + goto screwed; + } else + gps->lastev = ev.tv.tv_sec; +#endif /* TRAKPPS */ + if (!io_addclock(&gps->io)) { + goto screwed; + } + + /* + * All done. Initialize a few random peer variables, then + * return success. Note that root delay and root dispersion are + * always zero for this clock. + */ + peer->precision = GPSPRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + peer->stratum = stratumtouse[unit]; + if (stratumtouse[unit] <= 1) + bcopy(GPSREFID, (char *)&peer->refid, 4); + else + peer->refid = htonl(GPSHSREFID); + unitinuse[unit] = 1; + /* + * request to give time code + */ + { + void gps_send(); + gps_send(gps,"\rRQTS,U\r"); + gps_send(gps,"SEL 00\r"); + } + + return (1); + + /* + * Something broke; abandon ship. + */ +screwed: + (void) close(fd232); + return (0); +} + +/* + * trak_shutdown - shut down a GPS clock + */ +static void +trak_shutdown(unit) + int unit; +{ + register struct gpsunit *gps; + void gps_send(); + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "trak_shutdown: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "trak_shutdown: unit %d not in use", unit); + return; + } + gps = gpsunits[unit]; + /* + * request not to give time code any more + */ + gps_send(gps,"RQTX\r"); + /* + * Tell the I/O module to turn us off. We're history. + */ + io_closeclock(&gps->io); + + unitinuse[unit] = 0; +} + + +/* + * trak_report_event - note the occurance of an event + * + * This routine presently just remembers the report and logs it, but + * does nothing heroic for the trap handler. + */ +static void +trak_report_event(gps, code) + struct gpsunit *gps; + int code; +{ + struct peer *peer; + + peer = gps->peer; + if (gps->status != (u_char)code) { + gps->status = (u_char)code; + if (code != CEVNT_NOMINAL) + gps->lastevent = (u_char)code; + syslog(LOG_INFO, + "clock %s event %x\n", ntoa(&peer->srcadr), code); + } +} + + +/* + * trak_receive - receive data from the serial interface + */ +static void +trak_receive(rbufp) + struct recvbuf *rbufp; +{ + register int i,cmdtype; + register struct gpsunit *gps; + +#if defined(TRAKPPS) + struct ppsclockev ev; + l_fp trtmp; +#endif /* TRAKPPS */ + register u_char *dpt; + register u_char *cp; + register u_char *dpend; + l_fp tstmp; + u_fp dispersion; + + /* + * Get the clock this applies to and pointers to the data. + * Edit the timecode to remove control chars and trashbits. + */ + gps = (struct gpsunit *)rbufp->recv_srcclock; + dpt = (u_char *)&rbufp->recv_space; + dpend = dpt + rbufp->recv_length; + cp = (u_char *)gps->lastcode; + + while (dpt < dpend) { +#ifdef TRAKCLK /* prior to TRAKPPS due to timestamp */ + if ((*cp = 0x7f & *dpt++) != '*' ) cp++; + else if (*cp == '*' ) { /* caught magic character */ + if ( dpend - dpt < 8) { + /* short timestamp */ + if(debug) puts("gps: short timestamp."); + return; + } + if (!buftvtots(dpt,&gps->lastrec)) { + /* screwy timestamp */ + if(debug) puts("gps: screwy timestamp."); + return; + } + dpt += 8; + } +#else +#ifdef TRAKPPS + if ((*cp = 0x7f & *dpt++) >= ' ') cp++; +#else + /* both are not specified */ +#endif /* TRAKPPS */ +#endif /* TRAKCLK */ + } + *cp = '\0'; + gps->lencode = cp - (u_char *)gps->lastcode; + if (gps->lencode == 0) return; + +#ifdef DEBUG + if (debug) + printf("gps: timecode %d %s\n", + gps->lencode, gps->lastcode); +#endif + + /* + * We check the timecode format and decode its contents. The + * timecode has format *........RQTS U,ddd:hh:mm:ss.0,Q\r\n). + * 012345678901234567890123 + */ + cp = (u_char *)gps->lastcode; + gps->leap = 0; + cmdtype=0; + if (strncmp(cp,"RQTS",4)==0) { + cmdtype=1; + cp += 7; + } + else if(strncmp(cp,"*RQTS",5)==0) { + cmdtype=2; + cp += 8; + } + else return; + switch( cmdtype ) { + case 1: + case 2: + /* + * Check time code format of TRAK 8810 + */ + if( !isdigit(cp[0]) || + !isdigit(cp[1]) || + !isdigit(cp[2]) || + cp[3] != ':' || + !isdigit(cp[4]) || + !isdigit(cp[5]) || + cp[6] != ':' || + !isdigit(cp[7]) || + !isdigit(cp[8]) || + cp[9] != ':' || + !isdigit(cp[10])|| + !isdigit(cp[11])) { + gps->badformat++; + trak_report_event(gps, CEVNT_BADREPLY); + return; + } + break; + default: + return; + + } + + /* + * Convert date and check values. + */ + gps->day = cp[0] - '0'; + gps->day = MULBY10(gps->day) + cp[1] - '0'; + gps->day = MULBY10(gps->day) + cp[2] - '0'; + if (gps->day < 1 || gps->day > 366) { + gps->baddata++; + trak_report_event(gps, CEVNT_BADDATE); + return; + } + /* + * Convert time and check values. + */ + gps->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; + gps->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; + gps->second = MULBY10(cp[10] - '0') + cp[11] - '0'; + gps->msec = 0; + if (gps->hour > 23 || gps->minute > 59 || gps->second > 59) { + gps->baddata++; + trak_report_event(gps, CEVNT_BADTIME); + return; + } + + /* + * Test for synchronization + */ +/* + switch( cp[15] ) { + case '0': + if(gps->peer->stratum == stratumtouse[gps->unit]) { + gps->peer->stratum = 10 ; + bzero(&gps->peer->refid,4); + } + break; + default: + if(gps->peer->stratum != stratumtouse[gps->unit]) { + gps->peer->stratum = stratumtouse[gps->unit] ; + bcopy(GPSREFID,&gps->peer->refid,4); + } + break; + } +*/ + gps->lasttime = current_time; + + if (!gps->polled) return; + + /* + * Now, compute the reference time value. Use the heavy + * machinery for the second, which presumably is the one which + * occured at the last pps pulse and which was captured by the + * loop_filter module. All we have to do here is present a + * reasonable facsimile of the time at that pulse so the clock- + * filter and selection machinery declares us truechimer. The + * precision offset within the second is really tuned by the + * loop_filter module. Note that this code does not yet know how + * to do the years and relies on the clock-calendar chip for + * sanity. + */ + +#if defined(TRAKPPS) + + /* + * timestamp must be greater than previous one. + */ + if (ioctl(fdpps, CIOGETEV, (caddr_t)&ev) >= 0) { + ev.tv.tv_sec += (U_LONG)JAN_1970; + TVTOTS(&ev.tv,&gps->lastrec); + if (gps->lastev < ev.tv.tv_sec) { + gps->lastev = ev.tv.tv_sec; + } else { /* in case of 1-pps missing */ + gps->lastev = ev.tv.tv_sec; + return; + } + } + else + return; /* failed to get timestamp */ +#endif /* TRAKPPS */ + + if (!clocktime(gps->day, gps->hour, gps->minute, + gps->second, GMT, gps->lastrec.l_ui, + &gps->yearstart, &gps->lastref.l_ui)) { + gps->baddata++; + trak_report_event(gps, CEVNT_BADTIME); +#ifdef DEBUG + if(debug) printf("gps: bad date \n"); +#endif + return; + } + MSUTOTSF(gps->msec, gps->lastref.l_uf); + tstmp = gps->lastref; + + L_SUB(&tstmp, &gps->lastrec); + L_ADD(&tstmp, &(fudgefactor[gps->unit])); + i = ((int)(gps->coderecv)) % NCODES; + gps->offset[i] = tstmp; + gps->coderecv++; +#if DEBUG + if (debug) + printf("gps: times %s %s %s\n", + ulfptoa(&gps->lastref, 6), ulfptoa(&gps->lastrec, 6), + lfptoa(&tstmp, 6)); +#endif +/* if( tstmp.l_ui != 0 ) return; something wrong */ + + /* + * Process the samples in the median filter, add the fudge + * factor and pass the offset and dispersion along. We use + * lastref as both the reference time and receive time in order + * to avoid being cute, like setting the reference time later + * than the receive time, which may cause a paranoid protocol + * module to chuck out the data. + */ + if (gps->coderecv < NCODES) + return; + if (!trak_process(gps, &tstmp, &dispersion)) { + gps->baddata++; + trak_report_event(gps, CEVNT_BADTIME); + return; + } + refclock_receive(gps->peer, &tstmp, GMT, dispersion, + &gps->lastrec, &gps->lastrec, gps->leap); + /* + * after all, clear polled flag + */ + gps->polled = 0; +} + +/* + * ================================================================== + * gps_send(gps,cmd) Sends a command to the GPS receiver. + * as gps_send(gps,"rqts,u\r"); + * ================================================================== + */ +static void +gps_send(gps,cmd) + struct gpsunit *gps; + char *cmd; +{ + if (write(gps->io.fd, cmd, strlen(cmd)) == -1) { + syslog(LOG_ERR, "gps_send: unit %d: %m", gps->unit); + trak_report_event(gps,CEVNT_FAULT); + } else { + gps->polls++; + } +} + +/* + * trak_process - process a pile of samples from the clock + * + * This routine uses a three-stage median filter to calculate offset and + * dispersion and reduce jitter. The dispersion is calculated as the + * span of the filter (max - min). + */ +static char +trak_process(gps, offset, dispersion) + struct gpsunit *gps; + l_fp *offset; + u_fp *dispersion; +{ + register int i, j; + register U_LONG tmp_ui, tmp_uf; + int not_median1 = -1; /* XXX correct? */ + int not_median2 = -1; /* XXX correct? */ + int median; + u_fp disp_tmp, disp_tmp2; + + /* + * This code implements a three-stage median filter. First, we + * check if the samples are within 125 ms of each other. If not, + * dump the sample set. We take the median of the three offsets + * and use that as the sample offset. There probably is not much + * to be gained by a longer filter, since the clock filter in + * ntp_proto should do its thing. + */ + disp_tmp2 = 0; + for (i = 0; i < NCODES-1; i++) { + for (j = i+1; j < NCODES; j++) { + tmp_ui = gps->offset[i].l_ui; + tmp_uf = gps->offset[i].l_uf; + M_SUB(tmp_ui, tmp_uf, gps->offset[j].l_ui, + gps->offset[j].l_uf); + if (M_ISNEG(tmp_ui, tmp_uf)) { + M_NEG(tmp_ui, tmp_uf); + } + if (tmp_ui != 0 || tmp_uf > CODEDIFF) { + return (0); + } + disp_tmp = MFPTOFP(0, tmp_uf); + if (disp_tmp > disp_tmp2) { + disp_tmp2 = disp_tmp; + not_median1 = i; + not_median2 = j; + } + } + } + if (gps->lasttime == 0) + disp_tmp2 = NTP_MAXDISPERSE; + else + disp_tmp2 = current_time - gps->lasttime; + if (not_median1 == 0) { + if (not_median2 == 1) + median = 2; + else + median = 1; + } else { + median = 0; + } + *offset = gps->offset[median]; + *dispersion = disp_tmp2; + return (1); +} + +/* + * trak_poll - called by the transmit procedure + * + * We go to great pains to avoid changing state here, since there may be + * more than one eavesdropper receiving the same timecode. + */ +static void +trak_poll(unit, peer) + int unit; + struct peer *peer; +{ + struct gpsunit *gps; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "trak_poll: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "trak_poll: unit %d not in use", unit); + return; + } + gps = gpsunits[unit]; + if ((current_time - gps->lasttime) > 150) + trak_report_event(gpsunits[unit], CEVNT_TIMEOUT); + /* + * usually trak_receive can get a timestamp every second + */ +#ifndef TRAKPPS && TRAKCLK + gettstamp(&gps->lastrec); +#endif + gps->polls++; + /* + * may be polled every 64 seconds + */ + gps->polled = 1; +} + +/* + * trak_control - set fudge factors, return statistics + */ +static void +trak_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct gpsunit *gps; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "trak_control: unit %d invalid", unit); + return; + } + + if (in != 0) { + if (in->haveflags & CLK_HAVETIME1) + fudgefactor[unit] = in->fudgetime1; + if (in->haveflags & CLK_HAVEVAL1) { + stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf); + if (unitinuse[unit]) { + struct peer *peer; + + /* + * Should actually reselect clock, but + * will wait for the next timecode + */ + gps = gpsunits[unit]; + peer = gps->peer; + peer->stratum = stratumtouse[unit]; + if (stratumtouse[unit] <= 1) + bcopy(GPSREFID, (char *)&peer->refid, + 4); + else + peer->refid = htonl(GPSHSREFID); + } + } + } + + if (out != 0) { + out->type = REFCLK_GPS_TRAK; + out->haveflags + = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2; + out->clockdesc = GPSDESCRIPTION; + out->fudgetime1 = fudgefactor[unit]; + out->fudgetime2.l_ui = 0; + out->fudgetime2.l_uf = 0; + out->fudgeval1 = (LONG)stratumtouse[unit]; + out->fudgeval2 = 0; + out->flags = sloppyclockflag[unit]; + if (unitinuse[unit]) { + gps = gpsunits[unit]; + out->lencode = LENTOC; + out->timereset = current_time - gps->timestarted; + out->polls = gps->polls; + out->noresponse = gps->noreply; + out->badformat = gps->badformat; + out->baddata = gps->baddata; + out->lastevent = gps->lastevent; + out->currentstatus = gps->status; + } else { + out->lencode = 0; + out->lastcode = ""; + out->polls = out->noresponse = 0; + out->badformat = out->baddata = 0; + out->timereset = 0; + out->currentstatus = out->lastevent = CEVNT_NOMINAL; + } + } +} + +/* + * trak_buginfo - return clock dependent debugging info + */ +static void +trak_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct gpsunit *gps; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "trak_buginfo: unit %d invalid", unit); + return; + } + + if (!unitinuse[unit]) + return; + gps = gpsunits[unit]; + + bug->nvalues = 10; + bug->ntimes = 5; + if (gps->lasttime != 0) + bug->values[0] = current_time - gps->lasttime; + else + bug->values[0] = 0; + bug->values[1] = (U_LONG)gps->reason; + bug->values[2] = (U_LONG)gps->year; + bug->values[3] = (U_LONG)gps->day; + bug->values[4] = (U_LONG)gps->hour; + bug->values[5] = (U_LONG)gps->minute; + bug->values[6] = (U_LONG)gps->second; + bug->values[7] = (U_LONG)gps->msec; + bug->values[8] = gps->noreply; + bug->values[9] = gps->yearstart; + bug->stimes = 0x1c; + bug->times[0] = gps->lastref; + bug->times[1] = gps->lastrec; + bug->times[2] = gps->offset[0]; + bug->times[3] = gps->offset[1]; + bug->times[4] = gps->offset[2]; +} +#endif diff --git a/usr.sbin/xntpd/xntpdc/ntpdc.c b/usr.sbin/xntpd/xntpdc/ntpdc.c index 001bd77..aae1b40 100644 --- a/usr.sbin/xntpd/xntpdc/ntpdc.c +++ b/usr.sbin/xntpd/xntpdc/ntpdc.c @@ -598,8 +598,12 @@ again: /* * So far, so good. Copy this data into the output array. */ - if ((datap + datasize) > (pktdata + pktdatasize)) + if ((datap + datasize) > (pktdata + pktdatasize)) { + int offset = datap - pktdata; growpktdata(); + *rdata = pktdata; /* might have been realloced ! */ + datap = pktdata + offset; + } memmove(datap, (char *)rpkt.data, datasize); datap += datasize; if (firstpkt) { diff --git a/usr.sbin/xntpd/xntpdc/ntpdc_ops.c b/usr.sbin/xntpd/xntpdc/ntpdc_ops.c index 55483fa..d708d4b 100644 --- a/usr.sbin/xntpd/xntpdc/ntpdc_ops.c +++ b/usr.sbin/xntpd/xntpdc/ntpdc_ops.c @@ -846,8 +846,12 @@ sysstats(pcmd, fp) if (!check1item(items, fp)) return; - if (!checkitemsize(itemsize, sizeof(struct info_sys_stats))) + if (itemsize != sizeof(struct info_sys_stats) && + itemsize != sizeof(struct old_info_sys_stats)) { + /* issue warning according to new structure size */ + checkitemsize(itemsize, sizeof(struct info_sys_stats)); return; + } (void) fprintf(fp, "system uptime: %d\n", ntohl(ss->timeup)); @@ -869,6 +873,11 @@ sysstats(pcmd, fp) ntohl(ss->badauth)); (void) fprintf(fp, "wander hold downs: %d\n", ntohl(ss->wanderhold)); + if (itemsize != sizeof(struct info_sys_stats)) + return; + + (void) fprintf(fp, "limitation rejects: %d\n", + ntohl(ss->limitrejected)); } @@ -1243,6 +1252,7 @@ static struct resflags resflags[] = { { "nopeer", RES_NOPEER }, { "notrap", RES_NOTRAP }, { "lptrap", RES_LPTRAP }, + { "limited", RES_LIMITED }, { "", 0 } }; @@ -1463,6 +1473,7 @@ monlist(pcmd, fp) FILE *fp; { struct info_monitor *ml; + struct old_info_monitor *oml; int items; int itemsize; int res; @@ -1476,23 +1487,49 @@ monlist(pcmd, fp) if (!checkitems(items, fp)) return; - if (!checkitemsize(itemsize, sizeof(struct info_monitor))) - return; + if (itemsize == sizeof(struct info_monitor)) { - (void) fprintf(fp, - " address port count mode version lasttime firsttime\n"); - (void) fprintf(fp, - "=====================================================================\n"); - while (items > 0) { - (void) fprintf(fp, "%-20.20s %5d %9d %4d %3d %9u %9u\n", - nntohost(ml->addr), - ntohs(ml->port), - ntohl(ml->count), - ml->mode, ml->version, - ntohl(ml->lasttime), - ntohl(ml->firsttime)); - ml++; - items--; + (void) fprintf(fp, + " address port count mode version lastdrop lasttime firsttime\n"); + (void) fprintf(fp, + "===============================================================================\n"); + while (items > 0) { + (void) fprintf(fp, "%-20.20s %5d %9d %4d %3d %9u %9u %9u\n", + nntohost(ml->addr), + ntohs(ml->port), + ntohl(ml->count), + ml->mode, + ml->version, + ntohl(ml->lastdrop), + ntohl(ml->lasttime), + ntohl(ml->firsttime)); + ml++; + items--; + } + } else { + if (itemsize != sizeof(struct old_info_monitor)) { + /* issue warning according to new info_monitor size */ + checkitemsize(itemsize, sizeof(struct info_monitor)); + return; + } + + oml = (struct old_info_monitor *)ml; + (void) fprintf(fp, + " address port count mode version lasttime firsttime\n"); + (void) fprintf(fp, + "======================================================================\n"); + while (items > 0) { + (void) fprintf(fp, "%-20.20s %5d %9d %4d %3d %9u %9u\n", + nntohost(oml->addr), + ntohs(oml->port), + ntohl(oml->count), + oml->mode, + oml->version, + ntohl(oml->lasttime), + ntohl(oml->firsttime)); + oml++; + items--; + } } } -- cgit v1.1